Summary
A collaboration relay a self-hoster can run with Docker, so live sessions do not require a Cloudflare account.
Why
The relay in workers/collab is open source and in this repo, which makes collaboration look self-hostable. It is not, for anyone whose infrastructure is not Cloudflare.
CollabSession is built on Cloudflare primitives that have no local equivalent: Durable Objects for the single-instance-per-session guarantee, idFromName routing, the WebSocket Hibernation API with ws.serializeAttachment() for per-socket state that survives eviction, durable storage for latestSnapshot / rev / mode / hostToken / chat, and a storage alarm to reclaim an empty session. Those choices are well-reasoned for the hosted deployment — docs/collaboration.md explains why a Durable Object relay was picked over CRDT or WebRTC — but they mean the only local development path is wrangler dev, and the only production path is Cloudflare.
So a self-hoster who runs the Docker image today gets the map, the sidecar, and (with the companion issues) their own projects server, but live collaboration stays dark. The feature flag is documented as a build variable pointing at wss://collab.geolibre.app, and there is no other option to point it at.
The relay is also the one part of the system that inherently sees project content: snapshots pass through it and are persisted there. An organization that self-hosts everything else specifically to keep data inside its perimeter is the organization least able to use the hosted relay.
Proposal
1. Separate the protocol from the platform
The session logic is small and already well-factored: protocol.ts holds the shared message types, comment-validate.ts the validation, and session.ts the state machine and permission enforcement. Extract the platform-independent parts — message handling, effective-permission checks, snapshot/rev bookkeeping, bounded chat history, participant roster — from the Durable Object shell, so both a Worker and a plain Node process can host the same logic and the two cannot drift.
2. A Node relay in the same repo
A small WebSocket server (ws or equivalent) implementing the same protocol, with an in-process session registry, and the same storage semantics backed by SQLite or Redis. It does not need hibernation: a self-hosted instance is single-process, and idle sessions can simply be evicted by the existing TTL.
3. Container it, and put it in the Compose file
A geolibre-collab service in the same docker-compose.yml as the web container and the projects server, so GEOLIBRE_COLLAB_URL=ws://collab:8787 inside the network and wss://… at the ingress. Depends on the runtime-configuration issue, since the collab URL is build-time only today.
4. Keep one conformance test suite
The single most valuable part of this work: a protocol test suite both implementations must pass, covering the permission cases in particular — a guest snapshot rejected under session view-only, a per-participant override, the host token gate on set-mode / set-participant-mode, and the oversized-snapshot rejection. Two relays with subtly different permission behavior is the failure mode to design against, since one of them would be quietly less strict.
Scope notes
- Scale is not a goal. A self-hosted relay serving one classroom or one team does not need Durable Objects' distribution properties, and pretending otherwise would produce a much larger design than anyone needs.
- The ~1 MiB snapshot cap exists because of the Cloudflare frame limit. The Node relay can afford more, but the limit should stay configurable and default to the same value so a project that works on one relay works on the other.
- Keep the Worker as the hosted deployment. This adds a second host for the same logic; it does not replace the first.
- The hardening in the collaboration access-control issue must apply to both implementations, which is the practical reason to extract the shared core before that work rather than after.
Effort
Medium.
Part of #1665 (access control umbrella).
Summary
A collaboration relay a self-hoster can run with Docker, so live sessions do not require a Cloudflare account.
Why
The relay in
workers/collabis open source and in this repo, which makes collaboration look self-hostable. It is not, for anyone whose infrastructure is not Cloudflare.CollabSessionis built on Cloudflare primitives that have no local equivalent: Durable Objects for the single-instance-per-session guarantee,idFromNamerouting, the WebSocket Hibernation API withws.serializeAttachment()for per-socket state that survives eviction, durable storage forlatestSnapshot/rev/mode/hostToken/ chat, and a storage alarm to reclaim an empty session. Those choices are well-reasoned for the hosted deployment —docs/collaboration.mdexplains why a Durable Object relay was picked over CRDT or WebRTC — but they mean the only local development path iswrangler dev, and the only production path is Cloudflare.So a self-hoster who runs the Docker image today gets the map, the sidecar, and (with the companion issues) their own projects server, but live collaboration stays dark. The feature flag is documented as a build variable pointing at
wss://collab.geolibre.app, and there is no other option to point it at.The relay is also the one part of the system that inherently sees project content: snapshots pass through it and are persisted there. An organization that self-hosts everything else specifically to keep data inside its perimeter is the organization least able to use the hosted relay.
Proposal
1. Separate the protocol from the platform
The session logic is small and already well-factored:
protocol.tsholds the shared message types,comment-validate.tsthe validation, andsession.tsthe state machine and permission enforcement. Extract the platform-independent parts — message handling, effective-permission checks, snapshot/rev bookkeeping, bounded chat history, participant roster — from the Durable Object shell, so both a Worker and a plain Node process can host the same logic and the two cannot drift.2. A Node relay in the same repo
A small WebSocket server (
wsor equivalent) implementing the same protocol, with an in-process session registry, and the same storage semantics backed by SQLite or Redis. It does not need hibernation: a self-hosted instance is single-process, and idle sessions can simply be evicted by the existing TTL.3. Container it, and put it in the Compose file
A
geolibre-collabservice in the samedocker-compose.ymlas the web container and the projects server, soGEOLIBRE_COLLAB_URL=ws://collab:8787inside the network andwss://…at the ingress. Depends on the runtime-configuration issue, since the collab URL is build-time only today.4. Keep one conformance test suite
The single most valuable part of this work: a protocol test suite both implementations must pass, covering the permission cases in particular — a guest snapshot rejected under session
view-only, a per-participant override, the host token gate onset-mode/set-participant-mode, and the oversized-snapshot rejection. Two relays with subtly different permission behavior is the failure mode to design against, since one of them would be quietly less strict.Scope notes
Effort
Medium.
Part of #1665 (access control umbrella).