Operator turned off ExpressVPN auto-renew and purchased PIA. The single-shared- gateway model is retained; provider/client/protocol change to PIA via Gluetun over WireGuard with native port forwarding (qbittorrent-natmap port-sync). Simpler than ExpressVPN: no custom OpenVPN config, just PIA user/pass SOPS-encrypted; Gluetun provides the built-in kill-switch. Updated: ADR-0013 (new) + ADR-0012 superseded + index; docs 00,01,03,07; task board (T-3.2p/T-3.2, OQ-3). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
209 lines
13 KiB
Markdown
209 lines
13 KiB
Markdown
# 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 is delivered in two stages:
|
||
|
||
- **dev/test** on local KVMs — the active build target, used to prove viability.
|
||
- **prod** on 3 mini-PCs — the eventual home, designed for now but not built on yet.
|
||
|
||
## 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 multi-WAN egress including VPN routing.
|
||
- Observability with email + dashboard alerting.
|
||
- A command-and-control web UI at the root of `home.keithjackson.dev`.
|
||
- The initial service catalog (13 services, see [`13-service-catalog.md`](13-service-catalog.md)).
|
||
|
||
## Out of scope (for now)
|
||
|
||
- Building on production mini-PC hardware (design-only until the roadmap flips it to active).
|
||
- 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 and its records resolve to the network's public IP.
|
||
- 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; dev/test MAY run fewer nodes but MUST exercise the same multi-node topology.
|
||
- **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
|
||
**outside the service** (a single shared in-cluster gateway), so a service cannot leak by
|
||
misconfiguration (ADR-0013, 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 MUST require **no human, no AI, and no hand-authored GitOps change** to
|
||
occur. Automation controllers acting on declarative policy are the permitted mechanism.
|
||
- **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).
|
||
|
||
### 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).
|
||
|
||
### Environments — `R-ENV-*`
|
||
- **R-ENV-01** — dev/test MUST be provisioned as local KVMs via IaC: created, baselined, cluster-
|
||
joined, and managed as code.
|
||
- **R-ENV-02** — The same GitOps configuration MUST promote from dev/test to prod with only
|
||
environment-scoped overrides.
|
||
- **R-ENV-03** — prod is a 3× mini-PC bare-metal k3s cluster with local storage; it MUST NOT be an
|
||
active development surface until the roadmap declares it ready.
|
||
|
||
### 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`](../AGENTS.md) §2 and §11).
|
||
|
||
## Acceptance philosophy
|
||
|
||
Each requirement class maps to an **automated, reproducible, behavior-based conformance test** in
|
||
dev/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 (2026-08-15)
|
||
- **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).
|
||
- **OQ-2 (bulk media)** — **RESOLVED (deferred backing)**: Plex bulk media lives **outside the
|
||
cluster**; the specific backing is **TBD later** and does not block the core platform. Plex's
|
||
metadata DB still lives on replicated Longhorn. The **off-cluster DR backup target** (for app
|
||
config, Nextcloud, Gitea, etc.) remains to be chosen at Phase 4. See [`04`](04-storage-and-state.md).
|
||
- **OQ-3 (VPN)** — **RESOLVED**: **PIA** via Gluetun, cluster-provisioned, **single shared
|
||
in-cluster tunnel** over **WireGuard** (non-TCP), native port forwarding, rules enforced outside
|
||
the service (ADR-0013, supersedes ADR-0012's ExpressVPN choice). 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).
|
||
|
||
### Still open
|
||
- **OQ-BKP (off-cluster DR backup target)**: which S3-compatible endpoint or NFS host stores
|
||
Velero/Longhorn backups of the irreplaceable tier. Needed by **Phase 4**, not blocking Phase 2.
|
||
See [`04`](04-storage-and-state.md).
|