Server Discovery API
Server discovery enables clients and servers to automatically discover capabilities, configuration, and operational status of Cryptid servers. This follows the .well-known
standard (RFC 5785) used by many federated protocols.
Base URL
Section titled “Base URL”All Cryptid API endpoints are relative to the server’s base URL:
https://chat.example.com
URL Structure:
-
Protocol:
https://
(TLS required, no HTTP) -
Domain: Server’s fully qualified domain name
-
No path prefix (API endpoints start at root)
Example URLs:
-
Device announcement:
https://chat.example.com/v1/announce
-
WebSocket stream:
wss://chat.example.com/v1/stream
-
Server discovery:
https://chat.example.com/.well-known/cryptid
Server Capabilities
Section titled “Server Capabilities”Discover server capabilities, configuration, and federation parameters. This endpoint is publicly accessible (no authentication required) and is used by:
-
Clients choosing which server to connect to
-
Servers discovering federation capabilities of remote servers
-
Monitoring tools checking server availability
Standard: Follows RFC 5785 (.well-known
URI) convention used by protocols like Matrix, Mastodon, and WebFinger.
Endpoint
Section titled “Endpoint”GET /.well-known/cryptid
Request
Section titled “Request”GET /.well-known/cryptid HTTP/1.1Host: server.example.com
Response (200 OK)
Section titled “Response (200 OK)”{ "protocol_version": "1.0", "server_name": "server.example.com", "federation_enabled": true, "supported_features": ["mls_messaging"], "rate_limits": { "federated_messages_per_minute": 1000, "announcements_per_hour": 24 }, "message_limits": { "max_message_size": 10485760, "max_retention_days": 30 }, "certificate_fingerprint": "sha256:a1b2c3d4e5f6172839..."}
Response Fields:
-
protocol_version
: Cryptid protocol version this server implements (semantic versioning)- Clients should check compatibility (e.g., reject servers with incompatible major versions)
-
server_name
: Server’s canonical domain name (must match DNS)- Used for device address validation (e.g.,
device_id@server_name
)
- Used for device address validation (e.g.,
-
federation_enabled
: Whether this server accepts federated messages from other serverstrue
: Server accepts federation requests at/federation/v1/*
false
: Server is isolated (no cross-server messaging)
-
supported_features
: Array of optional features this server implementsmls_messaging
: MLS-encrypted messaging (required)websocket_streaming
: Real-time WebSocket deliveryoffline_queuing
: Stores messages for offline devices- Future:
relay_network
,multi_device_sync
, etc.
-
rate_limits
: Server’s rate limiting policiesfederated_messages_per_minute
: Max messages/minute in federation requestsannouncements_per_hour
: Max device announcements per hour per address
-
message_limits
: Message storage and size constraintsmax_message_size
: Maximum message size in bytes (default: 10MB)max_retention_days
: How long unacknowledged messages are kept (default: 30 days)
-
certificate_fingerprint
: SHA-256 fingerprint of server’s TLS certificate- Used for certificate pinning and trust-on-first-use (TOFU)
- Format:
sha256: prefix + lowercase hex (64 characters)
Use Cases
Section titled “Use Cases”Client Server Selection
Section titled “Client Server Selection”async fn choose_server(candidates: Vec<String>) -> Result<String> { for domain in candidates { let caps = discover_server(&domain).await?;
// Check requirements if caps.protocol_version.starts_with("1.") && caps.supported_features.contains(&"websocket_streaming".to_string()) { return Ok(domain); } }
Err(anyhow!("No compatible server found"))}
Federation Compatibility Check
Section titled “Federation Compatibility Check”async fn can_federate_with(domain: &str) -> Result<bool> { let caps = discover_server(domain).await?;
if !caps.federation_enabled { return Ok(false); }
// Check protocol version compatibility if caps.protocol_version.starts_with("1.") { return Ok(true); }
Ok(false)}
Certificate Pinning
Section titled “Certificate Pinning”async fn verify_server_certificate(domain: &str, observed_fingerprint: &str) -> Result<()> { let caps = discover_server(domain).await?;
if caps.certificate_fingerprint != observed_fingerprint { return Err(anyhow!("Certificate fingerprint mismatch! Possible MITM attack!")); }
Ok(())}
Error Responses
Section titled “Error Responses”404 Not Found
Section titled “404 Not Found”Server does not implement Cryptid or has disabled discovery.
{ "error": "ENDPOINT_NOT_FOUND", "message": "Server does not support Cryptid protocol"}
503 Service Unavailable
Section titled “503 Service Unavailable”Server is operational but is temporarily not accepting new connections.
{ "error": "SERVER_UNAVAILABLE", "message": "Server temporarily unavailable"}
Health Check
Section titled “Health Check”Check server operational status and uptime. Used by monitoring tools, load balancers, and administrators.
Use Cases:
-
Load balancer health checks
-
Monitoring/alerting systems
-
Admin dashboards
-
Client fallback logic (try different server if unhealthy)
Endpoint
Section titled “Endpoint”GET /v1/health
Request
Section titled “Request”GET /v1/health HTTP/1.1
Response (200 OK)
Section titled “Response (200 OK)”{ "status": "healthy", "timestamp": 1759000000, "version": "1.0.0", "uptime": 2592000}
Response Fields:
-
status
: Server health statushealthy
: Server fully operationaldegraded
: Server operational but experiencing issues (e.g., high load, partial service)unhealthy
: Server experiencing critical issues (still responding but unreliable)
-
timestamp
: Current server Unix timestamp- Used for client clock synchronization validation
-
version
: Server software version (semantic versioning)- Format:
major.minor.patch
(e.g.,1.2.3
) - Useful for debugging and compatibility tracking
- Format:
-
uptime
: Server uptime in seconds since last restart- Example:
2592000
= 30 days
- Example:
Response Status Codes
Section titled “Response Status Codes”200 OK: Server is healthy and operational
503 Service Unavailable: Server is unhealthy or under maintenance
{ "status": "unhealthy", "timestamp": 1759000000, "version": "1.0.0", "uptime": 3600, "details": "Database connection failed"}
Load balancers should remove servers from rotation upon a 503.
Important Notes
Section titled “Important Notes”Caching Server Capabilities
Section titled “Caching Server Capabilities”Server capabilities change infrequently and should be cached.
struct ServerCapabilitiesCache { cache: HashMap<String, (ServerCapabilities, Instant)>, ttl: Duration,}
impl ServerCapabilitiesCache { async fn get(&mut self, domain: &str) -> Result<ServerCapabilities> { if let Some((caps, cached_at)) = self.cache.get(domain) { if cached_at.elapsed() < self.ttl { return Ok(caps.clone()); } }
// Cache miss or expired, fetch new let caps = discover_server(domain).await?; self.cache.insert(domain.to_string(), (caps.clone(), Instant::now()));
Ok(caps) }}
Recommended TTL: 1h for server capabilities, 30s for health checks.
DNS and HTTPS Requirements
Section titled “DNS and HTTPS Requirements”Cryptid servers MUST:
-
Have valid DNS records pointing to their domain
-
Use TLS 1.3+ with valid certificates (no self-signed in production)
-
Serve
.well-known/cryptid
over HTTPS (no HTTP fallback)
This prevents:
-
DNS spoofing attacks
-
Man-in-the-middle attacks
-
Impersonation of legitimate servers
Certificate Fingerprint Validation
Section titled “Certificate Fingerprint Validation”The certificate_fingerprint
field enables Trust on First Use (TOFU):
-
Client connects to server for first time
-
Client records certificate fingerprint from
.well-known/cryptid
-
On subsequent connections, client verifies fingerprint hasn’t changed
-
If changed, client alerts user (possible MITM or legitimate rotation)
Calculating fingerprint:
use sha2::{Sha256, Digest};
fn calculate_fingerprint(cert_der: &[u8]) -> String { let hash = Sha256::digest(cert_der); format!("sha256:{}", hex::encode(hash))}
Protocol Version Compatibility
Section titled “Protocol Version Compatibility”Cryptid uses semantic versioning for protocol_version
:
-
Major version changes break compatibility (e.g.,
1.x
->2.x
) -
Minor version adds features in a backwards compatible way (e.g.,
1.0
->1.1
) -
Patch version fixes bugs (e.g.,
1.0.0
->1.0.1
)
Compatibility rules:
-
Clients MUST check major version matches
-
Clients MAY check minor version for required features
-
Clients SHOULD ignore patch version for compatibility decisions
Rate Limit Discovery
Section titled “Rate Limit Discovery”Clients SHOULD respect advertised rate limits from server capabilities.