prole/docs/plans/deployment-modes.md
chrisfu 00f0ebec07 Merge claude/crazy-bose-fec256 into main
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 (commit 9daa94b, 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 commit 9daa94b was 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>
2026-05-01 16:39:10 -07:00

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: min purple, k3d blue, k3s green, gke orange).
  • 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_env via 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.

  1. ./install.sh opens. The welcome screen shows four mode cards. The sidebar has no k3d/k3s/k8s tab strip.
  2. With no mode selected, the Next button is disabled.
  3. Click the min card. Next becomes enabled. Click Next.
    • The wizard skips the network-scan screen.
    • It skips init_cluster and cluster_nodes.
    • It skips kerberos_config, knoe_users, argocd_config, gitops_config, supabase_config, common_services.
    • It runs init_min.sh at the init_scripts step.
    • It lands on the security screen.
    • Going back through on_prev walks the same trimmed sequence in reverse without hitting any skipped screens.
  4. Click the k3d card on a fresh run. The wizard follows the existing k3d flow exactly as before.
  5. python3 -c "from knoe.ui.screens import KnoeInstaller" — no import errors.
  6. After completing min mode, nerdctl ps shows the knoe-db container running locally and psql "${printed_conn_string}" -c '\dt knoe.*' lists the knoe.user table.
  7. ./knoe.sh stop and ./knoe.sh start cleanly 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 k3d deployment to min (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 pyinstaller build. The current per-mode build is fine.
  • Letting one wizard run install multiple modes (e.g. set up k3d and gke from 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.