Files
home-network-k3s/decisions
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
..

Architecture Decision Records (ADRs)

ADRs are an append-only ledger of the significant technology and pattern choices for this platform. Each records the context, the decision, the rejected alternatives, and the requirements it serves. Do not edit an accepted decision in place — supersede it with a new ADR.

ADRs are specifications and are immutable to AI agents (R-TEST-06). Agents may draft a proposed ADR and escalate it, but only the human operator/architect approves/accepts or edits ADRs. See ../AGENTS.md §3 and §11.

These ADRs are not the implementation's handoff log. The dated files in the implementation repo's decisions/ directory record what each change did and how it was verified. ADRs here record locked technology and pattern choices.

Partial supersession. When a new ADR replaces only part of an accepted one, the older ADR stays Accepted and its Status line names the clause superseded (the ADR-0013 precedent).

Process (read before proposing a change)

  1. To change a decision, create a new ADR with the next number and set Status: Proposed, Supersedes: ADR-XXXX. (Agents draft + escalate; the human approves.)
  2. State what new information invalidates the old decision.
  3. On approval, set the new ADR Accepted and the old one Superseded by: ADR-YYYY.
  4. Never silently re-interpret an accepted ADR (see ../AGENTS.md §3).

Template

# ADR-XXXX — <title>
- Status: Proposed | Accepted | Superseded by ADR-YYYY
- Date: <YYYY-MM-DD>
- Serves: <requirement IDs, e.g. R-NET-01, R-NET-06>
- Supersedes: <ADR-ZZZZ or —>

## Context
<forces, constraints, why a decision is needed>

## Decision
<what we chose, stated decisively>

## Alternatives considered
<options and why rejected>

## Consequences
<positive, negative, follow-ups, risks>

Index

ADR Title Status Serves
0001 Kubernetes distribution: k3s Accepted — dev provisioning clause superseded by 0017 R-PLAT-01
0002 CNI & network policy & ingress: Cilium + Gateway API Accepted R-NET-01..07, R-DNS-01
0003 Distributed storage: Longhorn (+ bulk tier, Velero) Accepted — backup and bulk-backing clauses superseded by 0018 R-STATE-01..04
0004 GitOps engine: Flux CD Accepted R-GITOPS-01..03
0005 VM workloads: KubeVirt + CDI Accepted R-PLAT-03
0006 TLS: cert-manager + Let's Encrypt (DNS-01) Accepted R-TLS-01..03
0007 Secrets: SOPS + age Accepted R-SEC-01
0008 VPN egress model: Gluetun sidecar + Cilium Egress Gateway Superseded by 0012 R-NET-05/06
0009 Automated updates: Flux image automation + Renovate Superseded by 0023 R-UPD-01..03
0010 Conformance testing: automated, reproducible, behavior-based Accepted R-TEST-01..06
0011 Conformance test tooling: Kyverno + Chainsaw Accepted R-TEST-01..05
0012 VPN egress: single shared in-cluster ExpressVPN gateway Superseded by 0013 R-NET-05/06/07
0013 VPN provider: PIA over OpenVPN-UDP + port forwarding Accepted — provider/protocol only (client+steering superseded by 0015) R-NET-05/06/07
0014 Identity Provider: Keycloak (SSO via OIDC + forward-auth) Accepted — forward-auth enforcement point and app list superseded by 0022 R-IDP-01..06
0015 VPN egress steering: node-level tunnel + Cilium Egress Gateway (host kill-switch) Accepted — Kill-switch and Resilience bullets superseded by 0020 R-NET-05/06/07
0016 Public ingress: Cloudflare Tunnel (outbound-only) instead of edge port-forwarding Accepted R-DNS-01..05, R-TLS-01/02, R-SEC-02/04
0017 Single environment: bare-metal production; dev retired Accepted R-PLAT-01, R-ENV-01/02/03, R-STATE-01/02
0018 Storage and backup: TrueNAS NFS bulk tier; Velero/kopia + Barman to Cloudflare R2 Accepted R-STATE-03, R-STATE-04
0019 PostgreSQL via CloudNativePG Accepted R-IDP-05, R-STATE-01, R-STATE-02, R-STATE-03
0020 VPN egress: single gateway node, Cilium-enforced fail-closed Accepted R-NET-05, R-NET-06
0021 Inbound remote access: WireGuard on a hostNetwork DaemonSet Accepted remote operator access (no dedicated requirement ID); bounds R-NET-01, R-SEC-02, R-SEC-04
0022 Forward-auth via per-app oauth2-proxy sidecars Accepted R-IDP-02, R-IDP-06
0023 Automated updates: Renovate for images and charts, every change PR-gated Accepted R-UPD-01, R-UPD-02, R-UPD-03, R-TEST-03
0024 Delivery authority: the CI gate is the approver; sensitive-path review; attributed admin override Accepted R-TEST-03, R-TEST-06, R-GITOPS-01, R-SEC-02
0025 In-cluster AI Program Manager (pm-agent) Accepted R-GITOPS-01, R-TEST-03, R-TEST-06, R-SEC-02