Nu
Docs for Nu, the interaction primitive. Install, learn, and look up.
Every app is a set of interactions between systems: a database, a UI, AI agents, and services. Nu makes interaction the primitive: Refs name what you touch (a UI widget, an LLM endpoint, a memory slot, a KV slot), Interactions describe what to do with them (read, write, branch, iterate, compose). Persistence, reactivity, atomicity, observability, and scalability are inherent, not bolted on.
Get started
Install
pip install and run the bundled demo. 2 min.
Hello, Nu
Your first Nu program. 5 min.
Your first app
Build a counter dashboard end to end. 15 min.
The idea
A tiny program is a joy to write:
a = 2
b = 5
print(a + b)Three lines, one substrate.
Real apps do not stay here. a moves into a database. b comes from a form submission. The result renders in a browser. A background job reruns it when either input changes. Three lines become three hundred: an ORM, a request handler, a template, a websocket, a queue. Almost none of it is about a + b anymore. It is all interaction between substrates.
Nu makes interaction the primitive.
- Ref. A name for a value, wherever it lives. A KV slot, a UI widget, an LLM endpoint, a remote object.
- Interaction. What you do with a Ref. Read, write, branch, iterate, compose.
- Fabric. Binds Refs to a real backend.
Here is the same program with a and b persisted in a KV store:
import nu
import nustd
class DB(nu.Shape):
a = nustd.kv.IntRef.slot()
b = nustd.kv.IntRef.slot()
# compute a + b and print it
compute = DB.a.set(2) >> DB.b.set(5) >> nu.print(DB.a + DB.b)
# assemble: rocksdb-backed
app = nu.With(
nustd.kv.rocksdb_navigator(".dbsum"),
body=nustd.kv.auto_flow_atomic(compute),
)
nu.run(app)>> chains interactions in order. Kill the process, run it again, the values are still there.
Same program, but the result lands in a live browser dashboard:
import asyncio
import nu
import nustd
class DB(nu.Shape):
a = nustd.kv.IntRef.slot()
b = nustd.kv.IntRef.slot()
class Dashboard(nustd.ui.Page):
out = nustd.ui.TextRef.slot()
class App(nustd.ui.Index):
pages = nustd.ui.Pages({"/": Dashboard})
# compute a + b and render into the dashboard text block
compute = DB.a.set(2) >> DB.b.set(5) >> Dashboard.out.set(DB.a + DB.b)
# assemble: rocksdb-backed, served over the browser
app = nu.With(
nustd.kv.rocksdb_navigator(".dbsum"),
nustd.ui.server(nustd.kv.auto_flow_atomic(compute)),
)
asyncio.run(nu.arun(app))Dashboard.out is a Ref. DB.a is a Ref. One lives in RocksDB, the other in a browser tab. .set(...) on either is the same interaction.
Same primitive, different substrate. One Ref for any resource, one Interaction for any op. Nu doesn't care what the backend is.
What falls out
Because Refs are just names and Interactions are just descriptions, the runtime is free to persist them, replay them, ship them across the network, batch them into a transaction, run them on another machine. Persistence, reactivity, atomicity, observability, and distribution are not features Nu has. They are what falls out of naming interactions instead of executing them.
The same lines that put a + b on a dashboard can, without changing shape:
- Persist across restarts. The KV slot is already durable.
- Re-render live on input changes. Wrap in a
Reactinteraction. - Handle terabytes. Shard the KV Fabric. The Refs do not notice.
- Run distributed across a cluster. Bind through
nustd.cluster. The Refs do not notice.
For the algebra underneath (atoms, kinds, forms), see the Explanation section once it lands. For now, pick a card above and start building.
Nu STD
Batteries included. 26 modules ship on top of the kernel.
Twelve are fabrics. Bind one and every Ref inside the bracket reaches a real system.
The rest re-surface Python's standard library. Call them anywhere, no binding. Same names, same call shape as the stdlib, except a call hands back a tree, not a value. So it composes into the rest of the program.
| Module | What | Looks like |
|---|---|---|
nustd.kv | Persistent state. | State.movies.append(m) |
nustd.ui | Reactive web UI. | Dashboard.count.set_value(n) |
nustd.cluster | Cluster compute. | Teleport(op, target="gpu") |
nustd.llm | OpenAI-compatible chat. | Bot.chat(prompt="…") |
nustd.mem | In-memory state. | users.age.set(12) |
nustd.proxy | Fabrics over the network. | InvisiblesProxy(Nav, address=...) |
nustd.http | Nu meets the web. | Solana.get_slot() |
nustd.service | Python objects as Refs. | Calc.add(a=2, b=3) |
nustd.cc | Claude Code as a Ref. | Agent.ask(prompt="…") |
nustd.mp | Local parallel execution. | Teleport(op, target=0) |
nustd.mp_pool | A pool of workers, as a fabric. | Teleport(body, worker=w) |
nustd.ws_server | Serve websocket clients. | SessionFor(body=arm) |
nustd.math | Math functions and constants. | math.sqrt(2) |
nustd.cmath | complex and its companions. | cmath.polar(z) |
nustd.random | The global RNG. | random.randint(1, 6) |
nustd.time | The process clock. | time.monotonic() |
nustd.datetime | Dates, times, deltas, timezones. | datetime.date.today() |
nustd.itertools | Iterator building blocks. | itertools.product(a, b) |
nustd.functools | A fold over a stream. | functools.reduce(Add, xs) |
nustd.asyncio | The non-blocking sleep. | asyncio.sleep(1) |
nustd.logging | Loggers and level shortcuts. | logging.getLogger(__name__) |
nustd.pathlib | Lexical path operations. | pathlib.Path.of("a", "b") |
nustd.uuid | UUIDs, all four versions. | uuid.uuid4() |
nustd.decimal | Exact decimal arithmetic. | Decimal.of("0.1") |
nustd.fractions | Exact rational arithmetic. | Fraction.of(1, 3) |
nustd.fin | Nu's own financial types. | Percentage.of(75.5) |