Data model
A conceptual map of Sereal's core tables — what to read, not a schema dump.
This is an orientation map, not the schema itself. The authoritative definitions
are in source: the initial schema in
SCHEMA.sql and the
full, current shape in the ordered
supabase/migrations/
sequence. When this page and the migrations disagree, the migrations win.
Core entities
The original spine of the app — everything else hangs off these:
| Table | What it holds |
|---|---|
items | Every card and comic you own: medium, cost basis, grade, box, disposition. The center of gravity. |
vendors | Where things were bought from. Seeded per-tenant on owner bootstrap. |
spend_log | Sealed-box and other non-itemized spend. Counts toward Total In; pulled cards cost $0 because the box is already logged here. |
wishlist | Cards and comics you're chasing, on the buy side. |
deals | eBay listings surfaced by polling, with tier and motivation scoring. |
sold_log | What you've sold, for realized P&L. |
What later migrations add
The app has grown well beyond the initial schema. Rather than enumerate every table, here are the feature areas whose tables you'll encounter — each was added by a numbered migration:
- Breaks — group-break cost allocation (
break_spotsand a rollup view) for Pick-Your-Player / Pick-Your-Team / random breaks. - Import batches — provenance for CSV imports (PSA Vault, WhatNot, COMC), linking imported items back to a spend entry.
- Positions & theses — the decision layer: a thesis on a position drives buy/hold/trim recommendations.
- Signals — per-sport scoring config and star-player heuristics that feed intake triage and recommendations.
- Show sessions — the mobile card-show loadout.
- User settings — per-tenant flags, intent/onboarding state, box definitions.
Accounting rules baked into the schema
Two invariants matter when reading the data:
- Total In =
spend_logtotal + thetotal_costofitemswhose source is an individual purchase. Cards pulled from a sealed box are$0(the box is already inspend_log) — don't double-count. - Tenant scoping is enforced by Row-Level Security on
auth.uid(), and again in application code as defense-in-depth.
Generating types
After applying migrations, regenerate the TypeScript types the app builds against:
npx supabase gen types typescript --local > lib/supabase/types.ts