decentrl/docs
Mediators

How Mediators Work

Mediators are encryption-blind relay servers that route messages and store ciphertext through two communication channels.

Formal spec: DCTRL-0004 — Mediator Protocol defines the HTTP API, command wire format, WebSocket protocol, and all error codes.

Mediators are the infrastructure layer of Decentrl. They're lightweight servers that do exactly two things: route encrypted messages and store encrypted data. They never see plaintext.

What a Mediator Sees

When Alice sends Bob a message through a mediator, the mediator sees:

  • Sender DID — did:decentrl:z6MkpT... (Alice's public identity)
  • Recipient DID — did:decentrl:z6MkqR... (Bob's public identity)
  • Timestamp — when the command was sent
  • Encrypted payload — opaque ciphertext
  • Encrypted tags — opaque blobs for indexing (it can tell when two events share a tag, not what the tag is)
  • Dedupe keys — one opaque blob per stored event, so it can tell when a save repeats an event you already have (not which event)
  • Sizes — how large each command and event is
  • Contract terms — the DIDs, ephemeral public keys, timestamps, and signatures of every contract it verifies and enforces
  • Contract and epoch per event — the contract_id and epoch each private payload carries in the clear

Together this tells the mediator who talks to whom and when — the social graph. The mediator does not see: event content, conversation topics, read receipts, root secrets, epoch keys, storage keys, or tag plaintext.

Two Communication Channels

The protocol defines two channels that serve distinct purposes:

DIRECT_AUTHENTICATED

Used for identity-to-mediator administrative operations. The sender signs the command; the mediator verifies the signature against the sender's DID document and checks for an active registration contract.

CommandPurpose
REQUEST_COMMUNICATION_CONTRACTSend a contract request (for registration or forwarding)
COMMUNICATION_CONTRACT_RESPONSEDeliver signed contract response
QUERY_PENDING_COMMUNICATION_CONTRACT_REQUESTSList inbound pending contract requests
ACKNOWLEDGE_PENDING_COMMUNICATION_CONTRACT_REQUESTSDelete processed requests
SAVE_COMMUNICATION_CONTRACTStore a completed contract
QUERY_COMMUNICATION_CONTRACTSList your contracts (at most one per counterpart)
REVOKE_COMMUNICATION_CONTRACTWithdraw consent for one counterpart, silently
QUERY_PENDING_EVENTSList unacknowledged events from others
ACKNOWLEDGE_PENDING_EVENTSDelete processed events
SAVE_EVENTSStore encrypted events, one per dedupe_key (a repeat is DUPLICATE)
QUERY_EVENTSQuery stored events with tag filters
UPDATE_EVENT_TAGSReplace tags on stored events

TWO_WAY_PRIVATE

Used for encrypted event delivery between identities. When Alice sends Bob an event:

  1. The mediator verifies Alice's signature on the command
  2. Checks that Bob is registered with this mediator
  3. Checks that Bob holds a communication contract with Alice (contracts don't expire; one Bob revoked is gone, and Alice gets the same COMMUNICATION_CONTRACT_NOT_FOUND a stranger would)
  4. Stores the payload as a pending event for Bob

The payload is a small JSON wrapper: the contract ID and epoch in the clear, and the encrypted event. The mediator stores it without being able to read the event, or its event ID, which is signed inside the encryption. Once Bob acknowledges the event, the mediator deletes it, so delivered ciphertext doesn't linger on the server.

Retention

A mediator keeps only what it still needs. A cleanup job runs every 10 minutes and deletes expired nonces, and pending events and contract requests older than 30 days (never acknowledged). Contracts are never cleaned up: a contract goes away only when a newer one for the same pair replaces it, or its owner revokes it.

Authorization Model

Every command to a mediator must be signed. The mediator resolves the sender's DID, extracts their public signing key, and verifies the signature. Commands are also rate-limited per authenticated sender DID (by default 600 commands per minute). Additionally:

  • DIRECT_AUTHENTICATED commands require an active registration contract (client-to-mediator contract)
  • TWO_WAY_PRIVATE commands require both an active registration contract and a valid bilateral communication contract between sender and recipient

No unsigned command is ever processed. No command without proper contracts is ever accepted.