R-543: off-site ON by default is not off-site WORKING on day one
gates / gates (push) Successful in 21s

Measured on the fresh box: tier 3 sits at „Kulcsletétre vár" — the off-site copy is
paused until the household performs the key-escrow ceremony, and nothing asks them
to. POST /backup/offbox/run returns 302 and produces no snapshot; the controller log
shows only offsite-credential-retry.

That matters more after today, not less: the new default exists because a one-drive
box otherwise keeps the household's files in no tier at all, and the tier-1 row now
prints „Az alkalmazás fájljait a távoli másolat … védi". On day one that sentence
promises a copy that does not exist yet.

The good half, proven on the same page: tier 1 reads „DB + Konfig" with the new
sentence, and „DB + Konfig + Adatok" appears zero times — R-537 holds here too.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-09-16 19:21:28 +02:00
parent 0cfbfe709c
commit 63e2de9b9e
3 changed files with 65 additions and 0 deletions
@@ -344,3 +344,12 @@ urllib.error.HTTPError: HTTP Error 403: Forbidden
## the stack still pulling images. On 2026-09-16 morning the same moment produced
## „Alkalmazás telepítve: Mealie" — an install that was then killed 5 s later and never happened.
## The other half of the pair (app_deployed at COMPLETION) is checked when the stack reaches running.
## 2026-09-16T17:17:31Z the completion half of R-536:
2026/09/16 19:15:34 [INFO] Event from tester-1: app_deploy_started (info) — Alkalmazás telepítése elindult: Nextcloud
2026/09/16 19:16:23 [INFO] Event from tester-1: app_deployed (info) — Alkalmazás telepítve: Nextcloud
## THE PAIR, COMPLETE (R-536 proven live on a fresh box, both halves):
## 19:15:34 CEST app_deploy_started (info) — „Alkalmazás telepítése elindult: Nextcloud" <- the 202
## 19:16:23 CEST app_deployed (info) — „Alkalmazás telepítve: Nextcloud" <- 49 s later,
## when the stack was up
## Forty-nine seconds separate the acceptance from the installation. Before today those were the
## same instant, and an install that never finished kept the „telepítve" record for ever.
@@ -0,0 +1,55 @@
## 2026-09-16T17:17:39Z five photos into Nextcloud, through the app's own interface
app front door: status.php -> 200
MKCOL Fotok -> 201
PUT nyaralas-1.jpg (200000 B) -> 201
PUT nyaralas-2.jpg (400000 B) -> 201
PUT nyaralas-3.jpg (600000 B) -> 201
PUT nyaralas-4.jpg (800000 B) -> 201
PUT nyaralas-5.jpg (1000000 B) -> 201
total uploaded: 3000000 B in 5 files
listing:
nyaralas-1.jpg
nyaralas-2.jpg
nyaralas-3.jpg
nyaralas-4.jpg
nyaralas-5.jpg
## 2026-09-16T17:18:05Z pointing Nextcloud at the off-site tier
POST /backup/offbox/toggle app=nextcloud enabled=1 -> http=302
page now says: tkezés ↗ Biztonsági mentés — Távoli mentés enkicsifelhom.hu Aktív — nincs kijelölt alkalmazás 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üggetl
## 2026-09-16T17:18:31Z running the tier-1 leg (the local app backup) on the fresh box
POST /api/backup/run -> {"ok":true,"message":"Mentés elindítva"}
http=200
finished: {"ok":true,"data":{"db_dump":{"count":1,"duration":"22.245700003s","last_run":"2026-09-16T17:18:54.254096666Z","success":true},"enabled":true,"running":false}}
## 2026-09-16T17:19:20Z off-site toggle retried with the value the FORM uses (enabled=true, not 1)
POST toggle enabled=true -> http=302
app=nextcloud next-value=false (a toggle shows the value it would SET)
header: Biztonsági mentés — Távoli mentés — Felhom.eu Indítópult Vezérlőpult Alkalmazások Tárhely Meghajtók Hálózati tárhely Biztonsági mentés Áttekintés Táv
## THE TIER-1 LEG RAN on the fresh box, with the five photos already in place:
## POST /api/backup/run -> 200 {"ok":true,"message":"Mentés elindítva"}
## finished: db_dump count=1, duration 22.2 s, success=true
## (One database, because Nextcloud is the only app on this box. The unit capture rides the same run.)
## MY SHORTCUT, caught by the page rather than by luck: the first off-site toggle POST returned 302 —
## the SHAPE of success — and changed nothing, because I sent `enabled=1` while the form's own field
## carries `enabled=true`. Reading the per-app row instead of the redirect is what found it.
## 2026-09-16T17:19:41Z running the TIER-3 (off-site) leg — this is what carries the photos off the box
POST /backup/offbox/run -> http=302
status now: {"ok":true,"data":{"db_dump":{"count":1,"duration":"22.245700003s","last_run":"2026-09-16T17:18:54.254096666Z","success":true},"enabled":true,"running":false}}
## THE OFF-SITE TOGGLE IS ON for Nextcloud: the row's form now offers `enabled=false`, which is how a
## toggle says „currently on". Read from the row, not from the redirect — the first attempt returned
## 302 and changed nothing.
## So on this box, an hour after the default shipped: tier 3 provisioned by the hub automatically,
## and the household's one app pointed at it.
## 2026-09-16T17:2xZ THE BACKUP PAGE ON THE FRESH BOX — the good half and the gap, both measured:
## Nextcloud · Adatlemez · 62.2 MB
## „1. mentés Auto helyi Utolsó: 1 perce DB + Konfig"
## „Az alkalmazás fájljait a távoli másolat (és a második meghajtó) védi — ez a helyi mentés a
## beállításokat és az adatbázist tartalmazza."
## „2. mentés … Nincs 2. (off-drive) másolat"
## „3. mentés Kulcsletétre vár · A távoli mentés a titkosítási kulcs letétbe helyezéséig szünetel"
## „DB + Konfig + Adatok" appears ZERO times on this page — R-537 holds here too: the tier-1 row
## does not claim to hold the app's files.
## AND THE GAP (R-543): off-site is ON by default and PAUSED, waiting for the escrow ceremony that
## nothing asks the household to perform. `POST /backup/offbox/run` -> 302 and produced no snapshot;
## the controller log shows only `offsite-credential-retry`, no restic activity. So on day one the
## sentence above promises protection by a copy that does not exist yet.
+1
View File
@@ -725,6 +725,7 @@ class (an image `VOLUME` at an unmounted path) is still live — `immich-server`
| **R-540** | **[P3-LOW] The hub knows exactly ONE off-site pool box, so there is no rule for what happens when it fills.** Read from source 2026-09-16 while making off-site the default: `HETZNER_POOL_BOX_ID` is a single value, and every shared customer becomes a sub-account on that box. With off-site now ON for every new customer (hub v0.116.0) the box fills faster, and the fill warning (80%/90% of the box, `monitor/offsite.go`) tells the operator it is filling but nothing says which box a new customer should land on. **Needs a selection rule** (least-full, or explicit per-customer), not a bigger box. No customer is at risk today: the pool box read 0.3% full (2.7 GB of 1 TB), Σ shared quota 150 GB, oversub 0.15x. | **READY — rank P3-LOW; owner: CC (hub)** |
| **R-541** | **[P3-LOW] There is no path to move a customer between off-site boxes, or from shared to dedicated.** Read from source 2026-09-16: provisioning is idempotent-reuse keyed on the customer (`shared already provisioned for tester-1 (subaccount 311327)`), and a dedicated deprovision destroys the repository — so "move this customer" has no safe route today. It becomes reachable the moment R-540's second pool box exists, or when a customer outgrows the shared model. **Needs:** a move that copies the repository, re-keys, and only then releases the old sub-account — a new mechanism nobody has measured. | **READY — rank P3-LOW; owner: CC (hub) — design first** |
| **R-542** | **[P3-LOW] `/api/disks/candidates` offers a REGISTERED, in-use drive under „initialize".** MEASURED 2026-09-16 on the fresh box (controller 0.244.0): after `/dev/sdb` was formatted, mounted at `/mnt/felhom-drives/adatlemez` and registered as the default data drive, the endpoint still listed it under `initialize` (and again under `attach` with `already_mounted: null`). **Customer-invisible today:** the Meghajtók page filters correctly — its „Nem regisztrált meghajtók" section lists none, and the drive shows as „Adatlemez · Alapértelmezett · Aktív". So the defect is in the raw endpoint that feeds a FORMATTING flow, not in the page. **It also misleads a session:** I read „not mounted" off this endpoint and briefly filed a false finding against the product (corrected in `audits/evidence-backup-promise-2026-09-16/phaseE-freshbox.txt`). **Fix shape:** exclude paths the controller has registered from `initialize`, and set `already_mounted` from the real mount state rather than null. | **READY — rank P3-LOW; owner: CC (controller/agent)** |
| **R-543** | **[P1-HIGH] Off-site ON by default is not off-site WORKING: on a fresh box tier 3 sits at „Kulcsletétre vár" until the household does the escrow ceremony, and nothing asks them to — while the tier-1 row now tells them their files are protected by that very copy.** MEASURED 2026-09-16 on the fresh box (controller 0.244.0, hub 0.116.0, off-site provisioned automatically by the new default): the app-backup page reads „3. mentés — Kulcsletétre vár · A távoli mentés a titkosítási kulcs letétbe helyezéséig szünetel", the remote page reads „Helyreállítási kód szükséges", and `POST /backup/offbox/run` returns 302 while producing no snapshot (the controller log shows only `offsite-credential-retry`, no restic activity). **Why it matters more than before today:** hub v0.116.0 makes off-site the default *because* a one-drive box otherwise keeps the household's files in no tier at all (R-537/R-538), and controller v0.244.0 now prints „Az alkalmazás fájljait a távoli másolat (és a második meghajtó) védi" under the tier-1 row. On day one both are true-in-intent and false-in-fact: the copy is paused. **Fix shape (one of):** prompt the escrow ceremony as part of first-run when off-site is enabled and un-escrowed; and/or make the tier-1 sentence state the tier's actual state („…védené — a távoli mentés a helyreállítási kód létrehozásáig szünetel"). The ceremony itself works and is customer-facing („Helyreállítási kód létrehozása"); what is missing is that anyone is told to do it. | **READY — rank P1-HIGH; owner: CC (controller copy + first-run prompt)** |
| **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`. | **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`. | **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)** |