Roles & Permissions
Ironflow ships with built-in roles for both tenant and platform operations. All roles are stored in a unified roles table and enforced via RBAC on every authenticated request.
Built-in Tenant Roles
Section titled “Built-in Tenant Roles”| Role | Description |
|---|---|
| admin | Full access — all read, write, and delete operations |
| developer | Read and write access — can create and modify resources |
| viewer | Read-only access — can list and read resources |
Built-in Platform Roles
Section titled “Built-in Platform Roles”| Role | Description |
|---|---|
| platform_admin | Full access to all platform operations (wildcard permission) |
| platform_operator | Tenant management, impersonation, read access to users/keys/roles |
| platform_viewer | Read-only access to all platform resources |
See Platform Roles & Actions for the complete platform permissions matrix.
Permissions Matrix
Section titled “Permissions Matrix”The table below lists the core permission actions and which roles can perform them. A few admin-only actions (org:export, cluster:rotate-token) are matched solely by the admin wildcard and are omitted here.
| Action | Admin | Developer | Viewer |
|---|---|---|---|
functions:register |
✓ | ✓ | |
functions:invoke |
✓ | ✓ | |
functions:list |
✓ | ✓ | ✓ |
functions:read |
✓ | ✓ | ✓ |
runs:read |
✓ | ✓ | ✓ |
runs:cancel |
✓ | ✓ | |
events:emit |
✓ | ✓ | |
events:subscribe |
✓ | ✓ | ✓ |
streams:read |
✓ | ✓ | ✓ |
entities:read |
✓ | ✓ | ✓ |
entities:append |
✓ | ✓ | |
projections:read |
✓ | ✓ | ✓ |
projections:manage |
✓ | ✓ | |
secrets:read |
✓ | ✓ | |
secrets:manage |
✓ | ||
users:read |
✓ | ✓ | ✓ |
users:manage |
✓ | ||
apikeys:read |
✓ | ✓ | ✓ |
apikeys:manage |
✓ | ✓ | |
orgs:read |
✓ | ✓ | ✓ |
orgs:manage |
✓ | ||
agent:tools:register |
✓ | ✓ | |
agent:tools:invoke |
✓ | ✓ | |
agent:tools:unregister |
✓ | ✓ | |
agent:tools:read |
✓ | ✓ | ✓ |
How Actions Are Resolved
Section titled “How Actions Are Resolved”Each incoming request is mapped to a permission action based on the HTTP method and path:
- ConnectRPC — the RPC method name determines the action (e.g.
IronflowService/Invoke→functions:invoke) - REST API — the HTTP method, first path segment, and any verb sub-path determine the action. Most routes follow
method + resource(e.g.GET /api/v1/runs→runs:read); verb sub-paths override (e.g.POST /api/v1/runs/{id}/cancel→runs:cancel,POST /api/v1/functions/{id}/invoke→functions:invoke,POST /api/v1/projections/{name}/rebuild→projections:manage) - WebSocket — the
/wsupgrade requiresevents:subscribe
If a request maps to an action that the caller’s roles don’t include, the server returns 403 Forbidden. If no valid credentials are provided at all, the server returns 401 Unauthorized.
IRN (Ironflow Resource Naming)
Section titled “IRN (Ironflow Resource Naming)”Every resource in Ironflow has a unique identifier following the IRN format:
irn:ironflow:{org_id}:{project_id}:{type}:{environment}:{resource}Valid resource types: function, run, event, stream, projection, secret, org, role, policy, user, project.
IRN patterns support wildcards (*) for any segment, enabling flexible permission matching.
Custom Roles
Section titled “Custom Roles”Beyond the built-in roles, you can create custom roles scoped to an organization. Custom roles have no built-in permissions — they derive all access from attached policies.
Managing Custom Roles
Section titled “Managing Custom Roles”# Create a custom roleironflow role create billing-team --org org_default
# List all roles (built-in + custom)ironflow role list --org org_default
# Get a single roleironflow role get <role_id>
# Assign a policy to a roleironflow role assign-policy <role_id> <policy_id>
# Remove a policy from a roleironflow role remove-policy <role_id> <policy_id>
# Delete a custom role (built-in roles cannot be deleted)ironflow role delete <role_id>Policy-Based Authorization
Section titled “Policy-Based Authorization”Policies define fine-grained access rules using actions, resource patterns, and optional CEL conditions.
| Field | Description | Example |
|---|---|---|
name |
Human-readable policy name (unique per org) | deny-weekend-deploys |
effect |
deny only (#943, ADR 0016 T2 — see Evaluation Rules) |
deny |
actions |
Comma-separated action patterns (wildcards supported) | functions:list,runs:read |
resources |
Comma-separated IRN patterns (wildcards supported) | irn:ironflow:*:*:function:prod:* |
condition |
Optional CEL expression (must return boolean) | request.environment == "production" |
Evaluation Rules
Section titled “Evaluation Rules”Authorization is two-layered:
- System RBAC (Layer 1) is the authoritative ALLOW gate. If the caller’s roles do not grant the action, access is denied immediately — CEL policies are not consulted.
- CEL policies (Layer 2) are subtractive only. Only
deny-effect policies are evaluated. As of #943 (ADR 0016 T2),effect=allowis rejected at write with HTTP 400 (policy_effect_allow_deprecated) across every input surface (server API, platform API, template install, CLI, dashboard, SDK). Legacyallowrows in the database are inert at evaluation and are removed by migration 030. - Matching deny policies have their CEL conditions evaluated. Deny always wins — if any matching deny policy’s condition passes, access is denied.
- If RBAC allowed and no deny policy matches (or no policies exist), access is allowed.
To grant a capability that RBAC does not currently provide, edit the role assignment (Layer 1). Do not attempt to express grants as Layer 2 policies — that path is by design closed.
Managing Policies
Section titled “Managing Policies”# Create a deny policy with CEL conditionironflow policy create \ --name deny-weekend-deploys \ --effect deny \ --actions "functions:register" \ --resources "irn:ironflow:*:*:function:prod:*" \ --condition 'request.timestamp.getDayOfWeek() == 0 || request.timestamp.getDayOfWeek() == 6'
# List policiesironflow policy list --org org_default
# Get a single policyironflow policy get <policy_id>
# Update a policy (creates a new version)ironflow policy update <policy_id> --actions "..." --resources "..."
# Test a policy against a sample requestironflow policy test --policy-id <policy_id> --request-file request.json
# List a policy's version historyironflow policy versions list <policy_id>
# Roll back to a previous versionironflow policy rollback <policy_id> <version>
# Install a policy template bundleironflow policy template install <template_name>
# Delete a policyironflow policy delete <policy_id>For full details on CEL expressions and the REST API, see Custom Roles & CEL Policies.