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.
What This Covers
Section titled “What This Covers”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.
Design Principles
Section titled “Design Principles”-
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 and Bindings
Section titled “Operations and Bindings”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
Reading an Operation Definition
Section titled “Reading an Operation Definition”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
```httpPOST /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 |
Authorization
Section titled “Authorization”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.
One Token Per Action
Section titled “One Token Per Action”- Each metered action MUST spend exactly one token
- Servers MUST reject a token whose
challenge_digestdoes not match the server’s ownTokenChallenge - 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.
HTTP Binding Conventions
Section titled “HTTP Binding Conventions”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+tlsand 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>
Versioning
Section titled “Versioning”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.
Errors
Section titled “Errors”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.