Data-Level Permissions¶
The Question: What if a user can see the Jira ticket but not the customer call that explains it?
Beyond Tool-Level Permissions¶
The existing PermissionsKernel (CONCEPT:OS-5.1) controls which tools agents can call. This is necessary but insufficient for a Company Brain — we also need to control which data actors can see.
Data-level permissions provide: - Node-level ACLs — Per-node access control lists - Data classification labels — PUBLIC, INTERNAL, CONFIDENTIAL, RESTRICTED - Query-time filtering — Automatic removal of unauthorized nodes from results - Actor-agnostic enforcement — Humans and AIs subject to the same rules
Data Classification¶
Every node can carry a classification label:
| Level | Visibility | Audit | Example |
|---|---|---|---|
PUBLIC |
All authenticated actors | No | Company blog posts, public docs |
INTERNAL |
All actors within the tenant | No | Internal wikis, project plans |
CONFIDENTIAL |
Actors with explicit grant only | Optional | Customer PII, financial data |
RESTRICTED |
Data owners and admins only | Mandatory | Compensation data, legal holds |
from agent_utilities.knowledge_graph.core.company_brain import CompanyBrain
from agent_utilities.models.company_brain import ActorType, DataClassification
brain = CompanyBrain()
# Classify a node
brain.permissions.classify_node(
"customer:001:ssn",
DataClassification.RESTRICTED,
data_owner="compliance:officer-smith"
)
# Classify with default INTERNAL
brain.permissions.classify_node(
"project:roadmap-2025",
DataClassification.INTERNAL,
)
Node ACLs¶
Fine-grained access control on individual nodes:
from agent_utilities.models.company_brain import NodeACL
acl = NodeACL(
node_id="customer:001",
classification=DataClassification.CONFIDENTIAL,
data_owner="analyst:jane",
data_owner_type=ActorType.HUMAN,
read_actors=["analyst:jane", "agent:risk-analyzer", "manager:bob"],
write_actors=["analyst:jane", "agent:risk-analyzer"],
admin_actors=["analyst:jane"],
read_roles=["compliance_officer", "risk_manager"],
write_roles=["risk_analyst"],
tenant_id="trading-desk",
inherit_from_parent=True,
audit_on_access=True,
)
brain.permissions.set_acl(acl)
Permission Checks¶
# AI agent tries to read a confidential node
result = brain.permissions.check_permission(
node_id="customer:001",
actor_id="agent:risk-analyzer",
actor_type=ActorType.AI_AGENT,
action="read",
)
print(result.allowed) # True — agent is in read_actors
# Unknown agent tries to read
result = brain.permissions.check_permission(
node_id="customer:001",
actor_id="agent:random-bot",
actor_type=ActorType.AI_AGENT,
action="read",
)
print(result.allowed) # False
print(result.reason) # "Read access denied"
Query-Time Filtering¶
Automatically filter query results to respect permissions:
all_results = ["customer:001", "customer:002", "customer:003", "customer:004"]
# Filter to only nodes this agent can see
visible = brain.permissions.filter_nodes(
node_ids=all_results,
actor_id="agent:report-generator",
actor_type=ActorType.AI_AGENT,
action="read",
actor_roles=["risk_manager"],
)
# Returns only the nodes with matching ACLs
Actor-Agnostic Enforcement¶
Permission checks apply identically regardless of actor type: - A human analyst is denied access to RESTRICTED nodes if not in the ACL - An AI agent gets the same denial - A hybrid team is evaluated by their combined actor_id - No actor type receives implicit elevated access