New DEPLOY-runbook-2026-09-03.md, separate from the 2026-08-04 runbook.
Names the five new OKTA_* env vars, treats the pre-deploy backup as the
only way back once 1d60a608bb51 (drop_local_password) commits since its
downgrade() restores the column but not the data, and splits Rollback
into the fixable case (Okta app integration misconfigured, fix and
redeploy api, no data at risk) versus the severe case (abandoning Okta
for local-password code, which only a destructive backup restore can
reach). States D16's no-break-glass posture plainly.
D17 records the decision and why: staged-deploy-sequence docs and
backlog-only were both considered and declined in favor of the runbook.
Logged to backlog.md rather than fixed here: okta_auth.describe() has
no caller (BL-028), users.failed_attempts/locked_until are vestigial
(BL-029), DEPLOY-login-portal.md is fully stale (BL-030).
Decision, raised during T10.4 hazard review:
- Admin bootstrap: manage_users.py moves from creating an admin account to
promoting an existing one, by username, on a row Okta's JIT provisioning
(T10.3) already created. Rejected blind account creation — the exact
OKTA_IDENTITY_CLAIM format is still unconfirmed by security, and a
hand-typed username that doesn't match it produces an orphaned second
account instead of promoting the real one. Ongoing (non-bootstrap) admin
naming needs no new work: html/users.js's existing role dropdown already
handles it.
- Break glass: none, by design, matching the precedent already on record
for the abandoned LDAPS design (D13/D14) rather than assumed to carry
over untested. If Okta is unreachable, the app is unreachable for
everyone until Okta is restored. Rejected a toggleable emergency local
login — it would reintroduce the stored credential D15 exists to
eliminate.
Also corrects T10.4's scope in wave-10.md: hazard review found real call
sites of hash_password/verify_password/password_problem the original
bullet didn't name (create_user(), admin_reset_password(), users.js's
admin forms, browser_check.py/launcher_check.py fixtures), plus a
verification-gate ordering problem (smoketest.py and seed_demo.py
authenticate via POST /api/auth/login, which T10.4 removes, and both are
named explicitly in CLAUDE.md's verification section). Fixed by having
T10.4 switch both scripts to mint a session with auth.create_token()
directly, the same technique browser_check.py already uses, rather than
waiting on T10.7.
D16