Skip to content

Use RLS in Postgres instead of app-level filtering#165

Draft
prvnsmpth wants to merge 8 commits into
masterfrom
feat/rls
Draft

Use RLS in Postgres instead of app-level filtering#165
prvnsmpth wants to merge 8 commits into
masterfrom
feat/rls

Conversation

@prvnsmpth

@prvnsmpth prvnsmpth commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

Migrate from explicit user-based filtering in application code to
Postgres RLS policies. All user-scoped tables are now protected by
policies that reference app.current_user_id session variable.

Implementation:

  • Add RLS connection pool with per-request user scoping
  • Migrate web handlers to use RLS-scoped db connection
  • Remove explicit userId checks from web route handlers

@prvnsmpth prvnsmpth changed the title Use RLS instead of explicit user-based filtering Use RLS in Postgres instead of app-level filtering Apr 17, 2026
prvnsmpth and others added 7 commits April 26, 2026 10:17
Master shipped 083_rewrite_imap_thread_external_ids and
084_allow_realtime_sync_type while feat/rls was in flight, colliding
with the RLS migrations' numbers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
generate_permission_filter, build_common_filters, and
json_value_to_term_string in shared/db/repositories/document.rs were
only ever called by each other after the move to RLS-based filtering.
Cargo flagged them as dead code.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Some indexer-side code legitimately needs to read or update documents
across all users — IMAP cross-source dedup looks for any donor with
embeddings, and embedding_status updates have to land regardless of
who owns the document. Both paths previously used the unscoped pool;
under RLS that returns zero rows.

Adds system_db_connection() which sets app.is_admin = 'true' so the
indexer-bypass policies match. Routes find_embedded_duplicate through
it and restores update_embedding_status (still called from
batch_processor at three sites).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two runtime bugs in the RLS hook setup:

1. rlsClient.connect() doesn't exist on postgres-js (Sql<{}>). Every
   authenticated request would have crashed before reaching a route.
   Switched to reserve() / release().

2. SET app.current_user_id = $1 cannot be parameterised in plain SQL —
   SET parses its value at parse time, not bind time. Switched to
   set_config(...) which accepts bind parameters.

Also adds db: typeof db to App.Locals so callers can reference
locals.db without a type error (was 52 svelte-check errors).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cleanup of various call-sites that the main RLS commit missed:

- repositories/sources.ts: dropped non-existent drizzle-orm type
  imports (PgDatabase / AnyPgColumn / PgTable), retyped this.db as
  typeof db, fixed findActiveByTypeAndCreator which silently used the
  unscoped admin db instead of this.db.
- api/v1/api-keys: callers were passing locals.user.id where locals.db
  was expected (4 sites — list/create/revoke/delete).
- auth/google/{link,unlink}: userSession.userId → userSession.user.id.
- admin/settings/integrations/{drive,gmail}/[sourceId]: instantiate
  SourcesRepository(locals.db) instead of importing the singleton.
- db/{chats,response-feedback}: result.rowCount → result.count
  (postgres-js returns count, not rowCount).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The multi-line `CMD bash -c '...'` form was being parsed line-by-line
by the Dockerfile parser, breaking the build. Moved the script into
services/migrations/entrypoint.sh and switched CMD to exec form.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@prvnsmpth prvnsmpth added this to the Sync & search hardening milestone Apr 26, 2026
@prvnsmpth prvnsmpth added the area:infra Deploy, CI, infrastructure label Apr 26, 2026
@prvnsmpth
prvnsmpth marked this pull request as draft April 26, 2026 12:58
@prvnsmpth prvnsmpth moved this from Todo to In Progress in Omni Roadmap Apr 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:infra Deploy, CI, infrastructure

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

1 participant