@criticalbridge/conformance-sidecar
v1.0.10
Published
Language-agnostic conformance sidecar that makes an external A2A agent a fully governed CriticalBridge peer (registers, heartbeats, control-channel polls, proxies /tasks, emits per-task telemetry). Companion to the Brain's /api/brain/agents/v1 ingress (Ep
Maintainers
Readme
@criticalbridge/conformance-sidecar
Language-agnostic conformance sidecar that makes an external A2A agent a fully governed peer of the CriticalBridge Brain — handles registration, heartbeats, control-channel polling, the policy/HITL gate at
/tasks, telemetry emission, and JWT lifecycle. Companion to the Brain's/api/brain/agents/v1ingress.
If you're integrating a Python agent and want a higher-level API (@handler decorator, mint_credential(), etc.), use the criticalbridge Python SDK — it boots this sidecar for you. This package is the lower-level boundary for non-Python customers (Go, Java, Rust, …) or operators who want to manage the sidecar process independently.
Install
The Docker container is the recommended install path — it's language-agnostic, isolated from your agent runtime, and works identically on every host OS. npx and npm install -g are shortcuts for Node-native environments.
Docker (recommended)
docker run --rm -p 8080:8080 \
-v cb-sidecar-sessions:/home/sidecar/.criticalbridge/sidecar/sessions \
-e BRAIN_URL=https://api.criticalbridge.ai \
-e ENTITY_ID=your-entity-id \
-e ENROLLMENT_TOKEN=eyJhbGc.paste.your.token.here \
-e AGENT_VENDOR=langchain \
-e AGENT_BROKER_ID=your-agent-id \
-e AGENT_UPSTREAM_URL=http://your-agent-host:8000 \
-e SIDECAR_PUBLIC_URL=https://sidecar.your-domain.com \
ghcr.io/fburkitt/conformance-sidecar:1.0.2AGENT_UPSTREAM_URL DNS resolution. your-agent-host in the example is a placeholder. Replace with the hostname the sidecar container can reach:
host.docker.internal— if your agent runs on the host machine (Docker Desktop only)- A Docker Compose service name (e.g.
my-agent) — if the sidecar and agent share a network - A fully-qualified DNS name — if the agent runs on a separate machine reachable from inside the container
http:// is fine here (the upstream is private to the sidecar); SIDECAR_PUBLIC_URL still must be HTTPS.
Volume mount matters. The -v flag persists the 30-day registration JWT to a Docker volume. Skip it and container restarts have to re-exchange the (single-use) enrollment token — which fails after the first boot with enrollment_token_already_consumed. The path /home/sidecar/.criticalbridge/sidecar/sessions matches the image's non-root user (sidecar, uid 1001) — that's $HOME/.criticalbridge/sidecar/sessions for the default user, which is where the sidecar's default session directory now resolves (#8665). If you rebuild the image with a different user, adjust the mount path to that user's $HOME/.criticalbridge/sidecar/sessions.
npm
# Run on demand
npx @criticalbridge/conformance-sidecar
# Or install globally
npm install -g @criticalbridge/conformance-sidecar
cb-conformance-sidecarConfigure
All env vars are required; missing ones fail loud at boot. The error message lists every missing var in a single message — no need to iterate.
Required
| Variable | Purpose |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| BRAIN_URL | CriticalBridge Brain Coordinator base URL (HTTPS, no trailing slash) |
| ENTITY_ID | Entity ID the sidecar registers under |
| ENROLLMENT_TOKEN | One-time enrollment token (mint via Portal /dashboard/managed-agents/enrollment-tokens). Single-use; persisted as a 30-day JWT after first boot |
| AGENT_VENDOR | One of langchain, autogen, crewai, semantic-kernel, haystack, other |
| AGENT_BROKER_ID | Durable per-agent ID within the entity — stable per-deployment; the resulting agent id is derived from (vendor, brokerId) |
| AGENT_UPSTREAM_URL | Where your agent listens (private OK) — /tasks requests get forwarded here |
| SIDECAR_PUBLIC_URL | Where the Brain dispatches to the sidecar (must be HTTPS). Point this at your tunnel / reverse-proxy hostname |
Optional
| Variable | Purpose |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ |
| SIDECAR_PORT | Port the sidecar HTTP server listens on (default 8080) |
| AGENT_DISPLAY_NAME | Label shown in the operator fleet view |
| AGENT_CAPABILITY_TAGS | Comma-separated capability strings, e.g. expense.submit,expense.approve |
| AGENT_CATALOG_IDENTIFIER | AI Catalog URN, e.g. urn:air:criticalbridge.ai:acme:support-agent |
| AGENT_CATALOG_MEDIA_TYPE | AI Catalog media type, e.g. application/a2a+json |
| AGENT_REPRESENTATIVE_QUERIES | Pipe-separated (\|) example queries the agent handles — up to 5, up to 256 chars each |
| AGENT_PUBLISHABLE | true | false — whether the agent should appear in federated AI Catalog discovery. Defaults to server-side (typically off) |
What it does
- First boot: exchanges your one-time
ENROLLMENT_TOKENfor a 30-day registration JWT, persists it at~/.criticalbridge/sidecar/sessions/<entity>__<agent>.json(mode 0600), registers the agent with the Brain. - Subsequent boots: reads the persisted JWT from the session file — no exchange needed. Volume-mount the session directory so the file survives container restarts (see the Docker example above).
- Steady state:
- Heartbeats the Brain on a server-tuned cadence (5s–10min)
- Polls the control channel for
executionAllowed+hitlRequiredFor[]posture - Serves
POST /tasks— gates by policy, forwards toAGENT_UPSTREAM_URL - Emits 60s rollups of latency p50/p99/avg + tasks_processed_total + tasks_failed_total
- Refreshes the JWT inside the 7-day pre-expiry window
- Health:
GET /healthzreturns 200 for Kubernetes / Cloud Run liveness probes.
What your agent must provide
- An HTTP
/tasksendpoint that accepts the A2A task envelope{ skill, taskId, ... }and returns the result. The sidecar forwards verbatim.
That's the bare minimum. For richer governance (decision lineage, audit events, just-in-time credentials, HITL gating), see the External Agent Integration Guide.
Troubleshooting
[sidecar] Missing required env vars: … — batch fail-loud at boot. The message lists every unset required var. Fix them all at once and retry.
enrollment_token_already_consumed at boot — the enrollment token is single-use, and either (a) you're restarting the container without a volume mount for the session file, or (b) you copy-pasted the token to a second sidecar. Fix (a) with -v cb-sidecar-sessions:/home/sidecar/.criticalbridge/sidecar/sessions; fix (b) by re-minting a fresh token for each sidecar.
401 unauthorized on register — your registration JWT's entity or broker doesn't match the URL/body. Re-mint the enrollment token, making sure the AGENT_BROKER_ID on your sidecar matches the broker registration id in the Portal form.
503 Managed agent registration requires a seat license — the enrollment token you minted didn't carry a seatLicenseId claim (pre-1d token) or your entity has no available seats. Re-mint a fresh token via the Portal after your entity has at least one seat licensed.
Brain never sees the sidecar despite the local log saying register OK — your SIDECAR_PUBLIC_URL isn't reachable from the Brain. Test it: curl https://your-sidecar-public-url/healthz from a machine outside your network should return 200. If it fails, fix your tunnel / ingress before restarting the sidecar.
Enrollment token expired before boot — enrollment tokens are 1h TTL. Mint a fresh one via the Portal.
License
Apache-2.0 © Frank Burkitt and CriticalBridge.AI contributors.
