# `Anubis.Server.RequestState`

The integrity-protected `requestState` of multi round-trip requests.

A handler that returns an `Anubis.Server.InputRequired` may attach any Erlang
term as state. The client echoes the resulting string on its retry, so the
specification treats it as attacker-controlled input: a server whose state
influences what it does must protect its integrity and reject state that
fails verification.

This module signs the term with HMAC-SHA256 and binds it to:

  * the authenticated principal (`Anubis.Server.Frame.subject/1`), so state
    issued to one user is refused for another;
  * the server module that issued it, so state cannot cross to another
    server sharing the application's secret;
  * the originating request, its method plus the tool or prompt name or the
    resource URI, so state cannot move to a different request;
  * a digest of that request's parameters (`request_digest` on the frame's
    context), so a retry cannot keep the state and change the arguments;
  * an expiry, `:request_state_ttl` milliseconds after it was issued
    (default: ten minutes).

A server that authenticates outside `Anubis.Server.Authorization` has no
subject on the frame, and should carry its own principal inside the term and
check it on retry.

These measures bound replay; they do not make a state single-use. A server
that must consume a state at most once enforces that itself.

## Configuration

    config :anubis_mcp, request_state_secret: System.fetch_env!("MCP_REQUEST_STATE_SECRET")

The secret must be a binary of at least 32 bytes. Signing without one raises;
verifying without one refuses the state as `:invalid`.

# `binding`

```elixir
@type binding() :: {server :: module(), method :: String.t(), target :: String.t()}
```

# `sign`

```elixir
@spec sign(term(), Anubis.Server.Frame.t(), binding()) :: String.t()
```

Signs `term` for the request `binding` identifies, as the principal on `frame`.

# `verify`

```elixir
@spec verify(String.t(), Anubis.Server.Frame.t(), binding()) ::
  {:ok, term()} | {:error, :invalid | :mismatch | :expired}
```

Verifies a state the client echoed and returns the term it carries.

Fails with `:invalid` when the string is malformed or its signature does not
match, `:mismatch` when it was issued to another principal or request, and
`:expired` once its TTL has lapsed.

---

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