# `Flezha.SDK`
[🔗](https://github.com/flezha/flezha_elixir_sdk/blob/v0.1.0/lib/flezha/sdk.ex#L1)

The Elixir runner SDK — the runner-as-handle surface over `flz_core`, the
runtime every Flezha SDK runs on. A runner reaches its controller one way,
over UDS (`transport: :uds`, the default) or QUIC (`transport: :quic`),
wherever the controller runs: in this BEAM, on this machine or on another.
A handle accumulates its declared factory set with no connection or process;
`start/1` claims the process-wide runner slot (a named singleton) and
connects; `run/1` is `start/1` then `await_termination/1`. Authoring and
connection failures are named error tuples returned at the call site (never a
raise) and propagate through a pipe. `child_spec/1` wraps the same sequence for
a host application's supervision tree.

The connection settings are read from the OS environment first, then `config :flezha_sdk, ...`. A controller that enforces
authorization asks every runner for a credential: pass it as
`namespace_secret:`, or set `FLEZHA_NAMESPACE_SECRET`.

# `result`

```elixir
@type result() :: t() | {:error, Flezha.SDK.Error.t()}
```

# `t`

```elixir
@type t() :: %Flezha.SDK{
  modules: term(),
  namespace: term(),
  namespace_secret: term(),
  server: term(),
  transport: term()
}
```

# `await_termination`

```elixir
@spec await_termination(result()) :: :ok | {:error, Flezha.SDK.Error.t()}
```

Block until the runner terminates.

# `call`

```elixir
@spec call(result() | pid() | atom(), String.t(), String.t(), term()) ::
  {:ok, term()} | {:error, Flezha.SDK.Error.t()}
```

Call the entry channel `channel` of `relay_id` with `value`, through the
controller under the same role check as `send/5`, answering what the relay
answers: `{:ok, answer}` with a JSON value, a string for text, the bytes for
the binary formats, or `%{"accepted" => true}` from a channel no output
answers. `value` is encoded as an output with no serializer of its own
encodes it.

`runner` is a handle, or the runner server's pid or name.

# `child_spec`

A child spec wrapping create → register → start for the host application's
supervision tree: `namespace:`, `relays:`, `transport:` and
`namespace_secret:`. The supervised process is the runner. A runner runs
once per BEAM: one that stops or crashes ends its session, and the restart
that follows is refused, so the failure reaches
the node, which is the unit to restart. The controller moves the relays
meanwhile.

# `close`

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

Ship each relay's state, end the session and release the slot. A runner
runs once per BEAM: a later start in this node is refused as
`runtime.core_shut_down`. A never-started handle is a no-op.

# `create_runner`

```elixir
@spec create_runner(String.t(), keyword()) :: result()
```

Create a runner handle. No connection, no slot claim, no processes.

# `namespace`

```elixir
@spec namespace(t()) :: String.t()
```

# `register_factory`

```elixir
@spec register_factory(result(), module()) :: result()
```

Declare a relay: a module of the `Flezha.SDK.Relay` behaviour. Legal
before and after `start/1`.

Its declaration is judged here by `flz_core`, the check every SDK's builder
is judged by, and declared to the live core when the runner is started.

# `relays`

```elixir
@spec relays(result() | pid() | atom()) ::
  {:ok, [String.t()]} | {:error, Flezha.SDK.Error.t()}
```

The ids of the relay instances bound on this runner now, a partition
member by its member id, read without the controller.

# `run`

```elixir
@spec run(result()) :: :ok | {:error, Flezha.SDK.Error.t()}
```

`start/1` then `await_termination/1`.

# `send`

```elixir
@spec send(result() | pid() | atom(), String.t(), String.t(), String.t(), keyword()) ::
  {:ok, term()} | {:error, Flezha.SDK.Error.t()}
```

Send `message` to the entry channel `channel` of `relay_id`.

Through the controller by default, under the same role check as
`Flezha.SDK.Control`, answering its verdict: `{:ok, %{"to" => relay,
"verdict" => "delivered" | "absorbed" | "suspended"}}`. Options:

  * `msg_id:` the message's id, stamped as `flz.msg_id`: a relay whose
    input log still holds an input of this id absorbs the repeat.
  * `key:` the partition key whose member of a partition group takes it.
  * `local: true` in process, without the controller: the message is on no
    disk, is lost if the runner stops before handling it, and a relay's
    input log does not cover it. Answers `{:ok, nil}`; refused with
    `runtime.relay_not_local` for a relay this runner does not hold,
    and with `usage.options_conflict` beside `msg_id:` or `key:`.

`runner` is a handle, or the runner server's pid or name.

# `server`

```elixir
@spec server(result() | pid() | atom()) ::
  {:ok, pid() | atom()} | {:error, Flezha.SDK.Error.t()}
```

The live runner process behind a handle: `runtime.runner_not_started` before
`start/1`, and a refused handle's own error unchanged.

The one place a caller outside this module reads the handle's server, so the
struct's shape stays this module's business.

# `start`

```elixir
@spec start(result()) :: result()
```

Claim the runner slot and connect. The loser gets `runtime.runner_already_running`
naming the namespace.

# `unregister_factory`

```elixir
@spec unregister_factory(result(), module()) :: result()
```

Remove a declared relay — the inverse of `register_factory/2`, taking what it took.

---

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