Grants & permissions

Manage fine-grained access control with Engine-backed allow and deny grants.

Overview

Grants control who can access what data in your workspace. Each grant maps one workspace principal (human, group, Agent, orEveryone) to a stable resource (Workspace, Namespace, Table, or View) with an effect (allow or deny). Allow grants also have a role (viewer, editor, or admin). The grant stores oneresourceId; Loam derives its kind and current Namespace ancestry from the catalog tree.

Access is fail-closed: if no grant matches a request, it is denied. Workspace owners receive an explicit Admin allow targeted by the Workspace public ID. Ownership, a JWT role label, an API-key secret, or an endpoint never bypasses the grant resolver.

Roles

Three roles, each inclusive of the one below:

RolePermissions
viewerSELECT queries and list or browse metadata on the granted resource.
editorEverything in viewer, plus row mutations and creating, updating, renaming, or deleting ordinary Tables and Views. Editors cannot change Namespace structure, manage connections, publish data, or manage grants.
adminEverything in editor. A human Admin can also manage Namespace structure, connections, publication, and grants on a node where Admin is effective and throughout that node's subtree.

Allow and deny effects

An allow grant carries a role. A deny grant carries no role. For an authenticated caller, a deny addressed to the direct user or any of their current groups vetoes authenticated authority, even when another authenticated subject or a descendant node has an allow.

Everyone is evaluated as a separate lane for every caller. An Everyone deny vetoes that lane and a deeper Everyone allow cannot reopen it, but it does not cancel authenticated human or group grants. If no applicable authenticated deny exists, Loam selects the nearest matching allow for the direct human and each current group. It combines those results with any surviving Everyone allow using the most permissive role; Everyone is capped at viewer. If no applicable allow remains, access is denied.

Hierarchical narrowing

Grants inherit through the catalog's actual Workspace and Namespace ancestry, at any supported depth. A Workspace grant applies throughout the workspace, while a Namespace grant applies to all descendant Namespaces, Tables, and Views. For each subject, an allow on a nearer ancestor overrides its broader allows. Direct-principal and current-group denies veto authenticated authority regardless of distance; Everyone denies veto only the public lane regardless of distance.

After resolving the nearest allow independently for each subject, Loam combines those subject roles using the most permissive result.

GET /v1/workspaces/{workspacePublicId}/grants?resourceId={administeredRootId}

List grants on nodes that the requesting human currently administers. A Workspace Admin can list all grants in the workspace; a Namespace Admin sees only grants and protected subject metadata within the explicitly requested administered subtree. Use the canonical Workspace ID asresourceId to request the full workspace. Editors and machine credentials cannot list protected grant data.

GET/v1/workspaces/{workspacePublicId}/grants?resourceId={administeredRootId}
Example response
[
  {
    "id": "018f5f65-4a8c-7c2e-a4d9-7c7bf7e43101",
    "workspaceId": "rw-us-east1-ws-0195a3c47d3e7ab18c4f123456789abc",
    "effect": "allow",
    "subjectPrincipalId": "018f5f65-4a8c-7c2e-a4d9-7c7bf7e43104",
    "resourceId": "rw-us-east1-node-0195a3c47d3e7ab18c4f123456789abd",
    "role": "viewer",
    "createdAt": "2026-03-15T10:30:00Z"
  },
  {
    "id": "018f5f65-4a8c-7c2e-a4d9-7c7bf7e43105",
    "workspaceId": "rw-us-east1-ws-0195a3c47d3e7ab18c4f123456789abc",
    "effect": "allow",
    "subjectPrincipalId": "018f5f65-4a8c-7c2e-a4d9-7c7bf7e43100",
    "resourceId": "rw-us-east1-node-0195a3c47d3e7ab18c4f123456789abf",
    "role": "viewer",
    "createdAt": "2026-03-14T09:00:00Z"
  }
]

POST /v1/workspaces/{workspacePublicId}/grants

Create or replace the canonical grant for a subject and node. Only an authenticated human with effective Admin on the target node can do this. Namespace Admin authority therefore delegates within that subtree, but not to ancestors or siblings. Editors and delegated or machine Admins cannot manage grants. This endpoint manages human, group, Agent, and Everyone principals. API keys and MCP OAuth authorizations are authenticators and never own grants.

Request body

json
{
  "effect": "allow",
  "subjectPrincipalId": "018f5f65-4a8c-7c2e-a4d9-7c7bf7e43104",
  "resourceId": "rw-us-east1-node-0195a3c47d3e7ab18c4f123456789abd",
  "role": "viewer"
}
FieldTypeRequiredDescription
effectstringYesallow or deny. A deny is an absolute veto and must omit role.
subjectPrincipalIdstringYesThe canonical same-workspace UUID for one human, group, Agent, or the seeded Everyone principal. Principal kind is resolved by Loam and is never repeated in the grant.
resourceIdstringYesThe stable regional Workspace public ID or public node ID for the target Namespace, Table, or View. Do not send a resource kind, path, or ancestor list.
rolestringFor allowviewer, editor, or admin. Omit for deny.

Try it

POST/v1/workspaces/{workspacePublicId}/grants
Request body
{
  "effect": "allow",
  "subjectPrincipalId": "018f5f65-4a8c-7c2e-a4d9-7c7bf7e43104",
  "resourceId": "rw-us-east1-node-0195a3c47d3e7ab18c4f123456789abd",
  "role": "viewer"
}
Example response
{
  "id": "018f5f65-4a8c-7c2e-a4d9-7c7bf7e43106",
  "workspaceId": "rw-us-east1-ws-0195a3c47d3e7ab18c4f123456789abc",
  "effect": "allow",
  "subjectPrincipalId": "018f5f65-4a8c-7c2e-a4d9-7c7bf7e43104",
  "resourceId": "rw-us-east1-node-0195a3c47d3e7ab18c4f123456789abd",
  "role": "viewer",
  "createdAt": "2026-03-16T12:00:00Z"
}

DELETE /v1/workspaces/{workspacePublicId}/grants/{grantId}

Revoke a grant by its ID. Only an authenticated human with effective Admin on the grant's target node can do this. Queued durable work rechecks current grants before commit, so revocation can stop an in-flight mutation.

bash
curl -X DELETE https://rw-us-east1.api.loamdata.com/v1/workspaces/rw-us-east1-ws-0195a3c47d3e7ab18c4f123456789abc/grants/018f5f65-4a8c-7c2e-a4d9-7c7bf7e43101 \
  -H "Authorization: Bearer $JWT"

Returns 204 No Content on success.

Examples

Grant a user read access to a Namespace subtree

bash
curl -X POST https://rw-us-east1.api.loamdata.com/v1/workspaces/rw-us-east1-ws-0195a3c47d3e7ab18c4f123456789abc/grants \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "effect": "allow",
    "subjectPrincipalId": "018f5f65-4a8c-7c2e-a4d9-7c7bf7e43104",
    "resourceId": "rw-us-east1-node-0195a3c47d3e7ab18c4f123456789abd",
    "role": "viewer"
  }'

Deny a user access to a table

bash
curl -X POST https://rw-us-east1.api.loamdata.com/v1/workspaces/rw-us-east1-ws-0195a3c47d3e7ab18c4f123456789abc/grants \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "effect": "deny",
    "subjectPrincipalId": "018f5f65-4a8c-7c2e-a4d9-7c7bf7e43104",
    "resourceId": "rw-us-east1-node-0195a3c47d3e7ab18c4f123456789abe"
  }'

Publish a Namespace with an Everyone viewer grant

bash
curl -X POST https://rw-us-east1.api.loamdata.com/v1/workspaces/rw-us-east1-ws-0195a3c47d3e7ab18c4f123456789abc/grants \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "effect": "allow",
    "subjectPrincipalId": "018f5f65-4a8c-7c2e-a4d9-7c7bf7e43100",
    "resourceId": "rw-us-east1-node-0195a3c47d3e7ab18c4f123456789abd",
    "role": "viewer"
  }'

Group grants

Group grants resolve against current regional membership. Removing a human or Agent from a group removes inherited access on the next request. Adding a group as an Agent-use grantee does not add the Agent to that permission group.

Authenticator ceilings

API keys and MCP OAuth authorizations authenticate exactly one human or Agent principal. They do not appear as grant subjects and cannot copy grants. Optional node ceilings use stable public node IDs rather than kinds, paths, names, or caller-supplied ancestry.

Every request intersects the authenticator's live state and optional ceiling with the bound principal's current grants, groups, capabilities, and entitlements. A human related to an Agent assumption or OAuth connection supplies eligibility and audit attribution only; their personal data authority is never added. Machine principals cannot manage grants even when their data role is Admin.

Error responses

HTTP StatusCodeDescription
401INVALID_TOKENMissing, invalid, or expired JWT.
403ACL_DENIEDYour token does not have admin access to manage grants.
404NOT_FOUNDGrant not found (for DELETE).
400INVALID_ARGUMENTInvalid subject type, subject ID, node ID, role, or grant ID.

Last updated 2026-08-23