# `XGW.Peer.Datachannel`
[🔗](https://github.com/Lazarus404/xgw/blob/main/lib/xgw/peer/datachannel.ex#L1)

Per-Peer process that owns SCTP and DTLS application-data for data channels.

## What problem this solves

Production datachannel egress used to run on the `XGW.Peer` GenServer mailbox
together with ICE, RTP, and consent. Under fanout load, synchronous
`SctpAssociation.send/4` plus `:ssl.send/2` queued behind (and blocked) those
duties, so wire latency climbed into seconds. This process isolates SCTP and
DTLS app-data: Forwarder casts land here, and Peer stays responsive for media.

Casts are drained in batches (default up to 32) so multiple user messages can
share one flush of transmit events under load. Handler callbacks still use the
Peer pid as `peer_ref` so Session and Forwarder identity is unchanged.

A full SCTP-in-ETS SendHandle hot path is not shipped (`ssl_sock` ownership).

## RFCs

* RFC 8261 -- SCTP over DTLS
* RFC 8831 -- WebRTC data channels
* RFC 8841 -- SDP negotiation of SCTP

# `start_opt`

```elixir
@type start_opt() ::
  {:peer, pid()}
  | {:handler, pid()}
  | {:ssl_sock, term()}
  | {:role, :active | :passive}
  | {:assoc, XSockets.Sctp.Association.t()}
  | {:send_dtls, (map(), binary() -&gt; :ok | {:error, term()})}
  | {:batch_max, pos_integer()}
  | {:sctp_ports, map()}
```

Opaque options for `start_link/1`.

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `ensure_table!`

```elixir
@spec ensure_table!() :: :ok
```

Creates the ETS peer->Datachannel map (idempotent). Called from `XGW.Application`.

# `lookup`

```elixir
@spec lookup(pid()) :: {:ok, pid()} | :error
```

Looks up the Datachannel pid published for `peer`, if any.

# `open_datachannel`

```elixir
@spec open_datachannel(pid(), binary(), keyword()) :: {:ok, map()} | {:error, term()}
```

Opens a local data channel after the association is connected.

# `send_datachannel`

```elixir
@spec send_datachannel(pid(), reference(), binary(), :string | :binary) ::
  :ok | {:error, term()}
```

Sends a user message and waits for SCTP/DTLS flush on this process.

# `send_datachannel_async`

```elixir
@spec send_datachannel_async(pid(), reference(), binary(), :string | :binary) :: :ok
```

Queues a user message on this Datachannel process (cast; batched drain).

# `start_link`

```elixir
@spec start_link([start_opt()]) :: GenServer.on_start()
```

Starts a Datachannel process for `peer`.

When `ssl_sock` is present, takes over `:ssl.controlling_process/2` so DTLS
app-data messages arrive here. Optional `:assoc` injects an existing
association (tests). Optional `:send_dtls` replaces `:ssl.send/2` (tests).

---

*Consult [api-reference.md](api-reference.md) for complete listing*
