Hierarchical vs Flat Spicedb Roles: Decision Guide
Decision guide for choosing between Spicedb hierarchical role inheritance and flat role models, with schema example and verification steps.
09 Jun 2026, 20:18 UTC

Decision and constraints
You must decide how Spicedb represents roles for a multi-tenant application that needs fine-grained access control. The two supported approaches are hierarchical role inheritance, where a child role references a parent role through the inherits: keyword, and a flat role model, where every role lists its own permissions explicitly. The useful takeaway: hierarchical inheritance reduces duplication when many roles share common permissions, but it requires regular audits because a change to a parent role propagates to all descendants.
Constraints
- Multi-tenant: each tenant may maintain its own role set.
- Fine-grained access: permissions apply to individual resources or actions.
- Operational simplicity: administrators need predictable change impact.
- Stability: use behavior available in Spicedb v1.2 or later.
Supported options
| Model | Definition style | Policy duplication | Audit visibility | Operational simplicity |
|---|---|---|---|---|
| Hierarchical | Parent role declares permissions; child roles use inherits: to reuse them |
Low: shared permissions are defined once | Partial: the UI shows the hierarchy, but effective rights require evaluation | High: parent updates propagate automatically |
| Flat | Each role lists every permission it grants | High: identical permissions repeat across roles | Full: every permission is visible on the role itself | Medium: many similar roles become difficult to manage |
Trade-offs
Hierarchical roles are attractive when several roles need the same base permissions, such as manage:all and view:all. The risk is unintended privilege escalation: if a parent role is overly permissive, every child role inherits that excess access. Deep inheritance chains also make it harder to answer the question "what can this user actually do?" without running an authorization check. Flat roles avoid hidden inheritance, but they create redundant permission entries and make bulk changes tedious because each role must be edited individually.
Concrete implementation: hierarchical schema
This schema fragment defines a parent role admin and a child role app_reader that inherits from it. The inherits: keyword tells Spicedb that app_reader receives all permissions granted to admin, in addition to its own local permissions.
# schema.spice (Spicedb v1.2+)
role admin {
description: "Top-level administrative role"
permissions: [
"manage:all",
"view:all"
]
}
role app_reader {
description: "Read-only access for the application service"
inherits: ["admin"]
permissions: [
"read:app:data"
]
}
Apply the schema file using your Spicedb CLI. The exact subcommand varies by version; consult spicedb --help or your deployment documentation. After applying the schema, assign a subject to the child role through your role-management API or CLI.
To verify inheritance, run an authorization check. Replace alice with the subject identifier:
spicedb check --subject alice
Required permission: the token used must have authority to read authorization state for the subject. Expected check: the result should reflect permissions from both app_reader and its parent admin. Risk: if the parent role is later broadened, the subject gains those new permissions without a new assignment.
API equivalent
If your Spicedb deployment exposes the role-management API, use HTTP calls. Replace <TOKEN> with a valid admin JWT and https://spicedb.example.com with your endpoint.
- Create the parent role:
curl -X POST https://spicedb.example.com/v1/roles \
-H "Authorization: Bearer <TOKEN>" \
-H "Content-Type: application/json" \
-d '{"name":"admin","description":"Top-level admin","permissions":["manage:all","view:all"]}'
- Create the child role with inheritance:
curl -X POST https://spicedb.example.com/v1/roles \
-H "Authorization: Bearer <TOKEN>" \
-H "Content-Type: application/json" \
-d '{"name":"app_reader","description":"Read-only app role","inherits":["admin"],"permissions":["read:app:data"]}'
- Assign the subject:
curl -X POST https://spicedb.example.com/v1/subjects/alice/role-assignments \
-H "Authorization: Bearer <TOKEN>" \
-H "Content-Type: application/json" \
-d '{"role":"app_reader"}'
- Query effective permissions:
curl -X GET https://spicedb.example.com/v1/roles/app_reader/effective-permissions \
-H "Authorization: Bearer <TOKEN>"
The API response is a JSON array of permission strings. Check that it contains manage:all, view:all, and read:app:data. Do not treat this as a guarantee; validate against your deployed version and schema.
Verification and limitations
Before promoting the hierarchy to production, run both of these checks:
- Inspect the child role definition through the API or CLI and confirm the
inheritsfield references the intended parent. - Run an authorization check for a subject assigned to the child role. Confirm the returned permission set includes every parent permission plus the child-specific permission.
Limitations: changes to admin immediately affect app_reader and every other descendant. A permissive edit to the parent can silently escalate privileges across tenants. Deep chains make effective permissions opaque, so schedule periodic reviews of role definitions. If strict auditability is mandatory and the number of shared permissions is small, a flat model may be preferable despite the duplication.
Rollback
Role creation and subject assignment change state. To reverse an assignment, remove the subject from the role:
spicedb admin delete-subject alice --role app_reader --token <TOKEN>
To remove a role entirely, delete it only after confirming no subjects depend on it; otherwise dependent assignments may fail or behave unexpectedly.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.