Day 2026-10-19: agent 0.160.0 (released, not vouched), controller 0.332.0 floor (one sign-in R-1100, escrow confirm R-1151), hub 0.159.0 deployed (R-1150, R-1154), R-1153 not a defect, old-restic check passed; register 168 -> 166 (closed 10, opened R-1155..R-1162)
gates / gates (push) Successful in 7m20s
gates / gates (push) Successful in 7m20s
This commit is contained in:
@@ -539,8 +539,28 @@ state (both demo boxes) the panel says „Ezt a jelszót te állítottad be a F
|
||||
the press. The box uses FileBrowser's own CLI (`set -u admin,<pw>`) with the server STOPPED (the CLI opens the
|
||||
database with no lock timeout), verifies new=200 and admin/admin refused, stores the value encrypted and records
|
||||
`generated`; a failed verify leaves `operator`. Not the `FILEBROWSER_ADMIN_PASSWORD` env: it re-applies on every
|
||||
start. Spike: `audits/logins-folders-2026-10-16/P1b`. FileBrowser still asks for its own login the first time; one
|
||||
sign-in for both is R-1100.
|
||||
start. Spike: `audits/logins-folders-2026-10-16/P1b`. Since v0.332.0 the folder links open FileBrowser without its own
|
||||
login (the next paragraph); `admin`'s password is the fallback and the operator's way in.
|
||||
|
||||
**[DESIGN] One sign-in for the file manager (v0.332.0, R-1100, operator decision D16 = yes 2026-10-19; spike
|
||||
`audits/proxyauth-spike-2026-10-18/DESIGN-NOTE.md`).** Every dashboard link to a FileBrowser folder goes to
|
||||
`/__files/open?to=<path>` on the dashboard host, which — for a signed-in, not-cross-site request — mints a pass (60 s,
|
||||
one use, bound to `files.<domain>` and to the session) and sends the browser to `files.<domain>/_felhom/sso`. traefik
|
||||
asks the controller before every FileBrowser request (forwardAuth `felhom-files-sso` → `/__felhom_gate/files`, the setup
|
||||
gate's handshake): the pass becomes the host-only `felhom_files` cookie (a value derived from the session's fingerprint
|
||||
for that host — the R-976 shape); a valid cookie → 200 with the box's **secret header** naming FileBrowser user
|
||||
`felhom-household`; no cookie → 200 without it (FileBrowser's own password); a stale cookie (sign-out, password change)
|
||||
→ both cookies cleared, back to the dashboard. **Why a secret header name:** FileBrowser trusts the header blindly and
|
||||
every app container reaches `filebrowser:80` directly — measured: a forged fixed-name header from an app logged in. The
|
||||
name (`X-Felhom-User-<32 hex>`, per box, 0600 in the data dir) is written only into FileBrowser's config and traefik's
|
||||
route (`authResponseHeaders`, which strips a client's copy); never a log, a page or a report. **Why not a parent-domain
|
||||
cookie:** every app subdomain is third-party code and would receive the dashboard session. **What stays:** `admin` is a
|
||||
password user (R-513's reveal and reset, the R-1126 gate unchanged); the header user is a separate FileBrowser user whose
|
||||
rights come from the box's userDefaults (change, add, delete, share, download — not admin, not API); FileBrowser's logout
|
||||
goes to the dashboard. Residual: FileBrowser's own 2-hour token for the header user is cleared with the files cookie,
|
||||
but a browser that lost only the files cookie keeps it until it expires. Measured live on all three test boxes
|
||||
(`audits/day-2026-10-19/P2/`, `P4/`): opened at the folder with no login; forged headers refused through traefik and
|
||||
from an app container; a sign-out asks again.
|
||||
|
||||
**[DESIGN] A stored FileBrowser password is checked before it is offered (v0.321.0, R-1101).** In `generated`, the
|
||||
page asks FileBrowser whether the stored value still logs in; only FileBrowser's own 401/403 counts as refused (a
|
||||
@@ -562,8 +582,9 @@ role, else the first declared folder, else (a media app) the first present drive
|
||||
`movies`, the players both, a download client its `media_library.folder`) with NO backup sentence — the bind's class
|
||||
there means „not this app's data", which is not the class-driven „temporary, not backed up". **A folder whose drive
|
||||
is not bound into FileBrowser now is text, never a link** (`drivePresent` = the same gates the FileBrowser sync
|
||||
applies). The button never promises one click: FileBrowser's router brings a cold visitor back to the folder after its
|
||||
login (measured in the headless browser, `audits/logins-folders-2026-10-16/browser/`).
|
||||
applies). Since v0.332.0 the button opens the folder signed in (one sign-in, above); on a box without it, FileBrowser's
|
||||
router brings a cold visitor back to the folder after its login (measured in the headless browser,
|
||||
`audits/logins-folders-2026-10-16/browser/`).
|
||||
|
||||
**[DESIGN] „Cím és kulcsok" is rows, not the install form.** The settings tab does not re-render the
|
||||
install wizard's fields: the address is the „Cím" row, the storage is a „Tárhely: <név>" row, every
|
||||
|
||||
@@ -544,11 +544,18 @@ nicety.
|
||||
**loses Felhom's backup guarantees** — no PBS verify / monitoring on storage we can't reach. An
|
||||
honest signup-time tradeoff, not a hidden one.
|
||||
|
||||
**When the new package reaches the hub (R-978, agent `main` 2026-10-18, unreleased):** a ceremony started again no
|
||||
**When the new package reaches the hub (R-978; agent 0.160.0, 2026-10-19):** a ceremony started again no
|
||||
longer replaces the hub's package when it starts. The package travels with the pending ceremony and is uploaded when its
|
||||
code is claimed, before the code is handed out (an upload failure is a 502 and the code stays claimable). An abandoned
|
||||
ceremony — TTL, a new start, a restart — leaves the first code opening the hub's package. Residual: the typed-back check
|
||||
is the browser's, so a code opened and not written down still replaces the package (R-1151).
|
||||
ceremony — TTL, a new start, a restart — leaves the first code opening the hub's package. **[DESIGN] Only after the
|
||||
household typed it back (R-1151; agent 0.160.0 + controller 0.332.0, 2026-10-19):** a controller ≥ 0.332.0 claims with
|
||||
`{"defer_upload": true}`; the agent hands out the code with two word positions it drew, keeps the package (and only those
|
||||
two words, never the code) for 30 minutes, and uploads it only when `POST /escrow/ceremony/confirm` brings the two words
|
||||
back — so a code opened and not written down never replaces the package. Wrong words 422 (still waiting), window gone 410,
|
||||
nothing waiting 404, hub failure 502 (still waiting). A controller that has not updated claims with `{}` and gets the
|
||||
R-978 order. Proven on demo-hp 2026-10-19: ceremony 1 typed back → code 1 opens the hub's package, code 0 is a kept
|
||||
earlier one (422); ceremony 2 shown and abandoned → code 1 still opens it, code 2 opens nothing
|
||||
(`audits/day-2026-10-19/P1/escrow-*.txt`).
|
||||
|
||||
#### Custody across generations — what the hub retains when a blob is superseded (hub v0.60.0; **contract corrected hub v0.93.0, R-198**)
|
||||
|
||||
|
||||
@@ -383,7 +383,7 @@ never moved by an update or an undo, and the unit restore accepts it.
|
||||
| **Tier-1** recovery unit | `<nsRoot>/backups/primary/<app>/` on the app's own drive | compose + `.felhom.yml` + **secret-stripped** `app.yaml`, `db-dumps/*.sql`, `volume-dumps/*.tar`, `manifest.json` | nightly at W, plus a checksum-gated refresh on the periodic status pass | one point per app (`restore_points.go:14-18`) | no |
|
||||
| **Tier-2** cross-drive | `<target nsRoot>/backups/secondary/<app>/` | **always** a full mirror of the unit (`tier2.go:368-369`) **plus** `mandatory + optional` file legs, v2 layout `hdd/<rel>` + `userdata/<rel>` | nightly at W+60m | mirror (rsync) | no |
|
||||
| **Tier-3** restic offsite | Hetzner Storage Box over SFTP, one multi-path snapshot per app per run | the unit **plus** `mandatory` legs only; a separate `_shares` snapshot | nightly at W+105m | `--keep-daily 7 --keep-weekly 4 --keep-monthly 6` | **yes** (restic) |
|
||||
| **Plane-2** whole-guest, local | `local:` → `/var/lib/vz/dump` on the host | rootfs + `mp0 /var/lib/felhom` (its `docker/` and `sys_drive/` folders, bound in the guest to `/var/lib/docker` and `/mnt/sys_drive` — §7.5.1; `mp1` no longer exists, corrected 2026-10-15) | **24 h** (`backup_cadence_seconds: 0`) | `local_backup_retention: 3` | **no** |
|
||||
| **Plane-2** whole-guest, local | `local:` → `/var/lib/vz/dump` on the host | rootfs + `mp0 /var/lib/felhom` (its `docker/` and `sys_drive/` folders, bound in the guest to `/var/lib/docker` and `/mnt/sys_drive` — §7.5.1; `mp1` no longer exists, corrected 2026-10-15) | **24 h** (`backup_cadence_seconds: 0`) | `local_backup_retention: 3` → `--prune-backups keep-daily=3`: one copy per day, 3 days (keep-last until agent 0.160.0, R-1149) | **no** |
|
||||
| **Plane-2** whole-guest, offsite | `felhom-pbs:` → ep0 datastore `felhom-offsite`, per-customer namespace, over WireGuard | same contents | **7 days** (`604800`) | server-side prune on ep0, `keep-last 2` at `03:30` — **and the box asks for none** (R-191) | **yes** (per-customer key) |
|
||||
|
||||
> **[FACT] Tier-3 has a fifth state the table above does not show: PAUSED (R-543, controller
|
||||
@@ -479,15 +479,20 @@ upload library is a mandatory class) or uploaded again by the household — its
|
||||
`pct config 9201`, both hosts). So the whole-guest tiers carry the guest and **none of the customer's
|
||||
data drives** — 916 GB on demo-felhom, 938 GB on demo-hp.
|
||||
|
||||
> **The whole-guest tiers are capped per guest (R-973, agent `main` 2026-10-18, unreleased) — *decided by CC unattended,
|
||||
> operator may reverse*.** Because the tiers prune by COUNT, a compromised controller that runs `POST /backup` again and
|
||||
> **The whole-guest tiers are capped per guest (R-973; agent 0.160.0, released 2026-10-19 on the operator's yes — the
|
||||
> numbers were *decided by CC unattended, operator may reverse*, and the operator said yes to the release).** Because the tiers prune by COUNT, a compromised controller that runs `POST /backup` again and
|
||||
> again pushes out older restore points. The agent now refuses (429, logged, kept across a restart, a CRITICAL degraded
|
||||
> status for 24 h) a run beyond: the daily tier — **3 successful runs per rolling 24 h** (the night run, one „Mentés
|
||||
> most", the next night: a press never costs the night, R-899); the weekly off-site tier — **one per 6 days**
|
||||
> (max(cadence − 24 h, cadence / 2)). The question: how many runs may a guest make before the cap refuses? Options: no cap
|
||||
> (today, floodable); a per-tier count per rolling window (chosen: no ordinary day is refused); a time-bucketed prune
|
||||
> instead (the durable shape, but a retention change — R-1149 for the local tier, R-973's design line for ep0). Limits:
|
||||
> keep-last 3 can still be replaced within one day (R-1149); ep0's keep-last 2 is a count.
|
||||
> ep0's keep-last 2 is a count. **[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
|
||||
> same single newest copy either way) and read before on Tester 1 and demo-felhom (one copy per day each: the same copy
|
||||
> goes at their next night under either rule) — `audits/day-2026-10-19/P1/`.
|
||||
|
||||
### 6.1.1 A box that is not always on — the catch-up, the banner, the alarms (R-871..R-874)
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ Title: **"Where a household's data is copied"**
|
||||
| **Tier 1 — recovery unit** | each app: its settings, database dump, volumes | data drive 1, on the box | nightly at **02:30** (W) | 1 point per app | not encrypted (it never leaves the home) |
|
||||
| **Tier 2 — cross-drive** | a mirror of Tier 1, plus the app's files | data drive 2, on the box (counted only on a different physical disk; in a VM made, not counted) | nightly **03:30** | mirror | not encrypted |
|
||||
| **Tier 3 — off-site, per app** | Tier 1 + the app's must-keep files | **Hetzner Storage Box** (Germany), one folder per household | nightly **04:15** | 7 daily, 4 weekly, 6 monthly | the household's own restic password |
|
||||
| **Whole guest — local** | the guest's system (not the data drives) | the box's own system disk | daily, in the window **04:30–08:30** | up to 3 copies (fewer on a small disk) | not encrypted |
|
||||
| **Whole guest — local** | the guest's system (not the data drives) | the box's own system disk | daily, in the window **04:30–08:30** | one copy per day, up to 3 days (fewer on a small disk) | not encrypted |
|
||||
| **Whole guest — off-site** | the same | **ep0** (Hetzner, Germany), one namespace per household | weekly, same window | last 2 | the household's own key, escrowed with a recovery code |
|
||||
|
||||
Footnote: "W = the household's backup window start, 02:30 by default. Tiers 1–3 follow at fixed offsets so they cannot run out of order. A missed night is caught up 15 minutes after the box starts."
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user