Standards Docs
Concepts

RBAC

Role-based access control in standards — roles attached to actors, enforced on the API and in the UI.

standards has built-in role-based access control. Roles are attached to actors (users and agents); guards on the API and hooks on the UI enforce permissions.

Define roles

Roles live in the actor_roles table alongside roles and role_permissions. Seed them via Supabase migrations — standards does not ship a one-liner SDK helper for this because role definitions are project-specific. A typical seed creates at minimum an owner role and a member role, then inserts role_permissions rows that grant actions on schema objects. The runtime also auto-upserts a default agent_default role at boot so newly-created agent actors have a session-capable role out of the box.

To read all roles in the current tenant at runtime, use useRoles() from @stndrds/react:

// @noverify
import { useRoles } from "@stndrds/react";

const { data: roles } = useRoles();
// roles — Role[] with id, name, label, system

To read the roles assigned to a specific user, use useUserRoles(userId).

Protect the API

@stndrds/adapter-nestjs registers ApiKeyAuthGuard as a global guard when you use CoreModule. It validates the Authorization: Bearer <key> header and populates the AsyncLocalStorage actor context. Role enforcement on top of authentication is done with a custom guard in your application — read the actor roles from the context and reject if the required role is missing.

// @noverify
import { Injectable, CanActivate, ExecutionContext } from "@nestjs/common";
import { TenantContext } from "@stndrds/adapter-nestjs";

@Injectable()
export class RequireAdminGuard implements CanActivate {
  async canActivate(ctx: ExecutionContext): Promise<boolean> {
    const actorId = TenantContext.getActorId();
    // check actor_roles for the current tenant and actorId
    // return false to reject
    return true;
  }
}

Breaking in vNEXT: Actor is now the single canonical identity. userId has been removed from TenantContext, BaseService, BaseRepository, and PolicyContext. Use actorId everywhere. withTenantContext(tenantId, fn, actorId?) now takes an optional actorId (the userId parameter is gone). checkPermission and buildPolicyContext were renamed accordingly.

See the NestJS backend guide for the full wiring, including how TenantContext exposes the current actor id.

Gate the UI

useMyPermissions() returns the effective permissions for the current user — derived from all assigned roles. Use it to conditionally render actions. If you need to check for a specific role name rather than a permission, pair useMyProfile() with useUserRoles().

// @noverify
import { useMyPermissions } from "@stndrds/react";

function DeleteButton({ onDelete }: { onDelete: () => void }) {
  const { data: permissions } = useMyPermissions();
  const canDelete = permissions?.objectPermissions["companies"]?.includes("delete");
  if (!canDelete) return null;
  return <button onClick={onDelete}>Delete</button>;
}

Prefer useMyPermissions() for action gating. It reflects the full role stack and respects wildcard grants (*). Checking a raw role name is brittle when roles change.

Actor model

Actors are either users or agents. Both participate in the same RBAC system — both appear in actor_roles, both can be assigned any role, and both show up in audit logs. See Agents for how agent actors are created automatically.

On this page