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:

CommandWhat 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:

FlagApplies toMeaning
--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.

FieldTypeDefaultAllowed valuesMeaning
Identity & ports
namestring— codex claude pi gemini Required. The bridge's identity — it picks the adapter. Registry names only.
portinteger— 1–65535 Required in serve mode, unique per bridge. Omit for entries only used with acp.
apiKeystring— any string Empty or omitted = keyless (loopback binds only). Required on every entry when binding non-loopback.
cwdpathwhere you started a path The agent's working directory. ~ expands; relative paths resolve against the bridge's start directory.
envobject— {VAR: string} Merged over the daemon's environment for the spawned agent; entry env overrides the registry default.
Client doors
acpbooleanfalse true false true opens the ACP-over-WebSocket door ws://…/acp on this bridge's port.
corsOriginstringloopback only "*" By default only loopback origins are reflected. "*" is the only allowed value and the explicit cross-origin opt-in — pair it with apiKey.
commandstring[]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
codexBinstring"codex" path or command Where the codex executable lives.
codexHomestringcodex default a path Sets CODEX_HOME for the codex process — run side-by-side codex configs/logins.
sandboxenumread-only read-only workspace-write danger-full-access codex sandbox tier: where writes and network are allowed. See below.
networkbooleanfalse true false Network access under the workspace-write tier. Only meaningful there.
approvalenumnever 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

ValueFile writesNetworkFits
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

ValueWhen 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-write without network: true — one curl is enough);
  • the approval policy demands confirmation (untrusted asks 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 --bind requires an apiKey (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 with apiKey.
  • The config holds secrets: the bridge warns when agents.json is group/world-readable — chmod 600 agents.json.
  • Plain HTTP: TLS belongs in a reverse proxy (nginx/caddy); the bridge sends X-Accel-Buffering: no so SSE streams unbuffered.

fixed limits

Fixed limits

Deliberate protocol constants, not configurable:

ConstantValueWhy
Request body cap4 MBBounds inline base64 images.
Images per turn8Same reason.
SSE heartbeat15 s: ka comment frames keep silent stretches alive.
Disconnectdisconnect = abortA dropped client interrupts the running turn — no unattended long jobs.
Turn replaynoneTurns are live-only; missed turns are not resent.
Session lifetimesurvives bridge restartsAgents 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.