nustack

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

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 React interaction.
  • 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.

ModuleWhatLooks like
nustd.kvPersistent state.State.movies.append(m)
nustd.uiReactive web UI.Dashboard.count.set_value(n)
nustd.clusterCluster compute.Teleport(op, target="gpu")
nustd.llmOpenAI-compatible chat.Bot.chat(prompt="…")
nustd.memIn-memory state.users.age.set(12)
nustd.proxyFabrics over the network.InvisiblesProxy(Nav, address=...)
nustd.httpNu meets the web.Solana.get_slot()
nustd.servicePython objects as Refs.Calc.add(a=2, b=3)
nustd.ccClaude Code as a Ref.Agent.ask(prompt="…")
nustd.mpLocal parallel execution.Teleport(op, target=0)
nustd.mp_poolA pool of workers, as a fabric.Teleport(body, worker=w)
nustd.ws_serverServe websocket clients.SessionFor(body=arm)
nustd.mathMath functions and constants.math.sqrt(2)
nustd.cmathcomplex and its companions.cmath.polar(z)
nustd.randomThe global RNG.random.randint(1, 6)
nustd.timeThe process clock.time.monotonic()
nustd.datetimeDates, times, deltas, timezones.datetime.date.today()
nustd.itertoolsIterator building blocks.itertools.product(a, b)
nustd.functoolsA fold over a stream.functools.reduce(Add, xs)
nustd.asyncioThe non-blocking sleep.asyncio.sleep(1)
nustd.loggingLoggers and level shortcuts.logging.getLogger(__name__)
nustd.pathlibLexical path operations.pathlib.Path.of("a", "b")
nustd.uuidUUIDs, all four versions.uuid.uuid4()
nustd.decimalExact decimal arithmetic.Decimal.of("0.1")
nustd.fractionsExact rational arithmetic.Fraction.of(1, 3)
nustd.finNu's own financial types.Percentage.of(75.5)

Go deeper

On this page