ZMQ client library for browsers over WebSocket. Implements ZMTP 3.x NULL and PLAIN security over the ZWS 2.0 framing protocol, with optional LZ4 dictionary compression.
Supports connect-side WebSocket sockets for REQ/REP, PUB/SUB, XPUB/XSUB, PUSH/PULL, DEALER/ROUTER, PAIR, CLIENT/SERVER, RADIO/DISH, SCATTER/GATHER, PEER, and CHANNEL. Security mechanisms are NULL and PLAIN.
Published on JSR as @zeromq/omq.
deno add jsr:@zeromq/omqimport { initLz4, Sub } from "@zeromq/omq";
await initLz4();
const sub = new Sub();
sub.subscribe("market.");
sub.connect("lz4+ws://broker.example.com/sub");
for await (const msg of sub) {
console.log(msg.string(0), msg.string(1));
}import { Message, Req } from "@zeromq/omq";
const req = new Req();
req.connect("wss://broker.example.com/req");
const reply = await req.send(new Message("hello"));
console.log(reply.string(0));import { Message, Push } from "@zeromq/omq";
const push = new Push();
push.connect("wss://broker.example.com/push");
await push.send(new Message("event", JSON.stringify({ ts: Date.now() })));For lz4+ws:// URLs, call initLz4() once before connecting. An optional
pre-shared dictionary improves compression ratio on small messages:
import { initLz4, Sub } from "@zeromq/omq";
await initLz4();
const dict = new Uint8Array([...]); // pre-shared dictionary
const sub = new Sub({ lz4Dict: dict });
sub.subscribe("");
sub.connect("lz4+ws://broker.example.com/sub");TypeScript senders reject individual LZ4 parts at the 1 GiB multi-block boundary. Split huge payloads at the application layer.
Use PLAIN when the WebSocket server requires username/password auth. Prefer
wss:// because PLAIN itself does not encrypt credentials.
const push = new Push({
plain: { username: "alice", password: "secret" },
});
push.connect("wss://broker.example.com/push");Use wss:// for transport privacy. Browser JavaScript cannot choose or provide
a client certificate/key for WebSocket; mTLS has to be handled by the browser
profile, operating system, or a TLS-terminating proxy. It is not a portable
application-level auth mechanism for this library.
For browser clients, prefer wss:// plus PLAIN with a scoped token or JWT as
the password when the server needs to authenticate the application peer.
CURVE is not implemented in omq.ts for now. Over wss://, it would mostly
duplicate TLS encryption and add a larger ZMTP crypto handshake surface. CURVE
over ws:// could make sense later for ZMQ-style key identity without TLS
client-certificate deployment, but it is intentionally out of scope today.
Sockets reconnect by default after peer-side close or WebSocket error. Current subscriptions and group joins replay after reconnect.
const sub = new Sub({
reconnectInitialDelayMs: 100,
reconnectMaxDelayMs: 5000,
receiveHighWaterMark: 1000,
sendHighWaterMark: 1000,
onError: (error) => console.error(error),
});Browser WebSocket does not expose inbound TCP backpressure. If
receiveHighWaterMark is set and the receive queue is full, omq.ts calls
onError, closes the affected connection, and reconnects unless reconnects are
disabled. The overflow is reported as connection failure instead of a silent
queue drop.
Run npm run test:interop from this repo to start real omq-tokio WebSocket
peers from the crates.io test fixture and verify NULL, PLAIN, and lz4+ws://
traffic.
Run npm run test:browser for the headless Firefox browser interop test. It
starts a Rust WebSocket peer, serves a temporary browser bundle, and verifies
the same paths through native browser WebSocket and WASM LZ4, including
wss://.