Protect endpoints
The control plane fails closed. Reads (tasks.list, tasks.get, health) stay open for the dashboard; every mutation requires an operator-issued bearer credential, and without configuration every mutation is rejected.
Bearer credentials
AGENT_ZERO_CONTROL_PLANE_TOKENS holds comma-separated name:token pairs:
AGENT_ZERO_CONTROL_PLANE_TOKENS=ci:s3cret-token,ops:another-token
The authenticated principal's name (not the token) is what the system records — for example, the approval actor on approvals.decide is always the principal's name, never a wire-supplied value.
Repository allow-list
tasks.create additionally requires the target repository path to appear in AGENT_ZERO_CONTROL_PLANE_REPOSITORIES, so an HTTP caller cannot point a run at an arbitrary server-local path:
AGENT_ZERO_CONTROL_PLANE_REPOSITORIES=/srv/checkouts/app,/srv/checkouts/lib
Execution-mode grants
AGENT_ZERO_CONTROL_PLANE_MODES holds comma-separated name:mode|mode grants for the execution modes each principal may request:
AGENT_ZERO_CONTROL_PLANE_MODES=ci:observe|suggest,ops:fix
Without a grant, a principal may only request the non-writable observe and suggest modes — fix and autonomous require an explicit operator grant.
CORS
AGENT_ZERO_CONTROL_PLANE_ORIGINS lists origins allowed to read /api/v1/** cross-origin. It is empty by default: tasks.list, tasks.get, and health are unauthenticated by design, so letting a browser read their responses from another origin is an explicit opt-in, not the default.
Two independent schemes
The bearer-token scheme authorizes the control-plane API and is independent of the Better Auth session that protects the dashboard UI. A signed-in dashboard user does not hold control-plane authority, and a control-plane token grants no dashboard session. See Authentication.