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 link
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 achannel_id, registers the caller (or:owner) as the local channel owner, and sends an:openframe. The peer's:on_openspawns its handler.send_data/3sends channel I/O to the peer.close_channel/3closes 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
@type channel_id() :: binary()
@type server() :: GenServer.server()
Functions
@spec channels(server()) :: [channel_id()]
List this endpoint's open channel ids.
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec close_channel(server(), channel_id(), binary()) :: :ok
Close a channel on both ends.
@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.
The peer's hello payload (%{host_id, capabilities}) or nil.
@spec send_data(server(), channel_id(), binary(), keyword()) :: :ok | {:error, term()}
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.
@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 viaset_link/2.:name-- optional registered name.