Clients run in the calling BEAM at runtime.

One-shot calls

A one-shot call opens a socket, sends one request, waits for one reply, and closes the socket.

{:ok, status} = SafeRPC.call("/run/my-app/rpc.sock", {MyApp.AdminRPC, :status})

Pass a payload and metadata when needed:

SafeRPC.call(socket, {MyApp.AdminRPC, :lookup}, %{id: "123"}, meta: %{trace_id: "abc"})

Persistent clients

Use a persistent client process when making multiple calls:

{:ok, client} = SafeRPC.Client.start_link(socket: "/run/my-app/rpc.sock")

{:ok, status} = SafeRPC.call(client, {MyApp.AdminRPC, :status})
{:ok, item} = SafeRPC.call(client, {MyApp.AdminRPC, :lookup}, %{id: "123"})

Casts

Casts send a request and return the server acknowledgement:

{:ok, :noreply} = SafeRPC.cast(client, {MyApp.AdminRPC, :refresh}, %{scope: :all})

Async requests

task = SafeRPC.async(client, {MyApp.AdminRPC, :slow_report}, %{range: "24h"})
{:ok, report} = SafeRPC.await(task, 10_000)

Use yield/2 and cancel/1 for non-blocking waits and cancellation.

Atom preparation

If replies can contain atoms not yet present in the client VM, prepare the client before the first atom-rich call:

:ok = SafeRPC.prepare(client, max_atoms: 1_000, max_atom_length: 128)

Preparation asks the server for its compile-time vocabulary as strings, validates it on the client, and interns accepted atoms so later replies can be decoded with safe ETF.

See Atom vocabularies and safe ETF.