Commit Graph

212 Commits

Author SHA1 Message Date
admin 0887fd676d REPORT: R-182 — the run digest, the live proof, and the red-proof that did not fail first time
gates / gates (push) Successful in 9s
2026-08-03 13:59:42 +02:00
admin db0d4b129d REPORT: R-181 — the reserve, the live proof, the du measurement and the teardown
gates / gates (push) Successful in 9s
2026-08-03 11:37:33 +02:00
admin d5be67b913 REPORT: CI run ids and conclusions (all three commits green)
gates / gates (push) Successful in 9s
2026-08-02 23:57:07 +02:00
admin 95eb5c2c1a REPORT: record every CI run id, run number and conclusion
gates / gates (push) Successful in 9s
2026-08-02 20:38:52 +02:00
admin e6311f9fbc docs: CONTEXT + REPORT for v0.190.0 (R-157 A / R-170 / R-171)
gates / gates (push) Successful in 8s
2026-08-02 20:34:48 +02:00
admin 3446609420 REPORT: record all three CI run IDs and their conclusions
gates / gates (push) Successful in 9s
2026-08-02 18:58:56 +02:00
admin a8f7c61d41 docs: CONTEXT + REPORT for v0.189.0 (R-166)
gates / gates (push) Successful in 9s
2026-08-02 18:58:11 +02:00
admin e7c44c0e0f docs: CHANGELOG + REPORT for the CI workflow (no version bump)
gates / gates (push) Successful in 9s
2026-08-02 16:35:42 +02:00
admin eaded79b18 REPORT: gate enforcement session (no version bump) 2026-08-02 15:37:02 +02:00
admin 4115e88f68 REPORT: D5 — restore from the drive alone (v0.188.0), proven live 2026-07-30 17:00:32 +02:00
admin 2f27a363d5 R-108: network storage may not host an app's data namespace (v0.187.0)
This is D5's precondition and it is now met.

An app's namespace root IS its backup root: namespaceRoot returns a non-system
drive path as-is, so the recovery unit lands at <HDD_PATH>/backups/primary/<stack>/.
On a NAS that sits inside the share, which FileBrowser binds WHOLE — share root,
:rslave, download:true.

The bind was NOT narrowed, and establishing why inverted the fix. The share-root
:rslave bind is load-bearing (a 2026-07-22 probe proved an in-container access
through it wakes the idle automount trigger), and scoping is undefinable anyway:
apps on a share store at <share>/<app>, there is no userdata/ layer, and creating
one would write Felhom convention onto a customer's own NAS, which R-67 forbids.
So the browsing surface cannot be narrowed and the backup tree must never be
placed under it. Operator ruling: refuse the placement, keep the browse bind.
Tier 2 already refuses network targets for this reason (F-6C-1).

Nothing stranded: zero apps on network storage across all six hub customers
including Peti. R-67's browse capability is byte-identical.

FIVE surfaces, not the four the register named — settings.RefuseAsAppNamespace is
the single predicate. The deploy POST is the real boundary (it accepts any
caller-supplied HDD_PATH; DeployStack validates only os.Stat). Surface 4,
handleStorageDecommission mode=migrate, guarded only its SOURCE, so a whole
namespace could be decommissioned ONTO a NAS — that one is not in the register.

Fails closed: /mnt/felhom-drives holds both kinds, Kind exists only on a
registered path, so an unregistered path under that root refuses.

Supersedes README's "NAS backup locality — decision A" (v0.118.0).

9 tests, all non-effect (nil stackMgr, so a guard that misses panics rather than
passing). 4 red-proofs, each mutation asserted to have landed.
Suite rc=0, 27 packages, 0 FAIL. vet rc=0. Template + emoji gates OK.
2026-07-30 14:10:20 +02:00
admin b331f18424 v0.186.0 — R-114 + R-112: tell the truth about the backup target, then show it
Two defects E-2d found on a real box, fixed in this order deliberately: the
message is corrected BEFORE it is put on screen, because switching on a banner
that lies is worse than a silent one.

R-114 — the third state. resolveBackupTargetState had two outcomes: a disk
claims the target (healthy), or nothing does (degraded, "the backup is on the
system disk"). The state "configured, and its drive is gone" had no branch, so
it fell into the second and inherited its message AND its offer. Observed live
with the target detached: degraded:true, target:"felhom-backup" plus the
system-disk copy (false -- the backup was on a drive that had vanished) plus
offer_path naming that same vanished drive as the remedy.

New BackupTargetState.TargetAbsent discriminates. Degraded keeps its meaning
("is there a problem") so the wire contract is unchanged for every consumer;
TargetAbsent answers "which problem", because the two have opposite remedies --
attach any second drive, versus reconnect THAT one. Copy routed through
degradedMessageFor so one place still decides what a customer reads. The offer
is suppressed on the branch itself, NOT left to firstOfferableDrive's
Disconnected skip: that flag is set by the agent-side gate in another repo
(R-113), and this state must be correct independently of it.

R-112 — the state finally has a consumer. The endpoint was byte-correct and
nothing in the product ever asked for it: templates fetch 18 distinct
/api/storage/* endpoints and backup-target[/assign] were the only two with zero
references. Server-rendered on /backups now, following the existing
SingleCopyWarning banner pattern -- not a 19th JS fetch, because a banner that
needs JavaScript to appear is one more thing that can silently not happen.
backupTargetView returns nil for healthy and unknown so those render nothing at
all. The offer control POSTs to the existing assign endpoint behind the standard
inline confirm, never auto-submits, and surfaces restart_required honestly
instead of adding a self-restart.

Scenario E (the seam test) drives backupsHandler over httptest and asserts the
RENDERED HTML -- handler -> view -> resolver -> template. It deliberately does
not call the resolver and assert a string, which would prove the resolver that
was never broken. Deleting the one line that sets data["BackupTarget"]
reproduces the R-112 state and fails every render assertion.

Tests 326 -> 338 (+12) in internal/web; suite green (27 packages); both template
gates pass. Three red-proofs run and reverted, files byte-identical after.

MinAgent unchanged at 0.113.0: R-114 reads BackupTarget/MountPath/GuestPath/Role,
none of which R-113 altered (it changed BoundUnderParent, which this code does
not read). demo-hp on agent 0.113.0 is not held.

The absent copy is verbatim the hub's customerMessages["backup_target_absent"]
so the banner and the email tell one story -- filed as a two-repo drift risk,
not solved.

NOT LIVE-VALIDATED. Scenario C cannot occur on a healthy box; Session C proves it.
2026-07-29 19:21:32 +02:00
admin d8b3279731 REPORT + CONTEXT: R-101 + F-DIAG (v0.182.0), rendered dialog proven live 2026-07-28 16:46:16 +02:00
admin 3db8bfb953 REPORT + CONTEXT: R-100 producer half (v0.181.0), proven live 2026-07-28 13:36:00 +02:00
admin 4056feccee REPORT + CONTEXT: F-OBS shipped (v0.180.0); same shape found and fixed in the agent watchdog 2026-07-28 11:22:04 +02:00
admin a63409c843 docs: F-CRIT-1 + F-A1 — REPORT, CONTEXT (v0.179.0)
Six red-proofs with observed failure text, the bounds chosen for contention
(15m retry / 3h alarm) justified against the agent's own 120m PBS restore-test
ceiling and the 12m01s longest observed restore-test, and the live replay of
both findings on demo-hp with the hub DB as arbiter:
  - failed restart alarmed 9s after grace expiry; banner shows (stopped)
  - a deliberate user stop stayed silent through 9 dead-app scans
  - 409 contention produced 0 operator emails (8 -> 8)
  - a real transport failure produced 1 (8 -> 9)
Same box, same event type; the only difference is 409 vs a genuine error.
2026-07-28 09:47:55 +02:00
admin ca013c8d27 docs: REPORT for controller v0.178.0 (R-88 Part 2 + R-97c)
Records the valve-licence table, why a missing field means legacy rather than
unknown, and the near-miss: TieredBackend is runtime-asserted, so the signature
change built and vetted clean while the adapter silently stopped satisfying it.
2026-07-27 18:12:35 +02:00
admin ba8bf9cd75 docs: REPORT for R-97 (controller v0.177.0 + hub v0.78.0)
Includes the cooldown keying finding (per-event-type only; fixed narrowly with a
tier suffix), the 180s grace window derivation, and the end-to-end event evidence
with its control: the new type 200s, a bogus type 400s, and notification_log shows
one operator row and zero customer rows.
2026-07-27 17:07:10 +02:00
admin ccefff4f39 docs: record POSITIVE post-deploy verification for v0.176.0
The quiesce loop is silent by design when nothing is due, so 'no error lines' is
not evidence. The observable that is: every /backup/due makes the agent read that
tier's storage content, which lands in pveproxy/access.log. Both tiers polled
every 5 min, both HTTP 200, tick phase consistent with the deploy restart.
2026-07-27 16:36:30 +02:00
admin b8598361b8 docs: REPORT for R-88 Part 1 (v0.176.0)
Includes the Phase 0 findings: the nil is agent-side (case a, a type boundary
that cannot represent 'unknown'), the four restarts were deliberate rather than
self-update, and the loop produced zero backup_failed events because quiesce
never imports notify (filed R-97).
2026-07-27 16:27:21 +02:00
Claude Code 3f0420ff9c REPORT: correct a STALE deployment section — v0.175.0 IS deployed on both boxes
The section read 'NOT deployed'. True when written (17:56 CEST), stale 26 min
later when I deployed (18:22 CEST) and did not update it. REPORT.md is defined
as 'overwrite with the most recent state', so an artifact this project validates
against was left asserting the opposite of reality.

Precision matters on the apparent contradiction: a 17:01 quiesce log 'before' a
17:56 commit is a TIMEZONE artifact (controller logs UTC, git commits CEST), not
evidence of a false claim. In one clock: REPORT 17:56 -> deploy 18:22 -> quiesce
19:01. Consistent. That host=CEST/controller=UTC mismatch is a recorded project
trap and caught me once earlier the same day.

Also records the live multi-tier quiesce evidence and what remains unproven.
2026-07-26 20:43:02 +02:00
Claude Code f5e106440d REPORT: R-82 Slice B (controller v0.175.0) — unit-proven, deliberately NOT deployed
The multi-tier path only engages against an agent serving /backup/tiers, and the
agent rollout was still mid-flight, so both production controllers remain on
v0.173.0. Names the three things that are therefore unproven live.
2026-07-26 17:56:10 +02:00
admin 47fda06ba1 REPORT: R-77 v0.173.0 — Part 0 repair, red-proofs, live legs 1-4 2026-07-26 09:25:15 +02:00
admin c7a3a90782 REPORT: R-75 v0.172.0 — Part 1.0 probe, switch audit, red-proofs, live legs 2026-07-26 08:30:43 +02:00
admin 7d5b0163ef v0.171.0: REPORT (overwrite) — device-model card labels live on 9201 2026-07-25 08:32:21 +02:00
admin de14eedb9f v0.170.0: REPORT (overwrite) — root→launcher, gofmt, stale-note, gate-check finding, SMART spike 2026-07-25 07:57:00 +02:00
admin 0a582ea07b v0.169.x: REPORT — appended (disk-health card + degradation notification, live-validated on 9201) 2026-07-24 21:41:02 +02:00
admin e164fef70c v0.168.0: REPORT — live-validated backup window on 9201 (save/reschedule/restore, box left at 02:30) 2026-07-24 20:59:15 +02:00
admin e33c1aeabc v0.167.1: REPORT note — centered sidebar logo, live-verified on 9201 2026-07-24 13:59:29 +02:00
admin 59d182a6b9 v0.167.0: REPORT — outlined logo/favicon live-verified on 9201 (0 text, viewBox unchanged) 2026-07-24 13:50:37 +02:00
admin dbd1ff0c17 v0.166.0: REPORT — live validation on 9201 (drawer markup, no customer-name, versioned URLs; Part 4 gated) 2026-07-24 13:31:23 +02:00
admin 1fd070615d v0.165.1: REPORT — live rendered-page assertion on 9201 (all Group-A strings present, files absent) 2026-07-24 12:45:19 +02:00
admin a04afc367b v0.165.1: native Megosztás… button in the share modal (Web Share API)
Feature-detected navigator.share opens the OS share sheet with title+text+URL only
(no QR files: — narrow support drops the URL when given file+URL). Hidden unless
supported; Link másolása stays the universal fallback and catches the non-cancel
rejection; AbortError is silent. Template JS + tests only. 2 red-proofs verified red.
2026-07-24 12:43:14 +02:00
admin 570fb30147 v0.165.0: REPORT — live validation on 9201 (§13, all checks pass, token redacted) 2026-07-24 12:17:23 +02:00
admin 15206314ab v0.165.0: Indítópult megosztása — guest launcher via capability URL (+ optional password, QR)
Mint a 160-bit capability URL (/s/<token>) serving a standalone read-only guest
launcher: same tiles, opens apps in new tabs, no account, no admin session.
Information only, zero control — every privilege stays behind each app's own auth.

- /s/ pre-auth pass-through (after the claim gate) + session-CSRF exemption; guest
  password POST carries its own pre-auth HMAC CSRF.
- Constant-time token match; empty stored token = disabled = byte-identical mux 404.
- Optional per-share password: separate bcrypt hash + own attempt map; signed cookie
  = HMAC(token|passwordHash) keyed with web.session_secret, so rotate/change invalidates.
- Guest labels ride the v0.164.0 ruling; never expose internal state vocabulary.
- Token redacted in logs (/s/<redacted>); never in CHANGELOG/REPORT/CONTEXT.
- Admin modal: copy-link, QR (go-qrcode), set/clear password, rotate, disable.
- Tests: Groups A-G (14) + 3 red-proofs verified red.
2026-07-24 12:08:43 +02:00
admin 8e5edb2865 REPORT: v0.164.0 live-validated on 9201 (stop silent; fault alarms; stop->start clean) 2026-07-24 11:06:57 +02:00
admin 77956d8df2 REPORT: v0.163.1 launcher polish — live-validated on 9201 (monogram hidden, placeholder on backups_apps/stacks) 2026-07-24 10:17:43 +02:00
admin 7a53cb43ef REPORT: full authenticated live-validation of /launcher on 9201 (4 tiles, colors, filebrowser incl.) 2026-07-24 09:26:56 +02:00
admin ea432ca74a REPORT: v0.163.0 live-validation results (deploy healthy, placeholder + auth-gate verified live) 2026-07-24 09:22:48 +02:00
admin 4aa7d41cac REPORT: v0.163.0 Indítópult launcher + app placeholder 2026-07-24 09:17:39 +02:00
admin a71cc58327 docs(R-71a): live evidence — 0.162.0 deployed both boxes, settle-gate GO captured
demo-felhom 9201 + demo-hp 9201 (G1 break-glass, secret shredded) both clean +
healthy on 0.162.0 with the settle-gate GO line. Honest B' accounting recorded:
the floor is in-memory (not persisted) so both above-floor boxes logged
"awaiting floor knowledge" then GOed ~10s later once the report ACK landed —
the report-ACK latency the 90s sub-bound is sized to, not a regression. The
zero-wait-when-floor-known invariant stays unit-proven (test E).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7Drmtm2RzoqbkJZCNSFNQ
2026-07-24 07:53:49 +02:00
admin cb8bf14599 v0.162.0 — R-71(a): the apply-bridge settle-gate (kills the F10 day-0 race)
The day-0 race (DIAG-f10): a fresh box boots below the operator floor, the
apply-bridge consumes the single-use offsite password, then ~35s later the
managed auto-floor update replaces the container mid-install -> the new process
finds no installed key -> consume -> 404 -> offsite dead until an operator
Re-issue. Recurs on every onboarding whose ISO floor lags the managed floor.

Ordering-only fix (consume/install/persist internals + the 404-no-oracle
contract + the Consumer UNTOUCHED; R-71(b) rejected-by-design):
- New seam offsiteapply.SettleProvider.SettleState() + SettleFunc adapter over
  the self-updater's own GetFloor()/IsUpdateRunning() (no second floor path).
- Bridge.AwaitSettle polls 10s BEFORE the 3-min Reconcile ctx: defers while an
  update runs or the box is below the known floor; GOes at/above floor on the
  first poll with zero added latency (B'). Bounds 90s floor sub-bound / 5min
  overall, both GO+WARN (hub that can't serve a floor can't serve a consume ->
  no burn risk; R-71c is the belt). ReconcileWhenSettled = gate then reconcile.
- main.go: bridge goroutine moved after the updater is built; wired only when an
  updater exists (nil Settle = reconcile immediately, old behavior).

Finding: the floor is in-memory (report-ACK ~5-10s), NOT persisted -> unknown on
any restart until the first ACK; the 90s sub-bound is sized to that.

Tests (injectable clock, fake SettleState, recorded Consumer): A-E + nil-provider
+ cancelled-gate. Four red-proofs all observed FAIL then restored: gate removed /
updateRunning branch / floor sub-bound / overall bound. Deferral paths ship
unit-proven + red-proofed, NOT live-fired -- their precondition is now
structurally prevented by the v1.25.0 build gate. Layering: gate prevents, (a)
defers, (c) heals.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7Drmtm2RzoqbkJZCNSFNQ
2026-07-24 07:48:45 +02:00
admin ce8531426c v0.161.0 — R-70: truthful hub-managed offsite empty state (kiépítve/automatikus banner replaces igényelhető + nincs beállítva in the pre-apply window)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NKSN3gSg4TKVBBqkwW2djR
2026-07-23 12:54:59 +02:00
admin 0eba37d5cd docs(report): R-67 — probe GO, two-gate design, cross-box live leg incl. dead-NAS recovery
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UuFPHmHNrCJj1VhY6QdDMU
2026-07-22 14:18:03 +02:00
admin 9610906916 docs(report): R-66 implementation, the netns-door decision, and the two-box live validation
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UuFPHmHNrCJj1VhY6QdDMU
2026-07-22 13:59:07 +02:00
admin 4aa2ce4b61 docs(report): lifecycle implementation, the v0.158.1 defect, and the R-55 live leg 2026-07-21 16:36:14 +02:00
admin a96226a8ae docs(report): R-55 scope correction, spike-lite finding, red-proof and deploy verification 2026-07-21 15:34:13 +02:00
admin 83f20c8293 docs(report): header/§1/§4 reflect the deploy + STOP-1 results 2026-07-21 12:57:44 +02:00
admin 1dad3c97fd docs(report): STOP-1 evidence — both legs passed; P1 answered; new R-55 finding 2026-07-21 12:57:27 +02:00
admin 984ea8c8bd docs(report): TASK-D Parts 1-2 — R-51/R-52 shipped, image built, STOP legs pending 2026-07-21 12:39:13 +02:00