# `AshOnetime.Codec`
[🔗](https://github.com/baselabs/ash_onetime/blob/v1.5.0/lib/ash_onetime/codec.ex#L1)

Contract for response codecs and shared boundary validation.

# `decode`

```elixir
@callback decode(binary(), binary(), map(), keyword()) ::
  {:ok, term()} | {:error, AshOnetime.Error.t()}
```

# `encode`

```elixir
@callback encode(term(), map(), keyword()) ::
  {:ok, binary(), binary()} | {:error, AshOnetime.Error.t()}
```

# `format_tag`

```elixir
@callback format_tag() :: binary()
```

# `ash_resource?`

```elixir
@spec ash_resource?(term()) :: boolean()
```

# `hard_limits`

```elixir
@spec hard_limits() :: map()
```

# `max_bytes`

```elixir
@spec max_bytes(map()) :: pos_integer()
```

# `protect_only_ceilings`

```elixir
@spec protect_only_ceilings() :: keyword()
```

The protect-only limit keys with their package ceilings (the half of the limit vocabulary
that bounds the key/verification/cache paths, not the response codec). Single source shared
with `Response.response_limit/1`'s typo-discrimination set.

# `structural_limits`

```elixir
@spec structural_limits(map()) :: map()
```

Returns the response structural limits for `contract`, merging the contract's declared
limits over the package hard limits. Only the hard-limit keys are honored (`max_response_*`);
any other keys in `contract.limits` are ignored, so this is safe to call on an un-normalized
map. Values are taken as-is — callers building a `%AshOnetime.Response.Contract{}` must
validate values (the `Response.contract/4` path does so via `normalize_response_limits/1`,
which selects + validates the response-relevant subset before delegating to `normalize_limits/1`).

# `validate_tag`

```elixir
@spec validate_tag(term()) :: :ok | {:error, AshOnetime.Error.t()}
```

# `validate_value`

```elixir
@spec validate_value(term(), map(), atom()) :: :ok | {:error, AshOnetime.Error.t()}
```

---

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