Skip to content

API Overview

Cryptid defines a minimal server API for federated messaging built on Messaging Layer Security and device-centric cryptographic identity. Servers route opaque envelopes, hold short-lived state, and verify three narrow classes of signature. Everything else happens on devices.

The client-to-server API and the server-to-server federation protocol. It does not cover client behavior. Contact management, group state, trust decisions, and every cryptographic operation that produces or consumes message content are specified in the Reference pages and performed on devices.

  • Minimal server state. Servers store mailbox registrations, queued envelopes, KeyPackage ciphertext, and blobs. All of it expires. There are no accounts, no contact lists, and no group state.

  • Narrow verification. Servers verify exactly 3 things: PrivacyPass tokens, mailbox ownership signatures, and federation origin signatures. They MUST NOT verify MLS state, message signatures, or group membership, and they cannot decrypt anything they carry.

  • Automatic expiry. Every record has a lifetime. Nothing persists because nothing is meant to.

  • Client-side authority. Trust, membership, and identity are decided on devices. A server compromise only yields routing metadata and ciphertext.

Operations are defined transport-neutrally. Each has a name, a typed input and output, an authorization requirement, and a set of errors it may return. Bindings map that operation onto concrete transport.

This separation is deliberate. HTTP is the only binding specified today, but the delivery path is explicitly transport-agnostic, and an operation defined in terms of HTTP verbs and paths cannot move.

  • An operation’s name is its stable identifier and MUST be the same across every binding
  • A binding MUST NOT add, remove, or reinterpret an operation’s inputs, outputs, or errors
  • A binding MAY carry authorization differently, provided the requirement itself is unchanged

Every operation on the following pages uses this general shape:

## SubmitEnvelope
One sentence on what it does.
- **Input:** `CryptidEnvelope`
- **Output:** `SubmitReceipt { message_id, accepted_at }`
- **Auth:** PrivacyPass
- **Idempotent:** No
- **Errors:** `TOKEN_INVALID`, `TOKEN_REPLAYED`, `MAILBOX_UNKNOWN`, `PAYLOAD_TOO_LARGE`
### Requirements
- Servers MUST NOT inspect `encrypted_blob`
### HTTP Binding
```http
POST /api/v1/envelopes
```
Field Meaning
Input The type the caller supplies, serialized per Wire Format. None if the operation takes no body
Output The type returned on success. None if the operation returns no body
Auth Which authorization mechanism applies, and its scope. None for unauthenticated operations
Idempotent Whether repeating the request with identical input is safe. By key names the field that makes it so
Errors Codes this operation may return, beyond the universal ones. Every code is registered in Errors

Cryptid has no accounts and no sessions in the usual sense. Authorization is per-operation and comes in six forms, which are not interchangeable.

Mechanism Proves Used By
PrivacyPass Token A budget was spent, nothing about who spent it Envelope submission, mailbox registration, KeyPackage upload, blob upload
Mailbox Ownership Possession of a mailbox secret, via challenge-response Envelope retrieval and subscription
Capability Handle Possession of an unguessable identifier Blob fetch, InfoPackage fetch, KeyPackage fetch
Revocation Token Authority to withdraw something previously uploaded Blob deletion, InfoPackage revocation
Device Authentication A specific Device Identity Announcement and token issuance
Origin Signature A request came from a named peer server Federation

Only one of these identifies a device, and it exists solely so issuance budgets can be enforced where identity is knowable. Every other mechanism is deliberately anonymous.

Three resources share the capability-handle pattern, for the same reason: a fetch may cross a server boundary where your tokens do not redeem, so possession of the identifier is the authorization. They differ in how the identifier reaches you - inside MLS for blobs, out of band for InfoPackages, derived locally for KeyPackages - and in what a read consumes.

  • Each metered action MUST spend exactly one token
  • Servers MUST reject a token whose challenge_digest does not match the server’s own TokenChallenge
  • Servers MUST enforce budgets at issuance and MUST NOT rate limit redemption
  • Tokens are fungible. A token is spendable on any metered action.

Retrieval, blob fetch, and InfoPackage fetch spend nothing: they are authorized by possession, and they create no server work.

Details are in Transports; the route scheme is here because it is referenced throughout.

Prefix Audience
/.well-known/cryptid Public discovery, unversioned by convention
/api/v1/ Client to server
/federation/v1/ Server to server
/admin/v1/ Operator
  • Requests and responses carrying protocol types MUST use application/cryptid+tls and the canonical serialization
  • Requests and responses carrying operational metadata MAY use application/json
  • PrivacyPass tokens are carried per RFC 9577 as Authorization: PrivateToken token="..."
  • Retrieval sessions are carried as Authorization: MailboxSession <token>

There is one protocol version, cryptid_version, published at /.well-known/cryptid and negotiated at the federation boundary. Individual operations are not separately versioned, and the v1 in a route prefix denotes the binding’s shape, not the protocol’s.

Any change to a wire construct MUST introduce or bump a feature flag, and a server MUST NOT send a construct its peer has not advertised.

Every operation reports failure through the same envelope and the same registry.

  • Servers MUST NOT emit a code outside the registry
  • Clients MUST treat an unregistered code as INTERNAL_ERROR

See Errors for the envelope, the retry classes, and the full code list.