Peers talk directly over WebRTC data channels. Connecting everyone to everyone does not scale, so mistlib bounds each node's connection count and picks which peers to hold by 3D proximity: the nearest peers inside your area of interest, plus the nearest one in each direction so no direction is left uncovered. Messages for peers outside that set are relayed across the resulting overlay.
Written in Rust. Ships as a native library for engines and desktop apps, and as WebAssembly for the browser.
| Bounded fan-out | ~30 connections per node (configurable), however crowded the world gets |
| Area of interest | You hear about the nodes near you, not about everyone |
| Serverless signaling | Peers meet over Nostr relays — no signaling server to run |
| Multiple rooms | Several rooms per process, each with its own overlay |
| Shared storage | Content-addressed blobs replicated between peers, persisted to OPFS |
| Media | Audio and video tracks over the same WebRTC connections |
npm install @tik-choco/mistlibNo build step? Import a pinned version straight from a CDN:
import init, { init_with_config } from "https://cdn.jsdelivr.net/npm/@tik-choco/mistlib@0.6.0/mistlib_wasm.js";For engines and desktop apps, grab a native build from Releases:
| Platform | Asset | Library |
|---|---|---|
| Linux | mistlib-native-linux-x86_64-<version>.zip |
libmistlib.so |
| Windows | mistlib-native-windows-x86_64-<version>.zip |
mistlib.dll |
| macOS | mistlib-native-macos-aarch64-<version>.zip |
libmistlib.dylib |
| Web | mistlib-wasm-<version>.zip |
pkg/ — the same wasm build, to vendor |
Important
Pin a version. The overlay wire protocol may change between releases, and every peer in a deployment must run the same one.
Unity, Python and JavaScript wrappers with runnable samples live in mistlib-examples.
import init, {
init_with_config, join_room, update_position, send_message,
register_event_callback, get_version, get_build_info, MistEvent, Delivery,
} from "@tik-choco/mistlib";
await init();
console.log("MistLib", get_version(), JSON.parse(get_build_info()));
init_with_config("alice", JSON.stringify({
signaling: {
mode: "nostr",
nostr: {
relays: ["wss://relay.example.com"],
inviteSalt: "my-app-2026", // peers sharing a salt and code find each other
inviteCode: "a-shared-secret", // pick your own — the built-in values are placeholders
},
},
aoiRange: 20.0,
}));
register_event_callback((eventType, fromId, payload, roomId) => {
if (eventType === MistEvent.PeerConnected) console.log("peer joined:", fromId);
});
join_room("lobby");
update_position(0.0, 1.5, 0.0);
send_message("", new TextEncoder().encode("hello"), Delivery.Reliable); // "" = broadcastCall update_position as your avatar moves — it is what drives topology and area of interest.
| Call | Does |
|---|---|
init_with_config(id, json) |
Create the node. Returns false if the config is invalid |
join_room(id) / leave_room_id(id) |
Enter or leave a room |
update_position(x, y, z) |
Report where you are |
send_message(target, bytes, delivery) |
Send to one peer, or to the room with "" |
get_neighbors() / get_all_nodes() |
Current view of the room, as JSON |
storage_add(name, bytes) / storage_get(cid) |
Content-addressed storage |
register_event_callback(fn) |
Peer, area-of-interest and room events |
get_version() / get_build_info() |
Get the release version and the exact loaded build |
get_build_info() returns a JSON string containing version, commit, dirty, profile, and
target. It identifies the source commit and whether tracked local changes were present.
Without an explicit room, these act across every room you have joined: send_message("", …)
broadcasts to all of them and get_neighbors() merges their views. Multi-room applications
should use the *_in_room variants, which take a room id.
The native library offers the same operations over a C ABI with (pointer, length) pairs, with
two differences: there is no get_neighbors / get_all_nodes (that data arrives as events
instead), and only update_position and send_message have *_in_room variants. See
mistlib-native/src/ffi.rs.
Native get_version / get_build_info write into caller-owned buffers. If a buffer is too small,
they copy nothing and return the required byte length, so a zero-length call can query the size.
Delivery modes — Delivery.Reliable (0) arrives in order; Delivery.UnreliableOrdered
(1) may drop but never reorders; Delivery.Unreliable (2) may drop or reorder, for the
lowest latency. Event ids come from the MistEvent enum the same way.
A JSON object passed to init_with_config, or to set_config later. Anything you omit keeps its
default, and get_config() dumps the configuration currently in effect, which is the quickest way
to see every key and what it is set to.
| Key | Default | Meaning |
|---|---|---|
signaling.mode |
"nostr" |
"nostr" or "websocket" |
signaling.nostr.relays |
(empty) | Relays used for peer discovery. Left empty, mistlib fetches a relay list over the network at startup — set this to keep discovery under your control |
signaling.nostr.inviteSalt / inviteCode |
placeholders | Shared secret scoping discovery to your app |
aoiRange |
10.0 |
Radius, in world units, that counts as near |
maxConnectionCount |
30 |
Upper bound on simultaneous peer connections |
hopCount |
2 |
Maximum relay hops for a message |
maxMessageBytes |
65536 |
Rejection threshold for a single message |
storageMaxCapacityMb |
8192 |
Local budget for content-addressed storage |
iceServers |
3 public STUN servers | WebRTC ICE servers. Replace to add your own TURN |
The default iceServers are three public STUN servers, spread across operators so a peer
behind a network that blocks one still gets a reflexive candidate. STUN cannot get through
symmetric NAT, though — that needs a TURN relay, and relaying costs real bandwidth, so none
ships as a default. Supply your own to cover it:
{
"iceServers": [
{ "urls": ["stun:stun.l.google.com:19302"] },
{ "urls": ["turn:turn.example.com:3478"], "username": "user", "credential": "pass" }
]
}Without TURN, expect a small share of peer pairs to fail to connect.
The toolchain version is pinned in rust-toolchain.toml, so rustup picks it up for you.
cargo build --release -p mistlib-native # .so / .dll / .dylib
cd mistlib-wasm && wasm-pack build --target web --release # pkg/A family of browser apps shares state peer-to-peer using mistlib — TC Space, TC Town, TC Chat, TC Storage and more. Browse them from TC Home.
Under active development; the API is not yet stable. Releases are tagged, and the wire protocol may change between them.