The peer-to-peer mesh underneath MyOwnLLM — pure Rust, embed it in anything.
Quick start · Protocol · Architecture · Connection engine · Contributing · Releases
myownmesh # headless daemon + CLI (bin: crates/myownmesh)
myownmesh-core # library — runtime, engine, protocol (lib: crates/myownmesh-core)
myownmesh-gui # desktop GUI (Tauri + Svelte 5) (app: gui/)
Plus two supporting library crates the daemon and embedders share:
myownmesh-signaling # Nostr signaling driver + in-process LocalBroker
myownmesh-updater # self-update with configurable release feed
One command — detects platform, fetches the binary from
GitHub Releases,
verifies SHA-256, drops myownmesh on your PATH.
# macOS / Linux
curl -fsSL https://raw.githubusercontent.com/mrjeeves/MyOwnMesh/main/scripts/install.sh | sh# Windows
irm https://raw.githubusercontent.com/mrjeeves/MyOwnMesh/main/scripts/install.ps1 | iexThe installer writes to /usr/local/bin (or ~/.local/bin if not
writable) on Unix and %LOCALAPPDATA%\Programs\MyOwnMesh on
Windows, and adds the directory to PATH if it isn't already there.
Pass --serve (Unix) or -Serve (Windows) to launch the daemon
once the install finishes. myownmesh update self-applies later
releases against the same artifacts.
Prefer a tarball directly? The portable binaries
(myownmesh-<platform>.{tar.gz,zip} + .sha256 sidecar) are on
Releases for the
five platforms in the matrix.
Pick the persona that matches what you're doing — none of them depend on each other, so any combination works on the same box.
git clone https://github.com/mrjeeves/MyOwnMesh
cd MyOwnMesh
just setup # Rust toolchain via rustup
cargo install --path crates/myownmesh # daemon + CLI on $PATH
# or run without installing:
cargo run -p myownmesh -- serve
# or with debug logging:
just serve # MYOWNMESH_LOG=debug cargo run -p myownmesh -- servePre-built installers (.deb / .AppImage / .dmg / .msi /
.exe) ship in the same GitHub Releases
as the daemon. The GUI auto-spawns the daemon as a child process,
so installing only the GUI bundle gets you both.
From source, two shells:
just serve # one shell — daemon + control socket (with debug logging)
just dev # another shell — Tauri GUI with hot reloadOr without just:
cargo run -p myownmesh -- serve # one shell
cd gui && pnpm install && pnpm tauri dev # another shellFor a release build of the GUI: cd gui && pnpm tauri build.
The library crates aren't on crates.io yet — pull them as git dependencies pinned to a release tag. Cargo dedupes git deps by URL, so both crates resolve out of the same checkout:
[dependencies]
myownmesh-core = { git = "https://github.com/mrjeeves/MyOwnMesh", tag = "v0.1.0" }
myownmesh-signaling = { git = "https://github.com/mrjeeves/MyOwnMesh", tag = "v0.1.0" } # Nostr driver
tokio = { version = "1", features = ["full"] }use myownmesh_core::{Mesh, MeshConfig, NetworkConfig, TopologyMode};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mesh = Mesh::open(MeshConfig::load().unwrap_or_default()).await?;
let net = mesh.join(NetworkConfig {
id: "home".into(),
network_id: "my-cool-mesh".into(),
label: "Home mesh".into(),
topology: TopologyMode::default(), // Ring
signaling: Default::default(), // Nostr defaults
stun_servers: Default::default(),
turn_servers: Default::default(),
roster_path: None,
auto_approve: false,
}).await?;
let _nostr = myownmesh_core::engine::attach_nostr(&net.state());
let mut events = mesh.events();
while let Ok(event) = events.recv().await {
println!("{event:?}");
}
Ok(())
}Three other supported dependency shapes:
# Track the latest work (no API stability guarantees between commits).
myownmesh-core = { git = "https://github.com/mrjeeves/MyOwnMesh", branch = "main" }
# Pin to an exact commit for build reproducibility.
myownmesh-core = { git = "https://github.com/mrjeeves/MyOwnMesh", rev = "86e6736" }
# Sibling-directory monorepo / vendored checkout.
myownmesh-core = { path = "../MyOwnMesh/crates/myownmesh-core" }Override MYOWNMESH_HOME=~/.youapp/mesh to keep your app's identity
- rosters under its own directory tree (defaults to
~/.myownmesh/). Narrative walkthrough:docs/QUICKSTART.md.
Two ephemeral peers exchange a full handshake + a typed channel message in-process via the LocalBroker — no Nostr relays, no network, no installation:
git clone https://github.com/mrjeeves/MyOwnMesh
cd MyOwnMesh
cargo test -p myownmesh-core --test two_peer_handshake -- --nocaptureThe runnable examples cover the three common embedder shapes:
cargo run --example two_peer_chat -p myownmesh-core # typed channel
cargo run --example echo_rpc -p myownmesh-core # generic RPC
cargo run --example roster_demo -p myownmesh-core # approve / persist / reconnectjust setup # Rust toolchain via rustup (idempotent)
just build # cargo build --workspace
just test # cargo test --workspace (98 tests today)
just check # fmt + clippy -D warnings + test
just fmt # cargo fmt --all
just lint # cargo clippy --workspace --all-targets -- -D warningsCI runs fmt --check, clippy -D warnings, and the full test suite
across linux-x86_64, macos-aarch64, windows-x86_64. See
CONTRIBUTING.md for code conventions, the
protocol-message checklist, and the topology-mode checklist.
- ed25519 mutual auth, with eyeballs. Every peer encounter exchanges a
hello+auth_responsewhere each side signs the other's nonce undermyownmesh-mesh-auth-v1:. A 6-char[a-z0-9]verification code rides along for out-of-band confirmation ("the code I see matches what you read me"). Approved peers land in a per-network roster and skip the prompt on reconnect. - A 7-tier reconnection ladder. Steady → Wake probe → ICE watchdog → ICE restart → Re-handshake → Room rejoin → Stop-and-start. Each tier is the cheapest action that still recovers from the failure class above it. Every tunable constant is documented in
CONNECTION-ENGINE.mdwith the field bug it was discovered through. - Trystero-wire-compatible Nostr signaling. Same room-handle derivation as JS Trystero v0.24 (
SHA-256(app_id || ":" || network_id)), same deterministic relay shuffle. Five published-fix patches against@trystero-p2p/coreare baked in natively — catalogued incrates/myownmesh-signaling/src/upstream.rsso upstream-tracking is a code-level diff, not a patches/ folder. - Selectable topologies. Ring (default — sorted-lex with 2 neighbours + shortcuts), Star (explicit hub), FullMesh (everyone to everyone). All built on the same shelving primitive; both sides of every pair run the same pure-function selector over the same sorted input, so the result is symmetric without coordination.
- Typed pub/sub + generic RPC over one data channel.
Channel<T>is a typed publish/subscribe channel keyed by name.Rpc::call/serve/call_stream/serve_streamis the generic request/response surface. Embedders define their own message types — the mesh treats payloads opaquely. - Embed without the GUI or updater. The daemon, the library, and the desktop GUI are separate crates. An app embedding
myownmesh-coredoesn't pull in the HTTP self-updater or the Tauri stack. The GUI itself is a client of the daemon (over a local control socket) so crashing the UI never disturbs the running mesh. - One identity, many networks. Per-device long-lived ed25519 keypair under
~/.myownmesh/.secrets/identity.json(0600). Per-network rosters at~/.myownmesh/mesh/rosters/{network_id}.json. Switching the active network swaps rosters but preserves identity.
myownmesh # start the daemon (alias for `serve`)
myownmesh serve # run the daemon in foreground
myownmesh identity show # print this device's id
myownmesh ctl status # query a running daemon
myownmesh ctl networks list
myownmesh update check # poll the release feed
myownmesh config edit # open ~/.myownmesh/config.json in $EDITORDaemon reads ~/.myownmesh/config.json (auto-created on first edit;
sensible defaults until then), joins every network listed there,
attaches the Nostr signaling driver per network, and listens for
myownmesh ctl … clients on a local socket
(~/.myownmesh/daemon.sock on Unix, named pipe on Windows). Full
reference in crates/myownmesh/README.md.
A Tauri + Svelte 5 frontend in gui/, running as a client
of the daemon — talks to myownmesh serve over the local control
socket, never embeds myownmesh-core directly. Crashing the UI
never disturbs the running mesh.
- Node graph — self at the centre, peers laid out by topology, click a node for label / display suffix / RTT / capabilities. During pending approval the popup surfaces the per-session 6-char verification code as a tile for out-of-band confirmation.
- Approvals tab (default in Settings) — pending peer requests from every joined network flatten into one list with Approve / Deny inline.
- Networks — Status (topology selector + per-network rollup) · Connections (live peer table) · Roster (approved devices).
- Activity — unified event log: peer state transitions, phase changes, ICE / handshake / signaling diagnostics. Quiet toggle suppresses info-level chatter; warns and errors always land.
Layout / wire protocol in gui/README.md.
linux-x86_64 · linux-aarch64 (incl. Raspberry Pi 4 / 5) ·
macos-aarch64 · macos-x86_64 · windows-x86_64. The release
matrix builds and uploads bundles for all five. Linux is pinned to
Ubuntu 22.04 (glibc 2.35) so binaries run on Debian 12, Ubuntu
22.04+, and other distros still on glibc 2.35/2.36/2.38.
MyOwnMesh started as MyOwnLLM's
src/mesh-*.ts + src-tauri/src/mesh/ substrate. The connection
engine's seven tiers, the Trystero-patch catalogue, the 6-char
verification-code UX, the per-network roster model — all of it was
field-tested inside MyOwnLLM first, then lifted into pure Rust and
generalised so any app that wants a peer-to-peer substrate can
embed it without inheriting the LLM stack. See
ARCHITECTURE.md for the crate-by-crate
relationship to the original TypeScript modules.
docs/QUICKSTART.md — embedder walkthrough ·
docs/PROTOCOL.md — wire-protocol reference ·
docs/NETWORK-TYPES.md — open vs closed networks (role tiers, signed transitions, split + recovery) ·
ARCHITECTURE.md — crate layout, trust model, persistent state ·
CONNECTION-ENGINE.md — the 7-tier ladder, every tunable ·
CONTRIBUTING.md — setup, conventions, testing ·
RELEASE.md — cutting a release ·
gui/README.md — desktop GUI ·
Rustdoc: cargo doc --workspace --no-deps --open ·
LICENSE — MIT