Kizaki
ReferenceInspire

Namespaces

Define tenant scope once and let the runtime enforce it across reads, writes, includes, and live queries.

A namespace binds a set of entities to a principal attribute. Every query against those entities automatically injects the namespace scope. You never thread tenant filters through application code by hand.

Syntax

namespace Tenant {
  scope: principal.organizationId,
  entities: [Project, Invoice, Document],
}

The scope field is a path into the principal struct. The referenced attribute must be present in the auth block's principal section.

What It Affects

Every operation on a namespaced entity is automatically scoped:

  • Reads: WHERE organization_id = $principal.organizationId injected on every query
  • Writes: namespace column auto-set on every insert
  • Includes: related entities in the same namespace are scoped
  • Live queries: subscription results filtered to the current tenant

This happens at the runtime layer. Application code cannot omit the filter.

Configuring The Principal

The namespace scope attribute must come from the principal. Use @selectFrom for multi-tenant apps where a user belongs to multiple organizations:

auth {
  providers: [email, google],
  sessionDuration: 30d,

  principal {
    organizationId: Organization.id @selectFrom(OrgMembership.orgId),
  }
}

With @selectFrom, the user picks which organization context to operate in at login or context-switch. All subsequent queries scope to that organization.

For the full auth model, see Inspire Auth.

Cross-Tenant Access

Cross-tenant access requires a @system function. Normal user principals cannot access entities outside their namespace scope:

@system("platform_admin") {
  @grant read on Project
  @grant read on Invoice
}

A scheduled job or webhook handler running under this system role can read across namespaces.

Example

entity Organization {
  name: string,
  slug: string @unique,
}

entity OrgMembership {
  orgId:  Organization.id @onDelete(cascade),
  userId: __User.id @onDelete(cascade),
  role:   OrgRole,

  @unique([orgId, userId])
}

enum OrgRole { Member, Admin }

entity Project {
  name:   string,
  status: ProjectStatus = Active,
}

namespace Tenant {
  scope: principal.organizationId,
  entities: [Project],
}

Once Project is in the Tenant namespace, select(Project) returns only the current organization's projects. No manual where organizationId == ... needed.

  • Namespace entities that always belong to the current tenant
  • Namespace all entities that must never be visible across tenant boundaries
  • Use a system role for operations that genuinely cross tenant scope

Related guide: Multi-Tenancy

On this page