@happyvertical/smrt-tenancy
Automatic tenant isolation, context propagation, required or optional scope, and tenant-aware query enforcement.
What it gives you
Automatic tenant isolation, context propagation, required or optional scope, and tenant-aware query enforcement.
- 01Required, optional, and global data scopes
- 02AsyncLocalStorage context propagation
- 03Tenant-safe interceptors across lookups, hydration, search, and collection memory
- 04Super-admin bypass is a per-class opt-in, off by default
Directory changes can notify other systems
The tenancy interceptor emits directory.<class>.created, updated, and deleted through dispatchBus after save or delete for configured directoryClasses. It is an event hook for directory projections, not a replacement for the request tenant context.
Adapters establish the context for their host
SvelteKit is one adapter. Express applications use createExpressMiddleware and enterTenantContext. CLI work uses createCliContext: run keeps a caller context, runWithTenant selects one tenant, runAsSystem is cross-tenant by explicit system authority, and runAsSuperAdmin is the more privileged explicit path.
Scope covers each read path
Tenant context applies to list and get calls, slug lookups, model hydration, vector search, and collection memory. Required models fail closed without a tenant context. A system context removes the context entirely and skips every check. withSuperAdminBypass keeps the tenant context and lifts enforcement only for classes that set allowSuperAdminBypass to true. The flag defaults to false, and unresolved config fails closed. auditTenantScopedRegistrations reports runtime-only registrations whose natural key omits the tenant column.
Package UI
Components in this package
Import these public components directly. When the package also exports playground entries, the next tab renders its real components with package-owned example data.
Package previews
Try the working views
These previews are owned by the package and use its public components. Mock mode is intended for safe browsing; live mode appears only where the package supports it.
Generated interface
REST
The exact route set comes from each model's @smrt({ api }) policy
and the application manifest—not from a second controller definition.
GET /api/tenants
GET /api/tenants/:id
POST /api/tenants
PATCH /api/tenants/:idWhat stays consistent
- Tenant scope and operation permissions run before model access.
- OpenAPI and generated clients use the same manifest fields.
- Packages can expose read-only or custom action sets.
Generated interface
MCP
AI tools are generated from the methods you explicitly include. Their inputs are validated against the same model metadata used by REST and CLI.
tenants_list({ where, limit, orderBy })
tenants_get({ id })
tenants_create({ data })
tenants_update({ id, data })Agent-safe by construction
- Tool availability can be narrower than the HTTP API.
- Principal execution intersects RBAC, agent ceilings, and persona tools.
- Descriptions and schemas are emitted from source metadata.
Browser-native agent interface
WebMCP
The generated web definitions carry the same tool names, descriptions, and JSON Schema
inputs as MCP. smrt-web registers them with document.modelContext.
import { webMcpToolDefinitions } from '@happyvertical/smrt-virt-web';
import { registerWebMcpTools } from '@happyvertical/smrt-web';
// Omitting an exposure policy registers read-only tools only.
const dispose = registerWebMcpTools(webMcpToolDefinitions);The page keeps its security boundary
- Tools execute through generated REST fetchers as the signed-in page user.
- Authentication, tenant gates, permissions, and field policy stay server-side.
- Feature detection makes registration a safe no-op outside supported browsers.
Generated interface
CLI
The app CLI turns manifest resources into predictable operator commands while keeping package-specific actions close to their models.
smrt tenants list --limit 25 --order-by created_at:desc
smrt tenants get <id>
smrt tenants create --from-file ./record.jsonMade for automation
- Machine-readable output and file-based input.
- The same include/exclude policy as other surfaces.
- Migration, introspection, dispatch, and knowledge commands stay in smrt-cli.