Skip to content

Database & migrations

All durable state lives in Supabase Postgres. There are no local SQLite volumes anymore (that was pre-pivot). This page covers the schema and the one thing that trips everyone up: migrations don't auto-apply.

Migrations are manual

Database migrations are manual

  • Schema lives in supabase/migrations/0001..*.sql.
  • Nothing auto-applies them. When you add one, you paste the SQL into Supabase Studio's SQL Editor (or run psql "$SUPABASE_DB_URL" -f <file>).
  • All migrations are idempotent (CREATE TABLE IF NOT EXISTS, DROP TRIGGER IF EXISTS), so re-running is safe.

UndefinedTableError after a deploy = unapplied migration

The classic symptom of a forgotten migration is a 500 with UndefinedTableError right after a deploy. Before debugging deeper, check whether a migration is sitting unapplied.

Treat the SQL files as schema documentation

Because they're manual and idempotent, the migration files double as the authoritative description of the schema — read them to learn the tables.

Key tables to know

Table What it holds
public.global_settings Admin-set provider config (llm_provider, stt_provider, tts_provider, twilio) and feature flags (e.g. voice_debug_transcripts). See Secrets.
public.family_members Phone-number → family-account mapping. Powers WhatsApp routing. See WhatsApp.
public.telemetry_events One row per pipeline stage-transition, keyed by turn_id. See Observability.
public.user_settings Legacy — the table still exists but nothing reads from it. Don't add to it.

Per-family agent state is NOT in Postgres

Worth stressing: the agent's memory, sessions, and skills live in each hermes container's docker volume (hermes-user-<hex>-data:/opt/data), not in Supabase. Supabase holds account/auth/telemetry/config; the agent's working state is on its volume. That split matters when you reason about what survives what:

  • Recreating a hermes container (e.g. admin config save) keeps the volume → memory survives.
  • Deleting the volume wipes that family's memory/sessions.