Federation
Cross-instance coordination with HMAC-signed trust, scope constraints, hop limits, and signed receipts
Federation Protocol
Federation enables Chorus instances to coordinate across organizational boundaries. One instance can deliver signals to another, claim tasks on a peer's behalf, and acknowledge delivery -- all while maintaining each organization's autonomy and security.
Trust Lifecycle
Federation trust is established through a peer registration process:
1. Peer Registration
A peer org record is created with the remote instance's endpoint and a shared secret:
PeerOrg {
id: "peer:abc"
name: "Partner Org"
endpoint: "https://partner.example.com"
shared_secret: "generated-256-bit-key" // never exposed in API responses
allowed_scopes: { signal_types: ["task", "query"], max_urgency: 0.8 }
expires_at: "2026-06-01T00:00:00Z"
status: "active"
}
2. Shared Secret
The shared secret is a 256-bit key used for HMAC-SHA256 signing. It is:
- Generated during trust establishment
- Stored server-side only (the
shared_secretfield is never returned in API responses) - Shared out-of-band between the two organizations
- Used to sign and verify all federation requests
3. Expiry
Trust can have an optional expires_at datetime. Expired trust is rejected at the federation middleware level before any processing occurs.
HMAC-SHA256 Proof
Every federation request carries a cryptographic proof in the X-Federation-Proof header:
FederationProof Schema
{
"signature": "hmac-sha256-hex-string",
"timestamp": 1710000000000,
"nonce": "unique-random-string",
"body_hash": "sha256-of-request-body"
}
Signature Construction
The signature is computed over a canonical string:
message = method + "\n" + body_hash + "\n" + timestamp + "\n" + nonce
signature = HMAC-SHA256(shared_secret, message)
Where:
methodis the HTTP method (e.g.,POST)body_hashis the SHA-256 hash of the raw request bodytimestampis Unix epoch millisecondsnonceis a unique random string
Verification Steps
- Extract
X-Federation-Proofheader andX-Federation-Peer-Idheader - Look up peer by ID, verify status is
active - Check trust hasn't expired
- Verify nonce hasn't been used (replay prevention)
- Recompute HMAC signature and compare
- Mark nonce as consumed
Nonce Replay Prevention
Each nonce is single-use. The federation middleware:
- Extracts the nonce from the proof
- Checks if it has been seen before (via the nonce store)
- Rejects with
FED_REPLAY_DETECTEDif duplicate - Stores the nonce after successful verification
This prevents replay attacks where a captured request is resent.
Federation Routes
All routes are mounted at /federation and use HMAC proof auth (not Bearer token):
| Method | Path | Description |
|---|---|---|
POST | /federation/deliver | Receive a signal from a peer |
POST | /federation/claim | Claim a task on behalf of a peer |
POST | /federation/cancel | Cancel a task on behalf of a peer |
POST | /federation/ack | Acknowledge a signal on behalf of a peer |
Deliver
Creates a local signal with federation provenance metadata. The signal includes:
{
"metadata": {
"federation_source": {
"peer_id": "peer:abc",
"remote_identity": "identity:xyz",
"remote_org": "Partner Org"
},
"federation_hops": 1
}
}
Returns a signed FederationReceipt:
{
"signal_id": "signal:local123",
"receipt": {
"signal_id": "signal:local123",
"status": "delivered",
"timestamp": 1710000000000,
"signature": "hmac-sha256-receipt-signature"
}
}
Scope Constraints
Peers can be restricted in what they can do through AllowedScopes:
| Scope | Type | Description |
|---|---|---|
signal_types | string[] | Subset of the 8 signal types the peer may send |
ring_scopes | string[] | Ring IDs the peer may target |
max_urgency | number | Maximum urgency level (0.0-1.0) the peer may use |
The federation middleware checks scopes before processing any delivery:
checkFederationScope(peer, signal_type, to_ring, urgency) -> { allowed, reason? }
If a peer tries to send a signal type not in their allowed_scopes.signal_types, the request is rejected with FED_SCOPE_DENIED.
Hop Limit Prevention
Federation signals have a maximum hop count of 1 to prevent circular routing:
- When a peer delivers a signal, the
federation_hopsmetadata is set to1 - If a received signal already has
federation_hops >= 1, it is rejected withFED_HOP_LIMIT - This prevents Org A -> Org B -> Org A infinite loops
Federation Metadata
Signals delivered via federation carry provenance metadata:
{
"federation_source": {
"peer_id": "peer:abc",
"remote_identity": "identity:xyz",
"remote_org": "Partner Org"
}
}
This enables local agents to distinguish federated signals from local ones and understand their origin.
RPC Methods
Federation is also accessible via JSON-RPC:
federation/deliver-- Outbound: deliver a signal to a named peer (admin only)federation/status-- View active peer count and trust details (admin only)
See the RPC Reference for full parameter documentation.
Error Codes
| Code | HTTP | Description |
|---|---|---|
FED_PROOF_MISSING | 401 | No federation proof header |
FED_PROOF_INVALID | 401 | Malformed proof header |
FED_PROOF_FAILED | 401 | HMAC signature verification failed |
FED_PEER_MISSING | 401 | No peer ID header |
FED_PEER_DENIED | 403 | Peer not found or not active |
FED_REPLAY_DETECTED | 403 | Nonce already used |
FED_SCOPE_DENIED | 403 | Signal type or ring not in allowed scopes |
FED_DELIVERY_FAILED | 502 | Outbound delivery to peer failed |
FED_TRUST_NOT_ESTABLISHED | 403 | No shared secret with peer |
FED_TRUST_EXPIRED | 403 | Trust has expired |
FED_HOP_LIMIT | 400 | Signal already federated (max 1 hop) |