Skip to content

What is sqlite-rls?

sqlite-rls is a small loadable extension that adds row-level security to SQLite: rules that decide, per user, which rows of a table that user is allowed to see and change.

If you've used PostgreSQL's row-level security, this is the same idea, brought to SQLite and made to work from any language.

The problem it solves

Say you have a documents table and every document has an owner_id. In a normal app, every query that touches documents has to remember to add WHERE owner_id = ?. Miss it once — in a report, a join, an admin tool, a new endpoint a teammate adds six months from now — and you've leaked every user's data.

Row-level security moves that rule out of each query and into the database. You declare once that a user may only see their own documents, and from then on every query — SELECT *, joins, aggregates, subqueries — is automatically scoped to the person making the request. The forgotten WHERE clause stops being a data breach.

What makes this one different

Three design choices shape everything:

  • It's a native extension, not a library. Because it plugs into SQLite itself, the same build works from better-sqlite3, node:sqlite, Bun, Python, C, Rust — anything that can load an extension. You learn it once and use it everywhere. (See the language guides.)

  • User identity is per query. You tell the extension who the current user is immediately before running a statement — not when you open the connection, not when you start a transaction. A pooled connection can serve Alice on one query and Bob on the next. (See The user context.)

  • Where user info comes from is up to you. Claims like user_id and role can arrive as JSON you already decoded from a JWT, or the extension can run a query you configure to look them up from a table. The policy language doesn't care where they came from.

What it is not

  • Not a query rewriter. It never parses or edits your SQL. It replaces the table with a view of the same name, so your queries are untouched.
  • Not an ORM or a framework. It's one C file and a handful of SQL functions. It sits underneath whatever data layer you already use.
  • Not a sandbox for hostile SQL. It protects application code from its own bugs (the forgotten WHERE). Someone who can already run arbitrary SQL on the connection can name themselves — exactly like Postgres RLS. The security model spells out the boundary precisely.

Is it for me?

It's a good fit if you:

  • keep multi-tenant or per-user data in a single SQLite database, and
  • want "a user only sees their own rows" to hold everywhere, enforced by the database rather than by remembering to filter in each query, and
  • talk to SQLite from one or more languages and don't want to reimplement the rule in each.

Ready? The next page shows how it works under the hood — or skip ahead to getting started if you'd rather see it run.

A loadable SQLite extension. No warranty; test against your own schema.