# `Keyfob.Store`
[🔗](https://github.com/alexdont/keyfob/blob/v0.1.0/lib/keyfob/store.ex#L1)

Storage contract for Keyfob requests — a small keyed store with TTLs
and two atomicity guarantees the login flow leans on:

  * `update/2` applies its function serially per key (no lost updates
    between a racing approve and expiry sweep);
  * `take/1` removes-and-returns atomically (the single-use gate for
    login tokens: of two racing `consume` calls, exactly one wins).

`Keyfob.Store.ETS` is the built-in single-node implementation. For a
cluster, implement this behaviour over storage all nodes share (your
database, Redis) and set `config :keyfob, store: MyApp.KeyfobStore`.

Values must be treated as opaque. Expired entries must behave as
missing (`:error`) even if not yet swept.

# `key`

```elixir
@type key() :: binary()
```

# `value`

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

# `delete`

```elixir
@callback delete(key()) :: :ok
```

Removes a value. Idempotent.

# `get`

```elixir
@callback get(key()) :: {:ok, value()} | :error
```

Fetches a live (non-expired) value.

# `put`

```elixir
@callback put(key(), value(), ttl_ms :: pos_integer()) :: :ok
```

Stores `value` under `key` for `ttl_ms` milliseconds.

# `take`

```elixir
@callback take(key()) :: {:ok, value()} | :error
```

Atomically removes and returns a live value.

# `update`

```elixir
@callback update(key(), (value() -&gt; {:ok, value()} | {:error, term()})) ::
  {:ok, value()} | {:error, term()} | :error
```

Atomically applies `fun` to the live value under `key`.

`fun` returns `{:ok, new_value}` to store (keeping the remaining TTL)
or `{:error, reason}` to leave the value untouched. Returns the fun's
result, or `:error` when the key is missing/expired.

---

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