diff --git a/CHANGELOG.md b/CHANGELOG.md index 595375e..aab2e78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,35 @@ answer for minutes. Tell Felhom; we will fix it."). An older agent is not judged (`agentapi.GuestMemoryInfo` `host_swap_mb`/`guest_swap_mb`, 2 s timeout). The `deploy_new` i18n fixture regenerated on purpose (one line). - Tests (each seen failing first): `TestR1177_*` (5), `TestR1195_*` (5), `TestR1178_*`, `TestR1180_*`, `TestR1192_SwapShort*`. +- **W12 (rehearsal word table) — the family card takes a family's own names.** „Ödön", „Írisz" were refused („A név + 1–32 karakter: kisbetű, szám…"). The name is now kept exactly as typed and the LOGIN is made from it: accents removed, + lower case, a space becomes a dot, other signs dropped (`family.LoginFrom`; „Ödön" → `odon`, „Írisz" → `irisz`). A + second person whose name makes the same login gets the next number (`odon2`); the same name again is still „Már van + ilyen nevű családtag." The login's own check is unchanged (1–32 of `a-z 0-9 . _ -`). The card lists „Ödön + belépési név: odon" (EN „login: odon"), the once-shown password names the login, and a hint says „Írd be a nevet úgy, + ahogy szeretnéd, ékezettel is. A belépési név ebből készül: ékezet nélkül, kisbetűvel. A listában látod." (EN „Type + the name as you like it, accents too. The login is made from it: no accents, lower case. You see it in the list."). + The family sign-in page accepts the name typed either way. New refusal text: „Ebből a névből nem tudunk belépési + nevet készíteni. Írj be egy legfeljebb 64 betűs nevet latin betűkkel. Az ékezet jó." (EN „We cannot make a login from + this name. Type a name of at most 64 letters in the Latin alphabet. Accents are fine."). `family.json` gains + `display` (omitted when it equals the login). Tests `TestW12_AccentedNamesGetAPlainLogin`, + `TestW12_NumberedLoginStaysInsideTheLimit`, `TestW12_CardTakesAccentedNamesAndShowsTheLogin`. +- **W9 — the dashboard's yellow bar link** („Helyreállítási kód létrehozása →") takes the banner's colour, underlined, + instead of browser-default blue (`.alert-message a`). `TestW9_BannerSentenceLinkTakesTheBannerColour`. +- **W10 — the recovery-code page.** When every check passed the household reads one line, „Minden előfeltétel + rendben." (EN „All prerequisites are in place."); the rows (storage name, encryption tool path) wait behind + „Részletek" / „Details". A failed check still shows the list open. The step headings lose their numbers (cards 2–4 + hide as the wizard moves on, so the page read 1 → 5 → 6). +- **W11 + W24 — the „Távoli mentés" page.** The Felhom-provided store shows „Felhom távoli tárhely (Németország)" / + „Ide kerül a titkosított másolat." (EN „Felhom remote storage (Germany)" / „The encrypted copy goes here.") instead + of the provider login; the login waits behind „Haladó" / „Advanced". The SSH key and known-host fields of the target + form sit behind „Haladó" too; their hint reads „A célgép saját kulcsa. Ezzel a doboz csak ehhez a géphez kapcsolódik. + Így kérdezheted le:" (EN „This is the key of the target machine. With it, the box connects only to this machine. To + look it up:"; was „no blind TOFU"). The page's intro no longer says „restic + SFTP" or „3-2-1": „Az alkalmazásaid + mentéseiről titkosított másolat készül egy távoli helyre: a Felhom tárhelyére vagy a saját NAS-odra. Ott csak + titkosított adat van. Ez a másolat független a helyi másodpéldánytól és a teljes rendszermentéstől." New parity case + `backups_remote_pinned`; the `backups_remote_*`, `escrow_first`, `escrow_receremony` and `settings_security_*` + fixtures regenerated on purpose (only the lines above changed). ## v0.337.0 (2026-10-21) — kept second-drive copies (D25: R-1175, R-1181, R-1179); a `.fab` import keeps the family gate (R-1191); a stale restore temp no longer blocks the retry (R-1188 c); the overcommit sentence made true (R-1192); escrow recorded before the floor (R-1176); a finished off-site abandonment closes on the hub's word (R-1182); storage labels follow the language (W26) diff --git a/REUSE.md b/REUSE.md index b7ea728..82c71fe 100644 --- a/REUSE.md +++ b/REUSE.md @@ -29,7 +29,7 @@ | `stacks.RunAfterInstall` / `expandAfterInstall` / `web.defaultLoginInEffect` (v0.279.0, decision 45) | controller/internal/stacks/after_install.go · controller/internal/web/known_login.go | `(name, wait)` / `(cmd, allowed, env)` / `(meta, cfg, installed)` | A fresh install replaces a known default login; the page says when a default is still in effect | **Only from the deploy-done hook** — never after a restore/kept load (R-694). A `success:` marker is required (exit 0 lies). Never log the expanded command | | `stacks.OpenSetupGate` / `SetupGateTick` / `SetupGateHost` · `web.ServeGateAuth` / `ServeGateStart` (v0.280.0, decision 46) | controller/internal/stacks/setup_gate.go · controller/internal/web/setup_gate.go | `(name, by)` / `()` / `(host)` · handlers | The setup gate: a `setup_gate: true` install is closed to everyone but the household until its probe or the household's press opens it | **Write the gate BEFORE the first start** (spike F2). Open = record first, then remove the file. Never widen the dashboard cookie — the handshake mints a host-bound one-use token | | `stacks.OpenSignupWindow` / `SignupBlocked` / `SetupGateProbe` · `web.ServeSignupClosed` (v0.281.0, decision 47) | controller/internal/stacks/signup_block.go · controller/internal/web/setup_gate.go | `(name)` | Sign-up closed at the app's own address once the gate opens; the household's 15-minute window; the press asks the probe | **The block goes up BEFORE the gate comes down** (a failed write keeps the gate closed). Never on an app this box did not gate | -| `family.Store` · `stacks.FamilyGateHost` / `familyGateTick` / `FamilyExceptRegexp` · `web.ServeFamilyGateAuth` / `ServeFamilyStart` / `ServeFamilyLogin` / `ServeFamilyLogout` (v0.287.0, decisions 63/64) | controller/internal/family/family.go · controller/internal/stacks/family_gate.go · controller/internal/web/family_gate.go | store `Add/Reset/Remove/Verify/NewSession/HouseholdSession/Valid/EndSession/Find/EndAll` + `family.Fingerprint` · `(host)` / `()` / `(prefix)` · handlers | The PERMANENT family gate: family members with their own logins in front of a `family_gate: true` app | **A family cookie never opens the dashboard** (RequireAuth reads only `felhom_session`); the app cookie carries a token DERIVED per app host from a STORE session (`familyAppToken`, R-976 — never the session id itself), so the store is asked on every request (reset/remove/logout end access at once); **every exception goes through `FamilyExceptRegexp`** (anchored — never a hand-written PathPrefix); door written before the first start, like the setup gate. `familyStoreOverride` is the test seam. Fifth atomic-write helper (family.json, fsync) — see §6. **R-1112: a session is stored as its FINGERPRINT (`Session.FP`, the R-35 shape), never the cookie value** — `NewSession` returns the value once; `Valid`/`EndSession` take the value and hash it; `Find` and `familyAppToken` work on the fingerprint. An old file (`"id"` = the value) is converted and rewritten in `Open`; a failed rewrite is an error. **R-1113: the household path uses `HouseholdSession`** (its live session, a new one only past half its life) — never `NewSession("")` per pass | +| `family.Store` · `stacks.FamilyGateHost` / `familyGateTick` / `FamilyExceptRegexp` · `web.ServeFamilyGateAuth` / `ServeFamilyStart` / `ServeFamilyLogin` / `ServeFamilyLogout` (v0.287.0, decisions 63/64) | controller/internal/family/family.go · controller/internal/stacks/family_gate.go · controller/internal/web/family_gate.go | store `Add/AddPerson/People/Reset/Remove/Verify/NewSession/HouseholdSession/Valid/EndSession/Find/EndAll` + `family.Fingerprint` + `family.LoginFrom` · `(host)` / `()` / `(prefix)` · handlers | The PERMANENT family gate: family members with their own logins in front of a `family_gate: true` app | **A family cookie never opens the dashboard** (RequireAuth reads only `felhom_session`); the app cookie carries a token DERIVED per app host from a STORE session (`familyAppToken`, R-976 — never the session id itself), so the store is asked on every request (reset/remove/logout end access at once); **every exception goes through `FamilyExceptRegexp`** (anchored — never a hand-written PathPrefix); door written before the first start, like the setup gate. `familyStoreOverride` is the test seam. Fifth atomic-write helper (family.json, fsync) — see §6. **R-1112: a session is stored as its FINGERPRINT (`Session.FP`, the R-35 shape), never the cookie value** — `NewSession` returns the value once; `Valid`/`EndSession` take the value and hash it; `Find` and `familyAppToken` work on the fingerprint. An old file (`"id"` = the value) is converted and rewritten in `Open`; a failed rewrite is an error. **R-1113: the household path uses `HouseholdSession`** (its live session, a new one only past half its life) — never `NewSession("")` per pass. **W12: a typed name goes through `AddPerson` (keeps the display, makes the login with `LoginFrom`, numbers a taken login); `Add` stays the strict login-only primitive. Any place that turns a typed member name into a login calls `LoginFrom` — never a second fold table** | | `stacks.OpenInstallHold` / `installHoldTick` (v0.284.0, R-741) | controller/internal/stacks/install_hold.go | `(name, by)` / `()` | An `after_install` app held behind the setup gate's door until its known login is replaced | **Written before the first start**, like the gate; opens on `after_install` success or the household's "I changed it"; the door (`SetupGateHost`) reads holds first | | `stacks.RetainImagesAfterUpdate` / `RetainImagesAfterRemove` / `RunImageRetentionOnce` (→ `(deleted, done)` since v0.294.0) / `RunImageRetentionOnceUntilDone` · seam `imageDocker` (v0.284.0, decision 53) | controller/internal/stacks/image_retention.go | `(name, previous)` / `(name, repos)` / `()` | Deletes an app's images older than its running + previous one | **The keep set is box-wide and read at delete time** (containers, installed composes, installed/previous records); exact id, never forced or pruned; skipped while any update runs AND while any image-pulling compose command runs (R-863, `dockerexec.TryImageCleanup`); the one-time pass writes its marker only after a pass that RAN; tests use the `imageDocker` seam, never Docker | | `dockerexec.BeginImageWork(args)` / `TryImageCleanup()` / `ImagePulling(args)` (v0.294.0, R-863) | controller/internal/dockerexec/imagework.go | `defer dockerexec.BeginImageWork(args)()` | **Every NEW place that runs `compose up/pull/create/run` must hold it** (the stacks compose helpers, appexport's restore and the FileBrowser sync already do) | An image pulled by compose is named by no container until compose creates one; a clean-up pass in that window deleted it (measured, R-863). The pass takes the lock exclusively without waiting; pulling commands wait for a running pass (seconds) | diff --git a/controller/README.md b/controller/README.md index a1d891d..cb5160e 100644 --- a/controller/README.md +++ b/controller/README.md @@ -2190,6 +2190,8 @@ that folder is never a dead end, and an install never runs into it silently (R-6 `family.json`, stored as their FINGERPRINT (sha256 of the cookie value, the dashboard's R-35 shape — v0.325.0, R-1112; an older file is converted at load and rewritten). The household's pass reuses its one live family session (a new one only when less than half of its life is left — R-1113). A family cookie never opens the dashboard. `min_controller:` refuses a template too new for the box. + A member's name is kept as the household typed it („Ödön"); the LOGIN is made from it — accents removed, lower case + (`family.LoginFrom`: `odon`), the next number when taken (`odon2`) — and the card shows both (W12, unreleased). Code: `internal/family/`, `internal/stacks/family_gate.go`, `internal/web/family_gate.go`. - **A removed app restored from its backup gets its lock back (v0.286.0, R-773)** — with no app.yaml left, the restore (`PersistUnitRedeployConfig`) writes the lock record (`opened_by: restore`) and the block BEFORE anything starts; the diff --git a/controller/internal/family/family.go b/controller/internal/family/family.go index 1e4cd7a..2e779d8 100644 --- a/controller/internal/family/family.go +++ b/controller/internal/family/family.go @@ -28,8 +28,12 @@ import ( "path/filepath" "regexp" "sort" + "strconv" + "strings" "sync" "time" + "unicode" + "unicode/utf8" "golang.org/x/crypto/bcrypt" ) @@ -39,7 +43,8 @@ const SessionLife = 30 * 24 * time.Hour // Member is one family member. Gen moves on at every password reset, ending the member's earlier sessions. type Member struct { - Name string `json:"name"` + Name string `json:"name"` // the LOGIN: 1-32 of [a-z0-9._-] (nameRE); the key everywhere + Display string `json:"display,omitempty"` // the name as the household typed it (W12); "" for a member made before Hash string `json:"hash"` Gen int `json:"gen"` Created string `json:"created"` @@ -231,6 +236,139 @@ func (s *Store) Add(name string) (string, error) { return pw, nil } +// MaxDisplayRunes is the longest name the household may type for a family member (W12). +const MaxDisplayRunes = 64 + +// Person is one member as the dashboard's card shows them: the name the household typed and the login made from it. +type Person struct { + Login string `json:"login"` + Display string `json:"display"` +} + +// AddPerson makes a member from the name the household typed (W12, 2026-10-21): the display name is kept exactly as +// typed; the LOGIN is made from it by LoginFrom (accents removed, lower case) and must still pass nameRE — the login's +// own check is unchanged. When the login is taken by someone with another name, the next free numbered login is used +// („Ödön" → odon, then „Odon" → odon2). The same name again (letter case aside) is ErrExists, never a second login. +// Returns the login and the generated password (once). Pinned by TestW12_AccentedNamesGetAPlainLogin. +func (s *Store) AddPerson(display string) (login, pw string, err error) { + display = strings.TrimSpace(display) + base := LoginFrom(display) + if display == "" || utf8.RuneCountInString(display) > MaxDisplayRunes || !nameRE.MatchString(base) { + return "", "", ErrBadName + } + for _, r := range display { + if unicode.IsControl(r) { + return "", "", ErrBadName + } + } + pw = newPassword() + h, err := bcrypt.GenerateFromPassword([]byte(pw), s.cost) + if err != nil { + return "", "", err + } + s.mu.Lock() + defer s.mu.Unlock() + for _, m := range s.members { + if strings.EqualFold(m.displayName(), display) { + return "", "", ErrExists + } + } + login = base + for n := 2; s.members[login] != nil; n++ { + suffix := strconv.Itoa(n) + stem := base + if len(stem)+len(suffix) > 32 { + stem = stem[:32-len(suffix)] + } + login = stem + suffix + } + if !nameRE.MatchString(login) { + return "", "", ErrBadName + } + stored := display + if stored == login { + stored = "" // nothing to remember: the login IS the name + } + s.members[login] = &Member{Name: login, Display: stored, Hash: string(h), Gen: 1, Created: s.now().UTC().Format(time.RFC3339)} + if err := s.saveLocked(); err != nil { + delete(s.members, login) + return "", "", err + } + return login, pw, nil +} + +func (m *Member) displayName() string { + if m.Display != "" { + return m.Display + } + return m.Name +} + +// foldMap removes the accents of the letters a Hungarian (and a European) household types; sharp s becomes "ss". +// The letters are written as escapes: several of them are the mojibake gate's cp1250 signatures. +var foldMap = func() map[rune]string { + out := map[rune]string{'\u00df': "ss", '\u00e6': "ae", '\u0153': "oe", '\u00f8': "o", '\u0142': "l", '\u0111': "d", '\u00f0': "d", '\u00fe': "th"} + for base, letters := range map[string]string{ + "a": "\u00e1\u00e0\u00e2\u00e4\u00e3\u00e5\u0101\u0103\u0105", "c": "\u00e7\u0107\u010d\u0109\u010b", "d": "\u010f", "e": "\u00e9\u00e8\u00ea\u00eb\u0113\u0117\u0119\u011b\u0115", "g": "\u011f\u011d\u0121\u0123", "h": "\u0125\u0127", "i": "\u00ed\u00ec\u00ee\u00ef\u012b\u012f\u0129\u0131", + "j": "\u0135", "k": "\u0137", "l": "\u013a\u013c\u013e\u0140", "n": "\u00f1\u0144\u0148\u0146", "o": "\u00f3\u00f2\u00f4\u00f6\u00f5\u0151\u014d\u014f", "r": "\u0155\u0159\u0157", "s": "\u015b\u0161\u015f\u015d\u0219", "t": "\u0165\u0163\u021b", + "u": "\u00fa\u00f9\u00fb\u00fc\u0171\u016b\u016f\u0173\u0169\u016d", "y": "\u00fd\u00ff\u0177", "z": "\u017a\u017c\u017e", + } { + for _, r := range letters { + out[r] = base + } + } + return out +}() + +// LoginFrom makes a login from a typed name (W12): lower case, accents removed („Ödön" → odon, „Írisz" → irisz), +// a run of spaces becomes one dot („Kis Anna" → kis.anna), and every other sign outside a-z 0-9 . _ - is left out. +// Leading signs are dropped, and the result is cut to 32 characters. The caller still checks it with nameRE. +func LoginFrom(display string) string { + var b strings.Builder + space := false + for _, r := range strings.ToLower(strings.TrimSpace(display)) { + if unicode.IsSpace(r) { + space = true + continue + } + var part string + switch { + case r >= 'a' && r <= 'z', r >= '0' && r <= '9', r == '.', r == '_', r == '-': + part = string(r) + default: + part = foldMap[r] + } + if part == "" { + continue + } + if space && b.Len() > 0 { + b.WriteByte('.') + } + space = false + b.WriteString(part) + } + out := strings.TrimLeft(b.String(), "._-") + if len(out) > 32 { + out = out[:32] + } + return out +} + +// People lists the members with their typed names, sorted by login. +func (s *Store) People() []Person { + if s == nil { + return nil + } + s.mu.Lock() + defer s.mu.Unlock() + out := make([]Person, 0, len(s.members)) + for _, m := range s.members { + out = append(out, Person{Login: m.Name, Display: m.displayName()}) + } + sort.Slice(out, func(i, j int) bool { return out[i].Login < out[j].Login }) + return out +} + // Reset gives a member a new generated password (returned once) and ends every earlier session of theirs. func (s *Store) Reset(name string) (string, error) { pw := newPassword() diff --git a/controller/internal/family/w12_test.go b/controller/internal/family/w12_test.go new file mode 100644 index 0000000..df2cd2d --- /dev/null +++ b/controller/internal/family/w12_test.go @@ -0,0 +1,76 @@ +package family + +import ( + "strings" + "testing" +) + +// W12 (rehearsal 2026-10-20): a Hungarian family's own names („Ödön", „Írisz") were refused. The name is now kept as +// typed and the LOGIN is made from it — accents removed, lower case — and still passes the login's own check. +func TestW12_AccentedNamesGetAPlainLogin(t *testing.T) { + for in, want := range map[string]string{ + "Ödön": "odon", "Írisz": "irisz", "ŐRSÉG": "orseg", "Kis Anna": "kis.anna", "Anna-Mária": "anna-maria", + "anna": "anna", "odon2": "odon2", "O'Brien": "obrien", " Ünő ": "uno", "Анна": "", "!!!": "", ".x": "x", + } { + if got := LoginFrom(in); got != want { + t.Errorf("LoginFrom(%q) = %q, want %q", in, got, want) + } + } + if got := LoginFrom(strings.Repeat("é", 40)); got != strings.Repeat("e", 32) { + t.Errorf("a long name is not cut to 32: %q", got) + } + + s, dir := testStore(t) + login, pw, err := s.AddPerson("Ödön") + if err != nil || login != "odon" || !s.Verify("odon", pw) { + t.Fatalf("Ödön: %q %v", login, err) + } + if login, _, err := s.AddPerson("Írisz"); err != nil || login != "irisz" { + t.Fatalf("Írisz: %q %v", login, err) + } + // a second person whose name makes the same login gets the next number + login2, pw2, err := s.AddPerson("Odon") + if err != nil || login2 != "odon2" || !s.Verify("odon2", pw2) || s.Verify("odon", pw2) { + t.Fatalf("the second odon: %q %v", login2, err) + } + // the same name again is the same person: refused, never a third login + if _, _, err := s.AddPerson("ödön"); err != ErrExists { + t.Fatalf("the same name twice: %v", err) + } + for _, bad := range []string{"", " ", "Анна", "!!!", strings.Repeat("a", MaxDisplayRunes+1), "an\x00na"} { + if _, _, err := s.AddPerson(bad); err != ErrBadName { + t.Fatalf("name %q accepted: %v", bad, err) + } + } + // the display name is kept exactly as typed, and survives a reopen + s2, err := Open(dir) + if err != nil { + t.Fatal(err) + } + got := map[string]string{} + for _, p := range s2.People() { + got[p.Login] = p.Display + } + if got["odon"] != "Ödön" || got["irisz"] != "Írisz" || got["odon2"] != "Odon" || len(got) != 3 { + t.Fatalf("people: %v", got) + } + // the login's own check is unchanged + for _, bad := range []string{"Ödön", "a b", "Anna"} { + if _, err := s.Add(bad); err != ErrBadName { + t.Fatalf("Add(%q) accepted a login outside the safe set", bad) + } + } +} + +// A long name whose login is taken: the numbered login still fits 32 characters. +func TestW12_NumberedLoginStaysInsideTheLimit(t *testing.T) { + s, _ := testStore(t) + long := strings.Repeat("a", 32) + if l, _, err := s.AddPerson(long); err != nil || l != long { + t.Fatalf("%q %v", l, err) + } + l, _, err := s.AddPerson(strings.Repeat("á", 32)) + if err != nil || l != strings.Repeat("a", 31)+"2" { + t.Fatalf("%q %v", l, err) + } +} diff --git a/controller/internal/i18n/locales/en.json b/controller/internal/i18n/locales/en.json index 43f0f82..68cf533 100644 --- a/controller/internal/i18n/locales/en.json +++ b/controller/internal/i18n/locales/en.json @@ -350,13 +350,15 @@ "backups_apps.visszaallitas_a_mentesbol": "Restore from the backup", "backups_apps.visszaallitja_a_hianyzo_fajlokat_a": "Restore the missing files from the secondary copy? Existing files are NOT changed and NOT deleted. The app stops during the operation. Last copy: {{fmtTimeStr .Tier2LastRun}}", "backups_apps.visszaallitja_legutobbi_sikeres_masolat": "Restore the missing files from the secondary copy? Existing files are NOT changed and NOT deleted. The app stops during the operation. Last successful copy: {{fmtTimeStr .Tier2LastSuccess}}.", - "backups_escrow.1_elofeltetelek_ellenorzese": "1. Checking the requirements", - "backups_escrow.2_fontos_tudnivalok": "2. Important to know", - "backups_escrow.3_megerosites": "3. Confirmation", - "backups_escrow.4_letrehozas": "4. Creation", - "backups_escrow.5_a_helyreallitasi_kod": "5. The recovery code", - "backups_escrow.6_ellenorzes": "6. Check", - "backups_escrow.7_befejezes": "7. Finish", + "backups_escrow.1_elofeltetelek_ellenorzese": "Checking the requirements", + "backups_escrow.all_prereqs_ok": "All prerequisites are in place.", + "backups_escrow.prereq_details": "Details", + "backups_escrow.2_fontos_tudnivalok": "Important to know", + "backups_escrow.3_megerosites": "Confirmation", + "backups_escrow.4_letrehozas": "Creation", + "backups_escrow.5_a_helyreallitasi_kod": "The recovery code", + "backups_escrow.6_ellenorzes": "Check", + "backups_escrow.7_befejezes": "Finish", "backups_escrow.a_dr_szint_nincs_alkalmazva": "the DR level is not applied on this machine", "backups_escrow.a_folyamat_a_piros_feltetelek": "The process cannot start until the red requirements are met.", "backups_escrow.a_folyamat_inditasa_nem_sikerult": "Starting the process failed.", @@ -412,7 +414,7 @@ "backups_remote.a_funkciohoz_az_ugynok_frissitese": "This needs an agent update — the update arrives automatically.", "backups_remote.a_helyreallitasi_csomag_elkuldve_a": "The recovery package was sent to the central system and is waiting for confirmation — usually a few seconds, at most 15 minutes.", "backups_remote.a_helyreallitasi_kod_letetbe_helyezve": "The recovery code is in escrow.", - "backups_remote.a_host_kulcs_rogzitese_no": "Pins the host key (no blind TOFU). To look it up:", + "backups_remote.a_host_kulcs_rogzitese_no": "This is the key of the target machine. With it, the box connects only to this machine. To look it up:", "backups_remote.a_keresed_szerint_a_korabbi": "As you asked, we will permanently delete your earlier remote backups on {{.AbandonDate}}\n ({{.AbandonDaysLeft}} {{if eq .AbandonDaysLeft 1}}day{{else}}days{{end}} left).", "backups_remote.a_korabbi_hazon_kivuli_menteseid": "Your earlier off-site backups can be retrieved.\n This machine was reinstalled, and the Felhom central system keeps a sealed package for it. With your\n recovery code you can unlock your earlier backups and see what is in them.", "backups_remote.a_korabbi_menteseid_felre_vannak": "Your earlier backups are set aside — we did not delete them.", @@ -435,7 +437,10 @@ "backups_remote.addig_meggondolhatod_magad_ha_megvan": "Until then you can change your mind: if you have your recovery code,\n your backups can be retrieved, and the deletion is cancelled.", "backups_remote.aktiv_nincs_kijelolt_alkalmazas": "Active — no app selected", "backups_remote.amikor_uj_mentesi_kulcsot_kapott": "When your machine got a new backup key, we moved the older history\n on the remote storage, and it stayed there. It cannot be opened from here for now, and\n that does not depend on your code. If you need it, contact Felhom support.", - "backups_remote.az_alkalmazas_mentesek_titkositott_masol": "An encrypted copy of the app backups on a remote store — your own NAS or Felhom offsite storage — over restic + SFTP. The store sees only encrypted data. This is the „1 off-site\" leg of the 3-2-1 rule — independent of the local second copy and the full system backup.", + "backups_remote.az_alkalmazas_mentesek_titkositott_masol": "An encrypted copy of your app backups goes to a remote place: the Felhom storage or your own NAS. Only encrypted data is there. This copy is independent of the local second copy and of the full system backup.", + "backups_remote.felhom_store_name": "Felhom remote storage (Germany)", + "backups_remote.felhom_store_where": "The encrypted copy goes here.", + "backups_remote.halado": "Advanced", "backups_remote.biztonsagi_mentes_tavoli_mentes": "Backup — Remote backup", "backups_remote.cel_cime_ip_vagy_hosztnev": "Target address (IP or hostname — NAS or SFTP server)", "backups_remote.ehhez_a_gephez_egy_korabbi": "This machine has an earlier recovery code, and your earlier backups still exist.\n Creating a new code would make your old backups unreachable, so it cannot start now.\n First enter your existing code — or tell us there if you do not want the earlier data back.", @@ -2729,13 +2734,15 @@ "family_gate.msg.signed_in": "You are signed in. Open the app again.", "family_gate.msg.signed_out": "You are signed out. The family apps ask you to sign in again.", "family_gate.msg.store_unreadable": "The family list cannot be read right now.", - "family_gate.msg.bad_name": "A name is 1–32 characters: lower-case letters, digits, dot, dash or underscore.", + "family_gate.msg.bad_name": "We cannot make a login from this name. Type a name of at most 64 letters in the Latin alphabet. Accents are fine.", "family_gate.msg.exists": "A family member with that name already exists.", "family_gate.msg.no_member": "No family member with that name.", "family_gate.card_title": "Family", "family_gate.card_desc": "Family members sign in to the family-gated apps with their own name and password. It does not open this dashboard.", "family_gate.add": "Add a family member", - "family_gate.name_placeholder": "e.g. anna", + "family_gate.name_placeholder": "e.g. Anna", + "family_gate.name_hint": "Type the name as you like it, accents too. The login is made from it: no accents, lower case. You see it in the list.", + "family_gate.login_label": "login:", "family_gate.reset": "New password", "family_gate.remove": "Remove", "family_gate.none": "No family members yet.", diff --git a/controller/internal/i18n/locales/hu.json b/controller/internal/i18n/locales/hu.json index 71429e9..ee8b3b7 100644 --- a/controller/internal/i18n/locales/hu.json +++ b/controller/internal/i18n/locales/hu.json @@ -346,13 +346,15 @@ "backups_apps.visszaallitas_a_mentesbol": "Visszaállítás a mentésből", "backups_apps.visszaallitja_a_hianyzo_fajlokat_a": "Visszaállítod a hiányzó fájlokat a másodlagos másolatból? A meglévő fájlok NEM módosulnak és NEM törlődnek. Az alkalmazás a művelet idejére leáll. Legutóbbi másolat: {{fmtTimeStr .Tier2LastRun}}", "backups_apps.visszaallitja_legutobbi_sikeres_masolat": "Visszaállítod a hiányzó fájlokat a másodlagos másolatból? A meglévő fájlok NEM módosulnak és NEM törlődnek. Az alkalmazás a művelet idejére leáll. Legutóbbi sikeres másolat: {{fmtTimeStr .Tier2LastSuccess}}.", - "backups_escrow.1_elofeltetelek_ellenorzese": "1. Előfeltételek ellenőrzése", - "backups_escrow.2_fontos_tudnivalok": "2. Fontos tudnivalók", - "backups_escrow.3_megerosites": "3. Megerősítés", - "backups_escrow.4_letrehozas": "4. Létrehozás", - "backups_escrow.5_a_helyreallitasi_kod": "5. A helyreállítási kód", - "backups_escrow.6_ellenorzes": "6. Ellenőrzés", - "backups_escrow.7_befejezes": "7. Befejezés", + "backups_escrow.1_elofeltetelek_ellenorzese": "Előfeltételek ellenőrzése", + "backups_escrow.all_prereqs_ok": "Minden előfeltétel rendben.", + "backups_escrow.prereq_details": "Részletek", + "backups_escrow.2_fontos_tudnivalok": "Fontos tudnivalók", + "backups_escrow.3_megerosites": "Megerősítés", + "backups_escrow.4_letrehozas": "Létrehozás", + "backups_escrow.5_a_helyreallitasi_kod": "A helyreállítási kód", + "backups_escrow.6_ellenorzes": "Ellenőrzés", + "backups_escrow.7_befejezes": "Befejezés", "backups_escrow.a_dr_szint_nincs_alkalmazva": "a DR-szint nincs alkalmazva ezen a gépen", "backups_escrow.a_folyamat_a_piros_feltetelek": "A folyamat a piros feltételek teljesüléséig nem indítható.", "backups_escrow.a_folyamat_inditasa_nem_sikerult": "A folyamat indítása nem sikerült.", @@ -408,7 +410,7 @@ "backups_remote.a_funkciohoz_az_ugynok_frissitese": "A funkcióhoz az ügynök frissítése szükséges — a frissítés automatikusan megérkezik.", "backups_remote.a_helyreallitasi_csomag_elkuldve_a": "A helyreállítási csomag elküldve a központba, megerősítésre vár — ez általában néhány másodperc, legfeljebb 15 perc.", "backups_remote.a_helyreallitasi_kod_letetbe_helyezve": "A helyreállítási kód letétbe helyezve.", - "backups_remote.a_host_kulcs_rogzitese_no": "A host-kulcs rögzítése (no blind TOFU). Lekérdezhető:", + "backups_remote.a_host_kulcs_rogzitese_no": "A célgép saját kulcsa. Ezzel a doboz csak ehhez a géphez kapcsolódik. Így kérdezheted le:", "backups_remote.a_keresed_szerint_a_korabbi": "A kérésed szerint a korábbi távoli mentéseidet {{.AbandonDate}} napján véglegesen töröljük\n (még {{.AbandonDaysLeft}} nap).", "backups_remote.a_korabbi_hazon_kivuli_menteseid": "A korábbi, házon kívüli mentéseid visszaszerezhetők.\n Ezt a gépet újratelepítették, és a Felhom központi rendszere őriz hozzá egy lezárt csomagot. A\n helyreállítási kódoddal feloldhatod a korábbi mentéseidet, és megnézheted, mi van bennük.", "backups_remote.a_korabbi_menteseid_felre_vannak": "A korábbi mentéseid félre vannak téve — nem töröltük őket.", @@ -431,7 +433,10 @@ "backups_remote.addig_meggondolhatod_magad_ha_megvan": "Addig meggondolhatod magad: ha megvan a helyreállítási kódod,\n a mentéseid visszaszerezhetők, és a törlés elmarad.", "backups_remote.aktiv_nincs_kijelolt_alkalmazas": "Aktív — nincs kijelölt alkalmazás", "backups_remote.amikor_uj_mentesi_kulcsot_kapott": "Amikor új mentési kulcsot kapott a géped, a régebbi előzményt átmozgattuk\n a távoli tárhelyen, és ott is maradt. Megnyitni innen egyelőre nem lehet, és\n ez nem a kódodon múlik. Ha szükséged van rá, keresd a Felhom ügyfélszolgálatát.", - "backups_remote.az_alkalmazas_mentesek_titkositott_masol": "Az alkalmazás-mentések titkosított másolata egy távoli tárolóra — saját NAS vagy Felhom offsite tárhely — restic + SFTP kapcsolaton. A tároló csak titkosított adatot lát. Ez a 3-2-1 szabály „1 off-site\" lába — független a helyi másodpéldánytól és a teljes rendszermentéstől.", + "backups_remote.az_alkalmazas_mentesek_titkositott_masol": "Az alkalmazásaid mentéseiről titkosított másolat készül egy távoli helyre: a Felhom tárhelyére vagy a saját NAS-odra. Ott csak titkosított adat van. Ez a másolat független a helyi másodpéldánytól és a teljes rendszermentéstől.", + "backups_remote.felhom_store_name": "Felhom távoli tárhely (Németország)", + "backups_remote.felhom_store_where": "Ide kerül a titkosított másolat.", + "backups_remote.halado": "Haladó", "backups_remote.biztonsagi_mentes_tavoli_mentes": "Biztonsági mentés — Távoli mentés", "backups_remote.cel_cime_ip_vagy_hosztnev": "Cél címe (IP vagy hosztnév — NAS vagy SFTP-kiszolgáló)", "backups_remote.ehhez_a_gephez_egy_korabbi": "Ehhez a géphez egy korábbi helyreállítási kód tartozik, és a korábbi mentéseid még megvannak.\n Új kód létrehozása a régi mentéseidet elérhetetlenné tenné, ezért most nem indítható.\n Előbb add meg a meglévő kódodat — vagy ott jelezheted, ha nem kéred vissza a korábbi adatokat.", @@ -2717,13 +2722,15 @@ "family_gate.msg.signed_in": "Beléptél. Nyisd meg újra az alkalmazást.", "family_gate.msg.signed_out": "Kiléptél. A családi alkalmazások újra belépést kérnek.", "family_gate.msg.store_unreadable": "A családi lista most nem olvasható.", - "family_gate.msg.bad_name": "A név 1–32 karakter: kisbetű, szám, pont, kötőjel vagy aláhúzás.", + "family_gate.msg.bad_name": "Ebből a névből nem tudunk belépési nevet készíteni. Írj be egy legfeljebb 64 betűs nevet latin betűkkel. Az ékezet jó.", "family_gate.msg.exists": "Már van ilyen nevű családtag.", "family_gate.msg.no_member": "Nincs ilyen nevű családtag.", "family_gate.card_title": "Család", "family_gate.card_desc": "A családtagok a saját nevükkel és jelszavukkal lépnek be a családi kapus alkalmazásokba. Ezzel a vezérlőpultot nem érik el.", "family_gate.add": "Családtag hozzáadása", - "family_gate.name_placeholder": "pl. anna", + "family_gate.name_placeholder": "pl. Ödön", + "family_gate.name_hint": "Írd be a nevet úgy, ahogy szeretnéd, ékezettel is. A belépési név ebből készül: ékezet nélkül, kisbetűvel. A listában látod.", + "family_gate.login_label": "belépési név:", "family_gate.reset": "Új jelszó", "family_gate.remove": "Eltávolítás", "family_gate.none": "Még nincs családtag.", diff --git a/controller/internal/web/dashboard_layout_test.go b/controller/internal/web/dashboard_layout_test.go index 866464a..607f53e 100644 --- a/controller/internal/web/dashboard_layout_test.go +++ b/controller/internal/web/dashboard_layout_test.go @@ -146,3 +146,24 @@ func TestR907_PageHeaderWrapsOnAPhone(t *testing.T) { } t.Fatal("no launcher_full parity case") } + +// W9 (rehearsal 2026-10-20): the recovery-code banner's link sits INSIDE the sentence („Helyreállítási kód +// létrehozása →") and rendered browser-default blue on the amber bar. A link in a banner sentence takes the banner's +// colour and stays underlined. +func TestW9_BannerSentenceLinkTakesTheBannerColour(t *testing.T) { + data, err := StyleCSS() + if err != nil { + t.Fatal(err) + } + r := cssRule(t, string(data), ".alert-message a") + if !strings.Contains(r, "color: inherit") || !strings.Contains(r, "text-decoration: underline") { + t.Errorf(".alert-message a must take the banner's colour and stay underlined: %s", r) + } + // the banner that carries such a link still has it inside its message + s := i18nTestServer(t) + for _, lang := range []string{"hu", "en"} { + if !strings.Contains(s.i18n.Msg(lang, "layout.a_tavoli_mentes_szunetel_amig"), ``) { + t.Fatalf("%s: the escrow banner no longer links inside its sentence — revisit this rule", lang) + } + } +} diff --git a/controller/internal/web/family_gate.go b/controller/internal/web/family_gate.go index e9c6d17..eb7d3df 100644 --- a/controller/internal/web/family_gate.go +++ b/controller/internal/web/family_gate.go @@ -391,7 +391,9 @@ func (s *Server) ServeFamilyLogin(w http.ResponseWriter, r *http.Request) { s.renderFamilyPage(w, r, rd, s.msg(r, "family_gate.msg.form_expired"), http.StatusForbidden) return } - name := strings.ToLower(strings.TrimSpace(r.FormValue("name"))) + // W12: a member may type their name as the household wrote it („Ödön"); the login is made the same way the card + // made it. A login typed as shown („odon2") is unchanged by LoginFrom. + name := family.LoginFrom(r.FormValue("name")) visitor := rateKey(r) // R-983 (v0.324.0): the try is counted BEFORE the password is checked, in the same step as the lock check, so // parallel tries cannot all pass a check none of them has counted yet. A right password clears both counts. @@ -484,7 +486,7 @@ func (s *Server) familyMembersHandler(w http.ResponseWriter, r *http.Request) { escrowJSON(w, http.StatusServiceUnavailable, nil, s.msg(r, "family_gate.msg.store_unreadable")) return } - escrowJSON(w, http.StatusOK, map[string]any{"members": st.Names()}, "") + escrowJSON(w, http.StatusOK, map[string]any{"members": st.Names(), "people": st.People()}, "") } // familyMemberActionHandler: POST /family/members/{add,reset,remove} with `name`. add/reset answer the new password @@ -496,13 +498,19 @@ func (s *Server) familyMemberActionHandler(w http.ResponseWriter, r *http.Reques return } _ = r.ParseForm() + // W12 (2026-10-21): „add" takes the name as the household typed it („Ödön") and answers the login made from it + // (odon); reset and remove name the LOGIN, which the card holds. name := strings.ToLower(strings.TrimSpace(r.FormValue("name"))) w.Header().Set("Cache-Control", "no-store") var pw string var err error switch action { case "add": - pw, err = st.Add(name) + var login string + login, pw, err = st.AddPerson(r.FormValue("name")) + if err == nil { + name = login + } case "reset": pw, err = st.Reset(name) case "remove": @@ -527,7 +535,7 @@ func (s *Server) familyMemberActionHandler(w http.ResponseWriter, r *http.Reques return } s.logger.Printf("[INFO] [web] family: the household's %s of %q from %s (password never logged)", action, name, clientIP(r)) - out := map[string]any{"name": name, "members": st.Names()} + out := map[string]any{"name": name, "members": st.Names(), "people": st.People()} if pw != "" { out["password"] = pw } diff --git a/controller/internal/web/family_gate_test.go b/controller/internal/web/family_gate_test.go index b601067..cce85cb 100644 --- a/controller/internal/web/family_gate_test.go +++ b/controller/internal/web/family_gate_test.go @@ -369,7 +369,7 @@ func TestFamilyGate_CardActsAndNoPasswordInThePage(t *testing.T) { if w := act("add", "anna"); w.Code != http.StatusConflict { t.Fatalf("duplicate: %d", w.Code) } - if w := act("add", "Not Valid"); w.Code != http.StatusBadRequest { + if w := act("add", "!!!"); w.Code != http.StatusBadRequest { // W12: „Not Valid" is a name now (login not.valid) t.Fatalf("bad name: %d", w.Code) } if w := act("remove", "anna"); w.Code != http.StatusOK || strings.Contains(w.Body.String(), "password") { @@ -379,3 +379,37 @@ func TestFamilyGate_CardActsAndNoPasswordInThePage(t *testing.T) { t.Fatalf("reset of a removed member: %d", w.Code) } } + +// W12 (rehearsal 2026-10-20): the card takes „Ödön" as typed, answers the login made from it (odon) and lists both; +// a second name that makes the same login gets odon2; the member may sign in typing „Ödön" or „odon". +func TestW12_CardTakesAccentedNamesAndShowsTheLogin(t *testing.T) { + s, st := familyHarness(t) + add := func(name string) *httptest.ResponseRecorder { + r := httptest.NewRequest(http.MethodPost, "/family/members/add", strings.NewReader(url.Values{"name": {name}}.Encode())) + r.Header.Set("Content-Type", "application/x-www-form-urlencoded") + w := httptest.NewRecorder() + s.familyMemberActionHandler(w, r, "add") + return w + } + w := add("Ödön") + body := w.Body.String() + m := regexp.MustCompile(`"password":"([a-z0-9-]{19})"`).FindStringSubmatch(body) + if w.Code != http.StatusOK || m == nil || !strings.Contains(body, `"name":"odon"`) || + !strings.Contains(body, `{"login":"odon","display":"Ödön"}`) || !st.Verify("odon", m[1]) { + t.Fatalf("add Ödön: %d %s", w.Code, body) + } + if w := add("Odon"); w.Code != http.StatusOK || !strings.Contains(w.Body.String(), `"name":"odon2"`) { + t.Fatalf("the second odon: %d %s", w.Code, w.Body.String()) + } + for _, typed := range []string{"Ödön", "odon", " ÖDÖN "} { + if w := famLogin(s, "203.0.113.20", typed, m[1], ""); famCookie(w, familySessionCookie) == nil { + t.Fatalf("sign-in typing %q: %d", typed, w.Code) + } + } + r := httptest.NewRequest(http.MethodGet, "/family/members", nil) + lw := httptest.NewRecorder() + s.familyMembersHandler(lw, r) + if !strings.Contains(lw.Body.String(), `{"login":"odon2","display":"Odon"}`) { + t.Fatalf("the list: %s", lw.Body.String()) + } +} diff --git a/controller/internal/web/i18n_cases_b_test.go b/controller/internal/web/i18n_cases_b_test.go index 6af6703..c35c346 100644 --- a/controller/internal/web/i18n_cases_b_test.go +++ b/controller/internal/web/i18n_cases_b_test.go @@ -193,6 +193,13 @@ func i18nCasesB() []i18nCase { {"backups_remote_escrowed", "backups_remote", func() map[string]interface{} { return i18nRemoteData(func(d, o m) { d["EscrowAgentOK"] = true }) }}, + // W11: the Felhom-provided store shows its name; the provider login waits behind „Haladó". + {"backups_remote_pinned", "backups_remote", func() map[string]interface{} { + return i18nRemoteData(func(d, o m) { + d["EscrowAgentOK"] = true + o["Pinned"], o["User"], o["Host"], o["RepoPath"] = true, "u1-sub5", "u1-sub5.your-storagebox.de", "/home/felhom-repo" + }) + }}, {"backups_remote_notconf_hub", "backups_remote", func() map[string]interface{} { d := i18nLayoutData("backups-remote", "Távoli mentés beállítása") d["Backup"] = i18nBackupStatus(false, false) diff --git a/controller/internal/web/templates/backups_escrow.html b/controller/internal/web/templates/backups_escrow.html index cc63752..dc26aec 100644 --- a/controller/internal/web/templates/backups_escrow.html +++ b/controller/internal/web/templates/backups_escrow.html @@ -76,7 +76,8 @@ - + + {{if .Offbox.Pinned}} +
+ {{T "backups_remote.halado"}} + {{.Offbox.User}}@{{.Offbox.Host}} {{.Offbox.RepoPath}} +
+ {{end}} {{if and .Offbox.Enabled (gt .Offbox.QuotaGB 0)}} @@ -261,12 +270,17 @@
+ {{/* W24 (rehearsal 2026-10-20): the key and host-key fields are operator detail; they sit behind + „Haladó". A closed
still submits its fields. */}} +
+ {{T "backups_remote.halado"}}
{{T "backups_remote.a_kulcsot_0600_as_fajlba"}}
{{T "backups_remote.a_host_kulcs_rogzitese_no"}} ssh-keyscan -p <port> <host>
+
diff --git a/controller/internal/web/templates/settings_security.html b/controller/internal/web/templates/settings_security.html index 9a6d050..a41b155 100644 --- a/controller/internal/web/templates/settings_security.html +++ b/controller/internal/web/templates/settings_security.html @@ -411,25 +411,31 @@ function openDialog(opts){
- +
+

{{T "family_gate.name_hint"}}

diff --git a/controller/internal/web/templates/style.css b/controller/internal/web/templates/style.css index c937033..9528931 100644 --- a/controller/internal/web/templates/style.css +++ b/controller/internal/web/templates/style.css @@ -904,6 +904,10 @@ select.form-control option { background: var(--bg-2); color: var(--text-1); } /* The link takes the banner's own colour (it had none and rendered browser-default blue, 2026-10-08). */ .alert-link { margin-left: auto; white-space: nowrap; color: inherit; text-decoration: none; } .alert-link:hover { text-decoration: underline; } +/* W9 (rehearsal 2026-10-20): a link INSIDE a banner's sentence („Helyreállítási kód létrehozása →") rendered + browser-default blue on the amber bar. It takes the banner's colour too, underlined so it still reads as a link. */ +.alert-message a { color: inherit; text-decoration: underline; text-underline-offset: 2px; } +.alert-message a:hover { text-decoration-thickness: 2px; } /* Inline storage warnings (under storage bars on dashboard and monitoring) */ .inline-warnings { margin-top: 0.75rem; } diff --git a/controller/internal/web/testdata/i18n_parity/backups_remote_abandon_overdue.html b/controller/internal/web/testdata/i18n_parity/backups_remote_abandon_overdue.html index 8629814..fb7d265 100644 --- a/controller/internal/web/testdata/i18n_parity/backups_remote_abandon_overdue.html +++ b/controller/internal/web/testdata/i18n_parity/backups_remote_abandon_overdue.html @@ -191,7 +191,7 @@

Távoli mentés (3. mentés) — titkosított, offsite

-

Az alkalmazás-mentések titkosított másolata egy távoli tárolóra — saját NAS vagy Felhom offsite tárhely — restic + SFTP kapcsolaton. A tároló csak titkosított adatot lát. Ez a 3-2-1 szabály „1 off-site" lába — független a helyi másodpéldánytól és a teljes rendszermentéstől.

+

Az alkalmazásaid mentéseiről titkosított másolat készül egy távoli helyre: a Felhom tárhelyére vagy a saját NAS-odra. Ott csak titkosított adat van. Ez a másolat független a helyi másodpéldánytól és a teljes rendszermentéstől.

@@ -203,6 +203,7 @@
5 GB
Tároló méret · 10 pillanatkép
+
felhom@nas.example
/srv/repo
@@ -211,6 +212,7 @@ +
Tárhelykeret: 5 GB / 100 GB (5%)
@@ -272,12 +274,16 @@
+ +
+ Haladó
A kulcsot 0600-as fájlba írjuk; sosem naplózzuk és nem tároljuk a beállításokban.
- A host-kulcs rögzítése (no blind TOFU). Lekérdezhető: ssh-keyscan -p <port> <host>
+ A célgép saját kulcsa. Ezzel a doboz csak ehhez a géphez kapcsolódik. Így kérdezheted le: ssh-keyscan -p <port> <host>
+ diff --git a/controller/internal/web/testdata/i18n_parity/backups_remote_error.html b/controller/internal/web/testdata/i18n_parity/backups_remote_error.html index 8636bf9..06251b7 100644 --- a/controller/internal/web/testdata/i18n_parity/backups_remote_error.html +++ b/controller/internal/web/testdata/i18n_parity/backups_remote_error.html @@ -191,7 +191,7 @@

Távoli mentés (3. mentés) — titkosított, offsite

-

Az alkalmazás-mentések titkosított másolata egy távoli tárolóra — saját NAS vagy Felhom offsite tárhely — restic + SFTP kapcsolaton. A tároló csak titkosított adatot lát. Ez a 3-2-1 szabály „1 off-site" lába — független a helyi másodpéldánytól és a teljes rendszermentéstől.

+

Az alkalmazásaid mentéseiről titkosított másolat készül egy távoli helyre: a Felhom tárhelyére vagy a saját NAS-odra. Ott csak titkosított adat van. Ez a másolat független a helyi másodpéldánytól és a teljes rendszermentéstől.

@@ -203,6 +203,7 @@
–
Tároló méret · 10 pillanatkép
+
Kikapcsolva
A távoli mentés ki van kapcsolva
@@ -219,6 +220,7 @@ +

A megerősítés nem érkezett meg a várt időn belül. Ellenőrizd az internetkapcsolatot, majd készíts új helyreállítási kódot.

@@ -258,12 +260,16 @@
+ +
+ Haladó
A kulcsot 0600-as fájlba írjuk; sosem naplózzuk és nem tároljuk a beállításokban.
- A host-kulcs rögzítése (no blind TOFU). Lekérdezhető: ssh-keyscan -p <port> <host>
+ A célgép saját kulcsa. Ezzel a doboz csak ehhez a géphez kapcsolódik. Így kérdezheted le: ssh-keyscan -p <port> <host>
+ diff --git a/controller/internal/web/testdata/i18n_parity/backups_remote_escrowed.html b/controller/internal/web/testdata/i18n_parity/backups_remote_escrowed.html index 6796384..a73a762 100644 --- a/controller/internal/web/testdata/i18n_parity/backups_remote_escrowed.html +++ b/controller/internal/web/testdata/i18n_parity/backups_remote_escrowed.html @@ -191,7 +191,7 @@

Távoli mentés (3. mentés) — titkosított, offsite

-

Az alkalmazás-mentések titkosított másolata egy távoli tárolóra — saját NAS vagy Felhom offsite tárhely — restic + SFTP kapcsolaton. A tároló csak titkosított adatot lát. Ez a 3-2-1 szabály „1 off-site" lába — független a helyi másodpéldánytól és a teljes rendszermentéstől.

+

Az alkalmazásaid mentéseiről titkosított másolat készül egy távoli helyre: a Felhom tárhelyére vagy a saját NAS-odra. Ott csak titkosított adat van. Ez a másolat független a helyi másodpéldánytól és a teljes rendszermentéstől.

@@ -203,6 +203,7 @@
5 GB
Tároló méret · 10 pillanatkép
+
felhom@nas.example
/srv/repo
@@ -211,6 +212,7 @@ +
Tárhelykeret: 5 GB / 100 GB (5%)
@@ -261,12 +263,16 @@
+ +
+ Haladó
A kulcsot 0600-as fájlba írjuk; sosem naplózzuk és nem tároljuk a beállításokban.
- A host-kulcs rögzítése (no blind TOFU). Lekérdezhető: ssh-keyscan -p <port> <host>
+ A célgép saját kulcsa. Ezzel a doboz csak ehhez a géphez kapcsolódik. Így kérdezheted le: ssh-keyscan -p <port> <host>
+ diff --git a/controller/internal/web/testdata/i18n_parity/backups_remote_full.html b/controller/internal/web/testdata/i18n_parity/backups_remote_full.html index 5d7f645..f7d707a 100644 --- a/controller/internal/web/testdata/i18n_parity/backups_remote_full.html +++ b/controller/internal/web/testdata/i18n_parity/backups_remote_full.html @@ -203,7 +203,7 @@

Távoli mentés (3. mentés) — titkosított, offsite

-

Az alkalmazás-mentések titkosított másolata egy távoli tárolóra — saját NAS vagy Felhom offsite tárhely — restic + SFTP kapcsolaton. A tároló csak titkosított adatot lát. Ez a 3-2-1 szabály „1 off-site" lába — független a helyi másodpéldánytól és a teljes rendszermentéstől.

+

Az alkalmazásaid mentéseiről titkosított másolat készül egy távoli helyre: a Felhom tárhelyére vagy a saját NAS-odra. Ott csak titkosított adat van. Ez a másolat független a helyi másodpéldánytól és a teljes rendszermentéstől.

@@ -215,6 +215,7 @@
12 GB
Tároló méret · 40 pillanatkép
+
felhom@nas.example
/srv/repo
@@ -223,6 +224,7 @@ +
Tárhelykeret: 12 GB / 100 GB (110%)
@@ -359,12 +361,16 @@
+ +
+ Haladó
A kulcsot 0600-as fájlba írjuk; sosem naplózzuk és nem tároljuk a beállításokban.
- A host-kulcs rögzítése (no blind TOFU). Lekérdezhető: ssh-keyscan -p <port> <host>
+ A célgép saját kulcsa. Ezzel a doboz csak ehhez a géphez kapcsolódik. Így kérdezheted le: ssh-keyscan -p <port> <host>
+ diff --git a/controller/internal/web/testdata/i18n_parity/backups_remote_incomplete.html b/controller/internal/web/testdata/i18n_parity/backups_remote_incomplete.html index dcec146..1b14a00 100644 --- a/controller/internal/web/testdata/i18n_parity/backups_remote_incomplete.html +++ b/controller/internal/web/testdata/i18n_parity/backups_remote_incomplete.html @@ -191,7 +191,7 @@

Távoli mentés (3. mentés) — titkosított, offsite

-

Az alkalmazás-mentések titkosított másolata egy távoli tárolóra — saját NAS vagy Felhom offsite tárhely — restic + SFTP kapcsolaton. A tároló csak titkosított adatot lát. Ez a 3-2-1 szabály „1 off-site" lába — független a helyi másodpéldánytól és a teljes rendszermentéstől.

+

Az alkalmazásaid mentéseiről titkosított másolat készül egy távoli helyre: a Felhom tárhelyére vagy a saját NAS-odra. Ott csak titkosított adat van. Ez a másolat független a helyi másodpéldánytól és a teljes rendszermentéstől.

@@ -203,6 +203,7 @@
–
Tároló méret · a pillanatképek száma még ismeretlen
+
felhom@nas.example
/srv/repo
@@ -211,6 +212,7 @@ +
Tárhelykeret: még nem tudjuk, mennyi van a tárolóban — legfeljebb 50 GB
@@ -273,12 +275,16 @@
+ +
+ Haladó
A kulcsot 0600-as fájlba írjuk; sosem naplózzuk és nem tároljuk a beállításokban.
- A host-kulcs rögzítése (no blind TOFU). Lekérdezhető: ssh-keyscan -p <port> <host>
+ A célgép saját kulcsa. Ezzel a doboz csak ehhez a géphez kapcsolódik. Így kérdezheted le: ssh-keyscan -p <port> <host>
+ diff --git a/controller/internal/web/testdata/i18n_parity/backups_remote_notconf.html b/controller/internal/web/testdata/i18n_parity/backups_remote_notconf.html index e986a16..9324780 100644 --- a/controller/internal/web/testdata/i18n_parity/backups_remote_notconf.html +++ b/controller/internal/web/testdata/i18n_parity/backups_remote_notconf.html @@ -193,7 +193,7 @@

Távoli mentés (3. mentés) — titkosított, offsite

-

Az alkalmazás-mentések titkosított másolata egy távoli tárolóra — saját NAS vagy Felhom offsite tárhely — restic + SFTP kapcsolaton. A tároló csak titkosított adatot lát. Ez a 3-2-1 szabály „1 off-site" lába — független a helyi másodpéldánytól és a teljes rendszermentéstől.

+

Az alkalmazásaid mentéseiről titkosított másolat készül egy távoli helyre: a Felhom tárhelyére vagy a saját NAS-odra. Ott csak titkosított adat van. Ez a másolat független a helyi másodpéldánytól és a teljes rendszermentéstől.

@@ -209,12 +209,16 @@
+ +
+ Haladó
A kulcsot 0600-as fájlba írjuk; sosem naplózzuk és nem tároljuk a beállításokban.
- A host-kulcs rögzítése (no blind TOFU). Lekérdezhető: ssh-keyscan -p <port> <host>
+ A célgép saját kulcsa. Ezzel a doboz csak ehhez a géphez kapcsolódik. Így kérdezheted le: ssh-keyscan -p <port> <host>
+ diff --git a/controller/internal/web/testdata/i18n_parity/backups_remote_notconf_hub.html b/controller/internal/web/testdata/i18n_parity/backups_remote_notconf_hub.html index be57e25..77f315e 100644 --- a/controller/internal/web/testdata/i18n_parity/backups_remote_notconf_hub.html +++ b/controller/internal/web/testdata/i18n_parity/backups_remote_notconf_hub.html @@ -194,7 +194,7 @@

Távoli mentés (3. mentés) — titkosított, offsite

-

Az alkalmazás-mentések titkosított másolata egy távoli tárolóra — saját NAS vagy Felhom offsite tárhely — restic + SFTP kapcsolaton. A tároló csak titkosított adatot lát. Ez a 3-2-1 szabály „1 off-site" lába — független a helyi másodpéldánytól és a teljes rendszermentéstől.

+

Az alkalmazásaid mentéseiről titkosított másolat készül egy távoli helyre: a Felhom tárhelyére vagy a saját NAS-odra. Ott csak titkosított adat van. Ez a másolat független a helyi másodpéldánytól és a teljes rendszermentéstől.

@@ -210,12 +210,16 @@
+ +
+ Haladó
A kulcsot 0600-as fájlba írjuk; sosem naplózzuk és nem tároljuk a beállításokban.
- A host-kulcs rögzítése (no blind TOFU). Lekérdezhető: ssh-keyscan -p <port> <host>
+ A célgép saját kulcsa. Ezzel a doboz csak ehhez a géphez kapcsolódik. Így kérdezheted le: ssh-keyscan -p <port> <host>
+ diff --git a/controller/internal/web/testdata/i18n_parity/backups_remote_offsite_offer_fit.html b/controller/internal/web/testdata/i18n_parity/backups_remote_offsite_offer_fit.html index 9188291..5201247 100644 --- a/controller/internal/web/testdata/i18n_parity/backups_remote_offsite_offer_fit.html +++ b/controller/internal/web/testdata/i18n_parity/backups_remote_offsite_offer_fit.html @@ -191,7 +191,7 @@

Távoli mentés (3. mentés) — titkosított, offsite

-

Az alkalmazás-mentések titkosított másolata egy távoli tárolóra — saját NAS vagy Felhom offsite tárhely — restic + SFTP kapcsolaton. A tároló csak titkosított adatot lát. Ez a 3-2-1 szabály „1 off-site" lába — független a helyi másodpéldánytól és a teljes rendszermentéstől.

+

Az alkalmazásaid mentéseiről titkosított másolat készül egy távoli helyre: a Felhom tárhelyére vagy a saját NAS-odra. Ott csak titkosított adat van. Ez a másolat független a helyi másodpéldánytól és a teljes rendszermentéstől.

@@ -203,6 +203,7 @@
5 GB
Tároló méret · 10 pillanatkép
+
felhom@nas.example
/srv/repo
@@ -211,6 +212,7 @@ +
Tárhelykeret: 5 GB / 100 GB (5%)
@@ -274,12 +276,16 @@
+ +
+ Haladó
A kulcsot 0600-as fájlba írjuk; sosem naplózzuk és nem tároljuk a beállításokban.
- A host-kulcs rögzítése (no blind TOFU). Lekérdezhető: ssh-keyscan -p <port> <host>
+ A célgép saját kulcsa. Ezzel a doboz csak ehhez a géphez kapcsolódik. Így kérdezheted le: ssh-keyscan -p <port> <host>
+ diff --git a/controller/internal/web/testdata/i18n_parity/backups_remote_pending_agent.html b/controller/internal/web/testdata/i18n_parity/backups_remote_pending_agent.html index 0cda2a5..15e03eb 100644 --- a/controller/internal/web/testdata/i18n_parity/backups_remote_pending_agent.html +++ b/controller/internal/web/testdata/i18n_parity/backups_remote_pending_agent.html @@ -191,7 +191,7 @@

Távoli mentés (3. mentés) — titkosított, offsite

-

Az alkalmazás-mentések titkosított másolata egy távoli tárolóra — saját NAS vagy Felhom offsite tárhely — restic + SFTP kapcsolaton. A tároló csak titkosított adatot lát. Ez a 3-2-1 szabály „1 off-site" lába — független a helyi másodpéldánytól és a teljes rendszermentéstől.

+

Az alkalmazásaid mentéseiről titkosított másolat készül egy távoli helyre: a Felhom tárhelyére vagy a saját NAS-odra. Ott csak titkosított adat van. Ez a másolat független a helyi másodpéldánytól és a teljes rendszermentéstől.

@@ -203,6 +203,7 @@
5 GB
Tároló méret · 10 pillanatkép
+
felhom@nas.example
/srv/repo
@@ -211,6 +212,7 @@ +
Tárhelykeret: 5 GB / 100 GB (5%)
@@ -264,12 +266,16 @@
+ +
+ Haladó
A kulcsot 0600-as fájlba írjuk; sosem naplózzuk és nem tároljuk a beállításokban.
- A host-kulcs rögzítése (no blind TOFU). Lekérdezhető: ssh-keyscan -p <port> <host>
+ A célgép saját kulcsa. Ezzel a doboz csak ehhez a géphez kapcsolódik. Így kérdezheted le: ssh-keyscan -p <port> <host>
+ diff --git a/controller/internal/web/testdata/i18n_parity/backups_remote_pending_old.html b/controller/internal/web/testdata/i18n_parity/backups_remote_pending_old.html index cde7ad5..6e6716e 100644 --- a/controller/internal/web/testdata/i18n_parity/backups_remote_pending_old.html +++ b/controller/internal/web/testdata/i18n_parity/backups_remote_pending_old.html @@ -191,7 +191,7 @@

Távoli mentés (3. mentés) — titkosított, offsite

-

Az alkalmazás-mentések titkosított másolata egy távoli tárolóra — saját NAS vagy Felhom offsite tárhely — restic + SFTP kapcsolaton. A tároló csak titkosított adatot lát. Ez a 3-2-1 szabály „1 off-site" lába — független a helyi másodpéldánytól és a teljes rendszermentéstől.

+

Az alkalmazásaid mentéseiről titkosított másolat készül egy távoli helyre: a Felhom tárhelyére vagy a saját NAS-odra. Ott csak titkosított adat van. Ez a másolat független a helyi másodpéldánytól és a teljes rendszermentéstől.

@@ -203,6 +203,7 @@
5 GB
Tároló méret · 10 pillanatkép
+
felhom@nas.example
/srv/repo
@@ -211,6 +212,7 @@ +
Tárhelykeret: 5 GB / 100 GB (5%)
@@ -264,12 +266,16 @@
+ +
+ Haladó
A kulcsot 0600-as fájlba írjuk; sosem naplózzuk és nem tároljuk a beállításokban.
- A host-kulcs rögzítése (no blind TOFU). Lekérdezhető: ssh-keyscan -p <port> <host>
+ A célgép saját kulcsa. Ezzel a doboz csak ehhez a géphez kapcsolódik. Így kérdezheted le: ssh-keyscan -p <port> <host>
+ diff --git a/controller/internal/web/testdata/i18n_parity/backups_remote_pinned.html b/controller/internal/web/testdata/i18n_parity/backups_remote_pinned.html new file mode 100644 index 0000000..9fd3256 --- /dev/null +++ b/controller/internal/web/testdata/i18n_parity/backups_remote_pinned.html @@ -0,0 +1,727 @@ + + + + + + + + Távoli mentés beállítása — Felhom.eu + + + + + + + + +
+ + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + +

Távoli mentés (3. mentés) — titkosított, offsite

+

Az alkalmazásaid mentéseiről titkosított másolat készül egy távoli helyre: a Felhom tárhelyére vagy a saját NAS-odra. Ott csak titkosított adat van. Ez a másolat független a helyi másodpéldánytól és a teljes rendszermentéstől.

+
+ +
+
+
✓ Rendben
+
Utolsó távoli mentés
# napja
+
+
+
5 GB
+
Tároló méret · 10 pillanatkép
+
+ +
+
Felhom távoli tárhely (Németország)
+
Ide kerül a titkosított másolat.
+
+
+ +
+ Haladó + u1-sub5@u1-sub5.your-storagebox.de /home/felhom-repo +
+ + + + +
+
Tárhelykeret: 5 GB / 100 GB (5%)
+
+
+
+
+ + + + + + + + + + + +
+

A helyreállítási kód letétbe helyezve.

+ Új helyreállítási kód készítése +
+ + + +
+
+ +
+
+ + +

Mely alkalmazások mentődnek a távoli tárolóra?

+ +

Nincs telepített alkalmazás.

+ + + +
+ Távoli mentési cél beállítása +
+
+
+
+
+
+ +
+ Haladó +
+ + A kulcsot 0600-as fájlba írjuk; sosem naplózzuk és nem tároljuk a beállításokban.
+
+ + A célgép saját kulcsa. Ezzel a doboz csak ehhez a géphez kapcsolódik. Így kérdezheted le: ssh-keyscan -p <port> <host>
+
+ +
+
+ + +
+ + + + + +
+ + + + diff --git a/controller/internal/web/testdata/i18n_parity/backups_remote_running.html b/controller/internal/web/testdata/i18n_parity/backups_remote_running.html index b86a4f4..58e7135 100644 --- a/controller/internal/web/testdata/i18n_parity/backups_remote_running.html +++ b/controller/internal/web/testdata/i18n_parity/backups_remote_running.html @@ -201,7 +201,7 @@

Távoli mentés (3. mentés) — titkosított, offsite

-

Az alkalmazás-mentések titkosított másolata egy távoli tárolóra — saját NAS vagy Felhom offsite tárhely — restic + SFTP kapcsolaton. A tároló csak titkosított adatot lát. Ez a 3-2-1 szabály „1 off-site" lába — független a helyi másodpéldánytól és a teljes rendszermentéstől.

+

Az alkalmazásaid mentéseiről titkosított másolat készül egy távoli helyre: a Felhom tárhelyére vagy a saját NAS-odra. Ott csak titkosított adat van. Ez a másolat független a helyi másodpéldánytól és a teljes rendszermentéstől.

@@ -213,6 +213,7 @@
–
Tároló méret · 10 pillanatkép
+
felhom@nas.example
/srv/repo
@@ -221,6 +222,7 @@ +
Tárhelykeret: 0 / 10 GB (10%)
@@ -278,12 +280,16 @@
+ +
+ Haladó
A kulcsot 0600-as fájlba írjuk; sosem naplózzuk és nem tároljuk a beállításokban.
- A host-kulcs rögzítése (no blind TOFU). Lekérdezhető: ssh-keyscan -p <port> <host>
+ A célgép saját kulcsa. Ezzel a doboz csak ehhez a géphez kapcsolódik. Így kérdezheted le: ssh-keyscan -p <port> <host>
+ diff --git a/controller/internal/web/testdata/i18n_parity/backups_remote_stale.html b/controller/internal/web/testdata/i18n_parity/backups_remote_stale.html index 5ebf0c4..b7bd966 100644 --- a/controller/internal/web/testdata/i18n_parity/backups_remote_stale.html +++ b/controller/internal/web/testdata/i18n_parity/backups_remote_stale.html @@ -191,7 +191,7 @@

Távoli mentés (3. mentés) — titkosított, offsite

-

Az alkalmazás-mentések titkosított másolata egy távoli tárolóra — saját NAS vagy Felhom offsite tárhely — restic + SFTP kapcsolaton. A tároló csak titkosított adatot lát. Ez a 3-2-1 szabály „1 off-site" lába — független a helyi másodpéldánytól és a teljes rendszermentéstől.

+

Az alkalmazásaid mentéseiről titkosított másolat készül egy távoli helyre: a Felhom tárhelyére vagy a saját NAS-odra. Ott csak titkosított adat van. Ez a másolat független a helyi másodpéldánytól és a teljes rendszermentéstől.

@@ -203,6 +203,7 @@
5 GB
Tároló méret · 10 pillanatkép
+
felhom@nas.example
/srv/repo
@@ -211,6 +212,7 @@ +
Tárhelykeret: 5 GB / 100 GB (5%)
@@ -262,12 +264,16 @@
+ +
+ Haladó
A kulcsot 0600-as fájlba írjuk; sosem naplózzuk és nem tároljuk a beállításokban.
- A host-kulcs rögzítése (no blind TOFU). Lekérdezhető: ssh-keyscan -p <port> <host>
+ A célgép saját kulcsa. Ezzel a doboz csak ehhez a géphez kapcsolódik. Így kérdezheted le: ssh-keyscan -p <port> <host>
+ diff --git a/controller/internal/web/testdata/i18n_parity/backups_remote_stale_old.html b/controller/internal/web/testdata/i18n_parity/backups_remote_stale_old.html index c7fcba7..c4af658 100644 --- a/controller/internal/web/testdata/i18n_parity/backups_remote_stale_old.html +++ b/controller/internal/web/testdata/i18n_parity/backups_remote_stale_old.html @@ -191,7 +191,7 @@

Távoli mentés (3. mentés) — titkosított, offsite

-

Az alkalmazás-mentések titkosított másolata egy távoli tárolóra — saját NAS vagy Felhom offsite tárhely — restic + SFTP kapcsolaton. A tároló csak titkosított adatot lát. Ez a 3-2-1 szabály „1 off-site" lába — független a helyi másodpéldánytól és a teljes rendszermentéstől.

+

Az alkalmazásaid mentéseiről titkosított másolat készül egy távoli helyre: a Felhom tárhelyére vagy a saját NAS-odra. Ott csak titkosított adat van. Ez a másolat független a helyi másodpéldánytól és a teljes rendszermentéstől.

@@ -203,6 +203,7 @@
5 GB
Tároló méret · 10 pillanatkép
+
felhom@nas.example
/srv/repo
@@ -211,6 +212,7 @@ +
Tárhelykeret: 5 GB / 100 GB (5%)
@@ -262,12 +264,16 @@
+ +
+ Haladó
A kulcsot 0600-as fájlba írjuk; sosem naplózzuk és nem tároljuk a beállításokban.
- A host-kulcs rögzítése (no blind TOFU). Lekérdezhető: ssh-keyscan -p <port> <host>
+ A célgép saját kulcsa. Ezzel a doboz csak ehhez a géphez kapcsolódik. Így kérdezheted le: ssh-keyscan -p <port> <host>
+ diff --git a/controller/internal/web/testdata/i18n_parity/escrow_first.html b/controller/internal/web/testdata/i18n_parity/escrow_first.html index 893efc1..a45c0b1 100644 --- a/controller/internal/web/testdata/i18n_parity/escrow_first.html +++ b/controller/internal/web/testdata/i18n_parity/escrow_first.html @@ -186,14 +186,14 @@
-

1. Előfeltételek ellenőrzése

+

Előfeltételek ellenőrzése

Ellenőrzés folyamatban…