# Key-value Store

Store small durable records with linearizable reads and writes, compare-and-swap, atomic counters, TTL, and ordered prefix scans.

Ferrite Store is the default home for durable application records. Reads and
writes are linearizable, so a successful write is immediately visible to later
reads through the authoritative group.

## Declare a store

```json filename="ferrite.json"
{
  "services": [{ "name": "catalog", "primitive": "store" }],
  "applications": [{
    "name": "catalog-api",
    "artifact_key": "apps/catalog-api@v1",
    "artifact_sha256": "0000000000000000000000000000000000000000000000000000000000000000",
    "bindings": [{ "handle": "catalog", "kind": "store", "service": "catalog" }]
  }]
}
```

Obtain the handle from `AppContext`:

```rust filename="src/main.rs"
let mut catalog = ctx.store("catalog")?;
catalog.put(b"product/coffee", br#"{"name":"Coffee","stock":24}"#).await?;
let product = catalog.get(b"product/coffee").await?;
```

## Choose a key shape

Use stable prefixes such as `tenant/{tenant_id}/order/{order_id}`. Prefix scans
then return related records in key order without a global unbounded query.
Include tenant or realm identity in every private key.

## Guard concurrent updates

Use compare-and-swap for versioned documents and atomic ADD for counters. A
failed comparison means another writer won; reload the record and decide
whether to retry the domain action. Do not overwrite blindly after a stale read.

TTL is useful for caches, invitations, and ephemeral views. It is not a
substitute for a durable scheduled domain transition; use [Cron Jobs](/developers/cron-jobs/)
when an effect must occur after expiry.

Use [Transactions](/developers/transactions/) when one decision spans multiple
keys, [Queues](/developers/queues/) for work, and [Object Storage](/developers/object-storage/)
for large values.
