Layr8.Mediation (layr8 v0.3.3)

Copy Markdown View Source

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:

  1. enroll/3mediate-request, then recipient-update adding its own DID;
  2. declare/3 — tells its node to route offline messages to the mediator;
  3. pickup/3 — pulls everything queued, re-injects it, acknowledges;
  4. live/4 — turns live delivery on (unless mediator_live: false), so later forwards are pushed as delivery messages, 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

enroll/3declare/3pickup/3live/4 (when opts[:live], default true), logging and stopping at the first failure. Never raises.

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

client()

@type client() :: pid()

Functions

bootstrap(client, mediator, opts \\ [])

@spec bootstrap(client(), String.t(), keyword()) :: :ok | {:error, {atom(), term()}}

enroll/3declare/3pickup/3live/4 (when opts[:live], default true), logging and stopping at the first failure. Never raises.

collect(client, mediator, attachments, opts \\ [])

@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}.

declare(client, mediator, opts \\ [])

@spec declare(client(), String.t(), keyword()) :: :ok | {:error, term()}

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.

delivery_type()

@spec delivery_type() :: String.t()

The message type the client handles for live pushes.

enroll(client, mediator, opts \\ [])

@spec enroll(client(), String.t(), keyword()) :: {:ok, map()} | {:error, term()}

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).

live(client, mediator, flag, opts \\ [])

@spec live(client(), String.t(), boolean(), keyword()) ::
  {:ok, map()} | {:error, term()}

Turns live delivery on or off. Returns the mediator's status body.

pickup(client, mediator, opts \\ [])

@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.

protocol_bases()

@spec protocol_bases() :: [String.t()]

Protocol bases a mediated client subscribes to.

reinject(client, attachments, opts \\ [])

@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.

status(client, mediator, opts \\ [])

@spec status(client(), String.t(), keyword()) :: {:ok, map()} | {:error, term()}

Asks the mediator how many messages are waiting.

undeclare(client, opts \\ [])

@spec undeclare(
  client(),
  keyword()
) :: :ok | {:error, term()}

Removes this agent's mediator declaration on its node.