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

Main entry point for interacting with the Layr8 platform.

## Lifecycle

    # 1. Start the client
    {:ok, client} = Layr8.Client.start_link(%{
      node_url: "wss://node.example.com/plugin_socket/websocket",
      api_key: "my-api-key"
    })

    # 2. Register message handlers BEFORE connect
    :ok = Layr8.Client.handle(client, "https://example.com/proto/1.0/request", fn msg ->
      {:reply, %Layr8.Message{type: "https://example.com/proto/1.0/response", body: %{text: "pong"}}}
    end)

    # 3. Connect to the cloud-node
    :ok = Layr8.Client.connect(client)

    # 4. Send messages
    :ok = Layr8.Client.send(client, %Layr8.Message{
      type: "https://example.com/proto/1.0/request",
      to: ["did:example:bob"],
      body: %{text: "ping"}
    })

    # 5. Request/response
    {:ok, response} = Layr8.Client.request(client, %Layr8.Message{
      type: "https://example.com/proto/1.0/request",
      to: ["did:example:bob"],
      body: %{text: "ping"}
    })

    # 6. Shut down
    :ok = Layr8.Client.close(client)

## Events

Subscribe to lifecycle events by passing callbacks in `start_link/1` opts:

- `:on_disconnect` — `(reason :: term() -> any())`
- `:on_reconnect` — `(() -> any())`

## Credentials & Presentations

Credential and presentation operations use the REST API (no WebSocket required):

    {:ok, jwt} = Layr8.Client.sign_credential(client, credential, issuer_did: "did:example:alice")

# `start_opts`

```elixir
@type start_opts() :: %{
  optional(:node_url) =&gt; String.t(),
  optional(:api_key) =&gt; String.t(),
  optional(:agent_did) =&gt; String.t(),
  optional(:on_disconnect) =&gt; (term() -&gt; any()),
  optional(:on_reconnect) =&gt; (-&gt; any()),
  optional(:did_spec) =&gt; map()
}
```

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `close`

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

Gracefully shuts down the client.

# `connect`

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

Establishes the WebSocket connection and joins the Phoenix channel.

Blocks until the join is acknowledged. Returns `:ok` on success or raises on error.

# `did`

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

Returns the agent's DID — either provided in config or assigned by the node on connect.

# `get_credential`

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

Retrieves a stored credential by ID.

# `handle`

```elixir
@spec handle(pid(), String.t(), Layr8.Handler.handler_fn()) :: :ok
```

Registers a handler for a DIDComm message type.

Must be called **before** `connect/1`. Raises `Layr8.AlreadyConnectedError` if called after.

## Handler Signature

    fn msg -> {:reply, %Layr8.Message{...}} | :noreply | :pass | {:error, reason} end

# `handle_all`

```elixir
@spec handle_all(pid(), Layr8.Handler.handler_fn()) :: :ok
```

Registers a catch-all handler invoked when no specific handler matches.

Must be called **before** `connect/1`. Raises `Layr8.AlreadyConnectedError` if called after.

## Handler Signature

    fn msg -> {:reply, %Layr8.Message{...}} | :noreply | :pass | {:error, reason} end

# `list_credentials`

```elixir
@spec list_credentials(
  pid(),
  keyword()
) :: {:ok, [map()]} | {:error, term()}
```

Lists stored credentials for a holder. Defaults: holder = `did/1`.

# `request`

```elixir
@spec request(pid(), Layr8.Message.t() | map(), keyword()) :: {:ok, Layr8.Message.t()}
```

Sends a DIDComm message and waits for a correlated response (matched by thread ID).

Returns `{:ok, Layr8.Message.t()}` or raises `Layr8.ProblemReportError` / `Layr8.NotConnectedError`.

## Options

- `:timeout` — milliseconds to wait for a reply (default 30s)
- `:parent_thread` — set `pthid` for nested thread correlation

# `send`

```elixir
@spec send(pid(), Layr8.Message.t() | map(), keyword()) :: :ok
```

Sends a DIDComm message.

By default waits for server acknowledgment. Pass `fire_and_forget: true` to skip.

Returns `:ok` or raises `Layr8.NotConnectedError`.

# `sign_credential`

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

Signs a W3C Verifiable Credential. Defaults: issuer = `did/1`, format = `"compact_jwt"`.

# `sign_presentation`

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

Signs a W3C Verifiable Presentation. Defaults: holder = `did/1`, format = `"compact_jwt"`.

# `start_link`

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

Starts the Layr8 Client GenServer.

Accepts the same keys as `Layr8.Config.resolve!/1` plus:
- `:on_disconnect` — called with the disconnect reason
- `:on_reconnect` — called after successful reconnection

Raises `Layr8.Error` if required config fields are missing.

# `store_credential`

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

Stores a signed credential JWT for a holder. Defaults: holder = `did/1`.

# `verify_credential`

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

Verifies a signed credential. Defaults: verifier = `did/1`.

# `verify_presentation`

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

Verifies a signed presentation. Defaults: verifier = `did/1`.

---

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