Bringing the long-running session-feature branch back into main in one deliberate sweep. The branch carried the cluster work that's been live for weeks (cross-cluster CNPG metrics, Grafana w/ Google OAuth, supabase oauth2-proxy, cluster recovery, pg.0.knoe.dev + per-engineer onboarding, GCS-backed CNPG backups via Workload Identity, the env-contamination guard, the Junie brief queue, the cnpg-grafana CSRF + memory-request fixes from today), while main accumulated Junie's parallel knoe-auth Phase 2 OIDC work (full provider surface: discovery, authorize, token, userinfo, JWKS, RS256 signing, code exchange, session services). Key decision: the two branches did COMPETING rebrands off the same starting point (5ba9b63, 2026-04-27): - claude branch (commit b355855, earlier): org.prole.authority.* → dev.knoe.auth.* (artifact renamed to knoe-auth.jar) - main (commit9daa94b, recent): org.prole.authority.* → dev.knoe.authority.* (kept "authority" artifact name) dev.knoe.auth wins: cluster runs from this name, the Maven artifact is already knoe-auth.jar, and the broader rename is the documented namespace direction (per ~/.claude/projects/-Users-chrisfu-dev-knoe-db/ memory/MEMORY.md). All of main's recent Phase 2 OIDC content was ported from authority/src/.../dev/knoe/authority/ into authority/src/.../dev/knoe/auth/ with package declarations rewritten. == File-level resolution summary == Textual conflicts (4): authority/pom.xml - Took our artifactId="auth" - Took our branch's removal of spring-security-kerberos-client (verified: Junie's Phase 2 OIDC code does not import it; the dep was already-dead config) docs/pipeline-phases.md - Took our branch's "Phase 1 not started" status. Main had a misplaced "✅ Complete" with a knoe-auth-Phase-1 commit ref in the autobuild Phase 1 section — different domain. docs/plans/knoe-auth-round-1.md - Took our branch's dev.knoe.auth file table (vs main's dev.knoe.authority listing). Pure rename mismatch. supabase/helm/knoe-supabase/templates/kong/config.yaml - Took our branch's onboard route + plain dashboard wiring. Main had an oauth2proxy.enabled toggle that put oauth2-proxy as a Kong upstream — but the deployed architecture (commit 25f1b2e) has oauth2-proxy in FRONT of Kong, not behind. Main's wrapper reflected an architecture that was never deployed. - Took our branch's removal of basic-auth from dashboard route (queue #15 brief still tracks the matching values.yaml / kong/deployment.yaml cleanup). Java tree reconciliation (44 file-pairs): 20 dual-path source files + 2 dual-path tests Body-identical between main's authority/ and our branch's auth/ after stripping package decls — main's commit9daa94bwas a pure rebrand. Took our branch's auth/ version for all 22. 8 main-only source files (Phase 2 OIDC), ported into auth/: web/JwksController.java web/OidcAuthorizeController.java web/OidcDiscoveryController.java web/OidcTokenController.java web/OidcUserInfoController.java session/OidcCodeService.java session/OidcTokenService.java session/SessionService.java 12 main-only test files, ported into auth/: HealthControllerTest.java enroll/EnrollValueTypesTest.java enroll/EnrollmentControllerTest.java enroll/TotpServiceTest.java kerberos/KadminClientTest.java kerberos/KerberosSpnegoResultTest.java web/LoginControllerTest.java admin/AdminControllerTest.java user/PrincipalNormalizerTest.java regression/IdentityRegressionTest.java session/OidcCodeServiceTest.java session/SessionServiceTest.java Port mechanics: read main:authority/...<file> via git show, then sed rewrite `package dev.knoe.authority` → `package dev.knoe.auth` and `import dev.knoe.authority` → `import dev.knoe.auth`. Body content unchanged. authority/src/main/java/dev/knoe/authority/ — DELETED (duplicate) authority/src/test/java/dev/knoe/authority/ — DELETED (duplicate) == Verification == - grep -rln '<<<<<<<' across .java/.md/.yaml/.yml/.sh/.xml/.tpl: clean - find authority/src -path '*/dev/knoe/authority*': empty (subtree gone) - grep 'package dev.knoe.authority' across repo: clean - bash -n install.sh deploy.sh etc/preflight_kubecontext.sh: clean - git ls-files -u | wc -l: 0 unmerged paths - helm lint supabase/helm/knoe-supabase: pre-existing failure on studioIngress.enabled undefined in values.yaml (introduced by Junie on main; unrelated to this merge — flagging as follow-up). == Followups (carried into TODO ranked queue or noted here) == - helm lint failure: studioIngress block in values.yaml is missing enable flag; templates/studio/{ingress,oauth2proxy-deployment, oauth2proxy-service}.yaml all reference studioIngress.enabled with no default. Pre-existing on main; not introduced by this merge. - The five Junie briefs filed on this branch are now reachable from main at docs/plans/junie/{02,06,07,13,15}-*.md. Junie can pick them up in any order. - knoe-auth Phase 2 OIDC source (now at dev.knoe.auth.*) is not yet deployed to the cluster. Deployment is its own task. - The branch claude/crazy-bose-fec256 stays in place (worktree at .claude/worktrees/crazy-bose-fec256 may have ongoing context for Claude Code sessions). Safe to delete once next session starts cleanly from main. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
13 KiB
Deployment Modes — Welcome Screen + Wizard Fast-Path
Status: Implemented and operational. Architectural reference. Welcome-screen mode selector and min-mode fast-path shipped in Phase 0.
Owner: chrisfu
Audience: Jr/mid engineer onboarding to knoe.dev. Familiarity with Tk/tkinter helps but is not required.
1. Context
The knoe-db installer (./install.sh / ./knoe.sh install, driven by the Tk UI in knoe/ui/screens/) supports four ways to deploy the platform. Different audiences, different hardware, but the same architecture underneath.
Earlier installer revisions assumed the engineer already knew which mode they wanted, presented a welcome screen that was a wall of text, and shipped a sidebar tab strip (k3d / k3s / k8s) that didn't reliably refresh state across screens. Phase 0 replaced all of that with a first-screen mode selector that teaches the engineer what each mode is before asking them to choose, and added a min-mode fast-path that skips wizard screens irrelevant to a single-container deployment.
The four modes
| Mode | What it is | Who it's for | Required tools |
|---|---|---|---|
| min | A single knoe-db container under containerd — no Kubernetes, no Docker. The Spring Boot app runs locally on the host. |
Engineers running a Spring app on a laptop and wanting a CNPG-compatible Postgres without a full cluster. | Homebrew, 1Password |
| k3d | A Docker-based local k3s cluster. Full CNPG + optional Supabase / ArgoCD / Gitea / GitLab. | Local development with the full platform. | Docker, Homebrew, 1Password |
| k3s | A multi-node k3s cluster on real hardware. Full Kerberos auth stack, Garage S3, monitoring. Mirrors the GKE shape. | Homelab contributors with their own hardware (e.g. a 3-node Pi cluster). | k3sup, Homebrew, 1Password |
| gke | The production dual-cluster on Google Cloud (knoe-dev-0 + knoe-dev-cnpg-0 in us-west3). |
Production. | gcloud, kubectl, 1Password |
The architectural commitment matters: all four modes are scaled-down mirrors of the GKE shape. Not parallel implementations of the same idea, not divergent forks — the same components composed at different scales. CNPG runs in all four. The Spring Boot authority service runs in all four. knoe.user lives in the same schema in all four. The only thing that changes is what platform the components run on and which optional services are enabled.
Mode → cluster_env mapping
cluster_env |
KNOE_MODE |
Target |
|---|---|---|
dev |
k3d |
Local K3d cluster |
service |
k3s |
On-prem K3s cluster |
prod |
k8s (alias gke) |
GKE (or other cloud) |
min |
min |
Local containerd (no Kubernetes) |
The conversion lives in knoe/core/env.py (_deployment_mode_from_env()).
Config file mapping
knoe/knoe_conf.py maps environments to config files under conf/:
| Mode | Config file |
|---|---|
min |
conf/min.cfg |
k3d (dev) |
conf/k3d.cfg |
k3s (service) |
conf/k3s.cfg |
gke (prod) |
conf/gke.cfg |
Config is layered: env-specific file overrides base. KNOE_CONF env var or conf/service/ subdirs point to the active config.
2. How it's wired — file map
| Component | Location | What's there |
|---|---|---|
| Welcome screen with mode selector | knoe/ui/screens/welcome.py |
WelcomeScreenMixin._render_welcome_page() renders the four mode cards. Click handler updates deployment_mode and cluster_env, redraws card borders, re-evaluates the Next gate. |
| Sidebar nav | knoe/ui/screens/navigation.py |
_create_sidebar_nav() no longer renders the old k3d/k3s/k8s tab strip — the welcome card selector replaces it. _set_deployment_mode is still called from the welcome handler. |
| Mode/state initialization | knoe/ui/screens/__init__.py |
nav_items list, deployment_mode = tk.StringVar(...), cluster_env = tk.StringVar(...). |
| Wizard transitions | knoe/ui/screens/navigation.py on_next / on_prev |
Min-mode fast-path skips screens that don't apply (network scan, cluster init, Kerberos, ArgoCD, Supabase, common services). |
| Update-footer Next-button gate | knoe/ui/screens/navigation.py update_footer |
Welcome screen disables Next until a mode is selected. |
| Min-mode bootstrap | etc/init_min.sh |
Idempotent: ensures Homebrew + containerd, pulls and runs the knoe-db container locally, runs schema bootstrap, prints connection info. Invoked from init_scripts step in min mode. |
| Min-mode config | conf/min.cfg |
Modeled on conf/k3d.cfg. Just what init_min.sh needs. |
| Min-mode lifecycle | knoe.sh (start / stop / restart subcommands) |
Manages the local containerd knoe-db instance for users who don't want to run the wizard every time. |
3. UI layout — welcome screen
┌──────────────────────────────────────────────────────────────────┐
│ │
│ knoe.dev │
│ infrastructure.auto() │
│ │
│ Welcome │
│ ─────── │
│ Knoe.DB sets up a production-grade PostgreSQL cluster with │
│ Kerberos authentication. Pick the mode that matches your │
│ hardware — we'll walk you through every step. │
│ │
│ ┌────────────┬────────────┬────────────┬────────────┐ │
│ │ min │ k3d │ k3s │ gke │ │
│ │ Just a │ Local │ Homelab │ Production│ │
│ │ Database │ Cluster │ │ │ │
│ │ One knoe-db│ k3s-in- │ Multi-node │ Dual GKE │ │
│ │ container │ Docker │ k3s on real│ clusters on│ │
│ │ via │ cluster on │ hardware. │ Google │ │
│ │ containerd.│ your Mac. │ Full │ Cloud. │ │
│ │ No K8s. │ Full CNPG │ Kerberos │ CNPG + │ │
│ │ Perfect for│ database — │ auth stack,│ GCS backups│ │
│ │ a Spring │ add │ Garage S3, │ + Workload │ │
│ │ app on your│ Supabase, │ monitoring.│ Identity. │ │
│ │ laptop. │ ArgoCD, │ │ │ │
│ │ │ Gitea, or │ │ │ │
│ │ │ GitLab. │ │ │ │
│ │ Homebrew + │ Docker + │ k3sup + │ gcloud + │ │
│ │ 1Password │ Homebrew + │ Homebrew + │ 1Password │ │
│ │ │ 1Password │ 1Password │ │ │
│ └────────────┴────────────┴────────────┴────────────┘ │
│ │
│ You'll need: Docker · Homebrew · 1Password │
│ │
│ [ Next → ] │
└──────────────────────────────────────────────────────────────────┘
Behavior:
- The four cards are clickable.
- The selected card gets a colored border in the mode's accent color (
_MODE_COLORS:minpurple,k3dblue,k3sgreen,gkeorange). - The "You'll need:" line below the cards updates to reflect the selected mode's tools.
- The Next button is disabled until a mode is selected.
- Selecting a card also sets
cluster_envvia the mode-to-cluster_env map. - On Next, mode is persisted to
knoe_cfg_data["Global"]["DEPLOYMENT_MODE"].
4. Wizard transitions — min-mode fast-path
Default flow (k3d / k3s / gke)
welcome → network_scan → env_setup → init_cluster → cluster_nodes →
init_password → init_scripts → kerberos_config → knoe_users →
argocd_config → gitops_config → supabase_config → common_services →
… → security
Min-mode flow
welcome → dependencies → environment → init_password → init_scripts → security
Skipped in min: network_scan, init_cluster, cluster_nodes, kerberos_config, knoe_users, argocd_config, gitops_config, supabase_config, common_services. None of them apply when there's no Kubernetes cluster and no Kerberos KDC. init_scripts invokes etc/init_min.sh instead of the cluster-mode init scripts.
The fast-path is implemented as a _min_mode_next dictionary in knoe/ui/screens/navigation.py consulted from on_next / on_prev. The override is only active when _is_min_mode() returns true.
5. Verification
Run after any change touching the wizard or mode logic.
./install.shopens. The welcome screen shows four mode cards. The sidebar has no k3d/k3s/k8s tab strip.- With no mode selected, the Next button is disabled.
- Click the min card. Next becomes enabled. Click Next.
- The wizard skips the network-scan screen.
- It skips
init_clusterandcluster_nodes. - It skips
kerberos_config,knoe_users,argocd_config,gitops_config,supabase_config,common_services. - It runs
init_min.shat theinit_scriptsstep. - It lands on the
securityscreen. - Going back through
on_prevwalks the same trimmed sequence in reverse without hitting any skipped screens.
- Click the k3d card on a fresh run. The wizard follows the existing k3d flow exactly as before.
python3 -c "from knoe.ui.screens import KnoeInstaller"— no import errors.- After completing min mode,
nerdctl psshows theknoe-dbcontainer running locally andpsql "${printed_conn_string}" -c '\dt knoe.*'lists theknoe.usertable. ./knoe.sh stopand./knoe.sh startcleanly stop/restart the local containerd instance.
6. Out of scope
- A
min-mode equivalent of the full Kerberos auth stack. Min is "just a database". Auth in min is a follow-up. - Migrating an existing
k3ddeployment tomin(or any other mode-to-mode migration). Each mode is an independent target. - A unified package format that produces all four mode-specific installers from one
pyinstallerbuild. The current per-mode build is fine. - Letting one wizard run install multiple modes (e.g. set up
k3dandgkefrom the same session). One mode per run.
7. Glossary
k3s — A lightweight Kubernetes distribution by Rancher Labs. Single binary, runs comfortably on a Raspberry Pi. The same project, two delivery shapes: k3d is k3s wrapped in Docker; bare k3s is k3s on actual hardware.
k3d — A wrapper that runs k3s clusters inside Docker containers. Useful for local dev because it gives you a "real" Kubernetes cluster without having to provision VMs.
GKE (Google Kubernetes Engine) — Google Cloud's managed Kubernetes. We use Standard for knoe-dev-0 and knoe-dev-cnpg-0. See CLAUDE.md for cluster topology.
containerd — A container runtime. Speaks the OCI image and runtime specs. Both Docker and Kubernetes use containerd under the hood. In min mode we talk to it directly via nerdctl, skipping Docker entirely.
nerdctl — A Docker-CLI-compatible client for containerd. If you know docker run, you know nerdctl run.
CNPG (CloudNativePG) — The Postgres operator we use for knoe-db. See CLAUDE.md for which cluster runs it in production.
cluster_env — A label the installer carries internally that picks which conf/*.cfg file to read and which downstream config branches to take. Maps from deployment_mode per the table in §1. Don't reuse this name for new variables — it's already overloaded enough.
Tk / tkinter — Python's standard GUI toolkit. The installer is built on it. Canvas-based rendering means we draw text and shapes directly rather than using Tk's widget hierarchy for the welcome screen.
Wizard / pid — The installer is a multi-screen wizard. Each screen has a "page id" (pid) — welcome, network_scan, init_cluster, etc. nav_items is the ordered list; on_next / on_prev decide transitions based on the current pid and the user's state.