# `Snodo.MRTR.State`
[🔗](https://github.com/joshrotenberg/snodo/blob/v0.1.0/lib/snodo/mrtr/state.ex#L1)

Optional, integrity-protected state for multi-round-trip requests.

`seal/3` produces a JSON-backed token that `open/3` binds to the original
request method, salient request parameters, and an explicitly supplied
authenticated principal. Supply `principal: nil` only for deliberately
anonymous requests. Authentication and principal selection belong to the
application; the helper does not infer identity from client-controlled data.

Parameters named `"_meta"`, `"requestState"`, and `"inputResponses"` at the
top level are excluded from the binding, as are the JSON-RPC request ID and
transport. Map ordering is immaterial; list ordering and numeric types are
retained, so `1` and `1.0` bind differently as a conservative check.

Required options are `:secret` (at least 32 bytes of application-managed,
cryptographically random secret material) and `:principal` (a JSON value).
Optional `:ttl` defaults to 300 seconds and cannot exceed 900 seconds. When
opening a token, `:ttl` also limits its originally issued lifetime; changing
it never extends the signed expiration. `:clock` may be a trusted zero-arity
function returning Unix time in seconds, primarily for deterministic tests.

Tokens are signed, **not encrypted**: their state is readable by the client.
Never place credentials or other secrets in state. Tokens can be reused
until expiry and are not a replay-prevention or single-use mechanism. The
application owns idempotency, durable replay tracking, authorization on each
request, and secret distribution/rotation. Rotating the secret invalidates
previously issued tokens. This helper imposes a 16 KiB token size limit.

# `open`

```elixir
@spec open(term(), Snodo.Context.t(), keyword()) ::
  {:ok, term()} | {:error, Snodo.Error.t()}
```

Opens valid, unexpired state for this principal and request.

Untrusted token failures all return the same protocol error with no data or
cause. Invalid application configuration still raises an `ArgumentError`.

# `seal`

```elixir
@spec seal(term(), Snodo.Context.t(), keyword()) :: String.t()
```

Seals JSON state, raising for invalid application configuration or state.

---

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