@companyio/platform-tenancy
v0.1.32
Published
Shared SaaS tenant context and Fastify scope enforcement
Readme
@companyio/platform-tenancy
Universal tenant context and scope enforcement for CompanyIO.
This package owns the Business and Branch fragment (prisma/tenancy.prisma) and prisma/migrations/0001_tenancy_baseline. A business has many branches. It does not know about Fuel or other domain models. User and Session belong to @companyio/auth-fastify. There is no Membership model.
The host owns DATABASE_URL, the composed schema, and migration execution. tenancy init appends Business and Branch when they are missing. It does not connect to PostgreSQL. Fuel later splices its own back-relations onto those models.
At runtime this package turns an authenticated user into { businessId, branchId, userId } and rejects any other tenant a caller tries to send.
Domain packages keep their own records. They receive this context. They do not read tenant ids from a request body.
Use
import { authFastify } from '@companyio/auth-fastify';
import { domainContext, requireTenant, tenancyFastify } from '@companyio/platform-tenancy';
await app.register(authFastify, { verifyAccessToken });
await app.register(tenancyFastify, { authenticate: (request) => request.authenticate() });
app.get('/tenant-data', { preHandler: (request) => request.requireTenant() }, async (request) => {
const tenant = await requireTenant(request);
return domainContext(tenant);
});domainContext maps that contract onto the column names domain stores already use: main_business_id, branch_id, and user_id. Package services take PersistedTenant, which is that mapping. They do not define their own tenant type.
Optional x-main-business-id and x-branch-id headers, and body fields businessId / branchId, are checked against the user. A mismatch is BUSINESS_MISMATCH or BRANCH_MISMATCH. Those values never replace the authenticated scope.
Scope checks
assertBranchScope(record, tenant);
tenantFilter(tenant); // { main_business_id, branch_id }A record from another business is BUSINESS_MISMATCH. The same business and another branch is BRANCH_MISMATCH. Blank ids do not match.
Build
pnpm --filter @companyio/platform-tenancy test
pnpm --filter @companyio/platform-tenancy build