From 8cd5ab2e41853c7541a5fc1729d8fe666065c6df Mon Sep 17 00:00:00 2001 From: Keith Jackson Date: Sun, 8 Nov 2026 22:47:54 -0600 Subject: [PATCH] chore(spec): adopt the spec sync: registry parity and ADR cross-references Add R-DNS-04, R-DNS-05 and R-SEC-04 to the requirement mirror and tag the L0 tests that verify them. Align the R-ENV and R-UPD-02 summaries with the amended requirements. Point the VPN egress docs at ADR-0020; the drafted "ADR-0016" number went to the Cloudflare Tunnel. Co-Authored-By: Claude Opus 5.5 --- conformance/requirements/registry.yaml | 20 ++++++-- .../chainsaw-test.yaml | 5 +- .../chainsaw-test.yaml | 3 +- .../pihole-lan-names/chainsaw-test.yaml | 3 +- decisions/2026-11-08-adopt-spec-sync.md | 46 +++++++++++++++++++ platform/vpn-egress/README.md | 6 +-- platform/vpn-egress/egress-steering.yaml | 2 +- 7 files changed, 74 insertions(+), 11 deletions(-) create mode 100644 decisions/2026-11-08-adopt-spec-sync.md diff --git a/conformance/requirements/registry.yaml b/conformance/requirements/registry.yaml index a6ce4c3..4d5d30a 100644 --- a/conformance/requirements/registry.yaml +++ b/conformance/requirements/registry.yaml @@ -120,6 +120,14 @@ requirements: doc: docs/09-dns-and-ingress.md safety_critical: false summary: "Internal clients resolve to the internal Gateway (split-horizon)." + - id: R-DNS-04 + doc: docs/09-dns-and-ingress.md + safety_critical: false + summary: "Public HTTP(S) reachable with no inbound 80/443 forward; LAN works whichever resolver answers." + - id: R-DNS-05 + doc: docs/09-dns-and-ingress.md + safety_critical: false + summary: "Only declared-public names reachable through public ingress; LAN/VPN-only names are refused." # --- Automated updates --- - id: R-UPD-01 @@ -129,7 +137,7 @@ requirements: - id: R-UPD-02 doc: docs/11-automated-updates.md safety_critical: true - summary: "Updates need no human/AI/hand-authored change; bad update auto-rolls-back." + summary: "Allowed-class updates need no human/AI/hand-authored change; bad update auto-rolls-back." - id: R-UPD-03 doc: docs/11-automated-updates.md safety_critical: false @@ -175,15 +183,15 @@ requirements: - id: R-ENV-01 doc: docs/02-environments-and-provisioning.md safety_critical: false - summary: "dev/test provisioned as local KVMs via IaC (create/baseline/rebuild)." + summary: "Nodes baselined, cluster-joined and managed as code; bare-metal imaging is checklist-driven." - id: R-ENV-02 doc: docs/02-environments-and-provisioning.md safety_critical: false - summary: "Same GitOps config promotes dev/test -> prod (authored once)." + summary: "GitOps config authored once as base plus overlay; a future environment needs overrides only." - id: R-ENV-03 doc: docs/02-environments-and-provisioning.md safety_critical: false - summary: "prod is 3x mini-PC bare-metal k3s with local storage." + summary: "Production is 3x mini-PC bare metal; changes reach it only through the PR gate and Flux." # --- GitOps & delivery --- - id: R-GITOPS-01 @@ -212,6 +220,10 @@ requirements: doc: docs/07-security-and-secrets.md safety_critical: false summary: "All security properties demonstrable/auditable." + - id: R-SEC-04 + doc: docs/07-security-and-secrets.md + safety_critical: false + summary: "Kubernetes API not reachable from the public internet by any path; LAN/VPN-only." # --- Identity & access --- - id: R-IDP-01 diff --git a/conformance/tests/l0-static/cloudflared-ingress-rules/chainsaw-test.yaml b/conformance/tests/l0-static/cloudflared-ingress-rules/chainsaw-test.yaml index a32ab3e..140243b 100644 --- a/conformance/tests/l0-static/cloudflared-ingress-rules/chainsaw-test.yaml +++ b/conformance/tests/l0-static/cloudflared-ingress-rules/chainsaw-test.yaml @@ -22,8 +22,11 @@ metadata: labels: conformance.home.k3s/R-NET-01: "true" conformance.home.k3s/R-NET-02: "true" + conformance.home.k3s/R-DNS-04: "true" + conformance.home.k3s/R-DNS-05: "true" + conformance.home.k3s/R-SEC-04: "true" annotations: - conformance.home.k3s/requirements: "R-NET-01, R-NET-02" + conformance.home.k3s/requirements: "R-NET-01, R-NET-02, R-DNS-04, R-DNS-05, R-SEC-04" conformance.home.k3s/layer: "L0" conformance.home.k3s/scope: "whole-repo" conformance.home.k3s/safety-critical: "true" diff --git a/conformance/tests/l0-static/internal-services-publish-no-public-dns/chainsaw-test.yaml b/conformance/tests/l0-static/internal-services-publish-no-public-dns/chainsaw-test.yaml index 34d55f8..c77ee1e 100644 --- a/conformance/tests/l0-static/internal-services-publish-no-public-dns/chainsaw-test.yaml +++ b/conformance/tests/l0-static/internal-services-publish-no-public-dns/chainsaw-test.yaml @@ -105,8 +105,9 @@ metadata: labels: conformance.home.k3s/R-DNS-02: "true" conformance.home.k3s/R-DNS-03: "true" + conformance.home.k3s/R-DNS-05: "true" annotations: - conformance.home.k3s/requirements: "R-DNS-02, R-DNS-03" + conformance.home.k3s/requirements: "R-DNS-02, R-DNS-03, R-DNS-05" conformance.home.k3s/layer: "L0" # WHOLE-REPO SCOPE — this invariant quantifies over every ServiceDefinition in catalog/, every # HTTPRoute in apps/wave-* and platform/* and runbooks/, and every external-dns-annotated diff --git a/conformance/tests/l0-static/pihole-lan-names/chainsaw-test.yaml b/conformance/tests/l0-static/pihole-lan-names/chainsaw-test.yaml index 10404dd..4c76f84 100644 --- a/conformance/tests/l0-static/pihole-lan-names/chainsaw-test.yaml +++ b/conformance/tests/l0-static/pihole-lan-names/chainsaw-test.yaml @@ -20,8 +20,9 @@ metadata: name: pihole-lan-names labels: conformance.home.k3s/R-DNS-03: "true" + conformance.home.k3s/R-DNS-04: "true" annotations: - conformance.home.k3s/requirements: "R-DNS-03" + conformance.home.k3s/requirements: "R-DNS-03, R-DNS-04" conformance.home.k3s/layer: "L0" conformance.home.k3s/scope: "whole-repo" conformance.home.k3s/safety-critical: "true" diff --git a/decisions/2026-11-08-adopt-spec-sync.md b/decisions/2026-11-08-adopt-spec-sync.md new file mode 100644 index 0000000..838f5f8 --- /dev/null +++ b/decisions/2026-11-08-adopt-spec-sync.md @@ -0,0 +1,46 @@ +--- +date: 2026-11-08 +branch: chore/adopt-spec-sync +title: Adopt the spec sync (registry parity, ADR cross-references, spec pin) +--- + +## What changed + +The operator ruled this repo's `main` (`50c8719`) the accepted state and had the System Architect +bring the spec repo in line with it (spec PR `jakman85/home-network-k3s#2`). This is the +implementation half of that sync. + +- `conformance/requirements/registry.yaml`: added R-DNS-04, R-DNS-05 and R-SEC-04, which the spec + defined and this mirror lacked. Reworded the R-ENV-01/02/03 and R-UPD-02 summaries to match the + amended requirements. +- Tagged existing L0 tests against those IDs, because a registry entry with no tagged test blocks + the merge: `cloudflared-ingress-rules` (R-DNS-04, R-DNS-05, R-SEC-04), `pihole-lan-names` + (R-DNS-04), and `internal-services-publish-no-public-dns` (R-DNS-05). +- `platform/vpn-egress/README.md` and a comment in `egress-steering.yaml` pointed at a drafted + "ADR-0016 supersede". That number went to the Cloudflare Tunnel; the accepted ADR is ADR-0020. +- `ci/SPEC_PIN` must move to the spec merge commit. That SHA does not exist until the spec PR + merges, so this PR carries a `WIP:` title until the bump is added. + +## What I verified + +- Read each tagged test's assertions before tagging it. `cloudflared-ingress-rules` asserts every + LAN-only hostname maps to a 404, the catch-all exists, no rule targets the API, and the egress + policy admits no API or host entity. `pihole-lan-names` asserts LAN names resolve independently + of the public path. `internal-services-publish-no-public-dns` asserts internal routes carry the + disown annotation and pins the declared L4 set. +- The toolbox gate result for this commit is quoted in the PR description, with the spec branch + mounted so that the checks parsing spec text ran against the new spec. + +## What I did NOT verify + +- **These tags are static coverage, not proof.** No L2 test touches the tunnel, and nothing here + shows router 80/443 closed or 6443 unforwarded (spec `docs/16-known-gaps.md`). +- R-SEC-04 is registered `safety_critical: false`. Making it `true` needs an L3 fault-injection + test that does not exist. UNVERIFIED whether the operator wants that. +- No live cluster, tunnel or DNS state was measured. +- `spec-immutability` did not run against a pinned checkout: the pin is not bumped yet. +- Stale implementation prose found by the audit was deliberately left out of this PR, because the + operator ruled this repo the accepted state and none of it changes behaviour. Examples: + `catalog/gitea.yaml` still says "+ mirrored"; `platform/longhorn` still promises a backup target; + `README.md` says `node-loss` is outstanding; `hands-off-update` exercises Flux image policy rather + than Renovate; `dev-nodes-provisioned-as-code` is named for an environment that no longer exists. diff --git a/platform/vpn-egress/README.md b/platform/vpn-egress/README.md index b0dc022..2c72b8c 100644 --- a/platform/vpn-egress/README.md +++ b/platform/vpn-egress/README.md @@ -217,9 +217,9 @@ Consequences, stated plainly: **Governance:** ADR-0015 and `docs/03` are specifications and are immutable to agents (spec `AGENTS.md` §11, R-TEST-06, enforced by `ci/spec-immutability.sh`) — they are **not** edited -here. The correction is raised as an **escalation with a drafted ADR-0016 supersede** in -`DECISIONS-LOG.md` (entry of 2026-09-06) for the operator/architect to accept. Until then this file -and `egress-steering.yaml` are the accurate statement of what the implementation can actually do. +here. The correction, first escalated in `DECISIONS-LOG.md` (entry of 2026-09-06), is recorded in +spec **ADR-0020**, which supersedes ADR-0015's Kill-switch and Resilience bullets. This file and +`egress-steering.yaml` state what the implementation does. ## Secrets — host-side now PIA credentials + the `.ovpn` live **on the gateway node via Ansible vault** (see spec `docs/07`), **not** diff --git a/platform/vpn-egress/egress-steering.yaml b/platform/vpn-egress/egress-steering.yaml index bd06f2b..8063d3b 100644 --- a/platform/vpn-egress/egress-steering.yaml +++ b/platform/vpn-egress/egress-steering.yaml @@ -109,7 +109,7 @@ spec: # that shard and re-shards the rest (breaking live connections), and each shard exits from a # DIFFERENT public IP. Upstream: cilium/cilium#30157, cilium/cilium#18230. # ADR-0015's "Resilience: two egress-gateway nodes + Cilium Egress Gateway HA (gateway failover)" - # is therefore NOT IMPLEMENTABLE on Cilium OSS — escalated for an ADR-0016 supersede; see + # is therefore NOT IMPLEMENTABLE on Cilium OSS — superseded by spec ADR-0020; see # platform/vpn-egress/README.md and the DECISIONS-LOG entry of 2026-09-06. # # `interface: tun0` IS RESOLVED ONCE — the gateway node has to tell Cilium when tun0 changes.