Per-agent permission contracts for multi-agent CrewAI deployments
Without per-agent permission contracts, every agent in a CrewAI crew can invoke every tool available to the crew. Here's how to fix that.
Per-agent permission contracts and audit trails for multi-agent CrewAI deployments
CrewAI's role-based design is one of its most powerful features. Assigning specialized roles to individual agents — researcher, analyst, writer, reviewer — produces better outputs than a single general-purpose agent and makes complex tasks decomposable into manageable pieces.
That same role-based design creates a governance challenge: without per-agent permission enforcement, every agent in the crew implicitly has access to every tool available to the crew. A researcher agent that gets confused about its task, or a writer agent operating on malicious input, can invoke tools it was never supposed to reach.
This post shows how to add per-agent permission contracts and unified audit trails to an existing CrewAI deployment using AgentTrust OS.
When a manager agent delegates a task to a researcher, the researcher executes with its own tool set. If the researcher's tool set is not properly scoped, it may reach systems the manager itself is authorized to access but the researcher should not be. Privilege does not automatically scope downward through delegation chains.
Agents share task context in a crew. A malicious or hallucinated output from one agent can influence the next agent's tool calls — in ways that neither the framework nor the developer anticipated. Without per-agent policy enforcement, there is no boundary to stop a contaminated context from triggering an authorized-looking but unintended action.
When something goes wrong in a crew run, determining which agent made which decision — and under what context — is difficult without structured per-agent tracing. A single audit log that records tool calls without agent attribution cannot answer "which role authorized this action?" — which is the first question any compliance review will ask.
from agenttrust import define_policy
researcher_policy = define_policy(
name="researcher",
tools={
"web_search": {"trust_level": "standard", "threshold": 0.75},
"read_database": {"trust_level": "standard", "threshold": 0.80},
# researcher cannot write, send, or delete
},
defaults={"threshold": 0.80, "blocked_if_unlisted": True},
)
writer_policy = define_policy(
name="writer",
tools={
"read_file": {"trust_level": "standard", "threshold": 0.75},
"write_file": {"trust_level": "sensitive", "threshold": 0.90},
# writer cannot access database or external APIs
},
defaults={"threshold": 0.85, "blocked_if_unlisted": True},
)
manager_policy = define_policy(
name="manager",
tools={
"send_email": {"trust_level": "high-risk", "threshold": 0.95,
"require_approval": True},
"publish_content": {"trust_level": "sensitive", "threshold": 0.90},
},
defaults={"threshold": 0.90, "blocked_if_unlisted": True},
)from crewai import Agent, Crew, Task
from agenttrust import AgentTrustRuntime
# Wrap tools with per-agent policy before passing to CrewAI
researcher_runtime = AgentTrustRuntime(policy=researcher_policy)
writer_runtime = AgentTrustRuntime(policy=writer_policy)
manager_runtime = AgentTrustRuntime(policy=manager_policy)
researcher = Agent(
role="Research Specialist",
goal="Find accurate information from approved sources",
tools=researcher_runtime.wrap_tools([web_search, read_database]),
backstory="...",
)
writer = Agent(
role="Content Writer",
goal="Transform research into structured documents",
tools=writer_runtime.wrap_tools([read_file, write_file]),
backstory="...",
)
manager = Agent(
role="Publishing Manager",
goal="Review and distribute finalized content",
tools=manager_runtime.wrap_tools([send_email, publish_content]),
backstory="...",
)
crew = Crew(
agents=[researcher, writer, manager],
tasks=[research_task, writing_task, publishing_task],
verbose=True,
)from agenttrust import TrustAudit
audit = TrustAudit()
# All tool calls attributed to the researcher in this run
researcher_records = audit.query(
agent_role="researcher",
run_id=crew_run_id,
)
# All blocked actions across the entire crew
blocked_actions = audit.query(
run_id=crew_run_id,
route="blocked",
)
# Export for compliance review
audit.export_json(run_id=crew_run_id, path="./audit-export.json")Add Trust Runtime to your CrewAI crews. Each agent gets its own policy — no agent exceeds its scope.
View CrewAI Integration →