Raxol.Agent.Tunnel (Raxol Agent v2.6.0)

Copy Markdown View Source

One endpoint of the reverse co-driving tunnel.

The co-driving model from omnigent: a host dials OUT to a server over a single link (the server never dials in), and many logical channels are multiplexed over that one link. A teammate's browser "attaches" by opening a channel on the server endpoint; the frames tunnel down to the host endpoint, which spawns the channel's handler locally -- so the work runs on the owner's machine, and the owner's filesystem and credentials never leave it.

In the BEAM this is a GenServer holding the link plus a registry of channels. An endpoint is role: :server or role: :host. Only the host sets :on_open, the callback that materializes a local handler for each inbound channel.

The endpoint is transport-agnostic. Outbound frames go through a send_fun (binary -> any); inbound bytes arrive as {:tunnel_recv, binary} messages. Raxol.Agent.Tunnel.Link.connect/2 wires two endpoints together in-process (deterministic, no network). A real WebSocket transport (e.g. Mint.WebSocket on the host, Bandit on the server) is a drop-in adapter: read the socket and send {:tunnel_recv, bytes} to the endpoint; set send_fun to write the socket.

Channels

  • open_channel/3 (typically called on the server) allocates a channel_id, registers the caller (or :owner) as the local channel owner, and sends an :open frame. The peer's :on_open spawns its handler.
  • send_data/3 sends channel I/O to the peer.
  • close_channel/3 closes a channel on both ends.

A channel owner receives {:tunnel_data, channel_id, data} for inbound I/O and {:tunnel_closed, channel_id, reason} when the channel closes. Owners are monitored: if an owner dies, its channel is closed and the peer notified.

Summary

Functions

List this endpoint's open channel ids.

Returns a specification to start this module under a supervisor.

Close a channel on both ends.

Open a channel to the peer. Returns {:ok, channel_id}.

The peer's hello payload (%{host_id, capabilities}) or nil.

Send channel data to the peer. Pass binary: true for raw bytes.

Attach the outbound link writer. A host sends its hello frame on attach.

Start a tunnel endpoint.

Types

channel_id()

@type channel_id() :: binary()

server()

@type server() :: GenServer.server()

Functions

channels(tunnel)

@spec channels(server()) :: [channel_id()]

List this endpoint's open channel ids.

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

close_channel(tunnel, channel_id, reason \\ "")

@spec close_channel(server(), channel_id(), binary()) :: :ok

Close a channel on both ends.

open_channel(tunnel, path, opts \\ [])

@spec open_channel(server(), binary(), keyword()) ::
  {:ok, channel_id()} | {:error, :no_link}

Open a channel to the peer. Returns {:ok, channel_id}.

:owner (default the caller) receives this side's {:tunnel_data, ...} / {:tunnel_closed, ...} messages. :meta is an arbitrary map sent in the open frame.

peer_hello(tunnel)

@spec peer_hello(server()) :: map() | nil

The peer's hello payload (%{host_id, capabilities}) or nil.

send_data(tunnel, channel_id, data, opts \\ [])

@spec send_data(server(), channel_id(), binary(), keyword()) :: :ok | {:error, term()}

Send channel data to the peer. Pass binary: true for raw bytes.

set_link(tunnel, send_fun)

@spec set_link(server(), (binary() -> any())) :: :ok

Attach the outbound link writer. A host sends its hello frame on attach.

start_link(opts \\ [])

@spec start_link(keyword()) :: GenServer.on_start()

Start a tunnel endpoint.

Options:

  • :role -- :server (default) or :host.
  • :host_id / :capabilities -- host identity, sent in the hello frame.
  • :on_open -- host callback (%{channel_id, path, meta, tunnel} -> {:ok, pid} | {:error, term}).

  • :send_fun -- (binary -> any) link writer; may be set later via set_link/2.
  • :name -- optional registered name.