Agent delegation creates privilege escalation by default
Last week our multi-agent system turned into a digital turf war. Agent A would delegate file processing to Agent B, but Agent B inherited all of Agent A's permissions — including access to production databases. When Agent B needed help with a complex query, it spawned Agent C with the same elevated privileges.
Within two hours, we had a chain of three agents, each with full admin access, making decisions based on assumptions the previous agent had made. Agent C was modifying user records because Agent A had mentioned "data cleanup" in passing.
This is the handoff problem that nobody talks about: when agents delegate to other agents, they pass along their entire privilege set. There's no scoping, no attenuation, no "you can only touch these files for this specific task."
Standard delegation looks like this:
Agent A: "Hey Agent B, handle this data processing task" [Agent B inherits: database access, file system access, API keys, admin privileges] Agent B: "This is complex, delegating to Agent C" [Agent C inherits: everything Agent B had, plus whatever it assumes it needs]
The fix is attenuated delegation — each handoff comes with explicit, limited permissions:
Agent A → Agent B: - Task: "Process files in /data/uploads/" - Permissions: ["read:/data/uploads/*", "write:/data/processed/*"] - Duration: 30 minutes - Escalation: "Return to Agent A if you need database access" Agent B → Agent C: - Task: "Parse CSV files only" - Permissions: ["read:/data/uploads/*.csv"] - Duration: 10 minutes - No sub-delegation allowed
We built this with a simple permission wrapper that tracks the delegation chain:
class DelegationContext:
def __init__(self, allowed_paths, allowed_actions, max_duration, parent_context=None):
self.allowed_paths = allowed_paths
self.allowed_actions = allowed_actions
self.expires_at = time.time() + max_duration
self.parent_context = parent_context
self.delegation_depth = (parent_context.delegation_depth + 1) if parent_context else 0
def can_delegate_to(self, sub_context):
# Sub-context must be subset of current permissions
return (set(sub_context.allowed_paths).issubset(set(self.allowed_paths)) and
set(sub_context.allowed_actions).issubset(set(self.allowed_actions)) and
self.delegation_depth < MAX_DELEGATION_DEPTH)The key insight: Each delegation should reduce permissions, never expand them. Think of it like sudo with time limits and path restrictions.
Now when Agent B tries to delegate database access to Agent C, the system blocks it because Agent B was never given database permissions in the first place. The delegation chain becomes a security feature, not a vulnerability.
This pattern prevents privilege escalation, limits blast radius, and creates an audit trail of who did what with which permissions. Your agents can still collaborate, but they can't accidentally (or maliciously) grant each other admin access to your entire system.
Multi-agent systems are inevitable. Secure delegation is what makes them safe enough to run unsupervised.