Every tenant’s boundary, held at the database

One customer never sees another’s data, not because you remembered a filter, but because the boundary is enforced underneath every query. Teams, roles and invitations included, over the same database connection your app already has.

Try it on QuxCloud live demo · nothing to install
Open source
tenant-kit crystal
0
roles: owner, admin, member. Enough to run a team, few enough to explain
1
database connection, shared with billing-kit: tenancy adds no infrastructure
0
ways to leak one customer’s data by forgetting a filter
Never
deleted: offboarding archives, so “who had access in March?” still has an answer
What it is

One library, whole, not a platform you rent

Teams and members

A directory of your customers, who belongs to each and what they may do. The thing the rest of your app asks “whose data is this?”

The boundary holds itself

Isolation sits underneath your queries rather than inside each one, so a forgotten filter cannot turn into a customer seeing someone else’s data.

Three roles, not thirty

Owner, admin and member. Enough to run a real team, few enough that everyone already knows what they mean.

No new infrastructure

It uses the database connection your app already has. Adding multi-tenancy does not add a service to run or a bill to pay.

Archive, never delete

Offboarding archives rather than destroys, so the record of who belonged when survives the person leaving.

Enterprise, when they ask

Single sign-on, directory provisioning and role sync are ready when a big customer wants them: opt-in, not overhead you carry from day one.

By the shape of it

Isolation you can switch off by forgetting one line was never isolation.

3 roles
34% Owner
33% Admin
33% Member

Owner, admin, member. The whole permission surface, on purpose.

How it works

Three moves, in order

01
Identify

Work out which customer a request belongs to, from the subdomain, a header, or the path.

02
Authorize

Check the person actually belongs to that customer. Only a real member of a real team gets through.

03
Isolate

Run the work with the boundary in place, so only that customer’s data is reachable at all.

tenant-kit.ts
import { resolve, protect } from '@quxkit/tenant-kit';

// whose request is this, and do they actually belong?
const tenant = await resolve(sql, { host: req.headers.host, user });

// everything inside this block can only reach that customer's data
await protect(sql, tenant, async (db) => {
  return db.query('select * from invoices');   // only this tenant's rows
});
“

Isolation you can switch off by forgetting one line was never isolation.

Run it now, embed it later