Date: 2026-05-03 UTC
Author: PM agent (raxx-pm-bot)
Locked direction (Kristerpher 2026-05-03):
- Hybrid IDP: Google Workspace for all operator surfaces; passkeys remain primary for Antlers customer surfaces
- Programmatic: per-caller scoped service tokens (Velvet D3, already locked in ADR-0037–0041)
- Workspace 2FA enforced at Google level for all operator identities
- CF Access at edge for every operator surface (outer perimeter, non-negotiable)
Architect design doc: docs/architecture/auth-unification.md (landed 2026-05-03)
ADR: ADR-0042 (companion to this doc)
Parent epics: #907 (Velvet), #146 (console), #651 (support), #81 (SDLC/security hardening)
Cards from TradeMasterAPI repo only (cross-repo noise filtered). Cards are grouped by surface.
raxx.app)| # | Title | State | Key Labels | Summary |
|---|---|---|---|---|
| #128 | raxx.app passkey signup and login screens | OPEN | blocked, needs-grooming | Customer-facing passkey enrollment + login UI. Correct per Direction C. |
| #111 | WebAuthn login endpoint + session issuance (Raptor) | OPEN | blocked, needs-grooming | Backend WebAuthn login. Customer surface, correct. |
| #113 | Passkey / device management UI + endpoints (Antlers + Raptor) | OPEN | blocked, needs-grooming | Customer passkey management. Customer surface, correct. |
| #112 | Email verification service + /api/auth/email/verify endpoint | OPEN | area:backend-v2 | Email verification step after WebAuthn registration. Correct per invariant I3. |
| #670 | feat(support): multi-passkey enrollment + backup codes | OPEN | groomed, ready-for-dev | A+B recovery policy implementation. Customer surface. Correct. |
| #272 | Client-side E2E key derivation from passkey PRF | OPEN | needs-grooming | PRF extension for E2E encryption. Customer surface. |
| #173 | Implement passkey sign-in flow on iOS (AuthenticationServices) | OPEN | blocked, type:ios | iOS passkey. Customer surface. Correct per ADR-0001. |
| #174 | Serve AASA file from Raptor for iOS passkey association | OPEN | area:security | iOS AASA. Customer surface. |
| #250 | Epic: Passkey-keyed end-to-end encryption for customer data | OPEN | type:epic, blocked | PRF E2E encryption epic. Customer surface. |
| #472 | Bind passkey sign-up UI to WebAuthn registration endpoints | CLOSED (2026-04-30) | ready-for-dev | Completed. |
console.raxx.app)| # | Title | State | Key Labels | Summary |
|---|---|---|---|---|
| #146 | Epic: raxx-console — operator admin console | OPEN | type:epic, area:security | Parent epic for console. Encompasses all console auth work. |
| #620 | feat(console): admins-online — sessions, revoke per session | OPEN | area:security | Session management UI in console. Correct. |
| #300 | M11: Token rotation UI — secrets index, TOTP re-elevation, rotate-now flow | OPEN | area:security | TOTP elevation for rotation. CONFLICT — see demote list. |
| #254 | raxx-console: secret rotation dashboard + on-demand rotation UI | OPEN | area:security | Precursor to Velvet. Partially superseded. |
| #766 | Add console-billing-read RBAC role and middleware guard | OPEN | area:security | RBAC role for billing. Correct per RBAC design. |
| #954 | feat(velvet/ui): yaml-driven revocation auth gate (group/role/user) | OPEN | area:security, sprint:next | Velvet revocation auth gate. Correct per Velvet v2. |
tickets.raxx.app)| # | Title | State | Key Labels | Summary |
|---|---|---|---|---|
| #651 | Epic: support.raxx.app — public customer support portal | OPEN | type:epic | Parent epic for all FreeScout work. |
| #707 | epic(support): FreeScout productionization — first-customer-email readiness | OPEN | type:epic, groomed | FreeScout production hardiness. No auth conflict. |
| #695 | ops(security): tickets.raxx.app/admin now requires WebAuthn second factor | OPEN | groomed, type:docs | Doc card for WebAuthn-on-CF. CONFLICT — see demote list. |
| #725 | ops(support): FreeScout activity log retention policy | OPEN | area:security | Retention policy. Correct. |
| #712 | feat(support): brand FreeScout outbound email templates | OPEN | area:devops, blocked | Email branding. Not auth-related. |
| # | Title | State | Key Labels | Summary |
|---|---|---|---|---|
| #453 | feat: sync Founders waitlist to CF Access policy on raxx.app | OPEN | blocked, type:security | Pre-GA customer allowlist sync. Customer surface; no conflict with hybrid IDP. |
| #692 | ops: add raxx-agent-vault-access service token to CF Access policies | OPEN | type:infrastructure | Service token enrollment. Correct, applies to programmatic callers. |
| #948 | feat(velvet/adapters): CF User API + Access service-token adapters | OPEN | sprint:next, needs-grooming | Velvet CF adapter. Correct per Velvet v2. |
| #568 | reliability: configure TOTP/passkey MFA on CF Access | CLOSED (2026-04-30) | groomed | Implemented. TOTP/passkey MFA on Class 3/4 apps. Superseded by hybrid IDP direction — CF Access TOTP is transitional; Google Workspace 2FA is target. |
| #393 | feat(console): Mode A rotation for CF Access service tokens | CLOSED (2026-04-27) | groomed | Completed. |
| # | Title | State | Key Labels | Summary |
|---|---|---|---|---|
| #907 | epic(velvet): centralized token + rotation service | OPEN | type:epic, groomed | Parent Velvet epic. Per-caller service tokens (D3 locked). Correct. |
| #912 | feat(velvet/auth): service-token auth middleware + rotation-authz matrix | OPEN | groomed, sprint:next | M1 auth middleware. Correct per D3. |
| #908 | feat(velvet): scaffold Heroku app pair + Postgres add-on | OPEN | groomed, sprint:next | Velvet scaffold. Correct. |
| # | Title | State | Key Labels | Summary |
|---|---|---|---|---|
| #334 | ops: contingent authorization for break-glass credentials | OPEN | area:security | Break-glass authz. Correct per ADR-0031 and hybrid model. |
| #500 | Epic: Workflow UUID tracing — replay + support transparency | OPEN | type:epic, area:security | Audit/tracing epic. Correct. |
| #515 | feat(trace): SC-12 Ed25519 subsystem signing | OPEN | area:security | Audit integrity signing. Correct. |
| #115 | Retention job + audit-log tamper-evidence digest | OPEN | area:security | Audit retention. Correct per ADR-0003. |
| #114 | GDPR DSR endpoints: export, erase, rectify, retention | OPEN | blocked, area:security | GDPR endpoints. Correct. |
Why: This card documents the state where CF Access uses email-OTP + WebAuthn as the MFA layer at CF Access. The locked hybrid direction replaces this: CF Access moves to Google Workspace OIDC as the IDP. Workspace 2FA (not CF Access–native WebAuthn prompts) is the 2FA mechanism. The "extend to console + vault" follow-up in item 2 of this card is now superseded by the architect's Phase 1 (#NV01: CF Access Google Workspace OIDC binding).
The doc content in #695 remains factually correct as a historical record of the transitional state (PR #633), but the active "extend" action item conflicts with the new direction.
Action: Add priority:low label. Post supersede comment (below). Do NOT close — the card's body documents the transitional state which is still live until Phase 1 is complete.
Why: This card specifies TOTP re-elevation as the step-up factor in the Console rotation UI. The hybrid auth direction makes Google Workspace 2FA the MFA mechanism for operators. "TOTP re-elevation" at the app layer inside the Console is a duplicate layer on top of Workspace 2FA. The Velvet revocation auth gate (#954) already covers the yaml-driven step-up confirmation for rotation — its auth gate is configurable (group/role/user) and does not specify TOTP as the factor.
Conflict specifics: The auth.py TOTP flow in the Console is called out as a potential removal target in architect's DQ-2 (open question). The card as written mandates building TOTP into the rotation UI — this is premature and may need to be removed if DQ-2 resolves to "Workspace 2FA only."
Action: Add priority:low label. Post supersede comment flagging the DQ-2 dependency. Do NOT close — the non-TOTP parts of the card (secrets index, rotate-now flow) are still valid and are subsumed into Velvet v2 cards.
Why: This card was the V1 precursor card for token rotation UI. It is substantially superseded by the Velvet epic (#907) and the Velvet v2 card slate (#908–#918, #948, #954). The Velvet cards are finer-grained, better groomed, and tied to the current architecture. #254 is not closed but it represents older, coarser scope.
Action: Add priority:low label. Post a note pointing to #907 as the active work. Do NOT close.
These cards cover the architect's Phase 1–3 work that is NOT yet represented by any existing open card. Do NOT file yet — Kristerpher reviews this slate first.
Phase: 1 (sprint:current — unblocks every downstream Phase 1 card)
Parent epic: #146 (console) + #81 (security hardening)
Surface: CF Access (all gated apps)
Summary: Create Google OAuth app in Cloud Console (scoped to openid email profile + Workspace Admin SDK). Configure CF Zero Trust Google Workspace OIDC IDP. Create four CF Access groups (cf-ops, cf-superadmin, cf-developer, cf-auditor). Replace one-time-pin policies with group-membership policies on console, vault, FreeScout, and internal-docs. 48-hour dual-IDP validation window before removing one-time-pin. Terraform codified.
Depends on: DQ-4 (group taxonomy decision) from architect's auth-unification.md §12.
Risk: Group taxonomy locked before this card starts or CF group recreation breaks all CF JWTs.
Phase: 1 (sprint:current — FreeScout license key in vault is the gate)
Parent epic: #651 (support epic), #707 (FreeScout productionization)
Surface: FreeScout (tickets.raxx.app)
Summary: Install FreeScout OAuth Login module (Tagras). Create separate Google OAuth2 app in Cloud Console. Store credentials in Infisical under /freescout/google-oauth. Configure: @raxx.app domain only, auto-create users OFF. Test: Kristerpher Google login matches existing FreeScout user. CF Access outer gate remains.
Depends on: NV01 (CF Access IDP swap), FreeScout license key must be in vault at /freescout/license-key.
Risk: FreeScout module version compatibility with current FreeScout install.
google_sub migrationPhase: 1 (sprint:next)
Parent epic: #146 (console epic)
Surface: Console (console.raxx.app)
Summary: Add google_sub + google_email columns to console_admins (migration 0NNN_console_google_oidc.sql). New /auth/google/callback route with PKCE, JWKS validation, hd claim check, google_sub bind-on-first-login. Audit events: auth.google_bind, auth.google_login, auth.google_unbind. Feature-flagged CONSOLE_GOOGLE_AUTH=1. Passkey remains primary. Google is fallback only.
Depends on: NV01 (CF Access IDP), DQ-1 decision (passkey primary vs Google-only).
Risk: DQ-1 unresolved means scope may shrink (passkey primary + fallback) or expand (Google-only + remove TOTP flow). Card assumes passkey-primary recommendation accepted.
Phase: 1 (sprint:next)
Parent epic: #907 (Velvet), #249 (secrets vault)
Surface: Infisical (vault.raxx.app)
Summary: Configure Infisical org SSO to use Google OIDC. Map @raxx.app hosted domain. Test with Kristerpher's Google account. CF Access outer gate (Class 3, MFA non-relaxable) remains unchanged. Document in docs/ops/runbooks/operator-login.md.
Depends on: NV01 (CF Access IDP swap must complete first to establish the Google identity layer).
Risk: Infisical OIDC config is org-level; misconfiguration locks out all vault access. Rollback: disable OIDC in Infisical admin (local auth remains as fallback until OIDC is confirmed working).
Phase: 2 (sprint:next, tied to Velvet admin UI build milestone)
Parent epic: #907 (Velvet)
Surface: Velvet admin UI
Summary: Velvet admin UI ships with Google OIDC as the only auth path (no password path, no separate TOTP). OIDC library (PKCE, short-lived ID tokens, no token persistence). Validate hd == raxx.app. Map sub + email to velvet_admin_id in Velvet Postgres (schema: velvet_admins table per architect's §7.2). Read CF JWT groups claim for RBAC. Per-caller service tokens for programmatic access (already in #912).
Depends on: NV01 (CF Access IDP), #908 (Velvet scaffold deployed), #912 (service-token middleware).
Risk: Must coordinate with Velvet admin UI Milestone definition (NV12 milestone number not yet filed as a card).
Phase: 3 (do not start before MBT-v1 ships)
Parent epic: #78 (Exploration Platform)
Surface: Antlers (raxx.app)
Summary: Add Google OAuth as an optional second sign-in path for customers. Passkeys remain primary and required. Google is additive — no customer is forced to use it. Separate ADR required before implementation. DQ-3 must be answered (hard requirement: no customer forced to Google).
Depends on: ADR filed post-MBT-v1; NV01 for CF Access Google identity foundation; DQ-3 resolution.
Risk: Introducing Google OAuth on the customer surface after passkey launch creates a weaker fallback path. ADR must explicitly address the security regression risk.
Phase: 1 (sprint:current — blocks NV01 through NV05)
Parent epic: #146 (console)
Surface: Cross-cutting
Summary: Document the CF Access JWT groups claim taxonomy (cf-ops, cf-superadmin, cf-developer, cf-auditor), how each app layer reads and validates the CF JWT, and the RBAC mapping from groups claim to app-local roles (per rbac-design.md). This is the spec that all app-layer auth implementations in NV01–NV05 must reference. Lives in docs/architecture/jwt-claims.md.
Depends on: DQ-4 decision (group taxonomy). Architect's auth-unification.md §5.3 is the source draft.
Risk: If filed as a standalone doc, risk of drift from the CF Access config. Must be kept in sync via PR review policy.
Phase: 1 (sprint:next)
Parent epic: #500 (workflow tracing), #146 (console)
Surface: Console + Velvet
Summary: Define and implement audit log entries for Google OAuth events across all operator surfaces. Console: auth.google_bind, auth.google_login, auth.google_unbind (spec in architect's §7.1). Velvet: equivalent schema when NV05 ships. Retention: 2 years (ADR-0003). Verify Cloudflare Access login events are captured (CF already logs these; ensure they are exported or retrievable).
Depends on: NV03 (Console Google OAuth route), NV05 (Velvet admin UI).
Risk: Scope creep — this card is about the schema and implementation, not a full audit dashboard. Audit dashboard is a separate card (#620 partially covers session management).
Phase: 1 (sprint:current)
Parent epic: #81 (SDLC / security hardening)
Surface: Google Workspace admin
Summary: Operator action: Kristerpher enables "Require 2-Step Verification" in Google Workspace Admin Console for all users in the raxx.app domain. Verify 2FA enforcement policy is in place before NV01 (CF Access IDP swap) goes live. Document the enforcement state in docs/ops/runbooks/operator-login.md. This is a one-time infra action, not a code change — but it must be tracked and confirmed before Phase 1 can close.
Depends on: Nothing. But NV01 depends on this.
Risk: Enabling Workspace 2FA before operators are enrolled locks them out. Verify Kristerpher's 2FA enrollment before toggling the policy.
Issue #695 documents the current state where CF Access uses auth_method: swk (WebAuthn at CF Access) for tickets.raxx.app. The architect's Phase 1 replaces the CF Access IDP with Google Workspace OIDC, which changes the MFA mechanism from CF-native WebAuthn prompts to Workspace-enforced 2FA. These are not compatible as simultaneous goals: running Google Workspace OIDC through CF Access means CF is no longer prompting for WebAuthn — it trusts Workspace to enforce the second factor.
The transition window needs a documented handshake: during the 48-hour dual-IDP window (both one-time-pin and Google Workspace enabled in CF Zero Trust), which policy wins? If Workspace 2FA is already enrolled before the swap, the Class 3 surfaces (FreeScout, vault) retain their "strong MFA always" requirement via Workspace — this is functionally equivalent to the current auth_method: swk gate, but enforced by a different mechanism. This is architecturally sound per ADR-0031 D3 (the requirement is "strong MFA always" — the mechanism is not specified). The PM read: no contradiction, but the transition handshake must be explicit in the NV01 card ACs.
DQ-1 (passkey primary vs Google-only for Console) directly affects how much of #300's TOTP elevation flow is retained. If DQ-2 also resolves to "Workspace 2FA only" (remove Console TOTP), the #300 demote becomes a near-close rather than a priority:low. Recommend Kristerpher answers DQ-1 and DQ-2 before the NV03 card is claimed.
Item 2 in #695's body says "extend to console + vault" should be filed as separate sub-cards. That work is now NV01 (CF Access IDP swap) and NV04 (Infisical OIDC). The demote comment will point to NV01 and NV04 as the canonical replacements once they are filed.
@raxx.app Workspace account or an external Google account allowed by domain policy). This is a pre-GA customer surface concern, not an operator concern, but #453 should be reviewed after NV01 to confirm the sync mechanism still works. Flagging here; no demote at this stage.| Card | Phase | Proposed Sprint |
|---|---|---|
| NV09 — Workspace 2FA enforcement check (operator action) | 1 | sprint:current |
| NV01 — CF Access Google Workspace OIDC binding | 1 | sprint:current |
| NV02 — FreeScout OAuth Login module install | 1 | sprint:current |
| NV07 — JWT claims / role taxonomy doc | 1 | sprint:current |
| NV03 — Console "Sign in with Google" fallback | 1 | sprint:next |
| NV04 — Vault Infisical Google OIDC SSO | 1 | sprint:next |
| NV08 — Audit log schema for operator logins | 1 | sprint:next |
| NV05 — Velvet admin UI Google OIDC (NV12 sub-card) | 2 | sprint:next (when NV12 is scoped) |
| NV06 — Antlers optional Google OAuth for customers | 3 | future (post-MBT-v1) |
These are all from the architect's auth-unification.md §12. No new cards can be claimed until these are resolved:
| ID | Question | Blocks |
|---|---|---|
| DQ-1 | Console: passkey primary + Google fallback vs Google-only? | NV03 scope |
| DQ-2 | Console TOTP: retain for SEV-1 step-up vs remove (Workspace 2FA only)? | #300 demote scope, NV03 |
| DQ-3 | Customers: hard requirement "no customer forced to Google"? | NV06 scope |
| DQ-4 | CF Access group taxonomy: approve ops, superadmin, developer, auditor? |
NV01, NV07 |
End of rework doc. See docs/architecture/auth-unification.md for the architect's full design. See ADR-0042 for the companion decision record.