Edit the same file together, on a server you run.
Selvage is a live-coding collaboration protocol: one Rust binary you host holds the room, and an invite link is the whole permission. VS Code, Neovim and a browser page join the same file, with no account and no third party's cloud holding the room.
- The session layer is written down as a specification: prose, JSON Schema and conformance vectors.
- Guests see the paths you grant and edit the room's text. Only your own client writes to your folder, and the room ends when you leave.
- There is no account on either side. Rooms live in memory on the server you host, and nothing is written to disk.
The specification is a draft, and the wire version is selvage/1.
Get it working
One server holds the room, and the editor you already use is where you type. Run the published container, then open one row per editor below.
Start a server
docker run --rm -p 127.0.0.1:8080:8080 ghcr.io/selvage-protocol/selvaged:0.2.0The server answers ws://127.0.0.1:8080/session and reports on http://127.0.0.1:8080/meta, one port for the room and the page that joins it. Rooms live in memory, so a restart ends them. The 127.0.0.1 binding keeps the room on your own machine; serving guests on other machines means rebinding (for example -p 8080:8080) and an invite URL that names a reachable address. selvage-protocol/reference_server holds the compose file and the source build.
The server relays ciphertext, and cannot tell who is host. Documents, cursors, the file listing, which files are open and who may edit are sealed under keys that travel in the part of the link a browser never sends to a server, and the decisions a session used to ask the server to make are signed by the peers instead. The box — and whoever holds it — carries bytes it cannot read. It still sees that a room exists, who is in it, their names, and the sizes and timing of what moves.
Pick your editor
One row per editor, folded shut. The commands inside are the ones the editor's own README documents.
VS Code: package the unpublished extension, then host a session
Needs VS Code 1.85 or newer, and Node 22.18 or newer to build the extension. It is unpublished, so a checkout and one package step stand in for an install:
git clone https://github.com/selvage-protocol/vscode_client
cd vscode_client
npm ci --no-audit --no-fund
npm run package
code --install-extension selvage-client-<version>.vsixOpen the folder you want to share and run Selvage: Host a session against ws://127.0.0.1:8080; the invite link is copied as the room opens. A guest runs Selvage: Join a session from an invite link and pastes it, which reloads that window onto the room's own folder. selvage-protocol/vscode_client
Neovim: the plugin manager installs it, then host with one command
Needs Neovim 0.10 or newer, and Node 22.18 or newer on PATH for the companion process the plugin runs. The plugin manager runs npm ci when it installs the plugin:
{ 'selvage-protocol/nvim_client', build = 'npm ci' }That is the whole lazy.nvim spec, and vim-plug takes Plug 'selvage-protocol/nvim_client', { 'do': 'npm ci' }. Then :SelvageHost ws://127.0.0.1:8080 shares the current buffer and copies the invite, :SelvageCopyInvite copies it again later, and :SelvageJoin <invite> joins the room the link names. selvage-protocol/nvim_client
Browser page: join a room, or start one in Chrome or Edge
The server above serves the page on the same port, and the demo serves it too. A guest opens the invite link the host copied and edits in the page, which takes the room and token in its query string:
http://127.0.0.1:8080/?room=<room>&token=<token>With no invite link, Chrome or Edge can start a session from the page instead: the page asks for a folder, lists the paths inside it, and writes the room's settled text back into the file it came from. It has to be the page its own server serves, which is how the demo is served.
The page lives in web_client: npm ci, npm run build and npm run serve build and serve it on its own.
Try the demo
A small instance of the server runs at selvage-demo.dontblameme.dev. Point one of the editors above at wss://selvage-demo.dontblameme.dev and it hosts a room there: selvage.serverUrl in VS Code, vim.g.selvage_server_url in Neovim. A host started with neither setting asks for the address. The invite link it copies opens the room in the browser page, so a guest needs nothing installed. Chrome or Edge can start a room there from the page itself, with no editor running at all.
Rooms live in memory on one small box, and a restart ends every one of them. Run the server above for work you need to keep, on your own machine or on one you rent. The instance is for personal and evaluation use, and non-commercial: its terms cover this one box, and the licences below decide what the software itself may be used for, selvaged's FSL-1.1-MIT reserving commercial hosting for the project.
See it working
A room is the host's folder, seen from somebody else's editor.
- invite link?room=k7m2&token=4f9c…
Anyone with the link is in
Hold the link and you are in. There is no approval step, and nobody waits for the host to let them in. Treat an invite the way you would treat a password.
Two carets, one text
Everyone's edits land in the one CRDT, and carets and selections travel as anchors inside it. A peer's caret stays where it was while the text around it moves.
A guest sees the paths you granted
The host publishes the paths inside the folder it granted, and reads a file from its own disk when somebody opens it. The room never adds, renames or removes a path in your working copy.
Multiple clients, one engine
The VS Code client, the Neovim client and the browser page each drive a copy of the same engine and bridge, so a room's rules live in one implementation. The page joins a room from a link, and on the server's own page, Chrome or Edge can start one from a folder you pick.
How it works
The workflow is one sentence: share a link, come edit my code with me. In order, it comes to four moves.
- Host a folder. Start the server, open a folder in VS Code or Neovim, and host a session from the command palette. On the server's own page, Chrome or Edge can host from a folder the page asks for. The host lists the paths inside that folder and sends the listing.
- Send the invite. Copy the invite link and send it however you already talk to each other. The token in it is the permission, and only the paths you granted are behind it. The host's client holds that confinement; nothing on the wire enforces it.
- Type in the same file. A guest opens a file when they want it, and the host reads it from disk at that moment. A file nobody has opened sends no text, so opening one file never moves the whole project over the wire.
- Close the window. The room dies with its host after a short grace period, so a dropped connection does not end it. Rooms live in memory: nothing survives a restart of the server.
The session layer has no specification
Language tooling has the Language Server Protocol and debugging has the Debug Adapter Protocol. Document sync has y-protocols and the CRDT libraries beneath it. Which rooms exist, who is in one, what their role is, which documents are open, where their carets are, what happens when somebody leaves: every collaborative tool decides those for itself, so none of the tools can talk to each other.
The specification is Selvage's flagship artifact: that layer, written out as prose, a canonical byte form for a frame, JSON Schema documents, and 31 conformance vectors (34858 frame checks and 8642 assertions) replayed byte for byte against a real server. The numbers are constants in schema/validate.py, so deleting an assertion fails the run instead of shrinking a total in a line of output.
The vectors are also the honest test: nothing yet shows that code written from the prose alone agrees with this implementation byte for byte, and a corpus you can replay is what would settle it.
Read the specification. It is written to be implemented on its own, without reading the Rust.
Verify the corpus, and see what the clients do with the room
Check the corpus
The schemas and the vectors are checked on their own, with no server and no Rust: every schema-eligible frame parses and validates against the schema for the concern it names, and every expected frame is written in the canonical byte form.
git clone https://github.com/selvage-protocol/specification
cd specification
pip install jsonschema referencing
python3 schema/validate.pyReplay the vectors against a server
The same transcripts are replayed against a real selvaged over a WebSocket, with no Rust in the comparison: the runner reads the vectors, starts a server of its own on an ephemeral port, and compares what comes back byte for byte. It exits non-zero on any mismatch.
git clone https://github.com/selvage-protocol/reference_server
git clone https://github.com/selvage-protocol/specification
cd reference_server
nix develop . -c cargo build -p selvaged
export SELVAGE_SELVAGED=$PWD/target/debug/selvaged
cd ../specification
pip install websockets jsonschema referencing
python3 runner/run_vectors.pyWhat the clients do with the room
A host shares the documents it has open under the folder it granted, and a guest reads a file from that folder when it opens one, so nothing is copied until somebody asks for it. The Neovim client mirrors the granted folder into a real directory of its own, so ripgrep, ctags and a language server see ordinary paths, and it keeps its engine in a companion process.
VS Code adds Selvage: Copy the invite link, Selvage: Open a document from the room and Selvage: Leave the session; Neovim answers with :SelvageCopyInvite, :SelvageJoin and :SelvageLeave. Each repository's own README is the full command list.