0069 — MFA-exempt the pinned smoke/test account

Context

Site-wide MFA enforcement (ADR-era #1403, May 2026) requires every authenticated session to clear two gates in src/middleware.ts: an enrollment gate (a method must be registered, else → /setup/mfa) and a per-session challenge gate (mfaSatisfied, else → /login/mfa).

The shared automation identity smoke@propflowai.co — used by the /browse skill and e2e/auth.setup.ts — has no inbox/SMS a script can read, so its only satisfiable second factor was TOTP via a shared secret stored in four places (DynamoDB twoFactor row, Vercel env SMOKE_TOTP_SECRET, GitHub Actions secret, local Keychain/file). That secret drifted repeatedly through May–June 2026: any flow that re-enrolled or reset smoke@'s 2FA (the MFA e2e suite, a manual /setup/mfa, scripts/enroll-smoke-totp.ts, /api/test/reset-mfa) rewrote the DynamoDB row with a fresh secret while the four stored copies kept the old one, breaking /browse with 401 INVALID_CODE. A string of band-aids followed (enroll script, double-encryption fix, repair-smoke-totp.ts, moving nightly to smoke-nightly@, moving the MFA e2e suite to mfa-e2e@). Each treated the symptom; none removed the driftable secret itself. As of this writing smoke@ has zero twoFactor rows — it had been wiped again and was being bounced to /setup/mfa.

Decision

Exempt one hard-pinned account from the middleware MFA gates instead of trying to keep a shared TOTP secret in sync.

In the authoritative session-resolve block of src/middleware.ts, when process.env.SMOKE_MFA_EXEMPT_USER_ID is set and the resolved direct.userId is strictly equal to it, force mfaEnabled = true and mfaSatisfied = true. That clears both gates so the account is never routed to /setup/mfa or /login/mfa, regardless of its actual 2FA enrollment state — so there is no second factor to keep in sync and nothing to drift.

The guard is:

smoke@propflowai.co is a read-only admin on the demo tenant with no customer data; the worst case if the env var ever leaked or were mis-set is access to that one demo account — the same blast radius as a leaked TOTP secret, minus the recurring breakage.

This ADR proposes no new export interface, so the entity-classification table is omitted.

Consequences

Alternatives considered