Search documentation of macula

# macula_dist_relay_client (macula v4.4.9)

View Source

Client for the Macula dist relay (`macula-io/macula-dist-relay`).

Maintains a persistent QUIC connection to a dedicated dist relay and exposes a simple API for establishing point-to-point tunnels between Erlang nodes. Each tunnel is backed by a raw QUIC stream — no framing, no pub/sub, no application-level encryption.

This replaces the pub/sub bridge approach in `macula_dist_relay` (the SDK's previous dist-over-mesh implementation) which forced dist bytes through the station's MessagePack/handler pipeline.

### Protocol

Control frames on stream 0 (length-prefixed MessagePack):

         node → relay    identify{node_name}
         relay → node    identified{status}
         node → relay    tunnel_request{target}
         relay → node    tunnel_ok{tunnel_id} | tunnel_error{reason}
         relay → node    tunnel_notify{tunnel_id, source}
         node → relay    tunnel_close{tunnel_id}

Tunnel data streams (stream 1+) carry the raw dist wire bytes after a 32-byte tunnel_id prefix (written by the relay on stream open) that lets the client match new_stream events to pending tunnels.

### Lifecycle

1. `start_link(RelayUrl, NodeName)` connects + sends identify 2. Await identified reply 3. `request_tunnel(TargetNode)` blocks until tunnel_ok + tunnel stream arrive; returns `{ok, ConnRef, StreamRef}` for use as the dist Socket in `macula_dist` 4. Incoming tunnels: tunnel_notify arrives on control, then a new_stream event; client reads 32-byte prefix, matches to the notified tunnel, hands the stream to `net_kernel` via the standard `{accept, _, Socket, Family, Driver}` protocol.

### Not yet implemented (Phase 2 MVP)

- net_kernel handoff for incoming tunnels (needs `macula_dist` integration on the accept side) - automatic reconnect on relay disconnect - multiple relay failover

# Summary

## Functions

close_tunnel(Client, TunnelId)
handle_call(Msg, From, State)
handle_cast(Msg, State)
handle_info(Info, State)
init(_)
request_tunnel(Client, TargetNode)

Request a tunnel to TargetNode. Blocks until the tunnel stream is ready or the request fails/times out. Returns a {ConnRef, StreamRef} tuple suitable for use as the dist Socket in `macula_dist`.

set_kernel(Client, Kernel)

Tell the client which process to deliver `{accept, ...}` messages to when inbound tunnels arrive. Called by `macula_dist:accept/1` with the net_kernel pid (`self()` at that call site).

start_link(RelayUrl, NodeName)
start_link(RelayUrl, NodeName, Opts)

Start the client with a locally-registered name so macula_dist can find it without being passed the pid. Only one dist_relay_client per node makes sense — a node connects to exactly one dist relay for its cluster traffic.

status(Client)
terminate(Reason, State)
whereis_client()

Locate the registered client, if any.

# Functions

# close_tunnel(Client, TunnelId)

    -spec close_tunnel(pid(), binary()) -> ok.

# handle_call(Msg, From, State)

# handle_cast(Msg, State)

# handle_info(Info, State)

# init(_)

# request_tunnel(Client, TargetNode)

    -spec request_tunnel(pid(), binary()) -> {ok, reference(), reference()} | {error, term()}.

Request a tunnel to TargetNode. Blocks until the tunnel stream is ready or the request fails/times out. Returns a {ConnRef, StreamRef} tuple suitable for use as the dist Socket in `macula_dist`.

# set_kernel(Client, Kernel)

    -spec set_kernel(pid(), pid()) -> ok.

Tell the client which process to deliver `{accept, ...}` messages to when inbound tunnels arrive. Called by `macula_dist:accept/1` with the net_kernel pid (`self()` at that call site).

# start_link(RelayUrl, NodeName)

    -spec start_link(binary() | string(), binary()) -> {ok, pid()} | {error, term()}.

# start_link(RelayUrl, NodeName, Opts)

    -spec start_link(binary() | string(), binary(), map()) -> {ok, pid()} | {error, term()}.

Start the client with a locally-registered name so macula_dist can find it without being passed the pid. Only one dist_relay_client per node makes sense — a node connects to exactly one dist relay for its cluster traffic.

# status(Client)

    -spec status(pid()) -> map().

# terminate(Reason, State)

# whereis_client()

    -spec whereis_client() -> pid() | undefined.

Locate the registered client, if any.