nlqdb

Solve · Agent builders

How do I isolate AI agent memory per tenant so accounts can't read each other?

If your agent stores memory for many customers and one tenant's rows must stay invisible to another, nlqdb enforces it in the database: every provisioned Postgres carries a row-level-security policy keyed on the tenant, set per request, and fails closed — a missing scope returns no rows, never someone else's.

The moment agent memory goes multi-tenant in production, the fear is a cross-tenant leak: one customer's facts surfacing in another's answer. The usual defence is a `WHERE tenant_id = ?` filter in application code, but it lives in the same layer that writes the query — one forgotten predicate, one LLM-generated statement that drops it, and every tenant's memory is exposed at once. Scope you can forget isn't isolation.

The snippet that solves it.

> memories grouped by user with a count of each

What nlqdb does for this

  • Every Postgres gets a `tenant_isolation` RLS policy keyed on `app.tenant_id`, set per request — the engine filters reads and writes, whatever SQL the LLM emits.
  • RLS fails closed: if tenant id is unset, the policy blocks all rows — a missing scope returns nothing, not another tenant's data.
  • Per-`(mcp_host, device_id)` `sk_mcp_*` keys scope access per agent and device, so one tenant can share memory across an agent fleet without exposing another tenant's rows.
  • Want hard physical isolation? Give each customer their own database — `nlqdb_query` with no `db` set provisions a fresh Postgres from the first English goal.

Drop into any HTML page

<nlq-data goal="memories grouped by user with a count of each"></nlq-data>

The per-user breakdown an agent runs over its own memory — group by user, count each — is the same grain the engine isolates at the row level, below whatever SQL the model writes.

What this replaces

What nlqdb doesn't try to do here

  • No per-end-user row scoping *within one shared database* yet — agent-scope RLS keyed on `app.agent_id` is in progress (E-03, `SK-PIVOT-009`); today sub-tenant isolation is one key or one database per agent, not a single-DB row policy.
  • No role hierarchy or app-level RBAC — isolation is tenant-grained RLS plus per-device keys, not a permissions matrix; model roles and grants in your own app.
  • No applying this RLS isolation onto a database you already run — the tenant-isolation policy is set up on databases nlqdb provisions. You can connect an existing Postgres with the signed-in BYO connect verb, but nlqdb does not add its row-level-security policy to a database you bring; isolation there stays your schema's responsibility.

Questions buyers ask

How does nlqdb isolate AI agent memory between tenants?
Every table in a provisioned database has a `tenant_isolation` row-level-security policy keyed on `current_setting('app.tenant_id')`, and the read/write path sets that value transaction-locally on every request. Postgres applies the predicate to each statement, so isolation lives in the engine, not in application code you have to remember to write.
What stops the LLM's SQL from reading another tenant's rows?
Row-level security runs below the SQL. The policy predicate is enforced on every read regardless of the CTEs, JOINs, or aliases the model writes — the compiled SQL can't widen its own scope. Even a query with no tenant filter at all only sees the current tenant's rows, because the engine adds the boundary, not the query.
Can I isolate AI agent memory per end-user, not just per account?
Within a single shared database, per-user / per-agent row scoping (`app.agent_id`) is in progress (E-03, `SK-PIVOT-009`) — not shipped yet. Today the shipped boundaries are per-tenant RLS and per-device API keys; for hard per-user isolation now, give each end-user their own provisioned database.
How is this multi-tenant isolation different from a WHERE clause?
A `WHERE tenant_id = ?` filter lives in application code, so one forgotten predicate — or one LLM-generated query that omits it — leaks every tenant. RLS lives in the database and applies to every statement, and it fails closed: a missing scope returns no rows instead of someone else's. The blast radius of a mistake is nothing, not everything.

Where this pain shows up in public

Enduring discussion hubs where you can verify the theme without taking our word for it. We don't quote individual posts; we cite search-result and subreddit URLs that stay live as new threads land.

Try nlqdb in 30 seconds

No sign-in. The anonymous database lasts 72 hours; adopt it with one click if you keep it.

Start with a goal →

Looking at this from a different angle? Browse all solve pages or browse competitor comparisons.