# `Portals.Connection`
[🔗](https://github.com/Ziinc/portals/blob/v0.1.0/lib/portals/connection.ex#L1)

Owns one worker's socket, protocol state machine, and in-flight request
table (FR-1, FR-2). One `Portals.Connection` GenServer corresponds to
one launched worker process.

Lifecycle: allocates a private Unix-domain socket (or, on platforms
without Unix-socket support, a framed stdio port), launches the worker
through `Portals.WorkerLifecycle.Port`, accepts its single connection,
and completes the exact-version `HELLO`/`READY` handshake — all before
replying to `start_link/1`, so a successfully started connection is
always immediately usable.

A worker crash, protocol violation, or handshake failure fails every
outstanding request with a terminal `%Portals.Error{}` exactly once and
moves the connection to `:closed`; it never crashes the caller or the
BEAM. `Portals.Connection` does not restart itself — that is a pool's
responsibility (Phase 4).

# `start_opts`

```elixir
@type start_opts() :: [
  command: binary(),
  args: [binary()],
  transport: :unix | :stdio,
  limits: Portals.Protocol.limits(),
  connect_timeout: timeout(),
  handshake_timeout: timeout(),
  name: GenServer.name(),
  callback_allowlist: [{module(), atom(), arity()}] | nil,
  message_envelope: :wrapped | :raw
]
```

# `async`

```elixir
@spec async(GenServer.server(), binary(), binary(), list(), keyword()) ::
  {:ok, Portals.Request.t()} | {:error, Portals.Error.t()}
```

# `await`

```elixir
@spec await(Portals.Request.t(), timeout()) ::
  {:ok, term()} | {:error, Portals.Error.t()}
```

Block the calling process until `request` completes or `timeout` elapses.

# `call`

```elixir
@spec call(GenServer.server(), binary(), binary(), list(), keyword()) ::
  {:ok, term()} | {:error, Portals.Error.t()}
```

# `call!`

```elixir
@spec call!(GenServer.server(), binary(), binary(), list(), keyword()) :: term()
```

# `cancel`

```elixir
@spec cancel(Portals.Request.t()) :: :ok
```

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `health`

```elixir
@spec health(GenServer.server()) :: map()
```

# `open_stream`

```elixir
@spec open_stream(GenServer.server(), binary(), binary(), list(), keyword()) ::
  {:ok, Portals.Stream.t()} | {:error, Portals.Error.t()}
```

Open a bidirectional stream against `module`/`function` (FR-5).

Dispatches a `CALL` whose `request_id` doubles as the stream's
`stream_id`, grants the worker its initial inbound byte credit, and
returns a `%Portals.Stream{}` handle owned by `opts[:owner]`
(defaulting to the calling process). Stream capacity is bounded
separately from unary in-flight capacity: it is limited by the
`max_streams` the worker advertised in its `HELLO`.

# `start_link`

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

# `stop`

```elixir
@spec stop(GenServer.server(), timeout()) :: :ok
```

---

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