# `Aker.Gate`
[🔗](https://github.com/udin-io/aker/blob/v0.1.0/lib/aker/gate.ex#L1)

Whether this install may run. The gate checks the stored licence at boot
and every hour, holds `:licensed` or `{:locked, reason}` where every
reader finds it without a call, and renews the licence in its term's
last week.

The host starts one gate in its supervision tree, with the options below,
and decides what a lock blocks: it reads `status/1` or `locked?/1` on each
page or job, and the gate calls `:on_lock` and `:on_unlock` when the
install locks or unlocks. A gate name no check has run under reads
`{:locked, :not_checked}`, so a host that forgets to start the gate runs
locked, not free.

Every function takes the gate's options, so a host can build them from
runtime config at each call. The process only runs the boot check and the
hourly tick.

`load/3`, `activate/3` and `refresh/2` trust the host to have checked who
may call them; the actor passes to the store untouched. `renew/1` stores
as `:system`.

A running install stays licensed through a failure of its own machine (a
store it cannot read, a missing machine id, a slow chip): only the licence
itself locks a running install. A booting install stays locked until the
check passes.

# `status`

```elixir
@type status() :: :licensed | {:locked, atom()}
```

# `activate`

```elixir
@spec activate(keyword(), String.t(), term()) :: {:ok, map()} | {:error, term()}
```

Activates with `code` as `actor`: sends this install's request to the
licensing server and loads the licence that comes back. A code that is
not 1 to 64 base32 or base64url characters makes no call.

# `check`

```elixir
@spec check(keyword()) :: status()
```

Runs the boot check now and answers the status it holds.

# `context`

```elixir
@spec context(keyword()) ::
  {:ok, %{install_key: {module(), keyword()}, machine_id_hash: String.t()}}
  | {:error, atom()}
```

This install's key and machine id hash, or the reason they are missing.
Makes the install key when there is none.

# `current`

```elixir
@spec current(keyword()) :: map() | nil
```

The licence in force, or nil when none is stored or the store cannot be read.

# `load`

```elixir
@spec load(keyword(), binary(), term()) :: {:ok, map()} | {:error, term()}
```

Checks `bytes` as this install's licence and stores it as `actor`. The
install unlocks when it passes, and `:on_unlock` runs when it was locked.

# `locked?`

```elixir
@spec locked?(atom()) :: boolean()
```

Whether the install is locked.

# `options`

```elixir
@spec options() :: NimbleOptions.t()
```

The options every function takes:

* `:name` (`t:atom/0`) - Required. The gate's name; its status is kept under it.

* `:product` (`t:String.t/0`) - Required. The product this app is.

* `:product_version` (`t:String.t/0`) - Required. The app's version.

* `:root_keys` (list of `t:String.t/0`) - Required. The trusted Ed25519 root public keys, raw (`Aker.PublicKeys.decode/1`).

* `:licence_dir` - The folder that holds the install key.

* `:machine_id_path` - The host's machine id file (default `/etc/machine-id`).

* `:require_mount` (`t:boolean/0`) - Refuse with `:machine_id_not_mounted` unless `:machine_id_path` is given, for a container whose own `/etc/machine-id` every copy shares. The default value is `false`.

* `:store` (tuple of `t:atom/0`, `t:keyword/0` values) - Required. `{module, opts}` of an `Aker.Store`.

* `:server` (`t:keyword/0`) - `Aker.Client`'s server options. The default value is `[]`.

* `:on_lock` (function of arity 0) - Called when a running install locks.

* `:on_unlock` (function of arity 0) - Called when a locked install unlocks.

* `:check_every` (`t:pos_integer/0`) - Milliseconds between ticks. The default value is `3600000`.

* `:renew_within` (`t:pos_integer/0`) - Seconds before the term ends when each tick renews. The default value is `604800`.

* `:tpm_device` (`t:String.t/0`) - The TPM chip's device. The default value is `"/dev/tpmrm0"`.

* `:tpm_options` (`t:keyword/0`) - `Aker.InstallKey.Tpm` options. The default value is `[]`.

* `:settings` (`t:keyword/0`) - The setting names a log line tells the operator to set, for `:licence_dir`, `:machine_id_path` and `:server_url` (default each option's own name). The default value is `[]`.

* `:now` (struct of type `DateTime`) - The time to check against, for tests only: a host that sets it freezes the gate's clock.

# `recheck`

```elixir
@spec recheck(keyword()) :: status()
```

The hourly check: the boot check again, which also moves the latest time
seen. A running install it finds locked by the licence itself calls
`:on_lock`; a locked install it finds licensed calls `:on_unlock`.

# `refresh`

```elixir
@spec refresh(keyword(), term()) :: {:ok, map()} | {:error, term()}
```

Asks for this term's licence with the current seats, as `actor`, and loads it.

# `renew`

```elixir
@spec renew(keyword()) :: {:ok, map()} | {:error, term()}
```

Asks for the next term's licence and loads it, as `:system`.

# `request`

```elixir
@spec request(keyword()) :: {:ok, binary()} | {:error, atom()}
```

The activation request to send the licensing server, as the file's bytes.

# `reset`

```elixir
@spec reset(atom()) :: :ok
```

Forgets the status under `name`, which then reads `{:locked, :not_checked}`.

# `start_link`

```elixir
@spec start_link(keyword()) :: GenServer.on_start()
```

Starts the gate: the boot check now, then a tick each `:check_every`.

# `status`

```elixir
@spec status(atom()) :: status()
```

The install's licence status under `name`.

# `tick`

```elixir
@spec tick(keyword()) :: status()
```

One tick: renews when the licence's term ends within `:renew_within`,
whatever the lock state, so a paid renewal unlocks an install whose term
ended, then runs `recheck/1`. One renewal call per tick, with no retry:
the next tick is the retry.

---

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