Commit Graph
21 Commits
Author SHA1 Message Date
jakman85 1ddc14b918 Merge pull request 'ADR-0016 (Proposed): public ingress via Cloudflare Tunnel' (#1) from docs/adr-cloudflare-tunnel-ingress into main
Reviewed-on: jakman85/home-network-k3s#1
2026-11-09 04:21:47 +00:00
jakman85andClaude Opus 5.5 28d0e0a0f2 ADR-0016 (Proposed): public ingress via Cloudflare Tunnel
Record the operator's 2026-11-08 decision to move public HTTP(S) ingress
from edge port-forwarding of 80/443 to an outbound-only Cloudflare Tunnel
(in-cluster cloudflared, proxied CNAMEs via ExternalDNS). Motivation: the
GFiber router cannot hairpin NAT and advertises Google IPv6 DNS via RA, so
LAN clients bypass Pi-hole split horizon and fail.

Docs 00/01/03/07/08/09/13/14, GLOSSARY and definition-of-done brought into
line. R-DNS-01..03 and R-TLS-01..03 wording unchanged. New requirements:
R-DNS-04 (no 80/443 forward, resolver-independent LAN reachability),
R-DNS-05 (only `public` services at the tunnel; 404 catch-all; no public
record for `internal`), R-SEC-04 (Kubernetes API never publicly reachable).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-08 12:38:16 -06:00
jakman85 b633b7fb08 Merge feature/adr-0015-egress-invariants into main 2026-08-24 00:52:07 -05:00
jakman85andClaude Opus 4.8 c68349f4ab ADR-0015: add non-negotiable egress invariants (default=WAN, scoped kill-switch)
Make the operator's requirement explicit: default (every pod + the nodes) egresses
the local/private WAN, never the VPN; VPN is strictly opt-in via egress-class=vpn;
the kill-switch blackholes ONLY vpn-class. The two classes fail in opposite
directions — internet fails open (WAN), vpn fails closed (blackhole). Added as ADR-0015
Invariants + a default-WAN principle in docs/03, gated by the egress-class/vpn-killswitch
conformance tests.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-08-24 00:52:07 -05:00
jakman85 6770087d68 Merge feature/adr-0015-node-vpn-egress into main 2026-08-24 00:46:18 -05:00
jakman85andClaude Opus 4.8 7598cff945 ADR-0015: VPN egress steering — node-level tunnel + Cilium Egress Gateway
The in-cluster Gluetun-pod gateway (ADR-0013) cannot be steered: a pod's tun0 is
invisible to the node datapath, and hostNetwork Gluetun hijacks the node's own
default route (breaks k3s/etcd). The tunnel itself works (PIA egress validated),
but not the steering.

New decision (ADR-0015): move the tunnel to a HOST OpenVPN systemd service on
egress-gateway node(s) (Ansible-managed, split-tunnel so it doesn't touch the node
default route), steer egress-class=vpn pods with a CiliumEgressGatewayPolicy (native
eBPF, no client-pod changes), and enforce the kill-switch with a host nftables rule
(fail-closed independent of Cilium). PIA/OpenVPN-UDP provider decision (ADR-0013)
stands; the .ovpn transfers over; PF re-solved host-side (Phase 6).

Amended for consistency: ADR-0013 (client+steering superseded), decisions/README,
docs/00, docs/01, docs/03, docs/07, agents/task-board.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-08-24 00:46:18 -05:00
jakman85 0ba06ec25b Merge feature/vpn-protocol-openvpn into main 2026-08-22 09:02:32 -05:00
jakman85andClaude Opus 4.8 95b4b6af21 Amend ADR-0013: VPN protocol WireGuard -> OpenVPN-UDP (Gluetun lacks PIA+WG)
Gluetun does not support Private Internet Access over WireGuard (upstream:
native support "cannot be added... slow work in progress"; confirmed live
rejecting the provider on v3.40/v3.41/latest). PIA in Gluetun is OpenVPN-only.

Amend ADR-0013 to PIA over OpenVPN-UDP. The egress ARCHITECTURE is unchanged
(single shared gateway, central kill-switch/fail-closed, native PF, Cilium
egress steering, active/standby) and R-NET-05/06/07 remain met; only the tunnel
protocol changes. OpenVPN-UDP satisfies "non-TCP", and Gluetun's HTTP control
API (health + forwarded-port lookup) is what the shared-gateway topology needs.
thrnz/docker-wireguard-pia retained as a documented WireGuard fallback.

Propagated the protocol correction to all active specs: ADR-0013 (+amendment
note), decisions/README, docs/00, docs/01, docs/03, docs/07, agents/task-board.
Superseded ADR-0008/0012 left immutable as historical record.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-08-22 09:02:31 -05:00
jakman85andClaude Opus 4.8 a7fc19fad5 Merge feature/phase0-clarifications into main
Resolve the 5 spec ambiguities surfaced during Phase 0 implementation.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-08-16 00:27:25 -05:00
jakman85andClaude Opus 4.8 70a98675ac Phase 0 clarifications: resolve 5 ambiguities surfaced by implementation
- docs/07: correct plaintext-secret enforcement — CI-time ciphertext check vs
  admission SOPS-provenance (Flux decrypts before the API server, so an admitted
  Secret carries no ciphertext; a 'reject plaintext' admission rule is impossible).
- docs/13: add Contract clarifications (v1.1) — source exactly-one-of; longhorn⇒size
  (L0 policy); backup is a {policy} ref; update.channel free-string/stable default;
  auth.groups required for oidc|forward-auth; public⇒auth.mode≠none (schema);
  irreplaceable⇒off-cluster-backup (L0 policy).
- docs/02: Cilium install ownership — Ansible bootstraps, Flux adopts config, value-parity contract.
- docs/03: platform label-key convention (homelab.keithjackson.dev/{egress-class,auth-mode,...}).
- docs/14: ServiceDefinition validation is two complementary L0 gates (JSON Schema shape + Kyverno invariants).

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-08-16 00:27:24 -05:00
jakman85andClaude Opus 4.8 cc1fb1d3d5 Merge feature/idp-keycloak into main
Add Keycloak IdP/SSO (ADR-0014, docs/15, R-IDP-*); resolves the C2 auth
open question.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-08-15 23:33:50 -05:00
jakman85andClaude Opus 4.8 fb153f0bb5 Add Keycloak IdP / SSO (ADR-0014, docs/15, R-IDP-*)
New identity subsystem, and resolves the C2 auth open question:
- docs/15-identity-and-access.md + ADR-0014: Keycloak as central IdP; native
  OIDC where supported (Gitea, Nextcloud, Grafana, Kasm, Semaphore, Headlamp)
  and oauth2-proxy forward-auth for bare UIs (PiHole, *arr, qBittorrent,
  Homepage, Prometheus, Alertmanager, Hubble). Group-based authz + MFA;
  declarative realm/clients (GitOps); PostgreSQL on Longhorn, DR-critical +
  break-glass; hardened public exposure. Plex/Seerr use Plex auth; Minecraft N/A.
- Requirements R-IDP-01..06; ServiceDefinition gains an  block.
- Onboarded early (with Gitea) so services adopt SSO as they come up.
- Threaded through: docs 00,01,07,10,12,13; roadmap phases+workstreams;
  task board (T-6.1c/T-6.1d); glossary; ADR index; README.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-08-15 23:33:49 -05:00
jakman85andClaude Opus 4.8 1e0e67f902 Merge feature/vpn-provider-pia into main
VPN provider ExpressVPN -> PIA (ADR-0013 supersedes ADR-0012); Gluetun +
WireGuard + native port forwarding.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-08-15 23:16:33 -05:00
jakman85andClaude Opus 4.8 2cca9beac7 Switch VPN provider ExpressVPN -> PIA (ADR-0013 supersedes ADR-0012)
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]>
2026-08-15 23:16:33 -05:00
jakman85andClaude Opus 4.8 fdca4305cf Merge feature/oq4-cloudflare-token into main
Resolve OQ-4 (scoped Cloudflare API token). Phase 2 TLS/DNS unblocked.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-08-15 23:02:59 -05:00
jakman85andClaude Opus 4.8 cde2f2330c Resolve OQ-4: scoped Cloudflare API token for DNS-01 + ExternalDNS
Operator chose Option A. cert-manager solves ACME DNS-01 and ExternalDNS
manages *.home.keithjackson.dev records via a scoped Cloudflare token
(Zone.DNS.Edit, keithjackson.dev), stored SOPS-encrypted. Operator provides
the token once (new prereq T-2.4p); T-2.4/T-2.5 unblocked. Only OQ-BKP
(off-cluster DR backup target, Phase 4) remains open.

Updated: docs 00,08,09; task board.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-08-15 23:02:59 -05:00
jakman85andClaude Opus 4.8 192ce31bef Merge feature/oq-answers-vpn-wan-storage-dns into main
Fold operator answers to OQ-1/2/3 into the specs; refine OQ-4 to the one
remaining decision (ACME DNS-01 access). ADR-0012 supersedes ADR-0008.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-08-15 23:00:12 -05:00
jakman85andClaude Opus 4.8 a2cbdf656d Resolve OQ-1/2/3, refine OQ-4: single ExpressVPN gateway, single WAN, external media
Operator answers folded into the specs:
- OQ-3 VPN: ExpressVPN, cluster-controlled SINGLE shared in-cluster tunnel
  (OpenVPN UDP / non-TCP), rules enforced outside the service. New ADR-0012
  supersedes ADR-0008 (Gluetun-sidecar default dropped). Central kill-switch.
- OQ-1 WAN/edge: one commercial WAN, no multi-ISP plans; dumb SOHO edge (no
  VLAN/VPN/services). Multi-WAN out of scope; segmentation + VPN in-cluster;
  edge = NAT + manual port-forward. Gateway VIP resolved to Cilium L2
  announcement. T-3.3 marked won't-do.
- OQ-2 storage: bulk media lives outside the cluster (backing TBD, non-blocking)
  via a 'bulk' external-mount indirection; Plex metadata DB stays on Longhorn.
  Split out a separate OQ-BKP for the off-cluster DR backup target (Phase 4).
- OQ-4 refined: wildcard record covers reachability but NOT ACME DNS-01 (needs
  TXT-write access) and does not match *.home.* depth. Still OPEN: Cloudflare
  API token (Option A) vs acme-dns delegation (Option B). T-2.4/T-2.5 blocked.

Updated: docs 00,01,03,04,08,09; ADR-0008/0012 + index; task board.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-08-15 23:00:11 -05:00
jakman85andClaude Opus 4.8 1a67ab5cfa Merge feature/dev-workflow-and-gitea-bootstrap into main
Establishes the feature-branch→main workflow and Gitea-early bootstrap plan.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-08-15 22:40:17 -05:00
jakman85andClaude Opus 4.8 de26a53d4b Establish dev workflow (feature-branch→main) and pull Gitea bootstrap earlier
- Add CONTRIBUTING.md: interim local-git rules (no work on main; every change
  a feature branch merged into main) and the target state (Gitea-hosted repos,
  GitOps source cutover, CI/CD via Gitea Actions running the Kyverno gate +
  Chainsaw conformance Job, PR merge gate). Note the self-hosted-git
  bootstrapping circularity + off-cluster mirror/backup mitigation.
- AGENTS.md: add section 12 (version-control workflow).
- docs/05: source-of-truth migration (local bootstrap -> self-hosted Gitea).
- docs/13 + roadmap: reprioritize Gitea as bootstrapping-critical, onboarded
  early (WS-APPS step 2); nextcloud onboarding split out.
- task-board: T-6.1a (gitea early), T-6.1b (source+CI migration to Gitea).

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-08-15 22:40:11 -05:00
jakman85andClaude Opus 4.8 e8c555cfeb Initial baseline: home platform (k3s + Longhorn) specification repository
Greenfield spec set for a GitOps-driven PaaS+IaaS home platform: vision &
requirements (R-* IDs), architecture, 14 domain specs, 11 ADRs, roadmap
(phases + workstreams), and AI-agent coordination (roles, protocol,
definition-of-done, task board).

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-08-15 22:37:56 -05:00