answers 2026-10-12 Part A: the 38 rulings recorded (09 §3 196-233), 17 rows closed (128 -> 112), R-933 filed
gates / gates (push) Successful in 6m41s

- 09 §3: the decision sheet S1-S38, the operator's words quoted where they differ (S1, S2, S8, S32, S37)
- closed: R-831 R-870 R-698 R-327 R-230 R-719 R-352 R-526 R-770 R-771 R-913 R-290 R-209a R-769 R-616 R-884, and R-243 (dated check: the mail fired 2026-10-11 16:13:51Z, hub log agrees)
- R-698: the known limit written in 07 §6.6; new row R-933 (mirror supported catalog images, P3)
- R-327: capability-map row BUILT; stands claim re-titled and moved to built; page re-rendered
- R-913: the customer Cloudflare token recipe in day0-install A.1; 01 §7 points at it
- R-884: read only - ArgoCD Synced, live Deployment equals git
- R-887 dated check NOT met (4 lost jobs 10-10/11); re-dated 2026-10-19 after the S21 change
- 22 open rows carry their ruling; STATUS: the sheet is ANSWERED

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-10-12 14:07:50 +02:00
parent 25ab47eeaa
commit 1b45bb4d9b
12 changed files with 184 additions and 63 deletions
File diff suppressed because one or more lines are too long
@@ -222,7 +222,9 @@ app. Controller down → the gated app answers an error, never the app. Measured
customer added later would be reachable). Cloudflare unreachable → the save fails and the old token stays. The token
is never logged. **Limit:** `/zones` lists what the token can READ; a hand-built token with Zone:Read on one zone and
DNS:Edit on all zones would pass — the token wizard's single „Specific zone" scope does not build that, and a
customer token cannot read its own policies. Tokens saved before this check are checked the first time each config
customer token cannot read its own policies. **The guard for the write side is the recipe, not a check** (operator
ruling 2026-10-12, S31, `09` §3 decision 226): the operator mints every customer token from the one recipe in
`runbooks/day0-install.md` A.1, and the hub checks nothing more. Tokens saved before this check are checked the first time each config
is saved with a new token or domain.
- **Tunnel placement: INSIDE the guest** (corrected 2026-10-01, R-754 — the operator's brief of that evening: the build is
right, correct the document). `cloudflared` is a container the CONTROLLER renders and keeps up (`internal/infra`,
@@ -875,7 +875,11 @@ requires a database dump written after the conversion whose recorded engine is t
**[FACT] The limit — the image is not in the backup (R-698).** A unit stores image NAMES and digests; the
image is re-pulled on restore. A version its maker has deleted cannot start. Measured 2026-09-26: the 42 ladder
digests all resolve today (`audits/version-travel-2026-09-26/A7/`). Options are in R-698; nothing is decided.
digests all resolve today (`audits/version-travel-2026-09-26/A7/`). **Accepted as a known limit — operator ruling
2026-10-12 (S8, `09` §3 decision 203; R-698 closed).** What a household meets: a restore whose version the maker has
deleted stops at the image pull, the app stays as it was, and the household uses another copy or a newer version.
The operator's follow-up — *„will need to mirror the apps we support later, just to be sure"* — is **R-933**: mirror
the images of every catalog app we support into our own registry, before scale. Until it is built, this limit holds.
**Since decision 53 (`09` §3, 2026-09-30 evening):** a box keeps only the image each app service runs and the one before
it; older images of the app are deleted. A restore that needs an older version re-pulls it — as before; the limit
above is unchanged, and kept data (decision 40) is not touched by the image clean-up.
@@ -915,6 +915,70 @@ its length, and both fixes cost something the household would notice — operato
127. **The agent's three by-design abilities (`03` §3.1) stay for now**; revisited before the first paying customer.
*Operator ruling 2026-10-05.* (R-861)
### 2026-10-12 (13:50) — the decision sheet S1–S38 answered (recorded before the work; the „build the 38 answers" brief)
The sheet: `STATUS.md` „The decision sheet (2026-10-10 afternoon)". Each line names the sheet number and the row. Where the
operator's words differ from the sheet's pick, his words are quoted and they win. *Operator rulings 2026-10-12 13:50.*
196. **S1 — R-922: the hub's registered customer address stays; only the box's notification address can be cleared.**
*„the customer can only delete their email address in the controller, from the notification address. If they do that,
they won't get any more notifications (maybe a warning popup?). However in the HUB each customer needs an email
address, and the user can't delete that, so the HUB can reach the user if needed (accounting, or any other reason)."*
So: a warning before the household clears its notification address (controller), and the privacy-notice draft says
why the registered address is kept.
197. **S2 — R-831: the Hetzner storage token stays valid (option A, accept).** The sheet's pick was „rotate"; the operator
chose A. Row closed as accepted.
198. **S3 — R-870: Tester 1's two leaked Cloudflare tokens are accepted;** they are rotated when Tester 1 is retired.
199. **S4 — R-242: a hub alarm when the newest baked golden is not vouched,** before the first external install.
200. **S5 — R-388: the household notification settings are redesigned to „only what you can act on" — designed now,
built in slices later.**
201. **S6 — R-244: the hub removes a deleted customer's id from shared app-log issue rows, and sweeps once** (the count
is shown before the sweep).
202. **S7 — R-264: CC owns the six owed hub readers (decided 2026-08-12) and builds them when the queue is idle.**
203. **S8 — R-698: a restore of a version whose image the maker deleted cannot start — accepted as a known limit**
(`07` §6.6), row closed. *„Will need to mirror the apps we support later, just to be sure"* → new row R-933 (P3):
mirror the images of every catalog app we support to our own registry, before scale.
204. **S9 — R-255: one test renders every dashboard page with planted secrets and fails if any shows.**
205. **S10 — R-266: „disk reading failed" is carried to the hub,** so it is not read as an empty disk (hub + controller).
206. **S11 — R-333: no heat band for NVMe;** the drive's own critical warning still alarms.
207. **S12 — R-577: guest share pages get the language globe** (the visitor's own cookie).
208. **S13 — R-327: the code-naming arc is „built" in the capability map;** CC rewrites the title. Row closed.
209. **S14 — R-230: dated history entries in the memory index may keep version numbers;** (a) closed, the pilot (c)
dropped. Row closed.
210. **S15 — R-719: the expired self-bind link's „Új linket kérek" shape is accepted.** Row closed.
211. **S16 — R-352: hot data in the guest, bulk data on a drive, is by design.** Row closed.
212. **S17 — R-526: no „release only the PBS token" operation on ep0** — the R-511 adopt path already reuses the kept
token. Row closed.
213. **S18 — R-213: „see what a restore would change" is designed after the first customers.** Row stays open, P4.
214. **S19 — R-288: one session restructures the capability map** — one status line per capability with a dated pointer
to its evidence. Documentation only.
215. **S20 — R-231: DooPlex's backup scripts (`/opt/backup/scripts`) go into a repo with an install script;** the
operator is present for the install.
216. **S21 — R-887: raise the act-runner's fetch timeout and rate-limit the public Gitea pages the crawler walks**
(DooPlex; each change after the operator's yes in chat).
217. **S22 — R-886: `fsGroup: 65534` for Alertmanager;** a silence must be proven to survive a pod restart.
218. **S23 — R-211: the Prometheus config-reloader sidecar** (DooPlex, after the operator's yes in chat).
219. **S24 — R-770: no Invidious.** Row closed.
220. **S25 — R-771: no moonlight-web.** Row closed.
221. **S26 — R-782: Glance goes behind the family gate,** like Grimmory.
222. **S27 — R-554: `recovery-info.txt` points at the dashboard's Restore page and the recovery code; then the obsolete
setup wizard is deleted.** The operator approves the Hungarian text before it ships.
223. **S28 — R-562: Hungarian dates and numbers in Hungarian (decimal comma, `2026. 10. 10. 15:04`), ISO-like in English.**
224. **S29 — R-250: the off-site setup scan prefers IPv4 and waits longer, and its error says „safe to press again".**
225. **S30 — R-246: the hub's `stale_at` column is retired.**
226. **S31 — R-913: the operator mints every customer Cloudflare token from one recipe; the hub checks nothing more**
(option a). The recipe: `runbooks/day0-install.md` A.1. Row closed.
227. **S32 — R-521: one mail per lost drive to the household, in its language — *„if there is a notification option in the
controller settings, and enabled"*.** So the mail goes only when the household's own notification settings carry
that option and it is switched on (hub + controller).
228. **S33 — R-402: one line on the hub's host page: „Last full check: OK/FAILED, depth N, date".**
229. **S34 — R-290: folded into R-288** (the map restructure cites an evidence file for every green row). Row closed.
230. **S35 — R-209a: accepted until a planned reboot;** the next planned DooPlex reboot checks the SSD2 move. Row closed.
231. **S36 — R-769: no Pinchflat.** Row closed.
232. **S37 — R-884: *„Fixes, can be closed"*** — CC reads the ArgoCD state for Prometheus once, read only; Synced → close
with the evidence, drift → one line for the operator and the row stays.
233. **S38 — R-616: the Gitea admin token the catalog clone exposed was rotated (in the R-925 work).** Row closed.
### 2026-10-10 — operator ruling: the kernel approval rule (recorded before the work; the release brief)
195. **„Approve kernel set" offers the NEWEST kernel version that EVERY ring-0 box has booted healthily at least once
File diff suppressed because one or more lines are too long
@@ -126,16 +126,18 @@ claims:
- id: claim.code-naming
band: journey
stage: 2
title: "The same word is used for two different secrets across three surfaces; the email points at a page a rebuilt machine does not show"
status: partial
note: "Warning card. Cost a real code on 2026-08-09."
title: "Each of the two secrets has one name on every surface, and the re-enrol mail points at a page a rebuilt machine shows"
status: built
note: "Shipped (controller v0.211.0, hub v0.104.0/v0.105.0); no customer has typed the passphrase yet. Operator ruling 2026-10-12 (S13). The old defect cost a real code on 2026-08-09."
sources:
- register: "R-282"
- register: "R-283"
- register: "R-295"
- register: "R-323"
- register: "R-327"
verified:
date: 2026-08-09
verdict: confirmed
depth: source-read
date: 2026-10-12
verdict: upgraded
depth: register+map
- id: use.catalog
band: journey
stage: 3
@@ -0,0 +1,7 @@
# R-243 dated check, read 2026-10-12 ~12:05Z
channel 1 (operator mailbox, Gmail connector): thread 1a12bbe0e6599a61, from monitoring@felhom.eu to admin@felhom.eu,
2026-10-11T16:13:51Z, subject "[Felhom] ⚠️ Tester-2: offsite_escrow_pending",
"Customer Tester-2: off-site backup is ON but has not run for 168h0m0s since the box first ..."
channel 2 (the running hub pod, kubectl logs deploy/hub --since=30h):
2026/10/11 18:13:51 [INFO] Operator email sent for Tester-2/offsite_escrow_pending
Expected: first sweep after 2026-10-11 ~16:06Z (anchor = Tester 2's first report). Fired 16:13:51Z.
@@ -0,0 +1,10 @@
# R-884 read-back, 2026-10-12 (read only)
Mon Oct 12 11:56:40 UTC 2026
## channel 1: ArgoCD Application monitoring
sync=Synced health=Healthy rev=eeb009f790f8495d72434aa3500f8e1de391c6a0 reconciledAt=2026-10-12T11:52:32Z
resources 29 not Synced: []
prometheus Deployment: ['Synced']
## channel 2: the live Deployment vs git (homelab-manifests eeb009f, mon-system/monitoring.yaml)
git image prom/prometheus:v3.15.0, live image prom/prometheus:v3.15.0
live args ["--config.file=/etc/prometheus/prometheus.yml","--storage.tsdb.path=/prometheus","--storage.tsdb.retention.time=60d","--storage.tsdb.retention.size=19GB","--web.enable-lifecycle","--web.enable-admin-api"]
git args identical (6 flags, compared by script 2026-10-12 11:55Z)
+24
View File
@@ -26,6 +26,30 @@
---
## 2026-10-12 (afternoon) — the operator's 38 answers (sheet S1–S38): the rows that need no code
The full text of the rows below: `git show 25ab47eeaa:documentation/backlog/OPEN-ITEMS.md`. Evidence: `documentation/audits/answers-2026-10-12/` unless a row names another place.
| Row | What | Closed | Evidence |
|---|---|---|---|
| **R-243** | **A box in the R-241 state silently stops backing up off-site, and NO ALARM OF ANY KIND FIRES.** (P2) | CLOSED 2026-10-12 — **the alarm fired on time.** The `offsite_escrow_pending` mail for Tester-2 reached the operator mailbox 2026-10-11 16:13:51Z (the first sweep after the 7-day line, ~16:06Z); the running hub logged „Operator email sent for Tester-2/offsite_escrow_pending" at the same second. Two channels. | hub v0.144.0+ (`b119301c`); `r243-escrow-pending-mail.txt` |
| **R-831** | **The Hetzner storage API token (`HETZNER_TOKEN`, the storage project's token in `Secret/storagebox`) was printed into the 2026-10-03 session transcript** (P3) | CLOSED 2026-10-12 — operator ruling 2026-10-12 (S2, `09` §3 decision 197): **A — accept.** The Hetzner storage token stays valid. | the ruling |
| **R-870** | **Tester 1's two Cloudflare credentials — the zone API token (`infrastructure.cf_api_token`) and the tunnel token (`infrastructure.cf_tunnel_token`) of the hub's `customer_configs` row `tester-1` — were printed into the 2026-10-04 night session's transcript** (P3) | CLOSED 2026-10-12 — operator ruling 2026-10-12 (S3, `09` §3 decision 198): **accepted**; rotate when Tester 1 is retired. | the ruling |
| **R-698** | **[P3-LOW] A backup stores the image's NAME, not the image — a restore of a version its maker has deleted cannot start.** (P3) | CLOSED 2026-10-12 — operator ruling 2026-10-12 (S8, `09` §3 decision 203): **accepted as a known limit**, written in `07` §6.6. The operator's follow-up (mirror the images of every supported catalog app) is the new row R-933. | `architecture/07-backup-architecture.md` §6.6 |
| **R-327** | **The standing picture still describes a defect that has been fixed twice over.** (P4) | CLOSED 2026-10-12 — operator ruling 2026-10-12 (S13, `09` §3 decision 208): **„built"**: a capability-map row added (status BUILT), the standing-picture claim `claim.code-naming` re-titled as a capability and moved to `built`, the page re-rendered. | `00-capability-map.md`, `where-felhom-stands.yaml`/`.html` |
| **R-230** | **Three instruction/memory follow-ups deliberately left by the part-2 session (2026-08-06), each needing a decision rather than an implementation.** (P4) | CLOSED 2026-10-12 — operator ruling 2026-10-12 (S14, `09` §3 decision 209): (a) **ruled: dated history entries in the memory index may keep version numbers**; (b) was closed 2026-08-06; (c) **the pilot is dropped**. | the ruling |
| **R-719** | **[P2-MEDIUM] A customer who already exists never gets a fresh self-bind link when their new box registers: the last link expires in 7 days and nothing re-sends it.** (P4) | CLOSED 2026-10-12 — operator ruling 2026-10-12 (S15, `09` §3 decision 210): **the „Új linket kérek" shape is accepted.** | the ruling; the shape shipped earlier (row text) |
| **R-352** | **Four screens state something untrue about where an app's data goes, and the configured default is consulted by nothing that places data.** (P4) | CLOSED 2026-10-12 — operator ruling 2026-10-12 (S16, `09` §3 decision 211): **by design**: hot data in the guest, bulk on a drive. The visibility fix shipped; R-368 fixed the comment. | the ruling |
| **R-526** | **[P3-LOW] A host delete cannot release only the customer's ep0 PBS token: the endpoint's one removal op destroys every backup group too.** (P4) | CLOSED 2026-10-12 — operator ruling 2026-10-12 (S17, `09` §3 decision 212): **no** — the R-511 adopt path already reuses the kept token. | the ruling |
| **R-770** | **[P3-LOW] Invidious — fit check only; the recommendation is not to build it.** (P4) | CLOSED 2026-10-12 — operator ruling 2026-10-12 (S24, `09` §3 decision 219): **no Invidious.** | the ruling |
| **R-771** | **[P3-LOW] moonlight-web — fit check only; not buildable through an HTTP-only tunnel at usable latency.** (P4) | CLOSED 2026-10-12 — operator ruling 2026-10-12 (S25, `09` §3 decision 220): **no moonlight-web.** | the ruling |
| **R-913** | **The Cloudflare token check (R-138, decision 190) reads what a token can SEE, not what it can WRITE.** (P4) | CLOSED 2026-10-12 — operator ruling 2026-10-12 (S31, `09` §3 decision 226): **(a)** — the operator mints every customer token from one recipe; the hub checks nothing more. Recipe written: `runbooks/day0-install.md` A.1; `01` §7 points at it. | `runbooks/day0-install.md` A.1 (recipe read from the hub and controller Cloudflare calls 2026-10-12) |
| **R-290** | **Most capability-map rows that back a green dot cite no evidence document at all — measured, 20 of 28 probed.** (P4) | CLOSED 2026-10-12 — operator ruling 2026-10-12 (S34, `09` §3 decision 229): **folded into R-288** (the map restructure cites an evidence file for every green row). | R-288 |
| **R-209a** | **The SSD2 move has NOT survived a reboot, so by this project's own standard it is not fully validated** (P4) | CLOSED 2026-10-12 — operator ruling 2026-10-12 (S35, `09` §3 decision 230): **accepted until a planned reboot**; the next planned DooPlex reboot checks the SSD2 move (the mechanism is proven). | the ruling |
| **R-769** | **[P3-LOW] Pinchflat is not built: upstream is paused (no release in 2026, last push 2025-12-16) and its last release has no image tag.** (P4) | CLOSED 2026-10-12 — operator ruling 2026-10-12 (S36, `09` §3 decision 231): **no Pinchflat.** | the ruling |
| **R-616** | **[P3-LOW] The catalog credentials are stored in PLAINTEXT in the box's catalog clone and are printed by an ordinary `git remote -v`.** (P3) | CLOSED 2026-10-12 — operator ruling 2026-10-12 (S38, `09` §3 decision 233): **the Gitea admin token the clone exposed was rotated** (operator's word, the R-925 work). The leak itself was fixed and read on every box 2026-10-10. | the operator's word 2026-10-12; `audits/register-shrink-2026-10-10/` (the 2026-10-10 clone read) |
| **R-884** | **ArgoCD app `monitoring` shows `Deployment/prometheus` OutOfSync** (P4) | CLOSED 2026-10-12 — operator ruling 2026-10-12 (S37, `09` §3 decision 232): *„Fixes, can be closed"* — read once, read only: ArgoCD app `monitoring` Synced/Healthy, 29 resources, none OutOfSync, `Deployment/prometheus` Synced; the live Deployment's image and six args equal git (homelab-manifests `eeb009f`). Two channels. | `r884-prometheus-sync.txt` |
## 2026-10-10 (afternoon) — the register-shrink session: delivered rows read back, small fixes, the decision sheet
The full text of the rows below: `git show 7413e0dfab:documentation/backlog/OPEN-ITEMS.md`. Evidence: `documentation/audits/register-shrink-2026-10-10/` unless a row names another place.
File diff suppressed because one or more lines are too long
+17 -2
View File
@@ -54,8 +54,23 @@ Tunnels):
everything — cloudflared logs `No ingress rules were defined` (R-505). Make sure the domain is on
Cloudflare (nameservers active).
Also have ready (optional but recommended): a **Cloudflare API token** with Zone edit rights for the
customer's zone — the hub uses it for geo-restriction management.
Also have ready (optional but recommended): a **Cloudflare API token** for the customer's zone — the hub and the
box use it for geo-restriction management.
**The customer token recipe — the ONLY way a customer token is minted** (operator ruling 2026-10-12, S31, `09` §3
decision 226; R-913). The hub's save check sees what a token can READ, not what it can WRITE (`01` §7), so the guard
is this recipe. Cloudflare → My Profile → API Tokens → Create Token → **Create Custom Token**:
| Field | Value |
|---|---|
| Token name | `felhom-<customer-id>` |
| Permissions | `Zone` · `Zone` · **Read** — and `Zone` · `Zone WAF` · **Edit** (nothing else: no DNS, no Account permission) |
| Zone Resources | **Include** · **Specific zone** · the customer's domain (never „All zones", never an account) |
| Client IP filtering, TTL | leave empty |
Why these two: the hub and the controller call only `GET /zones?name=…` and the zone's `rulesets` (read 2026-10-12:
`hub/internal/cloudflare/unblock.go`, `felhom-controller/controller/internal/cloudflare/{zone,waf}.go`). Paste the token
once into the customer's config; the hub then checks it sees exactly that one zone. Never reuse a token across customers.
**„No TLS Verify" on the public hostname (R-510, 2026-09-15).** The route sends traffic to
`https://traefik`, and traefik answers the name `traefik` with its own default certificate. Tick