Skip to content

feat(keycloak): prod techgarden realm + WS-05 theme, realm withheld (WS-02 H3) - #280

Merged
TechGardenCode merged 1 commit into
mainfrom
ws02/prod-identity
Aug 8, 2026
Merged

feat(keycloak): prod techgarden realm + WS-05 theme, realm withheld (WS-02 H3)#280
TechGardenCode merged 1 commit into
mainfrom
ws02/prod-identity

Conversation

@TechGardenCode

Copy link
Copy Markdown
Owner

WS-02 PR2 of 3 — identity. Merge only after #279.

This is the only change in WS-02 that touches a live shared IdP (it also fronts hausparty and kian.coffee), which is exactly why it is isolated into its own PR — a broken login is attributable to this diff instantly rather than bisected out of a large merge.

🔒 The go switch stays OFF

base/realms/techgarden-realm.json exists in this repo but is deliberately absent from configMapGenerator.files.

config-cli imports /config/*.json from that ConfigMap, so an unlisted file is never seen by Keycloak. While it stays unlisted, https://sso.techgarden.gg/realms/techgarden keeps returning 404 — the pass condition for this phase, not a failure.

The flip is exactly one line:

  - name: keycloak-realm-config
    files:
      - base/realms/accounts-realm.json
      - base/realms/hausparty-realm.json
      - base/realms/kian-coffee-realm.json
+     - base/realms/techgarden-realm.json

in kubernetes/clusters/1276-prod/keycloak/keycloak/kustomization.yaml. Nothing else in that directory needs to change — both substitution vars and both secret keys ship here.

Realm — derived from dev, not copied

Diffed field-by-field against the dev seed. Only these deltas exist; nothing else drifted.

# Delta Why it matters
1 Apex hostnames only — https://techgarden.gg/*, /oauth2/callback ADR-0006: products are routes, not subdomains. No dev.techgarden.gg survives anywhere.
2 fromDisplayName: "TechGarden" (dev: "TechGarden (dev)") A verbatim copy puts the wrong sender name on every production email.
3 registrationAllowed: false, declared not defaulted ADR-0052. No signup, no invite flow, no allowlist — that scope is deleted.
4 All four action-token lifespans reset-credentials 1800 · verify-email 43200 · admin 259200 · base 300. The base value alone expires forgot-password links in 5 minutes — the bug dev shipped and fixed in #276. Carrying three of four reproduces it.
5 The four dev personas dropped dev/power/casual/fresh are a dev fixture with out-of-band passwords. Copying them seeds four test accounts into the IdP that also fronts hausparty and kian.coffee. Prod gets the owner only.

Carried over from WS-03: confidential client techgarden-gateway, revokeRefreshToken: true, dev-settled 30-day idle / 60-day cap.

Differs from the brief: also carries the ai realm role from #278 (WS-27), which landed after the brief was written. Owner-only, held by kian, non-composite and lowercase as its description requires.

Theme — 17 files, byte-identical to dev

11 login + 6 email, verified diff -r clean against the dev copy. Mounted via spec.unsupported.podTemplate at /opt/keycloak/themes/techgarden/, safe because the 26.6.3 image ships only a README.md there (the real themes live inside a jar, so parent=base keeps resolving).

Two ConfigMaps, not one — one cannot work. configMapGenerator keys off each file's base name and ConfigMap keys cannot contain /, so theme.properties and messages_en.properties collide across login/ and email/.

techgarden.gg/theme-revision: "1". Prod's counter is independent — dev is on 2. With disableNameSuffixHash: true the ConfigMap names never change, so the operator never notices new bytes; without this annotation the pod silently keeps serving the old theme. Bump it on every change under base/themes/.

Nothing references the theme until the realm is enabled, so it is inert for the three live realms.

Substitution wiring — the sharpest edge

IMPORT_VARSUBSTITUTION_ENABLED=true over IMPORT_FILES_LOCATIONS=/config/*.json means substitution runs across the whole batch: a seed referencing an unwired variable fails substitution for every realm file at once, not just its own.

So KC_CLIENT_TECHGARDEN_GATEWAY_SECRET and KC_SMTP_PASSWORD are wired now (prod's Job goes 4 → 6 KC_* vars), together with the two new keycloak-realm-secrets keys. The flip therefore cannot take hausparty and kian.coffee down with it.

📧 Resend: prod SHARES the dev sender

There is exactly one resend entry in the whole BWS org (generically named, not dev-), and the free tier allows exactly one verified sending domain. No prod SMTP secret was ever created. So prod reads the same BWS entry dev reads — the H4 parity pattern, one entry read twice.

Consequence: dev and prod share the 3,000/month · 100/day allowance, which makes ADR-0052's fail-closed-on-429 a property the two environments share — dev traffic can exhaust prod's quota. Splitting them needs a paid plan and a second verified domain.

Validation

kustomize build green. Asserted on the rendered output:

  • techgarden-realm.json absent from keycloak-realm-config (keys are exactly the three live realms)
  • ✅ login ConfigMap 11 keys · email ConfigMap 6 keys
  • theme-revision == "1"
  • ✅ all 6 KC_* vars resolve to keys that exist on the ExternalSecret
  • ✅ all 4 pre-existing KC_* vars still wired
  • ✅ no apex techgarden.gg HTTPRoute; both httproute*.yaml byte-untouched (the only eg-public route is Keycloak's own pre-existing sso.techgarden.gg)

⚠️ Reviewer note

Merging rolls the Keycloak StatefulSet (podTemplate change), 2 instances, rolling. Done-condition: hausparty and kian.coffee logins still work after the roll — I verify both immediately after merge.

🤖 Generated with Claude Code

https://claude.ai/code/session_015tcbrNifvkc8ryk4g2M7XK

…WS-02 H3)

Adds everything the prod techgarden realm needs EXCEPT the one line that turns it
on. This is the only change in WS-02 touching a live shared IdP, so it is isolated:
a broken hausparty or kian-coffee login is attributable to this PR instantly rather
than bisected out of a large merge.

THE GO SWITCH: base/realms/techgarden-realm.json exists but is deliberately absent
from configMapGenerator.files. config-cli imports /config/*.json from that
ConfigMap, so an unlisted file is never seen by Keycloak and
https://sso.techgarden.gg/realms/techgarden keeps returning 404 — the pass
condition for this phase, not a failure. The flip is that one line.

Realm — DERIVED from the dev seed, not copied. Verified deltas vs dev, and nothing
else drifted:
  1. apex hostnames only — https://techgarden.gg/* and /oauth2/callback (ADR-0006:
     products are routes, not subdomains). No dev.techgarden.gg anywhere.
  2. fromDisplayName "TechGarden", not "TechGarden (dev)" — a verbatim copy would
     put the wrong sender name on every production email.
  3. registrationAllowed: false, declared not defaulted (ADR-0052).
  4. all FOUR action-token lifespans carried (reset-credentials 1800, verify-email
     43200, admin 259200, base 300). The base value alone would expire
     forgot-password links in 5 minutes — the bug dev shipped and fixed in #276.
  5. THE FOUR DEV PERSONAS DROPPED. dev/power/casual/fresh are a dev fixture with
     out-of-band passwords; this IdP also fronts hausparty and kian.coffee. Prod
     gets the owner only.
Also carried from WS-03: confidential client techgarden-gateway,
revokeRefreshToken: true, 30-day idle / 60-day cap. And the `ai` realm role from
#278, which landed after the brief was written (owner-only, held by kian).

Theme — 17 files (11 login, 6 email), byte-identical to dev, mounted via
spec.unsupported.podTemplate at /opt/keycloak/themes/techgarden/. TWO ConfigMaps,
not one: configMapGenerator keys off each file's base name and ConfigMap keys
cannot contain "/", so theme.properties and messages_en.properties collide across
login/ and email/. theme-revision starts at "1" — prod's counter is independent of
dev's "2" and the two must never be synced up. Nothing references the theme until
the realm is enabled.

Substitution wiring — the sharpest edge. IMPORT_VARSUBSTITUTION_ENABLED is true
over /config/*.json, so a seed referencing an unwired variable fails substitution
for EVERY realm file in the batch at once. KC_CLIENT_TECHGARDEN_GATEWAY_SECRET and
KC_SMTP_PASSWORD are therefore wired NOW, alongside the two new ExternalSecret
keys, so the flip cannot take hausparty and kian-coffee down with it.

Resend: prod SHARES the dev sender. The free tier allows one verified domain and
there is exactly one `resend` entry in the BWS org — so both environments share the
3,000/month cap and ADR-0052's fail-closed-on-429 becomes a shared property.

Claude-Session: https://claude.ai/code/session_015tcbrNifvkc8ryk4g2M7XK
@TechGardenCode
TechGardenCode merged commit 62f859f into main Aug 8, 2026
6 checks passed
@TechGardenCode
TechGardenCode deleted the ws02/prod-identity branch August 8, 2026 23:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant