Skip to main content
Repo copy of this guide: docs/architecture/DATABASE_SCHEMA_REFERENCE.md, kept in sync with this page. Verified against the live production database and all 28 supabase/migrations/*.sql files. See Storage first for the conceptual model.
Connecting: the application connects directly via Postgres (DATABASE_URL), not through PostgREST — a direct connection bypasses Row Level Security the same way a table owner does. RLS below matters for what a different credential (e.g. Supabase’s anon role) could see, not what the application’s own connection can.

Quick index

Three Razorpay tables (razorpay_webhook_events, razorpay_webhook_audit_events, razorpay_daily_refund_reservations) were dropped 2026-09-16 — orphaned schema from the Razorpay connector, removed from this codebase entirely on 2026-08-12.

Tables that share one shape

consumed_nonces, consumed_approval_nonces, consumed_policy_change_step_up_nonces are three deliberately separate tables, not duplication — each backs a distinct trust domain’s nonce namespace (a Gateway envelope, an Approval Artifact, a checker’s step-up key), issued by different parties. Sharing one table would let a coincidental collision between unrelated namespaces falsely report “already consumed.” All three: TEXT PRIMARY KEY nonce column (the PK is the atomicity mechanism — a concurrent duplicate insert hits Postgres 23505 unique_violation, mapped to “already consumed,” no separate locking needed), expires_at TIMESTAMPTZ NOT NULL, consumed_at TIMESTAMPTZ DEFAULT now(). executions, verifications, receipts, overrides also share a near-identical shape: an id, a business_transaction_id FK (ON DELETE RESTRICT), a JSONB payload column, a timestamp, and a seq BIGSERIAL (added by 20260711120000 for exact insertion-order — millisecond timestamps can tie under fast concurrent appends).

Tables worth extra attention

caller_audit_events — the most-altered table in the schema (8 migrations). Its type CHECK constraint was widened 5 times. One of those widenings (20260911090000_add_capability_granted_to_caller_audit_events.sql) was a bug-fix: application code had already been writing 'caller.capability_granted' before any migration allowed it, so every successful authenticated request failed closed with 503 AUDIT_UNAVAILABLE until fixed. Adding a new event type here and forgetting the matching migration reproduces this exact outage. Chained per caller_id via a Postgres advisory lock on hashtext(caller_id), not one global lock. execution_audit_events — the only table written to by code outside this repository. parmana-paytm-agent writes directly to it (its own src/parmana/audit.ts), correlated by business_transaction_id (not authorization_id, which is Parmana-internal and never crosses the trust boundary). Its rows are unsigned/unchained — that service holds only Parmana’s public key, never a private key. Chained per authorization_id on this repo’s own side. policy_change_approval_records — the only table with a real RLS policy attached (anon role, read-only, for CI). As of this writing, zero rows — none of the 10 real production policies have been approved by a distinct human checker yet. Expected, not a bug. challenge_records — the only mutable table in the schema. Every other table here is append-only by convention; this one is explicitly updated as an investigation proceeds. Deliberately unsigned: the writer and the party who could misrepresent it are the same party, so a signature would prove tamper-evidence of bytes without addressing the actual trust question. rate_limit_counters — the only table without RLS enabled (deliberately — no business data, no dashboard access path). See docs/VERIFICATION-GAPS.md G-49 (repo root) for why this table’s keys are now prefixed per rate limiter.

Common SQL for troubleshooting, audit, and verification

Trace one transaction across every table

The single most useful query — everything that happened for one business_transaction_id:
This is the exact pattern used to find that parmana-paytm-agent had recorded authorization.verified for a transaction Parmana’s own logs only showed a generic 500 for.

Trace one caller’s audit history

Check a rate limiter’s current state for a caller

Audit the maker-checker approval queue

Migration-sync verification (files vs. tracking table)

Check a table’s actual CHECK constraints

Several tables’ constraints have been widened repeatedly — don’t trust the original CREATE TABLE statement alone:

Find foreign keys pointing at a table (before ever dropping one)

See the repo copy, docs/architecture/DATABASE_SCHEMA_REFERENCE.md, for the complete per-table column reference and additional queries (row-count health snapshots, RLS status listing, nonce-consumption checks).

Next

Storage

The conceptual model this reference builds on.

Caller Audit Trail

The concept caller_audit_events backs.