fabric ·nu.kv

Durable state for Nu apps, from prototype to terabytes.

nu.kv turns Refs into slots in a document-shaped data model over any KV storage, with snapshots for consistent reads and transactions for whole-state writes.

See it.

Declare a Shape, hang typed Refs off it, then read and write as plain Python attributes.

Refs are addresses. Shapes are your data model.

Any Python primitive, dict, list, tuple, nested Shape, or indexed collection can hang off a Shape as a typed Ref. Subscript and dot navigate through the tree. Nothing is fetched until a bracket runs it.

Wrap reads in Snapshot for a consistent view. Wrap writes in Transaction and the whole body commits or none of it does. Backend picks change one line at the top.

store.py
python · 26 locpy
import nu
class Profile(nu.Shape):
name: nu.v.StrRef
score: nu.v.IntRef
class Store(nu.Shape):
profiles: nu.v.ShapesDictRef[int, Profile]
top: nu.v.IntRef
# refs read and write like plain attributes
top_name = Store.profiles[Store.top].name
delta = Store.profiles[42].score - Store.profiles[7].score
# batch reads in one snapshot for consistency
view = nu.kv.Snapshot(top_name | delta)
# batch writes in one transaction for integrity
bump = nu.kv.Transaction(
Store.profiles[42].score.inc()
| Store.top.set(42)
)
# rocksdb, lmdb, in-memory. same refs, same code.
app = nu.With(nu.kv.rocksdb_navigator(".db"), body=bump)
nu.run(app)

What your program gains.

Four properties once your state lives on nu.kv.

persistence

State is there when the process comes back.

Every write goes to the backing store. Restart, redeploy, crash and recover. The values are still there in the same slots you declared.

scalability

Shard the storage. Refs do not notice.

The same Ref API runs against a laptop directory and a terabyte-scale store. Partition by key, split across disks, add read-only tailers. The Shape code does not change.

reactivity

Any Ref emits on change.

Subscribe with on_change and wake code on write. The default observer fires in-process. Swap in the Redis observer and the same subscription fires across the cluster.

durability

Snapshots and transactions built in.

Wrap reads in Snapshot for one consistent view. Wrap writes in Transaction and the whole body commits or none of it does.

Pick a backend.

Storage on one line. Notifications on another. The Ref API stays the same across every combination.

rocksdb

LSM store for terabytes and up.

Billions of keys, range scans, snapshots, WAL. Primary writer plus read-only tailers in other processes. The default when data outlives the process.

lmdb

Memory-mapped, single-writer ACID.

Zero-copy reads, one writer, real transactions in a single directory. Reach for it when one process owns writes and readers want speed.

in-memory

ACID in RAM for tests and drafts.

Same Ref API, same brackets, no disk. Real snapshots and transactions, so tests exercise the production code path.

observers

Redis or in-memory pub/sub.

The observer decides who wakes up on write. In-memory for one process, Redis for many. on_change stays the same call.

Try Nu.

One command gets you the wheel with every fabric. Then follow the movies tutorial to build a real app in an afternoon.

01 Install

pip install "nustack-py[all]"

02 Run the demo

nu demo movies

03 Build your app

Browse examples

Like what you see?

The project is young. Star it, join the room, watch what we ship next.