Commit Graph

205 Commits

Author SHA1 Message Date
admin b7cbded429 feat(configs): D1 Part 1 — self-update host artifacts (guarded wrapper, rollback unit, limits drop-in, sudoers alias)
Design provenance SPIKE-agent-selfupdate-2026-07-05 (SF findings binding):
- felhom-selfupdate-guarded: apply <staged> <sha256> / commit / rollback.
  Ordering [SF-7]: temp-sweep → path confinement (staging dir only, no '..')
  → 64-hex + sha256 RE-verify as root (BEFORE .prev) → same-fs assert →
  .prev snapshot → root-owned staging copy → atomic mv → pending marker →
  reset-failed [SF-4/5] → detached systemd-run restart, verbatim [SF-6].
  rollback is pending-guarded (no pending → exit 0 no-op, [SF-1]) and clears
  pending BEFORE its restart so per-crash OnFailure re-fires no-op. commit
  idempotent, .prev retained (S3d). No env-overridable paths (path-fixedness
  is the security property). shellcheck clean.
- felhom-agent-rollback.service: Type=oneshot OnFailure target; comment block
  documents the systemd-257 per-crash firing reality [SF-1].
- felhom-agent-limits.conf: [Unit]-ONLY drop-in [SF-3] with the spike's tuned
  values verbatim [SF-2]: StartLimitIntervalSec=120, StartLimitBurst=4,
  OnFailure=felhom-agent-rollback.service.
- sudoers: FELHOM_SELFUPDATE alias (coarse apply glob per S4b — the wrapper
  re-verify is the real gate) appended to the grant line. visudo -cf OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-05 15:20:37 +02:00
admin 72d7f05bf1 docs: REPORT + CONTEXT for S5 safe halves (WG-key install + directive→plan)
Parts 1-2 shipped + deployed (v0.69.0); Part 3 not needed (sqlite3 export);
Part 4-A re-attach safety already unit-proven; Part 4-B destructive 9201 drill
prepared + operator-gated (pre-flight green), not executed by CC.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-04 21:12:19 +02:00
admin bd4bced771 dr: recovered WG-key install + host_loss directive→restore-PLAN (S5 safe halves)
wgtunnel.InstallRecoveredKey: write an escrow-recovered WG private key (create-
only, refuse-overwrite) so the tunnel re-establishes with the same identity/pubkey
(same /32), no keygen. Wired into identity-consume -install-wg-key (opt-in;
pre-S3 blob → logged fresh-keygen fallback). Value never logged.

internal/dr (new): consume the host_loss restore_directive (was logged-ignored)
into an inspectable RestorePlan via the AddConsumer raw seam — per guest
{vmid,archive,target,sizing} + per drive {durable_id→mount} + offsite PBS coord.
DERIVE-AND-SURFACE only; the Consumer has no restore/destroy dependency (execute-
nothing is structural). guest_loss/absent → no plan.

Tests + red-proofs (WG create-only overwrite; plan mode-gate). No secrets on
argv/stdout/logs. The destructive in-place restore is a separate operator-present
STOP-gated drill.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-04 21:06:31 +02:00
admin 567cf9f401 docs: REPORT + CONTEXT for S4.1 (tier-aware deadline + teardown phantom confirmed)
Live diagnosis on the agent-token path: offsite restore-test pass:true,
verified:boot+running, teardown succeeded (no 403), scratch band clean. The
VM.Allocate follow-up was a PHANTOM — the earlier 403 was the 10m-timeout firing
teardown against a not-yet-pool-associated guest; with the timeout fixed the
guest is in /pool/felhom and teardown is authorized. No ACL/host-install change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-04 19:54:46 +02:00
admin dc70e15d28 reconcile: tier-aware restore-task deadline (S4.1 unattended offsite restore-test)
A WAN (pbs-tier) restore of a large guest exceeds the restore-task wait's 10m
default → the wait expired mid-restore, teardown fired against a still-restoring
(not-yet-pool-associated) scratch guest → leak + a phantom VM.Allocate 403.

- RestoreTestSpec.RestoreTaskTimeout (0→10m default); the restore WaitTask passes
  it. Local tier unchanged (10m).
- config RestoreTestPBSRestoreTimeoutSeconds + accessor (default 120m).
- main restoreTaskTimeout(cfg,tier): configured PBS timeout only when tier==pbs,
  else 0. Both scheduler + selftest spec builds.
- Tests + WaitOptions red-proof + accessor contract.

The "grant scratch-band VM.Allocate" follow-up is diagnosed not blind-applied:
the scratch is restored INTO /pool/felhom (ACL already grants VM.Allocate), so
the earlier 403 was a consequence of the timeout. No ACL/host-install change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-04 19:45:26 +02:00
admin fee1fcfccd docs: REPORT + CONTEXT for S4 (PBS over the tunnel, live-validated)
Scenario-D green: real vzdump of 9201 → ciphertext in ns over the tunnel;
ns-scoped verify=ok under the box's own DatastoreBackup token; WARN gone;
restore round-tripped (decrypt with box-born key → boot → teardown).

Findings recorded: retarget field is local_backup_target (reverted to local —
single-target offsite is near-continuous given the ~30-min controller cadence;
needs Tier-1/Tier-2 split); restore-test scheduler needs a WAN deadline +
scratch VM.Allocate; escrow left as an operator-present step.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-04 17:25:49 +02:00
admin 027948bf3f pbs: namespace-aware client for per-customer offsite tenancy (S4)
Phase-1 live probe (felhom-hetzner) proved backup/restore/list/isolation over
the tunnel with a per-customer DatastoreBackup token, but the agent's PBS client
was namespace-unaware: Snapshots hit the datastore root (403 for a scoped token)
and Verify was whole-datastore (needs Datastore.Verify ~ admin). Operator-
approved fix.

- pbs.Config.Namespace + Client.namespace; Snapshots appends ?ns=; Verify sends
  ns= (ns-scoped verify works with DatastoreBackup on the own ns — no admin
  widening, Phase-1 confirmed). Root-ns clients unchanged (whole-datastore).
- proxmox.Storage.Namespace (parsed from /storage `namespace`).
- pbsTargetsFromPVE threads s.Namespace into the client.

Confirmed tenant ACL: DatastoreBackup on /datastore/felhom-offsite/<ns> (NOT
/ns/<ns>) to BOTH felhom@pbs (user) AND felhom@pbs!<ns> (token) — PBS privsep =
intersection; isolation holds (cross-ns 403 proven). TestClient_NamespaceScoping
red-proofed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-04 16:12:15 +02:00
admin 734f45c422 wgtunnel: v4-pin + re-resolve watchdog; FELHOM_WG Critical flips (S4 agent half)
v4-pin (doc 06 §4.2): renderConf takes a pre-resolved IPv4 literal and writes
Endpoint=<ip>:<port> — never the DNS name, never AAAA. Resolver seam (A records
only, LookupNetIP "ip4"); multiple A → lowest (deterministic fleet-wide);
renderConf stays pure. Resolved IP cached: steady-state Apply = zero DNS + zero
execs. DNS failure keeps the last conf (never a teardown).

Watchdog (loop-only, so Apply's zero-exec steady state is untouched): handshake
age > wg_tunnel.stale_after_seconds (default 180) → re-resolve; IP changed →
re-render + restart (endpoint re-IP recovery); IP same → no churn (throttled
warn). Staleness read reuses wg show latest-handshakes (never dump).

Capability: wg-conf-install/enable/restart/handshake-read flipped Critical=true
(backups ride the tunnel from S4); apt-install + disable stay non-critical.
TestWGCapabilityCriticality pins the set.

Tests + red-proofs a/b/d all fire. No new sudoers grant; no wire/JSON change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-04 15:47:17 +02:00
admin c618fc69f7 docs(REPORT): publish + Day-0 vouch of 0.65.0 completed (operator-assisted)
Gitea publish sha256 ae282669… round-trip verified; hub manifest now vouches
agent 0.65.0 + golden 0.98.3 (flash + reload-persist verified); /offsite +
Hosts pages eyeballed (0.65.0 ONLINE).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-04 14:15:10 +02:00
admin ef18454e65 docs: REPORT + CONTEXT for S3.1 MTU 1280 (v0.65.0 live on felhom-pve)
Deploy + wired smoke recorded: agent re-rendered conf on restart, live iface
MTU 1280, PBS page loads (no regression). Red-proof outcome recorded.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-04 14:00:20 +02:00
admin 312fd5ee29 wgtunnel: client MTU 1420 → 1280 (S3.1, resolve §4.3 open decision)
The 2026-07-04 CGNAT smoke test found MTU 1420 silently black-holes bulk TCP
on sub-~1480 paths (mobile ~1400, DS-Lite ~1452): handshake+ping stay healthy,
PBS TLS page (and at S4 the backup itself) drops. Set a fleet-wide, permanent,
family-agnostic client MTU of 1280 (RFC 8200 IPv6-minimum floor; outer 1340 v4
/ 1360 v6 fits every realistic path).

Client-only by construction — interface MTU caps box→PBS, advertised MSS caps
PBS→box; the endpoint's wg0 is untouched (zero live-endpoint risk). New const
clientMTU=1280 as the single home; golden pins exact "MTU = 1280" (red-proofed
against a 1420 flip). Stale report.go comment updated. No wire/JSON change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-04 13:57:20 +02:00
admin 03edb40fc2 docs: v0.64.0 shipped — CHANGELOG + CONTEXT + REPORT (S3 live-validated on felhom-pve)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-04 08:57:56 +02:00
admin e2b6c63ea2 wgtunnel: S3 Part 3 — FELHOM_WG sudoers + capabilities + config (DEFAULT OFF) + main wiring + escrow join
Sudoers: fixed-path conf install, enable/restart/disable, latest-handshakes-only
wg read (dump FORBIDDEN — the S1 incident). 6 capability-manifest entries
(Critical=false until S4 makes the tunnel load-bearing). WGTunnelConfig with
enabled=false DEFAULT (the safety gate: a v0.64.0 rollout without explicit
config is a no-op). Daemon wiring mirrors lanresolver + AddConsumer +
SetWireguardReporter; --selftest=wgtunnel single-shot. IdentityBundle
+wg_private_key (omitempty; pre-S3 blobs cannot be retrofitted — documented)
with escrow-create auto-inject (field name only in logs). Red-proof (e) run.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-04 07:14:33 +02:00
admin fb248961c6 wgtunnel: S3 Part 2 — manager state machine + loop + desired raw-consumer seam
Manager: one-shot registration (marker gate; backoff cap 15m), adopt-lost-marker,
re-key-on-mismatch, REVOKED-STAYS-REVOKED teardown (marker kept, zero execs on
later ticks), no-teardown-on-absent-data, hash-gated apply (zero execs steady
state), restart-not-reload on conf change, self-heal enable. Status stanza with
latest-handshakes-ONLY wg read. Collector WireguardReporter seam. desired.Syncer
AddConsumer fan-out with panic containment. Red-proofs a/b/d run + reverted;
no-key-material-in-logs asserted.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-04 07:09:01 +02:00
admin 0daae92c4f wgtunnel: S3 Part 1 — pure-Go keygen + hub wire (WireWireguard, report stanza, RegisterWG)
key.go: create-once 0600/0700, corrupt-refusal (never overwrite — may be escrowed
identity), clamp for CANONICAL STORED form (x/crypto X25519 clamps derivation
internally — discovered during red-proof (c); the stored-clamped test is the
real anchor). Fixed vectors generated with real wg pubkey (provenance in test).
hub: WireDesiredState.Wireguard + WireguardStatus report stanza + RegisterWG
client (typed errors, token-free). S2 golden copied BYTE-IDENTICAL + field-exact
decode test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-04 07:00:19 +02:00
admin 4ba1b144d6 docs: REPORT/CONTEXT — golden 0.98.3 rebuild validated + vouched (B5+B1 closed)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-03 18:38:20 +02:00
admin ceca355493 configs: build-golden.sh v2.0.0 — mandatory controller tag (B5) + bootstrap .path unit (B1)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-03 18:21:21 +02:00
admin c9f963d9f4 docs: REPORT/CONTEXT — v0.63.0 shipped + live-verified (B3 reload-on-miss, B2 snippets dir)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-03 17:26:29 +02:00
admin f31a76f788 v0.63.0: B3+B2 fresh-install fixes — TokenStore reload-on-miss + guesthook snippets dir
B3: Lookup re-reads the append-only store once on a miss (cross-process
coherence with the one-shot provisioner; size short-circuit bounds the cost;
behind the TokenAuthority seam). B2: fenced mkdir -p /var/lib/vz/snippets
before the snippet install + the one narrow sudoers grant. Both red-proofed;
drill findings DRILL-day0-cleanroom-2026-07-03 B3/B2.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-03 15:48:24 +02:00
admin 84f3f7ddb1 docs: REPORT — v0.62.0 A1 fix shipped + live-verified (rescope → deploy → 47/47 caps, pool scan pool=felhom listed=1 scanned=1)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-03 13:36:08 +02:00
admin 3f37c5fc23 v0.62.0 — A1: pool-membership ownership check for the stale-lock reaper
staleLockController.Guests() = ListLXC ∩ GET /pools/felhom members (ownership
PROVEN via the pool registry, never assumed from enumeration scope); pool-read
failure fail-safes the whole recovery through the existing guest-list guard.
New Client.Pool read (needs Pool.Audit — host-install v1.9.0; Pool.Allocate
does NOT satisfy it, spike T2). Composed pve:pool-read capability (non-critical)
+ --selftest pool-read line. Red-proofed negative tests drive the REAL
controller over a broad-token-shaped fake.

Per SPIKE-a1-pool-membership-read-2026-07-03.md; audit A1
(AUDIT-blast-radius-hostroot-localapi-2026-07-02).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-03 13:25:05 +02:00
admin 3e760a559a docs: CLAUDE.md refresh — version narrative removed, layout completed (17 pkgs + opsign), skill pointers
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-03 11:25:56 +02:00
admin 2a024b6039 docs: REUSE.md introduced — cross-repo reuse map (exec/format-safety guards, durable-id seams, traps; docs-only, no version bump)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-03 09:39:32 +02:00
admin 0db09bf34f docs: REPORT — v0.61.0 audit-fix batch shipped, live-verified on felhom-pve, Gitea-published
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-03 07:31:32 +02:00
admin 3f382bf762 v0.61.0: audit fixes B1 (random temp staging) + D1 (mkfs wrapper member/RO re-checks) + D2 (empty-lsblk fail-safe) + D3 (blank-format anti-retarget)
From AUDIT-blast-radius-hostroot-localapi-2026-07-02.md. Each fix ships with a
non-hollow test + a companion red-proof (shown failing on the pre-fix impl).
Sudoers install-source grants became globs — deploy the sudoers drop-in with
the binary. A1 (stale-lock pool-membership) deliberately excluded (spike).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-03 07:25:50 +02:00
admin cc93dae792 docs: REPORT — v0.60.0 proof-of-launch gating shipped, live-proven, published
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 10:28:18 +02:00
admin b9356d60ab v0.60.0: proof-of-launch destroy gating (F1a/b/c) + restore-test band-advance (F2)
Campaign pool-effects F1 (HIGH): the bring-up compensating rollback and the
restore-test teardown destroyed the target vmid even when RestoreLXC failed
synchronously without creating anything — destroying a guest the transaction
never made (only the pool ACL 403 contained it). A RestoreLXC UPID is now the
sole destroy authorization in all three destroy paths (in-process bring-up
defer, in-process restore-test teardown, Recover). F2: the restore-test
advances past an 'already exists' band vmid (invisible squatter) instead of
failing + false-alerting; a fully-occupied band Skips.

Red-proof verified: with the gates reverted, the four new tests fail with the
innocent-guest destroy. go build/vet/test clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 10:18:51 +02:00
admin 55ade9e254 docs: REPORT — add agent v0.59.0 (registry-row device+capacity)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 21:37:21 +02:00
admin 39ec76dc5a v0.59.0: resolve registry-row BackingDevice to the real /dev node
Use ResolveStorageDevice (EvalSymlinks) so a registry drive shows /dev/sdd, not
the /dev/disk/by-uuid/<uuid> symlink path — matching Observe-sourced rows.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 21:35:48 +02:00
admin 5ff5f8e0ab v0.59.0: report backing device + capacity for a registry-sourced /disks row
Agent-view showed "—" device + no size for a raw (no-PVE-storage) drive because
the registry union row never set backing_device/total_bytes/used_bytes (Observe
drives get those from pvesm status). Resolve BackingDevice via ByUUIDDevicePath +
read capacity via statfsCapacity (build-tagged syscall.Statfs; no-op off-Linux).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 21:34:11 +02:00
admin b52680ab47 docs: REPORT for raw-drive first-class support (agent v0.56-0.58; live-validated)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 21:27:22 +02:00
admin 3825664aed v0.58.0: report GuestPath/BoundUnderParent for a registry-sourced /disks row
The Impl-2a registry union row omitted GuestPath + BoundUnderParent, so the
controller read a registry-only (raw) drive as "Leválasztva" despite being mounted
+ bound live. Populate them like the Observe path (StablePathForRaw +
boundUnderParent). Last piece of first-class raw-drive support.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 21:21:59 +02:00
admin 6448b80361 v0.57.0 fixup: wire HostReader into the local-API server (was nil in prod)
buildLocalAPIServer never passed Options.HostReader, so s.host was nil — the
v0.56.0 durableIDForMount raw-mount fallback + the role gate's host classification
silently no-op'd. Wire storage.NewProcHostReader(). This makes the raw-drive
intent/guest-bind resolution actually fire live.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 21:18:21 +02:00
admin 67e2144f11 v0.57.0: re-assert a raw drive's guest-bind (ReassertGuestBinds mount-table fallback)
ReassertGuestBinds mapped durable-id->mount from Observe() only, so a raw enrolled
drive was never found and its in-guest bind wasn't re-asserted after reboot/re-mount
(drive showed "Leválasztva"). Augment the map from the mount table (raw /mnt/<name>
-> device fs-UUID via HostReader), skipping the /mnt/felhom-drives bind; Observe
failure no longer aborts. Completes v0.56.0's raw-drive durability.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 18:37:31 +02:00
admin e593fb277a v0.56.0: resolve a raw enrolled drive's durable-id from the mount table
durableIDForMount was Observe-only, so a raw (non-PVE-storage) drive's enroll
intent + guest-bind went unrecorded ("durable-id unresolved") — it mounted+bound
but wasn't intent-tracked (RegistryKnownTargets skips intent==new). Fall back to
resolving the mount's device fs-UUID (HostReader.Mounts + ResolveUUID) →
uuid:<fs-uuid>, same scheme as Observe. Test + red-proof. Residual:
ReassertGuestBinds still Observe-based (raw guest-bind not re-asserted post-reboot).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 18:29:22 +02:00
admin 4f8d2fcb57 docs: REPORT for agent v0.55.0 (Impl-2a discovery + registry tracking; validated)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 17:38:52 +02:00
admin 91f6a26490 v0.55.0: raw-device discovery + registry-sourced drive tracking (Impl-2a)
GET /disks/candidates enumerates host disks the Impl-1 unclaimed filter proves
free (init/attach split). RegistryKnownTargets sources the watchdog's known-drive
set from the intent registry + Felhom .mount units (not Observe/PVE storages) —
decouples drive health from PVE storage (closes the registry-only false-detach
class); Observe kept for real PVE storages + a deduped /disks union. Idempotent
existing-drive migration at start. Tests + red-proof (Observe misses a
registry-only drive; registry provider tracks it). go build/vet/test clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 17:33:49 +02:00
admin 066e3bf153 docs: REPORT for agent v0.54.0 (Impl-1 format-safety; live-validated)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 17:01:08 +02:00
admin 52098302ab v0.54.0: format-safety foundation — unclaimed-disk guard + guarded-mkfs wrapper
Impl-1. Format now runs a mandatory unclaimed-disk guard (internal/storage/claim.go:
SystemDisks + lsblk member-FSTYPE + foreign-mount + RO + pvs/zpool; fail-safe →
CLAIMED) before any mkfs — refuses the OS disk / LVM PV / ZFS-mdraid member /
foreign-mounted device even when non-data-bearing (guard sits in Format, not the
handler). Below the agent, mkfs goes ONLY through configs/felhom-mkfs-guarded.sh
(sudoers no longer allowlists raw mkfs.*), which re-checks the catastrophic cases
as root. Read-only pvs/zpool added to FELHOM_DISK. Tests + red-proof; capability
manifest updated. go build/vet/test clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 16:52:31 +02:00
admin 05f22a9ab4 docs: REPORT for agent v0.53.0 (pool-scoped restore; supervised swap validated)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 13:01:29 +02:00
admin 7639ab5c4b v0.53.0: restore guests INTO the felhom pool (pool-scoped-ACL enabler)
RestoreLXCOptions.Pool → pct restore --pool (omit-when-empty). New
reconcile.DefaultPool="felhom"; BringUpSpec.Pool threaded to the bring-up
restore; BOTH restore sites pool the guest (provision/DR via spec.Pool set to
DefaultPool by the CLI; restore-test scratch via DefaultPool = SPIKE residual
#2). No agent ACL change (ships in host-install v1.6.0); the pool param is inert
until the token has Pool.Allocate + the pool exists, so publishing is safe ahead
of the coordinated swap. Tests + red-proofs; go build/vet/test clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 12:32:41 +02:00
admin 33e3443545 docs: REPORT for agent v0.52.0 (CPU/RAM cap; T-A scratch proof; manifest follow-up)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 08:19:14 +02:00
admin 0573ec95c1 v0.52.0: operator-opt-in CPU/RAM cap for the provisioned guest (-cores/-memory)
New -cores N / -memory M (MiB) flags for --selftest=bring-up|provision (0 = keep
golden default), flowed through bringUpSizing into reconcile.BringUpSpec so the
existing buildBringUpConfig emits cores/memory into the pre-start config PUT (the
cap lands before first boot). No engine change. New pure-function test
TestBuildBringUpConfig_ResourceCaps asserts the set + omit-when-zero cases
(red-proof run + reverted). go build/vet/test clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 08:03:57 +02:00
admin 1502ca644a docs: REPORT for agent v0.51.0 (vzdump retention, live-validated keep-last=3)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HxLA1mZurFq9kt8hneFeCs
2026-06-30 20:03:42 +02:00
admin 06e0bc9c25 agent v0.51.0: local vzdump retention default (--prune-backups keep-last=3)
The preventive counterpart to host_disk + storage_fill detectors: the periodic local
whole-guest vzdump now prunes its own old archives (keep-last=3, clamped >=1) so a box
can't refill its own root via its own backups. Local target only — PBS never pruned
(resolved via ListStorage; fail-safe skip on unknown). Seeded in host-install.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HxLA1mZurFq9kt8hneFeCs
2026-06-30 19:44:07 +02:00
admin 79eb0a8486 docs: REPORT for v0.50.0 (NAS Part A1, sim-validated A-D)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HxLA1mZurFq9kt8hneFeCs
2026-06-30 11:40:08 +02:00
admin 63aa63d0d6 agent v0.50.0: NAS network storage Part A1 (NFS/SMB automount foundation)
Host-side NFS/SMB automount of a bulk-media NAS share under /mnt/felhom-drives/<name>
(propagates into the guest via the existing shared bind), the +100000 uid recipe,
per-share liveness, and add/list/remove local-API endpoints. A NAS is a distinct class
that bypasses the drive enroll/eject/decommission/SMART/watchdog machinery.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HxLA1mZurFq9kt8hneFeCs
2026-06-30 11:12:27 +02:00
admin 7aeb7caefe docs: §D supervised reboot — F2-a + F2-b live-validated (both findings CLOSED)
F2-a red-proof PASS: parent came up own peer group (shared:94, not
shared:1), no doubling, guest sees both drives. F2-b PASS: reboot with the
exact snapshot-delete stuck state → agent auto-recovered (unlock → removed
real dangling vzdump snapshot → started CT). Zero spurious pages. Two
live-caught fixes noted (?source=active, AllowWarnings).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162BnMpUXscPsUB1cU8Tr6K
2026-06-30 10:04:58 +02:00
admin d077c8e4dd agent: stale-lock start allows the benign LXC nesting WARNINGS advisory
Live on 9999: the recovery's start succeeded (guest running) but WaitTask
false-failed on "WARNINGS: 1" (the systemd-nesting notice). Mirror the
restore-test's start step (AllowWarnings) so a booted guest isn't logged
as a start error.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162BnMpUXscPsUB1cU8Tr6K
2026-06-30 09:07:50 +02:00
admin 1c078b943d agent: stale-lock guard uses tasks?source=active (PVE 9.x rejects ?running=1)
Live test on felhom-pve (PVE 9.2.2) caught GET /nodes/{node}/tasks?running=1
returning HTTP 400 "property not defined in schema". The fail-safe correctly
left the lock, but the BackupRunning guard always errored. source=active is
the supported filter and returns the RUNNING tasks.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162BnMpUXscPsUB1cU8Tr6K
2026-06-30 09:06:01 +02:00