@hop-mesh/wasm
v0.0.2
Published
The wasm/browser binding of hop-core: a real Node<JsStore> (host-owned storage via a StoreBridge), JS-consumable via wasm-bindgen. Its primary consumer today is the browser swarm simulator.
Readme
Hop is a delay-tolerant mesh: end-to-end encrypted datagrams that hop device to device, over BLE, Wi-Fi, and the internet, until they reach the person you meant. Held, never dropped.
@hop-mesh/wasm is hop-core compiled to WebAssembly: a WasmNode is a genuine Hop node, the same
store-and-forward, crypto, and epidemic routing that runs on a phone, now running in a tab. JS owns
the bearer (it decides who's in range, pumps bytes across links, reads the inbox) and owns the storage
(bundles live in a host store you provide, not in wasm memory, so a tab full of nodes doesn't OOM). It
powers the live browser swarm simulator, where every dot on the map is a real instance of the protocol.
Install
npm install @hop-mesh/wasmTwo nodes, one link
Give each node a 32-byte identity seed and a synchronous StoreBridge (a Map here; SQLite-on-OPFS in a
real Worker). JS pumps each node's drained packets into the other, and A sends B a message:
import { WasmNode } from 'hop-wasm'
// A minimal in-memory host store. In a browser Worker this is SQLite over an OPFS sync-access handle.
const bridge = () => {
const seen = new Map(), held = new Map(), kv = new Map()
const hex = u => [...u].map(b => b.toString(16).padStart(2, '0')).join('')
return {
put(id, data, exp) { const h = hex(id); if (seen.has(h)) return false; seen.set(h, exp); held.set(h, data.slice()); return true },
get: id => held.get(hex(id)), remove(id) { const h = hex(id), d = held.get(h); held.delete(h); return d },
seen: id => seen.has(hex(id)), seenExpiry: id => seen.get(hex(id)), contains: id => held.has(hex(id)),
have() { const o = new Uint8Array(held.size * 32); let i = 0; for (const h of held.keys()) { o.set(Uint8Array.from(h.match(/../g).map(x => parseInt(x, 16))), i); i += 32 } return o },
prune(now) { for (const [h, e] of seen) if (e <= now) { seen.delete(h); held.delete(h) } },
kvPut: (k, v) => kv.set(k, v.slice()), kvGet: k => kv.get(k), kvRemove: k => kv.delete(k),
kvList() { return new Uint8Array() },
}
}
const seed = () => crypto.getRandomValues(new Uint8Array(32))
const a = new WasmNode(seed(), bridge())
const b = new WasmNode(seed(), bridge())
let now = 1_700_000_000_000
for (const n of [a, b]) { n.tick(now); n.publish_prekey() }
a.connected(1, true) // A dialed
b.connected(1, false) // B accepted
a.send(b.address, new TextEncoder().encode('meet at the ridge'))
for (let i = 0; i < 400; i++) {
for (const p of a.drain()) if (p.link === 1) b.receive(p.link, p.data)
for (const p of b.drain()) if (p.link === 1) a.receive(p.link, p.data)
for (const msg of b.inbox()) console.log(new TextDecoder().decode(msg.body))
now += 100; a.tick(now); b.tick(now)
}send is the untraceable path (§39); send_traced is the opt-in directed path. drain_transfers
surfaces each bundle crossing each link (so a visualizer can color the route), and the hps:// channel
methods (register_channel, channel_subscribe, channel_publish, take_channel) carry group posts.
The shape of it
- Poll-model.
tick(nowMs)the clock,drain()outbound packets to the bearer, and pollinbox(). Inbox polling is non-destructive; callaccept_inbox(id)after local persistence. Nothing pushes asynchronously. - Host-owned storage. You implement
StoreBridge(put/get/remove/seen/have/prune plus a small kv side store with atomickvBatch). The core reads and writes it; bundles never live in wasm memory. - JS is the bearer.
connected/receive/drain/disconnectedmove opaque bytes over whatever transport you have (a mock link, WebRTC, a WebSocket). The core owns all crypto. - Deterministic identity. A node built from the same 32-byte seed keeps its address across reloads.
Status
Prototype. The full WasmNode surface is exercised end to end against this exact wasm build by the
browser swarm simulator (15 real-world scenarios, each delivered and ACKed). The published bundle is the
nodejs/web wasm-pack output (hop_wasm.js + hop_wasm_bg.wasm + types).
The Hop family
@hop-mesh/wasm is the browser binding of the core, a peer of the C ABI. The protocol core is
hop-core; the C ABI is
libhop. The language SDKs:
node ·
python ·
go ·
ruby ·
crystal ·
elixir ·
apple ·
android.
License
FSL-1.1-ALv2: source-available, and converts to Apache-2.0 after two years. The SDKs that bind this are Apache-2.0.
