Files
home-network-k3s/docs/00-vision-and-scope.md
jakman85andClaude Opus 5.5 250fb54154 docs: bring the specification in line with the accepted implementation
The operator ruled the implementation repo's main (50c8719) the latest
and accepted state. Sync the spec to it.

Accepted changes are reflected: one bare-metal environment, TrueNAS bulk
storage, R2 off-cluster backup, CloudNativePG, single-node VPN gateway
with Cilium fail-closed, inbound WireGuard, per-app auth sidecars,
Renovate, the CI gate as approver, and the pm-agent. New ADR-0017..0025;
ADR-0016 corrected and accepted; R-* IDs unchanged, eight amended.

Where the implementation has simply not met a requirement, the
requirement stands and the shortfall is recorded in docs/16-known-gaps.md.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-08 22:46:00 -06:00

15 KiB
Raw Permalink Blame History

00 — Vision & Scope

Vision

Build a home platform that behaves like a small private cloud. An operator declares a service — a container app or a virtual machine — in git, and the platform provisions it, secures it, exposes it under a trusted TLS URL, routes its traffic on the correct WAN, protects its data, updates it automatically, and reports on its health. Onboarding a new service is a pull request against a catalog, not a weekend of manual work.

The platform runs as one live environment: prod, on 3 bare-metal mini-PCs (ADR-0017). The dev/test cluster that proved its viability was decommissioned in 2026-09; every change reaches prod through the PR gate and Flux.

Personas

  • Operator (primary user): runs the home network; wants to onboard/offboard services with minimal ceremony and zero data loss.
  • Platform (the system): reconciles declared state, enforces isolation and egress policy, renews certificates, updates workloads, and raises alerts.
  • Implementation agents: the AI teams building the platform from this spec.

In scope

  • A k3s-based platform with distributed storage (Longhorn) across 3 nodes.
  • Container workloads (PaaS) and VM workloads (IaaS) through one Kubernetes surface.
  • GitOps delivery, automated updates, trusted TLS, DNS, ingress.
  • Network isolation (default-deny east-west) and declared egress classes, including VPN routing.
  • Observability with email + dashboard alerting.
  • A command-and-control web UI at the root of home.keithjackson.dev.
  • The service catalog (see 13-service-catalog.md).

Out of scope (for now)

  • Multi-tenant / multi-operator RBAC beyond a single operator + platform automation.
  • Public multi-user SaaS concerns (billing, per-tenant isolation beyond network policy).
  • Non-keithjackson.dev domains.

The domain & DNS model

  • Public apex *.keithjackson.dev is available in Cloudflare DNS. public services (catalog exposure: public or a registered platform route) under *.home.keithjackson.dev are published through a Cloudflare Tunnel (proxied records, no inbound 80/443 at the edge — ADR-0016); the network's public IP stays in use for WireGuard and Plex only.
  • All platform services live under *.home.keithjackson.dev.
  • Each service is reachable at <service>.home.keithjackson.dev (e.g. plex.home.keithjackson.dev).
  • The root home.keithjackson.dev serves the Command & Control (C2) center UI.

Requirements (stable IDs)

Requirement IDs are stable references used across ADRs, specs, the task board, and PRs. MUST is mandatory; SHOULD is strongly preferred and requires an ADR to deviate.

Platform & API surface — R-PLAT-*

  • R-PLAT-01 — The platform MUST run on k3s across 3 nodes with production-grade HA control plane in prod.
  • R-PLAT-02 — Container workloads MUST be onboarded and managed declaratively via the Kubernetes API, kubectl-class CLI, and YAML.
  • R-PLAT-03 — Virtual-machine workloads MUST be created, managed, and destroyed through the same Kubernetes API/CLI/YAML surface as containers (no separate hypervisor management plane for application VMs).
  • R-PLAT-04 — The platform MUST support create / destroy / rebuild of any workload on demand.

GitOps & delivery — R-GITOPS-*

  • R-GITOPS-01 — All application and platform desired state MUST be defined in git and reconciled automatically (pull-based GitOps).
  • R-GITOPS-02 — Onboarding a new service MUST be expressible as a change to a versioned catalog in git.
  • R-GITOPS-03 — The system MUST detect and correct drift between git and cluster state.

Networking, isolation & egress — R-NET-*

  • R-NET-01 — East-west traffic between services MUST be denied by default; communication is allowed only by explicit, port-level configuration (firewall-like).
  • R-NET-02 — Allow rules MUST be declarative, reviewable in git, and scoped per source → destination → port/protocol.
  • R-NET-03 — Networks MUST be configurable and isolatable (segments/namespaces with enforced boundaries).
  • R-NET-04 — The platform MUST support multiple egress paths selected by routing rules. Per OQ-1 the environment has one physical commercial WAN and no multi-ISP plans, so this is realized as two paths — the local internet path and the in-cluster vpn path — not multi-ISP routing. Physical multi-WAN (multiple ISP uplinks) is out of scope unless a future ADR reopens it.
  • R-NET-05 — A service MUST be able to declare its egress class: internet (standard local network/internet) or vpn (routed through a custom VPN connection). VPN routing MUST be enforced by a single shared node-level gateway outside the service (ADR-0015, ADR-0020), so a service cannot leak by misconfiguration (OQ-3).
  • R-NET-06 — VPN-class egress MUST enforce a kill-switch: if the VPN tunnel is down, the service MUST NOT leak traffic to the standard internet.
  • R-NET-07 — Egress class MUST be a declarative property of the service, changeable via GitOps without re-architecting the workload.

Storage & state — R-STATE-*

  • R-STATE-01 — Stateful data MUST survive destroy/rebuild of the workload (PaaS and IaaS).
  • R-STATE-02 — Persistent storage MUST be distributed/replicated across the 3 nodes (Longhorn) so a single node loss does not lose data.
  • R-STATE-03 — The platform MUST provide scheduled backups to an off-cluster target and a tested restore path (disaster recovery).
  • R-STATE-04 — Bulk media storage (Plex library) MUST have a capacity strategy distinct from replicated app-config storage (replicating multi-TB media 3× is out of scope; see ADR-0003).

Automated updates — R-UPD-*

  • R-UPD-01 — Services MUST update to the latest stable versions on a schedule.
  • R-UPD-02 — Updates in the allowed classes MUST occur with no human, no AI, and no hand-authored GitOps change; automation acting on declarative policy opens machine-authored changes through the merge gate. Major versions and operator-designated blast-radius components MAY wait for human review of the machine-authored change.
  • R-UPD-03 — Update policy MUST be per-service configurable (cadence, stability channel, pin/hold, auto-rollback on failed health).

TLS & certificates — R-TLS-*

  • R-TLS-01 — Every externally reachable service MUST present TLS from a credible, trusted, non-self-signed authority.
  • R-TLS-02 — Certificates MUST be automatically issued, renewed, and revoked without manual steps.
  • R-TLS-03 — Certificate issuance MUST work for internal-only services (no requirement to expose a service publicly just to validate a certificate).

DNS & ingress — R-DNS-*

  • R-DNS-01 — Services MUST be reachable at <service>.home.keithjackson.dev.
  • R-DNS-02 — DNS records MUST be managed automatically from declared service intent.
  • R-DNS-03 — Internal clients MUST resolve service names correctly (split-horizon where public and internal answers differ).
  • R-DNS-04 — public HTTP(S) services MUST be reachable from the internet without inbound 80/443 port-forwarding at the edge, and LAN clients MUST reach them correctly whichever resolver answers (public or internal) — no dependency on NAT hairpin (ADR-0016).
  • R-DNS-05 — Only services declared public (catalog exposure: public or a registered platform route) MAY be reachable through the public ingress path. A request for a LAN/VPN-only hostname at that path MUST be refused (HTTP 404). internal services MUST have no public record, except non-HTTP L4 names, which MAY hold DNS-only records at LAN addresses (ADR-0016).

Observability & alerting — R-OBS-*

  • R-OBS-01 — Services MUST be monitored (metrics, logs, health).
  • R-OBS-02 — Issues MUST be reported by email.
  • R-OBS-03 — Issues and health MUST be visible on dashboards.
  • R-OBS-04 — The platform MUST alert on its own health (nodes, storage, GitOps reconciliation, certificate expiry, VPN tunnel state, backup success).

Command & control — R-C2-*

  • R-C2-01 — The root home.keithjackson.dev MUST serve a UI that surfaces the services running in the cluster and links to them.
  • R-C2-02 — The C2 UI MUST surface platform health (cluster, storage, GitOps, alerts) and provide controlled operational actions where safe.

Security & secrets — R-SEC-*

  • R-SEC-01 — Secrets MUST NOT be stored in plaintext in git; they MUST be encrypted at rest in the repo and decrypted only in-cluster.
  • R-SEC-02 — The platform MUST follow least-privilege for workload identities and network reach.
  • R-SEC-03 — All the above properties MUST be demonstrable/auditable (an operator can prove isolation, egress path, cert validity, and backup integrity).
  • R-SEC-04 — The Kubernetes API MUST NOT be reachable from the public internet by any path (no tunnel route, no edge forward, no public DNS record); it is LAN/VPN-only (ADR-0016).

Identity & access (SSO) — R-IDP-*

  • R-IDP-01 — The platform MUST provide a central Identity Provider (SSO) as the single user directory and login for platform services.
  • R-IDP-02 — Services MUST authenticate against the IdP — natively via OIDC where supported, or behind an identity-aware (forward-auth) proxy otherwise. No management UI may be exposed unauthenticated on the network. (Documented exceptions: Plex/Seerr use Plex auth; Minecraft uses Mojang/Microsoft; UniFi uses its native login on its own L4 VIP; the read-only runbooks at /runbooks carry no login; Crafty web uses forward-auth plus its own local login, no OIDC.)
  • R-IDP-03 — Identity configuration (realms, clients, groups, roles) MUST be declarative and GitOps-managed, not console-only.
  • R-IDP-04 — IdP secrets (admin credential, OIDC client secrets) MUST be SOPS-encrypted; MFA MUST be available to all users and required for privileged access.
  • R-IDP-05 — The IdP MUST be treated as DR-critical (replicated + off-cluster-backed datastore) with a documented break-glass path so an IdP outage does not lock the operator out of recovery tools.
  • R-IDP-06 — Access MUST be least-privilege via groups/roles; sensitive surfaces (C2, cluster console, alerting) require an authenticated user in an authorized group, not merely "logged in."

Environments — R-ENV-*

  • R-ENV-01 — Nodes MUST be baselined, cluster-joined and managed as code; machine creation on bare metal is an imaged, checklist-driven step.
  • R-ENV-02 — GitOps configuration MUST be authored once as a base plus environment-scoped overlay; any future environment MUST need overrides only.
  • R-ENV-03 — Production runs on 3× mini-PC bare metal. Changes MUST reach it only through the PR gate and Flux.

Conformance testing & governance — R-TEST-*

  • R-TEST-01 — Every requirement MUST be verified by automated, behavior-based tests (Given/When/Then style) that assert observable system behavior, not implementation internals.
  • R-TEST-02 — Tests MUST be reproducible: deterministic, hermetic (create and tear down their own fixtures), version-pinned, and runnable by a single command locally and in CI against any conforming cluster, producing machine-readable, requirement-tagged results.
  • R-TEST-03 — A conformance suite MUST gate delivery: merges are blocked unless the tests for the touched requirements pass, and phase exits are blocked unless the phase's full suite passes. "Demonstrated" means "the automated test passes," never a manual one-off.
  • R-TEST-04 — Tests MUST be traceable: each test declares the requirement ID(s) it verifies, and a coverage report maps every R-* requirement to its verifying test(s). Uncovered requirements are a gap to close, not an acceptable state.
  • R-TEST-05 — Safety-critical behaviors (default-deny, VPN kill-switch, data-survival on rebuild, DR restore, auto-rollback) MUST be verified by fault-injection behavior tests that prove fail-closed / no-data-loss outcomes, not merely happy-path checks.
  • R-TEST-06 (governance) — AI agents MUST NOT modify the specifications (this docs/ tree, the requirements, or accepted ADRs). Specs are changed only by the human operator/architect. Agents that find a spec wrong, blocking, ambiguous, or missing MUST stop and escalate and MAY draft a proposal, but MUST NOT self-approve or merge a spec/ADR change (see AGENTS.md §2 and §11).

Acceptance philosophy

Each requirement class maps to an automated, reproducible, behavior-based conformance test (R-TEST-*). "Done" means the conformance test for the touched requirements passes — a green, reproducible result traced to requirement IDs — never "manifest merged" and never a manual demonstration. The testing approach is specified in docs/14-testing-and-conformance.md and ADR-0010; the gate checklist lives in agents/definition-of-done.md.

Governance: the specifications are human-owned and immutable to agents (R-TEST-06). Agents implement against the spec and prove conformance with tests; they never edit the spec to fit the implementation.

Open questions

Resolved

  • OQ-1 (WAN / edge) — RESOLVED: one commercial WAN, no multi-ISP plans; dumb SOHO edge (no VLAN/VPN/services). All segmentation + VPN in-cluster; edge does NAT + port-forward only. Physical multi-WAN out of scope. See 03, 09. Amended 2026-11-08 (ADR-0016): public web ingress moved to an outbound-only Cloudflare Tunnel; the edge no longer forwards 80/443. Its remaining forwards are WireGuard udp/51820 and Plex tcp/32400.

  • OQ-2 (bulk media) — RESOLVED (ADR-0018): Plex bulk media lives outside the cluster on a standalone TrueNAS NFS server on the production LAN, and sits outside platform backup. Plex's metadata DB still lives on replicated Longhorn. See 04.

  • OQ-3 (VPN) — RESOLVED: PIA over OpenVPN-UDP (non-TCP; ADR-0013). Egress is a node-level host tunnel steered by Cilium Egress Gateway, failing closed under Cilium's own enforcement (ADR-0015, ADR-0020) — vpn-class pods routed through it, rules enforced outside the service. See 03.

  • OQ-4 (TLS/DNS challenge access) — RESOLVED: a scoped Cloudflare API token (Zone·DNS·Edit, keithjackson.dev), SOPS-encrypted, drives cert-manager DNS-01 and ExternalDNS. The static *.keithjackson.dev wildcard covers reachability but not TXT-challenge writes; the token handles issuance + record automation. Operator provides the token once. See 08.

  • OQ-BKP (off-cluster DR backup target) — RESOLVED (ADR-0018): Cloudflare R2 stores Velero backups (kopia file-system volume data) and CNPG Barman archives of the irreplaceable tier; Longhorn has no backup target of its own. See 04.

Still open

None tracked here. Unmet requirements are recorded in known gaps.