@bridge4dev/runner
The DevBridge dev runner. It connects one of your own machines to DevBridge and runs coding-agent sessions on it — Claude Code, Codex or OpenCode — against a checkout that never leaves that machine.
DevBridge gets no SSH access and no credentials for your server: the runner connects outwards over WSS and holds the only copy of its own token.
Requirements
- Node.js ≥ 20
- git
- an agent CLI: Claude Code or Codex, installed and signed in from the server card in DevBridge (or already on the machine), or OpenCode – Linux only, installed by the runner into a folder of its own (see Agent CLIs)
- for OpenCode:
bubblewrap– every OpenCode session runs inside it. The DevBridge installer puts it down for a runner that runs as root; a runner that is not root also needs unprivileged user namespaces (on Ubuntu 24.04 an AppArmor profile forbwrap) - a DevBridge account with a pairing code (dashboard → Development → Connect server)
Install
npm install -g @bridge4dev/runner
Then pair the machine with the code from the dashboard and install it as a service:
devbridge-runner pair DBR-XXXXXXXX
devbridge-runner install-service
devbridge-runner status # exit code 0 = connected
install-service writes a systemd user unit (CPUQuota=80%, MemoryMax=2G) and starts
it. If you would rather run it in the foreground, or under your own supervisor, use
devbridge-runner daemon.
Installing globally may print
ERESOLVEwarnings aboutzod: the Claude Agent SDK asks for zod 4 as a peer while the runner uses zod 3. The install succeeds — npm nests its own copy — and nothing is broken. Add--loglevel=errorif the noise bothers you.
Commands
| Command | What it does |
|---|---|
pair <DBR-code> [--api <url>] [--name <name>] | Claim a pairing code and store the runner token |
install-service | Install + start the systemd user service |
daemon | Run in the foreground |
status | Connection status; exit code 0 when connected |
set-token <dbr_token> | Store a token rotated from the dashboard |
What it does on your machine
- Runs a session either in the project folder itself — the default: the branch it is already on, nothing to apply afterwards — or in a git worktree on a session branch of its own, which DevBridge asks for when the folder is busy or a branch was named. Which one a session gets is decided per session, in the dashboard.
- Enforces a local policy layer. Part of it the dashboard cannot raise: secret files and
keys are never read, nothing is written into any repository's
.git, andsudo, service control, docker and the firewall are refused in every trust level. Part of it is the bound project's own setting, chosen by a manager in DevBridge: whether the agent maygit pushand to which branches, and whether it may read and change files outside the project folder — other projects on the same machine included. Out of the box the agent pushes nothing and stays in its folder. Plus secret masking in everything it streams, and its own ceiling on how many sessions may run at once. - Streams the session journal with sequence numbers and acknowledgements, so a restart of either side resumes instead of losing work.
- Never uploads your source. Diffs and file views are requested per file, and files matching the secret denylist are refused.
Files it owns
| Path | Contents |
|---|---|
~/.config/devbridge-runner/config.toml | API URL, server id, runner token (mode 0600 — this file is the secret store) |
~/.local/state/devbridge-runner/ | Session journals and status.json |
…/devbridge-runner/opencode-home/ | OpenCode: its own npm prefix, its home, and one data folder per subscription (its |
keys and conversations, mode 0700) – never on the machine's PATH |
Both honour XDG_CONFIG_HOME / XDG_STATE_HOME.
What the machine's owner can switch off
config.toml is read by the runner and cannot be changed from the dashboard — these are
the owner's vetoes, and they survive re-pairing. A capability switched off here is not
announced to the server at all, so the dashboard draws no control rather than a control
that answers «refused».
[verify]
enabled = true # running the project's own build/test recipe
[checkpoints]
enabled = true # restore points (copies of the working tree, on this disk)
[agents]
install_enabled = true # installing and updating the agent CLIs (0.47.0)
[agents] install_enabled = false covers both routes at once: the Install / Update
buttons on the server card and the automatic update a session start would otherwise
trigger. It is the same download onto the same disk either way.
Agent CLIs
Since 0.47.0 the runner does not carry a Claude binary and the installer does not install
agents. claude and codex are ordinary system installs, and the server card shows
each one with its version:
- Install / Update run the vendor's own installer for that agent, at a version the card picked from the registry. Free space is checked first, the new binary is smoke-tested by running it, and a build that will not run is rolled back. The daemon does not restart — sessions already running keep the file they opened, and the new version takes effect for the next one.
- Auto-update agents (on by default) does the same thing on its own when a session starts, for that session's agent only, at most once a day per agent.
claude --version
codex --version
OpenCode (2.x, Linux only) is the exception, on purpose. The runner installs it with npm
into a prefix inside its own state folder and starts it only from there, by absolute path –
an opencode you put on the PATH yourself is never used, and DevBridge does not pick up a
sign-in made in it. It follows the vendor's newest 2.x release; after every install and update
the runner checks the version and the package signatures and runs a short probe turn, and puts
the previous version back if any of it fails. Each session runs inside bubblewrap: it sees
the system read-only, its project folder and the keys of its own subscription, never another
subscription's. Where bubblewrap does not start, OpenCode is not offered at all. Keys and
subscriptions are added on the server's OpenCode page in DevBridge; devbridge-runner doctor
shows whether the isolation starts here (opencode isolation).
Updating
From the dashboard: the server card offers Update runner once a newer version is available, and the runner replaces itself — smoke-testing the new build before it restarts, and rolling back to the previous tarball if that test fails. Updating the runner disconnects every session running on this machine; updating an agent does not.
By hand:
npm install -g --omit=optional @bridge4dev/runner && systemctl --user restart devbridge-runner
License
MIT — see LICENSE.