configuration reference
Configuration reference
Every knob lives in one agents.json; the CLI takes only two flags. This page lists each field with type, default and allowed values, plus the codex sandbox/approval trade-offs.
modes
Modes & commands
One command, two modes, the same config file:
| Command | What it does |
|---|---|
agent-bridge serve |
The default. One bridge per config entry, each on its own port (HTTP+SSE, optionally plus the WS /acp door). |
agent-bridge acp <entry> |
Turns one entry into an ACP agent on stdio, for editors to spawn. stdout is protocol only, logs go to stderr, no port opens. |
The complete CLI surface:
| Flag | Applies to | Meaning |
|---|---|---|
--config FILE |
both | Config path, default ./agents.json (relative to where you start). |
--bind ADDR |
serve only | Listen address, default 127.0.0.1. Binding non-loopback requires every entry to set apiKey, or startup is refused. Rejected in acp mode. |
--name NAME |
acp only | Same as the positional <entry>. |
--help / -h |
both | Prints usage. |
file
The config file
JSON, with a top-level bridges array of at least one entry. Each entry is one bridge:
agents.json
{
"bridges": [
{ "name": "codex", "port": 3948, "apiKey": "",
"sandbox": "workspace-write", "approval": "on-request" },
{ "name": "claude", "port": 3949, "apiKey": "" },
{ "name": "pi", "port": 3950, "apiKey": "" },
{ "name": "gemini", "port": 3951, "apiKey": "" }
]
}
This is the shipped starter (agents.example.json, included with both install paths). name must come from the registry: codex, claude, pi, gemini — long-tail agents join by adding a line to agents-registry.mjs, not by loosening validation.
fields
All fields
Fields with illegal values refuse startup and are reported one by one. Anything not in this table is ignored.
| Field | Type | Default | Allowed values | Meaning |
|---|---|---|---|---|
| Identity & ports | ||||
| name | string | — | codex claude pi gemini | Required. The bridge's identity — it picks the adapter. Registry names only. |
| port | integer | — | 1–65535 | Required in serve mode, unique per bridge. Omit for entries only used with acp. |
| apiKey | string | — | any string | Empty or omitted = keyless (loopback binds only). Required on every entry when binding non-loopback. |
| cwd | path | where you started | a path | The agent's working directory. ~ expands; relative paths resolve against the bridge's start directory. |
| env | object | — | {VAR: string} | Merged over the daemon's environment for the spawned agent; entry env overrides the registry default. |
| Client doors | ||||
| acp | boolean | false | true false | true opens the ACP-over-WebSocket door ws://…/acp on this bridge's port. |
| corsOrigin | string | loopback only | "*" | By default only loopback origins are reflected. "*" is the only allowed value and the explicit cross-origin opt-in — pair it with apiKey. |
| command | string[] | registry default | non-empty string array | Overrides the ACP shim's spawn command (e.g. ["npx","-y","@agentclientprotocol/claude-agent-acp"]). Not for codex — it runs the native adapter; use codexBin. |
| codex tuning codex only | ||||
| codexBin | string | "codex" | path or command | Where the codex executable lives. |
| codexHome | string | codex default | a path | Sets CODEX_HOME for the codex process — run side-by-side codex configs/logins. |
| sandbox | enum | read-only | read-only workspace-write danger-full-access | codex sandbox tier: where writes and network are allowed. See below. |
| network | boolean | false | true false | Network access under the workspace-write tier. Only meaningful there. |
| approval | enum | never | never on-request untrusted | Approval policy — when codex asks you to confirm. See below. |
codex · sandbox & approval
codex sandbox & approval
Together these two fields decide "what codex may do on its own" and "when it interrupts you". Both are passed to codex verbatim at session start (thread/start's sandbox and approvalPolicy) and enforced by codex itself.
sandbox — the sandbox tier
| Value | File writes | Network | Fits |
|---|---|---|---|
read-only |
denied | none | The default. Reading, analysis, Q&A — any writing or networked command is out of bounds. |
workspace-write |
inside the workspace | only with network: true |
The everyday pick. Editing code works; network is opt-in. |
danger-full-access |
anywhere | yes | Full access. Risk is yours — for sealed environments or fully trusted automation. |
network only matters under workspace-write: danger-full-access comes with network anyway, and read-only ignores the field.
approval — the approval policy
| Value | When cards appear |
|---|---|
never |
Never. Out-of-policy actions fail instead of interrupting you. |
on-request |
When an action needs more than the sandbox/policy grants — the familiar "approve" cards. |
untrusted |
Strictest: nearly every command runs by you first. |
approve cards
Too many approval cards? The recipe
The mechanism first: the bridge does not decide when to ask — codex raises the request and the bridge forwards it to your client. More cards means your commands don't match the sandbox / network / approval combination.
Three things trigger a card:
- a command writes outside the sandbox (installing deps under
read-only, writing logs); - a command needs network but the tier has none (
workspace-writewithoutnetwork: true— onecurlis enough); - the
approvalpolicy demands confirmation (untrustedasks about almost everything).
The "stop asking" recipe
Full access — files, network, commands all open, so codex has nothing left to ask about:
{ "name": "codex", "port": 3948,
"sandbox": "danger-full-access", "approval": "never" }
The middle ground — writable workspace plus network, plenty for daily work with a boundary still in place:
{ "name": "codex", "port": 3948,
"sandbox": "workspace-write", "network": true, "approval": "never" }
Changes don't reach live sessions
Sandbox and approval are fixed at session start. After editing agents.json, restart the bridge and open a new session — old ones carry the old policy.
for client implementers
Approval wire flow
For client authors: an approval is one request and one answer on the v1 wire. The agent emits an approval event (SSE or WS frame); you render it as a card and answer over HTTP once the user decides:
agent → client (SSE data frame)
{"type":"approval","requestId":"…","tool":"command","command":"…","cwd":"…"}
client → bridge
POST /approvals/<requestId>
{ "choice": "once" | "always" | "deny" } // → {"ok":true}
once allows this one, always allows later ones like it, deny refuses. Echo the requestId verbatim — it may be a string.
security
Security posture
- Loopback by default. A non-loopback
--bindrequires anapiKey(Bearer token) on every entry, or startup is refused. - Host-header allowlist (the DNS-rebinding guard) applies to loopback binds only; remote binds are gated by the token.
- CORS reflects loopback origins only; cross-origin needs the explicit
"corsOrigin": "*", paired withapiKey. - The config holds secrets: the bridge warns when
agents.jsonis group/world-readable —chmod 600 agents.json. - Plain HTTP: TLS belongs in a reverse proxy (nginx/caddy); the bridge sends
X-Accel-Buffering: noso SSE streams unbuffered.
fixed limits
Fixed limits
Deliberate protocol constants, not configurable:
| Constant | Value | Why |
|---|---|---|
| Request body cap | 4 MB | Bounds inline base64 images. |
| Images per turn | 8 | Same reason. |
| SSE heartbeat | 15 s | : ka comment frames keep silent stretches alive. |
| Disconnect | disconnect = abort | A dropped client interrupts the running turn — no unattended long jobs. |
| Turn replay | none | Turns are live-only; missed turns are not resent. |
| Session lifetime | survives bridge restarts | Agents persist their own sessions; resume continues after a restart. |
zero-setup
Zero-setup fallback
acp mode resolves config in three steps: explicit --config → an agents.json beside you → the registry's built-in default. The last one is why agent-bridge acp claude runs with nothing configured (what registry-style auto-install invokes): it spawns the registry's default agent command and says so on stderr.