ADR 0009 — OAuth access token at rest: documented invariant exception
Status: Superseded by ADR 0013 and ADR 0014 (2026-04-22)
Original date: 2026-04-22
Deciders: product owner (user), software-architect
Related: ADR 0002 (no stored credentials), ADR 0008 (OAuth integration mode), docs/architecture/multi-tenant-alpaca.md §3
Parent epic: #183
Supersede note (2026-04-22): This ADR documented the exception to invariant #1 for storing Alpaca OAuth access tokens for every user. Under the MBT reframe (ADR 0013), the vast majority of users (free + most Pro) have no broker token because MBT is Raxx's own paper engine. ADR 0014 re-scopes this exception to the narrow Pro+ live-broker-handoff subset only. The content below is preserved for historical traceability.
Context
ADR 0002 says Raxx must be incapable of replaying a user secret. ADR 0008 committed Raxx to Alpaca OAuth 2.0 for all users. Alpaca's OAuth access token can be replayed for its validity window — so it is a credential in the replay sense. Retaining it between requests crossed invariant #1.
Decision (original — now superseded)
The Alpaca OAuth access token was declared a documented, bounded, audited exception to invariant #1, not a silent violation.
Four guardrails shipped together:
- Scope + blast-radius caps (minimum scope per feature,
env=papervsenv=liveseparation). - Encryption at rest via envelope encryption (per-row DEK wrapped by KMS envelope key).
- Rotation + short-lived-by-default (no life extension beyond Alpaca's issuance; 90-day re-consent cap).
- Audit + revocation (every mint/refresh/use/revoke logged; kill switch + mass-revoke script).
Schema sketch preserved below:
alpaca_connections
id TEXT PK
user_id TEXT FK -> users.id ON DELETE CASCADE
env TEXT NOT NULL -- 'paper' | 'live'
scopes TEXT NOT NULL
access_token_ciphertext BLOB NOT NULL
access_token_iv BLOB NOT NULL
access_token_wrapped_dek BLOB NOT NULL
kms_key_id TEXT NOT NULL
issued_at TIMESTAMP NOT NULL
expires_at TIMESTAMP NOT NULL
last_used_at TIMESTAMP NULL
needs_reauth BOOLEAN NOT NULL DEFAULT 0
revoked_at TIMESTAMP NULL
Why superseded
Under the MBT reframe:
- Free and most Pro users have no broker token stored. Invariant #1 is satisfied fully for them. The exception simply does not apply.
- Only Pro+ users who explicitly enroll live-broker handoff have a stored token. ADR 0014 restates the envelope-encryption / rotation / audit / revocation pattern for that narrower subset, with the same guardrails.
- The schema above is essentially what ADR 0014 adopts, renamed to
alpaca_live_connectionsto make the narrower scope visible at the schema layer.
The technical pattern was sound; its scope was too broad. ADR 0014 is the active record.
Compliance checklist
All prior checklist items carry over to ADR 0014 in narrower scope. This ADR itself is closed.
Revisit when
N/A — superseded. Amendments go on ADR 0014.