One Rust daemon that shares prediction-market order books with every local app
pm-ws is our feed daemon, part of the Titus product suite, released under the MIT license. It holds live order books from the Limitless prediction market and shares them with local Rust, Python and TypeScript programs through shared memory.
- Client
- Chain Labs' own product, part of the Titus suite
- Service
- Data
- When
- August to October 2026
- Team
- Mihir
- Status
- Live
- Public proof
The problem
Every service that follows prediction-market books rebuilds the same feed. An odds provider or a prediction-market aggregator follows many order books at once. Each service that reads the books must handle reconnects, updates that arrive twice, missed messages, market resolutions and readers that fall behind.
When each service builds this handling on its own, each copy can reach a different answer about what the current book is.
What we built
pm-ws runs one Rust daemon that holds the venue connections and keeps one authoritative book per market. Local programs attach to a shared-memory segment. A local program can read the latest book, follow a bounded stream of price-level changes, or wait for a wake-up and check an index of the markets that changed.
Python and TypeScript programs call a Rust helper. Memory checks and synchronization live in that one implementation. Prices and sizes stay exact decimals across the language boundary.
Shared memory ties every reader to the daemon's machine. Book updates reach readers with no broker and no JSON parsing, but a remote consumer needs its own bridge. The release runs on Linux and macOS only. We chose speed on one host and left remote distribution to a separate reader.
Limitless is the first venue. The book and delivery layers do not depend on its protocol, so another venue can add an adapter with its own snapshot and delta behavior.
What was hard
The daemon splits its markets into shards. Each shard is a venue connection with the books for its set of markets.
Redundant connections for a shard create a question of authority: when two copies of an update arrive, the daemon must choose which one goes out. The daemon races the connections only where the venue's observed version numbers allow it. It accepts the first new version, compares duplicates and rejects older arrivals. When the version numbers stop supporting this, the shard falls back to one publisher with standby connections. A standby takes over market by market, only where its book agrees.
The daemon states every gap it detects. If a later snapshot disagrees with a book built from deltas, the daemon starts a new continuity epoch from the snapshot and does not invent a correction. A reader that falls behind gets an explicit loss signal and reattaches to the latest book. Ingestion carries on without waiting for that reader.
The result
The benchmark report in the repository measures the time from a WebSocket frame reaching the daemon to a local reader seeing the update. It ran on an Apple M1 over a loopback connection, with a synthetic update rate and a second, busy-polling reader alongside.
Take 100 markets at 2,000 updates per second in total, with five price levels per side. At that load, 99 in 100 updates reached the waiting reader within 137 microseconds. At 10,000 markets and 20,000 updates per second, queueing overloaded the daemon. On the 5,000-market shard the readers sampled, the 99-in-100 time rose to about 33 milliseconds. The report counts the dropped frames and continuity losses.
Two 15-minute live runs, one per machine, started with 537 and 547 Limitless markets, each across 20 shards. Real Python and TypeScript processes read the books. The runs exercised subscriptions, market resolutions and recovery. Their reports record some continuity losses, and one of the 20 shards disconnected at the end.
We released version 0.1.0 on GitHub and crates.io in September 2026. The next version, in development, also reads Polymarket and keeps each venue event whole.
Built with
| Daemon | Rust with Tokio |
|---|---|
| Venue feed | Limitless over WebSocket, with tokio-tungstenite and rustls |
| Shared memory | memmap2, with futex waits on Linux and wait-on-address on macOS |
| Python reader | ctypes over the Rust C ABI |
| TypeScript reader | Node-API over the same Rust library |
| Metrics | Prometheus |
| Testing | proptest property tests and cross-process tests |
| Building | Claude Code |