---
id: w4/m7/t003
title: "Kratos values: enable courier from secret + verification flow; local overlay targets Mailpit"
worker: worker4
status: done
estimate: 35m
depends_on: [w4/m7/t002]
---

## Objective

Flip the two switches the values file has been carrying TODOs for: `courier.enabled: true` with the SMTP URI delivered via the `kratos` Secret key `smtpConnectionURI` (t001 — values carry only a non-secret placeholder in `courier.smtp.connection_uri` to gate the env injection), and `selfservice.flows.verification.enabled: true` **in both the base and the local overlay values** (the overlay re-declares `enabled: false` and Argo merges valueFiles later-wins). Locally the Mailpit URI comes from the local `auth-secrets.sh` run, not overlay values.

## Context

- `deploy/gitops/base/values/kratos.values.yaml` — the `recovery` block (enabled, with the "can't deliver until courier.smtp lands" comment), `verification` block (`enabled: false`), and `courier` block (`enabled: false`); line numbers drifted after the m9/m19 edits, cite blocks not lines.
- **Precondition (2026-07-11):** the m19 sweep pins kratos/hydra deployments+jobs and the CNPG auth DBs to `bex.co/pool=platform` — a label no CAPD mock node carries, so step 3's sync leaves the substrate Pending on a fresh mock. Blocked on the `w1` inbox note on mock pool-labels (mock node labeling or a local-overlay nodeSelector sweep — which also matches the docs/ADR012-auth.md local control-plane-pin quirk); alternatively verify against prod.
- Courier chart mechanics (0.62.1): `courier.enabled: true` renders a **separate courier StatefulSet** whose scheduling reads `.Values.statefulSet.nodeSelector/tolerations` — keys the m19 sweep never pinned (it pinned only `deployment:` and `job:`). Without a `statefulSet:` block the courier lands on the untainted tenant pool in prod and dies with tenant scale-downs.
- Local overlay values layering: `deploy/gitops/overlays/local/values/` (per docs/ADR012-auth.md §4); its kratos values re-declare `verification.enabled: false` — must be flipped there too.
- Recovery/verification `ui_url`s already point at the dashboard paths (note: the verification page itself doesn't exist yet — see the milestone README's 2026-07-11 note; API-flow proof only in t004).

## Steps

1. Base values: enable courier (`connection_uri` set to a non-secret placeholder — the real URI rides the `kratos` Secret key `smtpConnectionURI`), add a `statefulSet:` block with the same `bex.co/pool: platform` nodeSelector + `bex.co/platform` toleration as the m19 `deployment:`/`job:` pins, flip `verification.enabled: true`, remove the stale TODO comments.
2. Local overlay values: flip `verification.enabled: true` there too (later-wins merge otherwise leaves it off). The Mailpit URI does **not** go in overlay values — run the local `auth-secrets.sh` with `KRATOS_COURIER_SMTP_URI=smtp://mailpit.<ns>.svc:1025` (no TLS locally).
3. Sync on the mock (precondition above); Kratos healthy; courier logs show it is running.

## Files

- `deploy/gitops/base/values/kratos.values.yaml`
- `deploy/gitops/overlays/local/values/kratos.values.yaml` (or the overlay's inline override mechanism)

## Acceptance criteria

- [ ] Kratos pods healthy with courier enabled; no secret material in git.
- [ ] The courier StatefulSet carries the platform-pool nodeSelector + toleration in the rendered base output (in prod terms it schedules on a platform node — assert via `scripts/verify-substrate.sh` or a rendered-manifest check).
- [ ] `verification.enabled: true` in the **effective** merged config on the mock (both values files flipped).
- [ ] Triggering a recovery flow produces a message in Mailpit (manual check; scripted in t004).

## Out of scope

- E2E scripting (t004); SMTP provider account setup.
