Quickstart: 5 Minutes
What you’re about to do
Now we do together: Two modules. Two nodes. One counter.
You run them on the same machine. They find each other, exchange an increment, and both agree the counter is 2.
You write zero networking code. Numax handles it.
What you need
- Rust with the
wasm32-unknown-unknowntarget - Git
rustup target add wasm32-unknown-unknownThat’s it.
Step 1 - Get Numax
git clone https://github.com/GianIac/numaxcd numaxcargo build --releaseIf you want, you can set nx as an environment variable so every command stays short:
export NX=./target/release/nxNow on every command uses
$NX.
Step 2 - Build the example
cd examples/distributed_countercargo build --release --target wasm32-unknown-unknowncd ../..You now have a .wasm module at:
examples/distributed_counter/target/wasm32-unknown-unknown/release/distributed_counter.wasmA counter that increments by 1 every time it runs.
Step 3 - Run two nodes
Open two terminals in the numax/ root.
In both of them, set the variable again:
export NX=./target/release/nxexport WASM=examples/distributed_counter/target/wasm32-unknown-unknown/release/distributed_counter.wasmTerminal 1 - Node A:
$NX run $WASM \ --listen 0.0.0.0:9000 \ --peer 127.0.0.1:9001 \ --datastore-path ./data-a \ --wait-before-run 1500ms \ --settle-for 5s \ --print-gcounter counter:visits \ -vTerminal 2 - Node B:
$NX run $WASM \ --listen 0.0.0.0:9001 \ --peer 127.0.0.1:9000 \ --datastore-path ./data-b \ --wait-before-run 1500ms \ --settle-for 5s \ --print-gcounter counter:visits \ -vStart them within a few seconds of each other.
Step 4 - Watch it converge
After ~6 seconds, both terminals print:
counter:visits = 2Node A incremented once. Node B incremented once. They found each other, exchanged their state, and converged to the truth - without you doing anything.
What just happened?
Node A Node B | | +-- increment → local slot = 1 +-- increment → local slot = 1 | | +-- broadcast to B ────────────> +-- receive A's slot | | +<──────────── broadcast to A ────+ | | +-- merge: sum(1, 1) = 2 +-- merge: sum(1, 1) = 2 | |"counter:visits = 2" "counter:visits = 2"This is a GCounter - a grow-only CRDT. Each node owns its own slot. The total is the sum of all slots. Merging is just taking the max per slot. No coordinator. No conflict. Always converges.
Your .wasm module called exactly one function:
gcounter::inc("counter:visits", 1);Everything else - networking, sync, persistence, merge - was Numax.
Clean up & run again
rm -rf ./data-a ./data-bGCounter state is durable. Without removing the data directories the next run continues from where it left off.
Now try adding more nodes, go from 2 to 3 or 6 ! Just add more --peer flags and watch them all converge.
Want to go further?
If you want to go wild, the examples directory has everything - pick one, read 100 lines of Rust, and you’ll understand exactly what Numax does:
| Example | What it shows |
|---|---|
distributed_counter | GCounter - grow-only replicated counter |
distributed_status | LWW-Register - last writer wins |
distributed_settings | LWW-Map - replicated config map |
distributed_tags | ORSet - add/remove tags, no conflicts |
distributed_comments | RGA - ordered replicated comment stream |
distributed_inventory | restock / sale / return on a shared SKU |
distributed_chat | local chat log with the key-value API |