Day 3 2026-10-19: passphrase locks once the box uses its own key (D19; hub 0.161.0, controller 0.334.0 floor), every ep0 household gets a prune job (tenantsync v1.3.0), demo-hp address clean; register 163 -> 161 (closed 3, opened R-1167)
gates / gates (push) Successful in 7m23s

This commit is contained in:
2026-10-19 16:34:29 +02:00
parent 97f27a7528
commit a28f162c66
23 changed files with 330 additions and 14 deletions
+9
View File
@@ -16,6 +16,15 @@
> and holds nothing of its own; this file does hold its own content, namely the standing rulings below.
> **2026-10-19 (late afternoon) — controller 0.334.0 is the floor (MinAgent 0.158.1, 16:31 local); hub 0.161.0 DEPLOYED;
> tenantsync v1.3.0 on ep0 (operator yes).** R-1159 (D19 „box key first, then lock"): the passphrase is used AFTER enroll
> (installer `/config` for its git login, the agent bakes it into `bootstrap.json`, the guest's first-boot pull, and —
> until 0.334.0 — every config refresh), so "keys only before a host exists" was not buildable. Now: `GET /box-config`
> with the controller key; the hub refuses passphrase keys (409, `passphrase_keys_refused`) once a ≥ 0.334.0 controller
> has reported since the host record was made — an rc (`0.334.0-rc.N`) counts as older. **A reinstall over a live box
> needs the operator's delete first.** R-1165: every new ep0 namespace gets `prune-<id>`; the hub alarms on a namespace
> with none. demo-hp's .104 clean after the router reservation. Register 163 → 161. `REPORT-day3-2026-10-19.md`.
> **2026-10-19 (afternoon) — controller 0.333.0 is the floor (MinAgent 0.158.1, 12:16 local); hub 0.160.0 DEPLOYED
> (operator yes); ep0 prune `keep-weekly 2` (operator yes, D18).** ep0 change: `prune-demo-hp`/`prune-demo-felhom` only,
> after PBS's own `--dry-run true` (API `proxmox-backup-debug api create …/prune`; it creates no task) showed nothing
+104
View File
@@ -0,0 +1,104 @@
# REPORT — third day brief, 2026-10-19 (the passphrase locks once the box uses its own key; every ep0 household gets a prune job; demo-hp's address)
## In plain words
- **The household passphrase no longer gives a live box's keys** (your D19). Each box now fetches its config with its own
key. The passphrase still works for a new install. A reinstall over a live box needs you to delete the old host first.
- **The brief's first shape could not be built as written.** "Keys only before a box exists" would have broken every
install and every live box's config updates: both use the passphrase after the box record exists. CC stopped and asked;
you chose "box key first, then lock".
- **ep0:** every new household gets its weekly clean-up rule automatically, and the hub alarms on a household without
one. tester-1 and Tester-2 got theirs (your yes).
- **demo-hp's address** stayed demo-hp's for a 30-minute watch after your router change.
- **Live now:** hub 0.161.0, controller 0.334.0 (the floor), tenant-sync v1.3.0 on ep0.
## 1. The passphrase (R-1159; `05` §8 [DESIGN R-1159])
- **Install-order proof** (read before building, the brief's STOP): the passphrase is used AFTER `POST /host-enroll`:
- `scripts/felhom-host-install.sh:2631` → `:480`, and `:3525`: `step_agent_install` and `step_golden` fetch the
download login from `GET /config`;
- `felhom-agent/cmd/felhom-agent/main.go:2835`: the provision bakes the passphrase into the guest's `bootstrap.json`;
- `felhom-controller/controller/internal/bootstrap/bootstrap.go:142`: the guest's first boot pulls `GET /config` with it
(that is how the box gets its own key);
- `cmd/controller/main.go:1176` (`report.ConfigRefresher`): every live box re-pulled with it on each config change;
- `:2317`: the preflight checks it with `/config` on every run.
- **Your answer:** „Box key first, then lock".
- **Built:**
- Hub 0.161.0: `GET /box-config/{id}` (the controller's own key) and `passphraseKeysClosed`. Passphrase keys are
refused (409, no key, operator event `passphrase_keys_refused`) once a ≥ 0.334.0 controller has reported since the
host record was made. `/config`, `/recovery` and the `/host-enroll` reuse path are covered; `/artifacts` has no key
and is unchanged.
- Controller 0.334.0: the refresh pulls with its own key (`report.PullConfigPreferKey`). The passphrase is used only
against an older hub or before the box has a key, and never as a retry after a refused key.
- **Tests:**
- Hub: `TestR1159_PassphraseGivesKeysOnlyUntilTheBoxUsesItsOwn` (5 cases), `TestR1159_BoxPullsItsConfigWithItsOwnKey`,
`TestR1159_BoxConfigMinControllerIsTheReleaseThatPullsWithItsKey`.
- Controller: `TestR1159_RefreshUsesTheBoxKey`, `TestR1159_MainWiresTheKeyFirstRefresh`.
- Red-proofs: lock off → all three keys served; the old pull → 0 key pulls.
- **Live on demo-hp** (`audits/day-2026-10-19c/P4/`; the probe ran on the host from the box's own `bootstrap.json`,
printing status codes only):
- on 0.333.0, the passphrase → 200;
- after its first 0.334.0 report (14:30:13 UTC), the passphrase → **409, no key**, and `passphrase_keys_refused` was
raised (the operator got the mail — CC's probe);
- its own key → 200; a wrong key → 401.
## 2. ep0 prune jobs (R-1165; `07` Plane-2 row)
- **Automatic:** `felhom-tenantsync` v1.3.0 creates `prune-<id>` (keep-weekly 2, 03:30, max-depth 0) with a household's
namespace unless a job already names it; it removes the job with the household. Its `usage` answer lists namespaces no
job covers.
- The hub alarms on that list (`pbsdr_namespace_without_prune_job`). An unread list never reads as "all covered".
- Tests: 12 script cases (`scripts/test_tenantsync_prune.py`, run on every push), the checker and the client; each
red-proved.
- **Dry run before the question** (PBS's own, `P2/ep0-dry-run-tester.txt`; 32 snapshots before and after):
- tester-1: keep 2026-10-11, 2026-10-18; remove 2026-10-04.
- Tester-2: keep 2026-10-04.
- **Answer:** „Yes, add both". **After** (13:35 UTC, `P2/ep0-jobs-after.txt`): `prune-tester-1` and `prune-Tester-2`
(keep-weekly 2, 03:30) beside the demo-hp, demo-felhom and operator jobs.
- **Script on ep0** (14:07 UTC, the same yes): the installed v1.2.0 matched the repo byte for byte; v1.3.0 is installed
(hash = the repo's `36412c6a…`), the old copy kept as `.bak-1.2.0`. Live `usage` → `unpruned: []`; `fingerprint` still
answers.
## 3. demo-hp's address (R-1166)
- A watch every 10 s for 30 minutes (10:59–11:29 UTC): **180 samples, 0 with another MAC**, SSH open throughout.
- The host's own MAC (`cat /sys/class/net/vmbr0/address`): **`7c:d3:0a:77:d9:76`** — the same as your router entry.
- Note: your DHCP pool is .120–.160, so .104 was never in it. The other device (`b8:06:0d:60:45:58`) most likely had .104
set by hand. If it comes back, look there.
## 4. Hub deploy and the controller release
- **Hub 0.161.0**
- Image digest: `sha256:d56c96a59c86ce2d006938d0980803a12949bcbf027cea2834cabd64118838b8` (clean clone at `b6a911c3`).
- Manifest `97f27a75`; only Deployment/hub was synced. Result: Synced/Healthy; the running digest is the built one;
0 `[ERROR]` lines; the ep0 read at 14:13 UTC was OK, with no alarm (every namespace has a job).
- Checks: the floor was read back; host and controller reports from demo-hp, Tester 1 and demo-felhom arrived after
the restart.
- **Controller 0.334.0**
- Image digest: `sha256:deeabe59d932cd60f8939a37bccf94f145555101fc7be64b6c5cc15f3557031a` (from `546eef3`); rc.1 ran
on demo-hp only.
- Per-box floors at 14:16:11 UTC. Tester 1 and demo-felhom moved at once; demo-hp moved at 14:30 (from rc.1).
- Box walk (`P5/`): 0 unhealthy, 0 refusals, catalog in sync; https-only 28 OK, 0 failures; „Fájlok ↗" with no second
login on demo-hp and Tester 1.
- **Global floor 0.334.0 / MinAgent 0.158.1 at 14:31:44 UTC.** The per-box floors were cleared.
## 5. Versions, commits, rows
- **Hub 0.161.0** (`b6a911c3`, manifest `97f27a75`); **controller 0.334.0** (`546eef3`); **tenantsync v1.3.0** (in
`b6a911c3`, on ep0). No agent change. No i18n fixture changed (no page changed).
- **Rows: 163 before, 161 after; closed 3** (R-1159, R-1165, R-1166), **opened 1** (R-1167).
- **System poster:** facts file only (`felhom-system-poster.facts.md`: new households get their prune job); the drawing's
text („one per week, 2 weeks") is still true.
- **Teardown:**
- Machine: rc.1 only on demo-hp 9201, now on the release; the probe scripts removed from demo-hp.
- Host: none.
- Hub: per-box floors cleared; one `passphrase_keys_refused` event on demo-hp (CC's probe).
- ep0: two jobs added and the script replaced, both on the operator's yes; nothing else.
## Observations
1. The installer prints „unexpected hub status 409" instead of the hub's sentence when a re-run meets a live box. FILED: R-1167.
2. The brief's „keys only while no host exists" would have broken every install and every live box's config refresh. NOT-A-FINDING: measured before building and put to the operator, who chose the two-step shape (D19); recorded in `05` §8.
3. A reinstall over a live box now gets two operator mails (R-1154's reinstall warning and the refusal). NOT-A-FINDING: both are true and give the same instruction; the 1-hour cooldown limits repeats.
4. An rc build (`0.334.0-rc.N`) counts as older than 0.334.0, so the lock stays open on an rc. NOT-A-FINDING: by design (an rc runs on demo-hp only, and the floor moves it to the release); proven on rc.1 (200) and the release (409).
5. demo-hp's .104 lies outside the router's DHCP pool, yet another MAC held it this morning. NOT-A-FINDING: the router reservation fixed it (30 minutes clean); the other device's source is noted in R-1166's closing text in case it returns.
+2 -3
View File
File diff suppressed because one or more lines are too long
@@ -242,6 +242,24 @@ while"). Re-issuing on the reuse path would give those powers to anyone holding
switched off or silent. The cost of the loud shape is two operator acts per reinstall that kept its old record. Both
event types are hub-origin only. `hub/internal/api/reenroll.go`, pinned by `hub/internal/api/r1154_reenroll_test.go`.
**What the household's retrieval passphrase can get [DESIGN, 2026-10-19 — R-1159, operator decision D19 „box key
first, then lock"; hub v0.161.0 + controller v0.334.0].** The passphrase is the household's install secret: a NEW box
needs it (the installer's preflight, download login and enroll; the guest's first boot, `bootstrap.json` → `GET
/config`, which is how the box gets its own hub key). Measured the same day, it also gave anyone who held it the box's
keys for good, and through them every off-site key action (`audits/day-2026-10-19b/P2/`). **The rule:** the passphrase
gives keys only until the box uses its own — `GET /config`, `GET /recovery` and the reuse path of `POST /host-enroll`
answer **409 with no key** (and the operator gets `passphrase_keys_refused`) once a host record exists AND a controller
report arrived since it was made AND that controller pulls its config with its own key (≥ 0.334.0, `GET /box-config`,
`boxConfigMinController`). Open for a first install, the install in progress, a reinstall after the operator deleted
the old host, and a box on an older controller (which still refreshes with the passphrase — the lock arms box by box
as the floor moves). A reinstall over a live box therefore needs the operator's delete first, which R-1154 already
made the rule. **Why this shape and not „before a host exists":** the install itself uses the passphrase AFTER enroll
(`felhom-host-install.sh` step_agent_install → `/config`; the agent bakes it into `bootstrap.json`; the guest pulls with
it), and so did every live box's config refresh — measured from the code before building, and put to the operator.
`hub/internal/api/passphrase_scope.go`; pinned by `r1159_passphrase_scope_test.go`. Proven live on demo-hp
(2026-10-19 14:30 UTC): before 0.334.0 → 200; after its first 0.334.0 report → 409, no key, the event raised; the box's
own key → 200.
**[FACT, 2026-10-19 — R-1158, hub v0.160.0] What the MINT path's off-site re-issue still does.** It resets the Storage
Box sub-account password and stores the new one — a password only the hub itself uses (`offsitekeys.TargetFor`, the key
registrar's login). No box receives it since v0.127.0 (`consume-password` answers 410; `ConsumeOneTimeSecret` has no
@@ -492,8 +492,11 @@ data drives** — 916 GB on demo-felhom, 938 GB on demo-hp.
> ISO weeks, so a flood inside a week replaces only that week's copy and the previous week's survives, however many runs
> get through. Before the change, PBS's own dry run and an offline model of its selection both showed the new rule
> removing nothing on either box (`audits/day-2026-10-19b/P1/`). DooPlex's copy of ep0 already prunes by time
> (`prune-ep0-copy`, `keep-weekly 8`, decision 71). **A household namespace gets no prune job by itself:** `tester-1` and
> `Tester-2` have none and keep every weekly copy (R-1165). **[DESIGN] The local tier keeps one copy per day (R-1149, operator decision D17,
> (`prune-ep0-copy`, `keep-weekly 8`, decision 71). **[FACT] Every household namespace gets its prune job when it is made**
> (R-1165, 2026-10-19): `felhom-tenantsync` v1.3.0 creates `prune-<id>` (keep-weekly 2, 03:30) with the namespace and
> removes it with the household; the hub alarms on a namespace no job covers (`pbsdr_namespace_without_prune_job`,
> from the 15-minute ep0 read). `tester-1` and `Tester-2` got theirs by hand on the operator's yes (13:35 UTC; the
> dry run: tester-1's 2026-10-04 copy goes at the next run, nothing on Tester-2). **[DESIGN] The local tier keeps one copy per day (R-1149, operator decision D17,
> 2026-10-19; agent 0.160.0):** `keep-daily=3` instead of keep-last 3 — the same at most three archives, but extra runs on
> one day replace only that day's copy, so with the cap a day of flooding leaves the two earlier days' copies
> (`TestBackupCap_R1149_LocalFloodKeepsOlderDays`). Measured at the first new-rule prune on demo-hp (retention 1: the
@@ -99,7 +99,7 @@ Footnote: "W = the household's backup window start, 02:30 by default. Tiers 1–
- one namespace **per household** (whole-guest copies, each household's own key),
with a NEUTRAL note under it (not a red gap box): "By design: each household holds the key to its own copies; the operator cannot open them without their recovery code." — *fix (c), 2026-10-09: this is a property of the design, not a gap, and a red box said the opposite of what it means.*
- the **`operator`** namespace: two groups — **"hub database"** and **"DooPlex copy"** (code, password manager, secrets, signing keys).
Prune jobs on ep0: households keep one copy per week for 2 weeks (`keep-weekly 2`, R-1162, 2026-10-19 — a household without a job keeps all, R-1165); `operator` keeps 14 daily + 8 weekly.
Prune jobs on ep0: households keep one copy per week for 2 weeks (`keep-weekly 2`, R-1162, 2026-10-19; every new household's job is made with its namespace, R-1165); `operator` keeps 14 daily + 8 weekly.
Mark ep0 with a small shield: "**Protected** — holds the only off-premises copy of real household data."
- **Hetzner Storage Box** (grey): the shared box for the households' per-app restic copies (Tier 3), one sub-account per household, add-only keys.
- **Mail service** (grey, "Resend"): every mail the hub sends — to the operator and to households.
@@ -0,0 +1,43 @@
Mon Oct 19 11:01:33 UTC 2026
{"id":"prune-demo-felhom","ns":"demo-felhom","keep-last":null,"keep-weekly":2,"schedule":"03:30"}
{"id":"prune-operator-hubdb","ns":"operator","keep-last":null,"keep-weekly":8,"schedule":"03:45"}
{"id":"prune-demo-hp","ns":"demo-hp","keep-last":null,"keep-weekly":2,"schedule":"03:30"}
ns/Tester-2/ct/9201/2026-10-04T16:31:13Z
ns/demo-felhom/ct/9201/2026-10-06T04:21:09Z
ns/demo-felhom/ct/9201/2026-10-13T04:22:51Z
ns/demo-hp/ct/9201/2026-10-08T20:34:27Z
ns/demo-hp/ct/9201/2026-10-15T20:38:47Z
ns/operator/host/dooplex-gitea/2026-10-09T22:23:15Z
ns/operator/host/dooplex-gitea/2026-10-10T22:22:52Z
ns/operator/host/dooplex-gitea/2026-10-11T22:23:50Z
ns/operator/host/dooplex-gitea/2026-10-12T22:22:36Z
ns/operator/host/dooplex-gitea/2026-10-13T22:21:39Z
ns/operator/host/dooplex-gitea/2026-10-14T22:21:12Z
ns/operator/host/dooplex-gitea/2026-10-15T22:20:59Z
ns/operator/host/dooplex-gitea/2026-10-16T22:21:23Z
ns/operator/host/dooplex-gitea/2026-10-17T22:22:32Z
ns/operator/host/dooplex-gitea/2026-10-18T22:22:01Z
ns/operator/host/dooplex-hub/2026-10-06T00:32:21Z
ns/operator/host/dooplex-hub/2026-10-07T00:31:55Z
ns/operator/host/dooplex-hub/2026-10-08T00:31:27Z
ns/operator/host/dooplex-hub/2026-10-09T00:31:46Z
ns/operator/host/dooplex-hub/2026-10-10T00:30:56Z
ns/operator/host/dooplex-hub/2026-10-11T00:30:55Z
ns/operator/host/dooplex-hub/2026-10-12T00:32:07Z
ns/operator/host/dooplex-hub/2026-10-13T00:31:03Z
ns/operator/host/dooplex-hub/2026-10-14T00:31:15Z
ns/operator/host/dooplex-hub/2026-10-15T00:32:14Z
ns/operator/host/dooplex-hub/2026-10-16T00:30:12Z
ns/operator/host/dooplex-hub/2026-10-17T00:31:09Z
ns/operator/host/dooplex-hub/2026-10-18T00:31:48Z
ns/operator/host/dooplex-hub/2026-10-19T00:32:07Z
ns/tester-1/ct/9201/2026-10-04T19:57:18Z
ns/tester-1/ct/9201/2026-10-11T20:02:13Z
ns/tester-1/ct/9201/2026-10-18T20:04:51Z
== tester-1 keep-weekly 2 (PBS dry-run)
2026-10-04T19:57:18Z REMOVE
2026-10-11T20:02:13Z keep
2026-10-18T20:04:51Z keep
== Tester-2 keep-weekly 2 (PBS dry-run)
2026-10-04T16:31:13Z keep
32
@@ -0,0 +1,9 @@
Prune job created: prune-tester-1
Prune job created: prune-Tester-2
Mon Oct 19 13:35:57 UTC 2026
{"id":"prune-demo-felhom","ns":"demo-felhom","max-depth":0,"keep-weekly":2,"keep-daily":null,"schedule":"03:30"}
{"id":"prune-demo-hp","ns":"demo-hp","max-depth":0,"keep-weekly":2,"keep-daily":null,"schedule":"03:30"}
{"id":"prune-tester-1","ns":"tester-1","max-depth":0,"keep-weekly":2,"keep-daily":null,"schedule":"03:30"}
{"id":"prune-Tester-2","ns":"Tester-2","max-depth":0,"keep-weekly":2,"keep-daily":null,"schedule":"03:30"}
{"id":"prune-operator-hubdb","ns":"operator","max-depth":0,"keep-weekly":8,"keep-daily":14,"schedule":"03:45"}
32
@@ -0,0 +1,3 @@
ARP watch for 192.168.0.104 (demo-hp vmbr0 7c:d3:0a:77:d9:76), every 10 s for 30 min, from 10:59:56 UTC; a line is written on every change
10:59:56 mac=7c:d3:0a:77:d9:76 ssh=open
end 11:29:59: 180 samples, 0 not demo-hp's MAC
@@ -0,0 +1,3 @@
config file found: yes; key length: 64
GET /box-config with the box's own key: HTTP 200; top-level fields: assets backup customer git hub infrastructure logging monitoring notifications offsite paths self_update stacks system web
control, a wrong key: HTTP 401
@@ -0,0 +1,8 @@
Mon Oct 19 14:07:28 UTC 2026
36412c6a24e1a1e4a39627d0eb2c6e1441ebd12efe4d92951b4e13d017642ed6 /usr/local/bin/felhom-tenantsync
# felhom-tenantsync v1.3.0 — the offsite endpoint's per-customer PBS tenancy surface (PBS DR tier
== usage (read-only)
{"status":"ok","total":105089261568,"used":14276538368,"avail":90795945984,"unpruned_checked":true,"unpruned":[]}
== fingerprint op still answers
ok
prune-Tester-2 prune-demo-felhom prune-demo-hp prune-operator-hubdb prune-tester-1
@@ -0,0 +1,5 @@
b32b2f3579ce44048af6352606764469693a65df549cc27a0106c734c7a8bb0f /usr/local/bin/felhom-tenantsync
# felhom-tenantsync v1.2.0 — the offsite endpoint's per-customer PBS tenancy surface (PBS DR tier
-rwxr-xr-x 1 root root 12004 Jul 17 19:15 /usr/local/bin/felhom-tenantsync
-rwxr-xr-x 1 root root 8253 Jul 10 18:57 /usr/local/bin/felhom-tenantsync.bak-1.0.0
-rwxr-xr-x 1 root root 10480 Jul 17 11:12 /usr/local/bin/felhom-tenantsync.bak-1.1.0
@@ -0,0 +1,5 @@
GET /config with the box's passphrase: HTTP 409; carries hub.api_key: 0
refusal text: Conflict: this customer already has a live box, which uses its own key. The passphrase gives no keys now. For a reinstall the operator deletes the old host first.
config file found: yes; key length: 64
GET /box-config with the box's own key: HTTP 200; top-level fields: assets backup customer git hub infrastructure logging monitoring notifications offsite paths self_update stacks system web
control, a wrong key: HTTP 401
@@ -0,0 +1 @@
GET /config with the box's passphrase: HTTP 200; carries hub.api_key: 0
@@ -0,0 +1,4 @@
## per-customer floors 0.334.0 / MinAgent 0.158.1 — 14:16:11 UTC
tester-1: HTTP/1.1 303 See Other Location: /customers/tester-1?flash=floor_set
demo-hp: HTTP/1.1 303 See Other Location: /customers/demo-hp?flash=floor_set
demo-felhom: HTTP/1.1 303 See Other Location: /customers/demo-felhom?flash=floor_set
@@ -0,0 +1 @@
14:30:08 demo-hp=0.334.0 demo-felhom=0.334.0 tester-1=0.334.0
@@ -0,0 +1,27 @@
=== demo-hp (enkisfelhom.hu) 14:31:15
containers: 24 running; not healthy/running: traefik Up 3 days;
unhealthy: 0
2026/10/19 14:30:06 updater.go:855: [INFO] [selfupdate] Post-update startup: update successful (0.334.0-rc.1 → 0.334.0)
allow-list refusals since start: 0
2026/10/19 14:30:06 sync.go:142: [INFO] [sync] Initial sync: Sablonok naprakészek — nincs változás
installed apps (stacks with app.yaml): 12
public https://felhom.enkisfelhom.hu/: 302
public https://files.enkisfelhom.hu/: 200
=== demo-felhom (demo-felhom.eu) 14:31:18
containers: 9 running; not healthy/running: traefik Up 3 days;
unhealthy: 0
2026/10/19 14:16:13 [INFO] [selfupdate] Post-update startup: update successful (0.333.0 → 0.334.0)
allow-list refusals since start: 0
2026/10/19 14:16:13 [INFO] [sync] Initial sync: Sablonok naprakészek — nincs változás
installed apps (stacks with app.yaml): 5
public https://felhom.demo-felhom.eu/: 302
public https://files.demo-felhom.eu/: 200
=== tester-1 (enkicsifelhom.hu) 14:31:19
containers: 10 running; not healthy/running: traefik Up 3 days;
unhealthy: 0
2026/10/19 14:16:21 [INFO] [selfupdate] Post-update startup: update successful (0.333.0 → 0.334.0)
allow-list refusals since start: 0
2026/10/19 14:16:22 [INFO] [sync] Initial sync: Sablonok naprakészek — nincs változás
installed apps (stacks with app.yaml): 3
public https://felhom.enkicsifelhom.hu/: 302
public https://files.enkicsifelhom.hu/: 200
@@ -0,0 +1,33 @@
control NEGATIVE (a stub answering 200 over plain http): convicted, as it must (200 -)
== tester-1 (enkicsifelhom.hu): 6 public name(s)
OK control POSITIVE http://felhom.enkicsifelhom.hu/r1033-check?q=1 -> 301 https://felhom.enkicsifelhom.hu/r1033-check?q=1
OK http://files.enkicsifelhom.hu/r1033-check?q=1 -> 301 https://files.enkicsifelhom.hu/r1033-check?q=1
OK http://no-such-name-r1033.enkicsifelhom.hu/r1033-check?q=1 -> 301 https://no-such-name-r1033.enkicsifelhom.hu/r1033-check?q=1
OK http://paperless.enkicsifelhom.hu/r1033-check?q=1 -> 301 https://paperless.enkicsifelhom.hu/r1033-check?q=1
OK http://paste.enkicsifelhom.hu/r1033-check?q=1 -> 301 https://paste.enkicsifelhom.hu/r1033-check?q=1
OK http://wiki.enkicsifelhom.hu/r1033-check?q=1 -> 301 https://wiki.enkicsifelhom.hu/r1033-check?q=1
== demo-hp (enkisfelhom.hu): 14 public name(s)
OK http://arcade.enkisfelhom.hu/r1033-check?q=1 -> 301 https://arcade.enkisfelhom.hu/r1033-check?q=1
OK http://books.enkisfelhom.hu/r1033-check?q=1 -> 301 https://books.enkisfelhom.hu/r1033-check?q=1
OK http://docs.enkisfelhom.hu/r1033-check?q=1 -> 301 https://docs.enkisfelhom.hu/r1033-check?q=1
OK http://felhom.enkisfelhom.hu/r1033-check?q=1 -> 301 https://felhom.enkisfelhom.hu/r1033-check?q=1
OK http://files.enkisfelhom.hu/r1033-check?q=1 -> 301 https://files.enkisfelhom.hu/r1033-check?q=1
OK http://gist.enkisfelhom.hu/r1033-check?q=1 -> 301 https://gist.enkisfelhom.hu/r1033-check?q=1
OK http://ha.enkisfelhom.hu/r1033-check?q=1 -> 301 https://ha.enkisfelhom.hu/r1033-check?q=1
OK http://no-such-name-r1033.enkisfelhom.hu/r1033-check?q=1 -> 301 https://no-such-name-r1033.enkisfelhom.hu/r1033-check?q=1
OK http://papir.enkisfelhom.hu/r1033-check?q=1 -> 301 https://papir.enkisfelhom.hu/r1033-check?q=1
OK http://paste.enkisfelhom.hu/r1033-check?q=1 -> 301 https://paste.enkisfelhom.hu/r1033-check?q=1
OK http://pdf.enkisfelhom.hu/r1033-check?q=1 -> 301 https://pdf.enkisfelhom.hu/r1033-check?q=1
OK http://plex.enkisfelhom.hu/r1033-check?q=1 -> 301 https://plex.enkisfelhom.hu/r1033-check?q=1
OK http://travel.enkisfelhom.hu/r1033-check?q=1 -> 301 https://travel.enkisfelhom.hu/r1033-check?q=1
OK http://wiki.enkisfelhom.hu/r1033-check?q=1 -> 301 https://wiki.enkisfelhom.hu/r1033-check?q=1
== demo-felhom (demo-felhom.eu): 8 public name(s)
OK http://felhom.demo-felhom.eu/r1033-check?q=1 -> 301 https://felhom.demo-felhom.eu/r1033-check?q=1
OK http://files.demo-felhom.eu/r1033-check?q=1 -> 301 https://files.demo-felhom.eu/r1033-check?q=1
OK http://gist.demo-felhom.eu/r1033-check?q=1 -> 301 https://gist.demo-felhom.eu/r1033-check?q=1
OK http://no-such-name-r1033.demo-felhom.eu/r1033-check?q=1 -> 301 https://no-such-name-r1033.demo-felhom.eu/r1033-check?q=1
OK http://prowlarr.demo-felhom.eu/r1033-check?q=1 -> 301 https://prowlarr.demo-felhom.eu/r1033-check?q=1
OK http://radarr.demo-felhom.eu/r1033-check?q=1 -> 301 https://radarr.demo-felhom.eu/r1033-check?q=1
OK http://sonarr.demo-felhom.eu/r1033-check?q=1 -> 301 https://sonarr.demo-felhom.eu/r1033-check?q=1
OK http://torrent.demo-felhom.eu/r1033-check?q=1 -> 301 https://torrent.demo-felhom.eu/r1033-check?q=1
== every public name redirects http -> https
@@ -0,0 +1,16 @@
== demo-hp
dashboard sign-in: 302
the Fajlok button goes to: https://files.enkisfelhom.hu/_felhom/sso
the pass answer goes to: https://files.enkisfelhom.hu/files/; files cookie set: 1
FileBrowser with the files cookie: felhom-household admin: False modify: True
FileBrowser without it, a fresh browser: HTTP 401
dashboard sign-out: 302
after the sign-out, the files cookie sends the browser to: https://felhom.enkisfelhom.hu/__files/open?to=%2Ffiles%2F
== tester-1
dashboard sign-in: 302
the Fajlok button goes to: https://files.enkicsifelhom.hu/_felhom/sso
the pass answer goes to: https://files.enkicsifelhom.hu/files/; files cookie set: 1
FileBrowser with the files cookie: felhom-household admin: False modify: True
FileBrowser without it, a fresh browser: HTTP 401
dashboard sign-out: 302
after the sign-out, the files cookie sends the browser to: https://felhom.enkicsifelhom.hu/__files/open?to=%2Ffiles%2F
@@ -0,0 +1,13 @@
## global floor 0.334.0 / MinAgent 0.158.1 — 14:31:44 UTC
HTTP/1.1 303 See Other
Location: /configuration?flash=floor_set
## clear per-box floors
tester-1: HTTP/1.1 303 See Other Location: /customers/tester-1?flash=floor_set
demo-hp: HTTP/1.1 303 See Other Location: /customers/demo-hp?flash=floor_set
demo-felhom: HTTP/1.1 303 See Other Location: /customers/demo-felhom?flash=floor_set
## read back (hub.db + WAL)
min_controller_version|0.334.0
min_controller_version_declared_min_agent|0.334.0=0.158.1
demo-felhom|
demo-hp|
tester-1|
+8
View File
@@ -26,6 +26,14 @@
---
## 2026-10-19 (third day brief) — the passphrase locks once the box uses its own key, every ep0 household gets a prune job, demo-hp's address (full text of each row: `git show 97f27a75:documentation/backlog/OPEN-ITEMS.md`)
| Row | What | Closed | Evidence |
|---|---|---|---|
| **R-1159** | **The household's retrieval passphrase alone reaches the box's keys and through them the whole off-site key surface — MEASURED 2026-10-19** (P3) | CLOSED 2026-10-19 — **operator decision D19 = „box key first, then lock" (13:3x; the brief's „keys only before a box exists" was measured to break every install and every live box's config refresh, and put back to the operator). Hub 0.161.0 (deployed on the operator's yes) + controller 0.334.0 (the floor since 14:31 UTC): a live box refreshes with its own key (`GET /box-config`); `/config`, `/recovery` and the `/host-enroll` reuse path answer 409 with no key once the box's 0.334.0+ controller has reported since its host record was made, and the operator gets `passphrase_keys_refused`. Live on demo-hp: passphrase 200 on 0.333.0 → 409, no key, the event raised, after its first 0.334.0 report; its own key 200, a wrong key 401. Red-proved (lock off → all three keys served). Residual: the installer's wording of the 409 (R-1167). | `audits/day-2026-10-19c/P4/lock-before-demo-hp.txt`, `lock-after-demo-hp.txt`, `box-key-pull-demo-hp.txt` |
| **R-1165** | **A household namespace on ep0 gets no prune job by itself, so its whole-guest off-site copies are never pruned.** (P3) | CLOSED 2026-10-19 — **tenantsync v1.3.0 on ep0 (14:07 UTC, the operator's yes; the old copy kept as `.bak-1.2.0`) + hub 0.161.0**: provision/reissue create `prune-<id>` (keep-weekly 2, 03:30) unless a job names the namespace, deprovision removes it; the hub alarms on a namespace no job covers (`pbsdr_namespace_without_prune_job`, from the 15-minute ep0 read; an unread list never reads as all covered). Tests: 12 script cases + the checker + the client, each red-proved. tester-1 and Tester-2 got their jobs on the operator's yes after PBS's own dry run (tester-1's 2026-10-04 copy goes at the next run; nothing on Tester-2). Live: ep0's `usage` answers `unpruned: []`. | `audits/day-2026-10-19c/P2/`, `P4/ep0-tenantsync-after.txt` |
| **R-1166** | **Another device takes demo-hp's address (192.168.0.104) every few minutes.** (P3) | CLOSED 2026-10-19 — **the operator reserved 192.168.0.104 for demo-hp in the router (12:55)**; a 30-minute watch every 10 s (10:59–11:29 UTC): 180 samples, 0 with another MAC, SSH open throughout. demo-hp's own `vmbr0` MAC `7c:d3:0a:77:d9:76` matches the router entry. The router's DHCP pool is .120–.160, so .104 was never in it: the other device (`b8:06:0d:60:45:58`) most likely had .104 set by hand or from another DHCP server — worth a look if it returns. | `audits/day-2026-10-19c/P3/demo-hp-arp-watch.txt` |
## 2026-10-19 (second day brief) — ep0 one copy per week, the passphrase measured, hub 0.160.0, controller 0.333.0, gofmt gates (full text of each row: `git show 73f38992:documentation/backlog/OPEN-ITEMS.md`)
| Row | What | Closed | Evidence |
+5 -7
View File
@@ -112,7 +112,7 @@ match what the reader sees is how an instrument stops being believed (R-421). It
**nothing was proven on the day this line was drawn** and a stopping line that moves a status is a
stopping line that lies.
## Install & onboarding — 7 rows (P3 5, P4 2)
## Install & onboarding — 8 rows (P3 5, P4 3)
| ID | Category | Sev | What | State | Blocked on | Next action | Owner |
|---|---|---|---|---|---|---|---|
@@ -123,6 +123,7 @@ stopping line that lies.
| **R-993** | Install & onboarding | P3 | **Operator: approve the minimum machine in `documentation/audits/sizing-2026-10-13/SIZING.md` §5** (the input R-991's hardware page needs). MEASURED 2026-10-13 on the N100 demo box (61 apps deployed and removed through the product, 15 min at rest each; heavy apps with one real action). Proposal: **minimum** x86-64, 4 cores (N100 class), **8 GB**, 256 GB SSD; **recommended 16 GB**, 512 GB SSD; out: ARM / Raspberry Pi, Synology-QNAP, routers, cloud VPS. Worked from the floor (2.5 GB for the whole box with no apps) + Immich, Jellyfin, Nextcloud, Home Assistant, Vaultwarden, Paperless-ngx at their peaks (5.0 GB) + 50 % headroom ≈ 12.3 GB. 8 GB runs that set without Immich; 16 GB runs all of it twice over. Data for the estimator: `sizing.json`. **2026-10-13 (estimator session) — the installer accepts a host BELOW this minimum:** `autosize_guest_caps` has no floor on the host, only on the guest; on a 4 GB box (MemTotal ≈ 3.5 GiB) the guest gets host − 1 GB ≈ 2.4 GB and Proxmox keeps ≈ 1 GB, where it used ≈ 2.0 GB on the N100 (SIZING.md §1). Whatever minimum is approved, nothing enforces it at install today. The three-verdict wording of §5 now matches the estimator page (`/gepigeny`, R-997). | **WAITING-ON-OPERATOR** — filed 2026-10-13 | the operator's decision | Operator: approve or change §5. If nothing is done: no hardware page; a buyer with a Raspberry Pi can still download the installer (R-991 stays open) | operator |
| **R-494** | Install & onboarding | P4 | **NARROWED 2026-09-14 by operator ruling → [P3-LOW] the hub COULD create the tunnel at customer creation, for a domain already on Cloudflare. Not blocking: every customer has their own domain and the operator creates the tunnel per day-0 A.1 (`architecture/01-topology-and-trust.md`).** *Original finding, kept:* **[P1-HIGH] A new customer's dashboard has NO reachable address unless the operator hand-makes a Cloudflare tunnel — the link in the setup-code mail is dead.** MEASURED 2026-09-14 on a fresh install from the public ISO (drill intervention **I1**): the claim mail points at `https://felhom.drill0242.felhom.eu`; that name has **no A and no AAAA** record (`dig @1.1.1.1`, control `felhom.enkisfelhom.hu` resolves); the hub has **no tunnel- or DNS-creation code** (`hub/internal/cloudflare/` holds only geo-rule removal; `cf_tunnel_token` is a pasted, optional form field, `configs.go:1478`) — day-0 runbook A.1 makes it a manual Cloudflare-dashboard step that nothing on the customer-create page asks for; the box's own split-horizon resolver on the appliance LAN IP answered `google.com` but not the dashboard name at 13:27:39Z; the agent applied the record at **13:27:44Z** (`lanresolver: applied split-horizon record … ip=192.168.0.158`, 3 m 46 s after the controller started), so the box CAN answer the name — **but only to a device that uses the box as its DNS server, and no document, screen or mail tells a household to do that**; the router and the installer-offered DNS answer nothing. The page was reachable only at the guest's LAN address with the name forced (`curl --resolve …:443:192.168.0.158`). **A volunteer could not have done that.** **What it needs:** an operator ruling — the hub creates the tunnel and DNS at customer creation, or the product gives a household a LAN address that works with no DNS change. | **READY — rank P3-LOW; owner: CC** **Re-ranked 2026-10-03: P3→P4: operator-side automation; the operator creates the tunnel by hand per the day-0 runbook.** | — | — | CC |
| **R-504** | Install & onboarding | P4 | **[P3-LOW] `iso.felhom.eu` cannot show an index page on its own — its root returns 404, and the download page lives on the website instead.** MEASURED 2026-09-14: `https://iso.felhom.eu/` and `/index.html` → 404; only named objects answer. The host is an R2 bucket behind a custom domain; whether R2 would serve an uploaded `index.html` at `/` was **not measured** (uploading anything to the public bucket is a publication). The ISO v1.27.0 task puts the Hungarian download page at `felhom.eu/letoltes` (published with the ISO, after the operator's yes). **Remaining:** a redirect from `iso.felhom.eu/` to that page needs a Cloudflare rule the session has no credential for. | **WAITING-ON-OPERATOR — rank P3-LOW; owner: operator (Cloudflare rule)** **Re-ranked 2026-10-03: P3→P4: households are sent to the website's download page; the bare address is cosmetic.** | — | — | operator |
| **R-1167** | Install & onboarding | P4 | **The installer does not word the hub's new 409 („this customer already has a live box").** Since hub 0.161.0 (R-1159) a re-run of `felhom-host-install.sh` against a customer whose box is live (a repair, a late `--resume`, a reinstall without the operator's delete) gets 409 from `GET /config` at its preflight; the script prints only „unexpected hub status 409 on config preflight" (`step_preflight`), not the hub's sentence (delete the old host first). The installer is published (felhom.eu/scripts), so the fix is an installer release. Found 2026-10-19. | OPEN — filed 2026-10-19 | `scripts/felhom-host-install.sh` `step_preflight` | Next installer release: a 409 case that prints the hub's body | CC |
## Apps & catalog — 13 rows (P3 5, P4 8)
@@ -152,7 +153,7 @@ stopping line that lies.
| **R-683** | App updates | P3 | **[P3-LOW] Watch: after a power cut during an update's health check, the hold named an HOUR-OLD second-drive copy, not the one the update's own backup should have just made.** 2026-09-24 chaos round 3 (nextcloud, `backup_max_age: 1m`): no `backing-up` phase was seen and the hold named Tier 2 at 13:04 for an update pressed at 14:04; the pre-cut controller log was lost with the container (the runner now saves it at arm time — R-320). Round 11, the same action without a power cut, named a fresh 14:34 copy and logged the Tier-2 copy. The sentence was TRUE (it named the copy it offered); the question is why the update did not back up first. Not reproduced; watch the next power-cut drill. `audits/night-2026-09-24/E/round-03*.json`, `E/round-11-controller-pre.log` | **OPEN — P3; owner: CC (watch)** | — | — | CC |
| **R-785** | App updates | P3 | **[P3-LOW] SparkyFitness is pinned 11 releases and a major behind upstream (v0.17.3; upstream v1.7.3, v1.6.0 dated 2026-07-24).** READ 2026-10-01 (`audits/visitors-2026-10-01/C/bench/C1-previous-tag.txt`). **Needs:** an update walk 0.17 → 1.x through the ladder (bench + box), after R-784 is decided. | **OPEN — rank P3-LOW; owner: CC (after R-784)** | — | — | CC |
## Backup & restore — 28 rows (P2 3, P3 14, P4 11)
## Backup & restore — 27 rows (P2 3, P3 13, P4 11)
| ID | Category | Sev | What | State | Blocked on | Next action | Owner |
|---|---|---|---|---|---|---|---|
@@ -183,7 +184,6 @@ stopping line that lies.
| **R-32** | Backup & restore | P4 | **[P2-HIGH] RESET must purge the customer base dir; the orphan card must stay honest; unattributed bytes must be visible.** The rehearsal's S7 said in advance that an orphan card would BE a finding — and one appeared (16:58:14). Cause: RESET's `"hetzner":"ok"` leg destroys the sub-account, but **a Hetzner sub-account is an access-control object, not a data object** — its directory survives, so re-enabling offsite recreated an account over the previous lifecycle's ciphertext, encrypted under a key that same RESET had destroyed. **MIGRATED FROM `ROADMAP.md` 2026-08-22 (R-369) — originally filed 2026-07-21, size M, roadmap state `idea`.** Moved verbatim; nothing added or reinterpreted. The roadmap keeps its copy as history, marked moved. | **OPEN — migrated from ROADMAP 2026-08-22, rank unchanged** **2026-10-06 night: design written** (`audits/night-burndown-2026-10-06/design-R-32.md`) — pick A: purge the repo + set-aside copies through the sub-account's own password login BEFORE deleting it (no main-account credential); the „repo data destroyed" comment at `hub/internal/offsite/offsite.go:276-280` is false for the shared tier; part (3) waits on a read-only measurement (`du` on port 23). Operator: agree to route A. **2026-10-07 07:58: `09` §3 decision 167 — option A yes, and delete the old RESET leftovers once.** **2026-10-07 (Part D step 2, read only):** the hub shows bx11 holding 3.3 GB of data while the four live customers account for ~0.6 GB → **~2.7 GB belongs to no live customer**; no live customer shows a set-aside folder, so the leftovers are in homes whose sub-account is gone, reachable only by the pool box's MAIN account. **The one-time clean-up is STOPPED:** no main-account login for bx11 is held on DooPlex (the API token in the credentials file sees no box). Needs: the main-account login, or the operator lists and deletes in the Hetzner console (`audits/day-2026-10-07/D/D2-listing-readonly.txt`). **2026-10-07: option A DELIVERED (hub 0.142.0):** RESET purges the repo and every `.orphaned-*` through the sub-account's own login before deleting it, and keeps the sub-account if the purge fails (red-proved, `audits/day-2026-10-07/D/`). Not yet seen live (no scratch RESET today). **The one-time clean-up of the ~2.7 GB of leftovers is STOPPED:** it needs the pool box's main-account login (see above). **2026-10-07 14:43 — `09` §3 decision 173: leave the ~2.7 GB of old leftovers for now; re-ranked P2 → P4.** Option A itself is delivered (hub 0.142.0). What stays open is ONLY the leftovers: they were computed (box total minus live customers), never listed, and deleting them needs the pool box's MAIN login, which reaches every customer's data. **Blocker: a safe main-account path.** | — | **Ruling from the run (three parts, deliberately separate):** (1) because RESET destroys custody, the ciphertext it leaves behind is unrecoverable **BY DESIGN** → RESET gains a **main-account purge of the customer base dir** (the existing operator ack already covers it); (2) the **move-aside guard STAYS** for reinstall-*without*-RESET — there custody survives and the card's "history recoverable" promise is true (R-26 depends on exactly that); (3) the operator **Restic tab shows per-customer directory bytes vs attributed snapshot bytes**, so dead data cannot hide. Measured on the pool box that night: **49 M attributed** (2 snapshots, 48.717 MiB) against **1.4 G + 3.0 M unattributed** across TWO `.orphaned-*` dirs. Evidence `restic-and-pool.txt` | CC |
| **R-1087** | Backup & restore | P4 | **Whole-box copies left on the OLD local target are never pruned after the target moves.** MEASURED 2026-10-15 (storage-identity build B space check): the N100's host root (`local`) holds three copies from 2026-07-28 (5.5–5.7 GB each, ≈ 16.8 GB); its agent's `local_backup_target` is now `felhom-backup` (the USB disk, retention 3), and retention prunes only the current target. On a small-root box such leftovers would starve the local copies (R-548). Evidence: `audits/storage-identity-buildB-2026-10-15/space/felhom-pve-9201.txt`. | OPEN | — | Decide whether the agent prunes its own `vzdump-lxc-<vmid>-*` files on a former target (after a newer copy exists on the new one), or the target move asks; until then, the N100's three July files can be removed by hand with the operator's word. | CC |
| **R-1162** | Backup & restore | P3 | **ep0's whole-guest off-site prune was a count (keep-last 2), so a compromised controller could push out the pre-compromise copy, slowly.** Operator decision (STATUS D18, the brief's D1 = A, 2026-10-19 08:24): one copy per ISO week, 2 weeks. Before: PBS's own dry run and an offline model of its selection both showed `keep-weekly 2` removing nothing on demo-hp (2026-10-08, 10-15 kept) or demo-felhom (10-06, 10-13 kept); nothing deleted by the dry runs (8 snapshots before and after). On the operator's „yes": `prune-demo-hp` and `prune-demo-felhom` changed to `keep-weekly 2` (schedule 03:30 unchanged), read back 07:49 UTC. DooPlex's copy of ep0 already keeps `keep-weekly 8` (decision 71) — unchanged. Households without a job: R-1165. | NARROWED 2026-10-19 — changed and read back; the first scheduled run is 2026-10-20 03:30 UTC | `audits/day-2026-10-19b/P1/` | Read the 2026-10-20 03:30 UTC prune task on ep0 (`proxmox-backup-manager task list`, `prunejob` for both namespaces: OK, `keep-weekly 2`, two snapshots each), then close | CC |
| **R-1165** | Backup & restore | P3 | **A household namespace on ep0 gets no prune job by itself, so its whole-guest off-site copies are never pruned.** Read 2026-10-19: ep0 has prune jobs only for `demo-hp`, `demo-felhom` (made by hand in July, R-82) and `operator`; `tester-1` (3 weekly copies, growing one a week) and `Tester-2` (1) have none; `felhom-tenantsync.sh` creates no job and the hub's provisioner neither. The install script's own comment warns that a missing job is unbounded growth. Close to R-89 (per-customer retention on the hub), but this is the gap today, at every new household. The operator chose not to add a tester-1 job by hand today (2026-10-19). | OPEN — filed 2026-10-19 | `audits/day-2026-10-19b/P1/ep0-settings-before.txt` | The hub (or tenantsync) creates `prune-<ns>` with `keep-weekly 2` when it provisions a household's namespace; meanwhile one job per existing household | CC + operator |
## Storage & devices — 9 rows (P3 3, P4 6)
@@ -199,7 +199,7 @@ stopping line that lies.
| **R-1077** | Storage & devices | P4 | **No USB enclosure that hides or fakes a disk's serial has been measured.** The storage-identity spike (2026-10-15) had none at hand and found none in earlier evidence; the design's `usb_bridge` unknown rule (`architecture/16-storage-physical-identity.md` §3) is therefore an assumption. Cheap single- and dual-bay bridges are known to report one bridge serial for any disk. Evidence: `audits/storage-identity-2026-10-15/README.md` case 4b. | OPEN | — | Buy or borrow one cheap USB enclosure (single and dual bay), run `tools/physdisk_probe.py` against it, and set the rule from what it reports. | Operator (a device) then CC |
| **R-1094** | Storage & devices | P4 | **The whole-disk format refusal tells the household to format the free part — which is refused while the other part is in use.** SEEN 2026-10-16 (controller 0.318.0): 409 „Ezen a lemezen egy használatban lévő meghajtó van, ezért a teljes lemez nem formázható. Csak a szabad részét formázhatod." — since agent 0.158.1 a part next to a mounted part can only be USED (the root wrapper trusts no „Felhom's own" claim). Evidence: `audits/storage-identity-buildB-2026-10-15/live/14-samedisk-refusal-offer.txt`. | READY | — | Reword `err.web.meghajto_hasznalatban_formazas` (hu + en): „… nem formázható. A szabad részét használatba veheted." Next controller release; the copy gates run. | CC |
## Security & access — 21 rows (P1 1, P2 2, P3 12, P4 6)
## Security & access — 20 rows (P1 1, P2 2, P3 11, P4 6)
| ID | Category | Sev | What | State | Blocked on | Next action | Owner |
|---|---|---|---|---|---|---|---|
@@ -223,7 +223,6 @@ stopping line that lies.
| **R-1054** | Security & access | P3 | **The hub's operator/global API key is still one bearer that can do everything on `/api/v1`, including reading every box's break-glass root password (`GET /admin/hosts/{id}/recovery-credential`).** Hub v0.152.0 gave the web surface scoped tokens (`05` §17) but tokens are refused on `/api/v1` by design. Who uses the global key today is not listed in one place. Fix: list its consumers; move each operator use to a scoped token on the web side (or a token class on the API), and keep the global key only where a box path needs it — or retire it. | OPEN — filed 2026-10-15 by the token build | — | List the global key's consumers (pod env, scripts, runbooks) | CC |
| **R-1118** | Security & access | P3 | **CC's secrets moved off the plain `~/.config/credentials` into an encrypted store with a broker and guards; what is left is retiring the old file.** BUILT 2026-10-17 (store B, operator ruling 11:28): private repo `admin/cc-secrets` (`ARCHITECTURE.md`, the design verbatim in `docs/`), `sec` (no get), PreToolUse refusals + PostToolUse masking for Bash and Read in `~/.claude/settings.json` (every CC session on DooPlex), nightly transcript sweep with mail. 55 tests, 18 red-proofs, 36-row child-session proof in both launch modes, fresh-session proof (`audits/cc-creds-build-2026-10-17/`). 25 names in the store (23 imported, `RESEND_API` from CC's environment, a self-test value). `12-agent-tooling.md` §5 points at it. | NARROWED — built; the old file's retirement is R-1134 | the week with no reader (R-1134) | `sec doctor` stays all OK; R-1134 | CC |
| **R-1121** | Security & access | P3 | **`~/rotated-secrets-2026-10-09.txt` still exists on DooPlex** — 632 bytes, modified 2026-10-09 13:08 +0200, mode 600 (SPIKE Q6: `test -e` + `stat` only, not opened). Left by the R-925 rotation work. If any value in it is still valid, it is one more plaintext secrets file in the home folder, readable by every CC session. | WAITING-ON-OPERATOR | what the file holds — only the operator knows | Operator: confirm every value in it is revoked, then `shred -u` it; or move the live ones into the R-1118 store | operator |
| **R-1159** | Security & access | P3 | **The household's retrieval passphrase alone reaches the box's keys and through them the whole off-site key surface — MEASURED 2026-10-19** (the real hub router, a made-up customer, a fake off-site service; status codes and field names only — Tester4's passphrase is not in CC's store and reading it from the hub DB was refused by CC's own safety check). `GET /config/{id}` with the passphrase → 200, `hub` fields `api_key`, `enabled`, `url`, the key the box's controller uses; wrong passphrase 401. `POST /host-enroll` with it → 200 and the EXISTING host's key. With the controller key (wrong key → 401 each): `register-key` (pins the holder's own key append-only), `confirm-key` (removes every other line — the box's own key too), `move-aside` (renames the repository), `window-open` (a 20-min deleting line for the CONFIRMED key, weekly), `abandon-request` (the hub DELETES a set-aside copy after 7 days unless cancelled), `abandon-cancel`, `abandon-status` — all reach the service. With the host key: `GET /hosts/<id>/escrow` (sealed blob, loud), `PUT /hosts/<id>/escrow` and `PUT …/recovery-credential` (past auth: 400 on an empty body), `wg`, `desired-state`, `jobs`, `host-report`. A config download is a log line only. The passphrase is used only at install time (the installer, the controller's setup wizard). **Option A (CC's pick): the passphrase serves keys only while the customer has no host record** — `GET /config` leaves out `hub.api_key` and `/host-enroll` refuses (409, „delete the old host first") once a host exists; a reinstall needs the operator's delete first, which is already the rule (R-1154). Cost: a hub change; a guest rebuilt on a live host needs the operator. Closes the whole chain at its source. **Option B: a second factor on the destructive off-site routes** (`confirm-key` that removes a line, `move-aside`, `abandon-request`, `window-open`) — the operator's approval or the host key. Cost: every reinstall's key rotation waits for the operator, and the host key is itself reachable by the passphrase today, so B alone does not close it. | READY — measured 2026-10-19, options in STATUS D19 | `audits/day-2026-10-19b/P2/passphrase-reach.txt` | Operator picks A or B | operator + CC |
## Box system & updates — 12 rows (P2 1, P3 7, P4 4)
@@ -298,7 +297,7 @@ stopping line that lies.
| **R-990** | Business & legal | P4 | **Operator: read `documentation/audits/forum-research-2026-10-12/FELHOM.md` §5 and rule on each recommendation.** READ-ONLY research 2026-10-12: three years (#7943–#11092, 2,908 posts, 195 people, 82 beginner threads) of the public PROHARDVER! topic „Home server / házi szerver építése”, tagged and counted by person (`FINDINGS.md`, method and spot-check in `METHOD.md`; no username or post text in git). Ten ranked recommendations, none acted on; the top three: a „which machine works” page (8 of 33 beginners who name their hardware own only a Synology, a router disk or a Raspberry Pi — the installer cannot run there; the same gap is R-989's first recommendation from a different source, and the shipped-page half is R-991), „you do not choose an operating system — we do” (the biggest beginner question, 20 of 82 threads), and „bring your old office PC” as the cheap entry. Catalog gap (≥ 3 people): a DLNA server (7), an ad/DNS blocker (5), a camera recorder (5). | **WAITING-ON-OPERATOR** — filed 2026-10-12 | the operator's reading | Operator: rule on each of the ten (yes / no / later). If nothing is done: nothing changes; the research ages | operator |
| **R-1035** | Business & legal | P4 | **Did the „felhő tárhely” page earn anything? Two dated checks.** **Baseline (reported by the operator from Search Console, 2026-10-14, last 3 months):** 1 click, 48 impressions, CTR 2.1 %, average position **73.6**; „felhő tárhely” 19 impressions, „felhőtárhely” 5, „felhő alapú tárhely” 3. CC measured separately that the phrase „felhő tárhely” appeared **0 times** on the home, FAQ and backup pages — Google was showing the home page for those queries by accident. `/sajat-felho-tarhely` + `/en/own-cloud-storage` published **2026-10-14**; full baseline, price sources and the power provenance in `audits/seo-2026-10-14/BASELINE.md`. **What to measure, both times:** impressions and average position for „felhő tárhely” and „saját felhő tárhely”, plus the new page's own impressions, clicks and position. **A null result at +4 weeks means nothing on its own** — a new page on a site at position 73.6 takes weeks to months to move, and „felhő tárhely” alone is held by Microsoft, Google and the big list sites; the page aims at the two longer phrases. That is why there are two checks and not one, and why neither should be read as a verdict on the copy. **There is no Search Console connector in this project, so CC cannot read these numbers** — the operator reads them, CC records and compares. **Blocked until the operator asks for indexing** (Search Console → URL-ellenőrzés → the URL → „Indexelés kérése”): until that happens, or Google finds the page itself, both checks read zero for a reason that is not the page. **This is a slow channel and does not replace talking to people.** | **WATCHING** | operator: request indexing, then supply the Search Console read | 2026-11-11: first read (+4 weeks); 2026-12-09: second (+8 weeks), compared with the first | operator + CC |
## Process & tooling — 31 rows (P3 9, P4 22)
## Process & tooling — 30 rows (P3 8, P4 22)
| ID | Category | Sev | What | State | Blocked on | Next action | Owner |
|---|---|---|---|---|---|---|---|
@@ -332,7 +331,6 @@ stopping line that lies.
| **R-1128** | Process & tooling | P4 | **The controller's decoy suite edits real tracked files (`handler_debug.go`, `update.go`, the locale bundles, templates) and plants `.go` files, with no lock** — a full run while another session edits the controller tree can overwrite that session's edits. felhom.eu's suite took an `flock` after the 2026-10-08 clobber. Found by the R-1116 helper 2026-10-17. | OPEN — filed 2026-10-17 | — | Run the suite in a scratch copy of the tree, or take the felhom.eu-style lock | CC |
| **R-1123** | Process & tooling | P4 | **`reuse_refs_check.py` treats every git directory beside the repo as a sibling repo**, including stray clones in the workspace (`felhom.eu-media`, `felhom.eu-sizing-push`, `felhom.eu-wt-sizing`), so a citation can resolve against a stale clone and pass. Found by the R-1116 helper 2026-10-17 (it also makes the agent suite 20 s here vs 5 s in CI). | OPEN — filed 2026-10-17 | — | Search only the four known repo names (or the main clones), with a decoy | CC |
| **R-1152** | Process & tooling | P4 | **felhom CI's 5-minute job timeout is inert** (act_runner 0.6.1 does not enforce a job-level `timeout-minutes`, R-1136), so a hung step runs up to the runner's 3 h. Move it to a step-level `timeout-minutes` on each workflow's gate step (e.g. 20 min for felhom.eu, which runs ~7 min), measured first: a step timeout fails the job AND the `if: failure()` alarm still runs. Needs a scratch repo (the admin token cannot create one — the operator, or a token with `write:user`). Found 2026-10-18. | OPEN — filed 2026-10-18 | — | Measure on a scratch repo, then the four workflows | CC |
| **R-1166** | Process & tooling | P3 | **Another device takes demo-hp's address (192.168.0.104) every few minutes.** Measured 2026-10-19 from DooPlex: the LAN's ARP entry for .104 flipped between demo-hp's `7c:d3:0a:77:d9:76` and `b8:06:0d:60:45:58` eight times in 19 minutes (10:02–10:17 UTC); while the other MAC holds it, SSH and the Proxmox UI answer „connection refused" and ping still answers. 09:46–09:54 UTC demo-hp also lost its link to ep0 (`Can't connect to 10.77.0.1:8007`, 52 lines). demo-hp's address is static (`nodes.md`), so the other device most likely got .104 from the router's DHCP. | OPEN — filed 2026-10-19 | `audits/day-2026-10-19b/P3/demo-hp-arp-watch.txt` | Operator: find `b8:06:0d:60:45:58` in the router's DHCP list and reserve .104 for demo-hp (or move demo-hp out of the DHCP range) | operator |
<!-- DUE-CHECKS-BEGIN — machine-readable. Parsed by scripts/due_checks_gate.py.
One row per dated check. The R-number must have a row above. Dates are UTC.
+7 -1
View File
@@ -354,9 +354,15 @@ The hub's SECOND forced-command surface on the endpoint: per-customer PBS tenanc
privilege-separated token + dual-grant, the §4a dance automated). Same low-priv user as peersync, its
OWN keypair + script + sudoers drop-in — **the peersync files are untouched** (one script, one job;
peersync script + sudoers sha256 asserted identical before/after at install). Script source of truth:
`scripts/felhom-tenantsync.sh` (**v1.2.0**; JSON stdin/stdout, ops
`scripts/felhom-tenantsync.sh` (**v1.3.0**; JSON stdin/stdout, ops
`provision`/`reissue`/`deprovision`/`fingerprint`/`usage`). Hub client: `hub/internal/tenantsync`.
> **v1.3.0 (2026-10-19, R-1165; installed on ep0 14:07 UTC on the operator's yes, the old copy kept as
> `/usr/local/bin/felhom-tenantsync.bak-1.2.0`):** `provision`/`reissue` also create the namespace's prune job
> `prune-<id>` (keep-weekly 2, 03:30, max-depth 0) unless a job already names the namespace; `deprovision` removes it
> first; `usage` adds `unpruned` + `unpruned_checked`, which the hub alarms on (`pbsdr_namespace_without_prune_job`).
> Pinned by `scripts/test_tenantsync_prune.py`.
>
> **v1.2.0 (2026-07-17, hub R-5 PBS visibility):** adds the read-only **`usage`** op —
> `{"op":"usage"}` → `{"status":"ok","total":<bytes>,"used":<bytes>,"avail":<bytes>}` (a `df` on the
> `felhom-offsite` datastore path). **No customer_id, no admin token, NO mutation** — the structural twin