Confer — Protocol design

Defines all protocols between Confer instances, and between the user client and the server. Every protocol is built on open standards to ease future federation.

Agent identity

DID:web format

Each user/enterprise instance hosts its own DID document:

https://acme.com/.well-known/did.json

DID document structure (W3C DID v1.0 compatible):

{
  "@context": ["https://www.w3.org/ns/did/v1"],
  "id": "did:web:acme.com",
  "verificationMethod": [
    {
      "id": "did:web:acme.com#key-1",
      "type": "Ed25519VerificationKey2020",
      "controller": "did:web:acme.com",
      "publicKeyMultibase": "z6MkpTHR8VNsBxYAAWHut2Geadd9jSrue..."
    }
  ],
  "service": [
    {
      "id": "did:web:acme.com#confer-agent",
      "type": "ConferAgent",
      "serviceEndpoint": "https://acme.com/a2a/v1"
    }
  ]
}

The DID form of a user Agent: did:web:acme.com:agents:laowang — primary instance + path segment. This lets a single instance host multiple users.

Key rotation

AgentFacts (NANDA-compatible)

Each Agent publishes an AgentFacts document describing itself. Location:

https://acme.com/agents/{slug}/agent.json

Or the well-known master directory:

https://acme.com/.well-known/agents.json

Example structure:

{
  "@context": "https://nanda.dev/schemas/agent/v1",
  "did": "did:web:acme.com:agents:support",
  "name": "ABC Industries Support Agent",
  "description": "Technical support for X100, X200 industrial controllers",
  "owner": {
    "type": "Organization",
    "name": "ABC Industries Ltd.",
    "url": "https://acme.com"
  },
  "capabilities": [
    {
      "type": "qa",
      "scope": ["X100", "X200", "Modbus", "RTU", "TCP"],
      "languages": ["en", "zh", "de"]
    },
    {
      "type": "code-generation",
      "scope": ["python", "c", "embedded"],
      "languages": ["en", "zh"]
    }
  ],
  "endpoints": {
    "a2a": "https://acme.com/a2a/v1",
    "stream": "https://acme.com/a2a/v1/stream"
  },
  "trust": {
    "verifiedBy": ["did:web:nanda.org"],
    "issuedAt": "2024-10-01T00:00:00Z"
  },
  "publicKey": {
    "id": "did:web:acme.com#key-1",
    "type": "Ed25519VerificationKey2020"
  }
}

Field descriptions:

A2A protocol

Protocol layer

All A2A communication goes over HTTPS POST/GET, encoded as JSON.

Key point: use HTTP Message Signatures (RFC 9421) instead of bearer tokens. Reasons:

Inbound request example

POST /a2a/v1/messages HTTP/1.1
Host: acme.com
Content-Type: application/json
Date: Sun, 24 Nov 2024 14:30:00 GMT
Digest: SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=
Signature: keyId="did:web:vendor-x.com#key-1",
           algorithm="ed25519",
           headers="(request-target) host date digest",
           signature="aBcDeF..."
Authorization: Capability eyJhbGciOiJFZDI1NTE5IiwidHlwIjoiQ2FwIn0...

{
  "from": "did:web:vendor-x.com:agents:engineer-li",
  "to": "did:web:acme.com:agents:support",
  "thread_id": "thread_8f3a9c",
  "message": {
    "type": "question",
    "content": "X100 在 RTU 模式下的电压范围?",
    "language": "zh",
    "context": {
      "via": "claude-code",
      "project_hint": "modbus integration"
    }
  }
}

Verification flow (receiver)

  1. Parse the Signature header
  2. Extract the keyId (which contains the DID)
  3. Fetch the DID document (with caching: ETag + 60s TTL)
  4. Retrieve the public key and verify the signature
  5. Verify that Digest matches the body hash
  6. Check that Date is within 5 minutes (replay protection)
  7. Verify the Capability token (macaroon-style, detailed below)
  8. Connection consent gate: has the sender already been added as a contact by the receiver? Not connected → do not run the LLM; hold it as a connection request (see below)
  9. Connected → go through the policy engine to decide whether to respond

Capability token

A capability token lets the sending Agent state “I am acting on behalf of user X to ask about a question of type Y”, allowing fine-grained restriction of permissions.

JWT-style but using the macaroon approach:

{
  "iss": "did:web:vendor-x.com",
  "sub": "did:web:vendor-x.com:users:engineer-li",
  "aud": "did:web:acme.com",
  "scope": ["ask:technical", "ask:product:X100"],
  "exp": 1737000000,
  "ctx": {
    "thread_id": "thread_8f3a9c",
    "delegation_depth": 1
  }
}

Streaming the response

The LLM generates the answer as a stream, and A2A also supports SSE:

GET /a2a/v1/stream/{message_id} HTTP/1.1
Host: acme.com
Signature: ...

Returns text/event-stream:

event: token
data: {"text": "X100 "}

event: token
data: {"text": "在 RTU "}

event: citation
data: {"source": "X100 安装手册 p.12", "url": "..."}

event: done
data: {"thread_id": "thread_8f3a9c"}

Permission model (Claude Code-inspired)

A three-tier permission hierarchy:

L1 - Automatic (no confirmation needed)

L2 - Ask once

UI presentation: a permission card pops up with 4 options:

UI presentation: a modal dialog + a detailed list of actions + a countdown (to prevent misclicks).

Standing policies

The user can set rules in advance that override the default behavior:

peer.acme-industries:
  allow:
    - read: "src/modbus/**"
    - ask: "technical:*"
  deny:
    - read: ".env"
    - read: "**/secrets/**"
    - ask: "personal:*"
  always_consult: true

peer.unknown:
  default: ask_user
  require_human_in_loop: true

Answering an A2A message consumes the receiver’s LLM budget. To prevent a stranger Agent from blasting messages without the owner’s knowledge and burning through the owner’s tokens, a connection is a precondition for consumption:

The model mirrors LinkedIn / enterprise federation: the discovery layer is open (anyone can read agents.json and AgentFacts), while the interaction layer requires consent (you can only consume the other party’s compute after connecting).

There are two paths to becoming “connected”:

  1. The receiver proactively adds the peer via POST /contacts/lookupPOST /contacts;
  2. The peer initiates first, and the receiver approves its connection request in the inbox.

Pending inbox (offline auto-answer)

When the owner is offline and receives a question from a connected peer:

Federated discovery

Domain lookup

Given the domain acme.com, the client:

  1. Fetches https://acme.com/.well-known/did.json to get the primary DID
  2. Fetches https://acme.com/.well-known/agents.json to list all public Agents under that domain
  3. Picks one and adds it as a contact

Public registry (v2+)

Integrate with the NANDA Index or a similar public registry, supporting:

Trust graph (v2+)

Anti-spam

Translation policy

Protocol evolution strategy

← Back to Confer A2A · DID:web · RFC 9421 · NANDA