How the mediator works.
A sender encrypts a message to the recipient, then wraps that ciphertext in a forward envelope addressed to the mediator. The mediator unwraps only the outer envelope — the inner ciphertext, still sealed to the recipient, is never exposed. Below is the full sequence: CoordinateMediation 2.0 to set up routing, Routing to forward, Pickup 3.0 to collect — chain-anchored and credential-gated where Solidus diverges from a stock mediator.
Every hop, named.
From mediate-request to purge — the exact DIDComm v2 messages, with the chain-anchored resolution and credential gate marked where they apply.
mediate-request
CoordinateMediation 2.0
The recipient's own agent asks the mediator to coordinate delivery on its behalf.
mediate-grant
CoordinateMediation 2.0
Mediator grants mediation and returns a routing key the recipient publishes in its did:solidus document.
keylist-update
CoordinateMediation 2.0
Recipient registers the DID keys it wants routed through this mediator.
forward
Routing
Sender resolves the recipient's did:solidus document on-chain to find the routing key and mediator endpoint, wraps its JWE-encrypted message in a forward envelope, and sends it to the mediator.
delivery-request · live-delivery
Pickup 3.0
Recipient asks what is waiting with delivery-request — or, if a live-delivery connection is already open, the mediator pushes new messages automatically as they arrive.
messages-received
Pickup 3.0
Recipient acknowledges receipt; the mediator marks those messages delivered.
purge
Retention policy · 14d, purge-on-ack
Acknowledged ciphertext is purged on this schedule — the relay does not keep messages it no longer needs to hold.
Three protocols, nothing proprietary.
CoordinateMediation 2.0, Pickup Protocol 3.0, and Routing are the DIDComm v2 protocols in play — the relay implements the standard, not a fork of it.
| Protocol | Messages it defines | What the relay does |
|---|---|---|
| CoordinateMediation 2.0 | mediate-requestmediate-grantkeylist-updatekeylist-query | Grants or denies mediation, issues a routing key, and maintains the keylist of DIDs the relay will forward messages to. |
| Pickup Protocol 3.0 | status-requestdelivery-requestmessages-receivedlive-delivery | Holds messages for an offline recipient and delivers them on request, or streams them immediately over an open live-delivery connection. |
| Routing | forward | Unwraps only the outer forward envelope to find the next hop. The inner ciphertext, still encrypted to the recipient, is never exposed. |
Routing keys resolve on-chain, block-cited.
A stock mediator publishes its routing key and endpoint in a DID document hosted off-chain — a did:web file, a registry, a database row an operator controls. Change it, and every sender silently starts trusting a new destination.
A did:solidus document resolves from the chain. The routing key and service endpoint a sender reads back are the ones written at a specific, citable block — not whatever an operator's server happens to return today.
That is what the chain-anchor badge on every resolved route in the operator console means: this route was read from the chain, not a cache or a registry the mediator itself controls.
1{2 "id": "did:solidus:z6MkrQT4pWnvKb8sVdE2mHhXNfJcyLu3gA9tCiDoZxSe1RmF",3 "service": [4 {5 "id": "#didcomm-mediator",6 "type": "DIDCommMessaging",7 "serviceEndpoint": {8 "uri": "https://relay.solidus.network/didcomm",9 "routingKeys": [10 "z6LSfj9q…QtU0S1"11 ]12 }13 }14 ],15 "_resolution": {16 "source": "on-chain (testnet)",17 "block": 482739118 }19}
Delivery can require a verified credential.
A gate policy can require the recipient to hold a specific verifiable credential before the relay grants mediate-request or accepts a forward on its behalf. The check runs against verify.solidus.network — the same credential stack, not a bolted-on integration.
The example policy below only mediates for recipients holding a SolidusKYCCredential, disclosed selectively with BBS+ — the relay confirms the credential exists and is unrevoked without seeing fields the policy doesn't ask for.
No open-source mediator has a credential layer beneath it — a gate policy there is unconditional, protocol-level, or absent.
1{2 "id": "pol_kyc",3 "name": "KYC-verified recipients",4 "credentialType": "SolidusKYCCredential",5 "issuerDid": "did:solidus:z6MkverifyIssuer4tRq8wYbGd2kEuC9vHsNa1pLoZi5xVeW",6 "disclosure": "selective (BBS+)",7 "appliesTo": "mediation",8 "enabled": true9}
An issuer reaches an offline wallet.
One path through the sequence above, end to end: an issuer sending a credential offer to a wallet that's currently offline.
The wallet requests mediation
An offline-first wallet has no public IP of its own — it registers with the mediator once, in advance.
mediate-requestmediate-grantkeylist-updateThe wallet is granted mediation and registers the DID keys it wants routed. From now on, its did:solidus document points senders at this mediator.
The issuer resolves the wallet's DID on-chain
Before sending anything, the issuer looks up the wallet's did:solidus document.
The document resolves at blk #4,827,391 — the routing key and mediator endpoint it returns are exactly what's written on-chain, not a cached copy.
The issuer forwards the encrypted credential offer
The offer is a JWE addressed to the wallet, wrapped in a forward envelope addressed to the mediator.
forwardJWEThe mediator holds the ciphertext — it cannot read the credential offer inside. The wallet is offline right now, so nothing more happens yet.
The wallet comes online and picks it up
Later, the wallet opens a live-delivery connection — or sends a delivery-request if it isn't currently connected.
delivery-request·live-deliveryThe mediator hands over the queued offer. The wallet decrypts it locally — the mediator was never able to.
The wallet confirms receipt
messages-received tells the mediator the offer arrived; it's purged per the retention policy (14 days, purge-on-ack).
messages-receivedNothing about this exchange is stored beyond the retention window — the mediator's job was to hold ciphertext until the wallet was ready, then let it go.
Same sequence for any two DIDComm v2 agents — a credential offer, a presentation request, or an ordinary application message.
Point a mediator URL. Not a Kubernetes chart.
Planned launch pricing: $0 for the first 1,000 messages a day, no enterprise sales call. Testnet, pre-launch — the console runs on sample data while the mediator comes online.