Control plane

Distribute signed policy to a fleet of brokers and detect audit rollback.

Where to run this

Every command on this page runs from a clone of the AgentBox repository on the trusted host — never inside the agent workspace. If you haven't cloned it yet, start with the quickstart.

Signed policy bundles

Brokers can take policy from a control plane instead of a local file:

VariablePurpose
AGENTGATE_CONTROL_URLControl plane origin (https required)
AGENTGATE_BROKER_IDThis broker's ID
AGENTGATE_CONTROL_TOKEN_FILEBearer token (chmod 600)
AGENTGATE_CONTROL_KEYS_FILE{ "kid": "<Ed25519 public key PEM>" } (chmod 600)
AGENTGATE_CONTROL_POLL_MSPoll interval (default 60000)

Each document is { payload, kid, signature } — an Ed25519 signature over canonical JSON. Payloads carry kind, integer version, issuedAt/expiresAt (at most 7 days apart), and target brokers.

Sign documents

node scripts/sign-bundle.js --key control.pem --kid control1 --config policy.json \
  --version 7 --brokers broker-a,broker-b --days 7 --out policy.signed.json
 
node scripts/sign-bundle.js --key control.pem --kid control1 --revocations revoked.json \
  --version 3 --brokers '*' --days 1 --out revocations.signed.json

Fail-closed behavior

  • A policy applies only if its version is greater than the last accepted one. Versions persist, so a restart can't be rolled back.
  • The broker won't start without a valid policy.
  • If fetching fails, the last good policy serves until expiresAt, then every request gets 503 POLICY_EXPIRED.
  • In assertion mode, an expired revocation list yields 503 REVOCATIONS_EXPIRED. Re-publish revocations — even an empty list — well within their lifetime.

Note

The control plane never holds signing keys. A compromised control plane can withhold updates (brokers then fail closed at expiry) but can't forge policy.

Running the control plane

AGENTGATE_CONTROL_BROKER_TOKENS_FILE=broker-tokens.json \
AGENTGATE_CONTROL_ADMIN_TOKEN_SHA256_FILE=admin-token.sha256 \
AGENTGATE_CONTROL_DATA_DIR=./control-data node src/control-plane.js

Only token hashes are stored. Production requires AGENTGATE_CONTROL_TLS_CERT_FILE and AGENTGATE_CONTROL_TLS_KEY_FILE. examples/control-plane.compose.yaml runs it read-only, non-root, with capabilities dropped.

API

EndpointTokenPurpose
GET /v1/brokers/:id/policybrokerHighest-version document for :id or *
GET /v1/brokers/:id/revocationsbrokerSame, for revocations
POST /v1/brokers/:id/heartbeatbrokerReport versions and audit head; 409 AUDIT_ROLLBACK on regression
PUT /v1/admin/policyadminStore a signed policy (409 STALE_VERSION, 409 SHADOWED_BY_WILDCARD)
PUT /v1/admin/revocationsadminStore a signed revocation list
DELETE /v1/admin/policy/targets/:targetadminRecover from a mistaken publish
POST /v1/admin/brokers/:id/reset-rollbackadminRe-baseline after investigating a rollback
GET /v1/admin/inventoryadminFleet status, stale, rollbackDetected

Brokers heartbeat at startup and every 60 seconds. Alert on control.audit_rollback_reported, control.heartbeat_failed, bundle.*_rejected, bundle.*_expired, and inventory entries that are stale or rollbackDetected.