From 95853e3dffebf422a0124b65a497fb252ac096bb Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Thu, 16 Jul 2026 08:10:33 +0200 Subject: [PATCH] docs --- .../architecture/00-capability-map.md | 116 ++++++++++++++++++ documentation/backlog/ROADMAP.md | 57 +++++++++ 2 files changed, 173 insertions(+) create mode 100644 documentation/architecture/00-capability-map.md create mode 100644 documentation/backlog/ROADMAP.md diff --git a/documentation/architecture/00-capability-map.md b/documentation/architecture/00-capability-map.md new file mode 100644 index 0000000..05195c8 --- /dev/null +++ b/documentation/architecture/00-capability-map.md @@ -0,0 +1,116 @@ +# 00 — Felhom Capability Map + +> **What this is:** the single cross-component truth table of what the Felhom platform can do +> **today**, at what confidence level, with verifiable evidence. Rows are *scenarios* (user- or +> operator-visible outcomes), not modules — a scenario spans agent + controller + hub + catalog, +> and this is the only doc that shows that view. +> +> **What this is NOT:** a roadmap. Planned work lives in `documentation/backlog/ROADMAP.md` and is +> referenced from gap rows by ID (`→ R-n`). A row here never claims future behavior. +> +> **Status enum (strict):** +> | Status | Meaning | +> |--------|---------| +> | **PROVEN-LIVE** | Exercised end-to-end on real infrastructure; MUST cite a campaign/drill/validation doc in `documentation/audits/` or `documentation/tests/`. No citation → not PROVEN-LIVE. | +> | **IMPLEMENTED** | Shipped + unit/red-proof tested, but the real flow has not been exercised live (or not on the surface that matters — noted per row). | +> | **PARTIAL** | Some legs live, some missing/unvalidated — the note says which. | +> | **MISSING** | Does not exist. Present-tense fact; if planned, the row points at a roadmap ID. | +> +> **Update rule (end-of-session checklist item):** if a task changed any capability's status, +> update the row in the same session — with the new evidence citation. A PROVEN-LIVE claim is +> subject to the cardinal rule like any other claim. +> +> *Drafted 2026-07-16 by project-Claude from live CHANGELOGs (controller v0.137.0, agent v0.88.0, +> hub v0.56.0) + audit index. Verify statuses once against source before first commit.* + +--- + +## A. Provisioning & day-0 + +| Scenario | Components | Status | Evidence | Gap / roadmap | +|---|---|---|---|---| +| Appliance day-0 install: golden image → first boot → auto-confirm (zero clicks) → claimable box | installer, agent, hub, golden | **PROVEN-LIVE** (nested VM) | `DRILL-day0-vm-2026-07-12`, `DRILL-day0-take2-2026-07-12` | First firing on real customer hardware pending → R-1 | +| BYO install: `--mode byo`, mandatory caps, host-mutation disclosure, coexistence guards | installer v1.15+, agent | **PARTIAL** | `DRILL-GL6-2026-07-08` (demo box); GL-8 coexistence fixes | Peti clean-slate reinstall on proxmox2 is the first real BYO run of the current path → R-1 | +| Customer claim: one-time emailed code → customer sets own password (bcrypt, operator never sees it) | controller v0.122, hub v0.50 | **PROVEN-LIVE** (drill VM) | `DRILL-day0-vm-2026-07-12` (F-4 arc), `CAMPAIGN-4-2026-07-13` (F-C) | Never executed by a non-Viktor human → R-3 | +| Escrow ceremony: customer-facing wizard, one-shot R claim, operator zero-knowledge | controller v0.127, agent v0.88 | **PROVEN-LIVE** (demo, endpoint-exact) | agent v0.88.0 REPORT (ceremony ~4s, one-shot claim + 410); `SPIKE-controller-escrow-2026-07-13` | First supervised ceremony with a real customer pending → R-1 | +| DR tier by default: PBS + WireGuard base infra on every install, hub-controlled activation | installer v1.15, agent v0.86, hub v0.51 | **IMPLEMENTED** | `SPIKE-pbs-tier-provisioning-2026-07-10` | Live only on demo/drill fleet | +| Uninstall: KEPT-vs-WIPED statement, secret purge, enrolled-drive handling | installer | **PARTIAL** | GL-4 (uninstaller gaps closed) | Cluster-aware `felhom_guests` guard + saferemove cost warning missing → R-9 | + +## B. Apps & catalog + +| Scenario | Components | Status | Evidence | Gap / roadmap | +|---|---|---|---|---| +| Deploy an app from the catalog (env config, memory guard, health-aware progress) | controller, catalog (~52 apps, images pinned) | **PROVEN-LIVE** | `CAMPAIGN-2` T-DEPLOY-SET (7 apps), T-RES-MEMGUARD | | +| App lifecycle: start/stop/restart/update/logs/remove/redeploy | controller | **PROVEN-LIVE** | `CAMPAIGN-2` T-LIFECYCLE, T-REMOVE-REDEPLOY | | +| Protected infra stacks can't be stopped/removed from UI | controller | **PROVEN-LIVE** | `CAMPAIGN-2` T-SEC-PROTECTED | | +| Catalog sync (git, 15 min) + orphan lifecycle + validation choke point (bad `backup:` block degrades to legacy, loudly) | controller v0.132, catalog | **PROVEN-LIVE** | `CAMPAIGN-2` T-SYNC-IDEMPOTENT; v0.132 LoadMetadata red-proofs | | +| App crashes → customer notified (one event per transition, no flapping spam) | controller v0.120, hub v0.48 | **IMPLEMENTED** | CAMPAIGN-3 fix-3 arc | Delivery proven; long-run anti-spam behavior only unit-proven | +| Post-deploy optional config (API keys etc.) with restart | controller, catalog `.felhom.yml` | **PROVEN-LIVE** | long-standing; page sweep in `CAMPAIGN-2` T-PAGE-ALL | | +| Backup classification: 13 bind-bearing apps carry mandatory/optional/excluded classes | catalog, controller v0.132–133 | **PROVEN-LIVE** | `SPIKE-backup-classification-2026-07-14`, `CAMPAIGN-6D/6E` | Remaining ~39 apps are legacy-class by design (unit-only offsite) | + +## C. Protection & recovery (the product promise) + +| Scenario | Components | Status | Evidence | Gap / roadmap | +|---|---|---|---|---| +| Nightly DB dumps (postgres/mariadb autodiscovery), atomic writes | controller v0.118 | **PROVEN-LIVE** | `CAMPAIGN-2` T-BAK-FULL, T-RST-DB; CAMPAIGN-3 F7 fix | T-6E-1 dir-fsync asymmetry (LOW) → R-10 | +| Tier-2 secondary-drive copy: class-driven legs, v2 relpath layout, NAS-target exclusion, safe-remove boundary | controller v0.135 | **PROVEN-LIVE** | `CAMPAIGN-6E-2026-07-15` (P-TIER2 deep-4 PASS), `CAMPAIGN-6C` | | +| Offsite (restic → Hetzner Storage Box): mandatory class only, raw-data quota, enlargement gate, retention regrouping | controller v0.134, agent, hub | **PROVEN-LIVE** | `CAMPAIGN-6D-2026-07-15`, `SPIKE-restic-snapshot-shape-2026-07-14`, `VALIDATION-offbox-storagebox-2026-07-09` | | +| Offsite restore: local-preferred scratch, unit-only default, full two-step, missing-only place-to-live | controller v0.134/134.1/135 | **PROVEN-LIVE** | `CAMPAIGN-6D` accept legs (immich end-to-end from offsite alone) | | +| Manual `.fab` export/import: class-scoped capture, browser up/download, tunnel-proof chunking | controller v0.125/128/130/136 | **PROVEN-LIVE** | `CAMPAIGN-6D` Accept #1 (≥1 GiB full circle); `CAMPAIGN-6B` (C6B-F1 hollow-export fix) | | +| Guest-loss DR: PBS restore with full-fidelity layout from archive, restore-test verification | agent v0.75/0.76, PBS | **PROVEN-LIVE** | `VALIDATION-newbox-restore-2026-07-10`, `DRILL-GL6-2026-07-08` | | +| PBS-DR secret self-heal on reused-peer re-provision | hub v0.56 | **IMPLEMENTED** | `SPIKE-pbsdr-selfheal-2026-07-15` (root cause proven) | Reconciler fleet-wide since 07-15; first real-world firing pending | +| Crash/power-loss mid-backup/mid-migration → self-heal on next run | controller, agent | **PROVEN-LIVE** | `CAMPAIGN-2` T-FLT-KILL9, T-RBT-MIDBACKUP, T-RBT-MIDMIGRATION | | +| Soft-quota: usage bar, pre-push enlargement block, customer notification | controller v0.109/134, hub v0.41/55 | **PROVEN-LIVE** | 6D/6E; hub OffsiteChecker | | +| **A customer (not the operator) performs a restore via UI alone** | all | **MISSING** (as evidence) | — | Alpha will produce this; script it into R-3 | + +## D. Storage & devices + +| Scenario | Components | Status | Evidence | Gap / roadmap | +|---|---|---|---|---| +| Drive wizard: scan/format/mount/enroll, incl. legacy-boot LVM-root hosts | controller, agent v0.87 | **PROVEN-LIVE** | `CAMPAIGN-2` T-STG-ENROLL/SEC-FORMAT; `DISPOSITION-ia-finding2-systemdisks-2026-07-13` | | +| Data migration between drives (all / per-app), crash-safe | controller | **PROVEN-LIVE** | `CAMPAIGN-2` T-STG-MIGRATE-*, `CAMPAIGN-6C` (scope=app round-trip) | | +| NAS (NFS/SMB-client) verify-before-commit, uid-1000 probe, categorized Hungarian errors, DSM-validated | controller v0.113–117, agent v0.81/84/85 | **PROVEN-LIVE** | `SPIKE-nas-verify-2026-07-11`, `SPIKE-nas-dsm-2026-07-11`, `CAMPAIGN-3-2026-07-11` (boot/reassert fixes) | | +| USB drive enrollment + unplug detection + recommission | controller, agent | **PROVEN-LIVE** | `RUNBOOK-usb-enrollment-3bfix-2026-07-01`, `CAMPAIGN-2` T-STG-UNPLUG/RECOMMISSION | | +| Decommission (migrate-first and anyway-paths), eject | agent, controller | **PROVEN-LIVE** | `CAMPAIGN-2` T-STG-DECOM-*; `SPIKE-decommission-migration-2026-06-14` | | +| Boot ordering: automount + networking survive reboot; appliance self-heal watchdog | agent v0.85 | **PROVEN-LIVE** | `CAMPAIGN-3-2026-07-11` (F10/F11/F12) | | + +## E. Access, networking & household use + +| Scenario | Components | Status | Evidence | Gap / roadmap | +|---|---|---|---|---| +| Remote access via Cloudflare Tunnel + Traefik (per-app subdomains) | cloudflared, traefik | **PROVEN-LIVE** | `CAMPAIGN-2` T-FLT-CF | Per-customer zone-scoped CF tokens (blast-radius ruling) | +| LAN access when internet is down (lan_resolver) | agent | **IMPLEMENTED** | — | Never drilled as a customer experience ("net down — can I reach my photos?") → R-19 | +| Phone photo backup | immich (classified) | **PROVEN-LIVE** | 6D end-to-end restore proof | | +| Documents/OCR | paperless-ngx (classified) | **PROVEN-LIVE** | first deployed app; 6C/6E legs | Consume-folder ingestion awkward without SMB → R-7 | +| Files from Windows Explorer / Mac Finder (SMB **server**) | — | **MISSING** | — | → R-6 (spike), R-7 (feature) | +| Media to TV via DLNA | — | **MISSING** | — | Jellyfin app exists; DLNA/SSDP unvalidated → R-6, R-8 | +| File access via browser | FileBrowser (infra app, auto-mount sync) | **PROVEN-LIVE** | long-standing; `CAMPAIGN-2` T-PAGE-ALL | | +| Forgot dashboard password → instant reset code | controller v0.123, hub | **PROVEN-LIVE** | F-15 arc, drill validation | | +| Multiple household users / per-person accounts | — | **MISSING** | — | Single dashboard password; acceptable for alpha → R-15 | +| WireGuard base infra always-on; OOB operator access (felhom-sshd, /32 peer) | agent v0.72, hub v0.35 | **IMPLEMENTED** | `SPIKE-oob-wg-operator-peer-2026-07-05`, `SPIKE-felhom-sshd-2026-07-05` | Mutual-repair desired-state arc not built → R-13 | +| Break-glass management-plane recovery | agent v0.71, hub v0.34 | **IMPLEMENTED** | `runbooks/break-glass.md` | | + +## F. Notifications & monitoring + +| Scenario | Components | Status | Evidence | Gap / roadmap | +|---|---|---|---|---| +| Health-degradation email (edge-triggered, cooldowns, Hungarian) via hub → Resend | controller, hub | **PROVEN-LIVE** | 6D P3-DELIVERY | Deliverability to HU freemail providers untested → R-4 | +| Event catalog: app_start_failed, dead-app, offbox_enlarge_blocked, claim/reset codes, critical severity | controller, hub v0.31/48/50/55 | **PROVEN-LIVE** | per-arc validations | | +| Prefs safety: empty-email wipe guard | controller v0.137 | **IMPLEMENTED** | red-proofed 07-15 | Born from a live incident; guard itself unit-proven | +| System + container metrics (SQLite, Chart.js, 30-day downsampling) | controller | **PROVEN-LIVE** | `CAMPAIGN-2` T-RES-CPU/T-SOAK-LOOP | | +| Always-on debug rings + on-demand log-bundle pulls with TTL/custody | controller v0.116, agent v0.83, hub v0.46 | **PROVEN-LIVE** | observability arc 07-11 | | +| Operator alerting (Healthchecks → monitoring@felhom.eu) | k3s, Resend | **PROVEN-LIVE** | in production since 02-04 | | + +## G. Fleet & operator (hub) + +| Scenario | Components | Status | Evidence | Gap / roadmap | +|---|---|---|---|---| +| Customer/host management: 8-tab detail, scoped auto-refresh, safe stale-host deletion, capability chips | hub v0.47–0.53 | **PROVEN-LIVE** | in daily operator use | | +| Publish train: MinAgent floors, gated auto-Reissue, version channels, floor-field-LAST rules | hub v0.45/0.53, agent | **PARTIAL** | `runbooks/publish-train-rules.md`; demo-fleet updates proven | **Never proven on a real remote customer** — parked trains `RUNBOOK-publish-0.79/0.81/0.85-*` await Peti → R-1 | +| Agent self-update: A/B slots, crash-loop auto-rollback, operator-signed | agent v0.70+ | **PROVEN-LIVE** (demo) | `SPIKE-agent-selfupdate-2026-07-05` | Remote-customer proof pending → R-1 | +| Controller self-update: anonymous registry, no credentials in guest | controller v0.112 | **PROVEN-LIVE** (demo) | 07-10 arc | | +| Offsite provisioning: Hetzner API, sub-account per customer, host-key pinning, credential re-issue | hub v0.37–0.39 | **PROVEN-LIVE** | `VALIDATION-offsite-provisioning-e2e-2026-07-09`, `SPIKE-hetzner-api-provisioning-2026-07-09` | | +| Per-customer offsite fill + staleness + freeze lever | hub v0.41 | **PROVEN-LIVE** | OffsiteChecker in production | | +| **Box-level Storage Box aggregate (total fill, Σ quotas, oversubscription alert)** | — | **MISSING** | — | → R-5 | +| Secrets hygiene: bearer in k8s Secret, no secrets in git, single-quote credential store | hub v0.53, conventions | **IMPLEMENTED** | 07-13 closing bundle | | +| Operator login password changeable from UI | hub v0.54 | **IMPLEMENTED** | 07-13 | | \ No newline at end of file diff --git a/documentation/backlog/ROADMAP.md b/documentation/backlog/ROADMAP.md new file mode 100644 index 0000000..1df91a0 --- /dev/null +++ b/documentation/backlog/ROADMAP.md @@ -0,0 +1,57 @@ +# ROADMAP — future features & open work + +> **What this is:** the prioritized decision log of planned/open work. Items are *intentions*, not +> claims about live behavior — the capability map (`architecture/00-capability-map.md`) is the only +> place that states what the platform does today. +> +> **Lifecycle:** idea → spiked → spec'd → in-progress → **shipped** (item collapses to a one-liner +> with the version, and the corresponding capability-map row changes status with evidence). Items +> can also be **killed** (keep the one-liner + why — decisions are worth remembering). +> +> **Coupling rule:** every item names the capability-map row(s) it flips. Every map gap row points +> back here by ID. Neither file duplicates the other's content. +> +> **Priorities:** P1 = closed-alpha blocker · P2 = close during alpha · P3 = post-alpha. +> Existing loose notes in this folder (`FOLLOWUP-*`, `FIX-M*`) are absorbed as references below. + +--- + +## P1 — closed-alpha blockers + +| ID | Item | Size | Status | Notes / map rows flipped | +|----|------|------|--------|--------------------------| +| R-1 | **Peti convergence:** clean-slate proxmox2 reinstall (spec'd 07-15), first live auto-confirm, supervised escrow ceremony, execute parked publish trains (agent 0.81→0.88, controller → 0.137) | L | spec'd | Flips: publish train PARTIAL→PROVEN-LIVE; appliance/BYO/day-0 "real customer" notes; escrow ceremony. The single biggest unproven surface — an alpha where fixes can't ship remotely is dead | +| R-2 | Resolve ~215 lines of foreign WIP in felhom.eu clone (`hub/internal/notify/`, `store.go`, `hub/internal/claim/`) | S | open | Unknown uncommitted code in the repo that builds the hub; commit, stash-archive, or discard — but decide | +| R-3 | Friend-alpha onboarding runbook (generalized from `pilot/RUNBOOK-peti-return-2026-07-13`): hardware prep → golden → install → claim → ceremony → "first restore by the customer" scripted step | M | idea | Flips: "customer performs a restore" MISSING row; produces the tester-agreement sibling of `PETI-tester-agreement.md` | +| R-4 | Claim-code deliverability: test-send to gmail.com / freemail.hu / citromail.hu / t-online.hu; tighten DMARC `p=none` → `p=quarantine` (pending since email.md 02-04) | S | idea | A claim code in spam bricks onboarding at step 1. Cheap, do before first invite | + +## P2 — during alpha + +| ID | Item | Size | Status | Notes | +|----|------|------|--------|-------| +| R-5 | Hub: Storage Box box-level aggregate — total BX11 fill, Σ(customer soft quotas), oversubscription ratio, operator alert threshold | M | idea | Per-customer OffsiteChecker exists (hub v0.41). Source: Hetzner Storage Box API (verify Robot-legacy vs. new API endpoint for our box before speccing — 2025 migration); NOT ssh `du` over restic repos. Token → k8s Secret ref. Flips map row G/MISSING | +| R-6 | **Spike: LAN service discovery from the guest** — SSDP multicast (UDP 1900, DLNA), WSD (Windows discovery), mDNS; host-network vs macvlan; is the customer LXC LAN-bridged in appliance deployments? | M | idea | Shared prerequisite for R-7 + R-8. Spike-first: the traps are networking, not Samba/DLNA | +| R-7 | SMB **server** share (gated on R-6): samba + wsdd, LAN-only binding (never tunnel), user model (single household user first), which roots are shared (dedicated `shares/` vs app userdata — every SMB-writable path needs a backup class), uid-1000 convention, paperless consume flow | L | idea | Flips map row E/MISSING. Alpha-relevant: "my box is a NAS" is a core household expectation | +| R-8 | DLNA (gated on R-6): validate Jellyfin's built-in DLNA server first; only add minidlna to the catalog if Jellyfin-DLNA fails | S | idea | Don't add catalog weight before proving the cheap path | +| R-9 | Uninstaller trio (from 07-15 Peti session): cluster-aware `felhom_guests` guard (node-local `pct list` deletes cluster-wide pveum objects); saferemove detection + time estimate + opt-in `--quick-remove` (never mutate `storage.cfg`); smarter `restore_storage` default for BYO clusters (shared storage, not local-lvm) | M | idea | Second item's rejected alternative (temp-disable-and-restore) stays rejected — crash window silently downgrades cluster wipe policy | +| R-10 | T-6E-1: DB-dump dir-fsync asymmetry (LOW, confirmed in 6E) | XS | idea | One-line hardening; batch with the next controller task | +| R-11 | Tester-facing one-pager: what the box does, known limitations, how to report (channel decision: Messenger group?) | S | idea | Pairs with R-3 | +| R-16 | Operator hygiene: campaign6 autofs orphan (clears on host reboot) + tied-CreatedAt flash duplicates (audiobookshelf/komga/romm) | XS | open | Viktor's own action items from 6D/6E | +| R-17 | Old-box archive (u629193-sub1) retirement decision — 9/9 byte-identical restores verified | XS | awaiting-decision | Viktor ruling | +| R-19 | Internet-outage customer-experience drill: pull WAN on demo, verify lan_resolver path, document what the customer actually sees/does | S | idea | Flips map row E "LAN access" IMPLEMENTED→PROVEN-LIVE | +| R-20 | Verify operator-key pinning is fully in the day-0 install flow (GL-4 folded plumbing in; confirm no manual step remains) | XS | verify | If a manual step remains, promote to a task | + +## P3 — post-alpha + +| ID | Item | Size | Status | Notes | +|----|------|------|--------|-------| +| R-12 | Cluster mode: agent-follows-guest, bind-mount reconciliation on HA migration | XL | idea | Scoped 07-15; interim = HA-group pin to one node. Driven by Peti's two-node cluster | +| R-13 | OOB management arc: dual-use existing WireGuard + hub desired-state channel as mutual-repair | L | idea | | +| R-14 | Headscale/WireGuard spike: Minecraft/gaming port connectivity (CGNAT-proof, sovereign DERP fallback) | M | idea | | +| R-15 | Multi-user dashboard accounts (household members, roles) | L | idea | Single password is a stated alpha limitation (R-11) | + +## Absorbed / superseded notes in this folder + +- `FOLLOWUP-nas-automount-guest-reboot-reassert.md` — **shipped** (agent v0.84/v0.85, CAMPAIGN-3); keep for history +- `FOLLOWUP-golden-default-controller-tag.md` — verify against current golden flow; close or promote to an item +- `FIX-M18-NOTES.md`, `FIX-M19-NOTES.md`, `DIAGNOSIS-f9-storage-registration-gap-2026-06-14.md` — historical diagnoses; superseded by shipped fixes \ No newline at end of file