Agentic Trading
Decisions

ADR-0003: Database is Supabase (Postgres + pgvector + Auth)

We need a primary data store. Requirements:

  • Status: accepted
  • Date: 2026-05-27

Context

We need a primary data store. Requirements:

  • Postgres (familiar, transactional, rich type system, pgvector for news embeddings)
  • Auth — users sign in to author/deploy Skills; we don't want to roll our own
  • Hosted — we're a small team, not running pg ourselves
  • Vercel integration — auto env var provisioning, easy preview branches
  • Realtime — Postgres LISTEN/NOTIFY over a websocket bridge (needed at least for live-runner command dispatch; nice-to-have for future UI)
  • Storage for blobs (sim reports, optional Skill artifacts)

Candidates: Supabase, Neon, Vercel Postgres (deprecated, see knowledge-update doc), self-hosted on Fly/Railway.

Decision

Supabase as the unified database + Auth + Realtime + Storage layer. Provisioned via the Vercel Marketplace so env vars auto-populate. pgvector enabled for news embeddings.

All apps and packages access the DB through packages/db — a thin wrapper exposing a typed Supabase client. No app instantiates createClient(...) directly.

Alternatives considered

Alt A — Neon (pure Postgres serverless)

  • Best-in-class branching (per-PR DB branches, per-sim-run DB branches)
  • No bundled Auth — would need to add Clerk or build our own
  • No realtime bridge built-in
  • Storage requires Vercel Blob or S3
  • Not picked: the 3-product bundle Supabase gives us (Postgres + Auth + Realtime) is more useful than Neon's branching, given we'd otherwise need to bolt on Clerk + something for realtime. Branching is nice but solvable with a sim_run_id column.

Alt B — Self-hosted Postgres on Fly

  • Cheap at scale, full control
  • Operational burden (backups, upgrades, monitoring)
  • Not picked: premature optimization for a pre-MVP product. We'll absorb operational toil only when needed.

Alt C — Planetscale / Turso

  • Distributed-first, eventual consistency model
  • Not picked: trading data needs strict consistency; the eventual-consistency upside isn't worth the constraint.

Alt D — Supabase + separate Auth (Clerk)

  • Clerk is excellent
  • Adds another vendor + another bill
  • Not picked: Supabase Auth is good enough for MVP. Migrate to Clerk later if/when we outgrow it (Auth is well-isolated behind a single helper).

Consequences

Positive

  • One vendor for DB + Auth + Realtime + Storage = one bill, one dashboard, one set of credentials
  • Postgres + pgvector means our news embeddings live next to the news table (no cross-store joins)
  • Supabase Realtime is just LISTEN/NOTIFY exposed — same channel the live runner uses internally for commands; we can repurpose for future UI
  • Vercel Marketplace install auto-provisions env vars across Vercel envs
  • Supabase RLS gives us per-user data isolation enforced at the DB layer, not the app layer

Negative / trade-offs

  • All-in on a single hosted vendor; switching cost is real
  • Supabase Auth is less polished than Clerk for advanced flows (org management, social SSO matrix). Acceptable for MVP.
  • We lose Neon's per-PR branching for previews — schema changes ride in the same DB across preview deploys. Mitigate with careful migration discipline.
  • Realtime scaling has known limits (concurrent subscriptions); not a concern in MVP

Things we'll need to revisit

  • If we add team/org features with complex RBAC, evaluate Clerk migration
  • If we hit Supabase Realtime scale limits, front it with our own SSE layer
  • For backtests with very large bar datasets (>50M rows), evaluate moving bars to DuckDB/ClickHouse and keeping just metadata in Supabase

References

On this page