For anyone evaluating whether Fief is the right tool — no Fief knowledge assumed.

Fief assigns ownership of keys to nodes in an Elixir cluster, with strong consistency: at most one node owns a given key at any instant, in every failure mode. That sentence is unconditional for message routing, and holds for live processes and their side effects under the default fencing mode; the exact contract, scope conditions included, is one page away on guarantees.

Two first-class surfaces build on that ownership. Fief.Key backs each key with a process — lifecycle callbacks, state that follows ownership, fencing policy — and is the surface most of this documentation tours. Fief.Cache is a partitioned, single-owner cache: plain get/put/ delete, where the ownership machinery buys single-writer-per-key coherence instead of process lifecycles (using Fief.Cache). An instance runs exactly one surface; to run both, run two instances.

It is arbiter-based: a small linearizable store — Postgres, in the shipped adapter — holds the authoritative coordination state, and every node derives its behavior from it. When forced to choose, Fief chooses unavailability over inconsistency.

The gap it fills

The Elixir ecosystem is rich in AP (available, eventually consistent) distribution primitives — Horde, Swarm, syn, pg — and nearly empty of CP ones. The AP tools are CRDT- or gossip-based: during a netsplit, both sides of the partition can register the same key, and the conflict is resolved after the damage is done. :global is stronger within a connected cluster but resolves post-split name conflicts by killing one process at random. The one real CP building block, ra (Raft), is a library to build on, not a product to use.

Fief is, roughly, "Horde, but CP": a registry and ownership layer where exactly one node owns a key at any moment, at the cost of availability during failures — and it leans on infrastructure most teams already run (Postgres) instead of asking them to operate a consensus cluster.

How it works, in four sentences

Keys hash to a fixed number of vnodes (partitions), and the only shared state is the small vnode→node assignment table — a few kilobytes regardless of key count — held in the arbiter, versioned by a monotonic epoch, mutated only through compare-and-swap. Every node holds a TTL lease it must keep renewing; a node that cannot prove its lease is current stops serving itself, unilaterally, before anyone else is allowed to take over (leases). Per-key state is never replicated or agreed upon — it lives only on the owning node, and when ownership moves it either follows through your own serialize/rehydrate callbacks (Fief.Key, the key lifecycle) or is dropped and rebuilt from misses (Fief.Cache). That is why Fief scales in keys (millions) rather than nodes.

When to use it

Use Fief when a doubly-executed key is expensive or dangerous: exclusive side effects (billing, ledger postings, outbound API calls that must not race themselves), singleton workers per entity, per-key state machines whose invariants break under concurrent owners (Fief.Key) — or a cache where two nodes writing the same key behind each other's backs is a correctness bug, not a nuisance (Fief.Cache). The trade you accept in return: during failures, keys go unavailable — for at least a lease TTL — instead of ever being served twice.

When not to use it

(This section is advice.)

  • When AP is fine — which is most of the time. Presence, pub/sub fan-out, best-effort caches, anything where a brief double-registration is an annoyance rather than a correctness bug: Horde or syn are simpler and stay available through partitions. (A cache is the canonical example in both directions: if occasionally-stale entries are fine, a plain AP cache beats Fief.Cache; reach for Fief.Cache only when you need at most one writer per key.) Reach for CP because you need it, not because it sounds safer.
  • When you need delivery guarantees. Fief has none — a node crash takes mailboxes with it, and a timed-out call has an unknown outcome. No clustering layer can give you at-least-once processing; you build acks and retries above it either way (delivery and errors).
  • When the cluster is large. Fief assumes a full distribution mesh and targets tens of nodes, not hundreds. Millions of keys, a modest number of nodes.
  • When Postgres as a single point of failure is unacceptable. With the arbiter down, membership cannot change and (by default) nodes freeze within a lease TTL. The authority contracts are pluggable precisely so a consensus-backed store can replace Postgres later, but v1 ships Postgres and treats it as an accepted SPOF.

Non-goals, up front

No delivery guarantees; no per-sender message ordering across an ownership transfer; no large-cluster ambitions; no HA arbiter in v1. Each of these is deliberate, and the reasoning lives in the design notes below.

Where to go next

  • Guarantees — the exact contract: the four guarantees, their scope conditions, and what is never promised. If you read one page to decide on Fief, read that one.
  • Getting started — deps to first call/3 round-trip.
  • The key lifecycle — the callbacks you implement and when each runs.
  • Using Fief.Cache — the cache surface: coherent get/put/ delete without the process machinery.

Design notes: docs/design.md §1 (motivation), §3 (non-goals), §4 (core design decisions).