Raxx · internal docs

internal · gated ↑ index

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:

  1. Scope + blast-radius caps (minimum scope per feature, env=paper vs env=live separation).
  2. Encryption at rest via envelope encryption (per-row DEK wrapped by KMS envelope key).
  3. Rotation + short-lived-by-default (no life extension beyond Alpaca's issuance; 90-day re-consent cap).
  4. 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:

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.