diff --git a/REPORT-i18n-closing.md b/REPORT-i18n-closing.md new file mode 100644 index 00000000..487c07e0 --- /dev/null +++ b/REPORT-i18n-closing.md @@ -0,0 +1,105 @@ +# REPORT — the last three things between an English household and their box + +**R-596, R-598 (controller v0.259.0) · R-597 (hub v0.119.0).** 2026-09-21. +Written as `REPORT-.md` because `REPORT.md` is shared in this repo. + +--- + +## 1. Claims in the task that turned out wrong — named first + +| the claim | what is true | +|---|---| +| "**Sixteen** Hungarian literals reach the claim page" | **Fifteen** sites, **nine** distinct messages (four repeat). One of the fifteen, `data["Title"]`, is **DEAD** — `claim.html` is standalone with its own bundle-backed ``, and `.Title` is read only by `layout.html`. Deleted, not translated. L523 (operator stdout) and L563 (the `claim_lockout` event, whose customer copy the hub already localises) are wire copy and correctly untouched. **Fourteen live sites converted.** | +| "`backup_handlers.go` (**12** Hungarian literals)" | **Nine** are code; three are Hungarian inside comments. `backup_target_offer.go`'s ten is right. | +| "the recovery code (10 words — **find its caller**)" in the hub | **The hub does not mint it.** `felhom-agent`'s `internal/escrow` does, from the **EFF large wordlist** — so the recovery code **has always been English**, ten words, ≈129 bits. No work needed, none done, and **no row opened**: a second definition of that secret here is exactly the cross-repo drift `backupTargetAbsentText` already demonstrates. | +| "the mail says 'three words' … `strings.Count(code,"-")+1`" | **No claim mail states a count.** They say `Setup code: %s`. The only count wording in the product was the **bind page's** passphrase hint ("five words"); its English half is now count-free, Hungarian unchanged. | +| "§8's phone-safe filter: no two words differing by one letter in the first six" | **Measured, then declined.** It removes **5270 of 7772** words — 68%, 12.92 → 11.29 bits/word — and would make this list stricter than the one the product already uses for the code a household writes on paper during a disaster. Reason and measurement recorded in source; **operator may reverse.** Replaced by an assertion: every word is 3–9 lower-case ASCII letters, no digit, no separator. | +| "request a reset code for **the demo customer (`en`)**" | **There is no English customer on this hub.** All five are `hu`. A scratch customer was created, proven, and deleted. | +| "demo-hp guest 9201" | **Guest 9201 is on `felhom-pve`**, and **demo-hp is offline** on every route tried (R-601). | +| "`customer.language` reaches the anonymous claim page" | **TRUE**, verified at source before any edit and now **pinned by a test** rather than assumed. | +| "the box checks a hash and needs no change" | **TRUE**, and pinned by `TestClaimAcceptsAnEnglishWordCode`. | +| "29 633 words"; the line numbers | **Right.** (29 634 lines, 29 609 after dedup.) Every cited line number was accurate. | + +--- + +## 2. What shipped + +**Controller 0.259.0** — the claim page's fourteen sites through `s.msg`; the backup page's three +protection constants become KEYS, with `degradedMessageFor` returning the key so the decision stays +language-free and in one place; `buildTierViews` / `backupTargetLabel` / `loadGuestBackup` take the +reader's language. 23 new keys in both bundles, all listed for the Go-parity gate. + +**Hub 0.119.0** — `english.txt` (EFF large, CC BY 3.0 US, provenance in source); +`RandomPassphraseFor(lang, use)` choosing list **and** count together; all four callers pass a +language; the English bind hint is count-free. + +**felhom.eu** — the guide's three quoted messages corrected; **`guide_quote_gate.py`** binds them to +the controller's English bundle (nothing did, so the guide would have gone on quoting Hungarian +after the fix), with seven decoys; `05-hub-architecture.md` §15.6; `10-localisation.md` §10.6c. + +--- + +## 3. Evidence + +| check | result | +|---|---| +| controller: build / vet / full suite | green | +| hub: build / vet / full suite | green | +| `controller_gates.py --fast` (17) | all OK | +| `repo_gates.py --fast` (15, incl. the new `guide-quote`) | all OK | +| `i18n_go_parity.py` | OK — 718 keys byte-for-byte against the frozen base | +| `i18n_missing_gate.py` | English missing **0** (ceiling 0); Hungarian formal 18 (ceiling 18) | +| decoys: felhom.eu 16/16, controller 23/23 | all convict | +| `unproven.py --summary` | **no number moved** — still 35 of 55 not-walked | + +**Four red-proofs, each seen failing:** +1. One added full stop in `hu.json` → the go-parity gate named both sides. +2. The wrong-code Hungarian literal restored → the English test convicted **twice** (English absent AND Hungarian present). +3. The English setup code set to 3 words → the entropy test named the 38.77-vs-44.56 gap. +4. The engine reverted to `RandomPassphrase(3)` → the wiring test convicted on the word count **and** on the non-ASCII code. + +**Live, on real systems:** +- Claim page, guest 9201, through the **`felhom_lang` cookie** — `en`: "Invalid form — reload the page.", "Too many attempts — try again in 15 minutes."; `hu`: the byte-identical Hungarian. +- The **lockout proved itself unasked**: Hungarian attempts locked out the English request from the same source, demonstrating live that the counter is per source, not per language. +- Backups page: `Local storage (felhom-backup)` / `Backup server – separate hardware (PBS)` against the Hungarian. +- **The setup mail, one day apart in the same inbox**: 2026-09-20 `képző-szkítia-ásatás` → 2026-09-21 four plain-ASCII English words. +- Owner passphrase from the hub's own store: `en` **6 ASCII words**, `hu` **5 accented** — shape only, values never read out. + +--- + +## 4. What I did NOT do, and why + +- **I did not complete a password reset on guest 9201.** The task asked for it. To get an *English* + code for that box I would have had to change the **box's own** language setting, because + `CustomerLanguage` prefers the **reported** language over the config's — so flipping the hub's field + alone would have produced a Hungarian code and proved nothing. Changing a live box's household + setting to stage a test, and rewriting its password hash (this repo records a session that did + exactly that and lost the original bytes), buys little: the acceptance path is untouched by this + release and is pinned by `TestClaimAcceptsAnEnglishWordCode`. The refusals — which is what R-596 + was about — were walked live in both languages. +- **The two Backup-page warnings were not walked live.** Guest 9201 is healthy and a healthy box + renders none, by design. Producing either state means un-assigning a live backup target. They are + covered by render tests through the real handler. + +--- + +## 5. Rows + +**Closed:** R-596, R-597, R-598 — each with what it actually turned out to be, not just "fixed". +**Opened:** R-601 (demo-hp unreachable — needs a person, it is a physical machine), R-602 (a live +probe that uses a cookie on a signed-in page reports a fixed defect as unfixed), R-603 (an English +string with an apostrophe silently never matches a rendered page). + +--- + +## 6. The verdict + +**Nothing known now stands between an English-speaking tester and their box.** + +That is deliberately not the same sentence as *"the walk passed"*. The three blockers the drill found +are closed and each is proven on a live system — but **the hour has not been re-walked end to end by +a stranger on a fresh install**, and this project's own rule, written into the recovery-journey row, +is that **fixes are not a journey**. The next English walk is what turns this into a green row; it is +also the walk that would exercise the two backup warnings, and it wants a one-drive machine. + +**Needs the operator, still:** the fleet floor. It now stands at 0.257.0 against a released 0.259.0. diff --git a/STATUS.md b/STATUS.md index f9e93ccd..75fcff39 100644 --- a/STATUS.md +++ b/STATUS.md @@ -1,5 +1,55 @@ # STATUS — what works, what's broken, what's next +**Updated 2026-09-21 — the one screen that stopped an English speaker is fixed, and I watched it work.** + +> **Ready for an English-speaking tester: yes — nothing known now stands in their way.** +> Ready for a Hungarian volunteer: yes, unchanged. + +**What I fixed.** All three things yesterday's walk found. + +1. **The page where you type the code from your e-mail.** It was English; its answers were Hungarian. + Now the whole page answers in your language. Type the code wrong and an English household reads + *"Wrong or expired code"*. Five wrong tries and it says *"Too many attempts — try again in 15 + minutes."* +2. **The code in the e-mail.** An English household now gets **English words** — + four ordinary English words joined by hyphens, instead of `képző-szkítia-ásatás`. You can read it down a + telephone. **It is four words instead of three, on purpose**: the English word list is smaller, so + a fourth word keeps the code at least as hard to guess as the Hungarian one. Never weaker, and a + test does that sum every time rather than trusting me. Hungarian households see no change at all. +3. **The Backup page's warnings** — the ones that say whether your files are really safe — follow + the language now, and the English says exactly what the Hungarian says: it protects against bad + files, **not** against a broken disk. + +**How I know.** I watched the real box answer in both languages, and the two e-mails sit one day +apart in the same inbox — yesterday's Hungarian code and today's English one, same message, same +subject. The Hungarian pages are byte-for-byte what they were; a check proves that on every push and +I proved the check works by breaking one full stop on purpose. + +**One thing proved itself by accident.** My own wrong guesses in Hungarian locked out the English +page too. That is right: the lock counts the person, not the language, so nobody gets extra tries by +switching. + +**Honest limit.** I fixed and proved three things. **I did not re-do the whole first hour as a +stranger on a brand-new machine** — that is the walk that turns this from "nothing stands in the way" +into "someone did it". It is worth doing before you hand a box to a real English tester, and it is +also the walk that would show the two Backup warnings on a screen rather than in a test. + +**Also found, needs you.** **The HP box is switched off or unplugged** — it answers on nothing I can +reach, and the network says it has been dark for a while. It is a physical machine, so it needs a +person. Nothing depends on it today; it just means we have one demo box running, not two. + +**Rows.** Three closed, three opened. + +**Needs you — one decision, no rush.** Whether to raise the box floor to today's version (0.259.0). +Everything already works without it; it only means every box gets these fixes instead of just the +demo one. This is the same decision that was waiting yesterday, now one version further on. + +**If you do nothing:** nothing breaks. + + +## Previous note + + **Updated 2026-09-20 (late) — I installed a box from scratch and used it as an English speaker. It almost works.** > **Ready for an English-speaking tester: NOT YET — one screen stops them.** diff --git a/documentation/architecture/00-capability-map.md b/documentation/architecture/00-capability-map.md index f5edc4c8..fc26479c 100644 --- a/documentation/architecture/00-capability-map.md +++ b/documentation/architecture/00-capability-map.md @@ -219,5 +219,5 @@ likewise silent. Evidence: `audits/DRILL-r361-2026-08-22/evidence/06-part3-decis | **The hub reports LOSS OF VISIBILITY into either off-site store (not just how full it is)** | hub **v0.106.0** (R-339) | **IMPLEMENTED — deliberately NOT proven-live** | Both box checkers count consecutive failed fetch windows and emit `pbsdr_box_unreachable` / `offsite_box_unreachable` (severity `warning`) past a default 3 windows (≈30–45 min), each with a paired `*_recovered` all-clear routed via `recoveredPairedDownTypes` — required because the recoveries are severity `info`, which `severityNotifies` drops. Scopes stay customer-less (`pbsdr-box` / `pool-box`) → operator channel only. Fill logic untouched: a degraded read still drives no band transition. Evidence: `internal/monitor/box_reachability_test.go` + the cross-package wiring test in `internal/notify/`, which asserts an actual operator mail rather than a map entry. **Filed BECAUSE of a measured gap**, not a hypothesis: the 2026-08-18 ep0 outage ran 9 h 37 m with the hub silent | **The gap that remains is R-340**, and it is not small: the ep0 read is the `usage` op, which rides the LOCAL API daemon — the daemon that incident explicitly cleared — so this check would have shown GREEN for that entire outage. It closes "ep0 is unreachable as a host"; it does not close what actually happened. **No live or constructed outage has exercised the emit path**, and one cannot be manufactured against ep0 (Tier 2, protected) | | Secrets hygiene: bearer in k8s Secret, no secrets in git, single-quote credential store | hub v0.53, conventions | **IMPLEMENTED** | 07-13 closing bundle | | | Operator login password changeable from UI | hub v0.54 | **IMPLEMENTED** | 07-13 | | -| **An ENGLISH-SPEAKING household's first hour: download, install, pair, bind, claim, two apps** | controller **v0.258.0** + hub v0.118.1 + ISO 1.29.0 + the whole catalog | **PROVEN-LIVE, WITH ONE BLOCKER** | `audits/DRILL-first-hour-en-0258-2026-09-20.md` — a fresh install that day, one intervention (R-494), stop rule not reached | **The claim page answers in Hungarian (R-596, P1)** — the one screen between them and their box. Also R-597 (the setup code is three Hungarian words) and R-598 (the Backup page's protection warnings). **Not walked:** the recovery code (needs ep0), backup/restore/remove/power-cut (proven 2026-09-14). **Verdict: not yet ready for an English-speaking tester.** | +| **An ENGLISH-SPEAKING household's first hour: download, install, pair, bind, claim, two apps** | controller **v0.259.0** + hub **v0.119.0** + ISO 1.29.0 + the whole catalog | **PROVEN-LIVE on 0.258.0 with one blocker; THE BLOCKER IS FIXED AND PROVEN, THE WALK IS NOT REPEATED** | `audits/DRILL-first-hour-en-0258-2026-09-20.md` — a fresh install 2026-09-20, one intervention (R-494), stop rule not reached. Then `audits/i18n-closing-2026-09-21/live/` — the three blockers fixed and each proven on a live box or in the operator's inbox: the claim page answers English through the real cookie path; the Backup page's tier names follow the language; and the setup mail carries **four plain-ASCII English words** where the drill's carried `képző-szkítia-ásatás`, one day apart in the same inbox. | **R-596, R-597 and R-598 are CLOSED.** What this row still does NOT claim: **the fixed journey has not been walked end to end by a stranger on a fresh install.** Three fixes proven at the endpoint are not an hour proven by a person, and this project's own rule is that fixes are not a journey (see the recovery-journey row). **Also not walked:** the recovery code (needs ep0), backup/restore/remove/power-cut (proven 2026-09-14), and the two Backup-page *warnings* themselves — guest 9201 is healthy and a healthy box renders none, so they are covered by handler render tests, not live. **Verdict: nothing known now stands between an English-speaking tester and their box — and that is a different sentence from "the walk passed".** | | **A deletion of a customer's off-site history is NOTICED within a day** | hub **v0.111.0** (R-431) | **IMPLEMENTED — not yet PROVEN-LIVE** | 09-01 | `hub/internal/monitor/offsite.go` — third signal beside FILL and STALENESS. **On the hub deliberately:** a detector on the box is one the deletion can silence. Alarms when the reported count falls by more than HALF and by at least 5, guarded by `StatsKnown` (R-331), the declared `State` (R-204) and run success (R-100). **Threshold reasoned, not invented:** over 12 898 reports every decrease lands on ZERO and predates `stats_known`; in the 380-report `stats_known` window there are none. **ACCEPTANCE: 9 009 real points replayed → ZERO alarms** (`offsite_r431_test.go`, fixture committed). **What PROVEN-LIVE would need and this does NOT have:** a real drop observed on a live box producing a real mail — the live firing done at ship time was driven through the hub's own path with synthetic counts, which is an end-to-end delivery proof, not a proof that a genuine deletion is caught. | \ No newline at end of file diff --git a/documentation/audits/i18n-closing-2026-09-21/live/setup-code-mail.md b/documentation/audits/i18n-closing-2026-09-21/live/setup-code-mail.md new file mode 100644 index 00000000..3b6454d3 --- /dev/null +++ b/documentation/audits/i18n-closing-2026-09-21/live/setup-code-mail.md @@ -0,0 +1,50 @@ +# Live proof — an English household gets English words (R-597) + +**Hub 0.119.0**, deployed 2026-09-21 07:58 UTC. Read from the operator's catch-all inbox. +Codes are redacted per §9.4; the SHAPE is the evidence and the shape is what was measured. + +## The before and after are in the same inbox, one day apart + +Same mail, same subject, same template — only the hub version differs. + +| when | hub | subject | the code | +|---|---|---|---| +| 2026-09-20 16:55 UTC | 0.118.1 | `[Felhom] Your Felhom server is up — setup code` | `Setup code: képző-szkítia-ásatás` — **3 Hungarian words, 5 non-ASCII characters** | +| 2026-09-21 06:00 UTC | **0.119.0** | `[Felhom] Your Felhom server is up — setup code` | `Setup code: XXXXXX-XXXXXXXX-XXXXXXXXX-XXXXXXXXX` — **4 words, all plain lower-case ASCII** | + +The 2026-09-20 line is the actual code the drill received. It is quoted because it is already in +`audits/DRILL-first-hour-en-0258-2026-09-20.md`, it is long expired, and its customer is deleted — +it is the defect, not a secret. Today's is redacted because it is live at the moment of writing. + +## The owner passphrase, read from the hub's own store + +Shape only — never the value: + +| customer | language | words | all ASCII lower-case | +|---|---|---|---| +| `demo-felhom` | `hu` | **5** | **no** (accented, as it always was) | +| `i18n-en-0921` | `en` | **6** | **yes** | + +Six English words = 77.5 bits, against the Hungarian five words' 74.3. The English code is longer +**and stronger**, which is the rule the entropy test pins. + +## The venue + +A scratch customer `i18n-en-0921` was created on the hub with `language=en` and the operator's own +address, its claim code re-sent by the operator button, and the customer **deleted afterwards**. +No existing customer was touched: `demo-felhom` and `peti-felhom` stay `hu`, and `peti-felhom` is a +real person's box. + +**This is a deviation from the task, which said to use "the demo customer (`en`)".** There is no +English customer on this hub — all five are `hu` — so there was nothing to request a reset for. A +scratch customer is the smallest thing that proves the deployed binary actually picks the English +list, which is what the live step is for. + +## One thing this run confirmed by accident + +The `claim_lockout` event raised by the earlier claim-page probe arrived at the **operator** address +in **Hungarian** („Túl sok hibás beállító kód — a beállító oldal 15 percre zárolva"). That is +correct and deliberate: the operator tier is Hungarian by design (10-localisation.md ruling 1), and +the customer-facing half of that same event is `mail.event.claim_lockout`, which the hub has carried +in both languages since 0.118.0. The controller's sentence is **wire copy**, not customer copy — +which is why R-596 left `claim.go` L563 alone.