Federation API
Federation enables Cryptid servers to route messages across different domains, creating a truly decentralized messaging network. Servers authenticate with each other using server certificates, then relay encrypted messages without ever understanding their content or relationships.
Key Principle: Federated servers act as encrypted message relays. They forward opaque MLS ciphertext between domains without knowing senders, content, or social graphs.
Cross-Server Message Delivery
Section titled “Cross-Server Message Delivery”Delivers messages from another Cryptid server to local recipients. The receiving server validates the federation token, checks that recipients are announced locally, and queues messages for delivery.
Federation Flow
Section titled “Federation Flow”Device A Server A Server B Device B
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ 1. Announce (Register) │ │ │ ├───────────────────────────►│ │ │ │ │ │ │ │ │ │ │ │ JWT Token │ │ │ │◄───────────────────────────┤ │ │ │ │ │ │ │ │ │ │ │ 2. Send encrypted msg │ │ │ ├───────────────────────────►│ │ │ │ │ │ │ │ │ 3. Federation │ │ │ ├─────────────────────────────────►│ │ │ │ (relay) │ │ │ │ │ 4. Queue msg │ │ │ ├─────────────────────────────►│ │ │ │ │ │ │ │ │ │ │ │ 5. Retrieve (WebSocket) │ │ │ │◄─────────────────────────────┤ │ │ │ │
Neither Server A nor Server B learns message content or sender identity.
Endpoint
Section titled “Endpoint”PUT /federation/v1/deliver/{transaction_id}
Request
Section titled “Request”PUT /federation/v1/deliver/unique_transaction_id HTTP/1.1Content-Type: application/jsonAuthorization: Bearer federation_tokenX-Origin-Server: alice.server1.com
{ "origin_server": "alice.server1.com", "transaction_id": "unique_transaction_id", "timestamp": 1759000000, "messages": [ { "message_id": "32_byte_message_id_hex", "recipient_address": "bob@server2.com", "group_id": "32_byte_group_id_hex", "mls_ciphertext": "base64_encrypted_content", "sender_signature": "ed25519_signature", "timestamp": 1758999498, "message_type": "Text" } ]}
Parameters:
-
origin_server
: Domain of the originating server (must matchX-Origin-Server
header) -
transaction_id
: Unique transaction ID (typically UUIDv4) -
timestamp
: Unix timestamp of the federation request (not message timestamp) -
messages[]
: Array of encrypted messages to deliver
Message Fields:
-
recipient_address
: Must be a device announced on the receiving server -
mls_ciphertext
: Encrypted payload (opaque to both servers) -
sender_signature
: Ed25519 signature (server does not verify, recipient does)
The sender’s address is intentionally NOT included in the federated messages to protect sender privacy. Recipients can derive sender identity from the MLS group context or signature validation.
Response (200 OK)
Section titled “Response (200 OK)”{ "transaction_id": "unique_transaction_id", "status": "accepted", "accepted_messages": 1, "rejected_messages": 0, "timestamp": 1759000000}
Response Fields:
-
status
:accepted
: All messages queued successfullypartial
: Some messages rejected (seerejected_messages
)rejected
: All messages rejected
-
accepted_messages
: Count of messages successfully queued -
rejected_messages
: Count of messages rejected (recipient not announced, invalid format, etc) -
timestamp
: Server’s current timestamp
Partial Success: If some recipients are not announced locally, those messages are rejected while others are accepted.
Error Messages
Section titled “Error Messages”Server error notifications:
{ "type": "error", "error": "INVALID_TOKEN", "message": "Federation token invalid or expired", "code": 4008}
Common Errors:
4001
: Invalid signature/certificate4004
: Rate limit exceeded4006
: Federation failed4008
: Token expired
See Error Handling for complete error code reference.
Federation Authentication
Section titled “Federation Authentication”Authenticates a server for cross-server message delivery. Servers must prove ownership of their domain before they can relay messages, preventing impersonation attacks.
Certificate Validation: The receiving server validates that the origin server controls the claimed domain using the provided server certificate.
Endpoint
Section titled “Endpoint”POST /federation/v1/auth
Request
Section titled “Request”POST /federation/v1/auth HTTP/1.1Content-Type: application/json
{ "origin_server": "alice.server1.com", "server_certificate": "base64_server_certificate", "challenge_response": "proof_of_server_ownership"}
Parameters:
-
origin_server
: Domain of the server requesting federation access -
server_certificate
: Base64 encoded server certificate (TLS certificate or equivalent) -
challenge_response
: Cryptographic proof of domain ownership
Challenge-Response Flow:
-
Origin server requests authentication with certificate
-
Receiving server validates certificate matches
origin_server
domain -
Receiving server generates random challenge:
nonce = random_bytes(32)
-
Origin server signs challenge with server private key:
signature = sign(server_key, nonce)
-
Receiving server verifies signature with certificate public key
-
Receiving server issues time-limited federation token
Implementation Note: The challenge_response
field contains the signed challenge. The exact challenge mechanism is implementation-specific, but must cryptographically prove server ownership.
Response (200 OK)
Section titled “Response (200 OK)”{ "federation_token": "jwt_federation_token", "expires_at": 1759003600, "rate_limits": { "messages_per_minute": 1000, "transactions_per_minute": 100 }}
Response Fields:
-
federation_token
: JWT token for authenticated federation requests -
expires_at
: Unix timestamp when token expires (typically 1 hour) -
rate_limits
: Server’s federation rate limits-
messages_per_minute
: Total individual messages allowed per minute -
transactions_per_minute
: Total federation requests allowed per minute
-
Token Usage: Include this token in the Authorization: Bearer {federation_token}
header for all /federation/v1/deliver
requests.
Rate Limits Explained:
-
If you send 10 messages in 1 transaction, you consume: 1 transaction, 10 messages
-
If you send 1 message in 10 transactions, you consume: 10 transactions, 10 messages
-
Best Practice: Batch messages efficiently (e.g., 10 messages/transaction)
Error Responses
Section titled “Error Responses”403 Forbidden
Section titled “403 Forbidden”{ "error": "INVALID_CERTIFICATE", "message": "Server certificate invalid or domain mismatch", "code": 4001}
503 Service Unavailable
Section titled “503 Service Unavailable”{ "error": "FEDERATION_DISABLED", "message": "This server does not support federation", "code": 5003}
Federation Best Practices
Section titled “Federation Best Practices”Batching Messages
Section titled “Batching Messages”Efficient:
{ "messages": [ // 10 messages for different recipients ]}// Uses: 1 transaction, 10 messages
Inefficient:
{ // 10 separate API calls, each with 1 message // Uses: 10 transactions, 10 messages // Hits transaction limit 10x faster}
Recommendation: Batch up to 50-100 messages per transaction for optimal efficiency.
Token Caching
Section titled “Token Caching”Federation tokens typically expire after 1 hour. Cache them and reuse.
struct FederationClient { tokens: HashMap<String, CachedToken>,}
struct CachedToken { token: String, expires_at: u64,}
impl FederationClient { async fn get_token(&mut self, target_domain: &str) -> Result<String> { if let Some(cached) = self.tokens.get(target_domain) { let now = current_timestamp(); // Refresh if expiring in < 5 minutes if cached.expires_at > now && (cached.expires_at - now) > 300 { return Ok(cached.token.clone()) } }
// Authenticate and cache new token let response = self.authenticate(target_domain).await?; self.tokens.insert(target_domain.to_string(), CachedToken { token: response.federation_token.clone(), expires_at: response.expires_at, });
Ok(response.federation_token) }}
Server Discovery
Section titled “Server Discovery”Before federating with a server, discover its capabilities:
async fn discover_server(domain: &str) -> Result<ServerCapabilities> { let url = format!("https://{domain}/.well-known/cryptid"); let response = reqwest::get(&url).await?; let caps: ServerCapabilities = response.json().await?;
if !caps.federation_enabled { return Err(anyhow!("Federation not supported")); }
Ok(caps)}
See Server Discovery for details on the .well-known/cryptid
endpoint.
Retry Strategy
Section titled “Retry Strategy”Federation requests can fail due to network issues or temporary server unavailability:
async fn deliver_with_retry( target_domain: &str, messages: Vec<Message>, max_retries: u32,) -> Result<FederationResponse> { let mut retry_count = 0;
loop { match deliver_messages(target_domain, &messages).await { Ok(response) => return Ok(response), Err(e) if retry_count < max_retries => { retry_count += 1;
// Exponential backoff: 1s, 2s, 4s, 8s, ... let delay = 2u64.pow(retry_count - 1); tokio::time::sleep(Duration::from_secs(delay)).await; } Err(e) => return Err(e), } }}
Important Notes
Section titled “Important Notes”Transaction IDs
Section titled “Transaction IDs”Transaction IDs prevent duplicate message delivery if federation requests are retried:
-
Must be unique per federation request
-
Recommended format: UUIDv4
-
Receiving server may cache transaction IDs for ~1 hour to detect duplicates
-
Retrying the same transaction ID returns the original response (idempotent)
Message Privacy
Section titled “Message Privacy”Federation maintains Cryptid’s privacy principles:
Servers relay but never learn:
-
Message content (MLS encrypted end-to-end)
-
Sender identity (not included in federation request)
-
Social relationships (no group membership lists)
-
User metadata (no profiles, no presence information)
What servers DO know:
-
Recipient device address (required for delivery routing)
-
Origin server domain (e.g., alice.example.com, but not the specific device)
-
Which domains exchange messages (network metadata)
-
Message delivery timestamps (for routing only)
-
Approximate message volume between servers
This metadata is minimal and inherent to any federated system.
Security Considerations
Section titled “Security Considerations”Certificate Validation
Section titled “Certificate Validation”Production implementations must:
-
Verify server certificate is signed by trusted CA
-
Check certificate matches claimed domain (DNS validation)
-
Validate certificate has not expired or been revoked
-
Use TLS 1.3+ for all federation connections
Denial of Service Protection
Section titled “Denial of Service Protection”-
Rate limiting prevents spam from malicious servers
-
Transaction ID deduplication prevents replay attacks
-
Token expiration limits impact of stolen credentials
Trust Model
Section titled “Trust Model”Servers trust each other to:
-
Not inject fake messages (prevented by sender signatures)
-
Not drop messages maliciously (no guaranteed delivery in Cryptid)
-
Follow protocol honestly (Byzantine fault tolerance not guaranteed)