From fda783bdafb7f55670b315627c769250d7df6d18 Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Sun, 12 Jul 2026 20:43:25 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20DR-tier-by-default=20batch=20=E2=80=94?= =?UTF-8?q?=20decisions=201-5=20in=20CONTEXT,=20day0=20ACL=20advice=20reve?= =?UTF-8?q?rsed,=20ceremony=20decision-4=20policy=20block,=20WG=20disclosu?= =?UTF-8?q?re=20(tester=20agreement=20+=20GO-LIVE=20D3),=20drill=20F-dispo?= =?UTF-8?q?sition=20table,=20REPORT?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Claude-Session: https://claude.ai/code/session_01NptTCFtu7dz2Ru89qHRagN --- CONTEXT.md | 26 ++++++++ REPORT.md | 66 +++++++++++-------- .../audits/DRILL-day0-vm-2026-07-12.md | 16 ++--- documentation/pilot/GO-LIVE-PACKAGE.md | 2 +- documentation/pilot/PETI-tester-agreement.md | 15 +++-- .../runbooks/RUNBOOK-escrow-ceremony.md | 22 ++++--- documentation/runbooks/day0-install.md | 47 +++++++------ 7 files changed, 126 insertions(+), 68 deletions(-) diff --git a/CONTEXT.md b/CONTEXT.md index 53fd989..32c2ea4 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -3,6 +3,32 @@ > Created with the REUSE.md rollout (2026-07-03). Authoritative history: `hub/CHANGELOG.md` (hub), > `website/CHANGELOG.md`, `scripts/CHANGELOG.md`; end-of-task detail in `REPORT.md`. +- **2026-07-12 — DR-TIER-BY-DEFAULT BATCH SHIPPED: installer v1.15.0 + hub v0.51.0 (+ agent + v0.86.0 in felhom-agent)** — closes DRILL-day0-vm F-1/F-2/F-3/F-7/F-8/F-9/F-10 and F-6 (by + policy). **The five operator decisions (2026-07-12), recorded verbatim — future sessions must + treat these as settled:** + 1. *Every install deploys the full DR plumbing uniformly (pbs-apply wrapper, `age`, WG enabled, + full ACL). **Capability is baked; activation is a hub flag.*** + 2. ***DR tier defaults ON for new customers.** Disabling is the per-customer exception — and it + removes the Felhom-side cost entirely (no PBS namespace, no endpoint dependency).* + 3. ***Offsite app backup requires the DR tier ON** (fork-4 needs the ceremony; the ceremony + needs PBS). Enforced hub-side by extending the drill-proven dependency guard.* + 4. ***Identity-only escrow is formally PARKED** — closed by policy, not implemented. Revisit + only if a customer class demands lean-tier offsite. Record in the ceremony runbook + CONTEXT + so future sessions know it was a decision, not an oversight.* + 5. ***WG is base infrastructure, not a DR component** — always-on like cloudflared; it is also + the future OOB-management transport. BYO consent: a disclosure line lands in the + tester-agreement doc; per-customer hub disable remains possible.* + Mechanics: installer ships `felhom-pbs-apply` + `age` + `wg_tunnel.enabled: true` (byo wg-off + assert RETIRED — consent = typed disclosure ack + tester-agreement line), single VERSION source + gated by `scripts/hostinstall_gates.py` (cross-checks the hub Setup-tab const), default ACL + keeps felhom-pbs (doc advice REVERSED — narrowing caused the drill 403). Hub: per-customer + `dr_tier` flag (new default ON; legacy backfilled from descriptor reality, one-time), + cascade-not-error semantics + per-stage status on the edit form, WG-registration + auto-provision hook (scenario A hands-free), offsite-requires-DR guard (exact F-6 message), + host-page capability chips (agent 0.86.0 `inactive` = neutral; pre-1.15 "binary not found" → + migration hint). felhom-pve's latent F-3 state fixed live; Peti's box unreachable (his F-3 + check + D/E/G deferred). - **2026-07-12 — HUB v0.50.0 SHIPPED + LIVE: customer-claim password arc (closes DRILL-day0-vm F-4/F-5).** The customer OWNS the dashboard password via a one-time claim code the hub emails to the registered address (`bcrypt(code)` stored, plaintext only in the send). `internal/claim` diff --git a/REPORT.md b/REPORT.md index 7d22ad5..b4fab69 100644 --- a/REPORT.md +++ b/REPORT.md @@ -2,37 +2,49 @@ > **Overwrite** this file with a summary of the most recent task only (uniform with the other repos; not cumulative). The cumulative hub history lives in [hub/CHANGELOG.md](hub/CHANGELOG.md); the scripts history lives in [scripts/CHANGELOG.md](scripts/CHANGELOG.md). -## Hub v0.50.0 — customer-claim password arc (closes DRILL-day0-vm F-4/F-5) — 2026-07-12 +## DR-tier-by-default batch — installer v1.15.0 + hub v0.51.0 (+ agent v0.86.0) — 2026-07-12 -Hub half of the customer-owned dashboard-password gate (controller v0.122.0 is the other half). -The customer sets + owns the password via a one-time claim code the hub emails to the registered -address; the hub stores only `bcrypt(code)`. **DEPLOYED: `felhom-hub:0.50.0` live on k3s (ArgoCD -synced/healthy, `Listening on :8080` at 18:47 CEST).** +**Spec:** the DR-tier-by-default batch (uniform deployment, hub-controlled activation). Closes +drill findings F-1/F-2/F-3/F-7/F-8/F-9/F-10 shipped + **F-6 closed by policy** (decision 4: +identity-only escrow PARKED). Baselines re-confirmed at start: password arc fully deployed (hub +0.50.0 live, ctrl 0.122.0 + floor raised, drill box claimed); hub target shifted 0.50.0→**0.51.0** +(the arc took the spec's number — the 0.48→0.49 precedent). -### What shipped +### Shipped (this repo) -- **`internal/claim`** — the code engine: `EnsureIssued` (idempotent Day-0/live-box issue+email), - `Resend` (operator rotate), `RequestReset` (controller-forwarded, 3/day cap), `MarkClaimed` - (set-only + one confirmation email). Plaintext code exists ONLY inside the email send. -- **`store.customer_claims`** — `RotateClaimCode` (generation bump = single active code; PRESERVES - `claimed_at`), `GetClaim`, `MarkClaimed` (set-only), `BumpResetCount`. -- **Delivery**: `GET /api/v1/config/{id}` issues the first code + bakes `web.claim_code_*` into the - generated controller.yaml; the report ACK serves `claim {code_hash, generation, issued_at}` - (allowlisted) + ingests `claimed` (set-only); `POST /api/v1/claim/reset-request` (self-scoped). -- **UI**: customer-page Setup tab claim chip + "Kód újraküldése" (`POST /configs/{id}/claim-resend`). -- **Emails**: three Hungarian templates (claim / reset / claimed) via the notify dispatcher. -- 15 tests (engine, ACK/config, UI); red-proofs for the generation bump + reset-non-DoS proven. +- **`scripts/felhom-host-install.sh` v1.15.0** — F-7 ships `felhom-pbs-apply` (wrapper pattern, + uninstall parity); F-10 installs `age`; F-9/decision-5 renders `wg_tunnel.enabled: true` BOTH + modes (byo wg-off assert retired; consent = typed disclosure ack, WG now on the disclosed + list); default ACL keeps `felhom-pbs` (byo missing-storage refusal exempts it — pre-positioned + grant); F-1 single `SCRIPT_VERSION` source; F-2 honest dry-run auth lines; F-8 loud root@pam + rotation pointer (4b + final summary). `bash -n` clean; mode-harness 31/31 green. +- **`scripts/hostinstall_gates.py`** (NEW committed gate) — version single-source + hub-const + cross-check, age, pbs-apply ship+remove, wg default-on + assert retirement, felhom-pbs in ACL. + All red-proofed; the age assertion hardened TWICE by its own red-proof (prefix regex matched + `agekit`; loose regex matched the dry-run print). +- **Hub v0.51.0** — per-customer `dr_tier` flag (column + one-time backfill from descriptor + reality; new-customer default ON); cascade-not-error semantics + per-stage status (scenario D); + `PBSDRAutoProvision` fired by the WG-registration hook (scenario A hands-free); offsite- + requires-DR guard with the exact F-6 message (scenario C); host-page capability chips + (`inactive` = neutral, degraded stays loud + migration hint); Setup-tab copy 1.15.0. + Green gate: build/vet/test — 14 packages ok. Red-proofs: coupling guard, flag default, + backfill enabled-check, auto-provision unhook — all proven red then restored. +- **Docs:** day0-install.md (ACL narrowing advice REVERSED — the drill 403; DR-default note; + commands drop `--acl-storages`; byo wg disclosure), RUNBOOK-escrow-ceremony.md (decision-4 + policy block), PETI-tester-agreement.md + GO-LIVE-PACKAGE.md D3 (WG disclosure/supersede), + CONTEXT.md (decisions 1–5 verbatim), DRILL-day0-vm §7 F-disposition table, REUSE.md rows + (+ path-citation fixes; reuse_refs_check green). -### Live validation +### Sibling repo (felhom-agent v0.86.0, commit `c20814e`) -Drill box demo-vm-felhom: on the first report after hub 0.50.0 + controller 0.122.0, the hub issued -+ emailed the claim code to the registered address and the ACK delivered the hash — the controller -gate flipped ON (proven via the real Cloudflare edge: `/` → 302 claim page, `/api` → 401). Observed: -the hub issues a code for EVERY reporting managed customer on first report (peti + demo also received -emails) — benign, since a pre-v0.122 controller ignores the ACK claim field and has no gate. +Capability `inactive` state ("disabled by configuration"; broken plumbing stays degraded) gated +by `pbsdr.Manager.DRConfigured` (marker-backed); F-3 root-run provision parent chown +(`--reference`, non-recursive). felhom-pve's latent F-3 state confirmed + fixed live; Peti's box +unreachable (check deferred). -### Version note +### Pending (deploy + validation phase) -Hub was at v0.49.0; this is v0.50.0. Controller v0.122.0 committed + image built. The **floor raise -to 0.122.0 is the operator's supervised LAST step** (after the drill/demo claim validation) — a -gated controller must never reach a box before its hub can deliver the code hash. +Build/deploy hub 0.51.0 (ArgoCD manifest bump) + agent 0.86.0 (felhom-pve + publish + Day-0 +manifest vouch) + installer serve-verify; then the §13 "Day-0 take two" re-drill on qm 300 — +**requires Viktor's GO** (rolls back to `pre-day0-clean`, deletes the drill escrow via the +stale-host flow). Scenario B/C hub-side validation via tests + UI screenshots (drill box stays ON). diff --git a/documentation/audits/DRILL-day0-vm-2026-07-12.md b/documentation/audits/DRILL-day0-vm-2026-07-12.md index 4a400ed..321c9ea 100644 --- a/documentation/audits/DRILL-day0-vm-2026-07-12.md +++ b/documentation/audits/DRILL-day0-vm-2026-07-12.md @@ -129,16 +129,16 @@ tier (ep0)** — which converted the drill into a full rehearsal of Peti's pendi | # | Sev | Phase | Finding | Disposition | |---|---|---|---|---| -| F-1 | LOW | 3 | Version-string mismatches: `-h` v1.14.0 vs run banner v1.13.0 vs hub Setup-tab copy "1.12.0" | fix strings (installer + hub template) | -| F-2 | COSMETIC | 3 | dry-run prints `curl -u ` on the anonymous-fetch branch | fix placeholder | -| F-3 | MEDIUM | 4 | Root-run provision leaves `/var/lib/felhom-agent/guests{,/9201}` root:root 0700 inside the agent-owned state dir → non-root agent lanresolver "permission denied". LIVE-FIXED (chown the two parent dirs; the guest-root-owned bootstrap subtree untouched) | agent/installer: create parents agent-owned at provision; **check demo/felhom-pve for the same latent state** | +| F-1 | LOW | 3 | Version-string mismatches: `-h` v1.14.0 vs run banner v1.13.0 vs hub Setup-tab copy "1.12.0" | **SHIPPED 2026-07-12 (installer v1.15.0 + hub v0.51.0):** single `SCRIPT_VERSION` source (header carries no literal; `-h`/banners read it; hub `hostInstallVersion`=1.15.0) — drift now gated by `scripts/hostinstall_gates.py` (asserts installer == hub const) | +| F-2 | COSMETIC | 3 | dry-run prints `curl -u ` on the anonymous-fetch branch | **SHIPPED (installer v1.15.0):** dry-run fetch lines print the real auth mode (`curl (anonymous — no git credential)` vs `-u `) | +| F-3 | MEDIUM | 4 | Root-run provision leaves `/var/lib/felhom-agent/guests{,/9201}` root:root 0700 inside the agent-owned state dir → non-root agent lanresolver "permission denied". LIVE-FIXED (chown the two parent dirs; the guest-root-owned bootstrap subtree untouched) | **SHIPPED (agent v0.86.0):** a root-run provision chowns the `guests/`+`guests//` PARENTS to the state-dir owner (`chown --reference`, non-recursive; bootstrap leaf stays guest-root) — red-proofed. **felhom-pve latent state CONFIRMED + FIXED live 2026-07-12** (same chown); Peti's host unreachable — his check deferred to the D/E/G visit | | **F-4** | **HIGH** | 5 | **No operator-set dashboard password path exists anywhere** (hub has no UI/API for per-customer `password_hash`; controller open-state page defers to the operator; Day-0 preseeded path skips the wizard's password form) → **G10 unclosable; every fresh box's dashboard stays OPEN on the internet** | **RESOLVED 2026-07-12 — customer-claim arc (controller v0.122.0 + hub v0.50.0).** The customer OWNS the password, set via a one-time claim code the hub emails to the registered address; an unclaimed box serves only the claim page (no open window). The operator-set premise was correctly abandoned. Live-proven on the drill box (gate ON via the real edge). | | F-5 | MEDIUM | 5 | Geo-restriction APPLY is customer-dashboard-side; hub only disables. Runbook premise stale; also: the open dashboard (F-4) exposes the geo toggle unauthenticated | **RESOLVED with F-4** — the geo toggle (and every other route) is now behind the claim gate on an unclaimed box. The apply-is-customer-side design is accepted (documented). | -| **F-6** | **HIGH** | 6 | **Identity-only escrow ceremony was never implemented** (`escrow-create` hard-requires PBS storage + key; the ≥0.80.0 runbook claim was false) → on a no-PBS box (the documented appliance standard!) the offsite escrow chain can never complete | agent feature task (identity-only mode); ceremony runbook corrected in this commit | -| F-7 | MEDIUM | 6 | host-install ships the FELHOM_PBSDR sudoers alias but NOT the `felhom-pbs-apply` binary → pbsdr capabilities born DEGRADED on every fresh box | installer: ship the wrapper (like mkfs/selfupdate wrappers) | -| F-8 | LOW/UX | 3 | Step 4b rotates root@pam + vaults silently — operator surprised by 401 at the PVE GUI (live: Viktor) | installer: print "root@pam rotated + vaulted — retrieve at hub → host page" | -| F-9 | NOTE | 6 | Installer never sets `wg_tunnel.enabled`; WG registration itself is hands-free once enabled | decide appliance default alongside the F-6 spec | -| F-10 | MEDIUM | 6 | `age` (ceremony identity-wrap dependency) not installed by host-install — fresh-box ceremony dies; demo host masked it (spike-era install) | installer: add `age` to package set; runbook prereq added in this commit | +| **F-6** | **HIGH** | 6 | **Identity-only escrow ceremony was never implemented** (`escrow-create` hard-requires PBS storage + key; the ≥0.80.0 runbook claim was false) → on a no-PBS box (the documented appliance standard!) the offsite escrow chain can never complete | **CLOSED BY POLICY 2026-07-12 (DR-tier-by-default decisions 3/4):** identity-only escrow is formally PARKED; every install is DR-capable (installer v1.15.0), activation = the hub per-customer `dr_tier` flag (default ON, hub v0.51.0), and offsite REQUIRES the tier (guard: "Offsite backup requires the DR tier — enable it first (the escrow ceremony depends on the PBS key)"). The no-PBS shape can no longer reach the offsite arc. Ceremony runbook carries the decision block | +| F-7 | MEDIUM | 6 | host-install ships the FELHOM_PBSDR sudoers alias but NOT the `felhom-pbs-apply` binary → pbsdr capabilities born DEGRADED on every fresh box | **SHIPPED (installer v1.15.0):** wrapper fetched + `bash -n` + installed 0755 (the mkfs/selfupdate pattern); uninstall removes it; gate-asserted. Companion: agent v0.86.0 adds the `inactive` capability state (DR off = neutral chip, NEVER degraded; missing binary STAYS degraded + hub migration hint) | +| F-8 | LOW/UX | 3 | Step 4b rotates root@pam + vaults silently — operator surprised by 401 at the PVE GUI (live: Viktor) | **SHIPPED (installer v1.15.0):** loud WARN at 4b ("the root@pam password just CHANGED… retrieve at hub → host page") + repeated in the final summary; pointer only, never the credential | +| F-9 | NOTE | 6 | Installer never sets `wg_tunnel.enabled`; WG registration itself is hands-free once enabled | **DECIDED + SHIPPED (decision 5; installer v1.15.0):** WG is BASE infrastructure — `wg_tunnel.enabled: true` rendered in BOTH modes (setdefault; deliberate preserved-off respected). byo wg-off assert retired; consent = typed disclosure ack + tester-agreement line. Hub v0.51.0 auto-provisions the PBS-DR descriptor on the peer registration (hands-free cascade) | +| F-10 | MEDIUM | 6 | `age` (ceremony identity-wrap dependency) not installed by host-install — fresh-box ceremony dies; demo host masked it (spike-era install) | **SHIPPED (installer v1.15.0):** `age` installed idempotently in step 5 (both modes); gate-asserted; runbook prereq updated | | F-11 | LOW | 6 | Native `confirm()` on the offbox restore-verify form: blocks browser automation, easy to miss (operator missed it twice live; meanwhile launched the full tier-1 restore from the adjacent form — the two restore controls invite confusion) | convert to the design-system inline confirm pattern; consider renaming | Observations (not F-numbered): hub "REGISTRY LATEST v0.120.0 — up to date" vs the dashboard's own diff --git a/documentation/pilot/GO-LIVE-PACKAGE.md b/documentation/pilot/GO-LIVE-PACKAGE.md index 848e88b..a6a7894 100644 --- a/documentation/pilot/GO-LIVE-PACKAGE.md +++ b/documentation/pilot/GO-LIVE-PACKAGE.md @@ -21,7 +21,7 @@ catalog `2ebe082a` · felhom.eu `1a1e42ad`/hub v0.35.0 · host-install v1.9.1 · |---|------|----------| | D1 | Pool model | **One shared `felhom` pool.** No multi-tenant boxes yet; the pool separates Felhom guests from the host owner's own workloads. Effort goes to preflight pool-membership assertion + local-API per-guest token scoping (verified structural, see G10). | | D2 | Pilot topology | **BYO host.** Peti installs Felhom onto his OWN Proxmox server, already running his own LXCs. This reframes containment: not customer-vs-customer, but *Felhom-as-a-guest on a host the operator does not own*. See §2. | -| D3 | Backup posture | **Local-only for the pilot.** `wg_tunnel.enabled` and `oob.enabled` stay FALSE. Offsite (Tier-1/Tier-2 split + production WG endpoint) is a fast-follow, and the limitation is stated honestly in the tester agreement. | +| D3 | Backup posture | ~~Local-only for the pilot; `wg_tunnel.enabled` and `oob.enabled` stay FALSE.~~ **SUPERSEDED 2026-07-12 (DR-tier-by-default, decisions 1/5):** every install ships the full DR plumbing with `wg_tunnel.enabled=true` — WG is BASE infrastructure (outbound-only, like the CF tunnel; also the future OOB transport); activation of the PBS-DR tier is the hub's per-customer flag (default ON). `oob.enabled` stays FALSE on BYO. Consent moves to the installer's typed disclosure ack + the tester-agreement WG disclosure line (§2). | | D4 | Uninstall drill | **Full provision → uninstall → re-provision drill on the demo box BEFORE Peti's day-0.** Peti's box is never the first live test of anything destructive. | | D5 | DR bind-override | **Fix now** (small, spike-validated known-constant override per `SPIKE-dr-bindmount-source-2026-07-07.md`), not deferred to the intermediary-mount re-architecture. | | D6 | Packaging | **This one tracking doc + a sequenced set of CC tasks** (GL-1…GL-7). | diff --git a/documentation/pilot/PETI-tester-agreement.md b/documentation/pilot/PETI-tester-agreement.md index 57e2696..76e9c9e 100644 --- a/documentation/pilot/PETI-tester-agreement.md +++ b/documentation/pilot/PETI-tester-agreement.md @@ -44,12 +44,17 @@ mechanisms: credential to the hub. Peti's root password is his own and is never seen or changed. - **Out-of-band operator SSH is OFF** (`oob.enabled=false`). Felhom opens no operator tunnel into his box. -- **The offsite WireGuard tunnel is OFF** (`wg_tunnel.enabled=false`). No Felhom-side connection - reaches back into his network. +- **WireGuard tunnel DISCLOSURE (changed 2026-07-12, DR-tier-by-default decision 5):** the install + DOES enable an **outbound** WireGuard tunnel (`wg_tunnel.enabled=true`) — it is base + infrastructure like the Cloudflare tunnel, carries the offsite/DR backup traffic, and is the + future out-of-band management transport. It is strictly box-dials-out (no Felhom-side connection + reaches back into the network; the peer has no inbound route), it is listed in the installer's + typed host-mutation acknowledgement, and it can be disabled per-customer hub-side on request — + with the honest consequence that the DR tier and offsite backup stop working without it. -These are asserted at install time (the byo config-write asserts `oob`/`wg_tunnel`/`lan_resolver` -are all off before the install is marked complete) and were live-validated on the demo host during -the GL-6 drill. +These are asserted at install time (the byo config-write asserts `oob`/`lan_resolver` are off +before the install is marked complete — `wg_tunnel` moved to the disclosed-default list in +installer v1.15.0) and were live-validated on the demo host during the GL-6 drill. --- diff --git a/documentation/runbooks/RUNBOOK-escrow-ceremony.md b/documentation/runbooks/RUNBOOK-escrow-ceremony.md index dfad157..74dd09b 100644 --- a/documentation/runbooks/RUNBOOK-escrow-ceremony.md +++ b/documentation/runbooks/RUNBOOK-escrow-ceremony.md @@ -19,14 +19,20 @@ ## Prerequisites (check BEFORE scheduling with the customer) - **Agent version:** ≥ v0.79.0 (the ceremony records `restic_pw_sha256` — older agents produce a blob auto-confirm can never match). -- **⚠ No-PBS hosts: the ceremony CANNOT run.** The previously documented "identity-only mode - (≥ v0.80.0)" was NEVER implemented — v0.80.0's actual feature was seeding `escrow.pbs_storage_id` - on PBS hosts. `escrow-create` hard-requires a PBS storage id + its key file (drill-proven - 2026-07-12, finding F-6 of DRILL-day0-vm-2026-07-12.md). Until identity-only ships, a box MUST - have the PBS DR tier (which itself requires the WG tunnel peer first) before any escrow/offsite - arc can complete. -- **Host packages:** `age` must be installed (identity wrap dependency; NOT installed by - host-install as of v1.14.0 — drill finding F-10). `apt-get install -y age`. +- **⚠ No-PBS hosts: the ceremony CANNOT run — and that is now POLICY, not a gap.** The previously + documented "identity-only mode (≥ v0.80.0)" was NEVER implemented — v0.80.0's actual feature was + seeding `escrow.pbs_storage_id` on PBS hosts. `escrow-create` hard-requires a PBS storage id + + its key file (drill-proven 2026-07-12, finding F-6 of DRILL-day0-vm-2026-07-12.md). + **Operator decision 2026-07-12 (DR-tier-by-default, decision 4): identity-only escrow is + formally PARKED — F-6 is closed by policy, not by implementation.** Every install ships the + full DR plumbing (host-install ≥ v1.15.0); activation is the hub's per-customer DR-tier flag + (default ON), and offsite backup REQUIRES that flag (hub v0.51.0 enforces the coupling: + "Offsite backup requires the DR tier"). A box therefore always reaches the ceremony THROUGH + the tier: WG peer → PBS-DR descriptor applied → ceremony. Revisit identity-only ONLY if a + customer class demands lean-tier offsite — this line is here so a future session knows the + absence is a decision, not an oversight. +- **Host packages:** `age` must be installed (identity wrap dependency; installed by host-install + since v1.15.0 — drill finding F-10 closed. Pre-v1.15.0 boxes: `apt-get install -y age`). - **K gate (PBS hosts):** `escrow.pbs_storage_id` set and the key file present (`cfg.Backup.PBSEncKeyPath()`). - **Staged secret (offsite):** offsite enabled → `EscrowState="pending"` on the controller and the staged diff --git a/documentation/runbooks/day0-install.md b/documentation/runbooks/day0-install.md index bfb2a14..736dc8f 100644 --- a/documentation/runbooks/day0-install.md +++ b/documentation/runbooks/day0-install.md @@ -124,13 +124,16 @@ Notes: - vmid picking: the agent's token is pool-scoped, so the hub's guest inventory only shows Felhom-pool guests — other guests on a shared box are invisible there. Always pick from `pct list` + `qm list` on the box (`documentation/runbooks/provisioning.md`). -- Offsite backup (`felhom-pbs`) is NOT part of a go-live today: the PBS host is LAN-only. The - transport is DECIDED and DESIGNED — plain WireGuard, host-side agent-managed, box-dials-out to - a public endpoint VM running the offsite PBS. Evidence: - `documentation/audits/SPIKE-connectivity-wireguard-2026-07-03.md`; design-of-record + slice - roadmap: `documentation/architecture/06-offsite-connectivity.md` (MVP = slices S1→S2→S3, then - S4). Go-live standard = **local backups only** (that is what the Part C command configures); - offsite DR is retrofitted later via `--rescope-acl` / `--acl-storages` without reinstalling. +- **DR-tier-by-default (v1.15.0, 2026-07-12):** every install deploys the FULL DR plumbing + uniformly — the `felhom-pbs-apply` wrapper, `age`, `wg_tunnel.enabled: true` (WG is base + infrastructure like cloudflared), and the default ACL incl. the pre-positioned + `/storage/felhom-pbs` grant. **Capability is baked; ACTIVATION is the hub's per-customer + "DR tier" flag** (default ON for new customers). With the flag ON the cascade converges + hands-free after install: WG peer registers → the hub auto-provisions the ep0 tenancy → the + agent applies the storage entry → the escrow ceremony becomes possible. Flag OFF = zero + Felhom-side cost; the pbsdr capabilities report a neutral "inactive". Offsite app backup + REQUIRES the tier (the ceremony depends on the PBS key — drill F-6, closed by policy). + Design-of-record: `documentation/architecture/06-offsite-connectivity.md`. --- @@ -141,7 +144,7 @@ Notes: ```bash curl -fsSO https://felhom.eu/scripts/felhom-host-install.sh chmod +x felhom-host-install.sh -./felhom-host-install.sh -h | head -3 # sanity: must print v1.10.0 (or newer) — older scripts don't know --mode +./felhom-host-install.sh -h | head -3 # sanity: must print v1.15.0 (or newer) — DR-tier-by-default ships the full plumbing ``` ### C.2 Preview (recommended) @@ -152,7 +155,7 @@ read-only and asks for the passphrase): ```bash ./felhom-host-install.sh --customer-id --mode appliance --vmid \ --cores 2 --memory 4096 \ - --force-gitea-golden --acl-storages "local local-lvm" \ + --force-gitea-golden \ --dry-run ``` @@ -161,7 +164,7 @@ read-only and asks for the passphrase): ```bash ./felhom-host-install.sh --customer-id --mode appliance --vmid \ --cores 2 --memory 4096 \ - --force-gitea-golden --acl-storages "local local-lvm" + --force-gitea-golden ``` Placeholders and flags — what and why: @@ -173,7 +176,7 @@ Placeholders and flags — what and why: | `--vmid` | the free vmid from Part B | ALWAYS pass it explicitly — deterministic, and recorded in the install state for a later `--uninstall` | | `--cores 2 --memory 4096` | the appliance cap | protects the other guests on a shared box; size to roughly half the host if the box is small (cap ≤ host resources) | | `--force-gitea-golden` | — | a fresh box has no local golden archive; fetch it from Gitea and verify its sha256 against the hub manifest (this is the normal customer path) | -| `--acl-storages "local local-lvm"` | — | grants the agent's token write access on exactly these storages. The default set includes `felhom-pbs` (offsite), which does not exist on a customer box — granting it is harmless but misleading; scope to what is real. | +| `--acl-storages` | **do NOT pass it** | the DEFAULT set (`local local-lvm felhom-pbs`) is correct — the `felhom-pbs` grant is PRE-POSITIONED for the PBS-DR tier (the storage appears when the tier applies). Narrowing it 403'd the apply-bridge live (DRILL-day0-vm-2026-07-12: `Datastore.Allocate` on `/storage/felhom-pbs`) and needed a retrofit dual-grant. Only override on a box with genuinely different storage names. | | `--node ` | only on a **cluster** | the script refuses to auto-pick a node on a multi-node cluster | When prompted, type the **retrieval passphrase** (no echo; never on the command line). Alternative @@ -213,8 +216,10 @@ guest. The installer therefore (a) never touches the owner's credentials — the workloads as the thing to protect — the CPU/RAM caps are mandatory, they are the only noisy-neighbor containment there; (c) refuses anything host-invasive that the appliance profile allows (`--enable-oob`, `--rotate-recovery`, unvalidated PVE majors, ACL grants on storages the box -doesn't have); and (d) asserts the written agent config keeps `lan_resolver`/`wg_tunnel`/`oob` OFF -— the agent must not take over the owner's DNS or open tunnels. Recovery on a BYO box = the owner's +doesn't have); and (d) asserts the written agent config keeps `lan_resolver`/`oob` OFF +— the agent must not take over the owner's DNS or start a second sshd. (`wg_tunnel` is ON in +BOTH modes since v1.15.0 — WG is base infrastructure; byo consent = the typed disclosure ack, +which now lists the outbound tunnel, + the tester-agreement disclosure line.) Recovery on a BYO box = the owner's own console access, by design. **Recommended first run — preflight only** (all step-1 checks incl. the byo gates, an explicit @@ -223,7 +228,7 @@ PASS/FAIL verdict, exit 0/1, writes nothing — re-runnable any number of times) ```bash ./felhom-host-install.sh --customer-id --mode byo --vmid \ --cores --memory \ - --force-gitea-golden --acl-storages "local local-lvm" \ + --force-gitea-golden \ --preflight-only ``` @@ -232,14 +237,15 @@ PASS/FAIL verdict, exit 0/1, writes nothing — re-runnable any number of times) ```bash ./felhom-host-install.sh --customer-id --mode byo --vmid \ --cores --memory \ - --force-gitea-golden --acl-storages "local local-lvm" + --force-gitea-golden ``` What byo does differently (everything else matches C.4's eight steps): - `--cores` AND `--memory` are **required** — size them WITH the owner (the guest must never starve - the owner's guests). `--acl-storages` must name storages that actually exist on the box, or the - preflight dies naming the missing ones. + the owner's guests). `--acl-storages` (when overridden) must name storages that actually + exist on the box, or the preflight dies naming the missing ones — EXCEPT `felhom-pbs`, which + is expected absent pre-tier (the PBS-DR apply creates it; the grant is pre-positioned). - **The host's `:53` must be free** (v1.12.0/F6): Felhom publishes the guest by name on the LAN, so a resolver already bound to `:53` blocks it. byo preflight **REFUSES** (does not touch the owner's service — it is a refusal, not a change) with: *"a resolver is already bound to :53 … stop or @@ -334,8 +340,11 @@ golden older than 0.86.0? See Part F troubleshooting.) - **Hosts**: the new host reports (status OK, fresh last-report timestamp). - The host's guest list shows `` running. -- **Capabilities**: 0 degraded. On a reduced-storage box (no `felhom-pbs`) the offsite-backup - related capability is expected degraded — that is the documented go-live standard, not a fault. +- **Capabilities** (rendered on the host page since hub v0.51.0): **0 degraded** — v1.15.0 ships + the full DR plumbing, so the drill's "3 pbsdr-* born DEGRADED" is gone. With the customer's DR + tier flag OFF the pbsdr capabilities show a neutral **inactive** ("disabled by configuration") + chip — deliberate, not a fault. A pbsdr `degraded — binary not found` means the box predates + v1.15.0; the host page shows the retrofit one-liner (wrapper + `age` + `wg_tunnel.enabled`). - The controller version shown for the guest is the golden's baked (current) version — it matches what D.1b's check reported.