Hub v0.158.0 code (built, NOT deployed): agent-only vouch, R-895 snapshot alarms, the hub's own 90-day access log (R-1140 A); installer --rotate-recovery-only (R-1141); 02/03/05/09 + break-glass
gates / gates (push) Successful in 6m45s
gates / gates (push) Successful in 6m45s
Hub: the R-120 golden guard judges only a CHANGED golden selection; offsite_snapshots_vanished / _unexplained (operator-only, hub-origin) on the 07:10 listing; internal/accesslog writes path-only lines to /data/access-log, 90 days. Installer: --rotate-recovery-only runs step 4b alone (rides the next installer tag). 03: the orphan check runs after a failed backup (agent 6545ccc). 02: the allow-list is empty, appdata_folders (controller v0.329.0). All red-proofed.
This commit is contained in:
@@ -738,7 +738,8 @@ this app's service has EXACTLY that setting on the allow-list: `privileged`, `ca
|
||||
`include`, ANY `extends`, a `provider` service, `post_start`/`pre_stop` hooks, an `env_file`/`configs`/`secrets` file
|
||||
outside the stack dir, a named volume with `driver`/`driver_opts`/`external`/`name`, a `${VAR}` in any of those keys,
|
||||
and binds: the Docker socket anywhere, mount propagation, a `${VAR}` in a bind's options, and any source outside the
|
||||
data roots the box itself gives — `${HDD_PATH}/appdata/`, `${USERDATA_PATH}`, `${IMPORT_PATH}` (a bind source may use
|
||||
data roots the box itself gives — `${HDD_PATH}/appdata/<the app's own folder>/` (v0.329.0, R-1142), `${USERDATA_PATH}`,
|
||||
`${IMPORT_PATH}` (a bind source may use
|
||||
only those variables, as its leading token; each root must lie under `/mnt/felhom-drives/` or the system data path; on
|
||||
the box a source is judged where it really points, through its nearest existing ancestor, so a symlink cannot carry it
|
||||
out).
|
||||
@@ -747,9 +748,17 @@ out).
|
||||
Gitea — the threat R-971 names (whoever can push to the catalog can take every guest) — cannot add to it; a new entry
|
||||
needs a controller release. An entry is app + service + exact setting, and holds only while that service runs the
|
||||
entry's image REPOSITORY (a literal reference), with the image's own entrypoint and command, and the exact
|
||||
`healthcheck.test` the entry names — so a push cannot keep the allowed right and change what runs with it. Today: one
|
||||
entry, homepage's read-only Docker socket (the survey of all 123 catalog files found nothing else,
|
||||
`audits/agent-allowlist-rotation-2026-10-17/P2/survey.md`). The catalog's CI gate `check-privileges.py` mirrors the
|
||||
`healthcheck.test` the entry names — so a push cannot keep the allowed right and change what runs with it. **Since
|
||||
v0.329.0 the list has NO entry** (R-981): homepage's read-only Docker socket — the one entry of v0.328.0 — served no
|
||||
Felhom feature (its rendered `docker.yaml` is empty, nothing carries `homepage.*` labels; measured on demo-hp,
|
||||
`audits/night-2026-10-18/P1/`), so the template dropped it and so did the list. The entry-binding rules stay pinned on a
|
||||
test list, so a future entry is held to them. **The app's own folder (R-1142):** the list's `appdata_folders` map names
|
||||
an app's folder under `${HDD_PATH}/appdata/` when it is not the app's name — one entry, `paperless-ngx → paperless` (the
|
||||
survey of every template, the drill catalog and the three test boxes found no other). It is compiled in with the list,
|
||||
because the catalog is the threat and must not name the folder; an app NAMED like a folder the map gives to another app
|
||||
gets no `HDD_PATH` root, and one folder for two apps refuses to load. `USERDATA_PATH` is always the controller's own
|
||||
derivation from `HDD_PATH`, never the app's env — a deploy field a catalog push adds would otherwise name any folder
|
||||
(both found by the security review of the pushed commit, 2026-10-18). The catalog's CI gate `check-privileges.py` mirrors the
|
||||
check with a byte-identical copy of the list (pinned from both sides); the BOX is the authority.
|
||||
|
||||
**What a refusal does.** Nothing is written. A refused correction or update leaves the app on its last good compose,
|
||||
@@ -760,8 +769,8 @@ that cannot read the compose refuses. The sync never copies a catalog template n
|
||||
|
||||
**Out of scope, stated.** The controller's own infra stacks (traefik, cloudflared, filebrowser, samba) and its override
|
||||
files (setup gate, family gate, install hold, media library) are rendered by the controller, not the catalog. Published
|
||||
host ports are not a class (crafty-controller's game ports). Known limit: under `${HDD_PATH}/appdata/` one app can
|
||||
name another app's folder (a per-app rule needs a name map — paperless-ngx keeps `appdata/paperless`), R-1142.
|
||||
host ports are not a class (crafty-controller's game ports). `${USERDATA_PATH}` and `${IMPORT_PATH}` are shared by design:
|
||||
they are the household's own folders, which every media app reads.
|
||||
|
||||
**Pinned by** `internal/privpolicy/privpolicy_test.go` (each class refused, with positive controls),
|
||||
`stacks.TestAllowList_IsWiredIntoEveryComposeWriter` (AST: every compose writer and every env decision point asks
|
||||
|
||||
Reference in New Issue
Block a user