# REPORT — felhom-controller v0.203.0 (+ felhom-agent v0.127.0) **The two dead ends that kept the recovery journey failing are closed in code.** R-218's consume half and R-220 (both halves: the host-side detection and the customer-facing refusal). ## 1. R-218 — which retry shape, and why (§7.1) **Poll**, on a 5-minute scheduler tick, driven from the box's own published declaration (`OffboxReportStatus().State`) — **the very statement the hub acts on**, so the two cannot disagree about whether a retry is wanted. **Decided on Scenario B, measured against the actual promises:** | surface | what it promises | |---|---| | the no-target unlock message | „…**amint megvannak**, a mentéseid listája megjelenik…" — **no deadline** | | the backups card | „…**Ha egy napon belül nem áll be**, jelezd az üzemeltetőnek." — **within a day** | A 5-minute tick is inside both by a wide margin, and it needs **no hub change** (which §0 expected). **No text needed changing.** If either promise ever tightens to minutes, the ACK-driven shape is the one to reach for. **It stops by construction** (Scenario C) and **the settle gate is kept** (Scenario D) — the retry goes through `ReconcileWhenSettled`, unchanged. ## 2. The marker question (§7.2) — answered, and left alone `applied_marker` lives at `/offbox/applied_marker` — **inside the guest's data dir, which a rebuild destroys.** It therefore cannot suppress a legitimate post-rebuild re-run, and it is not part of this defect. Untouched. ## 3. R-220's shape (§7.3) — corroboration, not a wider prefix **Candidate (b)**, the claimed check distinguishing our own mount from a foreign one — because the host and its knowledge survive the rebuild while the guest's registry does not. Candidate (a) (a cleanup that clears the raw mounts) would have made correctness depend on a step running, and a step that does not run leaves exactly today's defect. **The discriminator is corroborated:** a non-managed mountpoint is forgiven **only when the same device is ALSO mounted under `/mnt/felhom-drives`** — a pairing only our enrolment produces. **`/proc/mounts`, not `lsblk MOUNTPOINTS`**, because the lsblk invocation is pinned verbatim in the sudoers file and changing it would have coupled this fix to a config rollout. **The fence holds and has its own test** (Scenario F): `/srv/data`, `/media/photos`, `/mnt/someone-elses-disk`, `/var/lib/other` are all still refused. **Fail-safe:** an unreadable mount table corroborates nothing. ## 4. Every changed Hungarian string **The deploy refusal (R-220, Scenario G).** Was: > „A megadott tárhely nem azonosítható regisztrált meghajtóként, ezért alkalmazás adatkönyvtáraként nem > használható. **Válasszon a listából csatlakoztatott meghajtót.**" Now: > „A megadott tárhely nem azonosítható regisztrált meghajtóként, ezért alkalmazás adatkönyvtáraként nem > használható. **Ha a gépet nemrég telepítettük újra, a meghajtóid megvannak, de még nincsenek újra > csatlakoztatva ehhez a géphez — a Tárhely → Meghajtók oldalon csatlakoztathatod őket, és utána > indítsd újra a telepítést. Ha ott sem látszanak, keresd a Felhom ügyfélszolgálatát.**" **No other customer-facing string changed.** The NAS refusal is a different situation and is untouched. ## 5. Tests and red-proofs — every one demonstrated failing, then restored | Scenario | Test | Red-proof mutation | Result | |---|---|---|---| | **A** | a credential staged after start-up is collected | make `RetryIfDeclared` return `(false, nil)` — the pre-v0.203.0 world | **FAILS**: "the box declared a need and no reconcile was attempted" | | **C** | a healthy box does nothing, and logs nothing | drop the `!declared()` stop condition | **FAILS**: a healthy box reconciles | | **D** | the settle gate holds on the retry path | call `Reconcile` instead of `ReconcileWhenSettled` | **FAILS**: "SETTLE GATE BYPASSED" | | **E** | the customer's own drive is offered after a rebuild | remove the `felhomOwnedMounts` exemption | **FAILS**: "device is mounted at /mnt/adatok (sdb)" | | **F** | a genuinely foreign mount is still refused | over-widen to any `/mnt/*` path | **FAILS**: "/mnt/someone-elses-disk was offered for formatting" | | **G** | an empty list explains itself | restore the old sentence | **FAILS**: "the refusal tells the customer to choose from a list that may be empty" | Plus: a nil bridge is a silent no-op; the corroboration requires both mounts of the **same** device, and another device's managed mount does not vouch for a foreign one. **Green:** controller **28 packages ok**, agent **29 packages ok**, both `go vet` clean, all gates OK in both repos. ## 6. Live checks (§7.4) **Deployed to demo-hp** from the **published** bytes (agent sha verified against the artifact before installing): agent **0.127.0**, controller **0.203.0**, healthy. **Scenario C — PROVEN LIVE, with a positive control.** The retry job is registered (`Registered periodic job: offsite-credential-retry (every 5m0s)`), **ran once** (`Running job: offsite-credential-retry` = 1) and produced **zero** log lines on a box whose tier is configured. The positive control matters: without it, silence could have meant the job never fired. *(A first reading counted 2 lines and looked like noise; they were the START-UP reconcile's settle-gate lines, not the retry job — the instrument was corrected before the conclusion.)* **Scenarios A and E are NOT live-proven yet, and deliberately so.** Both need a *rebuilt* box: A needs one declaring `needs_credential` (no target), E needs one whose registry lost its drives while the raw mounts survived. Contriving either on a demo box means dismantling its real off-site state; the state arises **naturally in Part 4**, which is where they belong and where the last re-walk measured them. ## 7. Not changed No hub change. No sudoers, no allowlisted command, no PVE surface. The settle gate, the marker, every other claim signal, and the NAS refusal are untouched.