Multi-user room server
This guide is the practical, end-to-end recipe for the collaboration topology: one Studio host acting as a shared room server, and two or more people — each on their own machine, each running their own local runtime — replicating through it. It’s the topology the CAS/blob distribution and multi-peer replication work is built for. Everyone edits against their own local runtime and workspace; the room server is a relay that carries the CRDT traffic (work units, artifacts, proposals) and blobs between them.The mental model: two different URLs
The single most common point of confusion is that there are two connection settings, and they do different jobs:
So each person keeps their own
runtimeUri (loopback 5080, auto-managed) and they
all set room.hostUri to the same room server address. The room server is a third
process — it is not anyone’s editing runtime. See
Guides → Standalone Studio host for how the
extension decides whether to spawn or adopt a runtimeUri host.
Running the room server on one of the participants’ machines is fine. That machine
then has two host processes: the shared room server (e.g. on
0.0.0.0:7878) and
that person’s own extension-spawned local runtime (127.0.0.1:5080). They don’t
conflict — different ports, different data.Step 1 — Start the room server
Any Studio host in server mode is a room server; it maps/ws/{roomId} from the
NodalMerge.DotNetHost package. Bind it to 0.0.0.0 (all interfaces) so other
machines can reach it — the default 127.0.0.1 is loopback-only and unreachable from
a second machine.
From a source checkout:
bin/<rid>/NodalMerge.Studio.Host(.exe) inside the installed extension directory:
7878 is just a convention. Leave the process running.
Persistence
Node storage defaults to SQLite on disk (NodalMerge:Providers:NodeStorage: Sqlite), so the room and its history survive a restart out of the box. By default the
files land in data/ next to the binary:
- Nodes:
NodalMerge:Storage:Sqlite:DbPath(defaultdata/nodalmerge-nodes.db) - Blobs:
NodalMerge:Storage:FileBlobs:RootPath(defaultdata/blobs)
Step 2 — Open the firewall
On the server machine, allow inbound TCP on the port you chose (7878 here). On
Windows:
ipconfig (e.g. 192.168.1.110).
Step 3 — Point each person’s extension at it
In VS Code settings (per-workspace.vscode/settings.json or user settings), each
participant sets:
room.workgroupmust match on every participant — it’s what resolves to the same room id, and therefore what puts everyone in the same room. Empty falls back to the server-side default (workgroup).blobOrigin.uripoints the local runtime’s blob store at the server so large blobs (artifacts, attachments) replicate through it rather than staying local. Use the same host/port as the room server; the scheme ishttp(s)://, notws://.- Leave
runtimeUriunset — each extension keeps managing its own local runtime.
Step 4 — Verify the server is reachable
From the server machine:What “working” looks like
Once two peers are connected to the same room:- Replication — a workspace or goal change made by peer A appears on peer B.
- Late joiner — a peer that connects after changes were made hydrates the existing room state, not just new deltas.
- Blob pull — a large artifact produced on peer A is fetched by peer B through the server blob origin (the CAS distribution path).
- Bidirectional — edits flow both directions between peers.
Room server vs. headless peer vs. standalone host
These three guides describe adjacent-but-distinct roles; pick by what you need:- This page — a shared server that multiple independent peers replicate through. Everyone keeps their own runtime and workspace.
- Standalone Studio host — running the host process yourself (instead of letting the extension spawn it), and how the extension adopts it. A room server is a standalone host bound to a reachable address.
- Headless peer — a runtime with no HTTP/WS
server that connects out to a room server (via
Peer:HostUri) to inject or run agent work non-interactively. It’s a client of a room server like this one.