What it is
This module adds reliable, event-driven messaging between separate SpacetimeDB databases, filling the gap until the platform ships native inter-database communication. It sits inside each participating database and handles sending, receiving, retrying, and deduplicating messages so that changes in one database can trigger reactions in another without polling.
Where it fits
- Sharded game worlds: a player character crossing from one world shard to another needs to transfer ownership atomically, with no risk of duplication or loss during a crash.
- Microservice-style backends: an order database and an inventory database need to stay in sync in real time, with each side reacting to the other's changes as events rather than on a polling schedule.
- Collaborative multi-tenant apps: a front-end database and a backend processing database need to exchange work items reliably, with guaranteed exactly-once application of each message.
- Live data mirroring: one database maintains a local mirror of a subset of another database's state, updated automatically whenever the source changes.
- Auction or marketplace systems: items or bids must move between databases with a clear ownership handoff, where the item is live in exactly one place at all times.
How it works
When a database wants to send a message to a peer, it records the message in a local outbox table as part of the same transaction that makes the underlying change. This means the message and the change are always consistent: either both exist or neither does. A scheduled background task then picks up pending outbox entries and delivers them to the peer over HTTP, retrying with increasing delays if the peer is unavailable, and stopping only once the peer confirms the message was durably applied.
On the receiving side, each incoming message is checked against a record of already-applied message identifiers before anything is written. Because that check and the resulting change happen in the same transaction, a message that arrives more than once (due to a retry) has its effect applied exactly once. The receiving database can also queue reply messages in that same transaction, making the communication fully event-driven in both directions with no timers running during normal operation.
For cases where an entity must live in exactly one database at a time, the module provides a structured ownership handoff. The source database locks the entity, offers it to the destination, waits for acceptance, deletes its own copy, and only then signals the destination to make the entity live. Every step is a single local transaction plus a durable message, so a crash at any point simply resumes from where it left off rather than leaving a duplicate or a gap.


