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
gates / gates (push) Successful in 7m23s
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -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|
|
||||
@@ -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 |
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user