# `XGW.ICE.Consent`
[🔗](https://github.com/Lazarus404/xgw/blob/main/lib/xgw/ice/consent.ex#L1)

Keeps an already-selected ICE path "alive" (consent freshness, RFC 7675).

## Why consent matters

After ICE nominates a pair, the path can still die silently: a NAT binding
may expire, a firewall may close UDP state, or a peer may disappear. WebRTC
therefore periodically sends lightweight STUN Binding probes. If no fresh
consent is observed within a timeout, the connection should be torn down
instead of sending media into a black hole.

Helpers here build outbound consent Binding requests and track
`last_consent_at` for both ICE-lite and ICE-full agents.

## RFCs

* RFC 7675 -- ICE consent freshness
* RFC 8489 -- STUN Binding used as consent probes
* RFC 8445 -- USERNAME / MESSAGE-INTEGRITY for Binding requests

# `ice`

```elixir
@type ice() :: map()
```

ICE agent map with consent fields (lite or full).

# `binding_request`

```elixir
@spec binding_request(ice()) :: binary()
```

Builds an outbound STUN Binding request for consent freshness.

Uses USERNAME `remote:local` (sender perspective per RFC 8445 pt.7.2.2) and
MESSAGE-INTEGRITY with the remote `ice-pwd`.
No ICE role attributes.

# `expired?`

```elixir
@spec expired?(ice(), integer(), non_neg_integer()) :: boolean()
```

Returns true when consent has expired (no refresh within `timeout_ms`).

## Examples

    iex> XGW.ICE.Consent.expired?(%{last_consent_at: nil}, 1000, 500)
    false

    iex> XGW.ICE.Consent.expired?(%{last_consent_at: 100}, 700, 500)
    true

    iex> XGW.ICE.Consent.expired?(%{last_consent_at: 100}, 400, 500)
    false

# `refresh`

```elixir
@spec refresh(ice(), integer()) :: ice()
```

Records consent freshness at monotonic time `now_ms`.

## Examples

    iex> XGW.ICE.Consent.refresh(%{last_consent_at: nil}, 1234)
    %{last_consent_at: 1234}

---

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