Skip to content

ADR-0010: Data layer (Supabase free tier)

  • Status: Accepted
  • Date: 2026-05-24
  • Decision-makers: Waldemar Szemat

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 Space 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 Space 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), with the Hugging Face Spaces CPU Basic free tier as a genuinely-$0 secondary target. 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) need a relational store that persists across Space 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 Space 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 seven 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 Space secret, 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).
  • SQLite on Hugging Face persistent storage: zero-vendor, but persistent storage is not guaranteed on Docker SDK Spaces, concurrent access risks corruption, and no dashboard for operator review.
  • 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 seven tables. The connection string is configured as a Space secret (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 seven 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 Space 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 Space 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 as a Space secret, 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 Space 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 zero-vendor dependency
  • Bad, because persistent storage is not guaranteed for Docker SDK Spaces
  • Bad, because concurrent writes risk corruption
  • Bad, because no operator dashboard
  • 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