v0.26.8 feat(migration): v35 auto-RLS event trigger — new tables always secure#612
Merged
garrytan merged 3 commits intoMay 5, 2026
Merged
Conversation
Postgres event trigger that fires on every CREATE TABLE and auto-enables Row Level Security. Prevents the face_detections bug: tables created outside gbrain migrations (Baku, manual SQL, other apps sharing the same Supabase project) were silently unprotected until gbrain doctor caught it. This is the Supabase-recommended approach — no dashboard toggle exists. Migration v35 (auto_rls_event_trigger): - CREATE FUNCTION auto_enable_rls() — event trigger handler - CREATE EVENT TRIGGER auto_rls_on_create_table — fires on ddl_command_end - PGLite: no-op (no RLS engine, no event triggers) Tests (3 cases): - Event trigger exists after migration - New table automatically gets RLS enabled - auto_enable_rls function exists Closes the gap identified in production on 2026-05-04 when face_detections was found without RLS.
…r CTAS+SELECT INTO Apply the corrections surfaced by /plan-eng-review + /codex consult against the original PR garrytan#612. The trigger now matches v24/v29/schema.sql posture (ENABLE only, no FORCE), scopes to the public schema, and covers all three table-creation syntaxes Postgres reports. Bundles a one-time backfill of every existing public.* table without RLS, honoring doctor.ts's GBRAIN:RLS_EXEMPT regex and quoting identifiers via format('%I.%I'). Drops the EXCEPTION wrap inside the trigger so per-table failures abort the offending CREATE TABLE (loud rollback) rather than producing a silent permissive default. Drops the hand-rolled privilege pre-check — the runner already fails loud on permission errors and gates the version bump. Breaking change: operators with intentionally-RLS-off public tables must add the GBRAIN:RLS_EXEMPT comment before upgrading or the backfill will flip them on. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…-trigger # Conflicts: # CHANGELOG.md
garrytan
added a commit
that referenced
this pull request
May 5, 2026
Pull v0.26.8 (auto-RLS event trigger, PR #612) into the OAuth/MCP hardening branch. Three conflicts resolved: - VERSION: kept 0.26.9 (this branch claims the next slot above master's freshly-shipped 0.26.8). - package.json: kept 0.26.9 to match VERSION. - CHANGELOG.md: my v0.26.9 entry stacks on top of master's v0.26.8 entry, contiguous version sequence preserved (0.26.9 → 0.26.8 → 0.26.7 → ...). CLAUDE.md auto-merged cleanly: my v0.26.9 OAuth/MCP-hardening annotations and master's v0.26.8 RLS-trigger annotations live in non-overlapping line ranges. Regenerated llms.txt + llms-full.txt to reflect both passes (the build-llms test pins the committed bundle against the current generator output and would otherwise fail). Verification: bun run typecheck clean, bun test 3773 pass / 0 fail. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Migration v35 auto-RLS event trigger — every gbrain brain becomes secure by default on upgrade.
A production incident on 2026-05-04 found
face_detections(a Baku-created table) sitting in the Supabase project without Row Level Security enabled.gbrain doctorcaught it after the fact, but the gap window between create and next doctor run was the silent vector. v0.26.8 closes that gap from both sides.The original PR #612 commit (86e7b7a) shipped a first-cut event trigger. This PR was then revised through
/plan-eng-review(7 architectural decisions D1-D7) and/codexoutside-voice consult (11 specific corrections), producing the final shape:auto_rls_on_create_tablefires on everyddl_command_endforWHEN TAG IN ('CREATE TABLE', 'CREATE TABLE AS', 'SELECT INTO')and runsALTER TABLE … ENABLE ROW LEVEL SECURITYfor every newpublic.*table.public.*base table without RLS, honoring doctor's exact^GBRAIN:RLS_EXEMPT\s+reason=\S.{3,}regex and quoting identifiers viaformat('%I.%I', schema, table).rls_event_triggercheck verifies the trigger is installed and enabled (evtenabled∈{O, A}).checkRls()fromsrc/core/supabase-admin.ts(zero callers, hardcoded 10-table list disagreed with doctor's posture).Posture: ENABLE only (no FORCE), public-schema-only (Supabase manages auth/storage/realtime), no EXCEPTION wrap inside the trigger (event triggers fire inside the DDL transaction, so a failed ALTER aborts the offending CREATE TABLE — wrapping would have replaced loud rollback with silent permissive default).
Test Coverage
Tests: 3720 → 3748 unit (3748 pass after master merge), +12 v35 E2E cases. Master's #613 test isolation work fixed the EMBEDDING_MODEL flake.
Pre-Landing Review
/plan-eng-reviewfound 6 architectural issues + 2 code-quality items + 6 test gaps + 5 silent-failure modes; 7 decisions resolved (D1-D7), 2 TODOs filed.Adversarial Review
/codexoutside-voice consult returned 11 specific findings, all material ones integrated into the plan. Two decisions reversed (D4 privilege pre-check dropped, D5 EXCEPTION wrap removed) and two new decisions resolved (D6 CTAS coverage, D7 dead-surface deletion). Codex caught 4 corrections eng review missed: D5 inversion, fakepg_create_event_triggerrole,evtenabled='R'healthy classification, GBRAIN:RLS_EXEMPT regex drift.Plan Completion
7/7 decisions resolved. All 11 implementation items DONE. Lake score: 7/7 chose the complete-coverage option after codex correction.
Verification Results
bun run typecheck: cleanbun run test: 3748 pass, 0 failgbrain doctor exits 0 on healthy DB— verified to fail on master baseline, unrelated)TODOS
No items completed in this PR. Two TODOs filed in the plan for follow-up:
gbrain doctor --fix-rlsfor the long tail (post-v35 if operator drift surfaces)Documentation
docs/guides/rls-and-you.md— new "v0.26.8 — auto-RLS event trigger and one-time backfill" section explaining the trigger, breaking-change semantics, and cross-app implications.CLAUDE.md— extendedsrc/core/migrate.tsannotation with v35 specifics; addedrls_event_triggercheck tosrc/commands/doctor.tsannotation.CHANGELOG.md— v0.26.8 release-summary block with required "To take advantage of v0.26.8" recovery steps and explicit breaking-change line for intentionally-RLS-off public tables.Breaking change
Operators with intentionally-RLS-off public tables MUST add the
GBRAIN:RLS_EXEMPT reason=…(≥4 chars afterreason=) comment before runninggbrain upgradeto v0.26.8. The migration's one-time backfill flips RLS on for any public table whose comment doesn't carry the exact contract. Recovery cost is one round-trip; no data is lost.Test plan
bun run typecheckcleanbun run test3748/3748 pass🤖 Generated with Claude Code