Nexum is a WASM Component Model host runtime for web3 modules. It supervises guest components built against the nexum:host WIT world, giving each module a capability-gated view of the host: chain access over JSON-RPC, an allowlisted wasi:http outbound gate, a local key-value store, clocks, and structured logging, with fuel and memory limits enforced per module.
This repository is the leaf of the Nullis runtime stack: it carries no cross-repo dependencies. Downstream repositories (videre, shepherd) build on the SDK and the runtime in this repository.
The crates are not published to crates.io.
A consumer takes them as git dependencies pinned to a commit: see docs/sdk.md.
- Build your first module - from a clone to a running module of your own, in this repository or in yours.
- The module SDK - the module macro, the host traits,
Fault, and each helper module. - Testing - the module mocks in
nexum-sdk-testand the engine-side harness. - Component lifecycle, trigger system, and packaging - the manifest reference.
- RPC namespace design - the
chaininterface and the permitted JSON-RPC methods. - Production deployment - the operator handbook.
docs/adranddocs/design- the decision records and the design notes.
crates/nexum-runtime- the engine host: wasmtime embedding, supervisor, capability providers, metrics.crates/nexum-primitives- module and interface identity, content digests, and host allowlist patterns.crates/nexum-cli- the barenexumengine binary.crates/nexum-launch- shared launch surface (config loading, logging, presets).crates/nexum-sdk- the guest-side SDK modules build against.crates/nexum-sdk-test- SDK acceptance-test harness.crates/nexum-module-macros- proc-macros for module entrypoints.crates/nexum-tasks- task lifecycle and graceful shutdown.crates/nexum-world- single-source capability and fault-label vocabularies.modules/example- minimal reference module.modules/examples/- example modules (balance-tracker, http-probe, price-alert).modules/fixtures/- adversarial test fixtures (clock-reader, env-reader, flaky-bomb, fuel-bomb, log-bomb, memory-bomb, panic-bomb, slow-host, tick-recorder, topic-parity).wit/nexum-host- thenexum:hostWIT package.
The repository pins its toolchain via a Nix flake (Rust 1.96.0, matching CI):
nix develop # or `direnv allow` once
just build # engine + all guest wasms
just test # host engine unit tests
just ci # full CI series locally (fmt, clippy, doc, wasms, nextest, doctests)Without Nix, any Rust 1.96+ toolchain with the wasm32-wasip2 target, cargo-nextest, and just works.
export SEPOLIA_RPC_URL=https://ethereum-sepolia-rpc.publicnode.com # any Sepolia JSON-RPC URL
just run # builds the example module and runs the engine with itThe example module declares a block trigger on Sepolia, and it logs one line for each new block.
engine.dev.toml takes the RPC URL from SEPOLIA_RPC_URL, and the engine refuses to start while the variable is unset.
docs/tutorial-first-module.md continues from here to a module of your own.
The engine takes a component wasm and its component.toml (dependencies + config).
The manifest is mandatory: pass its path, or ship a component.toml next to the wasm.
Every manifest must declare a [dependencies] table; an empty one grants nothing.
Every manifest must also declare a [component].name that is not blank.
The engine uses the name as the state namespace, and it refuses a missing, empty, or whitespace-only name.
just run ends in this command:
cargo run -p nexum-cli -- --engine-config engine.dev.tomlengine.dev.toml is committed, and it declares the example as a [[modules]] entry, which is the path a deployment uses.
The engine also takes a wasm and a manifest as positional arguments, which boots that one module in place of the [[modules]] entries:
cargo run -p nexum-cli -- target/wasm32-wasip2/release/example.wasm modules/example/component.toml --engine-config engine.dev.tomlWith no --engine-config and no engine.toml in the working directory, the engine runs on defaults, which configure no chain.
A module that declares a block or event trigger needs its chain declared in engine.toml, or the engine refuses to boot.
A schedule trigger fires on a cron expression in UTC and needs no chain.
The smallest working stanza is:
[chains.11155111]
rpc_url = "http://localhost:8545"http(s):// URLs are not dialled at boot; ws(s):// URLs are.
The example module and the modules under modules/examples/ declare a block trigger on Sepolia, and engine.dev.toml carries the entry for it.
A module whose only trigger is a schedule, like the tick-recorder fixture, needs no chain.
Every [[modules]] entry in engine.toml carries digest = "sha256:<64 hex chars>", and the engine refuses to boot an entry without one.
nexum digest <artifact> prints that value, and it prints nothing else, so the line pastes into the key:
nexum digest target/wasm32-wasip2/release/example.wasmThe refusal also reports the digest of the bytes it read, so a first boot tells you the value to paste.
Set require_component_digest = false under [engine] to relax the requirement, which is what the committed engine.dev.toml does for a build that changes on every just build-module.
A component given on the command line instead of in a [[modules]] entry is exempt: there is no entry for a pin to sit on, and naming the path is the same authorization.
A manifest may also pin its own artifact with digest in [component].
Both pins are verified strictly against the loaded bytes before compilation, and a mismatch or a malformed pin refuses the boot naming which pin failed.
The two are independent, so the author pin never satisfies the operator requirement.
The default sibling component.toml lives in the same trust domain as the artifact, so an author-side pin closes accidental drift only.
[[modules]].digest does not on its own close a compromised artifact store.
It fixes the bytes of the .wasm, but the sibling component.toml stays inside the compromised directory, and that manifest is the sole HTTP allowlist, the [config] source, and the state-namespace selector.
Closing the gap needs an operator-owned manifest outside the artifact directory, named by the manifest key on the [[modules]] entry.
AGPL-3.0-or-later. See LICENSE.