Core Concepts

SaaSLayer Permissions combines role-based access control, attribute-based policy conditions, relationship-aware hierarchy, and direct resource grants. The model is broad, but the core pieces are small.

Principals

A principal is something that can receive permissions.

Supported principal types include:

  • USER
  • AGENT
  • SERVICE_ACCOUNT

Users usually represent humans. Agents and service accounts represent automated actors, integrations, or delegated workflows.

Agent and service account ids and names

Agents and service accounts get a server-generated, immutable id (agt_… for agents, sa_… for service accounts). You choose a name, which is unique per owner and principal type and becomes reusable once the principal is deleted:

POST /principals/serviceAccount
Idempotency-Key: deploy-bot-v1

{ "name": "deploy-bot", "ownerType": "org", "ownerId": "org_acme" }
  • 201 returns the new principal with its generated id. Store the id wherever you reference the principal (grants, audit, your own data such as note.createdBy).
  • Retrying with the same Idempotency-Key (default: a hash of the payload) and the same payload returns 200 with the existing principal.
  • The same name under the same owner with a different key or payload returns 409 NameTaken.
  • Passing an id in the body returns 400.

Resolve a name to its principal with GET /principals/serviceAccount?name=deploy-bot&ownerType=org&ownerId=org_acme.

Users keep the id you give them. Creating a user never overwrites one: a repeat create whose fields already match is a no-op, anything else is 409 Conflict. During a maintenance cutover every principal create answers 503 PrincipalCreationPaused with a Retry-After header; retry after that many seconds.

Every decision is keyed by the principal's type as well as its id, so an agent never inherits a user's permissions, grants or orgs even when their ids are equal.

DELETE /principals/{type}/{id} removes the principal together with its role assignments, direct grants, memberships, sessions, delegated grants, standing delegations it owns or grants through, API keys, auth methods and cached permission documents, and frees its name. Re-creating the same name produces a new id, so nothing that referenced the old principal applies to the new one. A principal that still owns other principals returns 409 until they are transferred or deleted.

Authenticating principals (USER, AGENT, SERVICE_ACCOUNT) can also act for another principal via agent delegation, and can authenticate themselves through external identity providers via federated principal auth instead of long-lived API keys.

Organizations, workspaces, and groups

The hierarchy gives permissions a business context:

Organization
  Workspace
    Group

Use organizations for customer accounts. Use workspaces (/workspaces) for subdivisions inside an organization, such as environments or product boundaries. Use groups for teams or collections inside a workspace.

Membership and role assignment are separate. A user can belong to a workspace without having any useful permissions there.

Roles

A role is a named bundle of permissions.

Example permissions:

[
  { "effect": "can", "action": "read", "resource": "invoice", "service": "billing" },
  { "effect": "can", "action": "approve", "resource": "invoice", "service": "billing" },
  { "effect": "deny", "action": "delete", "resource": "invoice", "service": "billing" }
]

Roles can be assigned globally or at a scope such as a workspace. Roles can also inherit from parent roles.

Policies

Policies are reusable statement bundles that can attach to users, roles, organizations, workspaces, or groups.

Use policies when a rule is more expressive than a simple role permission, or when you want to attach the same conditional logic in multiple places.

Policies can be written as JSON statements or authored in Cedar and compiled into SaaSLayer Permissions statements at write time.

Resources

Resources are the things your application protects: invoices, devices, documents, projects, API routes, tools, or anything else.

Simple resource strings work:

device
invoice
document/doc-123

For cross-service resource identity, use XRNs:

xrn:saas:billing:us-west-1:tenant-acme-prod:invoice/inv-123

Actions

Actions are verbs in your product domain:

read
write
delete
approve
export
invite

Use stable product actions instead of HTTP verbs. For example, approve is clearer than POST.

Scopes

A scope limits where a permission applies.

Common scopes:

  • Workspace ID
  • Organization ID
  • Group ID
  • null for global permissions

When a permission check includes a scope, SaaSLayer Permissions evaluates global permissions plus permissions scoped to that value.

Denies

Deny rules override allows. If one matching permission says deny, access is denied even if another matching permission says can.

Use denies sparingly. They are useful for hard restrictions, compliance boundaries, and inherited roles where a child role must explicitly block a parent capability.

Permission boundaries

A permission boundary is a maximum-access ceiling. A role or principal might allow an action, but the boundary can still block it.

Use boundaries for administrative guardrails:

  • Support users can read customer data but cannot delete it.
  • Contractors can manage tickets but cannot export data.
  • Service accounts can write events but cannot manage users.

Grants

Grants are direct resource shares. They are useful when role-based access does not naturally express a one-off share.

Example: a user does not have read on every document in a tenant, but someone shares document/doc-123 directly with them.

A grant names the grantee's type and id, the action, and the resource's scope, service, type and id — all required except scope, which defaults to * (global). A grant answers only checks in its own service and scope, and a global grant answers only checks with no scope. The caller must be allowed in the grant's scope and hold the permission it grants. A grant is consulted only when no role or policy allowed or denied the check.

Materialized permission documents

SaaSLayer Permissions materializes effective permissions into a PERMISSIONS document per principal. Permission checks read that document and evaluate in memory.

That is why the hot path can be a single DynamoDB read. Writes may trigger rebuilds or invalidation, but reads stay fast.

Sessions

Three session shapes coexist:

KindSubject acts asWhen to use
Assumed-role sessionA different role for a bounded window.Support engineer temporarily assumes a customer-support role.
Direct principal sessionThe subject itself.Workload authenticates via federated principal auth and acts as itself.
Delegated principal sessionThe subject, but actingFor another principal.Agent acts on a user's behalf via agent delegation.

All three are bearer-token sess_<jwt> formats and authorize through the same one-row-lookup hot path.