Skip to content

ADR-0010: Data layer (Supabase free tier)

Several demo features introduce operational data that does not fit the in-memory posture of the current demo:

  • Demo key access control: per-row configured keys with TTL, budget caps, feature flags, pseudonymized fingerprint binding, rate limiting, cost tracking. This data must persist across revision restarts and be queryable by the operator.
  • Interaction logging: PII-redacted turn logs for the continuous improvement layer, with dedup hashes and compliance flags. Must persist for 90-day retention and be queryable by the batch improvement script.
  • Self-service request, consent, and session metrics: key requests, consent records, session tracking. Must persist across revision restarts and survive cold starts.

ADR-0007 sets the production deployment to Google Cloud Run as a single scale-to-zero instance (in-memory defaults, near-$0 hosting at demo scale). ADR-0004 locks Chroma embedded for RAG retrieval. ADR-0001 already provisions a Postgres checkpointer factory for durable conversation state.

The new operational tables (demo keys, demo turn usage, interactions, improvement suggestions, demo key requests, demo key consents, demo sessions, demo global budget, demo email verifications) need a relational store that persists across revision restarts, is queryable by both the demo backend and the operator, and remains $0/month for hosting.

How do we add managed Postgres for demo operational data without violating the $0/month hosting constraint and without displacing Chroma as the RAG vector store?

  • $0/month hosting cost: operator pays nothing for infrastructure in steady state (ADR-0007). Per-key API budgets for demo reviewers are operator-funded usage costs, not hosting costs.
  • Persistence across revision restarts: in-memory state is lost on cold start (48-hour idle sleep). Demo keys, consents, and sessions must survive.
  • Operator dashboard visibility: the operator needs a queryable view of keys, requests, sessions, and interactions for manual review and grant/revoke decisions.
  • Relational schema fit: all nine tables have foreign keys, indexes, check constraints, and JSONB columns. A relational store is the natural fit.
  • Minimal new operational surface: one connection string as a Secret Manager binding, no new infrastructure to manage.
  • Supabase free tier (Postgres) (chosen): 500 MB managed Postgres, pgvector available, auth optional, dashboard included, row-level security, free tier is strategic (not promotional).
  • Neon free tier: similar managed Postgres, but weaker dashboard and less brand recognition for enterprise reviewers.
  • Firestore (NoSQL): document model is a poor fit for the relational schema (FKs, check constraints, JSONB queries).
  • PlanetScale free tier: MySQL-based, withdrew its free tier in April 2024; not $0/month going forward.
  • Turso (libSQL): SQLite-compatible edge database; adds operational complexity for a low-volume demo.

Chosen option: Supabase free tier as the managed Postgres backend for demo operational data. One Supabase project hosts all nine tables. The connection string is configured as a Secret Manager binding (a Supabase URL plus a service key, or a single database URL). The demo backend connects at startup; if the connection fails, the agent continues with degraded demo-key enforcement and interaction logging (failure-mode: log locally, warn in UI, do not block agent flow).

The RAG layer remains Chroma embedded (ADR-0004, unchanged). Supabase is for operational data, not retrieval. This distinction is explicit: Chroma owns the vector index over the knowledge-base cards; Supabase owns the relational tables for access control, consent, sessions, and improvement.

The hosting cost stays near $0/month at demo scale: Cloud Run’s always-free allowance (ADR-0007) + Supabase free tier. Per-key API budgets (Anthropic, ElevenLabs) are usage costs funded by the operator, not hosting costs.

  • Schema migrations deploy the nine tables with FKs, indexes, and check constraints.
  • The demo backend reads the database connection settings at startup and connects to Supabase.
  • If the connection fails, the agent serves turns without demo-key enforcement and logs a warning; the agent flow is never blocked.
  • The operator dashboard at Supabase shows keys, requests, sessions, interactions, and suggestions in real time.
  • The Postgres connection for the LangGraph checkpointer (see ADR-0001) can point to the same Supabase instance, sharing the connection pool.
  • Demo key state, consent records, and sessions persist across revision restarts and cold starts.
  • Operator gets a real-time dashboard without building one.
  • Managed Postgres demonstrates production-aware data layer design.
  • Free tier (500 MB) is sufficient for low-volume demo usage (50-150 reviewers x 5-10 turns).
  • pgvector is available for future semantic clustering in the improvement layer.
  • Unifies storage for the demo operational data under one backend, avoiding fragmented state.
  • Adds a runtime dependency on an external managed service. If Supabase is down, demo-key enforcement degrades (agent still serves turns, but without access control).
  • Supabase free tier has a 500 MB limit; sufficient for demo scale but not for sustained production traffic.
  • Connection latency from the service to Supabase adds a few milliseconds per turn for the logging write; acceptable at demo scale.
  • The Supabase service-role key is a sensitive credential; must be stored in Secret Manager, never hardcoded.
  • A new dependency on a Supabase client or a Postgres driver.
  • Schema migrations become part of the deployment checklist.
  • The free tier does not include point-in-time recovery; data loss is possible on Supabase-side incidents. Acceptable for demo data.
  • Good, because managed Postgres persists across revision restarts
  • Good, because the dashboard gives the operator real-time visibility
  • Good, because the free tier is strategic (drives platform adoption), not promotional
  • Good, because pgvector is available for future semantic queries
  • Good, because it demonstrates a managed Postgres data layer even in the demo
  • Bad, because it adds a runtime dependency on an external service
  • Bad, because the free tier has a 500 MB cap
  • Good, because similar managed Postgres offering
  • Bad, because weaker dashboard for operator review
  • Bad, because less brand recognition for enterprise reviewers
  • Good, because Google-managed, generous free tier
  • Bad, because document model is a poor fit for relational schema
  • Bad, because no SQL, no FKs, no check constraints
  • Bad, because the free tier was withdrawn in April 2024
  • Good, because SQLite-compatible with edge replication
  • Bad, because adds operational complexity for a low-volume demo

Part of the portfolio of Waldemar Szemat · szemat.pro
GitHub · LinkedIn