3ea2b481da
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
4.7 KiB
4.7 KiB
felhom.eu — task reports
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; the scripts history lives in scripts/CHANGELOG.md.
TASK — PBS DR tier SLICE 1: ep0 tenantsync surface + hub provisioning (hub v0.44.0) — 2026-07-10
LIVE on k3s (Synced/Healthy, image 0.44.0, startup logs WG peer-sync enabled + PBS DR tenantsync enabled). NOT live-provisioned — no real customer was provisioned from this task; slice 2 (the agent
apply-bridge) + the supervised Peti enable are pending. Builds on SPIKE-pbs-tier-provisioning
(00afadc). Commits: ce6a566 (code) + 32ca1f6 (live fix + image bump).
What shipped
- ep0 surface (🛑 Viktor-approved, installed + smoked live):
scripts/felhom-tenantsync.shv1.0.0 as a SECOND forced-command key forfelhom-peersync(own sudoers drop-in; peersync script + sudoers sha256 asserted byte-identical before/after). Ops:provision(existing token → typedtoken_exists; own-ns self-check with one regen retry then rollback),reissue(delete→recreate→ re-grant — delete purges token ACLs, the spike fact),fingerprint. Secret hygiene: the token secret rides stdout ONLY (all tool stdout → stderr; hub error paths never embed stdout). Smoke (throwawayspike-smoke, torn down): all ops green; live fix caught by the smoke —proxmox-backup-client --output-format jsonwraps as{"data":[…]}→ ns-exists check fixed to(.data // .)and reinstalled before the hub ever used the surface. Orphan cleanup (spike flag #1):root@pam!spiketoken +/datastore/scratchDatastoreAdmin ACL removed; ep0 now holds exactly the realdemo-felhom-01tenancy. - Hub
internal/tenantsync: wgsync-twin SSH client (pinned host key, constrained HostKeyAlgorithms, per-op JSON exec, typedErrTokenExists). Contract testTestErrors_NeverEmbedStdoutpins the no-stdout-in-errors rule. - Provisioning flow (
web/pbsdr.go): config-form section "PBS DR tier (ep0)" → on enable+save (detached-ctx atom): WG peer existence check (the agent self-registers its key; absence fail-closes with "has not reported a WG key yet") → tenantsync provision → secret stored consume-once HOST-scoped (host_pbs_secrets, theone_time_secretstwin) → non-secretpbs_drdescriptor (enabled, storage_id, pbs_tunnel_ip, datastore, namespace, token_id, fingerprint) merged into the HOSTdesired_json+SetHostDesiredgeneration bump. Already-provisioned re-save = success-no-op; disable =enabled:falsedescriptor, tenancy kept; Re-issue PBS credentials button = the F4 precedent (fresh consume-once secret + bump). - Consume endpoint:
POST /api/v1/hosts/{id}/pbs/consume-token— per-host key self-scoped (global = operator recovery); 200 exactly once → 404; a foreign key's 403 does NOT burn the secret. (Deviation: task spec wrote/host/{id}/…; implemented under/hosts/matching every other agent-facing route — slice 2 must use the/hosts/form.) - Deploy:
TENANTSYNC_SSH_KEY_FILEenv + optionalSecret/tenantsyncmount (key generated on 180, moved into the Secret, local copy shredded); same endpoint addr + pinned host key as peersync.
Tests + red-proofs (all run, mutations reverted, gate green)
- Happy path: descriptor lands in desired_json + gen bump exactly 1; secret consumable once and absent from ConfigJSON/desired-state/captured logs (grep-asserts).
- Red-proof consume-once: dropping the
consumed_atUPDATE → store test re-served"secret-1", API test returned 200 with the secret again → both FAIL as designed. - Red-proof fail-closed: swallowing the applyPBSDR error in handleConfigUpdate → all four fail-closed subtests FAIL with 303 (half-enabled save).
- Red-proof idempotency: dropping the already-provisioned short-circuit → re-save re-keyed the token (2 provision calls), staged a fresh secret, bumped spuriously → FAIL.
- Also covered: foreign-host 403 non-burning, reissue rotation (old secret dead), disable no-op on second save, storage-id edit updates descriptor without re-provision, tenantsync client round-trip against an in-process SSH server incl. wrong-host-key refusal.
- UI leg: render tests (
TestPBSDR_FormRendersState+ suite) — the hub UI is password-gated, so render tests are the verification method per repo convention.
Operator follow-ups
- Slice 2 (agent apply-bridge) then the supervised Peti enable (floor + agent update first).
- The demo host's missing §4b step-3 storage ACL grants (spike flag #2) — re-assert before slice 2 codifies the self-grant.