The libraries
Two open-source TypeScript libraries. MIT. Use either alone, or compose them — independent of any runtime.
Observe every mutation to a live document — anywhere in the tree — and encode it as a structured op stream. Not a subtree, not a virtual DOM: the real browser DOM, from <html> down. Virtualize and stream it between any source and any number of clients.
TypeScript · MIT · @remdom/dom + 4 sibling packages
Repo →
Live mirror demo →
A running JS program is a state machine. Capture the inputs that cross a leader runtime's event loop — clicks, timers, network responses, reads of Math.random and Date.now — and apply them on a follower. Same code, same state. No re-execution of business logic on the follower.
TypeScript · MIT · v0.5.7
Repo →
Live mirror demo →
┌──────────────────────┐ ┌──────────────────────┐
│ leader runtime │ │ follower runtime │
│ │ │ │
│ patches/* ──emit──►│ ops │◄──apply── player.ts │
│ recorder.ts │═══════════════════►│ │
│ │ codec │ │
└──────────────────────┘ └──────────────────────┘
Diagram from the remjs README, MIT.
The platform: managed remdom peers
The libraries handle the protocol. The platform manages the peers that speak it: long-lived listener servers, source-DOM hosts, and programmatic clients that devs spin up, connect to, and tear down on demand. The unit is a peer. The workflow is lifecycle-driven.
What a peer is
- Source peer. A managed remdom source — Puppeteer-backed if you want headless Chromium, or BYO source connected over WebSocket. Streams mutation ops downstream.
- Listener peer. A managed
@remdom/server instance. Accepts incoming connections, fans out ops, brokers between source and clients. Long-lived.
- Client / agent peer. Programmatic remdom clients running on platform infra — useful when you want the agent or applier next to the source, not in a user's browser.
The lifecycle
// spin one up — stays warm for hours, days, weeks
const listener = await remdom.peers.create({
role: "listener",
region: "us-east",
});
// connect a source to it, then point clients at the listener
await listener.attach({ source: sourcePeerId });
// later, when you don't need it
await listener.shutdown();
What you get with the platform
- Long-lived, addressable peers. Each peer has a stable ID and endpoint. Reconnect across sessions.
- Auth & project scoping. Per-peer JWTs, role-scoped (source, listener, observer, agent).
- Routing across networks. Connect peers without setting up your own relay or punching NAT.
- Recording & replay. Capture the op stream from any peer; re-apply later for debugging or audit.
- Provider adapters. Wire Anthropic or OpenAI agents into an agent-peer in a couple of lines.
- Libraries stay MIT. Self-host the same thing if you'd rather. Platform is the operational shortcut, not a lock-in.
Pricing TBD at GA — likely billed per peer-hour with a free tier. Libraries stay MIT regardless.