Store-and-forward for an agent that is not always connected, against a
DIDComm mediator (layr8/mediator): coordinate-mediation/3.0 enrolment,
messagepickup/3.0 collection, and the cloud-node's mediator declaration.
The cloud-node deposits any message that arrives while this agent's plugin
is offline with the mediator the agent has declared on its node
(PUT /api/v1/dids/:did/mediator, cloud-node ADR 0005). What the mediator
holds is the original ciphertext, so collecting it means posting each
attachment back to this agent's own node at /didcomm, where it is
unpacked, sender-bound and authorized exactly like a first arrival, and
then delivered to this client's handlers. Nothing here ever decrypts.
Zero-config use
Give the client a mediator and it does the rest on every (re)connect:
Layr8.Client.start_link(%{mediator: "did:web:node.example:agents:mediator"})(or LAYR8_MEDIATOR_DID). The client then, in the background:
enroll/3—mediate-request, thenrecipient-updateadding its own DID;declare/3— tells its node to route offline messages to the mediator;pickup/3— pulls everything queued, re-injects it, acknowledges;live/4— turns live delivery on (unlessmediator_live: false), so later forwards are pushed asdeliverymessages, which the client re-injects and acknowledges the same way.
Every step is also callable by hand, and none of them raises.
Grants
The agent needs protocol grants on the mediator resource for
coordinate-mediation/3.0 and messagepickup/3.0 (issued in the portal).
Forwards to the mediator, including the ones the node builds, are open
deposits and need no grant.
Summary
Functions
Re-injects attachments through this agent's node and acknowledges the ones
that went in. Returns {:ok, count, :complete | :partial}.
Declares mediator as this agent's mediator on its own cloud-node, so the
node deposits messages there while the agent is offline and the DID
document advertises it as routingKeys.
The message type the client handles for live pushes.
Requests mediation and registers this agent's DID (plus opts[:recipients])
with mediator.
Turns live delivery on or off. Returns the mediator's status body.
Drains the mediator: repeats delivery-request (opts[:limit], default 10)
until a status reply, re-injecting every attachment through this agent's
node and acknowledging what was re-injected.
Protocol bases a mediated client subscribes to.
Posts each attachment's ciphertext (data.base64, base64url) to this
agent's node at /didcomm. Returns {re-injected ids, failed ids}.
Attachments without ciphertext are counted as failed and never acknowledged.
Asks the mediator how many messages are waiting.
Removes this agent's mediator declaration on its node.
Types
@type client() :: pid()
Functions
enroll/3 → declare/3 → pickup/3 → live/4 (when opts[:live],
default true), logging and stopping at the first failure. Never raises.
@spec collect(client(), String.t(), [Layr8.Attachment.t()], keyword()) :: {:ok, non_neg_integer(), :complete | :partial}
Re-injects attachments through this agent's node and acknowledges the ones
that went in. Returns {:ok, count, :complete | :partial}.
Declares mediator as this agent's mediator on its own cloud-node, so the
node deposits messages there while the agent is offline and the DID
document advertises it as routingKeys.
@spec delivery_type() :: String.t()
The message type the client handles for live pushes.
Requests mediation and registers this agent's DID (plus opts[:recipients])
with mediator.
Returns {:ok, %{routing_did: [..], updated: [..]}}. Idempotent: a second
call re-receives the grant and gets no_change on the registrations.
{:error, :mediate_denied} when the mediator refuses;
{:error, {:recipient_update, entries}} when the agent's own DID could not
be registered (someone else's routing key, or the mediator's keylist policy).
Turns live delivery on or off. Returns the mediator's status body.
@spec pickup(client(), String.t(), keyword()) :: {:ok, non_neg_integer()} | {:error, term()}
Drains the mediator: repeats delivery-request (opts[:limit], default 10)
until a status reply, re-injecting every attachment through this agent's
node and acknowledging what was re-injected.
Returns {:ok, count} of messages re-injected. An attachment whose
re-injection failed is left queued (not acknowledged) and the loop stops,
so nothing is lost and nothing is retried forever.
@spec protocol_bases() :: [String.t()]
Protocol bases a mediated client subscribes to.
@spec reinject(client(), [Layr8.Attachment.t()], keyword()) :: {[String.t()], [String.t()]}
Posts each attachment's ciphertext (data.base64, base64url) to this
agent's node at /didcomm. Returns {re-injected ids, failed ids}.
Attachments without ciphertext are counted as failed and never acknowledged.
Asks the mediator how many messages are waiting.
Removes this agent's mediator declaration on its node.