The next development thread is knoe-auth Phase 2 OIDC iteration, which
just landed on main but has no fast inner-loop. Instead of bouncing
edits through the GKE deploy chain, we want a laptop-resident model
where knoe-auth runs from the IDE / mvn against real Postgres + KDC
brought up in k3d.
Two new docs:
docs/plans/k3d-gke-mirror.md
Architectural plan. Top-to-bottom rationale for what the k3d
model should mirror, what we deliberately skip (supabase, kong,
oauth2-proxy, Studio — all unnecessary for knoe-auth's surfaces),
and the phasing. Captures the three scope decisions taken
2026-05-02 with the user:
- knoe-auth runs on the laptop, not as a pod (faster inner loop)
- stack scope is CNPG + KDC only
- single-replica CNPG, realm KNOE.LOCAL (distinct from KNOE.DEV)
Out-of-scope for Phase 1 listed explicitly so reviewer
expectations match scope: SPNEGO E2E, image-build-and-load,
supabase, oauth2-proxy, OidcCodeService DB persistence.
docs/plans/junie/k3d-knoe-auth-dev-loop.md
Phase 1 brief. Concrete deliverables:
- k8s/knoe/knoe-kdc-{deployment,service,configmap,pvc,init-job}.yaml
- etc/init_knoe_auth.sh --mode k3d flag (or sibling script)
- Makefile: k3d-knoe-{up,pf,down}
- etc/krb5.local.conf (with udp_preference_limit=1 for
kubectl port-forward UDP flakiness on macOS)
- docs/local-dev-knoe-auth.md (engineer-facing)
- scripts/k3d-knoe-smoke.sh
Definition of done: a fresh-clone laptop can `make k3d-knoe-up`
+ `make k3d-knoe-pf` + `mvn -pl authority spring-boot:run` and
hit /health, /.well-known/openid-configuration in <8 minutes.
Index updates:
docs/plans/README.md — k3d-gke-mirror.md row added
docs/plans/junie/README.md — split into Active / Shipped;
k3d brief listed under Active;
the 5 shipped-2026-05-02 briefs
moved to Shipped with commit refs.
docs/TODO.md In-progress — k3d brief now top of "In progress";
the previously in-flight "Phase 2
pg_oauth in install/deploy.sh"
item demoted to "paused" (resume
after the local dev loop lands).
No code changes; all docs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
14 KiB
k3d-mirror-of-GKE — local platform model for knoe-auth dev
Active plan. Phase 1 in flight (Junie). Strategic context for a running initiative; reads top-to-bottom. Phase 1 brief at
junie/k3d-knoe-auth-dev-loop.md.
1. Why
knoe-auth Phase 2 (OIDC provider — discovery, authorize, token, userinfo,
JWKS, RS256 signing) just landed on main (via the merge that brought
claude/crazy-bose-fec256 back in). It builds; it hasn't been deployed
to the cluster yet. The next development steps — wiring OAuth2 code
exchange to a persistent table (Phase 2.5), the
ProvisioningWorker→GitLab/Gitea/CNPG dispatch surface, future Round 2
work — all benefit from a tight inner-loop that doesn't require:
- a GKE cluster to push to,
- a deploy pipeline to wait on,
- a real Google OAuth tenant to bounce through, or
- the full supabase + kong + oauth2-proxy + studio stack just to test what knoe-auth does.
What we want is a laptop dev loop: edit Java, run from IntelliJ
(or mvn spring-boot:run), break-pointing freely, against a real
PostgreSQL with the canonical knoe.* schema and a real Kerberos KDC
the SPNEGO codepath can talk to.
The four-mode installer we already ship has k3d as one of those modes
(see deployment-modes.md). It brings up CNPG
today. It does not yet bring up the bits knoe-auth needs alongside.
This plan closes that gap deliberately, in phases.
2. Where the modes stand today
| Mode | DB | knoe-auth | KDC | Supabase | Studio | OAuth2-proxy |
|---|---|---|---|---|---|---|
gke |
CNPG (3-replica) | ✅ deployed | ✅ sidecar | ✅ | ✅ | ✅ Google OIDC |
k3s |
CNPG (single) | ✅ deployed (manifests at deploy/opentofu/k3s/manifests/knoe/knoe-auth-*) |
✅ kdc-configmap | partial | partial | ❌ |
k3d |
✅ CNPG via k8s/knoe/knoe-db.yaml |
❌ no manifests | ❌ no manifests | ❌ | ❌ | ❌ |
min |
❌ no Postgres | ❌ | ❌ | ❌ | ❌ | ❌ |
The k3s mode has the auth-stack manifests but is targeted at the on-prem-VM deploy pattern (talks to a single-node k3s on a customer machine), not the laptop-dev-loop pattern. Reusing them for k3d is attractive but the wiring assumptions differ enough (LB types, ingress class, service-account identity) that copy-and-adapt is easier than refactor-to-share.
3. Strategic shape
The goal is not a full GKE mirror — that would mean Studio + Kong
- supabase + oauth2-proxy + everything, and the laptop fan would not be happy. The goal is the smallest k3d-resident stack that lets a host-resident knoe-auth talk to it end-to-end.
Decisions (set 2026-05-02 with the user):
-
knoe-auth runs on the host, not in the cluster.
mvn spring-boot:run(or IntelliJ Run Configuration) starts the process on the laptop. It connects to the k3d cluster's DB and KDC viakubectl port-forward. This gives:- hot reload via Spring DevTools,
- direct debugger attach,
- immediate stack traces in the same terminal as the build,
- no image-build-and-load cycle on every change.
Trade-off: this isn't testing the deployed-pod-shaped behaviour (mounted secrets, SA identity, sidecar networking). That's covered by the GKE deploy when it's time to go live, and a future "in-cluster smoke" Phase 3 brief if we want pre-merge checks too.
-
Stack scope is CNPG + KDC only. No supabase, no Kong, no oauth2-proxy, no Studio, no garage/MinIO. knoe-auth doesn't depend on any of those for its own surfaces (
/health,/auth/enroll/*,/auth/admin/*, OIDC discovery/authorize/ token/userinfo/jwks). The ProvisioningWorker reaches OUT to GitLab/Gitea/CNPG — those are mockable or deferrable for dev. -
Single-replica CNPG. Three-replica is GKE-shaped for availability; on a laptop it's wasteful. One instance, the same PG18 image, same
postInitTemplateSQLschema bootstrap, sameknoe.*tables. -
Kerberos realm
KNOE.LOCAL. Distinct from productionKNOE.DEVso credentials don't accidentally cross-talk. Pre-seed one admin principal and one developer principal so the engineer cankinitimmediately. -
Plain
sslmode=requirefor libpq from host. Production usesverify-fullagainst the CNPG-issued cert. Locally that needs the CA chain and a SAN forlocalhost, which adds yarn-ball complexity.requirekeeps TLS on the wire without the cert plumbing. (Engineers can opt intoverify-fulllater by mounting the CA cert; not gated on Phase 1.)
4. Architecture (Phase 1 target)
┌─────────────────────────────────────── Laptop ───────────────────────────────────────┐
│ │
│ IntelliJ / mvn spring-boot:run │
│ │ │
│ ▼ │
│ knoe-auth :8080 ──[ http ]──▶ curl / browser tests │
│ │ │
│ │ jdbc:postgresql://localhost:5432/knoe-db │
│ │ KRB5_CONFIG=./etc/krb5.local.conf │
│ │ KDC at localhost:88 │
│ │ │
│ ▼ │
│ ┌─── kubectl port-forward ────┐ │
│ │ localhost:5432 → svc/knoe-db-rw:5432 │
│ │ localhost:88 → svc/knoe-kdc:88 (TCP + UDP) │
│ │ localhost:464 → svc/knoe-kdc:464 (kpasswd) │
│ └────────────────┬─────────────┘ │
└────────────────────┼─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────── k3d cluster (single node) ──────────────────────────┐
│ │
│ ┌──── ns: knoe-db-0 ─────────────┐ ┌──── ns: knoe-system ─────┐ │
│ │ │ │ │ │
│ │ CNPG operator │ │ knoe-kdc Deployment │ │
│ │ knoe-db CNPG Cluster (1 inst) │ │ (krb5kdc + kadmind, │ │
│ │ → schema knoe.* via │ │ realm KNOE.LOCAL, │ │
│ │ postInitTemplateSQL │ │ pre-seeded principals) │ │
│ │ │ │ │ │
│ └─────────────────────────────────┘ └────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────────────────────┘
5. Phase 1 scope (Junie brief)
Brief: junie/k3d-knoe-auth-dev-loop.md.
Deliverables:
- CNPG-on-k3d known-good — a single-replica CNPG cluster with
the canonical
knoe.*schema, brought up via a Make target or short shell script (make k3d-knoe-upstyle). Reuses existingk8s/knoe/knoe-db.yamlwith the_strip_k3d_synology_blocksflow the Python orchestrator already runs. - KDC manifest for k3d — a
Deployment+Service+ConfigMapunderk8s/knoe/knoe-kdc-*.yaml(parallel naming toknoe-auth-*) with realmKNOE.LOCALand a pre-seeded admin principal. Init script (etc/init_knoe_kdc_k3d.shor similar) adds the principal viakadmin.localonce the pod is Ready. - Port-forward helper — a
make k3d-knoe-pftarget (or shell script) that opens 5432, 88, 464 to localhost, prints the JDBC URL and theKRB5_CONFIGenv var the engineer shouldexport, and stays in the foreground until ^C. Background mode optional. - Engineer doc —
docs/local-dev-knoe-auth.md: one-time setup, daily loop (make k3d-knoe-up && make k3d-knoe-pf), how tokinit, how to point IntelliJ at it, how to reset. - Smoke test — a shell script that, given the port-forward up,
verifies: psql connects +
\dt knoe.*shows ≥6 tables;kadmin.local listprincsincludes the seed admin; from the host,mvn -pl authority testpasses;mvn -pl authority spring-boot:runstarts cleanly andcurl localhost:8080/healthreturnsokandcurl localhost:8080/.well-known/openid-configurationreturns valid JSON with the right issuer.
Done = a 5-minute path from git pull → editing
OidcTokenController.java → re-run → see the change reflected at
localhost:8080/oauth2/token, with the DB and KDC backing it real.
6. Out of scope for Phase 1 (potential later phases)
- Phase 2 — KDC: full SPNEGO E2E including a host-side
firefox/curlthat does negotiate againstlocalhost:8080. NeedsKRB5_CONFIG, an SPN likeHTTP/localhost@KNOE.LOCAL, a keytab file mounted into knoe-auth's runtime. Phase 1 stops at the KDC being reachable; Phase 2 makes SPNEGO actually work. - Phase 3 — image-build-and-load smoke so engineers can run
make k3d-knoe-deployand see knoe-auth as a pod in the cluster before pushing to GKE. Useful as a pre-merge gate; not required for daily dev. - Phase 4 — supabase stack on k3d for end-to-end SDK testing against a local DB. Heavy. Only if/when someone needs it.
- Phase 5 — oauth2-proxy + Google OIDC on localhost for testing
the Studio gate flow without going to GKE. Even heavier; needs a
Google OAuth client with
localhostredirect URLs registered. Probably never worth it. - Phase 2.5 (separate track) — persistent OAuth2 code/session
tables. Currently
OidcCodeServiceuses an in-memoryConcurrentHashMap, which means codes don't survive a knoe-auth restart. Not blocked by k3d work but useful to have once dev iteration is fast enough that engineers exercise the flow often. Tracked separately; would touchauthority/src/main/.../session/and add migrations underauthority/src/main/resources/db/migration/.
7. Risks / open questions
- Resource ceiling on a MacBook. A single-replica CNPG plus KDC
is light (~600 MB RAM combined), but k3d itself + Docker Desktop
- IntelliJ + Spring Boot can climb. Document a "low-RAM mode" if it becomes a real problem; don't speculate now.
- k3d's Docker networking and
kubectl port-forwardUDP. UDP port-forward (port 88 for Kerberos UDP, 464 for kpasswd) works in recent kubectl versions but has historically been finicky. If it fails, fall back to TCP-only Kerberos (tcp_onlyinkrb5.conf). Phase 1 should set this in the engineer-sidekrb5.local.confpreemptively. - CNPG operator CRD installation in fresh k3d. The
make k3d-upflow today runsinit_cnpg_gke.shfor GKE, but there's no symmetricinit_cnpg_k3d.sh. Either: (a) Junie writes a thin one that justkubectl apply -fs the CNPG release manifest with no GCP/IAM steps, or (b) the existing Python orchestratorknoe.core.ops.cloudnative_pgalready handles k3d (per the survey, it does —_strip_k3d_synology_blocksis in there). Verify which during Phase 1. - Schema bootstrap. The GKE flow seeds
knoe.*viaetc/init_knoe_auth.shrunning SQL against the CNPG primary throughkubectl exec. The same approach works in k3d; the brief should reuse the script (with aKUBECONTEXTswap), not duplicate it.
8. Verification (when Phase 1 is "done")
A new engineer, freshly cloning the repo on a Mac:
git clone <repo> && cd knoe-db
make k3d-knoe-up # provisions cluster, CNPG, KDC, schema
make k3d-knoe-pf & # port-forward in background
mvn -pl authority spring-boot:run
# in another terminal:
curl http://localhost:8080/health
# → ok
curl http://localhost:8080/.well-known/openid-configuration
# → JSON, issuer "http://localhost:8080"
Total elapsed time on a fresh laptop: ≤ 8 minutes. The engineer can
now edit authority/src/main/java/dev/knoe/auth/... and see changes
without leaving their editor.
9. Glossary
- k3d —
k3srunning inside a Docker container. Lightweight Kubernetes for laptops; no VM needed. - CNPG —
cloudnative-pg, a PostgreSQL Kubernetes operator. We run a single instance in k3d, three in GKE. - KDC — Key Distribution Center, the Kerberos protocol's auth server. SPNEGO browsers/clients fetch tickets from it; knoe-auth validates SPNEGO challenges using these.
- KNOE.LOCAL — the Kerberos realm name we use for laptop dev.
Distinct from
KNOE.DEV(production) so a stray ticket from one doesn't authenticate you on the other. - SPNEGO —
NegotiateHTTP auth scheme. Carries a Kerberos ticket inside an HTTP header. - Phase 2.5 — colloquial name for the gap between Phase 2 OIDC (in-memory codes) and a fully persistent OIDC provider (DB-backed codes/sessions). Not formally numbered; will get a name when work starts.