Skip to content

A2A Delegation

Agent-to-Agent Delegation (A2A Auth)

When Agent A tells Agent B to perform an action, both sides must be authorised. Xybern intermediates every agent-to-agent interaction with bidirectional authorisation, scope attenuation, and cryptographic delegation grants recorded in the Provenance Vault.

Info

Why this matters: Multi-agent frameworks (CrewAI, AutoGen, LangGraph) let agents freely call each other with zero authorisation. Xybern ensures Agent A is authorised to delegate, Agent B is authorised to accept, and the delegated scopes are the intersection of both, not a superset.

Core Concepts

Concept Description
delegation_policy Per-agent config: can_delegate, can_accept_delegation, delegable_scopes, acceptable_scopes, max_delegation_depth
Scope Attenuation Granted scopes = source's delegable ∩ target's acceptable ∩ requested. Wildcards supported (trade:*).
Delegation Grant First-class verifiable token (dlg_...) that the target agent presents when acting on behalf of the source.
Delegation Chains Agent B can re-delegate to Agent C if depth allows. Max depth enforced, cascading revocation propagates down the chain.

Register Agents with Delegation Policy

import requests

BASE = "https://xybern.com/api/v1"
HEADERS = {"X-API-Key": "xb_...", "Content-Type": "application/json"}

# Source agent - can delegate trade:read and db:read
requests.post(f"{BASE}/enforce/agents", headers=HEADERS, json={
    "name": "finance-agent",
    "framework": "crewai",
    "permissions": {"allowed_action_types": ["execute_trade", "query_database"]},
    "scopes": ["trade:write", "trade:read", "db:read", "agent:delegate"],
    "delegation_policy": {
        "can_delegate": True,
        "can_accept_delegation": False,
        "delegable_scopes": ["trade:read", "db:read"],
        "max_delegation_depth": 3
    }
})

# Target agent - accepts trade:read and db:read
requests.post(f"{BASE}/enforce/agents", headers=HEADERS, json={
    "name": "analyst-agent",
    "framework": "crewai",
    "permissions": {"allowed_action_types": ["query_database", "read_data"]},
    "scopes": ["trade:read", "db:read"],
    "delegation_policy": {
        "can_delegate": False,
        "can_accept_delegation": True,
        "acceptable_scopes": ["trade:read", "db:read"]
    }
})

POST /v1/enforce/delegate

Request a delegation grant from one agent to another.

resp = requests.post(f"{BASE}/enforce/delegate", headers=HEADERS, json={
    "source_agent_id": "agent_abc123",
    "target_agent_id": "agent_def456",
    "scopes": ["trade:read", "db:read"],
    "action_types": ["query_database"],
    "constraints": {"max_amount": 100000},
    "instruction": "Analyse Q1 trading performance",
    "ttl_hours": 12,
    "max_uses": 5
})

grant = resp.json()["grant"]
# grant["grant_id"]          → "dlg_42eb33adab69"
# grant["attenuated_scopes"] → ["db:read", "trade:read"]
# grant["delegation_depth"]  → 1
# grant["vault_entry_id"]    → "ve_590dba34..."

POST /v1/enforce/delegate/verify

Verify a delegation grant before executing an action.

resp = requests.post(f"{BASE}/enforce/delegate/verify", headers=HEADERS, json={
    "grant_id": "dlg_42eb33adab69",
    "agent_id": "agent_def456",
    "action_type": "query_database"
})
# resp.json()["valid"]  → True
# resp.json()["reason"] → "Grant verified"

Using a Grant with Intercept

Pass grant_id to /enforce/intercept so the control plane verifies the delegation before policy evaluation.

resp = requests.post(f"{BASE}/enforce/intercept", headers=HEADERS, json={
    "action_type": "query_database",
    "action_content": "SELECT * FROM trades WHERE quarter='Q1'",
    "agent_id": "agent_def456",
    "grant_id": "dlg_42eb33adab69",
    "metadata": {"table": "trades", "operation": "read"}
})
# If grant is invalid or revoked → decision: "block"
# If grant is valid → normal policy evaluation proceeds

The principal's authority always applies (strict confused-deputy)

A delegate acting under a grant may never do what its principal could not. On every delegated action the Authorisation Layer evaluates the principal's own authority as well as the delegate's, and the stricter outcome stands:

  • a principal that is deactivated or revoked lends no authority at all, so the delegated action is refused
  • the principal's access profile is evaluated for the action, and a violation refuses or holds it exactly as it would for the principal itself
  • every live rule scoped to the principal is evaluated for the action, and a rule that fires appears in the decision as Principal rule: <name>

The result is recorded on the decision (delegation.principal, with intersection: "applied"), in the receipt's lineage, and in the plain-language explanation ("evaluated against both authorities"). If the principal's authority cannot be evaluated the action is held for a person rather than allowed. Nothing changes for actions that do not carry a grant: a principal's rules never leak onto unrelated agents.

POST /v1/enforce/delegate/{grant_id}/revoke

Revoke a grant with cascading, all child grants in the delegation chain are revoked automatically.

resp = requests.post(f"{BASE}/enforce/delegate/dlg_42eb33adab69/revoke",
    headers=HEADERS, json={"reason": "task_complete"})
# resp.json()["revoked_count"]  → 1
# resp.json()["revoked_grants"] → ["dlg_42eb33adab69"]

GET /v1/enforce/delegations

List all delegation grants for the workspace. Filter by status (active, revoked, expired) or agent_id.

curl -H "X-API-Key: xb_..." \
  "https://xybern.com/api/v1/enforce/delegations?status=active"

Delegation Policies

Create enforcement policies of type delegation to control A2A behaviour at the workspace level.

requests.post(f"{BASE}/enforce/policies", headers=HEADERS, json={
    "name": "Block Finance→Marketing delegation",
    "policy_type": "delegation",
    "decision": "block",
    "conditions": {
        "blocked_pairs": [["agent_finance", "agent_marketing"]],
        "max_depth": 2,
        "blocked_scopes": ["payments:execute"]
    }
})