# `Rho.Dyn.Approval`
[🔗](https://github.com/GenericJam/rho/blob/v0.1.0/lib/rho/dyn/approval.ex#L1)

The approval gate on changing what runs (the pipeline's step 3, see
`Rho.Dyn`):
activating a proposed generation, or reverting to a chosen one by hand.

The UI asks the configured implementation for a token once the human
approved (`c:request/1`, reached through `Rho.Dyn.request_approval/2`);
`Rho.Dyn.Keeper` asks the same implementation to `c:verify/2` it
before it changes anything. Contract for implementations:

  * a token is for one subject: approving generation 5 doesn't activate
    generation 6, and an activation token doesn't revert;
  * a token expires (the production one after 60 s);
  * a token works once: `c:verify/2` consumes it. The Keeper also remembers
    every token it accepted and refuses it a second time
    (`{:error, :approval_used}`), whatever the implementation says.

Automatic reverts (crashes on probation, boot probation) need none.
Dropping a candidate (`Rho.Dyn.discard/2`) needs none either:
it changes nothing that runs.

The production implementation is `Rho.Dyn.Approval.Biometric`.
Tests configure their own (`approval:` option of the Keeper).

# `subject`

```elixir
@type subject() ::
  {:activate, pos_integer()}
  | {:revert_to, non_neg_integer()}
  | {:auto_approve, :on}
```

# `token`

```elixir
@type token() :: term()
```

# `request`

```elixir
@callback request(subject()) :: {:ok, token()} | {:error, term()}
```

A token for `subject`, if the human approved it.

# `verify`

```elixir
@callback verify(token(), subject()) :: :ok | {:error, term()}
```

Is `token` a valid, unexpired, unused approval of `subject`? Consumes it.

---

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