docs: v0.109.1 README + REPORT (SLICE 4 deployed + live-verified)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
@@ -1,40 +1,49 @@
|
||||
# REPORT — controller v0.108.0: SLICE 3 hub-verified escrow auto-confirm — DEPLOYED
|
||||
# REPORT — controller v0.109.1: SLICE 4 soft-quota + offsite health — DEPLOYED + live-verified
|
||||
|
||||
**Date:** 2026-07-09 · **Class:** implementation. Baseline v0.107.0 → **v0.108.0** (live on 9201,
|
||||
`Up (healthy)`). The chain pairs: **agent v0.79.0** (ceremony uploads `restic_pw_sha256`) + **hub v0.40.0**
|
||||
(stores it; report ACK serves `escrow:{identity_blob_present, restic_pw_sha256, created_at}`) — both live.
|
||||
**Date:** 2026-07-10 · **Class:** implementation. Baseline v0.108.0 → v0.109.0 → **v0.109.1** (live on
|
||||
9201, `Up (healthy)`). Pairs with **hub v0.41.0** (OffsiteChecker + freeze lever) — live. The offsite arc's
|
||||
last build item.
|
||||
|
||||
## What shipped (controller)
|
||||
`report.EscrowAutoConfirmer` — runs on every report ACK; flips offbox `EscrowState` pending→escrowed ONLY
|
||||
when `sha256(local repo_password) == restic_pw_sha256` from the ACK. Verifies the RIGHT fact: not "a blob
|
||||
exists" but "the blob covers the CURRENT key" (a stale blob — re-provision, inject, drive history — must
|
||||
never truthfully-claim; that would re-open the exact un-recoverable-ciphertext gap fork-4 closed).
|
||||
- Match → flip + wipe the agent-staged secret (the v0.107.0 DELETE path; best-effort, loud on failure).
|
||||
- Mismatch → stays pending + LOUD warn naming the fix ("run the escrow ceremony"), deduped per distinct
|
||||
hash (not per 15-min cycle).
|
||||
- No row / NULL hash / hash-without-identity-blob / no local password file → silently pending (fail-closed).
|
||||
- Non-pending → total no-op — **auto-UN-confirm does not exist**.
|
||||
- `backup.HashResticPassword` (canonical: sha256 hex of the TRIMMED string) + `Manager.OffboxRepoPasswordHash`;
|
||||
**pinned cross-repo test vector** (same in felhom-agent) so the two hashers can never drift silently.
|
||||
- Manual `POST /backup/offbox/confirm-escrow` → documented **deprecated fallback** (legacy hash-less blobs).
|
||||
- **Data:** `OffboxTarget.QuotaGB` (bridge maps it from the descriptor; the descriptor hash now includes it
|
||||
so a hub-side quota change re-applies — via key-auth-first, no password consumed) + `RepoSizeBytes`
|
||||
(persisted from `restic stats`; failed stats keeps last-known — stale-but-safe).
|
||||
- **Pre-run soft-quota gate:** ≥100% refuses NEW backups (Hungarian: "A NAS-mentés túllépte a
|
||||
tárhelykeretet (X/Y GB)…", `LastStatus=error`, operator alert via the existing notify) — but
|
||||
**`offboxPruneOnly` still runs the retention step** (pruning is the only way back under quota) and
|
||||
**restore is never gated**. ≥80% (<100%) sets the Hungarian usage `LastWarning`. Quota 0 = no gate.
|
||||
- **UI:** `/backups` usage bar (used/quota + %, green/amber/red) when quota>0. Template gates green.
|
||||
- **Report:** the hub report carries the non-secret
|
||||
`offsite:{enabled, escrow_state, last_run, last_status, snapshot_count, repo_size_bytes, quota_gb}`.
|
||||
- **v0.109.1 (live finding):** the deploy's own re-apply DEMOTED the escrowed demo to pending + wiped its
|
||||
runtime status (`ApplyOffsiteTarget` replaced the whole target). Now a re-apply preserves the existing
|
||||
`EscrowState` (custody tracks the preserved repo password, not the coords) + runtime status; a fresh
|
||||
guest still lands pending.
|
||||
|
||||
## Tests + companion red-proof (ALL-GREEN, red-proof fired)
|
||||
Scenarios: A match→flip+wipe (+repeat ACK no-op); B stale-blob stays pending + warn-once-per-hash (+new
|
||||
hash warns again); C all silent-pending cases; E never acts outside pending (even a mismatch on an escrowed
|
||||
target is silent); wipe-failure keeps the confirm. **Red-proof:** modeled the blob-present-only check → the
|
||||
stale-blob AND hash-less scenarios flipped when they must not → both tests FAILED. Reverted.
|
||||
Pinned vector + trim convention asserted in `internal/backup` (mirrors the agent's).
|
||||
## Tests + companion red-proofs (ALL-GREEN, all three fired)
|
||||
- Scenario A: over-quota → backup 0 calls, **prune 1 call**, Hungarian status, restore reaches the runner.
|
||||
**Red-proof:** gated the prune too → "prune MUST still run over quota, got 0" → FAIL.
|
||||
- Scenario B: 84% → run ok + "A NAS-mentés a keret 84%-át használja (42/50 GB)." + bytes persisted.
|
||||
- Scenario C: quota 0 → no gate/warn at any size. Bridge quota mapping + quota-change re-apply
|
||||
(panicking consumer) tested.
|
||||
- v0.109.1 preserve: **red-proof** dropped the EscrowState carry-over → "must NOT demote an escrowed
|
||||
target, got pending" → FAIL.
|
||||
- Report object present when enabled / nil when unconfigured (nil-safe hub-side).
|
||||
|
||||
## Live state check (deployed, no operator action)
|
||||
Hub 0.40.0 Synced/Healthy; migration applied (`host_escrow.restic_pw_sha256` present; all 3 legacy rows
|
||||
NULL — demo included). Controller 0.108.0 healthy; the startup report went through the new hub and the
|
||||
demo's `escrowed` state produced **zero `escrow-confirm` log lines** — Scenario E proven live for free.
|
||||
Agent 0.79.0 on felhom-pve (56/56 capabilities).
|
||||
## Live verification (all through the real pipeline)
|
||||
- **Key-auth-first proven LIVE:** the quota-hash change triggered a real re-apply — "existing key still
|
||||
authenticates … re-pinned + reconfigured without consuming a password" against the real box.
|
||||
- v0.109.1 deployed; demo repaired via the deprecated manual confirm (truthful — same escrowed password;
|
||||
the confirm's wipe also removed the re-staged agent secret, verified empty) + a manual run:
|
||||
**2 snapshots, 41s, `escrowed`, `quota_gb: 50`, `repo_size_bytes` 45 MB**.
|
||||
- The next report carried the full offsite object hub-side; the **OffsiteChecker held it healthy**
|
||||
(0 `offsite_*` events — 0% fill, not stale). The `/backups` usage bar renders live:
|
||||
"Tárhelykeret: 43.2 MB / 50 GB (0%)" (curl through the in-guest UI).
|
||||
|
||||
## NOT yet live-exercised
|
||||
The full auto-confirm chain (fresh enable → ceremony records the hash → ACK → auto-flip) — first real
|
||||
offsite enable proves it (Peti onboarding runbook). Unit red-proofs carry the logic per the task.
|
||||
## NOT live-exercised (per the STOP)
|
||||
The freeze lever (would write-lock the demo's working offsite) and the 100% quota-refusal (demo repo is
|
||||
0.08% of quota) — unit red-proofs cover both; live exercise waits for a real need / the Peti window.
|
||||
|
||||
## NEXT
|
||||
SLICE 4 (soft-quota on `quota_gb`), Peti onboarding runbook (will live-exercise auto-confirm + F4),
|
||||
NEW-box restore round-trip → retire the old-box archive.
|
||||
Peti onboarding runbook (live-exercises auto-confirm + F4 + fresh-provision quota), NEW-box restore
|
||||
round-trip → retire the old-box archive.
|
||||
|
||||
@@ -746,6 +746,17 @@ not just those with HDD data. Non-HDD apps can configure destination, method, an
|
||||
> target. Image: `restic` + `openssh-client` (re-added; restic's sftp backend shells out to `ssh`).
|
||||
> - UI: the "Külső (NAS) mentés" section on the backups page (configure target, per-app toggles, run-now,
|
||||
> restore, status). Config: `settings.OffboxTarget` + per-app `AppBackupPrefs.Offbox`. Daily at 04:15.
|
||||
> - **Soft quota (v0.109.x, SLICE 4; pairs with hub v0.41.0).** The shared-model quota (`quota_gb`) rides
|
||||
> the descriptor into `OffboxTarget.QuotaGB` (0 = no limit — dedicated boxes are Hetzner-enforced; the
|
||||
> hash includes it, so a hub-side quota change re-applies via key-auth-first, no password consumed).
|
||||
> `RepoSizeBytes` persists from `restic stats` (last-known on failure — stale-but-safe). Pre-run gate:
|
||||
> **≥100% refuses NEW backup runs** (Hungarian error + operator alert) but the **prune step still runs**
|
||||
> (the only way back under quota) and **restore is never gated**; ≥80% sets a Hungarian usage warning.
|
||||
> `/backups` shows a usage bar when quota>0. The hub report carries
|
||||
> `offsite:{enabled, escrow_state, last_run, last_status, snapshot_count, repo_size_bytes, quota_gb}` —
|
||||
> the hub's OffsiteChecker alerts on fill (90/95%) and staleness (escrowed + no run >48h); the Hetzner
|
||||
> `readonly` freeze is an OPERATOR lever on the hub (never automatic). A RE-apply preserves the existing
|
||||
> target's EscrowState + runtime status (v0.109.1 — custody tracks the preserved repo password).
|
||||
> - **Hub-driven provisioning apply-bridge (v0.106.x, SLICE 2; pairs with hub v0.38.x — validated live
|
||||
> 2026-07-09).** When the hub provisions the offsite tier (a Hetzner Storage Box sub-account or dedicated
|
||||
> box), the served `controller.yaml` gains an `offsite:` section (host/user/port/repo_path/quota_gb +
|
||||
|
||||
Reference in New Issue
Block a user