Files
felhom.eu/documentation/audits/i18n-slice2-2026-09-18/C/live/README.md
T
admin 95c10954ae
gates / gates (push) Successful in 23s
docs: localisation slice 2 CLOSED (controller v0.254.0) — R-577, R-578, and a probe rule
10-localisation.md §10.3: the saved notes follow the box language at write time, with the
one-night consequence stated rather than hidden; the globe, and the table of WHO reads which
page and where its globe posts — getting that wrong makes the button do nothing, which it did
on /recovery until the live probe found it. Decision 6 superseded a second time; decision 8
(a claim carries the visitor's language) recorded. Decision 5 of §11's anonymous-surface line:
changing what a VISITOR reads is within what an anonymous request may do; changing anything the
household owns is not, and POST /lang can do only the first.

R-578 — the deadlock, and why it is a row rather than a fixed bug: UpdateOffboxStatus holds the
settings write lock while running its callback, boxLang() wants the read lock, sync.RWMutex is
not reentrant. On a real box an off-site run would have hung FOREVER holding that lock. The
symptom was a test suite going from 8 minutes to a 25-minute timeout. Fixed and guarded, but the
guard covers one package and three helper names; the class needs a gate.

R-577 — a guest share visitor still has no way to pick a language, and the household's setting
is the wrong default for a stranger. Deliberately left, pinned by a test, and the operator's to
decide because it is a promise the share feature makes.

.claude/rules/live-probes.md, unconditional: never send a deploy request for an app that is not
installed, not even expecting a refusal — the endpoint accepts first and validates later. Two
sessions made that mistake in two days, the second WITH a prompt line forbidding it. A prompt is
read once; a rule file is loaded every session.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-09-18 14:31:49 +02:00

75 lines
4.2 KiB
Markdown

# Live validation — controller v0.254.0 on demo-hp guest 9201 (2026-09-18, release C)
**Method: endpoint-level, stated as such.** No browser on DooPlex. Nothing was installed, created,
formatted or deleted, and **the deploy endpoint was not touched at all** — the new rule
(`.claude/rules/live-probes.md`) written after yesterday's mistake. The password was passed as a file
and deleted from host and guest afterwards.
## 1. A visitor's language is their own (no session) — before and after
| probe | 0.253.0 | 0.254.0 |
|---|---|---|
| globe on `/login` | **0** | **1** |
| `POST /lang lang=en back=/login` | 302 (no such route → the login redirect) | **303, `felhom_lang=en` set** |
| `/login` with that cookie | `<html lang="hu">` | **`<html lang="en">`**, and "Forgot password" present |
| **`settings.json` `language` after it** | `"hu"` | **`"hu"` — unchanged.** An anonymous request cannot write what the household owns |
| `POST /lang lang=xx` | 302 | **400**, no cookie |
| `POST /lang back=//evil.example/x` | the login redirect | **`/`** — a protocol-relative URL is another origin |
| globe on `/claim` | 0 | **1** |
## 2. A signed-in household reads their own setting, never the cookie
`/launcher` with a session **and** the `felhom_lang=en` cookie → **`<html lang="hu">`**. The cookie is
not read once there is a session; that is the row that keeps a household from inheriting a language a
previous visitor picked in the same browser.
## 3. The dashboard globe, end to end
`POST /settings/language lang=en` (session CSRF) → 302 → `settings.json` `"language": "en"` →
`/launcher`, `/backups`, `/backups/remote` all `<html lang="en">` → switched back → `"hu"`.
The footer renders in order — version, globe, sign-out — and the old two-text-link switch is gone
(`lang-switch-btn` count 1 → **0**):
```
<div class="sidebar-footer">
<span class="version">0.254.0</span><details class="lang-globe">
<summary class="lang-globe-btn" aria-label="Nyelv" title="Nyelv"><svg class="ico" …globe…/></summary>
<ul class="lang-globe-menu">
<li><form method="POST" action="/settings/language">…<button … class="lang-globe-item current" aria-current="true">Magyar</button></form></li>
<li><form method="POST" action="/settings/language">…<button … class="lang-globe-item">English</button></form></li>
</ul>
</details>
<a href="/logout" class="logout-link">Kijelentkezés ↗</a>
```
## 4. The saved note, and the §16 consequence, seen live
With the box switched to English, the stored tier-2 note stayed as it was written:
`"last_warning": "A belső SSD-n csak a konfiguráció, adatbázis és a köte…"`. **That is the operator's
choice working, not a bug:** a saved note is written in the box's language at write time and shown
verbatim afterwards, so a household that switches sees the previous run's note in the old language
until the next run rewrites it.
## 5. What could NOT be proven live, and why
**The `/recovery` globe.** `recoveryPageHandler` redirects to `/backups/remote` unless
`recoveryOffer()` is true — the page exists only while a box is in a lost-machine situation, and
demo-hp is not. It 302s with or without a session. So its globe is proven by the render tests
(`TestGlobeOnAnonymousShells`, `TestI18nDirectRenderPagesFollowLanguage`) and by its 7 parity
fixtures, **not live**. Said plainly rather than left to be assumed from the other rows.
That page is also where release C's own defect was: it is an AUTHENTICATED route, so its globe must
write the household's SETTING; the first draft gave it the anonymous form, which sets a cookie
`langFor` ignores once there is a session — the button would have done nothing. Found by this probe
reporting no globe on `/recovery` and then reading the route table.
**The `<details>` menu opening in a browser.** `claude-in-chrome` is not available on DooPlex. The
markup is asserted; whether it looks and behaves right on screen is a manual click-through.
## 6. State left behind
Controller **0.254.0**; saved language **`hu`**; the visitor cookie was set only inside the probe's
own curl jar and is gone with it. 23 standing containers up, unchanged. Nothing installed, nothing
removed, no drive touched, no floor raised, no golden baked. **Provisioned nothing.**