Multi-tenancy

Multi-tenant by construction, not by convention.

Multi-tenancy is the thing most backends get wrong quietly. Aaly applies tenant scoping below the API, on every endpoint, so there is no isolation rule for a person or an agent to forget on a new route.

Why this fails quietly

On a conventional backend, isolation is something you remember to do. A filter in a query, a policy on a table, a check before a write. Each one is correct until the day one of them is missing, and a missing one does not fail loudly. The table works, the app works, the tests pass. The only signal is a customer reading another customer's data.

✕Tenant filter dropped from /orders during a refactor
✕Auth middleware skipped on 2 of 6 new routes
✕No ownership check before PATCH /invoices/:id
✕RLS enabled on the table, but no policy written against it
✕A policy that matches every row, which looks exactly like one that works

That is survivable when a human adds a table every few weeks and reviews the migration. It is much less survivable when an agent adds one on request, several times a day. Creating the table is one call. Enabling isolation and writing a correct rule against the right claim is a second thing that has to not be skipped, and skipping it looks exactly like success.

What Aaly does instead

Tenancy is a property of the project, not a rule you attach to each table. This is the entire configuration:

{ "project": "helpdesk", "multiTenant": true, "publicSignup": true }

From there, every record belongs to a tenant and every request is answered within one. The same request under two customers' tokens:

Tenant A's token
GET /orders 200 tenant A's orders
GET /orders/ORD-9f21 200 belongs to tenant A
Tenant B's token
GET /orders 200 tenant B's orders
GET /orders/ORD-9f21 403 not your record

There is no query you can write that returns another tenant's row, because you do not write the query. Aaly serves the API from your definition, so the isolation lives in the layer answering the request, not in code you own and have to keep correct.

Why there is no policy to get wrong

Aaly generates no backend code. Your agent writes a definition: entities, fields, relationships, rules. Aaly serves the API from it. There is no controller to review, no auth middleware to audit, and no tenant filter that a model may or may not have remembered on this particular run. The most reliable backend code is the code nobody writes.

This is also why a schema change stays cheap. Add a field and the contract updates; every client keeps working, because there is no generated backend in anyone's repository to fall out of sync with it. The cost of the tenth change looks like the cost of the first.

Check the backend you have now

If you are on Postgres or Supabase today, isolation depends on row-level security being switched on for each table and a policy being written against it. Both steps are per table, and a table that missed them answers to anyone holding the anon key.

Paste your schema and see which tables are exposed → It runs entirely in your browser. Nothing is uploaded.

The full Supabase comparison, including where Supabase is the better choice →

Multi-tenancy is on in every project, on every plan.

Plans differ in how many tenants they carry, never in whether isolation applies. The free tier includes 4.

Start building free