SPEC-0010: A2A Discovery + Human-Vended Friending
SPEC · SPEC-0010 · Status · draft · Date · 2026-07-06 · Implements · ADR-0010 · Requires · SPEC-0009
Overview
Switchboard runs two complementary protocols: MCP connects an agent to tools (the todo/webhook verbs on its vended endpoint, ADR-0008), and A2A connects an agent to other agents for discovery. Personas are published as A2A Agent Cards (SPEC-0009, ADR-0009). This capability defines how one agent gains scoped access to hand work to another: a friend request that a human approves, where approval is the vend that mints a scoped MCP endpoint.
The controlling rule is that A2A is strictly outward-facing (discovery/announcement) and grants nothing; inbound work is governed by human vending and lands as durable todos (ADR-0007), never as A2A direct peer tasks. Friend edges are per-direction, revocable, and non-transitive, and every request MUST carry verifiable, OIDC-signed provenance of the requesting human.
This spec realizes ADR-0010. It depends on SPEC-0009 for the personas/Agent Cards that are discovered, and on ADR-0011 for provenance.
Requirements
Requirement: Friend-Request Lifecycle
A friend edge MUST progress through the states pending → approved | denied, with approved edges
further transitioning to revoked. Sending a friend request MUST create a pending edge that grants no
access. Approval MUST transition the edge to approved and mint a scoped MCP endpoint; denial MUST
transition it to denied (terminal, no grant); revocation MUST transition an approved edge to revoked
and kill the vended endpoint. A request MUST carry a requested_scope (queues + verbs), a reason, and
verifiable OIDC-signed provenance of the requesting human.
Scenario: Pending edge grants nothing
- WHEN agent A sends a friend request to persona B and the edge is created in
pending - THEN A MUST have no access to B until a human approves; no MCP endpoint MUST exist for the edge
while it is
pending
Scenario: Approval mints the endpoint
- WHEN B's owning human approves the pending request
- THEN the edge MUST transition to
approvedand a scoped MCP endpoint MUST be minted granting A the approved access to B
Scenario: Denial is terminal
- WHEN B's owning human denies the pending request
- THEN the edge MUST transition to
denied, no endpoint MUST be minted, and the requester MAY re-request subject to quotas
Requirement: Approval Is the Vend, Narrow-Only
Approval MUST be the sole act that mints access; there MUST be no separate vend step. At approval the
human MAY narrow the scope, and the granted_scope MUST be a subset of the requested_scope — the
granted access MUST NOT exceed what was requested, and MUST NOT exceed what the human allows. The minted
endpoint's scope MUST equal requested_scope ∩ human-narrowing.
Scenario: Granted scope cannot exceed requested
- WHEN a human approves a request and supplies a
granted_scope - THEN the system MUST reject any
granted_scopethat is not a subset of therequested_scope, and the minted endpoint MUST carry only the narrowed scope
Scenario: Approval with no narrowing grants the requested scope
- WHEN a human approves without narrowing
- THEN the minted endpoint's scope MUST equal the
requested_scope
Requirement: Verifiable OIDC-Signed Provenance
A friend request MUST carry verifiable, OIDC-signed provenance of the requesting human
(ADR-0011), not the agent's
self-assertion of who owns it. A request whose provenance does not validate MUST be rejected as
unauthenticated and MUST NOT create a pending edge. The approval todo MUST carry a
provenance_verified flag that is true only when provenance validated.
Scenario: Missing or invalid provenance is rejected
- WHEN a friend request arrives without valid OIDC-signed human provenance
- THEN it MUST be rejected as
unauthenticatedand no pending edge MUST be created
Scenario: Approver sees an attested counterparty
- WHEN the approval todo is generated for a validly-attested request
- THEN its
from_humanfield MUST be the OIDC subject of the requesting human (attested, not self-asserted) andprovenance_verifiedMUST betrue
Requirement: Approval Delivered as a Todo
The approval request MUST be delivered as a todo in the target human's own queue, dogfooding the
todo primitive (ADR-0007). The approval todo MUST
carry a legible who / why / requested-scope summary — request_id, from_human, from_persona,
to_persona, requested_scope, reason, and provenance_verified — so the human decides with full
context rather than a raw blob.
Scenario: Approval lands as a legible todo
- WHEN a valid friend request is accepted
- THEN a todo MUST be created in the target human's queue carrying the who/why/requested-scope
summary and the
provenance_verifiedflag
Requirement: Per-Direction, Revocable, Non-Transitive Edges
Friend grants MUST be per-direction: an A→B grant MUST NOT imply a B→A grant; a B→A grant requires its own request and approval. Either grant MUST be revocable at any time, and revocation MUST be instant and one-sided — revoking one direction MUST leave the other intact. Friending MUST be non-transitive: friending B MUST reveal nothing about B's other friends, B's other personas, or B's queues beyond the one granted; there MUST be no graph traversal across edges.
Scenario: Approval is one-directional
- WHEN A's request to hand work to B is approved
- THEN B MUST NOT thereby gain any access to A; a B→A grant MUST require its own request and approval
Scenario: Revocation is one-sided
- WHEN one direction of a mutual friendship is revoked
- THEN that endpoint MUST be killed instantly and the other direction MUST remain intact
Scenario: Friendship reveals nothing transitive
- WHEN A friends B
- THEN A MUST NOT be able to enumerate B's other friends, B's other personas, or any of B's queues beyond the granted one
Requirement: Work Flows as Todos, Not A2A Tasks
After a grant is minted, cross-agent work MUST flow as todos created in the granted queue (via
create_for), which are durable, owned, dedup'd, and leaseable
(ADR-0007). Switchboard MUST NOT expose an A2A
direct-task delegation intake; A2A's role MUST be limited to discovery/announcement.
Scenario: Cross-agent work arrives as a todo
- WHEN A, holding an approved grant, hands work to B
- THEN the work MUST land as a todo in B's granted queue via
create_for, and switchboard MUST expose no A2A direct-task endpoint to accept it otherwise
Requirement: Anti-Spam — Bounded Discovery and Quotas
Discovery MUST be limited to a bounded set of known directories, not the open internet, so an agent cannot be friend-requested by an arbitrary unknown party at will. Friend requests MUST be quota'd and rate-limited per requester to blunt flooding. Every approval todo MUST present a legible who/why/scope summary.
Scenario: Requests over quota are refused
- WHEN a requester exceeds its friend-request quota or rate limit
- THEN further requests MUST be refused until the quota window resets, and no pending edge MUST be created for the refused requests
Security Requirements
Authentication
Every endpoint in this capability defaults to requiring authentication. The friend-request intake is authenticated by OIDC-signed human provenance (not a bearer credential and not the agent's self-assertion); the approval/deny/revoke actions are authenticated as the target human via the web-UI session; work handoff is authenticated by the vended MCP endpoint credential (ADR-0008).
| Endpoint | Auth | Justification |
|---|---|---|
send_friend_request (A2A intake) | Required | Must carry verifiable OIDC-signed human provenance; invalid → unauthenticated, no edge. |
list_pending_approvals | Required | Authenticated target human only; lists their own pending approval todos. |
approve / deny | Required | Authenticated target human only; approval mints an endpoint (the vend). |
revoke | Required | Authenticated owning human only; kills a vended endpoint instantly. |
create_for (work handoff) | Required | Bearer credential of the minted, scoped MCP endpoint; verb + queue enforced at the boundary. |
There are no public endpoints in this capability. (Persona discovery cards are public but belong to SPEC-0009; friending itself grants and mutates access and is authenticated end-to-end.)
Rate Limiting
Friend requests MUST be quota'd and rate-limited per requesting human/persona (RECOMMENDED default:
a bounded number of pending requests per target and a per-hour request ceiling per requester) to blunt
flooding. Rate-limit rejections MUST NOT create pending edges. Approve/deny/revoke inherit the web-UI
session rate limits. Work handoff (create_for) is bounded by the todo-queue's idempotency and the
endpoint's scope.
Security Headers
All HTTP responses MUST include:
Content-Security-Policy:default-src 'self'for the human approval web UI;default-src 'none'for JSON API responses.X-Frame-Options: DENYX-Content-Type-Options: nosniffReferrer-Policy: strict-origin-when-cross-origin
Request Body Size Limits
All endpoints accepting bodies MUST bound them with http.MaxBytesReader. Default limit: 64 KiB for
friend-request and approval payloads (a requested_scope is a small list of queues/verbs plus a reason
string, not a large blob). Oversized bodies MUST be rejected before parsing.
CSRF Protection
State-changing human actions — approve, deny, revoke — MUST implement CSRF protection via the
switchboard web UI's session-bound token strategy (SameSite session cookie + per-form token). The
send_friend_request A2A intake is not a browser-form flow; it is protected by OIDC-signed provenance
rather than a CSRF token.
Redirect Validation
No user-supplied redirects exist in this capability. Approval and revocation return to fixed, server-owned
web-UI paths; the requesting persona's url is server-derived (SPEC-0009), not caller-supplied. Open
redirects MUST NOT be permitted.