Install
Net ships as one release across five surfaces: a Rust crate, native Node and Python bindings built from that same crate, a Go module over its C ABI, and the C ABI itself. They are not independent ports. A version number means the same code underneath, whichever package you install.
What you are installing
Two layers, and knowing which one you have saves an afternoon:
- The core binding — the bus, the mesh transport, the storage stack. In Rust that is the crate; in Node and Python it is the native addon.
- The ergonomic SDK — typed channels, node builders, RPC helpers. A thin layer over the core, published separately.
Some surfaces reach features only through the core layer, never the SDK. That is not a bug to work around and it is the most common way to be wrong about Net in Node and Python: if an import fails, check which layer the symbol lives on before concluding the feature is missing.
Versions move together
Every package publishes at the same version from the same commit. The current published release is 0.33. Pin the same version across layers — a core at one version and an SDK at another is a combination nobody built or tested.
What an install gives you
Whichever surface you start on:
- the event bus — publish, poll, filters, shards;
- mesh transport with NAT traversal — peer discovery, encrypted sessions, identity-bound routing;
- the storage stack — RedEX logs, CortEX folds, NetDB queries, Dataforts blobs;
- daemon authoring through MeshOS;
- typed RPC through nRPC.
You do not have to use any of the higher layers to use the bus. They are there when you need them.
One thing to expect on your first run
The default transport is memory, and memory discards events after counting them. A first program that publishes and then tries to read the event back will not fail — it will hang, waiting for something that is never coming. Verifying that the bus accepted an event is the right first check, and every language's instructions below end with exactly that.
Receiving events back needs an adapter that retains them (Redis, JetStream) or the mesh transport between two nodes. That is a separate decision, not a default.
Deck, the operator TUI
A separate binary that nothing else depends on. Install it from whichever ecosystem is convenient — it is the same tool either way:
cargo install net-deck
npm i -g @net-mesh/deck
pip install net-deckSee the Deck reference for the tabs and the signed admin surface.
Install it — Python
pip install net-mesh # the native binding
pip install net-mesh-sdk # the ergonomic SDK, if you want typed channelsBoth publish at 0.33. The distribution is named net-mesh but the import
stays net — symmetry with the Rust crate, and so existing code keeps working.
The SDK installs as net-mesh-sdk and imports as net_sdk. Python 3.10 or
newer.
Built with maturin, shipping prebuilt wheels for the same targets as the Node
binding, with a source build as fallback.
What is peculiar about Python here
Two names per layer, and they do not match. pip install net-mesh →
import net; pip install net-mesh-sdk → import net_sdk. Four strings for two
packages is a lot to keep straight, and a ModuleNotFoundError here usually means
the right package is installed under the other name.
The exported bus class is Net, not EventBus. EventBus is the internal
Rust type the binding is built from, not a Python export.
Several surfaces live only on net, never on net_sdk — payments is the
clearest case. Check the lower layer before concluding a feature is absent.
A stale binding fails as a missing attribute, not a missing module. If net
imports but a symbol net_sdk needs is gone, you have two versions installed;
check both before reading anything else.
Verify it worked
from dataclasses import dataclass
from net_sdk import NetNode
@dataclass
class Hello:
msg: str
def main() -> None:
with NetNode(shards=1) as node:
ch = node.channel("hello/world", Hello)
ch.publish(Hello(msg="hello, mesh"))
# Counts at the PRODUCER boundary: accepted, not received or stored.
stats = node.stats()
assert stats.events_ingested == 1, "the bus did not accept the event"
print(f"accepted: ingested={stats.events_ingested}")
if __name__ == "__main__":
main()Expect one line, accepted: ingested=1, and a clean exit. The context manager
handles shutdown; leaving it out is how a Python program ends up with a drain
worker still holding events.
Next: the Python SDK spine.