Neon RLS mirroring
Mirror Keyring access into your Neon Postgres so RLS checks keyring.has_for() / keyring.can_for() locally.
Neon is plain Postgres — no auth.uid(), no Auth hooks. Your app sets the current subject per request, and RLS reads the same keyring.access mirror as Supabase (single-org, expired rows stay but are filtered).
1. Run the mirror schema
Same file as Supabase: templates/customer-mirror/schema.sql. For Neon use the _for variants:
-- per request, after keyring.check() allows it:
select set_config('app.uid', 'user_abc123', true);
create policy "teachers insert records" on public.records
for insert with check (
keyring.has_for(current_setting('app.uid', true), 'teacher')
);
create policy "clinicians read doctors" on public.doctors
for select using (
keyring.can_for(current_setting('app.uid', true), 'doctors.read')
);2. Provision subjects
Neon has no auth.users trigger — provision from wherever your auth lives (Clerk user.created webhook, Auth.js callback, your signup route):
await keyring.grantRole({ role: "app_user", subject: user.id, displayName: user.email });3. Feed the mirror
Same two feeds as Supabase:
- Push:
POST /api/v1/webhookspointing at any HTTPS applier you run (the Denoapplier.tstemplate works on any runtime with a Postgres client — swap the Supabase admin client forneon-http/pg). Verify the HMAC signature, then upsert/deletekeyring.accessrows. - Pull: nightly cron over user IDs with
keyring.getSubjectAccess(user.id)→ replace rows. Heals missed webhooks and role-definition fan-out.
await keyring.createWebhook({
name: "neon mirror",
url: "https://api.you.dev/hooks/keyring",
events: ["grant.*", "subject.deleted"],
});
// → { secret: "kr_ws_live_…" } — server-minted, shown ONCE. Copy it now.Branching is free
Neon branches copy app data, not Keyring grants. Access stays canonical in Keyring — preview branches get correct authorization without syncing, as long as they share the mirror feed or call keyring.check() directly.
For request-path gating without RLS, the SDK alone is enough: check() / assert() before issuing SQL. The mirror only matters when you want enforcement inside Postgres policies.