Main entry point for interacting with the Layr8 platform.
Lifecycle
# 1. Start the client
{:ok, client} = Layr8.Client.start_link(%{
node_url: "wss://node.example.com/plugin_socket/websocket",
api_key: "my-api-key"
})
# 2. Register message handlers BEFORE connect
:ok = Layr8.Client.handle(client, "https://example.com/proto/1.0/request", fn msg ->
{:reply, %Layr8.Message{type: "https://example.com/proto/1.0/response", body: %{text: "pong"}}}
end)
# 3. Connect to the cloud-node
:ok = Layr8.Client.connect(client)
# 4. Send messages
:ok = Layr8.Client.send(client, %Layr8.Message{
type: "https://example.com/proto/1.0/request",
to: ["did:example:bob"],
body: %{text: "ping"}
})
# 5. Request/response
{:ok, response} = Layr8.Client.request(client, %Layr8.Message{
type: "https://example.com/proto/1.0/request",
to: ["did:example:bob"],
body: %{text: "ping"}
})
# 6. Shut down
:ok = Layr8.Client.close(client)Events
Subscribe to lifecycle events by passing callbacks in start_link/1 opts:
:on_disconnect—(reason :: term() -> any()):on_reconnect—(() -> any())
Credentials & Presentations
Credential and presentation operations use the REST API (no WebSocket required):
{:ok, jwt} = Layr8.Client.sign_credential(client, credential, issuer_did: "did:example:alice")
Summary
Functions
Returns a specification to start this module under a supervisor.
Gracefully shuts down the client.
Establishes the WebSocket connection and joins the Phoenix channel.
Returns the agent's DID — either provided in config or assigned by the node on connect.
Retrieves a stored credential by ID.
Registers a handler for a DIDComm message type.
Registers a catch-all handler invoked when no specific handler matches.
Lists stored credentials for a holder. Defaults: holder = did/1.
Sets up MCP (Model Context Protocol) over DIDComm on a protocol base and
returns a binding whose Layr8.Mcp.peer/2 yields a caller.
Forgets the cached Verifiable Grants for did (default: this agent's), so the
next message re-reads them.
Sends a DIDComm message and waits for a correlated response (matched by thread ID).
request/3 without the raises: returns {:ok, Message.t()} or
{:error, reason} — :timeout, :not_connected, or
{:problem_report, code, comment}.
Sends a DIDComm message.
Signs a W3C Verifiable Credential. Defaults: issuer = did/1, format = "compact_jwt".
Signs a W3C Verifiable Presentation. Defaults: holder = did/1, format = "compact_jwt".
Starts the Layr8 Client GenServer.
Stores a signed credential JWT for a holder. Defaults: holder = did/1.
Verifies a signed credential. Defaults: verifier = did/1.
Verifies a signed presentation. Defaults: verifier = did/1.
Types
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec close(pid()) :: :ok
Gracefully shuts down the client.
@spec connect(pid()) :: :ok
Establishes the WebSocket connection and joins the Phoenix channel.
Blocks until the join is acknowledged. Returns :ok on success or raises on error.
Returns the agent's DID — either provided in config or assigned by the node on connect.
Retrieves a stored credential by ID.
@spec handle(pid(), String.t(), Layr8.Handler.handler_fn()) :: :ok
Registers a handler for a DIDComm message type.
Must be called before connect/1. Raises Layr8.AlreadyConnectedError if called after.
Handler Signature
fn msg -> {:reply, %Layr8.Message{...}} | :noreply | :pass | {:error, reason} end
@spec handle_all(pid(), Layr8.Handler.handler_fn()) :: :ok
Registers a catch-all handler invoked when no specific handler matches.
Must be called before connect/1. Raises Layr8.AlreadyConnectedError if called after.
Handler Signature
fn msg -> {:reply, %Layr8.Message{...}} | :noreply | :pass | {:error, reason} end
Lists stored credentials for a holder. Defaults: holder = did/1.
@spec mcp(pid(), String.t()) :: {:ok, Layr8.Mcp.Binding.t()} | {:error, term()}
Sets up MCP (Model Context Protocol) over DIDComm on a protocol base and
returns a binding whose Layr8.Mcp.peer/2 yields a caller.
Must be called before connect/1 (like handle/3): it registers the
protocol subscription the cloud-node needs in order to deliver #{base}/*
replies. Idempotent per base — calling it twice returns a second binding over
the same subscription rather than raising the way a duplicate handle/3
would. Compose freely with your own handle/3 registrations.
See Layr8.Mcp for the call surface.
Forgets the cached Verifiable Grants for did (default: this agent's), so the
next message re-reads them.
The cache TTL is the whole freshness story: a grant minted seconds ago is invisible until it lapses. An agent that has just been TOLD it was granted something — by a request/approve flow, or by a person on the other end of a chat — should not have to wait out a timer it cannot see.
@spec request(pid(), Layr8.Message.t() | map(), keyword()) :: {:ok, Layr8.Message.t()}
Sends a DIDComm message and waits for a correlated response (matched by thread ID).
Returns {:ok, Layr8.Message.t()} or raises Layr8.ProblemReportError / Layr8.NotConnectedError.
Options
:timeout— milliseconds to wait for a reply (default 30s):parent_thread— setpthidfor nested thread correlation
@spec request_result(pid(), Layr8.Message.t() | map(), keyword()) :: {:ok, Layr8.Message.t()} | {:error, term()}
request/3 without the raises: returns {:ok, Message.t()} or
{:error, reason} — :timeout, :not_connected, or
{:problem_report, code, comment}.
Same request path, same options. This exists because a remote call failing is
an ordinary outcome for some callers (Layr8.Mcp routes on it rather than
rescuing), while for others an unanswered request is exceptional — the SDK
should not force either one to convert.
@spec send(pid(), Layr8.Message.t() | map(), keyword()) :: :ok
Sends a DIDComm message.
By default waits for server acknowledgment. Pass fire_and_forget: true to skip.
Returns :ok or raises Layr8.NotConnectedError.
Signs a W3C Verifiable Credential. Defaults: issuer = did/1, format = "compact_jwt".
Signs a W3C Verifiable Presentation. Defaults: holder = did/1, format = "compact_jwt".
@spec start_link(start_opts()) :: GenServer.on_start()
Starts the Layr8 Client GenServer.
Accepts the same keys as Layr8.Config.resolve!/1 plus:
:on_disconnect— called with the disconnect reason:on_reconnect— called after successful reconnection:on_grant_miss— called when a message went out with NO covering Verifiable Grant and the node then denied it, when the covering set had to be capped, or when the grants could not be read at all. SeeLayr8.Wallet: the sender is the only party that knows nothing was attached, and the node's denial names the grant it could not find, which sends people to check a grant that is fine.
Raises Layr8.Error if required config fields are missing.
Stores a signed credential JWT for a holder. Defaults: holder = did/1.
Verifies a signed credential. Defaults: verifier = did/1.
Verifies a signed presentation. Defaults: verifier = did/1.