Appearance
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_idandrolecan 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.