ReBAC Explained¶
What is ReBAC?¶
ReBAC (Relationship-Based Access Control) is a fine-grained permission system that controls access based on relationships between entities, not just roles. Nexus's ReBAC is inspired by Google's Zanzibar system, used at massive scale for Google Drive, Docs, Calendar, and more.
Why ReBAC vs Traditional Permissions?¶
| Traditional (RBAC/ACL) | ReBAC (Zanzibar-style) |
|---|---|
| ❌ Fixed roles (admin, user, guest) | ✅ Dynamic relationships (owner, editor, viewer) |
| ❌ Can't express "share with team" | ✅ Groups, inheritance, delegation |
| ❌ No hierarchical permissions | ✅ Parent-child inheritance |
| ❌ Hardcoded access logic | ✅ Declarative relationship rules |
| ❌ Scales poorly (N users × M resources) | ✅ Graph-based, efficient checking |
Key Innovation: Permissions are computed from relationships, not stored directly.
Core Concept: The ReBAC Tuple¶
A ReBAC tuple represents a relationship:
Examples:
(agent:alice, member-of, group:eng-team)
(group:eng-team, owner-of, file:/workspace/project.txt)
(file:/workspace, parent, file:/workspace/project.txt)
graph LR
A["Subject<br>agent:alice"] -->|"Relation<br>member-of"| B["Object<br>group:eng-team"]
B -->|owner-of| C["file:/workspace/project.txt"]
D["file:/workspace"] -->|parent| C
style A fill:#e1f5e1
style B fill:#fff3cd
style C fill:#f8d7da
style D fill:#d1ecf1 Tuple Components¶
@dataclass
class ReBACTuple:
tuple_id: str # Unique ID
subject: Entity # Who (agent, user, group)
relation: str # Relationship type
object: Entity # What (file, memory, skill)
subject_relation: str | None # For group expansion (member)
created_at: datetime # Creation time
expires_at: datetime | None # Optional expiration
conditions: dict | None # JSON conditions
Entity Structure:
@dataclass
class Entity:
entity_type: str # "agent", "user", "group", "file", "memory"
entity_id: str # Unique identifier
tenant_id: str | None # Multi-tenant isolation
Relationship Types¶
Direct Relations (Explicit Grants)¶
| Relation | Description | Example |
|---|---|---|
direct_owner | Full control (read, write, execute) | Alice owns her workspace |
direct_editor | Read + write access | Bob can edit shared doc |
direct_viewer | Read-only access | Guests can view public files |
member-of | Group membership | Alice is in eng-team |
parent | Hierarchical relationship | /workspace parent of /workspace/file.txt |
Computed Relations (Inherited)¶
| Relation | Description | Computed From |
|---|---|---|
parent_owner | Owner via parent directory | Parent has owner permission |
group_owner | Owner via group membership | Group has owner + user is member |
owner | Any owner permission | Union of direct_owner, parent_owner, group_owner |
editor | Any editor permission | Union of direct_editor, parent_editor, group_editor, owner |
viewer | Any viewer permission | Union of direct_viewer, parent_viewer, group_viewer |
Permission Checking: How It Works¶
Check Algorithm¶
Question: Does alice have read permission on /workspace/file.txt?
Step 1: Get namespace config for "file"
Step 2: Map "read" permission → [viewer, editor, owner]
Step 3: Check each relation:
Check viewer:
├─ Direct: (alice, direct_viewer, file.txt)? NO
├─ Parent: (alice, parent_viewer, file.txt)?
│ └─ Find parent: /workspace parent-of file.txt
│ └─ Check: (alice, viewer, /workspace)? NO
└─ Group: (alice, group_viewer, file.txt)?
└─ Find groups with direct_viewer on file.txt: eng-team
└─ Check: (alice, member-of, eng-team)? NO
Check editor:
├─ Direct: (alice, direct_editor, file.txt)? NO
└─ ... (similar checks)
Check owner:
├─ Direct: (alice, direct_owner, file.txt)? NO
├─ Parent: (alice, parent_owner, file.txt)?
│ └─ Find parent: /workspace parent-of file.txt
│ └─ Check: (alice, owner, /workspace)? YES! ✅
│ └─ Result: alice has parent_owner on file.txt
└─ Return: ✅ GRANTED (alice has read)
Step 4: Cache result for 5 minutes
Namespace Configuration¶
Namespaces define how permissions work for each object type (file, memory, group, skill, etc.).
Example: File Namespace¶
DEFAULT_FILE_NAMESPACE = NamespaceConfig(
object_type="file",
config={
"relations": {
# Structural relation
"parent": {}, # Parent directory
# Direct relations (explicitly granted)
"direct_owner": {},
"direct_editor": {},
"direct_viewer": {},
# Parent inheritance (tupleToUserset)
"parent_owner": {
"tupleToUserset": {
"tupleset": "parent", # Find parent
"computedUserset": "owner" # Check owner on parent
}
},
# Group inheritance (tupleToUserset)
"group_owner": {
"tupleToUserset": {
"tupleset": "direct_owner", # Find groups with direct_owner
"computedUserset": "member" # Check if subject is member
}
},
# Union relations (OR logic)
"owner": {
"union": ["direct_owner", "parent_owner", "group_owner"]
},
"editor": {
"union": ["direct_editor", "parent_editor", "group_editor", "owner"]
},
"viewer": {
"union": ["direct_viewer", "parent_viewer", "group_viewer"]
},
},
# Permission-to-relation mapping
"permissions": {
"read": ["viewer", "editor", "owner"], # read = viewer OR editor OR owner
"write": ["editor", "owner"], # write = editor OR owner
"execute": ["owner"], # execute = owner only
}
}
)
Namespace Rules Explained¶
1. Union (OR Logic)¶
Meaning: Subject has owner relation if they have ANY of: - direct_owner (explicit grant) - parent_owner (inherited from parent) - group_owner (inherited from group)
2. TupleToUserset (Expansion)¶
"parent_owner": {
"tupleToUserset": {
"tupleset": "parent", # Find objects where subject is parent
"computedUserset": "owner" # Check if subject has owner on that object
}
}
Meaning: Subject has parent_owner relation if: 1. Find objects X where (X, parent, object) exists 2. Check if (subject, owner, X) exists 3. If yes → subject inherits owner via parent
Example:
Tuples:
(file:/workspace, parent, file:/workspace/child.txt)
(alice, direct_owner, file:/workspace)
Query: Does alice have parent_owner on /workspace/child.txt?
1. Find parent: /workspace parent-of /workspace/child.txt
2. Check owner: alice has owner on /workspace? YES
3. Result: alice has parent_owner ✅
3. Permission Mapping¶
"permissions": {
"read": ["viewer", "editor", "owner"],
"write": ["editor", "owner"],
"execute": ["owner"]
}
Meaning: - read permission granted if subject has viewer OR editor OR owner relation - write permission granted if subject has editor OR owner relation - execute permission granted if subject has owner relation
Real-World Examples¶
Example 1: Directory Hierarchy¶
Setup:
# Alice owns /workspace
nexus rebac create user alice direct_owner file /workspace
# Create subdirectory
nexus mkdir /workspace/project
# Define parent relationship
nexus rebac create file /workspace parent file /workspace/project
Permission Flow:
Question: Does alice have write permission on /workspace/project?
1. Map write → [editor, owner]
2. Check owner:
├─ Union: [direct_owner, parent_owner, group_owner]
├─ Check direct_owner: NO
├─ Check parent_owner:
│ ├─ Find parent: /workspace parent-of /workspace/project ✅
│ └─ Check: alice has owner on /workspace? ✅ (direct_owner)
│ └─ Result: alice has parent_owner ✅
└─ Return: alice has owner ✅
3. Result: alice has write permission ✅
Mermaid Diagram:
graph TB
A[alice] -->|direct_owner| B["/workspace"]
B -->|parent| C["/workspace/project"]
A -.->|"parent_owner<br>inherited"| C
style A fill:#e1f5e1
style B fill:#d1ecf1
style C fill:#d1ecf1 Example 2: Group-Based Sharing¶
Setup:
# Alice and Bob join eng-team
nexus rebac create agent alice member-of group eng-team
nexus rebac create agent bob member-of group eng-team
# Grant eng-team editor permission on project
nexus rebac create group eng-team direct_editor file /workspace/project
Permission Flow:
Question: Does alice have write permission on /workspace/project?
1. Map write → [editor, owner]
2. Check editor:
├─ Union: [direct_editor, parent_editor, group_editor, owner]
├─ Check group_editor:
│ ├─ Find groups with direct_editor on /workspace/project: eng-team ✅
│ └─ Check: alice member-of eng-team? ✅
│ └─ Result: alice has group_editor ✅
└─ Return: alice has editor ✅
3. Result: alice has write permission ✅
Mermaid Diagram:
graph TB
A[alice] -->|member-of| G["group:eng-team"]
B[bob] -->|member-of| G
G -->|direct_editor| F["/workspace/project"]
A -.->|"group_editor<br>inherited"| F
B -.->|"group_editor<br>inherited"| F
style A fill:#e1f5e1
style B fill:#e1f5e1
style G fill:#fff3cd
style F fill:#d1ecf1 Example 3: Public File Access¶
Setup:
# Make file publicly readable
nexus rebac create '*' '*' direct_viewer file /workspace/public.txt --wildcard
Permission Flow:
Question: Does anyone have read permission on /workspace/public.txt?
1. Map read → [viewer, editor, owner]
2. Check viewer:
├─ Check direct_viewer: ('*', '*') wildcard matches ALL subjects ✅
└─ Return: ANY subject has viewer ✅
3. Result: Public read access ✅
Core Operations¶
1. Create Relationship (Write)¶
# Grant alice owner permission on file
nexus rebac create agent alice direct_owner file /workspace/data.txt
# Alice joins group
nexus rebac create agent alice member-of group eng-team
# Group gets editor permission
nexus rebac create group eng-team direct_editor file /shared/doc.txt
# Public viewer access
nexus rebac create '*' '*' direct_viewer file /public/readme.txt --wildcard
Python API:
from nexus.core.rebac_manager import ReBACManager
manager = ReBACManager(engine)
# Create tuple
tuple_id = manager.rebac_write(
subject=("agent", "alice"),
relation="direct_owner",
object=("file", "/workspace/data.txt"),
tenant_id="acme",
expires_at=None, # Optional expiration
conditions=None # Optional JSON conditions
)
2. Check Permission¶
# Check if alice can write
nexus rebac check agent alice write file /workspace/data.txt
# Output:
# ✅ Granted
# or
# ❌ Denied
Python API:
can_write = manager.rebac_check(
subject=("agent", "alice"),
permission="write",
object=("file", "/workspace/data.txt"),
tenant_id="acme"
)
# Returns: True or False
3. Expand (Who Has Permission?)¶
# Find all subjects with read permission
nexus rebac expand read file /workspace/data.txt
# Output:
# agent:alice
# agent:bob
# group:eng-team
Python API:
subjects = manager.rebac_expand(
permission="read",
object=("file", "/workspace/data.txt"),
tenant_id="acme"
)
# Returns: [("agent", "alice"), ("agent", "bob"), ("group", "eng-team")]
4. Explain (Why Does Alice Have Permission?)¶
# Explain why alice has read permission
nexus rebac explain agent alice read file /workspace/project/file.txt --verbose
# Output:
# ✅ alice has read permission on /workspace/project/file.txt
#
# Reason:
# 1. read permission requires: viewer OR editor OR owner
# 2. alice has owner relation via:
# - parent_owner relation:
# - /workspace is parent of /workspace/project/file.txt
# - alice has direct_owner on /workspace
# - ✅ Inherited owner permission
5. Delete Relationship¶
# Delete specific tuple
nexus rebac delete <tuple_id>
# Revoke alice's owner permission
nexus rebac list --subject-type agent --subject-id alice --object-id /workspace/data.txt
# Find tuple_id, then:
nexus rebac delete tuple_abc123
Python API:
Caching for Performance¶
ReBAC checks are cached with TTL for performance:
Cache Strategy¶
manager = ReBACManager(
engine=engine,
cache_ttl_seconds=300, # 5 minutes (default)
max_depth=50 # Max graph traversal depth
)
Cache Entry:
Key: (tenant_id, subject_type, subject_id, permission, object_type, object_id)
Value: (result: bool, expires_at: datetime)
Cache Invalidation:
When tuples are created/deleted, affected cache entries are automatically invalidated:
Tuple created: (alice, direct_owner, /workspace)
→ Invalidate all cache entries for:
- subject=alice
- object=/workspace
- Any permission checks involving alice or /workspace
Multi-Tenancy¶
All ReBAC operations are tenant-scoped for complete isolation:
# Create tuple in tenant "acme"
nexus rebac create agent alice direct_owner file /data.txt --tenant-id acme
# Create tuple in tenant "techcorp"
nexus rebac create agent alice direct_owner file /data.txt --tenant-id techcorp
# Different tuples, different permissions, complete isolation
Cross-Tenant Protection:
# REJECTED: Cross-tenant relationships are not allowed
manager.rebac_write(
subject=("agent", "alice", tenant_id="acme"),
relation="member-of",
object=("group", "eng", tenant_id="techcorp"), # Different tenant!
tenant_id="acme"
)
# Raises: ValueError("Cross-tenant relationships not allowed")
Advanced: Custom Namespaces¶
You can define custom namespaces for your own object types:
# Create custom namespace for "document" type
nexus rebac namespace-create document \
--relations owner --relations editor --relations viewer \
--permission read:viewer,editor,owner \
--permission write:editor,owner \
--permission delete:owner
Python API:
from nexus.core.rebac import NamespaceConfig
custom_namespace = NamespaceConfig(
object_type="document",
config={
"relations": {
"direct_owner": {},
"direct_editor": {},
"direct_viewer": {},
"owner": {"union": ["direct_owner"]},
"editor": {"union": ["direct_editor", "owner"]},
"viewer": {"union": ["direct_viewer", "editor"]},
},
"permissions": {
"read": ["viewer", "editor", "owner"],
"write": ["editor", "owner"],
"delete": ["owner"],
}
}
)
manager.register_namespace(custom_namespace)
CLI Reference¶
Tuple Management¶
# Create relationship
nexus rebac create <subject_type> <subject_id> <relation> <object_type> <object_id>
# Examples
nexus rebac create agent alice direct_owner file /workspace/data.txt
nexus rebac create agent alice member-of group eng-team
nexus rebac create group eng-team direct_editor file readme.md
# List tuples
nexus rebac list-tuples
nexus rebac list-tuples --subject-type agent --subject-id alice
nexus rebac list-tuples --object-type file --object-id /workspace/data.txt
# Delete tuple
nexus rebac delete <tuple_id>
Permission Checking¶
# Check permission
nexus rebac check <subject_type> <subject_id> <permission> <object_type> <object_id>
# Examples
nexus rebac check agent alice read file /workspace/data.txt
nexus rebac check agent alice write file /workspace/data.txt
# Batch check from JSON
echo '[
{"subject": ["agent", "alice"], "permission": "read", "object": ["file", "/data.txt"]},
{"subject": ["agent", "bob"], "permission": "write", "object": ["file", "/data.txt"]}
]' | nexus rebac check-batch --format table
# Expand (who has permission?)
nexus rebac expand <permission> <object_type> <object_id>
# Example
nexus rebac expand read file /workspace/data.txt
# Output: agent:alice, agent:bob, group:eng-team
# Explain (why does subject have permission?)
nexus rebac explain <subject_type> <subject_id> <permission> <object_type> <object_id>
# Example
nexus rebac explain agent alice read file /workspace/project/file.txt --verbose
Namespace Management¶
# Create namespace
nexus rebac namespace-create <object_type> \
--relations <relation1> --relations <relation2> \
--permission <perm>:<relations>
# List namespaces
nexus rebac namespace-list
# Get namespace config
nexus rebac namespace-get <object_type> --format yaml
# Delete namespace
nexus rebac namespace-delete <object_type> --yes
Security Best Practices¶
1. Principle of Least Privilege¶
# ✅ Good: Grant specific permission
nexus rebac create agent alice direct_viewer file /logs/system.log
# ❌ Bad: Grant broad permission
nexus rebac create agent alice direct_owner file /
2. Use Groups for Team Permissions¶
# ✅ Good: Grant permission to group
nexus rebac create agent alice member-of group eng-team
nexus rebac create agent bob member-of group eng-team
nexus rebac create group eng-team direct_editor file /shared/project/
# ❌ Bad: Grant individually (hard to manage)
nexus rebac create agent alice direct_editor file /shared/project/
nexus rebac create agent bob direct_editor file /shared/project/
3. Use Expiring Tuples for Temporary Access¶
# Grant temporary access (1 hour)
nexus rebac create agent alice direct_viewer file /sensitive.txt \
--expires 2025-12-31T23:59:59
4. Audit Permission Changes¶
ReBAC automatically logs all changes in rebac_changelog table:
Troubleshooting¶
Permission Denied (Expected to Grant)¶
Check: 1. Verify tuple exists:
-
Explain why permission denied:
-
Check parent inheritance:
Permission Granted (Expected to Deny)¶
Check: 1. Check for wildcard grants:
-
Check group memberships:
-
Check parent inheritance:
Slow Permission Checks¶
Solutions: 1. Increase cache TTL:
-
Check cache hit rate:
-
Reduce graph depth (limit nested groups):
FAQ¶
Q: How is this different from AWS IAM or Google Cloud IAM?¶
A: IAM is policy-based (roles with permissions). ReBAC is relationship-based (permissions computed from relationships). ReBAC is more flexible for fine-grained, dynamic permissions (e.g., "share with my team").
Q: Can I use ReBAC with external auth (OAuth, LDAP)?¶
A: Yes! ReBAC handles authorization (what you can do). Authentication (who you are) is separate. After OAuth/LDAP authentication, map the user to a Nexus subject, then use ReBAC for authorization.
Q: What's the performance at scale?¶
A: Google's Zanzibar handles 10 trillion checks/day with 95th percentile latency <10ms. Nexus uses similar architecture: - Caching with TTL (default 5 minutes) - Version sequences for bounded staleness - Database indexes on tuple queries
Q: Can I implement custom permission logic?¶
A: Yes, via namespace configuration. Define custom relations, unions, and tupleToUserset rules for your object types.
Next Steps¶
- Agent Permissions - How agents use ReBAC
- Memory System - ReBAC for agent memory
- Multi-Tenancy - Tenant isolation patterns
- API Reference: ReBAC API - Complete API docs
Related Files¶
- Core:
src/nexus/core/rebac.py:1 - Manager:
src/nexus/core/rebac_manager.py:1 - Permissions:
src/nexus/core/permissions_enhanced.py:1 - Models:
src/nexus/storage/models.py:977 - CLI:
src/nexus/cli/commands/rebac.py:1 - Tests:
tests/unit/core/test_rebac.py:1 - Examples:
examples/cli/agent_permissions_inheritance_demo.sh:1