Role-Based Access Control Enterprise
Define roles and issue scoped API tokens for Rivet Enterprise Edition.
Role-based access control (RBAC) determines which actors, workers, gateways, and KV entries each API bearer token can access.
RBAC requires Rivet Enterprise Edition. It is separate from Kubernetes RBAC, which controls access to your cluster.
Use the ACL API to define permissions and issue tokens for a namespace. Keep your bootstrap admin token private; use scoped tokens for applications and individual users.
Setup
- API endpoint: Your control-plane endpoint, such as
https://rivet.mycompany.com - Admin token: Configured as
RIVET__AUTH__ADMIN_TOKEN. For BYOC, see Credentials & dashboard. Use it to bootstrap ACL. - Auth header:
Authorization: Bearer <token>for/acl/*calls. The admin token hasacl:create+token:createby default.
Model
Rivet groups permissions into four layers, each composed of the layer below it:
rule -> policy -> role -> token
- Rule: a single permission grant. Allows the listed operations on a resource in a namespace.
- Policy: a named bundle of rules.
- Role: a named bundle of policies.
- Token: a bearer string bound to one or more roles. Carries the union of all granted permissions.
Rules are additive. There are no deny rules. A token is allowed an operation if any of its rules grant it.
Rule schema
| Field | Type | Description |
|---|---|---|
namespace | { id: "<ns_id>" } or "any" | Which namespace the rule applies to. |
resource | enum | One of actor, actor_gateway, actor_kv, runner, runner_config, namespace, datacenter, acl, token. |
target | { id: "<id>" } or "any" | Which specific instance of the resource. |
operations | string[] | Subset of create, read, update, delete, list. |
Manifest
The example namespace ID must be replaced with your actual namespace ID.
POST /acl/manifest is an atomic upsert of rules, policies, and roles in one call. It is the recommended way to provision ACL.
curl -X POST "$RIVET_ENDPOINT/acl/manifest" \
-H "Authorization: Bearer $RIVET_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"rules": { "pub-actor": { "namespace": { "id": "ns_abc123" }, "resource": "actor", "target": "any", "operations": ["read"] } },
"policies": { "pub-policy": { "rules": ["pub-actor"] } },
"roles": { "pub-role": { "policies": ["pub-policy"] } }
}'
# Expected: JSON containing the upserted rules, policies, and roles
All ACL endpoints
Manifest covers most provisioning. The per-resource endpoints below exist for fine-grained reads, individual upserts, and deletes.
| Method | Path | Use |
|---|---|---|
POST | /acl/manifest | Atomic upsert of rules + policies + roles. |
POST | /acl/tokens | Mint a token. Body: { roles, prefix?, duration?, token? }. |
GET | /acl/tokens/inspect | Inspect the current Authorization token. |
DELETE | /acl/tokens/{token_id} | Revoke a token. |
GET / POST / DELETE | /acl/rules/{rule_name} | Read, upsert, or delete a rule. |
GET / POST / DELETE | /acl/policies/{policy_name} | Read, upsert, or delete a policy. |
GET / POST / DELETE | /acl/roles/{role_name} | Read, upsert, or delete a role. |
Common token types
The patterns below cover the publishable / secret / inspector split most deployments need.
Set your endpoint, admin token, and actual namespace ID before running these examples:
export RIVET_ENDPOINT="http://localhost:6420"
export RIVET_ADMIN_TOKEN="..." # control-plane RIVET__AUTH__ADMIN_TOKEN value
export RIVET_NAMESPACE="ns_abc123" # from GET /namespaces?name=...
Dashboard role
For a person signing into the Enterprise dashboard. This example grants namespace-scoped actor, worker, pool, and gateway management, inspector access through actor_kv:read, and permission to list datacenters. It does not grant ACL or token administration.
cat > dash-manifest.json <<EOF
{
"rules": {
"dash-namespace": { "namespace": { "id": "$RIVET_NAMESPACE" }, "resource": "namespace", "target": { "id": "$RIVET_NAMESPACE" }, "operations": ["read"] },
"dash-actor": { "namespace": { "id": "$RIVET_NAMESPACE" }, "resource": "actor", "target": "any", "operations": ["create", "read", "update", "delete", "list"] },
"dash-actor-kv": { "namespace": { "id": "$RIVET_NAMESPACE" }, "resource": "actor_kv", "target": "any", "operations": ["read"] },
"dash-actor-gateway": { "namespace": { "id": "$RIVET_NAMESPACE" }, "resource": "actor_gateway", "target": "any", "operations": ["create", "read", "update", "delete", "list"] },
"dash-runner": { "namespace": { "id": "$RIVET_NAMESPACE" }, "resource": "runner", "target": "any", "operations": ["create", "read", "update", "delete", "list"] },
"dash-runner-config": { "namespace": { "id": "$RIVET_NAMESPACE" }, "resource": "runner_config", "target": "any", "operations": ["create", "read", "update", "delete", "list"] },
"dash-datacenter": { "namespace": "any", "resource": "datacenter", "target": "any", "operations": ["read", "list"] }
},
"policies": { "dash-policy": { "rules": ["dash-namespace", "dash-actor", "dash-actor-kv", "dash-actor-gateway", "dash-runner", "dash-runner-config", "dash-datacenter"] } },
"roles": { "dash-role": { "policies": ["dash-policy"] } }
}
EOF
curl -X POST "$RIVET_ENDPOINT/acl/manifest" \
-H "Authorization: Bearer $RIVET_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d @dash-manifest.json
curl -X POST "$RIVET_ENDPOINT/acl/tokens" \
-H "Authorization: Bearer $RIVET_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "roles": ["dash-role"] }'
# Expected: token metadata and a token value; keep the token private
Issue one token per dashboard user so revoking individual access does not log everyone out. Optionally pass "duration": 900 (seconds) to mint a short-lived token and re-issue on demand from a session layer.
Publishable key
For a frontend client only when every holder should have the listed permissions across the namespace. This example can create, read, update, and connect to actors, but cannot list or delete actors or read their KV.
A publishable token is not a user identity or tenant boundary. Anyone can copy it and exercise its permissions. Restrict rules and enforce application authorization for private actors. The pk prefix is only a label; it does not limit permissions.
cat > pk-manifest.json <<EOF
{
"rules": {
"pub-actor": { "namespace": { "id": "$RIVET_NAMESPACE" }, "resource": "actor", "target": "any", "operations": ["create", "read", "update"] },
"pub-gateway": { "namespace": { "id": "$RIVET_NAMESPACE" }, "resource": "actor_gateway", "target": "any", "operations": ["create", "read", "update", "delete", "list"] }
},
"policies": { "pub-policy": { "rules": ["pub-actor", "pub-gateway"] } },
"roles": { "pub-role": { "policies": ["pub-policy"] } }
}
EOF
curl -X POST "$RIVET_ENDPOINT/acl/manifest" \
-H "Authorization: Bearer $RIVET_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d @pk-manifest.json
curl -X POST "$RIVET_ENDPOINT/acl/tokens" \
-H "Authorization: Bearer $RIVET_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "roles": ["pub-role"], "prefix": "pk" }'
# { "token": "pk_...", ... }
Secret key
Keep this token on your backend; never ship it to a frontend. This example grants actor and gateway management and worker registration (runner:create), but not actor KV access. The sk prefix does not itself provide additional protection.
cat > sk-manifest.json <<EOF
{
"rules": {
"sec-runner": { "namespace": { "id": "$RIVET_NAMESPACE" }, "resource": "runner", "target": "any", "operations": ["create"] },
"sec-actor": { "namespace": { "id": "$RIVET_NAMESPACE" }, "resource": "actor", "target": "any", "operations": ["create", "read", "update", "delete", "list"] },
"sec-gateway": { "namespace": { "id": "$RIVET_NAMESPACE" }, "resource": "actor_gateway", "target": "any", "operations": ["create", "read", "update", "delete", "list"] }
},
"policies": { "sec-policy": { "rules": ["sec-runner", "sec-actor", "sec-gateway"] } },
"roles": { "sec-role": { "policies": ["sec-policy"] } }
}
EOF
curl -X POST "$RIVET_ENDPOINT/acl/manifest" \
-H "Authorization: Bearer $RIVET_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d @sk-manifest.json
curl -X POST "$RIVET_ENDPOINT/acl/tokens" \
-H "Authorization: Bearer $RIVET_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "roles": ["sec-role"], "prefix": "sk" }'
# { "token": "sk_...", ... }
Inspector-capable role
Same permissions as the publishable key, plus actor_kv:read so the holder can fetch per-actor inspector tokens.
cat > insp-manifest.json <<EOF
{
"rules": {
"insp-actor": { "namespace": { "id": "$RIVET_NAMESPACE" }, "resource": "actor", "target": "any", "operations": ["create", "read", "update"] },
"insp-gateway": { "namespace": { "id": "$RIVET_NAMESPACE" }, "resource": "actor_gateway", "target": "any", "operations": ["create", "read", "update", "delete", "list"] },
"insp-kv": { "namespace": { "id": "$RIVET_NAMESPACE" }, "resource": "actor_kv", "target": "any", "operations": ["read"] }
},
"policies": { "insp-policy": { "rules": ["insp-actor", "insp-gateway", "insp-kv"] } },
"roles": { "insp-role": { "policies": ["insp-policy"] } }
}
EOF
curl -X POST "$RIVET_ENDPOINT/acl/manifest" \
-H "Authorization: Bearer $RIVET_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d @insp-manifest.json
curl -X POST "$RIVET_ENDPOINT/acl/tokens" \
-H "Authorization: Bearer $RIVET_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "roles": ["insp-role"] }'
# Expected: token metadata and a token value; keep the token private
Keep inspector-capable tokens private. See Debugging for how to use them.