Copy the files to be processed in here. The app reads them and then deletes them from here — this folder is temporary and is not backed up.
+
+
+
+
+
+
+
+
+
+
+
+
+
What is it for?
+
+
Turn paper documents into files and keep them in order
Automatic OCR text recognition on scanned documents
Smart automatic sorting and tagging
Full text search across every document
Documents that arrive by e-mail are processed automatically
+
+
+
+
+
+
+
First steps
+
+
Open paperless.DOMAIN in your browser
Sign in: user "admin". The password is on the Settings page, under Automatically generated values
Upload a document in the app, OR drop it into the import/paperless folder in the File manager (FileBrowser) - it is read in and processed with OCR from there. The File manager sign-in is on the File manager app page
You can upload many documents at once: they are processed one by one, and a larger batch takes a few minutes
Create tags and correspondents so new documents sort themselves
+
+
+
+
+
+
+
Before you start
+
+
An external hard drive is recommended to keep the documents on
Email-ből érkező dokumentumok automatikus feldolgozása
+
+
+
+
+
+
+
Első lépések
+
+
Nyisd meg a paperless.DOMAIN címet a böngészőben
Lépj be: felhasználó "admin", a jelszó a Beállítások oldalon található (Automatikusan generált értékek)
Tölts fel egy dokumentumot a webfelületen, VAGY dobd a Fájlkezelőben (FileBrowser) az import/paperless mappába — onnan automatikusan beolvassa és OCR-rel feldolgozza. A Fájlkezelő belépése a Fájlkezelő alkalmazás oldalán található
Sok dokumentumot egyszerre is feltölthetsz: egyenként dolgozza fel őket, egy nagyobb köteg néhány percig tart
Hozz létre címkéket és levelezőket az automatikus rendszerezéshez
Encrypted text sharing - the server never sees the content
+
+
+ ~30M RAM
+ security
+
+ Runs on Pi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Move to another storage
+
You can move this app’s data to another connected storage. The app stops briefly during the move, and the data is deleted from the old place only after the check and a successful restart.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
What is it for?
+
+
Share sensitive text safely
End-to-end encryption - the server cannot reach the content
You choose how long it lasts (5 minutes to 1 year, or never)
Optional: delete it automatically after it is read
Password protection for extra safety
+
+
+
+
+
+
+
First steps
+
+
Open paste.DOMAIN in your browser
Type your text and select Send
Share the link you get - the encryption key is part of the URL
Titkosított szöveg megosztás - a szerver nem látja a tartalmat
+
+
+ ~30M RAM
+ security
+
+ Pi kompatibilis
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Áthelyezés másik tárhelyre
+
Ennek az alkalmazásnak az adatait másik csatlakoztatott tárhelyre helyezheted át. Az alkalmazás az áthelyezés alatt rövid időre leáll, az adatok pedig csak az ellenőrzés és a sikeres újraindítás után törlődnek a régi helyről.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Mire használható?
+
+
Érzékeny szövegek biztonságos megosztása
E2E titkosítás - a szerver nem fér hozzá a tartalomhoz
Beállítható lejárati idő (5 perc - 1 év, vagy soha)
Olvasás után automatikus törlés opció
Jelszóvédelem a még nagyobb biztonságért
+
+
+
+
+
+
+
Első lépések
+
+
Nyisd meg a paste.DOMAIN címet a böngészőben
Írd be a szöveget és kattints a Küldés gombra
Oszd meg a generált linket - a titkosítási kulcs az URL-ben van
+
+
+
+
+
+
+
+
+
diff --git a/documentation/backlog/OPEN-ITEMS.md b/documentation/backlog/OPEN-ITEMS.md
index 13f31b7d..ddb24912 100644
--- a/documentation/backlog/OPEN-ITEMS.md
+++ b/documentation/backlog/OPEN-ITEMS.md
@@ -770,6 +770,7 @@ class (an image `VOLUME` at an unmounted path) is still live — `immich-server`
| **R-592** | **[P3-LOW] Two defects in the new catalog copy gate, each found by its own decoy rather than by reading it.** FOUND AND FIXED 2026-09-20 building `app-catalog-felhom.eu/scripts/check-copy-i18n.py` (R-560 slice 5). (1) **Coverage was counted only for the apps NAMED on the command line**, so `check-copy-i18n.py privatebin` reported 47 more untranslated strings than the same tree unscoped — a ratchet anybody could loosen by naming one app, and either number could have been made to "pass". (2) **The credential check searched for its token as a bare substring**, so a `default_creds` of „admin / adminadmin" translated to "administrator / hunter2" PASSED: "admin" is inside "administrator". (3) **The ASCII-Hungarian stems matched as bare substrings too**, so „ird be" convicted "the third best" and „angol" convicted "Angola". All three now have their own case in `scripts/test_gate_decoys.py` (33 cases, every one seen to convict or pass as intended). **Recorded rather than left in a commit message** because the general form is worth the row: every one of the three was a check that matched a LABEL where the FACT was a word, a login or a whole catalog — the R-421 shapes, inside the gate written to enforce R-421. | **CLOSED 2026-09-20 — fixed in the same session, decoys added** |
| **R-593** | **[P3-LOW] papra's session-signing key is described as „the app's subdomain".** FOUND 2026-09-20 translating the catalog (R-560 slice 5, batch 1). `templates/papra/.felhom.yml` `deploy_fields[AUTH_SECRET].description` reads **„Az alkalmazás aldomainje"** — the sentence that belongs on `SUBDOMAIN`, on a field that signs sessions. `SUBDOMAIN` itself has NO description at all in that file, so this is a copy-paste that landed one field too low and took the original with it. A customer opening papra's install page reads a wrong explanation under a key they must not regenerate. **A localisation release may not change Hungarian bytes (10 §1)**, so it was not fixed there; and translating a wrong sentence faithfully would have shipped the error in a second language, so **that one field was left untranslated** — it falls back to the Hungarian exactly as today, and it is the ONE string keeping papra at 13/14 and the catalog ceiling off zero. **Fix shape:** move the sentence to `SUBDOMAIN` and give `AUTH_SECRET` its own („A munkamenetek aláírásához használt kulcs" or similar), re-capture that app's two entries in `scripts/copy_freeze/hu.json` in the same commit with the reason, then add the English. Owned by R-516 as a Hungarian-words change. | **READY - rank P3-LOW; owner: CC (catalog)** |
| **R-594** | **[P3-LOW] The catalog copy gate can CONVICT a retrieval promise but has no way to REGISTER a true one.** FOUND 2026-09-20 translating batch 3 (R-560 slice 5). Vaultwarden's invite step and its sign-up setting both ended „…can open an account", and the English retrieval-promise pattern reads `can … open` as the claim that sealed backups can be opened. **The conviction was a FALSE POSITIVE** — opening an account is not opening a backup — and the two sentences were reworded to „can sign up", which is also the better copy, so nothing is blocked today. **The gap is structural.** The shared vocabulary this gate copies (`scripts/customer_copy_vocab.py`) states the design explicitly: these stems are NOT banned, because each carries a claim that is sometimes TRUE, and *"an occurrence must be REGISTERED with a reason in the consuming gate's allowlist"*. The hub gate has `ALLOWLIST_EN`; `app-catalog-felhom.eu/scripts/check-copy-i18n.py` has none, so the only ways past it are to reword or to bypass the gate — and a catalog app whose English genuinely says a file can be restored (a backup app, a versioned document store) has no honest third option. **Fix shape:** an `ALLOWLIST_EN` of `(app, path, reason)` in the gate, a decoy proving a REGISTERED occurrence passes and an unregistered one still convicts, and a check that every entry still matches something (a stale allowlist entry was R-299's shape, and the retrieval gate has gone red on stale entries before). | **READY - rank P3-LOW; owner: CC (catalog)** |
+| **R-595** | **[P2-MEDIUM] The catalog's new copy gate could not RUN in CI at all — six pushes red, six alarm mails, while the local hook was green.** FOUND 2026-09-20 by reading the operator's inbox at the start of localisation slice 6 — **not** by anything in the session that caused it, which is the point worth keeping. Every one of slice 5's six catalog pushes (`e81d41e`, `d9aa02a`, `0695d8e`, `5fe70d1`, `1f80650`, `de392cd`) produced *[felhom CI] gates FAILED*, and the alarm's own text says what that means: *"If the local pre-push hook was GREEN for this commit, then CI and the hook disagree — that is a finding about the gates themselves, not about CI, and it outranks whatever the push was for."* **Cause, found by CONTRAST:** `check-copy-i18n.py` imports PyYAML and is the only gate in that repo importing anything outside the standard library; `.gitea/workflows/gates.yml` states in its own header that the runner is *"a host-mode container with python3 and git and nothing else"*. The gate raised `ImportError` before checking anything, so the runner exited non-zero on every push, clean ones included. **FIXED the same session (catalog `18a6d2d`, CI job 791 SUCCESS, verified by id):** a DEGRADED MODE rather than a skip — without PyYAML the gate runs the check that needs no parser and matters most (every frozen Hungarian string must still occur verbatim in its app's bytes) and prints in full what it did NOT check. Measured first: 1 030 of 1 032 frozen strings appear byte-for-byte in the raw files; the two that do not are romm help_texts whose YAML escapes an inner double quote, so the escaped spelling is accepted too — 1 032 of 1 032 found, so the degraded check convicts nothing honest. Five new decoy cases run with PyYAML shadowed by a module that refuses to import, i.e. what CI actually executes. **The general form, and the reason this is P2 rather than P3:** a new gate is written and tested on the machine that has every library, and the runner deliberately has none. **Nothing in the pre-push hook can catch that** — the hook runs on the same rich machine. The only detector is the alarm mail, and it worked; what failed is that six of them went unread for two hours inside the session that caused them. | **CLOSED 2026-09-20 — degraded mode; CI job 791 green** |
| **R-537** | **[P1-HIGH] The app-backup page labels the tier-1 backup „DB + Konfig + Adatok" and prints the app's data-drive size next to it — but the tier-1 unit contains NO drive-side app data at all.** MEASURED 2026-09-16 on the drill box (fresh install, controller 0.243.0, one drive, tier 2 and tier 3 both „Nincs beállítva"): five photos (3 000 000 B) were uploaded into Nextcloud through its own WebDAV interface, then the customer-visible „Mentés most" was pressed (`POST /api/backup/run` → 200, the unit grew 25 337 B → 978 MB). The resulting unit's `manifest.json` lists `db-dumps` + three **docker volume** dumps and nothing else; listing the 781 MB `nextcloud_nextcloud_html.tar` (29 346 entries, positive control `version.php` = 3 hits) gives **`Fotok` = 0 and `nyaralas` = 0**, and `./data/` is the empty bind-mount point. A `find` over the whole `backups/` tree for `*appdata*` / `*Fotok*` returns nothing. The page nevertheless renders „1. mentés … DB + Konfig + Adatok" and „Nextcloud Adatlemez 65.1 MB" — a size measured on exactly the data it does not copy (`internal/web/handlers.go:1176-1178`, `BackupContents`). **This is a truth defect, not a design defect:** `07-backup-architecture.md` §6.2 places nextcloud's file leg at **Tier 2 and Tier 3 only**, and its „[FACT] What the whole-guest tiers do NOT carry" says `mp8 /mnt/felhom-drives` is out of vzdump scope (confirmed live: „excluding bind mount point mp8 … (not a volume)"). So on a one-drive box with no off-site tier — the state every fresh install starts in — the household's files are in **no backup**, while the page says „Adatok". Same family as R-517/R-518. **Fix shape:** render tier-1 contents from the capture set actually written (`ComputeCaptureSet`), so a unit with no file leg reads „DB + Konfig" and the drive size is not shown beside it; and say on the page that the app's files need tier 2 or tier 3. Evidence: `audits/evidence-drill-0243-2026-09-16/phase2-f10.txt`. **CLOSED 2026-09-16 — controller v0.244.0, proven live.** The contents label is computed PER TIER from what that tier captures: Tier 1 says „Adatok" only when the app's data really is in the volumes the unit captured, and a class-A app carries one sentence saying where its files ARE protected. Proven on demo-hp through the page the customer opens: Paperless-ngx reads „1. mentés … DB + Konfig" with „Az alkalmazás fájljait a távoli másolat (és a második meghajtó) védi …", while its „2. mentés" row still reads „DB + Konfig + Adatok". Red-proof: restoring the old app-shaped label fails `TestAppBackupRows_Tier1LabelDoesNotClaimFilesItCannotHold`. **RE-PROVEN 2026-09-16 on a FRESH box** (installed from the built ISO 1.28.0, controller 0.244.0, off-site on by default): the Nextcloud row read „1. mentés … DB + Konfig" with the new sentence, „2. mentés … Nincs 2. (off-drive) másolat", „3. mentés Sikeres restic → …your-storagebox.de"; „DB + Konfig + Adatok" appeared ZERO times while the local unit held no file leg. | **CLOSED 2026-09-16 — controller v0.244.0 (proven live on demo-hp)** |
| **R-538** | **[P1-HIGH] A tier-1 app restore reports plain success and leaves Nextcloud listing files whose bytes were never in the backup — and it destroys the app's own trash, the customer's last copy.** MEASURED 2026-09-16 on the drill box, F10 („a child deletes the photo folder"): the five photos were deleted through Nextcloud (DELETE 204, PROPFIND 404), then restored through the page exactly as a customer would (`POST /backup/restore` `stack_name=nextcloud` `snapshot_id=helyi` → 302, finished in **35 s**, „A(z) nextcloud: 3 adatkötet és az adatbázis visszaállítva — az alkalmazás újraindult."). Afterwards the folder is back and **lists all five photos**, and **none of them opens**: `GET nyaralas-1..5` = 404 / 503×4 with `Sabre\DAV\Exception\NotFound`, while the positive controls at the same moment pass (`status.php` 200, WebDAV PUT 201, GET 200). Cause: the replayed MariaDB dump (11:01:45Z) knows the photos, the bytes live on `mp8` and were never captured (R-537). **Worse:** the bytes were still on the drive in Nextcloud's own trash (`appdata/nextcloud/admin/files_trashbin/files/Fotok.d1789556707/nyaralas-1..5.jpg`, all five present) and the restored database no longer references them — the trash listing comes back **empty**, so „restore from trash", the one route that would have worked, is gone. The customer is left with five unopenable photos, a success message, and no warning. **Fix shape:** before replaying a database whose app has an uncaptured file leg, refuse or warn („ennek az alkalmazásnak a fájljai nincsenek ebben a mentésben — a visszaállítás után a fájlok hiányozni fognak"); and never present a DB-only restore of a class-A app as a complete one. Evidence: `audits/evidence-drill-0243-2026-09-16/phase2-f10.txt`. **CLOSED 2026-09-16 — controller v0.244.0, proven live.** A unit restore refuses before anything is touched when the unit cannot return the app's drive-side files, and names the route that can. Fired live on demo-hp: `POST /backup/restore` for paperless-ngx → 302 with „Ez a mentés nem tartalmazza az alkalmazás fájljait, ezért nem állítjuk vissza az adatbázist föléjük — a fájlok így a helyükön maradnak. A fájlok a távoli másolatból állíthatók vissza …", and the app read `running` before AND after, so nothing was stopped and no trash was made unreachable. The database-and-settings-only path exists as a separately worded second step. Red-proof: disabling the guard fails `TestUnitRestore_RefusesWhenTheUnitCannotHoldTheFiles`. **RE-PROVEN 2026-09-16 on a FRESH box, and this time the refusal had somewhere to point:** after five photos were deleted, `POST /backup/restore` was refused with „…a fájlok így a helyükön maradnak. A fájlok a távoli másolatból állíthatók vissza: … „Teljes visszaállítás (fájlok + adatbázis)"", the app read `running` before AND after, and the wastebasket was untouched. The off-site route then returned all five photos — 200 with the exact uploaded sizes and sha256 IDENTICAL to the originals, 5/5, with a negative control. Evidence: `audits/evidence-backup-promise-2026-09-16/phaseE-photos.txt`. | **CLOSED 2026-09-16 — controller v0.244.0 (proven live on demo-hp)** |
| **R-525** | **[P3-LOW] FileBrowser has its own login; putting it behind the dashboard session (traefik forwardAuth or Quantum proxy auth) is a new mechanism nobody has measured.** Filed 2026-09-15 by the P1-fixes task (B.5). R-513 closed the default-password hole with a generated password; a household still has two logins. **What it needs:** a spike on a scratch guest — forwardAuth to the controller session, and what FileBrowser Quantum does with a trusted header. | **READY — rank P3-LOW; owner: CC (spike)** |
diff --git a/documentation/runbooks/VOLUNTEER-first-hour.en.md b/documentation/runbooks/VOLUNTEER-first-hour.en.md
new file mode 100644
index 00000000..ef276de2
--- /dev/null
+++ b/documentation/runbooks/VOLUNTEER-first-hour.en.md
@@ -0,0 +1,171 @@
+# Felhom — your first hour (for volunteer testers)
+
+> **ENGLISH TWIN of `VOLUNTEER-first-hour.md`, written by localisation slice 6 (R-561, 2026-09-2X).**
+> Same sections, same order, same steps, same warnings. **It is a translation, not a rewrite:** where
+> the Hungarian says something that is stale or wrong, the English says what the screen actually
+> shows and the difference is a register row, named in the session report — never a silent fix.
+> The operator chooses the channel and approves the text. This header is for the operator.
+>
+> **Screen names are the dashboard's own English** (`controller/internal/i18n/locales/en.json`):
+> Launcher, Dashboard, Apps, Storage, Backup, Sharing, System monitor, Settings. The console text is
+> the bilingual banner's English block, verbatim. The download step points at the English download
+> page, `felhom.eu/en/download` (published 2026-09-18, R-559 — same installer and same checksum as
+> the Hungarian page; a site gate refuses the two pages naming different ones).
+>
+> **What the operator must still do before sending this** — unchanged from the Hungarian, and listed
+> there in full: create the customer **with the language set to English**, create the Cloudflare
+> tunnel and paste its token (R-494 — without it the dashboard address does not open), and hand over
+> the Owner passphrase out of band.
+
+---
+
+## What the operator does first (not the volunteer's job)
+
+1. Creates the customer on the hub (name, e-mail, domain), **with the language set to English** — the
+ claim e-mail, the bind page and the box's first screen all follow that setting.
+ The hub **sends the connect e-mail by itself** when the customer has an e-mail address and no box
+ yet (at creation, when an address is added, and when an earlier box is deleted). There is no
+ button to press; the customer page shows when it went out (R-509, 2026-09-15).
+2. **Creates the Cloudflare tunnel and enters its token** on the customer's page — without it the
+ dashboard address does not open (R-494).
+3. **Hands over the five-word "Owner passphrase" in person or in a message.** No e-mail contains it.
+
+## What you will need
+
+- A machine where **all data will be erased** (the install overwrites the chosen disk completely).
+- A USB stick of at least 2 GB.
+- A network cable to your router.
+- The **Owner passphrase** (5 words) you received from Felhom.
+- The e-mail account you gave to Felhom.
+
+## 1. Download the installer (~1 minute)
+
+Open **https://felhom.eu/en/download** and download the installer it names (about 1.7 GB). The page
+also gives the file's checksum (SHA-256).
+
+Write it to a USB stick (Windows: Rufus, **in "DD" mode**; Mac/Linux: balenaEtcher).
+
+## 2. Install (~15 minutes, about 3 of them copying)
+
+Start the machine from the USB stick. **The installer screens are in English** — that is expected.
+
+| Screen | What to do |
+|---|---|
+| Felhom logo, two lines | Choose **"Felhom telepítés / Install Felhom"** (or wait; it starts by itself). The second line, **"… (szöveges mód) / … (text mode)"**, installs exactly the same thing without the graphics — take it if the first one shows nothing. |
+| END USER LICENSE AGREEMENT | **I agree** |
+| Target harddisk | **The installer never chooses for you.** It lists the machine's disks with their size and type; choose the one **the system should go on — that disk will be erased.** Unplug your external backup drive for the install. If there are several internal disks and you do not know which is the right one, stop and ask the operator. |
+| Country / Timezone / Keyboard | Leave as they are |
+| Root password | Enter a password of at least 8 characters, twice. **You do not need to remember it** — Felhom replaces it after the first start. |
+| Administrator email | Type your own e-mail address (instead of `mail@example.invalid`) |
+| Hostname (FQDN) | **Change it**, because the default is refused. Type: `felhom.` (the domain you got from Felhom) |
+| IP address, Gateway, DNS | Leave as they are |
+| Summary | **Install** |
+| "Installation finished — reboot now?" | **Remove the USB stick**, then **Reboot now** |
+
+## 3. The first start (~2 minutes)
+
+**This screen is bilingual** — Hungarian first, English under it. About 2 minutes later it shows:
+
+> Felhom — the box is ready and waiting to be paired.
+> Pairing code: XXX-XXX
+
+Write the **pairing code** down. It is printed once in each language; both are the same code.
+
+## 4. Connect the box to your account (~2 minutes)
+
+1. Open the e-mail with the subject **"[Felhom] Connect your Felhom box"** and follow the link in it
+ (valid for 7 days).
+2. Enter:
+ - the **pairing code** from the box's screen;
+ - the **Owner passphrase** (5 words) you received from the Felhom operator.
+3. Leave the box switched on. The next e-mail arrives in **about 3 minutes**.
+
+*(Note: the box's screen keeps showing the pairing code after it is connected — this is a known
+fault, and you do not need to connect it again.)*
+
+## 5. Set up the dashboard (~1 minute)
+
+1. Open the e-mail with the subject **"[Felhom] Your Felhom server is up — setup code"**.
+2. Follow the address in it (`https://felhom.`).
+ If the address does not open, tell the operator.
+3. On the **"Set up the server"** page enter the **setup code** (3 words, like `word-word-word`) and
+ choose a password of **at least 12 characters**. This becomes your dashboard password.
+4. If the code did not arrive: **"Did not get the code? Ask for a new one"** — it always goes to the
+ same e-mail address.
+
+## 6. Install your first two apps (~2 minutes)
+
+1. On the dashboard: **Apps**. Find **BookStack** (a family wiki) and **PrivateBin** (encrypted
+ notes), and select **Install**.
+2. On the install page the only question is the **subdomain** — leave the default (`wiki`, `paste`).
+ You do not need to write down the "Automatically generated values".
+3. **Start the install.** BookStack takes about a minute, PrivateBin about 20 seconds.
+
+## 7. The recovery code (~2 minutes) — do not skip this
+
+The box sends an **encrypted** copy of your files to Felhom's remote storage. **Only you** get the
+key to that encryption: it is the **recovery code**. Until you create it, **the remote backup does
+not start**.
+
+**When the yellow bar appears (a few minutes after the setup)**, that is the moment. The box spends
+its first minutes preparing the remote storage, and until then it cannot create the code — which is
+why the bar only appears once it can. The bar says:
+"Remote backup is paused until you create your recovery code."
+If the bar is not there yet, install your first apps (the step before) and look again.
+If you open the page too early it says "The box is still getting ready — … in a few minutes …", and
+it refreshes itself.
+
+1. **Backup → Remote backup**, or simply select **"Create recovery code"** on the bar.
+2. Enter your dashboard password and start it. About half a minute.
+3. The code is shown **once and once only**. **Write it on paper** and put it where you keep your
+ important documents. A photo on your phone is not enough if the phone is lost too.
+
+> ⚠ **Felhom cannot get this code back for you.** Not because we will not: your copies are encrypted
+> so that we ourselves cannot open them. If the code is lost, the remote copies remain, but
+> **nobody — not us either — can open them again**.
+
+Once you are done, the bar disappears and the remote backup starts by itself.
+
+## 8. Signing in to the apps for the first time
+
+- **BookStack:** on the app's page the "First steps" give the address as `wiki.DOMAIN` — put your own
+ domain in place of DOMAIN (known fault). Sign in: `admin@admin.com` / `password`. Change it
+ **straight away**: your profile at the top right → Settings → password and e-mail.
+- **PrivateBin:** there is no sign-in. Type your text, select **Send**, and share the link you get —
+ the key is in the link, and the server does not see the content.
+
+## 9. Backups
+
+- **Backup → Overview:** you will see two yellow warnings ("only one copy is made", "it is on the
+ same disk") — **both are true**: until there is a second drive or a remote backup, this does not
+ protect you against a disk failure.
+- **Backup → Apps → Back up now:** about 20 seconds, after which the date updates.
+- *An individual app's "Backup 2 settings" page may say that its data is "already part of the full
+ system backup (PBS)" — this is not true on every box (known fault). The Overview page is the
+ accurate one.*
+
+## 10. Restoring
+
+**Backup → Restore:** choose the app and the backup, tick the "I understand" box, **Start restore**.
+The app stops for about half a minute, then starts again in the state of the last backup.
+
+## 11. Removing an app
+
+**Apps:** first **Stop**, then **Remove** appears. The dialog lists what will be deleted in any case,
+and you can also tick the box to delete the backups.
+
+## 12. A power cut
+
+If the power goes, the box comes back by itself; after about 2 minutes every app is running on the
+same version it was before. You will have to sign in to the dashboard again.
+
+## 13. If you mistype the code
+
+The setup page rejects it with "Wrong or expired code" — type it again. After **five** wrong
+attempts it locks for 15 minutes ("Too many attempts — try again in 15 minutes."). You can ask for a
+new code with the "Did not get the code? Ask for a new one" link, and the sign-in page's "Forgot
+password" link leads to the same place.
+
+## If you get stuck
+
+Write to **support@felhom.eu**, and send a screenshot if you can.
diff --git a/documentation/runbooks/VOLUNTEER-first-hour.md b/documentation/runbooks/VOLUNTEER-first-hour.md
index 9ca4b75d..9b667502 100644
--- a/documentation/runbooks/VOLUNTEER-first-hour.md
+++ b/documentation/runbooks/VOLUNTEER-first-hour.md
@@ -1,5 +1,7 @@
# Felhom — az első óra (önkéntes tesztelőknek)
+> **English version: [`VOLUNTEER-first-hour.en.md`](VOLUNTEER-first-hour.en.md)** (localisation slice 6, R-561). A twin, not a rewrite: same sections, same steps, same warnings.
+
> **STOPGAP, written by the 2026-09-14 drill (R-493) — NOT yet sent to anyone.** No customer-facing
> install instruction existed when the drill started, so this is the minimal honest one, built from
> what the product really shows on 0.242.0. The operator chooses the channel and approves the text.
diff --git a/documentation/tests/golden-0.258.0-2026-09-20/README.md b/documentation/tests/golden-0.258.0-2026-09-20/README.md
new file mode 100644
index 00000000..cf6440f8
--- /dev/null
+++ b/documentation/tests/golden-0.258.0-2026-09-20/README.md
@@ -0,0 +1,42 @@
+# Golden 0.258.0 — baked, published and vouched 2026-09-20
+
+Baked as **Phase 0 of localisation slice 6's fresh-install drill** (R-561). The cadence ruling
+(R-468, 2026-09-13) is *weekly, and always before any drill or fresh install* — this is the second
+half of that sentence. It also means the drill's box boots at the version under test instead of
+installing an eleven-version-old controller and self-updating.
+
+| | |
+|---|---|
+| `GOLDEN_VERSION` | 0.258.0 (controller image `gitea.dooplex.hu/admin/felhom-controller:0.258.0`) |
+| `GOLDEN_SHA256` | `49d765d29dc013e8e270204cadaa6f3df96e8f26b11ff18796f5cab3efe6a6cc` |
+| archive | 654 443 692 B · rootfs 32G + one data volume 24G @ `/var/lib/felhom` |
+| template | `debian-13-standard_13.6-1_amd64.tar.zst`, container architecture **amd64** |
+| markers | `docker OK (overlay2` 1 · `including mount point rootfs` 1 · `including mount point mp0` 1 · `upload OK (HTTP 201)` 1 · `excluding` 0 · `FATAL` 0 |
+| token leak | **0** in the committed `bake.log`; the planted control (a throwaway copy with the token appended) read **1**, so the grep was shown to work before the 0 was believed |
+| registry | `…/generic/felhom-golden/0.258.0/golden.tar.zst` → **200**, and only then teardown |
+| teardown | CT 9100 destroyed `--purge`; token, runner and log shredded inside the VM (`/root` left with only its dotfiles); qemu exited; `drill.qcow2` reverted to `virgin` |
+| vouched | `Artifact manifest set: agent=0.132.0 golden=0.258.0 min_agent="0.131.0" wrapper_sha=false` — read back from the form: golden `49d765d2…` selected, agent `0.132.0` selected, `min_agent` `0.131.0` |
+
+**The three-field vouch, and why all three moved together:** `golden_version` → 0.258.0,
+`agent_version` → 0.132.0 (unchanged, and ≥ the release's `MinAgent`), `min_agent` → 0.131.0, read
+from v0.258.0's CHANGELOG header. `golden_version` alone would ship a controller onto an older agent
+than it declares it needs.
+
+**One run, no aborted attempts.** The 0.246.0 bake's two traps were both avoided by following its
+record: the template was chosen by NAME (`debian-13-standard_13.6-1_amd64`), not by a `sort -V` that
+ranks arm64 after amd64; and the bake ran as a transient unit inside the VM, so nothing on the host
+could kill it.
+
+## The waiver was NOT retired, and that is deliberate
+
+The task that commissioned this bake said the drill "retires" `golden-waiver.yml`. It is left in
+place, and here is the one line saying why: the waiver is the **mechanism** of operator ruling R-468
+(goldens on a weekly cadence, the gate advisory in between), not a note about this particular golden.
+Deleting it would turn the next release without a bake red immediately — that is a reversal of a
+documented operator decision, which is not a session's to make. **It is also not load-bearing today:**
+with golden 0.258.0 recorded, `golden_currency_gate.py` passes on its own merits, and the waiver's
+line in the output reads "VALID until 2026-09-27 (7 days left)" beside a gate that no longer needs it.
+
+## Files
+
+- `bake.log` — the full bake, copied off the VM before teardown (R-320), token-grepped with a control.
diff --git a/documentation/tests/golden-0.258.0-2026-09-20/bake.log b/documentation/tests/golden-0.258.0-2026-09-20/bake.log
new file mode 100644
index 00000000..ebe579c7
--- /dev/null
+++ b/documentation/tests/golden-0.258.0-2026-09-20/bake.log
@@ -0,0 +1,325 @@
+[golden] build-golden.sh v3.0.0 — baking controller gitea.dooplex.hu/admin/felhom-controller:0.258.0
+[golden] creating build LXC 9100 (nesting=1,keyctl=1, unprivileged; rootfs 32G + ONE data volume 24G @ /var/lib/felhom, backup=1) …
+ Logical volume "vm-9100-disk-0" created.
+ Logical volume pve/vm-9100-disk-0 changed.
+Creating filesystem with 8388608 4k blocks and 2097152 inodes
+Filesystem UUID: 324a27fb-a632-4580-8357-bbdcfd413474
+Superblock backups stored on blocks:
+ 32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
+ 4096000, 7962624
+ Logical volume "vm-9100-disk-1" created.
+ Logical volume pve/vm-9100-disk-1 changed.
+Creating filesystem with 6291456 4k blocks and 1572864 inodes
+Filesystem UUID: 9449d6f8-e1ff-4df5-883f-1cd013d97f3d
+Superblock backups stored on blocks:
+ 32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
+extracting archive '/var/lib/vz/template/cache/debian-13-standard_13.6-1_amd64.tar.zst'
+Total bytes read: 553512960 (528MiB, 125MiB/s)
+Detected container architecture: amd64
+Creating SSH host key 'ssh_host_ecdsa_key' - this may take some time ...
+done: SHA256:YGAFums3afIANrsQrvTzzn9ICrqYPezHO80MXPnRSOU root@felhom-golden
+Creating SSH host key 'ssh_host_ed25519_key' - this may take some time ...
+done: SHA256:DC70LoTsTJ+2ND65hWWSCKuw4kGaTEUFtDmsCsrySac root@felhom-golden
+Creating SSH host key 'ssh_host_rsa_key' - this may take some time ...
+done: SHA256:OOChZsF9hcwJJ8YQmn5rAf5tGAxHDK7VR9XAuKOBtmg root@felhom-golden
+[golden] starting + installing Docker (official repo, trixie channel) …
+apt-listchanges: Can't set locale; make sure $LC_* and $LANG are correct!
+perl: warning: Setting locale failed.
+perl: warning: Please check that your locale settings:
+ LANGUAGE = (unset),
+ LC_ALL = (unset),
+ LC_CTYPE = (unset),
+ LC_NUMERIC = (unset),
+ LC_COLLATE = (unset),
+ LC_TIME = (unset),
+ LC_MESSAGES = (unset),
+ LC_MONETARY = (unset),
+ LC_ADDRESS = (unset),
+ LC_IDENTIFICATION = (unset),
+ LC_MEASUREMENT = (unset),
+ LC_PAPER = (unset),
+ LC_TELEPHONE = (unset),
+ LC_NAME = (unset),
+ LANG = "en_US.UTF-8"
+ are supported and installed on your system.
+perl: warning: Falling back to the standard locale ("C").
+locale: Cannot set LC_CTYPE to default locale: No such file or directory
+locale: Cannot set LC_MESSAGES to default locale: No such file or directory
+locale: Cannot set LC_ALL to default locale: No such file or directory
+apt-listchanges: Can't set locale; make sure $LC_* and $LANG are correct!
+perl: warning: Setting locale failed.
+perl: warning: Please check that your locale settings:
+ LANGUAGE = (unset),
+ LC_ALL = (unset),
+ LC_CTYPE = (unset),
+ LC_NUMERIC = (unset),
+ LC_COLLATE = (unset),
+ LC_TIME = (unset),
+ LC_MESSAGES = (unset),
+ LC_MONETARY = (unset),
+ LC_ADDRESS = (unset),
+ LC_IDENTIFICATION = (unset),
+ LC_MEASUREMENT = (unset),
+ LC_PAPER = (unset),
+ LC_TELEPHONE = (unset),
+ LC_NAME = (unset),
+ LANG = "en_US.UTF-8"
+ are supported and installed on your system.
+perl: warning: Falling back to the standard locale ("C").
+locale: Cannot set LC_CTYPE to default locale: No such file or directory
+locale: Cannot set LC_MESSAGES to default locale: No such file or directory
+locale: Cannot set LC_ALL to default locale: No such file or directory
+[golden] baking daemon.json: classic overlay2 driver (containerd-snapshotter OFF) + log rotation …
+[golden] wiring the single data volume (R-165 variant V-c): /var/lib/felhom/{docker,sys_drive} -> binds …
+[golden] verifying Docker works in the build guest (storage driver should be overlay2 on the ext4 data volume) …
+Unable to find image 'hello-world:latest' locally
+latest: Pulling from library/hello-world
+4f55086f7dd0: Pulling fs layer
+4f55086f7dd0: Download complete
+4f55086f7dd0: Pull complete
+Digest: sha256:5e23090353324d887c48ad5e5c56d294eab81588df9605b07d1afe895f9cc8f8
+Status: Downloaded newer image for hello-world:latest
+ docker OK (overlay2; data-root /var/lib/docker)
+ /var/lib/docker is a real mount: /dev/mapper/pve-vm--9100--disk--1[/docker] ext4
+ /mnt/sys_drive is a real mount: /dev/mapper/pve-vm--9100--disk--1[/sys_drive] ext4
+ both paths are ONE filesystem: /dev/mapper/pve-vm--9100--disk--1 23317576
+[golden] baking the in-guest controller image gitea.dooplex.hu/admin/felhom-controller:0.258.0 (no registry cred at deploy) …
+
+WARNING! Your credentials are stored unencrypted in '/root/.docker/config.json'.
+Configure a credential helper to remove this warning. See
+https://docs.docker.com/go/credential-store/
+
+0.258.0: Pulling from admin/felhom-controller
+a8ac7f6c67ab: Pulling fs layer
+bf30769d36e7: Pulling fs layer
+044b66fbe46c: Pulling fs layer
+b5c41a28e83f: Pulling fs layer
+29f115062265: Pulling fs layer
+c58d135450d8: Pulling fs layer
+b5c41a28e83f: Waiting
+29f115062265: Waiting
+c58d135450d8: Waiting
+a8ac7f6c67ab: Verifying Checksum
+a8ac7f6c67ab: Download complete
+b5c41a28e83f: Verifying Checksum
+b5c41a28e83f: Download complete
+29f115062265: Verifying Checksum
+29f115062265: Download complete
+044b66fbe46c: Verifying Checksum
+044b66fbe46c: Download complete
+c58d135450d8: Verifying Checksum
+c58d135450d8: Download complete
+bf30769d36e7: Verifying Checksum
+bf30769d36e7: Download complete
+a8ac7f6c67ab: Pull complete
+bf30769d36e7: Pull complete
+044b66fbe46c: Pull complete
+b5c41a28e83f: Pull complete
+29f115062265: Pull complete
+c58d135450d8: Pull complete
+Digest: sha256:3a66661fc1876a3107d8413c6cf7e9204141d1cc5dcafb226fcb6890cb62a926
+Status: Downloaded newer image for gitea.dooplex.hu/admin/felhom-controller:0.258.0
+gitea.dooplex.hu/admin/felhom-controller:0.258.0
+[golden] asking the controller which infra images it manages …
+[golden] baking infra images (4): traefik:v3.6.7 cloudflare/cloudflared:2026.6.0 gtstef/filebrowser:1.3.3-stable gitea.dooplex.hu/admin/felhom-samba:1.1.0 …
+v3.6.7: Pulling from library/traefik
+589002ba0eae: Pulling fs layer
+ef63511ea6cc: Pulling fs layer
+0738e5cb835e: Pulling fs layer
+3e6813f70c64: Pulling fs layer
+3e6813f70c64: Waiting
+589002ba0eae: Verifying Checksum
+589002ba0eae: Download complete
+ef63511ea6cc: Download complete
+589002ba0eae: Pull complete
+0738e5cb835e: Verifying Checksum
+0738e5cb835e: Download complete
+3e6813f70c64: Verifying Checksum
+3e6813f70c64: Download complete
+ef63511ea6cc: Pull complete
+0738e5cb835e: Pull complete
+3e6813f70c64: Pull complete
+Digest: sha256:a9890c898f379c1905ee5b28342f6b408dc863f08db2dab20e46c267d1ff463a
+Status: Downloaded newer image for traefik:v3.6.7
+docker.io/library/traefik:v3.6.7
+2026.6.0: Pulling from cloudflare/cloudflared
+47de5dd0b812: Pulling fs layer
+c172f21841df: Pulling fs layer
+99515e7b4d35: Pulling fs layer
+99ba982a9142: Pulling fs layer
+d6b1b89eccac: Pulling fs layer
+2780920e5dbf: Pulling fs layer
+7c12895b777b: Pulling fs layer
+3214acf345c0: Pulling fs layer
+52630fc75a18: Pulling fs layer
+dd64bf2dd177: Pulling fs layer
+b839dfae01f6: Pulling fs layer
+ebddc55facdc: Pulling fs layer
+bdfd7f7e5bf6: Pulling fs layer
+2d4d7adf6272: Pulling fs layer
+40008157d8d2: Pulling fs layer
+bd8962e29291: Pulling fs layer
+cac2ae0193cb: Pulling fs layer
+74d1dac84ecc: Pulling fs layer
+d6b1b89eccac: Waiting
+2780920e5dbf: Waiting
+7c12895b777b: Waiting
+3214acf345c0: Waiting
+52630fc75a18: Waiting
+dd64bf2dd177: Waiting
+b839dfae01f6: Waiting
+ebddc55facdc: Waiting
+bdfd7f7e5bf6: Waiting
+2d4d7adf6272: Waiting
+40008157d8d2: Waiting
+bd8962e29291: Waiting
+99ba982a9142: Waiting
+cac2ae0193cb: Waiting
+74d1dac84ecc: Waiting
+47de5dd0b812: Download complete
+c172f21841df: Download complete
+47de5dd0b812: Pull complete
+99515e7b4d35: Verifying Checksum
+99515e7b4d35: Download complete
+99ba982a9142: Verifying Checksum
+99ba982a9142: Download complete
+d6b1b89eccac: Verifying Checksum
+d6b1b89eccac: Download complete
+2780920e5dbf: Verifying Checksum
+2780920e5dbf: Download complete
+7c12895b777b: Verifying Checksum
+7c12895b777b: Download complete
+3214acf345c0: Verifying Checksum
+3214acf345c0: Download complete
+52630fc75a18: Verifying Checksum
+52630fc75a18: Download complete
+dd64bf2dd177: Verifying Checksum
+dd64bf2dd177: Download complete
+b839dfae01f6: Verifying Checksum
+b839dfae01f6: Download complete
+ebddc55facdc: Verifying Checksum
+ebddc55facdc: Download complete
+c172f21841df: Pull complete
+bdfd7f7e5bf6: Verifying Checksum
+bdfd7f7e5bf6: Download complete
+bd8962e29291: Verifying Checksum
+bd8962e29291: Download complete
+2d4d7adf6272: Verifying Checksum
+2d4d7adf6272: Download complete
+cac2ae0193cb: Verifying Checksum
+cac2ae0193cb: Download complete
+99515e7b4d35: Pull complete
+40008157d8d2: Verifying Checksum
+40008157d8d2: Download complete
+74d1dac84ecc: Verifying Checksum
+74d1dac84ecc: Download complete
+99ba982a9142: Pull complete
+d6b1b89eccac: Pull complete
+2780920e5dbf: Pull complete
+7c12895b777b: Pull complete
+3214acf345c0: Pull complete
+52630fc75a18: Pull complete
+dd64bf2dd177: Pull complete
+b839dfae01f6: Pull complete
+ebddc55facdc: Pull complete
+bdfd7f7e5bf6: Pull complete
+2d4d7adf6272: Pull complete
+40008157d8d2: Pull complete
+bd8962e29291: Pull complete
+cac2ae0193cb: Pull complete
+74d1dac84ecc: Pull complete
+Digest: sha256:ba461b8aa9c042156dbd39c38657fe7431bafa063220eab8d5330a523863da9f
+Status: Downloaded newer image for cloudflare/cloudflared:2026.6.0
+docker.io/cloudflare/cloudflared:2026.6.0
+1.3.3-stable: Pulling from gtstef/filebrowser
+6a0ac1617861: Pulling fs layer
+ef8806083e82: Pulling fs layer
+b74107c861c7: Pulling fs layer
+adc935def003: Pulling fs layer
+4f4fb700ef54: Pulling fs layer
+18695ccc900a: Pulling fs layer
+45d119d5c397: Pulling fs layer
+dac52db4fc51: Pulling fs layer
+6d598f86b2f2: Pulling fs layer
+8aa349c8396c: Pulling fs layer
+dac52db4fc51: Waiting
+adc935def003: Waiting
+4f4fb700ef54: Waiting
+18695ccc900a: Waiting
+45d119d5c397: Waiting
+6d598f86b2f2: Waiting
+8aa349c8396c: Waiting
+b74107c861c7: Verifying Checksum
+b74107c861c7: Download complete
+6a0ac1617861: Verifying Checksum
+6a0ac1617861: Download complete
+ef8806083e82: Verifying Checksum
+ef8806083e82: Download complete
+adc935def003: Verifying Checksum
+adc935def003: Download complete
+4f4fb700ef54: Verifying Checksum
+4f4fb700ef54: Download complete
+45d119d5c397: Verifying Checksum
+45d119d5c397: Download complete
+dac52db4fc51: Verifying Checksum
+dac52db4fc51: Download complete
+18695ccc900a: Verifying Checksum
+18695ccc900a: Download complete
+6d598f86b2f2: Verifying Checksum
+6d598f86b2f2: Download complete
+8aa349c8396c: Verifying Checksum
+8aa349c8396c: Download complete
+6a0ac1617861: Pull complete
+ef8806083e82: Pull complete
+b74107c861c7: Pull complete
+adc935def003: Pull complete
+4f4fb700ef54: Pull complete
+18695ccc900a: Pull complete
+45d119d5c397: Pull complete
+dac52db4fc51: Pull complete
+6d598f86b2f2: Pull complete
+8aa349c8396c: Pull complete
+Digest: sha256:eb3733681db8757412632c61a99ad656f0d94ed6781bb2ea114b4d70babab78c
+Status: Downloaded newer image for gtstef/filebrowser:1.3.3-stable
+docker.io/gtstef/filebrowser:1.3.3-stable
+1.1.0: Pulling from admin/felhom-samba
+897d797d2723: Pulling fs layer
+3051591aa250: Pulling fs layer
+ce57a3f93416: Pulling fs layer
+fb94eeec2fe1: Pulling fs layer
+fb94eeec2fe1: Waiting
+ce57a3f93416: Verifying Checksum
+ce57a3f93416: Download complete
+fb94eeec2fe1: Verifying Checksum
+fb94eeec2fe1: Download complete
+897d797d2723: Verifying Checksum
+897d797d2723: Download complete
+3051591aa250: Verifying Checksum
+3051591aa250: Download complete
+897d797d2723: Pull complete
+3051591aa250: Pull complete
+ce57a3f93416: Pull complete
+fb94eeec2fe1: Pull complete
+Digest: sha256:1c17c09422bec0366d7cf0e0fcfc1486ba6c90334a0a5d5c851073a9342f8f10
+Status: Downloaded newer image for gitea.dooplex.hu/admin/felhom-samba:1.1.0
+gitea.dooplex.hu/admin/felhom-samba:1.1.0
+[golden] baking the controller-bootstrap unit (deploys the BAKED controller from the config mount) …
+Created symlink '/etc/systemd/system/multi-user.target.wants/felhom-controller-bootstrap.service' → '/etc/systemd/system/felhom-controller-bootstrap.service'.
+[golden] baking the controller-bootstrap PATH unit (starts the service on bootstrap-mount hot-plug — B1) …
+Created symlink '/etc/systemd/system/multi-user.target.wants/felhom-controller-bootstrap.path' → '/etc/systemd/system/felhom-controller-bootstrap.path'.
+[golden] baking the first-boot SSH host-key regeneration unit (F3) …
+Created symlink '/etc/systemd/system/multi-user.target.wants/felhom-regen-hostkeys.service' → '/etc/systemd/system/felhom-regen-hostkeys.service'.
+[golden] identity-clean + minimize …
+[golden] stop + archive …
+INFO: including mount point rootfs ('/') in backup
+INFO: including mount point mp0 ('/var/lib/felhom') in backup
+INFO: archive file size: 624MB
+INFO: Finished Backup of VM 9100 (00:00:39)
+[golden] DONE. golden archive volid: local:backup/vzdump-lxc-9100-2026_09_20-18_19_29.tar.zst (rootfs 32G + ONE data volume 24G @ /var/lib/felhom, all in the archive)
+[golden] publishing golden (654443692 bytes, sha256 49d765d29dc013e8…) → https://gitea.dooplex.hu/api/packages/admin/generic/felhom-golden/0.258.0/golden.tar.zst
+[golden] pre-delete existing: HTTP 404 (404/204 expected)
+[golden] upload OK (HTTP 201)
+GOLDEN_VERSION=0.258.0
+GOLDEN_SHA256=49d765d29dc013e8e270204cadaa6f3df96e8f26b11ff18796f5cab3efe6a6cc
+[golden] Record in the hub operator UI (Configs → Day-0 artifacts): golden 0.258.0 / 49d765d29dc013e8e270204cadaa6f3df96e8f26b11ff18796f5cab3efe6a6cc
+[golden] (the build guest 9100 is stopped; destroy it with: pct destroy 9100 --purge)