Files
home-network-k3s/docs/00-vision-and-scope.md
T
jakman85andClaude Opus 5.5 18f7ba8d0f docs: apply the operator's rulings on Kasm, revocation and fault injection
Retire Kasm from the catalog. Drop ACME revocation from R-TLS-02. Make L3
fault injection operator-run and exclude it from the merge gate (R-TEST-03,
ADR-0026). Update the gap register to match; Homepage authentication is now
ruled must-enforce and stays a gap until the implementation lands.

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

242 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`](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 and renewed** 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. Fault-injection
tests (R-TEST-05) are not automated: the operator runs them deliberately and records the result,
and they do not gate individual merges (ADR-0026).
- **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`](../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`](14-testing-and-conformance.md) and
[ADR-0010](../decisions/adr-0010-conformance-testing.md); the gate checklist lives in
[`agents/definition-of-done.md`](../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`](03-networking-isolation-and-egress.md), [`09`](09-dns-and-ingress.md).
**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`](04-storage-and-state.md).
- **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`](03-networking-isolation-and-egress.md).
- **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`](08-tls-and-certificates.md).
- **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`](04-storage-and-state.md).
### Still open
None tracked here. Unmet requirements are recorded in [known gaps](16-known-gaps.md).