Foundation 04
Connect each user to a tenant and a role
Memberships answer where someone has access. Roles collect the exact operations they may perform in that tenant.
Derive common permissions from the model
Public model operations contribute permission names. Sync the catalog after migration, then seed or assign roles with the permissions your app needs.
- items.read covers list and get.
- items.create, items.update, and items.delete stay separate.
- Custom methods can add names such as articles.publish.
await syncPermissionCatalog(getSmrtConfig('Permission'));
const roles = await RoleCollection.create(getSmrtConfig('Role'));
await roles.seedSystemRoles({ seedPermissions: true });Enforce the same decision at every entry point
Generated routes are authentication-gated and tenant-scoped. Custom SvelteKit actions, jobs, and in-process writes must also check the principal permission snapshot. assertOperationPermission derives the same names the catalog holds and refuses an operation whose name is not in it.
import { assertOperationPermission } from '@happyvertical/smrt-users';
// Throws OperationPermissionError (status: 403) unless the caller holds
// articles.publish in the tenant that owns this article, or is running in
// system or super-admin context. Map error.status to a response yourself.
await assertOperationPermission({
...getSmrtConfig('Permission'),
collection: 'articles',
action: 'publish',
userId: locals.user.id,
tenantId: article.tenantId
});Postgres can check the same names on every row
A Postgres application can generate row-level security policies that check the same names. The request context publishes the resolved permissions to the database session. Each policy requires a tenant match and the applicable permission before a row operation. System context and super-admin sessions bypass these checks. Policy generation and application are deliberate steps. SQLite applications keep the catalog and guard but get no data-layer check.
Some packages contribute their own permissions
A framework package can add named permissions to the same catalog. Field policy contributes two: fields.policy.manage for organization-wide administration and fields.policy.personalize for a principal maintaining only their own preferences.