- Service account management — Create, rotate, delete service accounts and issue JWT tokens. Restricted to privileged workspaces defined via
PRIVILEGED_WORKSPACES. - Product bindings — CRUD operations on the
product_bindingscollection to link resources to users, orgs, or groups. Available to any workspace. - Access checking — High-level
checkAccessfunction that combines permissions, scopes, and bindings to determine if a caller can access a resource. Available to any workspace.
Service Account Functions
getServiceAccountToken — Get a JWT token for a service account
Returns the token response including
accessToken, tokenType, expiresAt, permissions, and scopes.
createServiceAccount — Create a new service account
Returns the created service account including
slug and clientSecret. If the service account already exists, returns { slug } without error.
rotateServiceAccountSecret — Rotate a service account’s client secret
Returns the new
clientSecret.
deleteServiceAccount — Delete a service account
Cache behavior
The module caches client secrets in memory after creation or rotation. This cache is automatically invalidated when:- A service account is deleted (clears secret + permissions cache)
- A service account secret is rotated (clears secret cache)
- A service account is updated (clears permissions cache)
Service Account Example
Product Bindings Functions
Product bindings associate resources (agents, workflows, etc.) to principals (users, orgs, groups) within a workspace. All binding functions automatically scope queries to the caller’sworkspaceId — it cannot be overridden.
findBindings — Query bindings
Returns an array of binding documents.
findAndCountBindings — Query bindings with total count
findBindings. Returns { items: [...], total: number }.
countBindings — Count matching bindings
Returns a number.
insertBinding — Create a binding
The
workspaceId and workspaceSlug are set automatically from the caller’s context. Emits a runtime.bindings.created event.
A unique constraint prevents duplicate bindings for the same (workspaceId, resourceType, resourceId, principalType, principalId).
updateBinding — Update an existing binding
Only
roleSlug can be updated through this function — other fields are immutable. Emits a runtime.bindings.updated event when at least one binding is modified.
deleteOneBinding — Delete a single binding
Emits a
runtime.bindings.deleted event if a binding was deleted.
deleteManyBindings — Delete multiple bindings
Emits a
runtime.bindings.deleted.many event if any bindings were deleted.
Workspace cleanup
When a workspace is deleted, all its bindings are automatically removed.Access Check Function
ThecheckAccess function provides a high-level access control check that combines three sources: permissions (from run.permissions), scopes (from run.scopes), and bindings (from the product_bindings collection). This replaces the need for complex DSUL-based permission checking.
checkAccess — Check resource access
resourceType/action, it only checks authentication and returns isWorkspaceAdmin.
Return value
The
error object follows the same shape as _auth automations:
Resolution order
-
Authentication — The caller must have a
userId(user session) or anorgSlug(org API key). If neither is present, returnsUnauthorizedimmediately. -
Permissions — Checked from
run.permissions(set by the token’s role). The function checks in order:*withmanage→ workspace admin{workspaceSlug}withmanage→ workspace admin{workspaceSlug}:{resourceType}withmanageor{action}→ access- If no permission matches → returns
Forbiddenimmediately (scopes and bindings are not checked)
-
Scopes — Parsed from
run.scopes(only if permission was granted):*,{workspaceSlug}:*, or{workspaceSlug}:{resourceType}:*→ wildcard scope (all resources){workspaceSlug}:{resourceType}:{id}→ addsidto scoped IDs
-
Bindings — Looked up in
product_bindingsfor the caller’s identity (userId, org, groups). Only checked for single resource mode when the scope doesn’t match. Each matching binding is then evaluated against the requested action through role-based bindings.
Role-based bindings
Bindings can carry aroleSlug to restrict which actions they grant. The role definitions themselves live outside the binding — they must be passed to checkAccess via the roles parameter (typically loaded from config.roles):
checkAccess only stops at the first binding that grants the requested action. If a user has multiple bindings (e.g. reader and editor), the most permissive applicable one wins.
When the binding is granted, the reason field reflects the role: binding:user:editor, binding:org:admin, etc.
Modes
Auth-only (noresourceType, no action):
- Returns
{ granted: true, isWorkspaceAdmin }— just confirms the caller is authenticated
- Returns
{ granted: false, error: { error: 'Unauthorized', message: 'Authentication required' } }
resourceId provided):
- If wildcard scope →
{ granted: true, reason: 'wildcard-scope', hasWildcardScope: true, isWorkspaceAdmin } - If scoped ID match →
{ granted: true, reason: 'scope', hasWildcardScope: false, isWorkspaceAdmin } - If binding found and the binding’s
roleSlugallows the action →{ granted: true, reason: 'binding:{principalType}' or 'binding:{principalType}:{roleSlug}', hasWildcardScope: false, isWorkspaceAdmin } - Otherwise →
{ granted: false, hasWildcardScope: false, error: { error: 'Forbidden', message: '...' } }
list: true, no resourceId):
- If wildcard scope →
{ granted: true, grantedIds: [], hasWildcardScope: true }(caller sees everything) - Otherwise →
{ granted: true, grantedIds: [...], hasWildcardScope: false }(merged+deduplicated scoped IDs + binding IDs)
resourceType + action, no resourceId, no list):
- Returns
{ granted: true, reason: 'permission', hasWildcardScope, isWorkspaceAdmin }