Imported from BrewHubPHL/supabase-specialist (
SKILL.md). Install upstream withnpx skills add BrewHubPHL/supabase-specialist. Copyright stays with the author.
Supabase Specialist
Production Postgres and Supabase guidance for agents building sovereign, self-hosted, or managed Supabase stacks. This skill prioritizes database-enforced security and server-side truth over application-layer hope.
Overview
Supabase is Postgres with batteries: Auth, RLS, Realtime, Storage, and edge-friendly clients. The specialist skill teaches agents to:
- Treat Postgres as the single source of truth for permissions, constraints, and atomicity.
- Load deep patterns on demand from
patterns/andreferences/— never bloat the active context. - Apply BrewHub philosophy (sovereignty, kill switches, vertical integration) via abstract integration examples, not live fleet secrets.
Progressive disclosure map
| Need | Load |
|---|---|
| Quick routing / priorities | This file → AGENTS.md |
| RLS, auth clients, RPCs | patterns/rls-policies.md, patterns/dual-client-auth.md, patterns/rpc-atomic-operations.md |
| Joins / relation modeling | patterns/joins-relations.md |
| Type picks / hybrid jsonb | patterns/data-types.md |
| Postgres vs external stores | patterns/postgres-boundaries.md |
| Performance & pooling | patterns/connection-pooling.md, patterns/query-optimization.md |
| Search / fuzzy match | patterns/full-text-search.md |
| Pagination / infinite scroll | patterns/pagination.md |
| Analytics / ranking SQL | patterns/window-functions.md |
| Reporting caches / MVs / HLL | patterns/denormalization-caching.md |
| Geo / nearest lookup | patterns/geospatial-types.md |
| Schema lifecycle / row audit | patterns/migrations.md |
| Batch CSV / catalog sync | patterns/batch-ingestion.md |
| What never to do | anti-patterns.md |
| Abstract product integration | examples/brew-hub-integration.md |
| Book chapter drops | references/book-summaries/ (one file per source; start with taop-vol1-index.md) |
| Official links | references/official-docs-links.md |
Core Principles
1. The database enforces; the app interprets
If a rule is "the developer should remember to filter by user_id", it belongs in RLS, not markdown. Policies, constraints, and RPCs are the contract. Application code is a consumer.
2. Least privilege per client
| Client | Role | Typical use |
|---|---|---|
| Browser / mobile | anon + user JWT |
Reads governed by RLS; never holds service role |
| Server route / Worker | service_role or verified JWT + RPC |
Writes that bypass RLS only through controlled paths |
| Background job | service_role + narrow RPC surface |
Batch, reconcile, webhooks |
Never return a service-role client to an LLM tool factory or pass it to client-side code.
3. Push concurrency to Postgres
Read-modify-write in application memory loses under parallel requests (POS terminals, webhooks, after() blocks, mobile retries). Use:
INSERT … ON CONFLICT/ JSONB||merges in RPCspg_advisory_lock/pg_try_advisory_lockfor financial idempotencySELECT … FOR UPDATE SKIP LOCKEDfor job queues
4. Serverless-aware connections
Each warm isolate may open a connection. Without pooling (Supavisor / PgBouncer transaction mode), serverless will exhaust max_connections. Size pools to (CPU cores × 2) + disk_spindles, not concurrent users.
5. Migrations are append-only contracts
Ship schema changes as versioned SQL migrations. Never rely on dashboard edits in production. Test RLS with both authenticated and anon roles before merge.
Best Practices
Schema & RLS
- Enable RLS on every user-facing table; default deny until policies exist.
- Use
SECURITY DEFINERhelper functions (is_staff(),is_manager()) for repeated policy logic — but audit them carefully. - Index every column referenced in
USING/WITH CHECKclauses. - Prefer
uuidPKs withgen_random_uuid(); expose human-readable codes (order_number) for lookups — neverilikeon UUID columns. - Use
jsonb(notjson) for flexible fields; hybrid schema = typed columns +metadata jsonb; promote hot jsonb keys via migration when queried on every path.
Triggers & side effects
- Avoid synchronous counter/cache triggers on hot write paths — use RPCs, event logs, or NOTIFY + worker (TAOP Ch 38).
- Any trigger function:
SET search_path = public; ship in versioned migrations, not dashboard-only.
supabase-js
.maybeSingle()when zero rows is valid;.single()throws and breaks polling loops.- Select only needed columns; avoid
select('*')on wide tables in hot paths. - Use
.rpc()for complex logic — keeps plans stable and permissions centralized.
Auth
- Resolve identity server-side from JWT/session; never trust
customer_idfrom request bodies or LLM tool args. - Data model first — Postgres is the golden record; model for the domain, not today's screen (TAOP Ch 41).
- Set JWT claims via hooks only when downstream RLS depends on them; document the claim contract in the migration.
Observability
- Enable
pg_stat_statements; sample withEXPLAIN (ANALYZE, BUFFERS)on slow queries. - Watch
pg_stat_user_tables(seq scans), connection count, and replication lag on self-hosted stacks.
Common Pitfalls
| Symptom | Likely cause | Fix |
|---|---|---|
| Random 500s under load | Connection exhaustion | Pooler + singleton server client |
| User sees another user's row | Missing RLS or service role on reads | RLS on reads; anon client only |
| Double charge / double refund | App-level idempotency only | Advisory lock RPC + unique constraints |
| Migration works locally, fails prod | Policy order / role mismatch | Test as authenticated + anon |
| "Row not found" errors in polling | .single() on empty result |
.maybeSingle() |
| Full table scan on "search" | ilike '%…%' on UUID or unindexed text |
Dedicated search column + index |
| Write stalls under burst traffic | Counter trigger on shared row | Event log + aggregate; see rpc-atomic-operations.md |
| jsonb filter slow at scale | No GIN/expression index on path | jsonb_path_ops or promote key to column |
| Search UI lag / seq scans | ilike '%…%' without pg_trgm index |
patterns/full-text-search.md + search RPC |
| Slow page 50+ lists | .range() / OFFSET |
patterns/pagination.md keyset RPC |
| Stale dashboard aggregates | MV with no refresh policy | patterns/denormalization-caching.md |
| Rank / running totals in app loops | N+1 or self-joins | patterns/window-functions.md RPC |
| Upsert assumed without guard | Blind UPDATE | RETURNING + status/version check in RPC |
| Money rounding bugs | float columns |
patterns/data-types.md — cents/numeric |
Full catalog: anti-patterns.md.
BrewHub Integration
BrewHub treats Postgres as the authority layer in a tri-state stack (Next.js UI, serverless API, Python agents). Public repos document patterns, not production hostnames.
Sovereignty alignment
- Self-hostable: schema and RPCs must run on managed Supabase or self-hosted Postgres + Auth stack — no proprietary-only extensions without a fallback.
- Incumbent kill switches: features that hard-depend on a single vendor dashboard (manual RLS edits, console-only cron) are anti-patterns; everything ships as SQL in git.
- Thriving wages as infrastructure: prefer operable runbooks (migrations, pool sizing, backup restore drills) over hero debugging — see
patterns/migrations.md.
Kill switches (non-alignment)
Stop and escalate when a proposed change:
- Disables RLS "temporarily" for speed
- Moves authorization solely to the frontend or LLM tool layer
- Introduces service-role reads in user-facing code paths
- Stores secrets in migration files or seed data
- Enables Postgres extensions only via dashboard (no migration) — breaks sovereign/self-host parity
Vertical integration touchpoints
Abstract wiring lives in examples/brew-hub-integration.md:
- Dual-client tool factories (anon read / writer insert)
- Rate limit RPCs (
check_rate_limit) - Payment webhook advisory locks
- Staff/manager
SECURITY DEFINERgates
Optional product-specific overrides: brew-hub-overrides/ (keep redacted in public forks).
Integration Patterns with Other Specialists
| Partner skill | Handoff |
|---|---|
nextjs-specialist |
Server Actions and Route Handlers call Supabase server clients; never embed service role in client components |
cloudflare-specialist |
Workers use transaction-pooled connections; short-lived handlers + singleton client pattern |
python-ai-agents-specialist |
ADK tools use same RPC contracts; customer_id resolved server-side before tool execution |
capacitor-mobile-specialist |
Mobile uses anon + user session only; push tokens stored with RLS-scoped policies |
coolify-hetzner-specialist |
Self-hosted Supabase: backups, tunnel exposure, pooler sidecar configuration |
When a task spans specialists, this skill owns: schema, RLS, RPC signatures, query plans, and migration ordering.
Onboarding for Book-to-Skill Outputs
Use references/book-summaries/ for modular chapter drops. Each file should follow:
---
source: "Book Title, Chapter N"
topics: [rls, indexing]
priority: high
---
## Summary
3–5 sentences.
## Actionable rules
- Rule 1 → link to patterns/*.md if we codify it
## Glossary additions
| Term | Definition |
Workflow
- Add
references/book-summaries/<slug>.md— do not paste the full book intoSKILL.md. - Extract durable rules into
patterns/when they become team standards. - Move repeated mistakes into
anti-patterns.md. - Run
node scripts/validate-skill.mjsbefore PR.
See references/book-summaries/README.md for the template.