VibeBridge is a browser control plane for running Claude Code and Codex across one or more machines.
main_server.pyis the only browser-facing entry.app.pyruns on each node and executes local filesystem, shell, Git, and provider work.- A session is created by choosing
node + path + provider, so execution stays on the machine that owns the workspace.
Browser
-> Main Server (main_server.py)
- serves dist/
- auth + JWT
- node registry and routing
- browser WebSocket and shell relay
-> Node Server(s) (app.py)
- connect to Main
- run Claude Code / Codex locally
- expose local project, filesystem, shell, and Git APIs
The browser should talk only to Main. Nodes are execution workers, not separate browser entry points.
Set node.main_server_url in configs/node.toml.
Set node.main_register_url in configs/node.toml.
If both are set, the current implementation prefers direct WebSocket mode. Use node.advertise_host and node.advertise_port when Main needs a different callback address than the Node sees locally.
cd /path/to/VibeBridge
pip install -r requirements.txtUse any environment manager you like; a dedicated virtualenv or conda environment is recommended.
cd /path/to/VibeBridge
cp configs/main.toml.example configs/main.toml
cp configs/node.toml.example configs/node.tomlFor same-machine local development, use different database.path values in the two files so Main and Node do not share the same SQLite database. Update node.main_server_url or node.main_register_url if the Node should connect to a different Main host.
cd /path/to/VibeBridge
python main_server.pycd /path/to/VibeBridge
python app.pyTo add more nodes, repeat the Node setup on other machines with each machine's own configs/node.toml.
http://127.0.0.1:4457/
If the database is empty, the first visit will go through the registration flow.
| File or Key | Used By | Description |
|---|---|---|
configs/main.toml |
Main | Main runtime config |
configs/node.toml |
Node | Node runtime config |
server.host / server.port |
Main / Node | Listen address and port |
database.path |
Main / Node | SQLite database path |
auth.jwt_secret |
Main | JWT secret; generated and persisted if omitted |
main.node_register_tokens |
Main | Allowed node registration tokens |
main.node_addresses |
Main | Nodes that Main should connect to proactively |
node.main_server_url |
Node | Direct WebSocket target |
node.main_register_url |
Node | HTTP registration target for Main callback mode |
node.id / node.name |
Node | Stable node identifier and display name |
node.register_token |
Node | Node registration token |
node.labels / node.capabilities |
Node | Labels and declared capabilities |
node.advertise_host / node.advertise_port |
Node | Override the address Main should use to reach this Node |
filesystem.* |
Node | File browser guardrails |
terminal.default_shell |
Node | Default shell for the built-in terminal |
providers.claude.* / providers.codex.* |
Node | Provider-specific timeouts and limits |
VibeBridge/
├── app.py
├── main_server.py
├── config.py
├── configs/
├── database/
├── main/
├── middleware/
├── providers/
├── routes/
├── ws/
├── frontend-src/
└── dist/
Good starting points:
main_server.pyapp.pynode_connector.pymain/browser_gateway.pyproviders/claude_sdk.pyproviders/codex_mcp.py
- Implementation:
providers/claude_sdk.py - Uses the real Python SDK path
- Dependency:
claude-agent-sdk
- Implementation:
providers/codex_mcp.py - Uses
codex mcp-serverfirst - Falls back to
codex exec --jsonif MCP bootstrap fails - Requires the
codexCLI on the node machine
Main:
curl -sf http://<main-host>:4457/healthNode:
curl -sf http://<node-host>:4456/healthdist/is already included in the repository. Rebuild the frontend only when you actually change frontend code.Dockerfilecurrently describes the Node role only.

