# `Layr8.Channel`
[🔗](https://github.com/layr8/elixir_sdk/blob/main/lib/layr8/channel.ex#L1)

Phoenix Channel transport over WebSocket.

Implements the Phoenix Channel V2 wire protocol:

    [join_ref, ref, topic, event, payload]

## Connection Flow

1. Connects to `{node_url}?api_key={key}&vsn=2.0.0`
2. Sends `phx_join` with registered protocols and DID spec
3. Fires heartbeat every 30 seconds
4. Auto-reconnects with exponential backoff (1s → 30s) on disconnect

## Callbacks

- `on_message/1` — called with the decoded payload of inbound `"message"` events
- `on_disconnect/1` — called with the error reason on disconnect
- `on_reconnect/0` — called after successful reconnection

Not meant to be used directly; interact through `Layr8.Client`.

# `callbacks`

```elixir
@type callbacks() :: %{
  on_message: on_message_fn(),
  on_disconnect: on_disconnect_fn() | nil,
  on_reconnect: on_reconnect_fn() | nil
}
```

# `on_disconnect_fn`

```elixir
@type on_disconnect_fn() :: (term() -&gt; any())
```

# `on_message_fn`

```elixir
@type on_message_fn() :: (map() -&gt; any())
```

# `on_reconnect_fn`

```elixir
@type on_reconnect_fn() :: (-&gt; any())
```

# `start_opts`

```elixir
@type start_opts() :: %{
  :ws_url =&gt; String.t(),
  :api_key =&gt; String.t(),
  :agent_did =&gt; String.t(),
  :callbacks =&gt; callbacks(),
  optional(:did_spec) =&gt; map()
}
```

# `assigned_did`

```elixir
@spec assigned_did(pid()) :: String.t()
```

Returns the DID assigned by the cloud-node (for ephemeral identities).

# `capabilities`

```elixir
@spec capabilities(pid()) :: [String.t()]
```

Returns the capabilities advertised by the cloud-node on join.

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `close`

```elixir
@spec close(pid()) :: :ok
```

Closes the WebSocket connection.

# `connect`

```elixir
@spec connect(pid(), [String.t()]) :: :ok | {:error, term()}
```

Connects to the cloud-node WebSocket and joins the Phoenix channel.

Blocks until the join is acknowledged or returns `{:error, reason}`.

# `send_ack`

```elixir
@spec send_ack(pid(), [String.t()]) :: :ok
```

Sends an acknowledgment for a list of message IDs.

Used in legacy mode (cloud nodes without reply protocol).

# `send_fire_and_forget`

```elixir
@spec send_fire_and_forget(pid(), String.t(), map()) :: :ok
```

Sends a message event fire-and-forget (no reply tracking).

# `send_msg`

```elixir
@spec send_msg(pid(), String.t(), map()) :: {:ok, map()} | {:error, term()}
```

Sends a message event and waits for a server acknowledgment.

Returns `{:ok, reply}` where `reply` is `%{status: String.t(), reason: String.t()}`,
or `{:error, reason}` on timeout or disconnect.

# `start_link`

```elixir
@spec start_link(start_opts()) :: GenServer.on_start()
```

Starts the Channel GenServer.

## Options

- `:ws_url` — WebSocket URL
- `:api_key` — API key for the `?api_key=` query parameter
- `:agent_did` — agent DID (used as the Phoenix topic `plugins:{did}`)
- `:callbacks` — map of callback functions (see module doc)

---

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