Agent Mesh
V2 only — invite-only edition. This is part of AI Partner V2 and is not in the open-source V1 you self-host from the Quick Start. V2 is available now, by invite. See V1 vs V2.
Overview
Agent Mesh operates in two directions:
- Outbound (orchestrator mode) — AI Partner discovers external agents on the internet, delegates tasks to them with cryptographically scoped authority, classifies their behavior in real time, and maintains an immutable audit trail.
- Inbound (orchestratee mode) — AI Partner accepts tasks delegated by external A2A orchestrators, including another AI Partner instance. Toggled independently via a sub-setting.
Both directions are disabled by default. Enable them in Settings → Agent Mesh.
How it works
Outbound — AI Partner as orchestrator
AI Partner (orchestrator)
│
├─ discovers external agent via /.well-known/agent.json
├─ mints a scoped Ed25519 JWT (5 min TTL)
├─ plants a canary tripwire token alongside it
├─ delegates the task (stream / push / poll)
├─ classifies agent behavior in real time
└─ revokes all authority the moment anything looks wrong
Inbound — AI Partner as orchestratee
External orchestrator (e.g. another AI Partner instance)
│
├─ reads /.well-known/agent.json → sees url=/api/a2a, streaming=true
├─ sends POST /api/a2a { method: "message/stream", goal: "..." }
│ X-API-Key: <consumer key from Admin Console → Agent Access>
│
AI Partner (orchestratee)
├─ validates API key → resolves the calling consumer's service account
├─ creates inbound_task record owned by that consumer (taskId returned)
├─ runs the goal AS the consumer —
│ isolated workspace, metered usage, per-consumer concurrency cap
├─ streams TaskStatusUpdateEvent / TaskArtifactUpdateEvent back via SSE
└─ updates inbound_tasks with status + result on completion
Protocol model
Protocol detection (at registration)
When you register an agent URL, AI Partner runs a two-step probe:
- A2A probe —
GET {url}/.well-known/agent.json. If a valid AgentCard is returned, protocol isa2a. - REST fallback probe —
POST {url}/tasks. If it responds, protocol isrest_fallback. - Otherwise — protocol is
unknown(observe-only, no tokens issued, no canary planted).
Delivery mode (at delegation time)
For a2a agents, the delivery mode is chosen from the registered AgentCard:
| Mode | Condition | Mechanism |
|---|---|---|
stream | capabilities.streaming = true | Agent streams TaskStatusUpdateEvent / TaskArtifactUpdateEvent back via SSE. Relayed to frontend via Socket.IO. |
push | capabilities.pushNotifications = true | Agent POSTs a notification to our callback URL. We then call tasks/get. |
poll | neither | We poll tasks/get every 5 seconds until terminal state. |
rest_fallback agents always use poll.
Security model
SSRF Guard
Every URL is validated before any network call. Blocked ranges:
- Loopback:
localhost,127.0.0.0/8,::1 - Private:
10.0.0.0/8,172.16–31.x,192.168.0.0/16 - Link-local / metadata:
169.254.0.0/16,100.64.0.0/10 - Non-HTTP schemes (
file://,ftp://, etc.)
The guard runs at three explicit points: registration, task dispatch, and the delegate_to_external_agent executor tool.
Scoped JWT tokens
Each delegation gets a fresh Ed25519-signed JWT:
- Lifetime: 5 minutes
- Scope: array of permitted actions (e.g.
["task:read", "task:write"]) - Stored:
jtiinexternal_tokenstable - Verified via:
active_tokensview (revoked_at IS NULL AND expires_at > NOW()) - Revoked immediately: on delegation revoke, canary trip, or HOSTILE classification
AI Partner's public key is available at GET /.well-known/jwks.json so external agents can verify tokens you issue.
Canary tokens
A hidden "tripwire" credential is planted alongside every real token for a2a and rest_fallback agents. If any inbound request ever presents this canary:
canary_triggeredevent written to the audit ledger- Agent immediately forced to
HOSTILE - All active tokens for the agent revoked
- Forensic snapshot written to
data/forensics/{agentId}-{timestamp}.json - Socket.IO
agent:hostilealert sent to the Triage tab
Canary tokens are never minted for unknown protocol agents (no comms channel to plant through).
Callback verification (push mode)
Inbound callbacks are verified by verifyCallbackPoI middleware:
Authorizationheader = valid JWT inactive_tokensviewX-Poi-Signature= valid Ed25519 signature over the raw request bodycorrelationIdin body matches an active delegationX-Timestampheader: reject if|now − timestamp| > 30 secondsX-Nonceheader: reject if seen within the last 60 seconds (in-memory LRU)
Any failure → HTTP 401 + callback_rejected event in audit ledger.
Classification
Classification is fully deterministic — rules run in priority order and the first match wins. The LLM is called only to generate the human-readable reasoning string, never to determine the class.
| Rule | Trigger | Class | Confidence |
|---|---|---|---|
| 1 (highest) | Canary token presented | HOSTILE | 100 |
| 2 | PoI absent or signature invalid | ROGUE | 90 |
| 3 | Correlation ID absent on callback | ROGUE | 80 |
| 4 | JSON-RPC response malformed | CONFUSED | 70 |
| 5a | 0 out-of-scope skills observed | FRIENDLY | 85 |
| 5b | 1–2 minor out-of-scope skills | CONFUSED | 65 |
| 5c | 3+ or any sensitive capability | ROGUE | 80 |
Trust classes: UNCLASSIFIED → FRIENDLY → CONFUSED → ROGUE → HOSTILE
You can manually override any classification in the Triage tab.
The five tabs
Registry
Register external agents by pasting a URL. AI Partner probes the agent automatically and displays:
- Protocol badge —
a2a(green),rest_fallback(blue), orunknown(gray) - Trust class badge — color-coded from UNCLASSIFIED to HOSTILE
- Online/offline indicator — based on consecutive failure tracking (circuit breaker at 5 failures)
Delegate
Compose a task for any registered agent:
- Select target agent
- Enter a goal
- Add optional constraints
- Set a timeout
Returns a delegationId for tracking.
Delegations
Live view of all delegations. For each:
- Status badge (
pending→running→completed/failed/revoked) - Delivery mode indicator (
stream/push/poll) - Result preview (truncated at 300 chars)
- Respond inline form when status is
input_required - Revoke button for active delegations
Updates arrive in real time via Socket.IO delegation:update events.
Triage
Agents grouped by trust class (HOSTILE first). For each:
- Fired rules that led to the classification
- Manual override buttons (FRIENDLY / CONFUSED / ROGUE / HOSTILE)
- Forensic snapshot link for HOSTILE agents
- Real-time alert banner on
agent:hostileSocket.IO event
Audit Ledger
Immutable, append-only audit trail. Features:
- 50-row pagination
- Filter by
correlation_idorevent_type - Expandable payload viewer
- CSV export —
audit-{correlationId}.csvdownload
(PoI column is SHA-256 hash only, not raw blob; formula-injection cells sanitized)
AI Partner's own A2A identity
AI Partner publishes its own AgentCard so other A2A-compatible orchestrators can discover it:
GET /.well-known/agent.json — AgentCard for AI Partner
GET /.well-known/jwks.json — Ed25519 public key in JWK format
These endpoints are served before any authentication middleware so they're reachable without credentials.
Using Agent Mesh from goal execution
The delegate_to_external_agent tool is available inside any goal:
Tool: delegate_to_external_agent
Args:
agentId: <id from the registry>
goal: "Fetch the latest pricing from Acme Corp's agent and return a JSON summary"
constraints: ["return_json", "no_pii"]
timeout_ms: 30000
The executor validates the agent URL against the SSRF guard before dispatch, then monitors the delegation until completion and returns the result to the goal context.
Forensic snapshots
When an agent is classified HOSTILE (by canary trip or manual override), a JSON snapshot is written to {appDataDir}/forensics/:
{
"agentId": "...",
"agentCard": { ... },
"trustClass": "HOSTILE",
"classificationReason": "Canary token presented at 2026-05-24T09:15:00Z",
"auditRows": [ ... ],
"observedSkills": [ ... ],
"recentRequests": [ ... ]
}
Download via:
GET /api/external-agents/forensics/:filename
The filename is validated against a strict [\w-]+\.json regex to prevent path traversal.
Data model
Five tables are added to the SQLite database:
| Table | Purpose |
|---|---|
external_agents | Registered agents — protocol, AgentCard, trust class, circuit breaker |
external_delegations | Active and historical task delegations — status, delivery mode, result |
external_agent_audit | Immutable append-only event ledger — never deleted |
external_tokens | JWT jti registry + canary tokens — verified via active_tokens view |
inbound_tasks | Tasks received from external orchestrators — goal, execution_id, status, result |
Enabling Agent Mesh (outbound)
- Open Settings in the sidebar
- Find the Agent Mesh toggle card
- Switch the main toggle on — AI Partner verifies the backend subsystem is ready
- The Agent Mesh sidebar item appears
To disable: toggle off — the sidebar item disappears and no new delegations can be issued. Existing audit records are preserved.
Enabling inbound tasks
Within the Agent Mesh settings card there is an Inbound sub-section:
- Enable the main Agent Mesh toggle first (or enable inbound independently)
- Turn on Accept inbound tasks
- The AgentCard at
GET /.well-known/agent.jsonimmediately updates:url→/api/a2acapabilities.streaming→truesecurity→X-API-Keyheader
External orchestrators that re-read the AgentCard will automatically switch to streaming delivery mode.
Two-instance AI Partner collaboration
The primary use case for inbound A2A is two AI Partner instances working in tandem — Instance A coordinates, Instance B specialises.
Setup
Instance B (executor) — configure first:
- Admin Console → Agent Access → toggle Inbound Agent API ON (and
Outbound Agent Mesh if B will also delegate). Also available via
PATCH /api/admin/agent-access. - Admin Console → Agent Access → New Consumer (e.g.
instance-a) — creates a service account and mints its API key in one step. Copy theaip_…key immediately; it is shown only once. Each external caller gets its own consumer: isolated workspace, metered usage, instantly-revocable key. - Note Instance B's base URL, e.g.
http://instance-b:3000
Full consumer model, limits, billing statements, and error codes: see the Agent API guide.
Instance A (coordinator):
- Agent Mesh → Registry → Register
http://instance-b:3000- AI Partner probes
/.well-known/agent.json - Sees
url=/api/a2a,streaming: true→ protocola2a, delivery modestream
- AI Partner probes
- Agent Mesh → Delegate:
- Agent: Instance B
- Goal:
"Summarise all files in /workspace/reports" - → sends
POST /api/a2a { method: "message/stream" }withX-API-Key: instance-a-key
- Agent Mesh → Delegations:
- Live streaming status as Instance B executes the goal
- Result appears when complete
- Instance B is classified by AgentClassifier (rule 5: skills diff)
Wire format (what Instance A sends)
POST /api/a2a
X-API-Key: instance-a-key
{
"jsonrpc": "2.0",
"id": "rpc-uuid",
"method": "message/stream",
"params": {
"message": {
"role": "user",
"parts": [{ "text": "Summarise all files in /workspace/reports" }]
},
"metadata": { "correlationId": "corr-uuid" }
}
}
SSE events streamed back
data: {"type":"TaskStatusUpdateEvent","taskId":"...","status":{"state":"running"}}
data: {"type":"TaskStatusUpdateEvent","taskId":"...","status":{"state":"running","message":"Iter 2: Reading files..."}}
data: {"type":"TaskArtifactUpdateEvent","taskId":"...","artifact":{"parts":[{"text":"Here is the summary..."}]}}
data: {"type":"TaskStatusUpdateEvent","taskId":"...","status":{"state":"completed"}}