Demo: Multi-Agent Governance
Run a self-contained Docker compose stack that puts three Claude-powered agents to work — an orchestrator and two specialist workers — and exercises MITRITY's governance across real agent-to-agent delegation. Unlike the single-agent demos, every delegation hop, every privilege boundary, and every threat-intel match emerges from genuine multi-agent activity: no seed data, no synthetic UUIDs.
The demo takes about 5 minutes to run and requires Docker Desktop, an Anthropic API key, and a MITRITY tenant on the Pro or Enterprise plan.
First time with MITRITY? Start with the single-agent Mitrity Gateway or Mitrity MCP Sidecar demos. The multi-agent demo assumes you're comfortable with the dashboard and want to see delegation chains in action.
Prerequisites
- Docker Desktop (or any Docker runtime with compose v2)
- A MITRITY tenant on Pro or Enterprise — Starter doesn't include delegation chains or threat intelligence; the orchestrator pre-flight check exits cleanly on Starter rather than running scenarios that would silently no-op
- An Anthropic API key for the three Claude agents
Step 1: Provision three agents in the dashboard
Go to Agents → + New Agent and create the following three agents. Copy each Agent ID and Agent Key (ak_...) — the key cannot be retrieved later.
| Agent name | Mission scope | Policy (one per agent) |
|---|---|---|
demo-orchestrator | "Coordinate customer requests by delegating to specialist worker agents" | Allow filesystem reads; deny database writes; allow delegate |
demo-data-worker | "Read and write the customer database on behalf of the orchestrator" | Allow database read + write; allow delegate |
demo-notification-worker | "Send customer-facing notifications on behalf of the orchestrator" | Allow notify:*; allow delegate |
The "Policy" column above describes MITRITY policy rules you'll author in /app/policies in the next step — one policy per agent, because the whole point of the demo is the privilege diff between them. Policy rules govern tool access through three fields per rule: tool_pattern + operations + action.
The tool names themselves (filesystem, database, notifications, delegate) are built into the demo wrapper's gateway — they're declared in tools_config.yaml shipped inside the Docker image. You can browse the tools available to your tenant at /app/tools, but you don't need to "enable" anything there for this demo. The demo uses policy rules (not capabilities) to drive the privilege diff between the orchestrator and data-worker, because policies are the simpler surface to reason about for a five-minute walkthrough. The only thing that's actually plan-gated in this demo is delegation chains (Pro/Enterprise tenants).
Step 1b: Author policy rules
Go to /app/policies and create one policy per agent, then attach each policy to its corresponding agent. The exact rules below are what make the S2 privilege-escalation scenario meaningful — they create the orchestrator-vs-data-worker permission gap the delegation engine compares.
demo-orchestrator-policy — attach to demo-orchestrator
- Rule 1:
tool_pattern = "filesystem:*",operations = ["read"],action = allow - Rule 2:
tool_pattern = "database:write*",action = deny← this is what makes the S2 privilege-escalation scenario fire - Rule 3:
tool_pattern = "delegate:*",action = allow - Default action:
deny
demo-data-worker-policy — attach to demo-data-worker
- Rule 1:
tool_pattern = "database:read*",action = allow - Rule 2:
tool_pattern = "database:write*",action = allow - Rule 3:
tool_pattern = "delegate:*",action = allow - Default action:
deny
demo-notification-worker-policy — attach to demo-notification-worker
- Rule 1:
tool_pattern = "notify:*",action = allow - Rule 2:
tool_pattern = "delegate:*",action = allow - Default action:
deny
If you skip the explicit policy rules, the demo still runs but the S2 privilege-escalation scenario silently no-ops — the delegation engine has no permission gap to detect, so there's nothing to flag as an escalation.
The privilege difference between the orchestrator (no DB write) and the data-worker (DB write) is what makes scenario S2 (privilege escalation) fire — the orchestrator has to delegate the write because it can't perform it directly, which the policy rules in Step 1b enforce.
Step 2: Clone and configure
git clone git@github.com:mitrity-io/iag-demo-multi-agent.git
cd iag-demo-multi-agent
cp .env.example .env
Edit .env with your six MITRITY agent values (one ID + one key per agent) and your Anthropic key:
ANTHROPIC_API_KEY=sk-ant-...
MITRITY_CONTROL_PLANE_URL=https://api.mitrity.com
MITRITY_AGENT_ID_ORCHESTRATOR=<uuid>
MITRITY_AGENT_KEY_ORCHESTRATOR=ak_...
MITRITY_AGENT_ID_DATA=<uuid>
MITRITY_AGENT_KEY_DATA=ak_...
MITRITY_AGENT_ID_NOTIFY=<uuid>
MITRITY_AGENT_KEY_NOTIFY=ak_...
Step 3: Run the demo
docker compose up --build
Three containers boot — orchestrator, data-worker, notification-worker — each with its own MITRITY Gateway, its own agent identity, and its own role-scoped tool surface. The orchestrator runner cycles through six scenarios over ~5 minutes.
Watch the dashboard at mitrity.com/app while it runs — specifically /delegation-chains, /threat-intel, and /audit.
What the demo does
Six scenarios run in order. Each takes ~30 seconds with real Claude-driven tool use across multiple agents.
S1 — Clean delegation. The orchestrator inspects a customer request, decides it needs data, and delegates the lookup to the data-worker. A two-hop chain appears in /delegation-chains showing the request flowing orchestrator → data-worker → response.
S2 — Privilege escalation. The orchestrator gets a request that requires writing to the database. It can't do that itself (no write permission), so it delegates to the data-worker. The backend's /evaluate call compares the two agents' tool permissions, finds the data-worker holds (tool, op) pairs the orchestrator does not, and flags the hop with blocked_reason: "privilege_escalation". The hop is persisted to delegation_hops (denied attempts are stored, not dropped). Visible on /delegation-chains under the Blocked tab; the chain detail page renders the Escalated resources card showing the (tool, op) diff.
S3 — End-to-end pipeline. A three-hop chain: orchestrator → data-worker (fetch order) → notification-worker (email customer). The full pipeline runs as governed delegation — every continuation hop hits the backend /evaluate endpoint, the engine clears each one, and the chain accumulates real hops across three event streams with action_taken: "allowed".
S4 — Deep chain. A delegation chain that exceeds the agent's max_chain_depth (default 5). The depth check runs server-side as part of /evaluate — the backend walks the persisted chain, sees the next hop would push depth to 6, and returns blocked_reason: "depth_exceeded". The hop is persisted as blocked. Visible on /delegation-chains under the Blocked tab with blocked_reason: depth_exceeded.
S5 — Threat-intel match. One of the workers attempts to read /etc/passwd — matched against MITRITY's built-in threat indicator catalog. The worker's profile policy (critical → block) blocks the action; a threat_match event lands on /threat-intel.
S6 — Circular delegation. A worker tries to delegate back to the orchestrator that originally invoked it. Because chain state is rebuilt server-side from delegation_hops on every /evaluate call (not carried in args by the calling agent), the backend sees the orchestrator already in the chain regardless of how the hop is shaped — the cycle is caught even when it's transitive across multiple intermediate hops. The hop is persisted as blocked with blocked_reason: "circular_delegation". Visible on /delegation-chains under the Blocked tab.
What to look for
Open these dashboard pages while the demo runs:
/delegation-chains— Real-time view of multi-hop delegation. Each scenario produces at least one chain; click into one to see hop-by-hop timing, the Escalated resources card (when a hop carries theprivilege_escalationsignal), and blocking decisions. The Blocked tab populates with the denied hops from S2, S4, and S6 — every attempted-but-denied hop is persisted./threat-intel— Indicator matches from S5 plus any other matches the built-in indicator catalog catches on agent activity. Both the Matches tab (per-event) and Landscape tab (trending indicators) populate./audit— Every tool call across all three agents with its governance decision, latency, and policy match. Filter by agent to see each worker's stream separately.
Architecture
Docker Compose
├── orchestrator container
│ ├── Claude scenario runner (S1-S6)
│ └── Mitrity Gateway (own agent identity, own profile)
│ ├── filesystem (read-only inspection of the request file)
│ └── delegate (HTTP POST to a named worker)
│
├── data-worker container
│ ├── FastAPI /task endpoint (per-request Claude loop)
│ └── Mitrity Gateway (own agent identity, own profile)
│ ├── data (query_database, fetch_orders, create_order)
│ └── delegate (worker can also delegate further)
│
└── notification-worker container
├── FastAPI /task endpoint
└── Mitrity Gateway (own agent identity, own profile)
├── notify (send_notification, email_customer)
└── delegate (worker can also delegate further)
Each container's gateway speaks to your MITRITY control plane independently. Delegation hops happen via real governed HTTP between the containers — the gateway captures each hop and reports it to the control plane. The dashboard reassembles the chain across the three event streams.
Credential broker (per-agent, hot rotation)
All three gateways ship with credentials.injection_enabled: true. None of S1–S6 exercise it directly — those scenarios focus on delegation + threat intel — but the capability is fully available for ad-hoc testing.
To try it: provision a credential in the dashboard (/app/credentials), grant it to one of the three agents (e.g., the data-worker), then drive a tool call whose args include ${credential:<id>}. The gateway substitutes the placeholder via the broker before the upstream tool runs. Rotation in the dashboard propagates to the running container within 30 seconds with no agent or gateway restart.
The multi-agent setting adds one twist worth noting: per-agent credential scoping. The data-worker can hold DB credentials while the notification-worker independently holds SMTP / Slack / SendGrid keys; rotating one does not invalidate the other's cache.
For a guided walkthrough (provisioning, substitution, mid-scenario rotation, fail-closed), see Phase 6 in either single-agent demo:
- Demo: Mitrity Gateway — single-agent + aggregating-gateway form
- Demo: Mitrity MCP Sidecar — single-agent + transparent-proxy form
When to use this demo (vs. the single-agent demos)
| Single-agent demos | Multi-agent demo (this) | |
|---|---|---|
| Containers | 1 | 3 (compose stack) |
| Agents | 1 | 3 distinct identities |
| Primary focus | Policies, DLP, injection, holds, credential broker | Delegation chains, per-agent threat intel, privilege boundaries |
| Plan required | Any (Starter included) | Pro or Enterprise |
| Setup complexity | 1 agent in dashboard | 3 agents + one policy per agent |
| Best for | First-time evaluation, single-agent governance demos | Showcasing multi-agent architecture, delegation, privilege escalation detection |
Start with the single-agent demos to learn the governance core, then run this one to see multi-agent specifics.
Environment variables
| Variable | Required | Description |
|---|---|---|
ANTHROPIC_API_KEY | Yes | Anthropic API key (shared across all three containers) |
MITRITY_CONTROL_PLANE_URL | Yes | Control plane URL (e.g., https://api.mitrity.com) |
MITRITY_AGENT_ID_ORCHESTRATOR / _KEY_ORCHESTRATOR | Yes | Orchestrator agent identity |
MITRITY_AGENT_ID_DATA / _KEY_DATA | Yes | Data-worker agent identity |
MITRITY_AGENT_ID_NOTIFY / _KEY_NOTIFY | Yes | Notification-worker agent identity |
See .env.example in the repo.
Troubleshooting
The orchestrator boots and prints "Skipping delegation/TI scenarios — current tenant is on the Starter plan."
Your tenant is on Starter. Delegation chains and threat intelligence require Pro or Enterprise. Upgrade at /app/billing.
Containers boot but I see no delegation chains in the dashboard.
Check /audit first — if there are no events from any of the three agents, verify each agent's AGENT_ID + AGENT_KEY env var pair matches what's in the dashboard. Each container needs its own correct pair.
The S2 privilege-escalation scenario doesn't fire.
Confirm the three policies from Step 1b are authored and attached to the matching agents. The orchestrator's policy must deny database:write* while the data-worker's policy allows it — that gap is what the delegation engine compares. If both agents resolve to the same default-allow, there's no escalation to detect.
Next steps
- Demo: Mitrity Gateway — single-agent demo focused on policies, DLP, injection, holds
- Demo: Mitrity MCP Sidecar — single-agent demo using the transparent-proxy deployment form
- Integration modes — gateway vs. sidecar architectural comparison
- Delegation chains — per-agent allow/disallow lists, depth limits, fan-out windows, privilege-escalation detection
- Threat intelligence — indicator feeds, per-severity policies, per-indicator overrides