Documentation · v0.1.0

/.relay docs

/.relay is the coordination layer for AI coding agents. Claude Code, Cursor, Copilot, Codex and Antigravity all write to the same files and none of them know the others exist. /.relay gives them one shared lock table, a dependency-aware view of the blast radius, and live patch sync across every machine on the team.

Everything on this page runs on your own hardware. There is no hosted backend, no database, and no account server — one person on the team runs relay serve and that machine is the server.

the shiftdiff
- 12 agents · 12 blind writers · conflicts discovered at merge
+ 12 agents · 1 lock table   · conflicts prevented at the edit

What is in here

If you want to…Go to
Install the CLI and run it for the first timeInstallationQuickstart
Understand what a lock actually guardsCore concepts
Look up a command and its flagsCLI reference
Wire a specific agent into the claim/release cycleAgent hooks
Read room context from an MCP clientMCP server
Fix an empty board or a broken roomTroubleshooting
Note

Reading order matters less than you would think — the quickstart is four commands. If you are impatient, jump to Quickstart · Host and come back for the concepts once something is locked.

Get started

Installation

/.relay is a Node CLI plus a local API and a Next.js board. Everything ships in one repository, so installing means cloning it and linking the CLI onto your PATH.

Requirements

StatusWhatWhy
RequiredNode.js 18+ (20+ recommended), npmRuns the CLI, the API, and Mission Control
Requiredgitrelay clone shells out to it
Optionalgh CLIFastest path for relay login
OptionalngrokOnly needed for cross-machine rooms
Not neededMongoDB, Redis, Docker, a hosted backend, an accountState lives in files on your disk

Install the CLI

terminalbash
$ git clone https://github.com/prakrititz/relayBrain
$ cd relayBrain
$ npm install                # deps for the CLI, API and Mission Control
$ npm link                   # puts `relay` on your PATH

Confirm the binary resolves:

terminalbash
$ relay help
outputconsole
Relay — GitHub Desktop for AI agents

  relay login              Sign in with GitHub (uses gh CLI, or device flow if GITHUB_CLIENT_ID is set)
  relay logout             Clear the local session
  relay whoami             Show the signed-in GitHub user
  relay clone <url> [dir]  git clone + register workspace + install agent hooks
  relay add <path>         Attach an existing local repo
  relay serve              Start API on 127.0.0.1:3001 and Mission Control on :3002
  relay serve --no-ui      Start API + coordinator only
  relay push               Send your dirty working-tree files to the shared room
  relay pull               Apply the host's current dirty files onto this clone
  relay status             Health check
  relay mcp-url            Print the MCP config for this room's shared context endpoint
  relay doctor             Why the Coordinator board is empty
Heads up

npm link writes a global symlink. If your Node install lives under a system path you may need elevated permissions, or an nvm/fnm-managed Node — which is the friendlier option.

Get started

Quickstart · Host

The host is the machine that owns the lock table. Exactly one person on the team is the host; everyone else is a guest. Four commands and you are live.

Sign in with GitHub

Locks are attributed to a GitHub identity, so the board can say @pony holds this file rather than agent-4f2 holds this file.

$ relay login

This reuses the gh CLI session. If gh is not installed, set GITHUB_CLIENT_ID and /.relay falls back to the GitHub device flow.

Bring a repository under /.relay

Cloning through relay clones the repo, registers the workspace, and installs the claim/release hooks for every supported agent in one shot.

$ relay clone <repo-url> [dir]

Already have the repo on disk? Attach it in place instead — same hook install, no re-clone:

$ relay add /path/to/repo

Start the relay

$ relay serve                # API → 127.0.0.1:3001 · Mission Control → :3002
$ relay serve --no-ui        # API + coordinator only, no board

Mission Control is a Next.js dev server; the CLI waits for /api/health to answer before booting it, and shuts both down together on Ctrl-C.

Mint the invite link

Open localhost:3002Team → Share. /.relay opens an ngrok tunnel to your machine and hands you a link. That link, plus the member token baked into it, is the whole room.

Tip

Nothing else to remember. Once hooks are installed, agents claim and release locks on their own — no one on the team ever types a lock command.

Get started

Quickstart · Teammate

Guests run their own relay against their own filesystem. They do not share a working tree with the host — they share a lock table and a patch stream.

teammatebash
$ relay login                     # their own GitHub identity
$ relay clone <same-repo-url>     # their own clone
$ relay serve                     # their own local relay
# then: Mission Control → Team → Join, paste the host's invite link
$ relay pull                      # take the host's current working state

From that point on, the two machines see one lock table. A claim on the guest's machine is arbitrated against the host over the tunnel, and the board mirrors the host's locks back down.

RoleOwnsBoard is fed by
HostThe canonical lock tableIts own local table
GuestIts own clone and agentsThe lock mirror polling the host
Warning

If the host's tunnel dies, locks still arbitrate correctly on the machines that can reach it — the board is what degrades first. relay doctor walks that chain and names the broken link.

Get started

Your first coordinated write

Open your agent as you normally would and ask it to edit a file. Nothing about your workflow changes — the hooks fire underneath.

relay statusconsole
lock table — 4 held
├── src/auth/session.ts   # @pony   · claude  · write
├── src/api/routes.ts     # @unnath · cursor  · write
├── lib/db/client.ts      # @arjun  · codex   · write
└── ui/Header.tsx         # @pony   · copilot · write
    └── dep-blocked: ui/Nav.tsx, ui/Layout.tsx

Now have a second agent try the same file. Instead of a silent overwrite discovered three hours later at the merge, the write is refused at the edit and the agent is told who holds it.

Confirm the whole chain is healthy at any time:

$ relay doctor

Core concepts

File locking

Every agent write goes through a claim/release cycle against a shared, TTL-based lock table. A pre-tool hook fires before the agent touches a file and claims it. A post-tool hook fires after and releases it. If another agent holds the file, the claim fails and the write never lands.

write lifecycletext
agent wants to write src/auth/session.ts
        │
        ├─ pre-tool hook ──► POST /api/locks/claim
        │                       ├─ free?          → granted, TTL starts
        │                       ├─ held by other? → REFUSED (+ holder identity)
        │                       └─ dep-affected?  → REFUSED (+ blast radius)
        │
        ├─ the edit runs
        │
        └─ post-tool hook ─► POST /api/locks/release
                                └─ change fans out as a patch to the room

Locks are TTL-based and auto-expire, so a crashed agent or a killed terminal cannot wedge a file permanently. Hooks call the host directly over HTTP — arbitration never depends on the board being up.

Core concepts

Dependency-graph locking

Files do not exist independently. Editing one module can break a dozen importers, so path-exact locking is not enough. /.relay parses imports with tree-sitter and asks the real question: does this change affect a file another agent is working on? — not merely is this exact path locked?

Agents stay parallel wherever the graph says they are independent. The blast radius is what gets serialized.

Languages parsed

FamilyLanguages
WebTypeScript, JavaScript
SystemsGo, Rust, C, C++
JVM / .NETJava, C#
ScriptingPython, PHP, Ruby
Note

The graph is rebuilt from the working tree, not from a hosted index. A file in a language outside this list still locks exactly — it just does not contribute edges to the blast radius.

Core concepts

Rooms & the host model

A room is one host machine plus the guests connected to it. There is no cloud component: relay serve binds the API to 127.0.0.1:3001, and sharing means opening an ngrok tunnel straight to that laptop.

topologytext
  Cursor ──┐  pre-tool hook: claim
  Claude ──┤  post-tool hook: release      ┌──────────────┐
  Copilot ─┼──► lock table ◄── dep graph ──│ relay serve  │──► Mission Control
  Codex ───┤        ▲                      │  (your host) │          ▲
  Antigravity ┘     │                      └──────┬───────┘          │
                    │       ngrok tunnel          │                  │
              teammate's relay ◄──── patches ─────┴──────────────────┘
LayerWho runs itWhat it guards
Claim / releasepre- and post-tool hooksExclusive write access to a file
Dependency graphtree-sitter import scanFiles affected by the edit
Roomrelay serve + ngrok tunnelOne shared lock table across machines
Patchesrelay push / relay pullWorking-tree state without full copies
BoardMission Control (:3002)Who holds what, right now

Room state is written to room.json and carries the role (host or guest), the tunnel URL, the host project id, and a member token for invite-only rooms.

Core concepts

Patch sync

Your teammate has their own filesystem, their own environment, and their own agents. /.relay does not copy projects around — it propagates patches of the dirty working tree, so both clones converge on the same uncommitted state without anyone committing.

terminalbash
$ relay push     # send your dirty working-tree files to the room
$ relay pull     # apply the host's current dirty files onto this clone
outputconsole
push 3 file(s)
  src/auth/session.ts
  src/api/routes.ts
  lib/db/client.ts

Both commands are thin clients over the local API — if the relay is not running you get offline — run relay serve and a non-zero exit code, which makes them safe to use in scripts.

Reference

CLI reference

Every command /.relay ships. Expand one for usage, output, and the failure modes worth knowing.

Identity

relay loginSign in with GitHub

Reads the existing gh CLI session and stores a local /.relay session. Locks and board entries are attributed to this identity.

$ relay login
signed in as Krishna V (@pony) via GitHub CLI

No gh installed? Set GITHUB_CLIENT_ID (and GITHUB_CLIENT_SECRET) and the command falls back to the GitHub device flow, printing a verification URL and a one-time code. You can also sign in from Mission Control with Continue with GitHub.

relay whoamiShow the signed-in user
$ relay whoami
Krishna V (@pony)
project prj_8f31a2

Exits non-zero with not signed in — run relay login when there is no session.

relay logoutClear the local session
$ relay logout

Removes the stored session only. Your gh credentials are untouched.

Workspaces

relay clone <url> [dir]Clone + register + install hooks

Three steps in one: git clone, register the workspace as a /.relay project, and install pre-tool, post-tool, pre-read and stop hooks for every supported agent.

$ relay clone https://github.com/acme/api-server
$ relay clone https://github.com/acme/api-server ./api
cloned https://github.com/acme/api-server
workspace /Users/pony/code/api-server
registered api-server (prj_8f31a2)
relay add <path>Attach an existing local repo

Same registration and hook install as clone, without touching git. Use this for a repo you already have on disk.

$ relay add ~/code/api-server
added /Users/pony/code/api-server as api-server

Runtime

relay serveStart the API and Mission Control

Boots the coordination API on 127.0.0.1:3001, waits for /api/health, then starts Mission Control on :3002. Both processes shut down together on Ctrl-C, including their child process trees on Windows.

$ relay serve                    # API + Mission Control
$ relay serve --no-ui            # API + coordinator only
$ relay serve --port 4001        # move the API off 3001
FlagEffect
--no-uiSkip Mission Control; run the API and coordinator only
--port <n>API port (overrides RELAY_PORT, default 3001)

The board port comes from RELAY_UI_PORT (default 3002). If next is missing you will get Mission Control is not installed — run npm install.

relay statusHealth check as JSON

Prints the raw /api/health payload — room, role, host, and ports.

$ relay status
{
  "ok": true,
  "port": 3001,
  "room": {
    "role": "host",
    "url": "https://a1b2c3.ngrok-free.app",
    "hostProjectId": "prj_8f31a2",
    "hostProjectName": "api-server",
    "hostWorkspacePath": "/Users/pony/code/api-server"
  }
}

Exits non-zero with offline — run relay serve when no local API answers.

Sync

relay pushSend dirty files to the room
$ relay push
push 2 file(s)
  src/api/routes.ts
  lib/db/client.ts

Prints push: nothing to push when the working tree is clean.

relay pullApply the host's working state
$ relay pull
pull 3 file(s)
  src/auth/session.ts
  src/api/routes.ts
  ui/Header.tsx

Patches land on your clone — no whole-project copy, no commit required on either side.

Diagnostics & integration

relay doctorExplain an empty Coordinator board

Locks and the board are separate subsystems — hooks arbitrate straight against the host over HTTP, while the board is fed by a lock mirror and SSE. That makes "locking works but I see nothing" a normal and otherwise invisible failure. doctor walks that exact chain and names the broken link.

$ relay doctor
$ relay doctor --port 4001
healthy guestconsole
relay doctor — room + Coordinator board

  ok    local API on http://127.0.0.1:3001
  ok    room joined as guest -> https://a1b2c3.ngrok-free.app
  ok    mirroring host project prj_8f31a2
  ok    host answers through the tunnel
  ok    host reports 4 lock(s) for the shared project

  board would render 4 lock(s):
        3 mirrored from the room, 1 claimed on this machine
        src/auth/session.ts  @pony  write

Run it on the machine that sees nothing — not on the host.

relay mcp-urlPrint the room's MCP config

Emits a ready-to-paste mcpServers block pointing at this room's shared-context endpoint. See MCP server.

$ relay mcp-url

Exits non-zero with a hint when you are not in a room — locally your agents already reach relay over stdio, so there is nothing to configure.

relay helpFull command list
$ relay help
$ relay -h
$ relay --help

Cheat sheet

CommandDescription
relay loginGitHub identity via gh, or device flow with GITHUB_CLIENT_ID
relay logoutClear the local session
relay whoamiShow the signed-in GitHub user and active project
relay clone <url> [dir]git clone + register workspace + install agent hooks
relay add <path>Attach an existing local repo
relay serveAPI on 127.0.0.1:3001 + Mission Control on :3002
relay serve --no-uiAPI + coordinator only
relay pushSend your dirty working-tree files to the room
relay pullApply the host's current dirty files onto this clone
relay statusHealth check (room, role, host, ports)
relay mcp-urlPrint the MCP config for this room's shared-context endpoint
relay doctorDiagnose an empty Coordinator board

Reference

Agent hooks

relay clone and relay add install four hooks into your project: pre-tool (claim), post-tool (release), pre-read, and stop. They are written into each agent's own config format, so every tool participates without a plugin.

AgentConfig fileWrite tools intercepted
Claude Code.claude/settings.jsonEdit, Write, NotebookEdit
Cursor.cursor/hooks.jsonWrite, Edit, Delete
Codex.codex/hooks.jsonapply_patch, Edit, Write
Copilot CLI.github/hooks/relay-os.jsonedit, create
Antigravity.agents/hooks.jsonwrite_to_file, replace_file_content, multi_replace_file_content

Verifying the install

$ ls -a .claude .cursor .codex .agents .github/hooks
Note

The hook files are committed alongside the repo, so a teammate who clones it normally still gets the claim/release wiring — they only need their own relay serve running for it to have something to talk to.

Reference

MCP server

Any MCP client can read the room's shared context — even on a machine with no relay installed. Print the config and paste it into your agent:

$ relay mcp-url
mcp configjson
{
  "mcpServers": {
    "relay-room": {
      "type": "http",
      "url": "https://a1b2c3.ngrok-free.app/mcp",
      "headers": {
        "ngrok-skip-browser-warning": "relay",
        "x-relay-room-token": "rmt_9f81…"
      }
    }
  }
}
Scope

The room endpoint is read-only for anyone off the host machine. Coordination tools that mutate state — relay_claim_file, relay_release_file, relay_status — run against each member's own local relay.

Tools exposed

ToolPurpose
relay_claim_fileAcquire an exclusive write lock before editing
relay_release_fileRelease the lock after editing
relay_statusView the full lock table
relay_get_conflictsOverlapping edits in the last 5 minutes
relay_get_recent_changesRecent code_edit events
relay_get_chat_historyUnified agent chat across every room member
relay_report_changePush a code-change event
relay_report_decision / relay_get_decisionsAppend / read decisions
relay_update_task / relay_get_active_tasksAppend / read tasks
relay_get_project_contextFull JSON project context
relay_syncRe-read agent transcripts into unified history

If you are a guest and no member token is on file, re-join with the invite link — the host may have made the room invite-only.

Reference

Mission Control

One board for every agent on every machine. Started by relay serve, running on your own hardware, with no hosted account behind it.

SurfaceURL
Dashboardhttp://localhost:3002
API healthhttp://127.0.0.1:3001/api/health

Panels

PanelWhat it shows
File locksWho holds what file, right now, with agent and mode
Lock graphThe dependency blast radius as a live canvas
Code editsRecent code_edit events across the room
Agent chatUnified session history from every agent on every machine
Activity timelineClaims, releases, pushes and pulls in order
Team & presenceWho is online, plus Share and Join for the room
Conflict noticesOverlapping edits surfaced within seconds

Reference

HTTP API

The CLI is a thin client over a local HTTP API. Anything the CLI does, you can script.

EndpointMethodPurpose
/api/healthGETLiveness, port, room role and host details
/api/locksGETThe lock table this machine would render
/api/locks?projectId=…GETLocks for one shared project (used across the tunnel)
/api/pushPOSTSend dirty working-tree files to the room
/api/pullPOSTApply the host's dirty files onto this clone
/mcpHTTPRead-only room context for MCP clients
scripting the APIbash
$ curl -s http://127.0.0.1:3001/api/health
$ curl -s http://127.0.0.1:3001/api/locks
$ curl -s -X POST http://127.0.0.1:3001/api/push -d '{}'

Requests that cross the tunnel to a host carry two headers: ngrok-skip-browser-warning: relay and, in invite-only rooms, x-relay-room-token.

Reference

Configuration

Environment variables

VariableDefaultWhat it controls
RELAY_PORT3001Coordination API port
RELAY_UI_PORT3002Mission Control port
RELAY_UI_ORIGINhttp://localhost:3002Origin the API accepts board requests from (CORS)
GITHUB_CLIENT_IDEnables the device-flow login fallback
GITHUB_CLIENT_SECRETPairs with the client id for Mission Control sign-in
moving off the default portsbash
$ export RELAY_PORT=4001
$ export RELAY_UI_PORT=4002
$ export RELAY_UI_ORIGIN=http://localhost:4002
$ relay serve

Where state lives

There is no database. Everything is files on your own disk.

FileHolds
room.jsonRole, tunnel URL, host project id and name, member token
Session storeThe GitHub identity from relay login
Project registryWorkspaces registered by relay clone / relay add
Hook configsPer-agent files listed under Agent hooks

Bundled dependencies

AreaPackages
API & coordinationexpress, cors, ws
Dependency graph@vscode/tree-sitter-wasm
Mission Controlnext, react

Operations

Troubleshooting

Start here, always:

$ relay doctor    # run it on the machine that sees nothing

The board is empty but locking works

Expected, and the single most common report. Hooks claim straight against the host over HTTP; the board is fed by a separate lock mirror. If the mirror is not committing its polls, locks stay correct while the board stays blank.

  BROKE the host holds locks but this machine mirrors none of them.
        Locks still arbitrate correctly (hooks call the host directly); it is only
        the board that is fed by the mirror. Restart `relay serve` here.

Common failures

SymptomCauseFix
offline — run relay serve No local API on the expected port Start relay serve, or match RELAY_PORT
no local API on http://127.0.0.1:3001 Same, seen from doctor Run relay serve on this machine
room.json has no hostProjectId Joined without a complete invite Re-join with the host's invite link
cannot reach the host Tunnel down or rotated Host re-shares; /.relay looks up the current tunnel and reconnects
host refused /api/locks Missing member token in an invite-only room Re-join with the invite link to store the token
Mission Control is not installed next not present in node_modules npm install at the repo root
Install GitHub CLI and run: gh auth login No gh session and no GITHUB_CLIENT_ID Install gh, or set the client id for device flow
No agent ever claims a lock Hooks not installed in that workspace Run relay add <path> on the repo

Port already in use

$ relay serve --port 4001                    # API only
$ RELAY_UI_PORT=4002 relay serve --port 4001  # both, plus set RELAY_UI_ORIGIN
Tip

An empty board with nothing locked anywhere is correct. Have someone start an agent edit and run relay doctor again before assuming something is broken.

Operations

FAQ

Do I need an account?

No. relay login reads a GitHub identity so locks can be attributed to a person, but there is no /.relay account server, no sign-up, and no hosted state.

Does this replace Git?

No — it sits under it. Git coordinates humans committing minutes apart. /.relay coordinates agents editing the same files right now. You still branch, commit and merge exactly as before.

What happens if the host goes offline?

Guests keep their own local relay and their own clone. Claims that need the host will fail loudly rather than silently succeed, and the board falls back to local locks until the tunnel is back.

My language is not in the dependency-graph list.

File locking still works exactly the same — every write is claimed and released. Only the blast-radius edges are missing, so /.relay will not know that editing your file affects someone else's.

What is next?

  • Git worktree isolation — every agent in its own workspace off the same repository, free to experiment and test independently, integrated back into main when the work is ready.
  • Richer conflict resolution on top of the existing OT / patch layer.