Backlog triage Part B: 125 finished rows + 20 id-less rows moved to CLOSED-ITEMS (full text at 9e2786c); open rows normalised to one 6-column shape; narratives archived verbatim; closed_register_gate RULE 3 refuses a finished row in OPEN-ITEMS (decoys, seen red); rules rehomed to CONTEXT + 07 §11; loose notes triaged; R-814..R-819 filed; register 444 -> 325
(ruling 2 — still owed, still needs its own scoped session), and **R-198's retention, which remains
UNIT-PROVEN ONLY** — nothing has superseded a key in production, and proving it needs a SECOND
deliberate wipe. That retention drill is the next item, and it is not this session's.
v0.93.0 made the key survive. v0.94.0/v0.125.0/v0.195.0 make it come back. v0.197.0 got the file into
the snapshot and the drill got it out again. **v0.198.0/v0.95.0 remove three of the four crutches the
drill needed — the fourth is R-193, and until it goes the recovery is still operator-assisted.**
## R-201 — THE RE-WALK, 2026-08-06 (attended)
**The question was asked a second time, on the fixed build, on a brand-new appliance built from the
published ISO. The answer is still no — but it is a nearer no.**
| half | verdict |
|---|---|
| **the data** | **PASS** — all three sentinels byte-identical, including a 12 MB binary and an accented Hungarian filename **whose NAME BYTES are also byte-identical** (verified as hex, not as rendered text). Restored in **23 s** out of the pre-destruction snapshot `a7bc23bd`, through the customer's own two-step full-restore flow |
| **the journey** | **FAIL — two dead ends**, against Phase 1's four. One needed a command line **inside the guest**, one a **Proxmox-host** action |
**RTO: the unaided figure is STILL UNDEFINED**, because the unaided journey still does not complete.
Attended: login 11:42:22 → key placed **+45 s** → tier up **+24 m 12 s** (after intervention 1) →
all three sentinels restored and verified **+30 m 13 s** (after intervention 2). **The 30 m figure must
not be quoted as the customer number.** The only segment that reflects the product working alone is
**23 seconds** to pull 12.8 MB back once everything was in place.
**The two dead ends:** **R-218's consume half** (row corrected above) and **R-220** (drives
unenrollable after a rebuild — reproduced and red-proved again; without it no app can be redeployed,
and without a redeployed app the restore page is empty, which is R-213's territory and follows from
R-220 rather than being separate).
**What PASSED and is worth keeping:** the recovery screen **appeared without being sought**
(`/` → `/launcher` → `/recovery`); it answered all three of its questions and its seal date matched the
hub's `created_at` exactly; the emailed reset code worked **first try**; the unlock took **1.528 s** —
a real unseal — and placed the key; and **R-225's fix was seen working in the wild** (the store read
„a pillanatképek száma még ismeretlen" rather than a false zero).
**R-216 part 4 reproduced live:** the reinstall **downgraded the agent 0.126.0 → 0.125.0**, back to the
vouched version — an operator's hand-fix undone by the very event that makes recovery necessary.
⚠ **THE DELIVERY GAP, and it is owed.** A fresh install landed on controller **0.201.0** / agent
**0.125.0** — the vouched versions, **neither carrying the fixes**. They were installed **by hand**.
Fleet delivery needs a golden carrying 0.202.0 **and** a vouched agent 0.126.0. **Nothing was vouched;
that is the operator's act.** **This re-walk proves the JOURNEY on the fixed build; it does NOT prove a
**The gate was built BEFORE the fixes and was seen failing on 40 fields**, captured verbatim in
`documentation/tests/wire-contract-gate-2026-08-08/BEFORE.md`. That order was the method, not
bureaucracy: the night before, `deadcode` was rejected for class C6 precisely because it was made to
prove itself first and found neither of the two defects it was meant for.
**⚠ A COUNT THIS SESSION'S OWN PROMPT GOT WRONG, corrected against the repo rather than quoted.** The
prompt said *"465 emitted tags, eight unreachable"*. R-260's wording was "at least eight
DECISION-BEARING facts", never eight tags in total, and its own census already listed more. Measured
on the three declared wires: **210 tags checked, 51 skipped, 40 convicted.** Two prompt claims were
wrong this week and both were caught the same way.
**Two things the gate's CONTROL caught before it was trusted**, each a defect in the instrument:
1. **A substring false negative.**`grep -F healed_at` also matches `privsep_healed_at`, so a
genuinely dropped field read as received. R-260 named `healed_at`, so its absence from the output
was the tell. Now a whole-token regex.
2. **`dr_recipe` is not wholly opaque.** The hub stores each half as `json.RawMessage` and re-emits
nested shapes verbatim — but the TOP-LEVEL section keys are decoded by `hostHalfShape` /
`appHalfShape`, and **those are allow-lists**: a section an emitter adds is silently dropped until
named in both. That already cost `offsite_restic` (R-122). The gate is therefore opaque BELOW
depth 1, so the sections are checked; treating the whole subtree as opaque would have put R-122's
shape back outside its reach.
**THE FORTY, BY DISPOSITION.** Full per-field reasons live in the gate's own `ALLOWLIST`, where each
entry is a claim someone can re-check.
| # | field(s) | direction | decision | what changed |
|---|---|---|---|---|
| 1 | `oob.operator_key_configured` | agent → hub | **RECEIVE AND ACT ON IT** | decoded as a pointer; `oobDegraded` now fails when the key is absent, and the alert NAMES it. Hub v0.99.0 |
| 2 | `oob.wg_handshake_age_s`, `oob.healed_at` | agent → hub | **RECEIVE, for the message only** | decoded into `HostOOBRow` and put in the event payload; deliberately NOT in the predicate — widening the check beyond the fact that is now arriving is how a check stops being read |
| 3 | `escrow_stale` | hub → controller | **RECEIVE AND ACT ON IT** | `report.EscrowStatus.Stale`; the box tells a withheld hash from a hash-less one. Controller v0.209.0. **This is R-247** |
| 4 | `host.{cpu_temp_c,loadavg,memory_total_bytes,memory_used_bytes,uptime_seconds}`, `system.{load_avg_1,5,15, memory_total_mb, memory_used_mb, temperature_celsius, uptime_seconds}` | agent/controller → hub | **NO CONSUMER WANTED — redundant** | allowlisted: the hub decodes `cpu_percent` / `memory_percent` / `disk_percent` from the same stanzas and every threshold is expressed on those |
| 5 | `guests.spec.{disk_bytes,memory_bytes}` | agent → hub | **NO CONSUMER WANTED — redundant** | allowlisted: guest sizing is hub-owned INTENT (the manifest), not mirrored reality |
| 6 | `storage_targets.smart.model_name` | agent → hub | **NO CONSUMER WANTED — redundant** | allowlisted: a display label with no threshold on it; `smart.health` and every counter the hub bands on ARE decoded |
| 7 | `wireguard.last_handshake_age_s` | agent → hub | **NO CONSUMER WANTED — redundant** | allowlisted: wgsync reconciles from its own state, and the OOB path's own handshake age is now decoded |
| 8 | `guest_net` + its 7 children, `selfupdate_pending(_version)`, `mgmt_plane.healed_recently`, `pbs_dr.applied_at`, `restore_tests.mount_{parity,inventory}`, `config_hash`, `reporting_disabled`, `stacks`, `storage.migrated_to`, `backup.last_db_dump`, `backup.last_integrity_check` | agent/controller → hub | **NO CONSUMER TODAY, AND ONE IS ARGUABLY OWED** | allowlisted **against R-264, which stays OPEN**. Allowlisting is not deciding, and the entries say so |
**A C7 instance found while doing it, and corrected.** `HostReport.SelfUpdatePending`'s own comment
claimed *"The hub reads an absent field as pending=false, the correct default."* The hub has no field
for it and reads nothing either way. Comment corrected in the agent (no version bump — comment only).
**The hub's OOB test fixture was part of why this survived.** `oobReport()` omitted
`operator_key_configured` entirely, so every pre-existing scenario ran against a report shape **no
released agent produces**. Fixed, and the new tests drive the raw JSON decode boundary — a test that
builds the receiving struct by hand cannot see a field that never decodes, which is the whole class.
**Explicitly still open, untouched by this session:** R-246 (the wrong stale flag on `demo-hp` —
clearing it is an operator act hub-side), R-255, R-256, R-257, R-258, R-259, R-261, R-262, R-263, and
**C7's test-comment half**, which Campaign 12 recorded as *owed, not done* (60 of 2652 production
invariant comments sampled; none of the 1440 test comments).
## The seed that never ran twice, and three pictures that were not true — 2026-08-08
Four defects of one family: something the box already knows, either thrown away or drawn as its
opposite. Agent **v0.128.0**, controller **v0.210.0**, `gates.yml` (no hub change, no hub bump).
**R-221's writer, ESTABLISHED at `file:line` rather than assumed** — the prompt asked for this and it
was owed. `step_agent_config` (`felhom.eu/scripts/felhom-host-install.sh:2396`) renders `agent.json`
from `base = {}` unless an explicit `--preserve-from` is passed (flag `:1246`, defaulting empty at
`:256`), and writes it with `O_TRUNC` (`:2579`). **The render never writes an `escrow` section at
all** — grep over the whole heredoc returns zero hits. The pbsdr marker lives host-side
(`<agent-state>/pbsdr/marker.json`) and survives. So a rebuild keeps the marker and takes the key:
same descriptor, same hash, early return, seed never re-runs. **The attribution in R-221 was
correct.** A rebuild is nonetheless only the case that was measured — the same hole opens for a
hand-edited or restored config, which is the honest reason the fix is at the seam and not in the
installer.
**The idempotent early return was KEPT**, and that is load-bearing: it stops a converged box
re-running Proxmox operations every 60 s.
`TestSeedReasserted_OnConvergedTick_WithZeroProxmoxCalls` asserts **zero** recorded runner calls on
that tick, so a "fix" that simply deleted the return fails the test. Verified by mutation.
**The §7.3 truth table as implemented** (R-258):
| this app's own most recent dump result | restore point | verdict |
| any of its databases failed | yes | `error` — cross |
| all clean | yes | `ok` — tick |
| none recorded (no database / no run yet) | yes | **no icon**, time only, title „Erről a mentésről nincs eredményünk." |
| any | no | no tier-1 row at all, unchanged |
**An existing test was asserting the defect and was corrected, not deleted.**
`TestBuildAppBackupRows_Tier1FromRestorePoints` expected `"ok"` for a `FullBackupStatus` with **no
`LastDBDump` at all** — a green tick derived from nothing but a file's existence, i.e. Scenario G.
Its real subject, the `Tier1LastRun` time, is unchanged.
**The convention is now ruled** (§7.2, `CONTEXT.md` S-39): a `…Known bool` companion beside the
figures. `ROADMAP.md` G-3 was blocked on that decision and is unblocked.
**Six red-proofs, every one demonstrated failing and restored, each with the mutation asserted
applied.** The one that matters: Scenario A **fails against today's tree** with the intended message
— so the test tests the defect.
**Explicitly still open, untouched by this session:** R-246, R-255, R-256, R-257, R-261, R-262,
R-263, **R-264** (the twenty-one undecided facts — a design session of its own), R-240, R-243,
R-202, R-213, R-244, R-214/R-235, and **C7's test-comment half**, which Campaign 12 recorded as
*owed, not done*. **G-8's other half** (a hub-side check that notices a *vouch* has been forgotten)
was deliberately not built: it is hub work whose payoff is a daily email, and this session already
ends with a bake-and-vouch cycle in front of the operator.
## Why the TOP READY rows rank this way
This covers the next few only — it is deliberately **not** a full ordering of the table above, so that
there is one ranking to maintain rather than two.
1. **R-95** — the largest *data* exposure: the tier holding the customer's documents and photos is the
one whose credential can delete. **THIS PARAGRAPH WAS STALE UNTIL 2026-09-01 AND ITS OLD TEXT IS
NAMED SO THE CORRECTION IS NOT MISTAKEN FOR A RE-RANK.** It said the snapshot mitigation was
*"armed (daily 00:00, keep 7), but it has taken zero snapshots so far"*. **Both halves were wrong:**
seven daily snapshots do exist (R-429), and the word "armed" was withdrawn by the spike that same
day. **What is true now:** the snapshots exist and the box cannot write into them, but **no account
we hold can read anything out of one** (R-433, measured — 777,600 names, zero hits, controlled), so
they do not yet bound this exposure. The root cause is untouched either way — the box can still
`forget --prune` its own repo, from two call sites. **The ORDER of this list is unchanged and is
Viktor's**; only the facts under item 1 were corrected.
2. **R-94** — **de-ranked 2026-07-29.** The prior rationale ("until it moves every hub-driven
install gets the pre-R-82 default") was false: the constant selects no script and every install
already fetches 1.22.0. What remains is a wrong label plus two pieces of dead safety equipment —
a drift gate nobody runs and a test that compares a constant to itself. Cheap and worth doing;
not high-consequence, and it blocks nothing.
3. ~~**R-86**~~ — **CLOSED 2026-08-03**, agent v0.121.0 + hub v0.91.0, proven live on demo-felhom.
4. **R-87** — **SPIKED 2026-08-31 and now a DECISION, not work.** It also spent 2026-08-22..31 in
`CLOSED-ITEMS.md` by mistake while this paragraph ranked it fourth and pointed at nothing
(R-405). The spike measured it rather than designing it: a scratch restore of all 8 apps costs
25 s against the 40.3 s weekly check, but it would have caught ONE of the five drill-found
restore defects. **Recommendation: build the NARROW version — prove the snapshot still
CONTAINS a recoverable unit — or close the row.** Viktor's call; see
## 2026-10-03 — the triage: finished rows moved out of the open register
> Every row below sat in `OPEN-ITEMS.md` with a finished LEADING verdict (or was verified finished against
> live source on this day, or folded into an older duplicate). Full original text of each:
> `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`. Since this day `closed_register_gate.py` RULE 3
> refuses a finished row left in the open register.
| Row | What | Closed | Full text |
|---|---|---|---|
| **R-88a** | **~~Failing backup re-quiesces every 5 min, no backoff~~** **Reasoning kept:** «breaker 15m→4h, per-tier, never permanent» | SHIPPED (controller v0.176.0, 2026-07-27). Live on both boxes; breaker 15m→4h, per-tier, never permanent. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-88b** | **~~`/backup/due` cannot say *unknown*~~** | SHIPPED + PROVEN-LIVE (agent v0.105.0 + controller v0.178.0, 2026-07-27). `age_state=unknown` captured on real hardware during a deliberate ep0 outage; controller deferred, zero app stacks stopped. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-123** | **R-105 and R-106 were `READY` in `ROADMAP.md` with no row on THIS page** | CLOSED 2026-10-03 (triage, verified) — its last residue — "R-105 still needs a row" — is done: R-105 has its own row; the process gap is R-369's gate | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-131** | **`sess-f` is a fourth orphaned scratch customer** | CLOSED 2026-10-03 (triage, verified) — `sess-f` was already gone by 2026-08-05 (`documentation/tests/campaign11-evidence-2026-08-05/journal.md:24-27`) | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-176** | **Two prerequisites for the R-165 merge are UNMEASURED, and both are cheap.** | (a) ANSWERED 2026-08-03 (P1: PASS). (b) NOT REQUIRED — operator ruling: every node is reinstalled, none migrated (withdrawn, not deferred; CONTEXT.md:2076). — residue NOT-A-FINDING (2026-10-03): its residue ("re-run (a) once against agent v0.120.0") is moot: every node was reinstalled, none migrated (`CONTEXT.md:2076`) | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/SPIKE-r165-phase0-2026-08-03.md |
| **R-191** | **Every weekly offsite backup UPLOADS successfully and then FAILS the job on a prune the box is deliberately not allowed to do — on both demo boxes.****Reasoning kept:** «R-89 moved PBS pruning SERVER-SIDE — *"boxes set `keep_last: 0`, ep0 runs prune jobs; box tokens stay write-only, never widen the grant"*.» | CLOSED — SHIPPED 2026-08-04 (installer 1.25.0; both live boxes corrected to offsite `keep_last: 0`, `prune_pbs_allowed=false`). ep0 prune jobs verified first (18 tasks OK). hostinstall_gates.py asserts it (red-proved). The 'next weekly run OK' observation: offsite snapshots dated 2026-08-11 recorded in audits/INCIDENT-ep0-pbs-fd-exhaustion-2026-08-18.md:118. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/INCIDENT-ep0-pbs-fd-exhaustion-2026-08-18.md |
| **R-201** | **Nothing in the offsite DR chain has ever been exercised past the ceremony — and the one live proof that exists predates the field it is cited for.****Reasoning kept:** «**The pass condition is unchanged** — a byte-identical sentinel sha256, not "the store opened"» «**a good snapshot is not durable against a later bad run on the same day.**» | CLOSED 2026-08-07 — BOTH HALVES PASS on the fifth walk (DATA 2026-08-04 night run; JOURNEY 2026-08-07, zero guest command lines). Does NOT claim a smooth journey (R-252/R-253, both since CLOSED 2026-08-08) nor shape (c)'s positive half. Walls along the way: R-203, R-204, R-241. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/DRILL-r201-night-run-2026-08-04.md, audits/RECON-offsite-dr-chain-2026-08-04.md, tests/finalwalk-r201-2026-08-07/journal.md, tests/walk5-r201-2026-08-07/journal.md |
| **R-202** | **NOW EVIDENCED, NOT ARGUED (2026-08-10).** | CLOSED 2026-10-03 (triage, verified) — the false recoverability promise was removed (R-294/R-299; `controller/internal/web/templates/backups_remote.html:93-108`) | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-209** | **~~Should the containerd store move to SSD2 at all?~~** **Reasoning kept:** «**A TRAP was found while proving the guard, and it is the reusable part: `RequiresMountsFor` on a path with NO mount unit is a SILENT NO-OP**» «**`-X` is load-bearing** — overlayfs stacking rides `trusted.overlay.*`.» | EXECUTED 2026-08-05 on operator ruling — reboot validation DEFERRED → R-209a (still open, WATCHING, OPEN-ITEMS.md:339). Zero-loss move verified on four observables; storageReserved on SSD2 0 → 80 GB; pre-move tree moved aside. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/SPIKE-dooplex-buildcache-2026-08-05.md |
| **R-214** | **The physical console never stops asking to be paired.** | CLOSED 2026-09-20 - ISO 1.28.0+/R-535, proven on a fresh install (localisation slice 6's walk on ISO 1.29.0: last console paint after self-bind is the bilingual bound banner). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/i18n-slice6-2026-09-20/drill/screens/22-console-after-claim.png |
| **R-229** | **The instruction-file rightsizing landed for `felhom-controller` and the workspace root; three pieces were deliberately deferred.** | CLOSED 2026-10-03 (triage, verified) — legs (a), (b), (c) CLOSED 2026-08-06 in the row's own text; leg (d) moved to R-230 | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-233** | **The golden bake's acceptance checks were a list of strings the script does not print.****Reasoning kept:** «**The general lesson:** a runbook's pass markers must be **copied from a captured log, never written from memory**» | CLOSED 2026-08-06 — fixed in `RUNBOOK-manual-build.md` (§4.1): markers re-captured from the real log, pre-gate URL corrected to `golden.tar.zst`, token moved off the command line into an in-VM runner script, positive control on the token-leak grep, vouch step rewritten. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: runbooks/RUNBOOK-manual-build.md |
| **R-245** | **Should a customer who never decides be auto-abandoned after 30 days? RECORDED, NOT BUILT.****Reasoning kept:** «**The real harm, if it comes, is QUOTA** — old history blocking new backups — and **that is a condition, not a calendar**. An automatic ending should trigger on the harm, with a dated warning, never on a date alone. **If this is ever built, build it that way.**» | DECIDED 2026-08-07 — not built; reopens on quota. Built instead: escalating reminders (1/3/7/14 days) and operator levers `--abandon-extend` / `--abandon-stop`. Re-filed 2026-08-08 as a decision taken. Reopens verbatim: "THE CONDITION THAT REOPENS IT, which the reasoning already names: QUOTA — old set-aside history blocking new backups." | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-248** | **A flag that changes behaviour is visible to nobody who would look for it.** | FOLDED into R-246 2026-10-03 — the same ruling: give `stale_at` a visible, evidence-bearing setter, or retire it | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-254** | **The same render-then-hide pattern R-249 fixed is live in two more places, and one of them carries a real per-install secret.****Reasoning kept:** «`escrow_handlers.go`'s rule that a secret is revealed by an XHR and never templated server-side into HTML.» «**A form must carry what it submits.**» | CLOSED 2026-08-08 — both sites closed in controller v0.208.0: `POST /apps/<slug>/initial-credentials/reveal` (re-reads container, no-store, CSRF, logged) and `POST /stacks/<name>/auto-field/reveal` for deployed pages; pre-deploy hidden input kept deliberately. Gate `scripts/secret_in_markup_gate.py`; its blind spot → R-255. Measured exposure: none; rotation not indicated. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: scripts/secret_in_markup_gate.py |
| **R-272** | **RANK 1 — Felhom's `--uninstall` leaves the exact condition that makes Felhom's own reinstall REFUSE.** | CLOSED 2026-10-03 (triage, verified) — the uninstall purges the dnsmasq Felhom installed (R-316; `scripts/felhom-host-install.sh:858-882`, `_dnsmasq_purge_owned`) | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-295** | **One name per secret — CONTROLLER HALF SHIPPED.** | CLOSED 2026-10-03 (triage, verified) — hub half shipped in hub v0.104.0 (`4d6ec7c`, hub `CHANGELOG.md:844,905`); the controller half shipped v0.211.0 | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-303** | **`markOrphaned` has no guard against an active abandon countdown — the co-render is made HARMLESS, not IMPOSSIBLE.** **Reasoning kept:** «the wrong fix (suppressing the orphan card during a countdown) would hide a real second fault.» | DECIDED 2026-08-13 — left as it is; reopens on a real-world sighting. Verbatim trigger: "an observation of the combined state occurring OUTSIDE a constructed test." Related: R-302. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-312** | **There is no in-product route from the recovery screen to a set-aside store, and building one is not wiring — it is new surface.****Reasoning kept:** «retention is an **operator-only** capability, and nothing anywhere may promise the customer can perform it themselves» | DECIDED 2026-08-13 — deliberately not built; re-evaluate on a real customer request. Verbatim trigger: "a real need appearing — one request from a customer who is not us." Related: R-304, R-311. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-313** | **`demo-felhom`'s set-aside store is UNRECOVERABLE — 36 snapshots whose key we destroyed ourselves.** | DECIDED 2026-08-13 — kept as a test fixture; delete when R-312 ships or is abandoned. Verbatim: "when R-312 is built, or when R-312's ruling above is made permanent. On either event, delete it deliberately and record why". Related: R-198, R-307, R-312. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-341** | **Does the fd slope change after the PBS 4.2.5 upgrade? — two dated checks, and the answer is expected to be NO.****Reasoning kept:** «the anchor must be `ps -o lstart= -p $MainPID`, NOT `systemctl show -p ActiveEnterTimestamp`, which reads 03:54:54Z for this generation (the upgrade re-exec'd the proxy; systemd never saw a stop, `NRestarts` is still 0) and would put the rate ~15% low.» | CLOSED 2026-08-30 — second check taken (fd back to baseline 17, ESTAB 0) but it cannot answer the question: the leak was REMOVED mid-interval by our own fix (R-344, agent 0.130.0). Question moot; fix confirmed holding at 12 days. First check 2026-08-20: unchanged, 201.6 fd/day. R-336 keeps the request-rate scaling item; ActiveEnterTimestamp trap → R-346. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/SPIKE-ep0-established-connections-2026-08-20.md, audits/evidence-r341-plus7d-2026-08-30/step1-fd-and-sockets.txt, evidence-ep0-pbs-upgrade-2026-08-18/stop1-ruling.txt |
| **R-343** | **The managed controller floor was raised 0.214.0 → 0.216.0 — and it was NOT the no-op it was expected to be: it moved a live box nine seconds later.** | CLOSED 2026-10-03 (triage, verified) — obsolete — its only step was "confirm 0.216.0 is healthy in normal operation"; both demo boxes have run every release since and are on 0.288.0 (`STATUS.md`, 2026-10-02) | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-369** | **There are TWO registers, only one calls itself the source of truth, and work filed in the other is invisible to every standing rule that says "grep the register".** | CLOSED 2026-10-03 (triage, verified) — RULED 2026-08-22 (one register) and enforced by `scripts/one_register_gate.py` (`ef6ac6f`) | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-378** | **A status word inside a longer verdict fooled the session's own compressor, moving six still-open rows into the closed file.****Reasoning kept:** «Match the LEADING verdict.» | CLOSED 2026-08-22 — corrected in the same session; the six rows (R-123, R-190, R-214, R-264, R-295, R-352) restored verbatim from commit `fddfe00ce268`. Successor: R-369. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-385** | **A controller was built, baked AND vouched with no CHANGELOG entry of its own, and every gate stayed green.****Reasoning kept:** «**Membership, not `baked > released`, deliberately:** a comparison against the newest heading alone goes green the moment any later entry is written, leaving the unrecorded version permanently unrecorded and the gate permanently silent about it.» | CLOSED — 2026-08-23. v0.221.1 given its own CHANGELOG heading (commit da75603); scripts/golden_currency_gate.py now requires the baked version's own `## vX.Y.Z` heading anywhere in the CHANGELOG (membership); INCONCLUSIVE exit 2 preserved; both directions red-proofed. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/DRILL-r384-dead-db-alarm-2026-08-23/evidence/gate-0*.txt |
| **R-387** | **The hub REWRITES an unknown severity and says nothing, and the guard built to catch that sits downstream of the rewrite.****Reasoning kept:** «**The coercion STAYS; only the silence is fixed** — a rejected event is a LOST event, and losing an alarm is worse than mis-routing one.» | CLOSED — hub v0.107.0, 2026-08-23. Coercion kept; a WARN now names customer, event type, rejected value and consequence. Dispatcher severity branch kept (it is the only guard for monitor-originated events). Proven live with an `error` control silent. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/DRILL-r329-r386-2026-08-23/evidence/live-19-scenarioH-after.txt |
| **R-398** | **`resticStep` is not a seam, so no test can drive any restic-backed path.** | CLOSED 2026-10-03 (triage, verified) — premise corrected 2026-08-30; the execution test exists (`controller/internal/backup/r358_scratch_marker_test.go:200-205`, `TestR358_MarkerOrderingIsExecuted`) and the `resticStepFn` seam was deliberately NOT built — it would hide the `unlock --remove-all` escalation the test asserts | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-405** | **R-87 sat in CLOSED-ITEMS.md for nine days while still open; the ranking paragraph ranked it fourth pointing at nothing.** | CLOSED 2026-08-31 — corrected + gated in the same session: R-87 restored verbatim from `ef6ac6f^`; `scripts/closed_register_gate.py` is the 12th gate; R-398 stub turned to prose. Gate's residual holes in its docstring; hole 4 is R-406. Predecessor R-378. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-413** | **R-87's proof caught a naturally-produced hollow snapshot, end to end, unattended.** | CLOSED 2026-08-31 — the claim it upgrades is recorded (opengist `volumes_expected_none_captured`, one `offsite_proof_empty` at error; four apps ahead passed). Related R-87, R-412. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-428** | **The decoy-coverage gate identified a repository by its DIRECTORY NAME.** | CLOSED 2026-09-01 — fixed, and kept as the class's best example (felhom.eu CI job 490 found it; the gate now identifies a repo by which registered runner FILE exists under the root; verified under a renamed directory). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-432** | **A customer's own sub-account can REACH the snapshot door and is REFUSED writes — but sees it EMPTY, so per-file recovery is not product-reachable.** | ANSWERED 2026-09-01 — negatively; the panel-read next step is WITHDRAWN as unnecessary (777,600 exact snapshot names, zero hits; /.zfs/snapshot is a different filesystem from /home). Per-file recovery unreachable from the box entirely → R-433. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/evidence-drill-r95-recovery-2026-09-01/ |
| **R-434** | **The snapshot-drop alarm promises a recovery that cannot be performed.****Reasoning kept:** «when a verdict changes which fact it counts from, the alarm text has to change with it, or the operator acts on a promise nobody can keep.» «**That sentence is true under EVERY possible answer to the provider questions, so it never needs a second rewrite** — which is the whole reason it was not blocked.» | CLOSED 2026-09-01 — hub v0.111.1; the promise was DELETED (not replaced) with a sentence true under every provider answer; three tests in `hub/internal/monitor/offsite_r434_test.go`, red-proofed. The "blocked on R-433" verdict above was mine and it was wrong. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-463** | **The day the catalog moves postgres:16 to 17, ELEVEN apps are affected and the container image will NOT perform the conversion. (P2)****Reasoning kept:** «A later move of any of the three needs its own two-venue proof: the engine gate enforces it per app.» «PostgreSQL majors are converted BY THE BOX as a guarded-update step — save everything from the old engine, start the new one empty, load it back, check.» | CLOSED 2026-09-30 — 8 of 11 moved by the box's own conversion (docmost, paperless-ngx, tandoor, claper, calcom, rallly, outline, sparkyfitness; controller v0.273.0+, `09` decisions 16/42/43); 3 stay by decision 42's rule (zipline, adventurelog, immich). A later move of any of the three needs its own two-venue proof: the engine gate enforces it per app. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/SPIKE-r459-mariadb-upgrade-2026-09-06.md, audits/update-night-2026-09-21/24-Q5-postgres-conversion-costed.md, audits/pg-calcom-claper-2026-09-28/, audits/pg-last-six-2026-09-30/ |
| **R-497** | **No product channel ever gives the customer the „Tulajdonosi jelmondat”, yet the self-bind mail says they received it at setup. (P2)** | CLOSED — hub v0.113.0 (2026-09-14, ArgoCD Synced): customer page renders the hand-over sentence; tests red first (`passphrase_handover_test.go`); mail change pinned by test, not read live. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/DOORSTEP-walk-1270-2026-09-14.md |
| **R-500** | **[P3-LOW] The dashboard shows the last backup in UTC while every backup page shows it in local time — two different clock times for one backup.** | CLOSED 2026-10-03 (triage, verified) — controller v0.283.0 — the dashboard's last-backup time uses `fmtTime`, local time (`dashboard.html:154`; controller `CHANGELOG.md:222`) | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-505** | **A fresh box on customer tester-1 connects its Cloudflare tunnel but receives NO routes, so the dashboard answers 503. (P1)** | CLOSED 2026-09-29 — proven end to end on a fresh box. Cause: the tester-1 tunnel had no public hostnames; operator added a route 2026-09-14; box-side hop proven by the 2026-09-29 new-household drill. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-506** | **[P3-LOW] `day0-install.md` A.1 says "the controller manages per-app hostnames itself via the tunnel" — it does not.** | CLOSED 2026-10-03 (triage, verified) — the day-0 runbook names the published-route step and says the controller creates no routes or DNS (`documentation/runbooks/day0-install.md:47-53`) | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-511** | **A rebuilt customer's box keeps its ep0 PBS token, and the DR tier can be neither provisioned nor re-issued. (P2)** | CLOSED 2026-09-16 — proven live on a fresh box: hub v0.114.0 re-issue ADOPTS the endpoint token (shipped 2026-09-15); made live by R-534's ep0 grant. Token-only release on host delete not built → R-526. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/evidence-p1fixes-2026-09-15/ |
| **R-520** | **A power cut during a guarded Update leaves no record that an update was running. (P3)****Reasoning kept:** «**`pct mount` maps the guest's ROOTFS ONLY and does not apply the guest's own internal mounts**, and 9202 keeps `/var/lib/docker` on its own ext4 mount under the `mp0` volume, so from the host that path is an EMPTY STUB.» «**An empty directory is not evidence of an absent file.**» | CLOSED 2026-09-21 — measured on a real version change (scratch 9202, controller v0.260.0, uptime-kuma 2.4.0→2.5.0 cut in `pulling`; box put the pin back itself). The `starting`-phase cut → R-610. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/update-arc-2026-09-21/ |
| **R-524** | **When the catalog moves an app back to an older version, a box that already updated shows „Frissítés elérhető” — and the offered Update is a downgrade. (P2)****Reasoning kept:** «**Ahead is NARROW on purpose:** every differing service must be orderable AND newer, or the verdict falls back to Behind — this gate can BLOCK an update, so it errs towards letting one run.» | CLOSED 2026-09-21 — controller v0.260.0: `stacks.CatalogOrder` four verdicts incl. Ahead; `UpdatePreflight` refuses `downgrade` (409); three red-proofs; `09` §3 decision 10 (decided by CC unattended — operator may reverse). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-534** | **The off-site tier cannot be provisioned or adopted for a rebuilt box: the hub's endpoint token lacks Datastore.Modify. (P1)** | CLOSED 2026-09-16 — grant given and proven end to end: DatastoreAdmin for the hub's `felhom@pbs` on `/datastore/felhom-offsite` only (narrowest role carrying Modify; PBS has no custom roles); re-issue then ADOPTED on a fresh box. Closes R-511. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/evidence-drill-0243-2026-09-16/phase1-pbsdr.txt, audits/evidence-backup-promise-2026-09-16/phaseC-ep0-grant.txt, audits/evidence-backup-promise-2026-09-16/phaseC-reissue.txt |
| **R-535** | **The box's console still says it waits for pairing long after the box is bound — and promises the screen refreshes itself. (P2)** | CLOSED 2026-09-16 — shipped in ISO 1.28.0 (published); on-screen effect not photographed (`print_bound_banner`). Later SEEN on screen by R-214's closure 2026-09-20. Deliberately does not name the dashboard URL nor reflect the later claim. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/evidence-drill-0243-2026-09-16/screens/33-console-after-claim.png |
| **R-536** | **The hub is told „Alkalmazás telepítve” the moment a deploy is ACCEPTED, so an install that never finishes is recorded as completed. (P2)****Reasoning kept:** «The accept-time `app.yaml` is deliberately NOT deleted on failure — it is the crash-safe record with `Deployed:false` and it holds the settings the customer typed; the state every surface reads is `not_deployed`.» | CLOSED 2026-09-16 — controller v0.244.0 + hub v0.116.0: `app_deploy_started` at accept, `app_deployed` from the async end, `app_deploy_failed` (warning); both new types in `allowedEventTypes` and `customerMessages`; two red-proofs. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-537** | **The app-backup page labels the tier-1 backup „DB + Konfig + Adatok" and shows the data-drive size, but the tier-1 unit contains NO drive-side app data (P1-HIGH)****Reasoning kept:** «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» | CLOSED 2026-09-16 — controller v0.244.0, proven live on demo-hp; label computed per tier; re-proven on a fresh box (ISO 1.28.0). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/evidence-drill-0243-2026-09-16/phase2-f10.txt |
| **R-538** | **A tier-1 app restore reports plain success, leaves Nextcloud listing files whose bytes were never backed up, and destroys the app's own trash (P1-HIGH)****Reasoning kept:** «never present a DB-only restore of a class-A app as a complete one.» «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.» | CLOSED 2026-09-16 — controller v0.244.0, proven live on demo-hp; unit restore refuses when the unit cannot return the drive-side files; re-proven on a fresh box, off-site route returned 5/5 photos sha256-identical. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/evidence-drill-0243-2026-09-16/phase2-f10.txt, audits/evidence-backup-promise-2026-09-16/phaseE-photos.txt |
| **R-543** | **Off-site ON by default is not off-site WORKING: a fresh box's tier 3 waits on escrow and nothing asks the household. (P1)****Reasoning kept:** «The pause is untouched: it is the zero-knowledge escrow design, and this row was never about the mechanism.» | CLOSED 2026-09-16 — shipped in controller v0.245.0 and proven live: escrow-pending bar on every authenticated page (via `executeTemplate`) + tier-1 sentence rendered by `tier3State`; both red-proofed; live on 9202 (paused) and 9201 (escrowed). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/evidence-recovery-code-2026-09-16/ |
| **R-557** | **Localisation slice 2 — Go-side customer strings follow the language. (P3)****Reasoning kept:** «Plurals are a BUNDLE rule (a key with `.one`/`.other` takes its count first), not a call-site flag.» «EXCEPT one producer: `"Sikeres — nincs mentésre jelölt alkalmazás"` (controller/internal/backup/offbox.go) must stay Hungarian until **R-570** closes, because the page's legacy fallback still reads it on boxes that have not run off-site since 0.251.0.» | CLOSED 2026-09-18 - controller v0.252.0 + v0.253.0 + v0.254.0 (release A: 226 literals + `i18n_go_parity.py`; B: all 179 error literals keyed via `util.MsgError`; C: saved notes in box language, globe switch). Gaps filed: R-570, R-572..R-578; R-566 closed with it. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-558** | **Localisation slice 3 — the hub's customer e-mails follow the household's language. (P3)****Reasoning kept:** «The bind page is per-language with `expired` pinned to Hungarian (the language would otherwise be the oracle the text refuses to be).» | CLOSED 2026-09-18 - hub v0.118.1 + controller v0.256.1 (hub v0.118.0/v0.118.1 + controller v0.256.0/v0.256.1; 56 Hungarian mail goldens unchanged; proven live twice). Gaps filed: R-581..R-585; R-555 closed with it. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-559** | **Localisation slice 4 — the console banner and the download page in English. (P3)** | CLOSED 2026-09-18 — ISO 1.29.0 published (sha256 `dceacae5…e94829`), proof installs on both menu entries + reboot; `felhom.eu/en/download` live; release gate G16 rewritten per ruling 1b. Gaps filed: R-586, R-587, R-588. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-560** | **Localisation slice 5 — catalog cards, settings and first steps in English. (P3)****Reasoning kept:** «lists replace WHOLE and every other list is matched by its own key (`env_var`, option `value`, `match_group`, `target`, `path`), never by position;» «**The blocks are GENERATED from a flat `{path: english}` map**, not hand-written: a mistyped `env_var` is INERT on the box rather than an error, and the generator can only write paths that exist on the Hungarian side.» | CLOSED 2026-09-20 — controller v0.257.0, catalog fully translated (catalog `e81d41e` + three batches; 1 031/1 032 strings, ceiling 1), fleet floor 0.257.0 (min_agent 0.131.0). Gaps filed: R-589..R-594. — residue NOT-A-FINDING (2026-10-03): its residue (Peti's box and tester-1 taking floor 0.257.0 unattended) is moot: Peti's box was retired 2026-09-25 and tester-1 takes every floor like any box | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-561** | **Localisation slice 6 — the volunteer guide in English, then a stranger's first hour in English; closes R-516. (P3)** | CLOSED 2026-09-20 - controller v0.258.0, the English guide, and the walk (`runbooks/VOLUNTEER-first-hour.en.md`; R-589/R-590/R-573 fixed; R-214 closed as a side effect). Verdict: not yet ready for an English tester (R-596). Gaps filed: R-596..R-599. R-516 does NOT close. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/DRILL-first-hour-en-0258-2026-09-20.md, audits/i18n-slice6-2026-09-20/R-516-item-by-item.md, runbooks/VOLUNTEER-first-hour.en.md |
| **R-566** | **Three page titles built in Go around an app name stay Hungarian in the English browser tab. (P3)** | CLOSED 2026-09-18 - controller v0.252.0 (four `%s` keys via `data["TitleArgs"]`; pinned by `TestParameterisedPageTitles` and `i18n_go_parity.py`). Part of R-557. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-572** | **[P3-LOW] Two copy-producing template helpers have no English form, so an English page renders „vasárnap" and „%d órája" in Hungarian.** | CLOSED 2026-10-03 (triage, verified) — controller v0.258.0 — `pruneLabel`/`nextPruneLabel` deleted as dead code (`controller/internal/web/funcmap.go:368-377`) | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-580** | **[P3-LOW] `curl -w '%{redirect_url}'` prints Basic-auth credentials back into the session transcript.** | FOLDED into R-132 2026-10-03 — the same `curl -w %{redirect_url}` credential echo, seen again 2026-09-18 | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-582** | **An English copy guard written from the Hungarian one matches ordinary words instead of the claim. (P3)****Reasoning kept:** «**The general form worth keeping: a guard ported between languages must be re-derived from what the claim IS in the new language, not translated word for word — and a guard that convicts 141 true sentences is worse than no guard, because it earns an allowlist entry per sentence and then nobody reads it.**» | CLOSED 2026-09-18 - hub v0.118.0 (recorded for the lesson; three decoys incl. an innocent control): patterns carry the modal and admit an adverb; gate scans bundle VALUES only. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-583** | **The test-notification mail was the one customer mail that did not follow the language. (P3)****Reasoning kept:** «**The general form worth keeping: the surface you would use to CHECK a feature is the one most worth checking first — a broken instrument that reports success is worse than a broken feature.**» | CLOSED 2026-09-18 - hub v0.118.1 (`mail.test.subject`/`mail.test.body`, red-proofed, proven live in both languages 74 s apart on demo-hp). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-589** | **The update badge is Hungarian on an English app page — and it is the badge, not a corner case. (P3)****Reasoning kept:** «**The lesson, because it cost a reviewer pass and half a task brief: a reviewer who reads ONE producer cannot see a SECOND producer that overrides it** — reading `updatebadge.go` alone gives exactly the wrong answer.» | CLOSED 2026-09-21 — shipped in v0.258.0; the row was stale (English built in `web.localeFuncs` "updateBadge", pinned by `TestUpdateBadgeFollowsTheLanguage`; verified at controller `19ef0329ab66`). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/DRILL-first-hour-en-0258-2026-09-20.md |
| **R-590** | **[P3-LOW] The data-folder card tells an English household, in Hungarian, whether its files are backed up.** | CLOSED 2026-10-03 (triage, verified) — controller v0.258.0 — `consequenceFor` renders through message keys in the household's language (`controller/internal/web/datapath_card.go:32-50`) | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-592** | **Two defects in the new catalog copy gate, each found by its own decoy rather than by reading it. (P3)** | CLOSED 2026-09-20 — fixed in the same session, decoys added (`scripts/test_gate_decoys.py`, 33 cases): coverage scoped to named apps, credential and ASCII-stem bare-substring matches. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-595** | **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. (P2)****Reasoning kept:** «**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.» | CLOSED 2026-09-20 — degraded mode; CI job 791 green (catalog `18a6d2d`: without PyYAML the gate runs the freeze check and prints what it did not check; five decoys with PyYAML shadowed). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-596** | **The claim page — the FIRST screen an English household touches — is English chrome with HUNGARIAN messages. (P1)****Reasoning kept:** «It was **deleted, not translated**: a translated dead field would have read for ever after as evidence that this page's title is decided in the handler.» | CLOSED 2026-09-21 — controller v0.259.0, proven live (fourteen sites via `s.msg(r, "claim.msg.*")`; dead `data["Title"]` deleted; language chain pinned by a test; live on guest 9201 both languages; red-proofed). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-597** | **The setup code is three Hungarian words inside an otherwise fully English e-mail to an English household. (P2)****Reasoning kept:** «**The task's proposed "read it over the phone" filter was MEASURED and NOT adopted** — 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;» «The hub does not own that secret and no row was opened for it: a second definition here is the drift `backupTargetAbsentText` already demonstrates across two repos.» | CLOSED 2026-09-21 — hub v0.119.0: `RandomPassphraseFor(lang, use)`; setup code 4 en words (51.7 bits), owner passphrase 6 en (77.5); entropy floor computed from embedded lists, red-proofed. Recovery code was never Hungarian (agent, EFF list). Decision in source, operator may reverse. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-598** | **The Backup page's two protection warnings are Hungarian on an English dashboard. (P2)****Reasoning kept:** «**The English is asserted to carry the same NEGATION the Hungarian does** ("protects against corrupted files, **but not** against a disk failure"); an English sentence that promised disk-failure protection would be worse than leaving it Hungarian.» | CLOSED 2026-09-21 — controller v0.259.0; the two warnings proven by render test, not live (tier names proven live on 9201; `degradedMessageFor` returns a KEY). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-601** | **demo-hp is unreachable — WRONG, WITHDRAWN THE SAME DAY. The box was never down; MY ROUTES WERE. (P2)****Reasoning kept:** «**The standing rule says a "no access" claim must list what was tried; it does not say the list makes the claim true.**» «Six failed routes to a stale address are six failures of one assumption, not six pieces of evidence.» | CLOSED 2026-09-21 — withdrawn, the claim was false; the routes are fixed (both `~/.ssh/config` entries repointed to 192.168.0.104; `nodes.md` corrected; tailscale not installed on demo-hp). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-608** | **The controller swaps ITSELF in the middle of a guarded app update, and 04:30 sits inside the proposed auto-update window. (P2)****Reasoning kept:** «**THE PROPERTY THAT MATTERS MOST IS THAT THE LOCK DOES NOT LATCH:** `Stack.Updating` is cleared on done, failed AND held, so a HELD app does not block the controller's own updates — including the release that might fix whatever held it.» «**`stacks` never imports `selfupdate`.**» | CLOSED 2026-09-21 — controller v0.261.0: `Manager.AnyUpdating()` → `Updater.SetAppUpdatingCheck`; `UpdatePreflight` refuses `self_updating`; lock does not latch (`TestR608_LockReleasesAfterHold`); four red-proofs. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-609** | **An update refusal has a machine-readable reason inside the process and none on the wire, so an unattended caller cannot tell "wait" from "never" (P3-LOW)****Reasoning kept:** «`busy`/`updating`/`deploying`/`migrating`/`self_updating` are TRANSIENT and `held`/`downgrade` are TERMINAL until a person acts.» | CLOSED 2026-09-21 — controller v0.261.0: 409 body gains data.reason additively; the held refusal in actionStack (before UpdatePreflight) carries it too; red-proofed. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-611** | **A session reported "everything is done" over a phase it had silently skipped — the process failure, not the missing measurement (P3-LOW)****Reasoning kept:** «the report's FIRST section is "not done", even when empty.» «Every part and scenario of a brief is listed there if it was skipped, shortened or changed, with the reason.» | CLOSED 2026-09-21 — run by the successor session (scenarios F and G); "not done" is now the report's first section. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/update-arc-gaps-2026-09-21/ |
| **R-614** | **A stale `update_phase` survives a remove and redeploy, so a freshly installed app can read "Frissitve" before it has ever been updated (P3-LOW)****Reasoning kept:** «The name is the only thing a new install shares with the old one, so the record has to go when the app does.» | CLOSED 2026-09-22 — controller v0.262.0: RemoveStack calls ClearUpdateState; red-proofed. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-623** | **The unattended-update caller turned every SUCCESS into a `timeout`, then refused to press that app again — the instrument, not the box (P3-LOW)****Reasoning kept:** «an instrument that can report a success as a timeout is not a measurement» | CLOSED 2026-09-21 — fixed in audits/update-arc-gaps-2026-09-21/unattended-caller.py (unwraps data). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/update-arc-gaps-2026-09-21/unattended-caller.py |
| **R-627** | **Nothing checked that the register is a well-formed table; an append ate two rows' state cells and R-254 was broken for 45 days (P2-MEDIUM)** | CLOSED 2026-09-22 — gate 14 (scripts/register_shape_gate.py in repo_gates.py), four decoys, register repaired 317→315. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-628** | **An empty search of a mailbox I do not control was turned into a claim about what a THIRD PARTY had done, and entered the register as fact (P2-MEDIUM)****Reasoning kept:** «*an empty search may be reported as "absent from the place I looked", never as "it did not happen" — and only after a control query that MUST hit has been seen to hit.*» | CLOSED 2026-09-22 — rule recorded in the Gmail-access memory; R-433 corrected with the real answers. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-629** | **The drill catalog sent the operator 47 CI-failure alarms in one night; the drill method did not mention CI (P2-MEDIUM)** | CLOSED 2026-09-22 — has_actions false on admin/app-catalog-drill, `09` §6.5 method updated; the 47 mails are the operator's to clear. — residue NOT-A-FINDING (2026-10-03): its residue (47 unread CI mails) is the operator's inbox housekeeping, not product work | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-630** | **`paperless-ngx`'s health probe has never run on any box; a successful update stops the app (P2-MEDIUM, raised to P1)** **Reasoning kept:** «A stack with no probe is not healthy and not failing — it is SETTLED ON CONTAINER STATE (`09` §3), and never a reason to stop a running app.» | CLOSED 2026-09-22 — controller v0.262.0 + catalog: the no-probe wait settles on container state, the target is decidable (explicit container field), the gate refuses an ambiguity. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/the-28-2026-09-22/sidejobs/r630-controller-words.txt, audits/the-28-2026-09-22/sidejobs/r630.json |
| **R-631** | **Five templates cannot be judged by the probe gate at all, and one is correct only by accident (P3-LOW)****Reasoning kept:** «Add `expect: {status: 200}` to that template — a change that looks like a tightening — and home-assistant goes permanently unhealthy and every successful update of it starts stopping it.» | CLOSED 2026-09-22 — all five read live on guest 9202, all five probes correct; home-assistant's fragility is now a number (401 with no expect). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/the-28-2026-09-22/sidejobs/r631.json |
| **R-632** | **Twenty-eight of the 53 templates have never been deployed by any update drill (P3-LOW)** | CLOSED 2026-09-22 — all 28 walked in one night (26 deployed, 6 proven); findings → R-630, R-633, R-634. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/DRILL-the-28-2026-09-22.md, audits/probe-fix-2026-09-22/not-judged.json |
| **R-633** | **A remove sent while a restore runs reports success, deletes the record, and leaves a container restarting forever with a live public route (P2-MEDIUM)****Reasoning kept:** «`down` returning 0 is a request, not a result» «A refusal from the wrong rule is not evidence for the new one.» | CLOSED 2026-09-22 — controller v0.262.0 (busy guard + verified teardown) + v0.262.1 (typed RemoveBusyError → HTTP 409); both halves proven live on 9202. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/the-28-2026-09-22/apps/gokapi/came-back-evidence.txt, audits/the-28-2026-09-22/apps/gokapi/log.txt |
| **R-701** | **demo-hp's whole-guest restore test can never run: every 6 h it picks the right archive and the space preflight refuses it (P3-LOW)** | CLOSED — option (b), 2026-09-28 (operator, `09` §3 decision 44): restore_storage → nvme-scratch, FelhomAgentStore granted on /storage/nvme-scratch; manual and first scheduled cycle passed. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/version-travel-2026-09-26/D1/D1-cycle-demo-hp.txt, audits/evidence-golden-0276-2026-09-28/phaseD1-reclaim.txt, audits/logins-nvme-2026-09-28/C/ |
| **R-702** | **Every claper install creates an admin `admin@claper.co` with the public password `claper`, published on the household's domain (P1-HIGH)** | CLOSED — claper fixed 2026-09-28 (catalog 9dc8a05, controller v0.279.0 after_install); the class → R-707. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/pg-calcom-claper-2026-09-28/box/C0-claper-default-admin.txt |
| **R-703** | **calcom v6.2.0 cannot start at its catalog memory limit — a fresh install crash-loops and the box stops it (P2)** | CLOSED — catalog 9555e73, 2026-09-28: memory 768M → 1536M (anon peak 817 MiB, 53%). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/pg-calcom-claper-2026-09-28/box/C0-calcom-crash.txt, audits/pg-calcom-claper-2026-09-28/box/C0-calcom-memory.txt |
| **R-708** | **grafana falls back to password `admin` when its admin field is empty (P3-LOW)****Reasoning kept:** «no default in the compose (`${GF_SECURITY_ADMIN_PASSWORD:?}` refuses to start instead).» | CLOSED — 2026-09-29 (catalog d0e7e2e): `${GF_SECURITY_ADMIN_PASSWORD:?…}` refuses to start empty. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/login-gate-2026-09-29/D/D4-grafana-r708.txt |
| **R-709** | **The deploy page writes the generated admin passwords of installed apps into its HTML (P3-LOW)** | CLOSED — 2026-09-29, controller v0.280.0: password field renders empty with a reveal eye; red-proofs RP14/RP15; live on 9202. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/login-gate-2026-09-29/D/D6-r709-live.txt |
| **R-710** | **An app installed before its template gained an `after_install:` is never warned about its default login (P2-MEDIUM)** | CLOSED — 2026-09-29, controller v0.280.0 (RP13): absent record is "not run yet" only 30 min after install; „Megváltoztattam" press; live on demo-hp. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/login-gate-2026-09-29/A/A2-page-warning-after.txt, audits/login-gate-2026-09-29/A/A3-demo-hp-changed-it.txt |
| **R-711** | **About a dozen class-4 apps keep open sign-up after their first admin exists — the setup gate does not close that (P2-MEDIUM)** | CLOSED — 2026-09-29 (decision 47, controller v0.281.0, catalog 6faf432): per-app signup_block written when the gate opens; wanderer → R-714. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/login-gate-2026-09-29/B/B-VERDICT.md, audits/gate-rollout-2026-09-29/ |
| **R-712** | **wger refused every browser sign-in behind traefik: "CSRF verification failed" (P2-MEDIUM)** | CLOSED — 2026-09-29 (catalog d0e7e2e): CSRF_TRUSTED_ORIGINS + X_FORWARDED_PROTO_HEADER_SET; proven on a fresh install. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/login-gate-2026-09-29/D/D2-live.txt |
| **R-713** | **claper's `after_install` pastes the household's password into Elixir code, and the controller does not refuse a value that would break such code (P3-LOW)****Reasoning kept:** «a code-bound value holding a quote, backslash, `$`, `{`, `}`, backtick or line break is refused» | CLOSED — 2026-09-29, controller v0.281.0 (RP24) + catalog 6faf432: code-bound unsafe values refused, `${NAME¦base64}` added; proven live. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/gate-rollout-2026-09-29/ |
| **R-714** | **wanderer cannot be gated: its web part calls its own database host through the public name (P2-MEDIUM)** | CLOSED — 2026-09-29 (decision 48, controller 0.282.0): resolved without a gate; sign-up closed by "Close sign-up now" block + PUBLIC_DISABLE_SIGNUP; proven on 9202. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/signup-lock-2026-09-29/ |
| **R-715** | **The setup gate's probe reads only an HTTP-200 JSON object, so three apps with a real status get the button (P3-LOW)****Reasoning kept:** «a catalog gate that refuses a probe without a before/after measurement in its comment.» | CLOSED — 2026-09-29, controller v0.282.0 (RP29, RP30): list indexes in field, done_status:; new catalog gate check-probe-measured.py (5 decoys). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/signup-lock-2026-09-29/ |
| **R-716** | **Apps installed before controller 0.281.0 keep their open sign-up — decision 47 closes it only where the box opened the gate (P3-LOW)****Reasoning kept:** «a catalog change never touches an installed app» | CLOSED — 2026-09-29: operator ruled A (decision 49); built in controller v0.282.0 and pressed on demo-hp adventurelog/opengist and demo-felhom opengist. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/gate-rollout-2026-09-29/0/P0-3-demo-boxes-after-push.txt, audits/signup-lock-2026-09-29/ |
| **R-720** | **A new household's apps are not in the off-site copy: every app starts „3. mentés Kikapcsolva" (P2-MEDIUM)** | CLOSED 2026-09-30 — controller v0.283.0 (decision 50): fresh install on an off-site box switches the app ON; one-press offer for older apps. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/evidence-fixes-first-tester-2026-09-30/partA/ |
| **R-721** | **The household presses Stop during a whole-guest backup, and the backup starts the app again (P2-MEDIUM)****Reasoning kept:** «unquiesce restarts only stacks whose desired state is still running» | CLOSED 2026-09-30 — controller v0.283.1, proven live (v0.283.0 was wrong in production; v0.283.1 wires the adapter and pins the production types). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/evidence-drill-new-household-2026-09-30/phase1/step10-stop-undone-by-quiesce.log, audits/evidence-fixes-first-tester-2026-09-30/partE/ |
| **R-722** | **The volunteer guide is stale in five places a volunteer reads literally (P2-MEDIUM)** | CLOSED 2026-09-30 — both guides rewritten as measured (operator: CC rewrites); every changed line dated. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: runbooks/VOLUNTEER-first-hour.md |
| **R-727** | **The whole-guest restore test picks a PREVIOUS box's archive, fails on its key, and the ✗ is labelled with the wrong tier (P2-MEDIUM)****Reasoning kept:** «The restore test skips an archive written with another key (logged by name).» | CLOSED 2026-09-30 — agent v0.138.0 + decision 51 (restore test skips archives written with another key); ✗ card names the tier in controller v0.283.0; ep0 drill archives in tester-1 removed; delivered by signed jobs to both demo boxes. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/evidence-fixes-first-tester-2026-09-30/partC/ |
| **R-730** | **The published ISO 1.29.0 was built from a tree that git cannot name, so it cannot be tagged (P3-LOW)****Reasoning kept:** «`scripts/iso/build-felhom-iso.sh` now runs a clean-tree gate right after argument parsing: any uncommitted or untracked change, or HEAD ≠ `origin/main`, refuses the build (no bypass flag); the manifest records `repo-commit` from the gate and `iso-version-tag : iso-v<version>`.» «the `installer-v*` tags are the host-install SCRIPT's versions (`SCRIPT_VERSION` 1.28.0 on main; the website serves `/scripts/` from `installer-v1.28.0`), a different number line from the ISO — an `installer-v1.29.0` tag would block the next script release.» | CLOSED 2026-09-30 — the gate + test; 1.29.0 untagged by decision (its build tree is not a commit). build-felhom-iso.sh clean-tree gate (no bypass), manifest records iso-v<version>; test scripts/iso/test/clean-tree.sh red-proofed. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/pg-last-six-2026-09-30/E/E4-installer-tag.txt, scripts/iso/test/clean-tree.sh |
| **R-732** | **immich's FIRST start at the catalog pin could not finish on the bench: its database container was OOM-killed at 512 MiB (P2-MEDIUM)** | CLOSED 2026-09-30 — catalog `56c4888` (768M + v3.2.4); older step 0b8272068aab36bf re-proven at 768M (catalog `48440ce`, `63a96b0`); an installed immich gets it with its next guarded Update. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/pg-last-six-2026-09-30/F/immich-first-start-oom.txt, audits/immich-first-start-2026-09-30/A-cause.md, audits/more-night-apps-2026-09-30/ |
| **R-735** | **The test bench generates a `password:N:special` deploy field WITHOUT a special character, so calibre-web refuses it (P3-LOW)** | CLOSED 2026-09-30 — catalog `5b1972b` (`_gen` mirrors `randomWithSpecial`; test seen failing first). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/more-night-apps-2026-09-30/bench/apps/calibre-web/, A/R735-red.txt, A/R735-green.txt |
| **R-736** | **Removing or updating an app never deletes the image it pulled, so a box's Docker disk fills until an install or update is refused (P2-MEDIUM)****Reasoning kept:** «Keep set box-wide at delete time; exact id; no pass while an update runs; one summary line per pass; a one-time sweep.» «the Remove button runs `RemoveStack`, not `DeleteStack`; `docker image ls` without `-a` hides the untagged digest-pulled images» | CLOSED 2026-09-30 — controller v0.284.2, floor 0.284.2 (decision 53, option A; 0.284.0/0.284.1 never floored). Old controller images → R-745. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/more-night-apps-2026-09-30/box/T2-images-removed-by-name.txt, audits/night-rulings-2026-09-30/ |
| **R-737** | **wger's app login API answers 500 on a CORRECT password: the template sets no `JWT_PRIVATE_KEY` (P3-LOW)** | CLOSED 2026-09-30 — catalog `45d8482` (RSA pair generated once by wger's `manage.py generate-jwt-keys`, kept 0600 on the data volume; bench-proven, not proven on a box). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/more-night-apps-2026-09-30/B/wger-probe.txt, audits/night-rulings-2026-09-30/ |
| **R-740** | **A same-tag upstream security fix (postgres:18-alpine, redis:7-alpine, mariadb:12.3 …) reaches NO box, because the catalog never records a re-test of a tag at a new digest (P2-MEDIUM)****Reasoning kept:** «Not a cron job (runbook `runbooks/monthly-floating-retest.md` says why); who presses it monthly is the operator's word (STATUS).» | CLOSED 2026-09-30 — built (catalog `6a3ead9`); the monthly run is a standing step. Decision 52 option A: re-test entries + `scripts/retest-floating.py`; proven end to end on 9202. Exact-tag re-pushes → R-743. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/more-night-apps-2026-09-30/C/C3-floating-repush.txt, audits/more-night-apps-2026-09-30/C/C4-same-tag-retest.txt, runbooks/monthly-floating-retest.md, audits/night-rulings-2026-09-30/ |
| **R-741** | **For a few seconds after a fresh install, an `after_install` app answers its PUBLIC default login through the front door (P3-LOW)****Reasoning kept:** «an `after_install` app is installed HELD behind the setup gate's door until the login is replaced (or the household says it changed it).» | CLOSED 2026-09-30 — controller v0.284.2 (install hold: an after_install app is installed held behind the setup gate's door until the login is replaced; red-proofs RP-IH1..4). mealie lock → R-747. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/more-night-apps-2026-09-30/A/A3-calibre-default-login-window.txt, audits/night-rulings-2026-09-30/ |
| **R-742** | **zipline 4.8.0 cannot be reached from 4.6.1 in one step: it refuses to start until the database ran the release before it (P3-LOW)** | CLOSED 2026-09-30 — catalog `a9700e2` + `fb87030` (two steps 4.6.1 → 4.7.0 → 4.8.0, each proven on both venues). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/more-night-apps-2026-09-30/bench/apps/zipline/, audits/more-night-apps-2026-09-30/box/zipline/ |
| **R-743** | **Exact version tags are re-pushed under the same name too — not only floating lines (P3-LOW)****Reasoning kept:** «decisions 54 (a CC session the operator starts with the standing brief runs it monthly) and 55 (every app with a proven ladder; `--engines-only` a switch).» | CLOSED 2026-10-01 — decisions 54, 55; both tags re-tested (nextcloud catalog `3b59dfb`, sonarr `1a37032`). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/night-rulings-2026-09-30/A/, audits/retest-2026-10/ |
| **R-744** | **outline's fixture cannot seed outline 1.10.1: no `csrfToken` cookie after `installation.create` (P3-LOW)** | CLOSED 2026-10-01 — fixture fixed, proven on 9202 (accepts `__Host-csrfToken`; catalog `9fc7052`). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/rulings-2026-10-01/D/D2-outline-fixture-9202.txt |
| **R-745** | **Old CONTROLLER images are never deleted: ~50 versions on each demo box (P3-LOW)****Reasoning kept:** «the agent rolls back to the RUNNING image (what `/etc/felhom-controller-image` named), never to a previous one the controller hands it.» | CLOSED 2026-10-01 — controller v0.285.0, floor 0.285.0 (decision 56: keep running + previous, delete older/untagged). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/night-rulings-2026-09-30/E/, audits/rulings-2026-10-01/B/ |
| **R-746** | **`image_digest.resolve` ignores a `@digest` in its argument — it answers the TAG's current digest (P3-LOW)** | CLOSED 2026-10-01 — catalog 804884a (manifest asked by digest; malformed digest refused; scripts/test_image_digest.py red-proofed). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/rulings-2026-10-01/D/D1-r746-digest.txt |
| **R-748** | **The register-shape gate skipped every row whose id has a letter suffix (R-88a, R-88b, R-209a) (P3-LOW)** | CLOSED 2026-09-30 — `scripts/register_shape_gate.py` (`R-\d+[a-z]?`; decoy `suffix-row-eaten-state`). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-749** | **`retest-floating.py` could never start on a fresh bench: it checked for `/opt/upg/upgrade-test.py` before the step that copies it (P3-LOW)** | CLOSED 2026-10-01 — catalog 9e53205. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/rulings-2026-10-01/A/ |
| **R-750** | **The registry no longer holds controller releases older than 0.213.0 — something removed them, and nothing records what (P3-LOW)****Reasoning kept:** «`gitea-image-prune.sh` keeps the newest 20 + every version in use (floor, vouched golden, vouched agent, `min_agent`, the golden's baked images, the running hub), refuses (exit 3) when that list is unreadable» | CLOSED 2026-10-01 — decision 62, misc-scripts c9d5ed5 (cause: manual `gitea-image-prune.sh --keep 7` run 2026-08-22/23, HM-024; prune now keeps newest 20 + every in-use version, exit 3 when unreadable). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/rulings-2026-10-01/B/, audits/lockouts-2026-10-01/C/C1-registry-read.txt, audits/calibre-name-and-prune-2026-10-01/B/ |
| **R-751** | **The image clean-up after an app update could crash the whole controller: nil stack dereference when the app was gone (P2)** | CLOSED 2026-10-01 — controller v0.285.0, floor 0.285.0 (TestRetainImagesAfterUpdate_AppGoneDoesNotPanic seen panicking first). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/rulings-2026-10-01/B/B1-red-proofs.txt |
| **R-752** | **Four more catalog apps let a stranger lock the household out with wrong passwords for a known login name — like mealie (P3-LOW)****Reasoning kept:** «Installed apps: a settings-only change reaches the stack file at the next sync (images equal, ≤15 min) and the running app at the next `compose up -d` — Restart/Start (measured: the env changed only at Restart), an Update, or a backup's restart (`backup.go:972`, read).» | CLOSED 2026-10-01 — decisions 58–61 (wger catalog `82fff32`; BookStack, Grafana kept; calibre-web generated ADMIN_USER catalog `e9f50b5`). Installed calibre-web's invented name → R-757. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/lockouts-2026-10-01/B/ |
| **R-753** | **Behind the tunnel every visitor reaches an app with the SAME address, so every per-address guard is an 'everyone' guard (P3-LOW)****Reasoning kept:** «cloudflared alone on `felhom-tunnel` at the fixed `172.16.253.2`, traefik trusts forwarded headers from it only, an entrypoint middleware removes client-writable host/path/address headers» | CLOSED 2026-10-01 — controller v0.286.1 (decision 63; v0.286.0 never floored; catalog `04e9516`..`50e4fb4`) (rows R-776..R-779 carry what is left). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/lockouts-2026-10-01/A/A1-client-address.txt, audits/visitors-2026-10-01/A/ |
| **R-754** | **`01-topology-and-trust.md` §7 says cloudflared runs on the Proxmox HOST; on every box it runs INSIDE the guest (P3-LOW)** **Reasoning kept:** «the data path is not independent of the guest.» | CLOSED 2026-10-01 — document corrected (operator: the build is right). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-765** | **A new app that builds its login from a `type: password` value at EVERY start loses the login on a restore after a remove — Radicale (P2-MEDIUM)****Reasoning kept:** «a restore carries only `type: secret` values (`stacks.PortableSecretEnvVars`, the D5 ruling — a `type: password` is an internet-reachable login and stays out of the drive's backup)» «the login file is written on the FIRST start only and lives on the data volume.» | CLOSED 2026-10-01 — Radicale's template, before publishing (login file written on first start only, on the data volume). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/new-apps-2026-10-01/box/radicale-attempt1/restore.txt, audits/new-apps-2026-10-01/box/radicale/restore.txt |
| **R-767** | **MeTube has no login at all, by design, and the box has no permanent household-only door — so it is not built (P3-LOW)** | CLOSED 2026-10-02 — controller v0.287.0 + catalog (MeTube published) behind the family gate, decision 64, no exception. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/new-apps-2026-10-01/FIT.md, audits/family-gate-2026-10-02/A/items.txt, audits/family-gate-2026-10-02/B/box/metube-fresh.txt |
| **R-772** | **A health probe that finds NO container to probe records `healthy: true` (P3-LOW)****Reasoning kept:** «a not-run probe records `healthy: false, not_checked: true`, is looked at on the next tick» | CLOSED 2026-10-01 — controller v0.286.1 (not-run probe records healthy false, not_checked true; red-proofs RP-D1, RP-D1b). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/new-apps-2026-10-01/box/karakeep-768M/neg-and-crawl.txt, audits/visitors-2026-10-01/D/ |
| **R-773** | **After a remove + restore, an app's sign-up route block (decision 47) is gone (P3-LOW)****Reasoning kept:** «a REMOVED app restored from its backup gets the lock record (`opened_by: restore`) and the block written before anything starts» | CLOSED 2026-10-01 — controller v0.286.0 (restored app gets lock record `opened_by: restore` + block before start; red-proof RP-D2). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/new-apps-2026-10-01/box/karakeep/restore.txt, audits/visitors-2026-10-01/D/r773-live.txt |
| **R-780** | **A permanent household gate with family accounts — the spike PASSED; the build waits for the operator's go (P2-MEDIUM)****Reasoning kept:** «**Build requirement F1:** exceptions must be anchored (`PathPrefix(/api/v1/opds)` also matched `/api/v1/opdsx`).» | CLOSED 2026-10-02 — controller v0.287.0 (decision 64; internal/family, Család card, family_gate fields, catalog gate check-family-gate.py; Grimmory and MeTube published). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/permanent-gate-2026-10-01/VERDICT.md, audits/family-gate-2026-10-02/A/items.txt, audits/family-gate-2026-10-02/E/floor.txt |
| **R-787** | **No catalog app's LICENCE has been checked against Felhom being a paid service (P2-MEDIUM)****Reasoning kept:** «New apps carry their licence in record row 0.1.» | CLOSED 2026-10-02 — the read (decisions are R-784, R-789..R-795). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/licences-2026-10-02/TABLE.md |
| **R-788** | **The volume-persistence gate reads an EMPTY declared volume as CLEAN (P3-LOW)****Reasoning kept:** «an app that wrote nothing is UNDETERMINED (exit 2), never a pass.» | CLOSED 2026-10-02 — catalog (empty declared volume after the exercise is UNDETERMINED, red-proofed; APP_EXERCISE added). Bind mounts → R-805. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/family-gate-2026-10-02/C-metube-bench/C2-volume-persistence-metube.txt, audits/persistence-sweep-2026-10-02/A/RP-R788-empty-volume.txt |
| **R-790** | **Emby is proprietary: a personal, non-commercial, non-transferable licence (P2-MEDIUM)****Reasoning kept:** «KEPT — the household is the licensee; Felhom installs the official, unchanged image.» | CLOSED 2026-10-02 — operator ruling (kept), decision 66; part of the lawyer's review R-802. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/licences-2026-10-02/TABLE.md |
| **R-791** | **n8n's Sustainable Use License allows own internal or personal use; distribution only free of charge for non-commercial purposes (P3-LOW)****Reasoning kept:** «KEPT — the household is the user; the official, unchanged image.» | CLOSED 2026-10-02 — operator ruling (kept), decision 66; part of the lawyer's review R-802. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/licences-2026-10-02/TABLE.md |
| **R-792** | **Plex is proprietary: the HOUSEHOLD is the licensee under its own Plex account (P3-LOW)****Reasoning kept:** «KEPT — the household is the licensee under its own Plex account.» | CLOSED 2026-10-02 — operator ruling (kept), decision 66; part of the lawyer's review R-802. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/licences-2026-10-02/TABLE.md |
| **R-795** | **recipe-importer, Felhom's own image, has no licence file (P3-LOW)****Reasoning kept:** «Felhom's own code — no action unless it is shared with anyone.» | CLOSED 2026-10-02 — operator ruling (no action unless shared), decision 66. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/licences-2026-10-02/TABLE.md |
| **R-800** | **[P2-MEDIUM] "Delete your data from the hard drive" keeps the app's files in the household's userdata folder — and neither the dialog nor the result says so.** | CLOSED 2026-10-03 (triage, verified) — controller v0.288.0 (`580b656`, decision 67) — the remove dialog and result name the kept userdata folder; live on 9202 (`0945332`) | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| **R-801** | **The volume-persistence gate never sent a request to ANY app: it read the routed port from label VALUES, not the label NAME (P2-MEDIUM)** | CLOSED 2026-10-02 — catalog (the fixed gate `routed_ports()` + the re-sweep of all 58: CLEAN 39 · UNDETERMINED 19 · BROKEN 0). Follow-ups R-805..R-807. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/family-gate-2026-10-02/B/volume-persistence-metube-exerciser-diag.txt, audits/family-gate-2026-10-02/B/RP-R801-routed-ports.txt, audits/persistence-sweep-2026-10-02/A/sweep/TABLE.md |
| **R-803** | **papra cannot start under its own memory limit: a fresh install is OOM-killed in its migration and crash-loops (P1-HIGH)** | CLOSED 2026-10-02 — catalog (papra 768M, mem_request 256M; measured from birth, 0 kills, persistence CLEAN; no box ran papra). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/persistence-sweep-2026-10-02/A/sweep/papra-oom-diagnosis.txt, audits/persistence-sweep-2026-10-02/A/papra/SUMMARY.md |
**Rows that never had an `R-` id** (the 2026-07-28 table of campaign findings and watches):
| Row | What | Closed | Full text |
|---|---|---|---|
| E-2d | **Prove E-2 on a fresh VM — a real host-install 1.22.0 run, Case B, a claimable customer, add a drive and unplug it.** | CLOSED — PARTIALLY PROVEN (2026-07-29). C1, C2 proven; C3, C4 proven live; C5 FAILED → R-116 (the single named open leg; R-116 since CLOSED in CLOSED-ITEMS). Per Session-C runbook §9 a failed claim closes as partially proven, no re-run. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/E2D-fresh-vm-2026-07-29.md, audits/SESSION-C-2026-07-29.md |
| — (watch, line 285) | **Storage Box **snapshots** on `storage-box-pool-1` — plan SET (daily 00:00, keep 7) but **0 taken yet**** | CLOSED 2026-10-03 (triage) — snapshots exist: R-429 recorded seven daily Storage Box snapshots (2026-09-01) | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| — (watch, line 289) | **demo-felhom's next weekly PBS backup (newest is 2026-07-26)** | CLOSED 2026-10-03 (triage) — folded into R-91, whose trigger is exactly this backup | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| — (watch, line 290) | **demo-felhom's next restore-test (84 h cadence, last 2026-07-27 06:38 UTC)** | CLOSED 2026-10-03 (triage) — restore tests have run on demo-felhom on their cadence ever since (R-672, R-689 in `CLOSED-ITEMS.md`) | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| F-CRIT-2 | **~~A failed offsite backup left a phantom snapshot that RESET the tier's freshness clock — 7 days silent~~** **Reasoning kept:** «`NewestArchiveTime` now counts only plausibly-complete entries (measured 1 MiB floor; undecidable ⇒ not counted).» | SHIPPED + PROVEN-LIVE (agent v0.106.0, 2026-07-28). Campaign fault 2 replayed on demo-hp: phantom rejected + logged once, tier DUE and backed up, no thrash on the inverse. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| F-CRIT-1 | **~~An app that fails to restart after a quiesce never alarms on any channel~~** | SHIPPED + PROVEN-LIVE (controller v0.179.0, 2026-07-28). Live on demo-hp: alarmed 9s after grace expiry; a deliberate user stop stayed silent through 9 dead-app scans. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| F-A1 | **~~A restore-test in flight made a healthy backup report as FAILED (HTTP 409 read as a tier failure)~~** **Reasoning kept:** «409 → contention: tier stays DUE, dropped before anything stops (15m), and BLOCKED alarm if contention outlives the agent's 120m ceiling (3h).» | SHIPPED + PROVEN-LIVE (controller v0.179.0, 2026-07-28). Hub DB: 409 → 0 operator emails, real failure → 1. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| C9-F1 | **~~Tier-2 „Fájlok visszaállítása" is offered for apps whose copy has no restorable file leg; restores 0 files and reports all files present~~** **Reasoning kept:** «`Tier2RestoreCoverage` refuses UP FRONT without stopping the app and NAMES the working action; a run that proceeds claims only what it **examined** and discloses that the database and volumes are not covered.» | SHIPPED + PROVEN-LIVE (controller v0.183.0, 2026-07-28). Live on demo-felhom: bookstack refused without restart; paperless re-run byte-identical, 16/16 docs. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| C9-F2 | **~~An app in a Docker crash loop never alarms on any channel; `StateRestarting` is in no down-set~~** **Reasoning kept:** «`StateRestarting` deliberately NOT added to `IsDownState` (that alarms on every deploy fleet-wide)» | SHIPPED + PROVEN-LIVE (controller v0.183.0, 2026-07-28). Sustained restarting becomes down after `crashLoopAfter`=5m; dashboard counter uses the same predicate. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| C9-F3 → R-104 | **An **interrupted offsite run leaves an exclusive restic lock the self-heal cannot reach**: `resticStep` (`offbox.go:634-648`) has `unlock --remove-all`, but `ensureOffboxRepo`'s probe fails first, `classifyResticProbe` (`offbox.go:77-93`) has no lock case → …** | FOLDED into R-104 — the same finding; R-104 has its own open row | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| D5 | **~~Move app secrets into the LOCAL recovery unit so Tier-1/Tier-2 restore stop needing the guest~~** **Reasoning kept:** «**Operator ruling 2026-07-30: `type: secret` travels (45 fields), `type: password` NEVER (7) plus a code register (`vaultwarden/ADMIN_TOKEN`); plaintext.** The exclusion is what LICENSES the plaintext — coupled, not independent.» «the register is **code, not a catalog flag** (a boundary a catalog push can move is not a boundary — R-97a).» «**Precedence: the UNIT WINS** over the guest, because the unit's secrets were captured in the same run as the dumps beside them and therefore match the data being restored; pinned both directions.» | SHIPPED + PROVEN-LIVE (controller v0.188.0, 2026-07-30). Operator ruling 2026-07-30 on which secrets travel; manifest schema 2; live proof on scratch guest (AdventureLog secrets recovered 2/2, app read seeded row over TCP); 4 red-proofs. Related: R-127. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/D5-drive-alone-restore-2026-07-30.md |
| F-DIAG | **~~Four distinct offsite failure causes collapse into two operator-visible strings~~** **Reasoning kept:** «Unclassifiable says so rather than being folded into a neighbour.» | SHIPPED (controller v0.182.0, 2026-07-28). `ClassifyOffsiteFailure` → quota / orphaned / no_repo / no_units / transport / unknown; redaction by the target's actual host/user/path. Unit-proven; not yet exercised by a live offsite failure of each class. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| F-OPS | **~~A manual `pct restore` inherits the source guest's bind mounts — during a real DR, on a different host, under pressure~~** **Reasoning kept:** «Docs only by design — the agent already neutralises binds on its own restore paths, and a second implementation would drift» | DOCUMENTED (2026-07-28) in `runbooks/RUNBOOK-manual-guest-restore.md` (mpN volumes vs binds, the mp9 source-VMID trap, strip-and-re-add, positive pre-start verification). | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: runbooks/RUNBOOK-manual-guest-restore.md |
| F-REBOOT | **~~A guest rebooted during its backup does not come back — 9m47s outage with every alarm silent~~** **Reasoning kept:** «`onboot` is the deliberate-stop discriminator (already the stale-lock path's, and what `pve-guests` consults)» | SHIPPED + PROVEN-LIVE (agent v0.107.0, 2026-07-28). 60 s guest-power watchdog, retry 3x/1m-2m-4m then escalate. Live on demo-hp: 120 s unattended vs 587 s; `onboot:0` guest left stopped. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| F-LEAK | **~~A failed restore-test cannot destroy its own scratch guest (403 `VM.Allocate`); the 10-slot VMID band shrinks silently~~** **Reasoning kept:** «4th root-fenced exception, band enforced in sudoers **literally** (`pct destroy 99000[0-9] --purge`) + in code + at the caller; API destroy still tried first.» | SHIPPED + PROVEN-LIVE (agent v0.110.0 + host-install v1.21.0, 2026-07-28). Third attempt shipped: 4th root-fenced exception; live band permitted, 9201/9100/9999/990010/1 and `pct start 990000` refused. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| F-OBS | **~~`deadapp-check` leaves NO positive observable on a default (info-level) box~~** | SHIPPED + PROVEN-LIVE (controller v0.180.0 + agent v0.109.0, 2026-07-28). INFO summary every 20th scan; agent v0.109.0 fixes the same shape in the guest-power watchdog. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| E-2 | **~~Drive-role machinery around the moved vzdump target~~** | CLOSED — PARTIALLY PROVEN (Session C, 2026-07-29). C1/C2 proven in E-2d; C3, C4 proven live (R-114, R-112); C5 FAILED → R-116. Broken legs R-112, R-113, R-114, R-116 all since CLOSED (CLOSED-ITEMS). Arc's definition of done: R-106+R-109, R-108, D5. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md`; evidence: audits/SESSION-C-2026-07-29.md, audits/E2D-fresh-vm-2026-07-29.md |
| E-2a | **~~The target move needs a root-fenced wrapper — the agent cannot do it~~** **Reasoning kept:** «the agent's PVE role was NOT widened.» «has NO storage-removal path (grep-assertable), is idempotent and refuses to repoint.» | SHIPPED + PROVEN-LIVE (agent v0.113.0 + host-install v1.22.0, 2026-07-29). `felhom-backup-target-apply` behind literal `FELHOM_BACKUPTARGET` sudoers alias; all five laws proven live as root on demo-hp, 0 stray storages. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| E-2b | **~~`NotifyStorageDisconnected`/`Reconnected` defined and called NOWHERE — a drive going absent emitted no event~~** | SHIPPED + PROVEN-LIVE (controller v0.184.1 + agent v0.112.0 + hub v0.81.0, 2026-07-29). Seam wired in `ReconcileDriveGates`; keying bug (guest path vs host MountPath) caught before deploy. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
| E-2c | **~~E-1 put the whole-guest backups on a drive `POST /disks/eject` would eject~~** **Reasoning kept:** «NOT a role reclassification — `RoleForStorage` untouched, because on both boxes that drive is ALSO the enrolled user-data drive» | SHIPPED + PROVEN-LIVE (agent v0.112.0, 2026-07-29). Eject + decommission refuse 409 on the backup-target mount; refused live on both boxes. | full text: `git show 9e2786c:documentation/backlog/OPEN-ITEMS.md` |
## 2026-09-17 — nothing decides by reading a Hungarian word (controller v0.251.0)
**`OPEN-ITEMS.md`** is the register of open work and the file to read first — it holds only what is
open, one row per item, every row with a state and an owner. **`ROADMAP.md`** is the full history and
reasoning behind the `R-n` IDs, including shipped and killed items; an ID is minted there, and a new
instance of an existing item attaches to that ID rather than getting its own.
open, one row per item, every row with a category, a severity, a state and an owner (the layout is at its
top). **`CLOSED-ITEMS.md`** holds every finished row, compressed, with the commit that keeps its full text;
a row moves there **in the same commit that closes it** (`scripts/closed_register_gate.py` RULE 3).
**`ROADMAP.md`** holds intentions — features and spikes that do not exist yet — and **`ROADMAP-HISTORY.md`**
the shipped and killed ones. An `R-n` id is minted once and never reused.
The rest of this folder: verified-LIVE findings with implementable fix plans that are **not yet
implemented**. Preserved here
(instead of on git branches) per the trunk-based, no-branches rule — the fix itself is implemented later
**directly on `main`**, during a normal/supervised session.
## The other files in this folder — verdict per file (2026-10-03 triage)
- **FIX-M18-NOTES.md** — dump re-validation runs every 5 min (perf). **FIXED** in controller v0.62.0 @
`f8afe5c` (2026-06-14). (was on the deleted `felhom-controller` branch `fix/m18-dump-validation-cache`.)
- **FIX-M19-NOTES.md** — `deriveStackName` misattribution edge (low-incidence correctness). **FIXED** in
controller v0.62.0 @ `6bab68b` (2026-06-14). (was on the deleted branch `fix/m19-stackname-crossref`.)
| File | Verdict |
|---|---|
| `SPEC-app-data-placement-2026-08-21.md` | **KEEP — still referenced by open rows** R-352 (placement still open) and R-368. |
| `SPEC-r85-phase4-5-2026-07-26.md` | **KEEP as history — a spec of shipped work** (R-85, agent v0.104.0). `CONTEXT.md` and `architecture/00-capability-map.md` link here. |
| `FOLLOWUP-nas-automount-guest-reboot-reassert.md` | **FINISHED, kept in place** — shipped in agent v0.84/v0.85 (CAMPAIGN-3). Not moved because `CONTEXT.md`, `controller/network-storage-nas.md` and `felhom-controller/CONTEXT.md` link to this path. |
- **FOLLOWUP-golden-default-controller-tag.md** — the golden bakes a stale controller (`:0.43.0` when
queued; had rotted again to `:0.85.1` by resolution). **FIXED** in felhom-agent @ `ceca355`
(2026-07-03): `build-golden.sh` v2.0.0 makes the controller tag a MANDATORY argument (a required arg
cannot rot) and golden **0.98.3** was baked + clean-room-validated (bake → first-boot-current →
self-manage → app deploy, on the drill VM — no supervised touch of live guests needed) + published +
| R-7b | **Share backup EXECUTION** — put share data into the live tier-2 + offsite runs (the design fork reported by R-7 slice 1) | M | **SHIPPED (controller v0.145.0, 2026-07-18)** | **Viktor's ruling: Model B′ — a SIBLING shares source.** New, additive job/leg code reusing the proven primitives (tier-2 mirror seam, restic wrappers, soft-quota/enlargement gate, status recorders) while leaving **every per-app engine path byte-identical** — NOT a synthetic recovery unit (breaks on multi-drive shares, wraps 1 KB of JSON in dump machinery) and NOT engine-loop surgery. The B′ invariant is enforced by test in both tiers, red-proofed. Tier 2 → `RunSharesTier2` (legs grouped by SOURCE drive → `backups/secondary/_shares/<driveKey>/<share>`, payload at `_payload/`, layout marker LAST). Tier 3 → `runOffboxSharesLeg`: ONE extra `restic backup --tag felhom-offbox --tag _shares` placed after the app loop and BEFORE retention, so `forget --group-by host,tags` covers the new group with no flag change; a quota-blocked push degrades to the **manifest only, never to nothing**. Restore → „Megosztások" on `/backups/restore`: scratch, then a missing-only merge whose every destination is PREFIX-ASSERTED against live storage roots, definitions merged existing-wins, then `ReconcileSamba`, then the credential. The **payload** (`_shares-manifest.json` + a best-effort secret-bearing `passdb.tar`) is what makes DR return files + configuration + password rather than loose bytes. **Fold-in: samba joins the liveness set** — `EffectiveProtected` adds the CONTAINER `felhom-samba` exactly while sharing is on. **FULLY PROVEN-LIVE on demo (2026-07-18), all four legs.** (1) tier-2: real `/api/backup/tier2` trigger → `_shares` tree + marker + payload on the cross-drive target, mirrored file md5-identical, payload 0600 preserved. (2) offsite: Viktor's manual run 12:18:16Z → snapshot **`e0b9d723`** (tags `felhom-offbox,_shares`) with the payload dir + both share folders; a second run via the „Távoli mentés" button → **`4e2b15ec`**, containing `_shares-manifest.json` (418 B) AND `passdb.tar` (855 040 B), both 0600, share files with uid 1000 preserved. (3) restore round-trip: probe file + the `dokumentumok` DEFINITION deleted via the real endpoints, then „Megosztások" restore + place → `1 file(s), 1 definition(s) re-added, 1 kept, 0 refused, credential=true`; probe back md5-identical, the two pre-existing files NOT overwritten (missing-only proven on live data), definition back with its ORIGINAL flags and created_at, `smb.conf` re-rendered, `filmek` untouched. (4) liveness: samba stopped → `health_critical` pushed and hub-accepted (200) → self-healed. Remaining human leg: SMB positive auth with the real household password (never persisted by design). **Correction:** an earlier revision of this row and of the ship REPORT wrongly claimed the demo box had no offsite target — the verification read a guessed settings key (`offbox_target`) instead of the real one (`offbox`); root cause dissected in REPORT §7b. Findings: the reserved-name assumption was FALSE (`nbNameRe` accepted „_shares" as a share name — now refused); the alert/e-mail pipeline needed NO change and adds no new event type. Docs: `controller/sharing.md`; ship report `felhom-controller/REPORT.md`. |
| R-8 | DLNA (**gate input now exists — R-6 spiked 2026-07-18: SSDP reaches LAN clients from host-net**): validate Jellyfin's built-in DLNA server first; only add minidlna to the catalog if Jellyfin-DLNA fails | S | idea (unblocked) | Don't add catalog weight before proving the cheap path. **R-6 confirmed the cheap path is physically viable — Jellyfin DLNA must run host-network (same multicast constraint as R-7)** |
| R-9 | Uninstaller trio (from 07-15 Peti session): cluster-aware `felhom_guests` guard (node-local `pct list` deletes cluster-wide pveum objects); saferemove detection + time estimate + opt-in `--quick-remove` (never mutate `storage.cfg`); smarter `restore_storage` default for BYO clusters (shared storage, not local-lvm) | M | idea | Second item's rejected alternative (temp-disable-and-restore) stays rejected — crash window silently downgrades cluster wipe policy |
| R-202 | **The orphan card promises recoverability unconditionally**, which after R-198 is true going forward and false for anything already orphaned | S | **gate hit 2026-08-04 — card untouched, sentence still live** | Blocked on knowing which escrow generation an orphaned repo belongs to (R-199/R-201). A single ACK boolean can say a retained recoverable blob EXISTS but not that one COVERS this repo; a conditional promise that can still be false is worse on that surface than a hedged one |
| R-201 | **The wipe-and-recover drill** | L | **PREPARED, HALTED BEFORE THE WIPE (2026-08-04)** | Nothing irreversible done. Established live for the first time: a rebuilt box's off-site run REFUSES (the orphan card, not a silent fresh history — closes R-193 Q3), the orphan reset works move-aside-never-delete, and demo-hp's pre-rebuild key is permanently gone (superseded four hours before v0.93.0). Resume needs R-203 |
| R-202 | **The orphan card promises recoverability unconditionally**, which after R-198 is true going forward and false for anything already orphaned | S | **CLOSED 2026-10-03 — the register row moved to `CLOSED-ITEMS.md` (triage)** — **gate hit 2026-08-04 — card untouched, sentence still live** | Blocked on knowing which escrow generation an orphaned repo belongs to (R-199/R-201). A single ACK boolean can say a retained recoverable blob EXISTS but not that one COVERS this repo; a conditional promise that can still be false is worse on that surface than a hedged one |
| R-201 | **The wipe-and-recover drill** | L | **CLOSED 2026-10-03 — the register row moved to `CLOSED-ITEMS.md` (triage)** — **PREPARED, HALTED BEFORE THE WIPE (2026-08-04)** | Nothing irreversible done. Established live for the first time: a rebuilt box's off-site run REFUSES (the orphan card, not a silent fresh history — closes R-193 Q3), the orphan reset works move-aside-never-delete, and demo-hp's pre-rebuild key is permanently gone (superseded four hours before v0.93.0). Resume needs R-203 |
| R-19 | Internet-outage customer-experience drill: pull WAN on demo, verify lan_resolver path, document what the customer actually sees/does | S | idea | Flips map row E "LAN access" IMPLEMENTED→PROVEN-LIVE |
| R-34 | **Backup data lifecycle management.** An "inactive backups" section on „Távoli mentés": apps that have snapshots but no active backup — **disabled OR uninstalled** — listed with name / size / last snapshot / restorable, plus an explicit **double-confirmed per-app delete** via `restic forget --tag` + nightly prune. | M | idea | **RULING: the offsite toggle NEVER offers deletion — policy and destruction stay decoupled.** Turning backups off must never be a data-destroying act, and deletion must never hide behind a toggle. Origin: 2026-07-18 rehearsal. Pairs with R-32 (that one is the operator's view of dead bytes; this one is the customer's) |
| R-27c | **Customer self-bind, slice 2 — console-passphrase bind.** Viktor's direction: bind using a passphrase shown on the box console, alongside (not instead of) the emailed capability link. | M | idea | **Security constraints from the session ruling, all load-bearing:** passphrase **issued at customer creation**; the global-lookup endpoint must be **spray-hardened** — per-appliance **and** per-IP caps, constant-time comparison, a **single generic failure** (no oracle), alerting on abuse; an **accent-free wordlist** (console keymaps are not Hungarian); the **web capability-link path is RETAINED**; **claim-by-email is RETAINED** as the delivery-channel proof. **Also under this item:** the self-bind email gains the **public universal-ISO download link + two-line instructions** (the DIY case). **Secret-bearing per-customer ISOs are ruled OUT.** Sibling of R-27b (second-box flow) — different axis, both build on the same `/bind/` page |
@@ -167,5 +167,5 @@ Everything here is **remote-doable** — the N100 is packed, and none of it need
## Absorbed / superseded notes in this folder
- `FOLLOWUP-nas-automount-guest-reboot-reassert.md` — **shipped** (agent v0.84/v0.85, CAMPAIGN-3); keep for history
- `FOLLOWUP-golden-default-controller-tag.md` — verify against current golden flow; close or promote to an item
- `FOLLOWUP-golden-default-controller-tag.md` — **FIXED** (agent `ceca355`); moved to `documentation/archive/` 2026-10-03
- `FIX-M18-NOTES.md`, `FIX-M19-NOTES.md`, `DIAGNOSIS-f9-storage-registration-gap-2026-06-14.md` — historical diagnoses; superseded by shipped fixes; moved to `documentation/archive/` 2026-10-03
> **Status 2026-10-03 (backlog triage): SHIPPED — a spec of finished work, kept as history** (R-85: agent v0.104.0,
> `ROADMAP-HISTORY.md`). `CONTEXT.md` and `architecture/00-capability-map.md` link to this path.
# SPEC — R-85 Phases 4 & 5: docs, then deploy
**Written** 2026-07-26 by Claude Code, at the operator's request, after Phases 0–3 shipped.
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.