Keyfob.Store behaviour (Keyfob v0.1.0)

Copy Markdown View Source

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.

Summary

Callbacks

Removes a value. Idempotent.

Fetches a live (non-expired) value.

Stores value under key for ttl_ms milliseconds.

Atomically removes and returns a live value.

Atomically applies fun to the live value under key.

Types

key()

@type key() :: binary()

value()

@type value() :: term()

Callbacks

delete(key)

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

Removes a value. Idempotent.

get(key)

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

Fetches a live (non-expired) value.

put(key, value, ttl_ms)

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

Stores value under key for ttl_ms milliseconds.

take(key)

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

Atomically removes and returns a live value.

update(key, function)

@callback update(key(), (value() -> {: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.