Supervisor reference
The Ampbase supervisor is configured via a YAML file (default: supervisor.yaml). The schema has four top-level sections: version, server, agent, and supervisor.
For install instructions, see Install the supervisor. For the quick path to a connected agent, see the quickstart.
Schema version
| Field | Type | Required | Description |
|---|---|---|---|
version |
u32 |
yes | Schema version. Must be 1. |
server — OpAMP server connection
| Field | Type | Default | Description |
|---|---|---|---|
endpoint |
URL | required | OpAMP endpoint, e.g. wss://acme.ampbase.io/channels/clickhouse/v1/opamp. |
api_key |
string | required | Channel agent key (agent_{channel_short_id}_{random}). |
transport |
string | "http" |
"http" (polling) or "websocket" (persistent). |
poll_interval_seconds |
u32 |
30 |
HTTP poll interval. Must be > 0. Values > 5 with WebSocket trigger a warning. |
max_message_size_kb |
u32 |
64 |
Maximum WebSocket message size in KiB. Must match the server's maxMessageSize. WebSocket only. |
agent — Managed agent settings
| Field | Type | Default | Description |
|---|---|---|---|
type |
string | required | "otelcol", "fluent-bit", "vector", "telegraf", "refinery", or "coding-agents". For coding-agents the supervisor manages runtime telemetry + an events pipeline rather than a single agent binary — see AI coding-agent policy. |
binary_path |
path | required | Path to the agent binary. |
config_path |
path | — | Path to the agent's config file. Required for otelcol, fluent-bit, vector, telegraf. Unused by refinery (managed via state-dir symlinks). |
health_check_url |
URL | — | Health endpoint. Recommended for otelcol and refinery. Ignored for telegraf and vector (detected from the agent's own config). When absent, the supervisor drops to PID-only monitoring. |
stop_timeout_seconds |
u32 |
10 |
Wait this long after SIGTERM before SIGKILL. |
reload_delay_ms |
u64 |
17000 |
Refinery only. Time to wait for refinery's file poller to pick up config changes. Must be ≥ ConfigReloadInterval + jitter. |
config_format |
string | "yaml" |
"yaml" (otelcol, fluent-bit, vector, refinery) or "toml" (telegraf, vector). "ini" is reserved. |
attributes |
map | {} |
Operator-supplied key/value pairs available to feature-flag targeting. See Feature flags. |
supervisor — Runtime settings
| Field | Type | Default | Description |
|---|---|---|---|
state_dir |
path | /var/lib/ampbase |
Directory for state.json and instance_uid. Created with mode 0700. |
log_level |
string | "info" |
"trace", "debug", "info", "warn", "error". |
log_filter |
string | — | Per-module filter using tracing-subscriber EnvFilter syntax, e.g. "ampbase::transport=debug,info". Overrides log_level. |
health_check_delay_ms |
u64 |
2000 |
Wait after config apply before running the post-apply health check. |
startup_delay_ms |
u64 |
1000 |
Wait after agent start before the initial health check. |
reload_grace_period_ms |
u64 |
5000 |
Wait after SIGHUP before health-checking. Used by fluent-bit and vector. |
validation_timeout_seconds |
u64 |
10 |
Timeout for telegraf --test pre-validation. |
metrics_port |
u16 |
9464 |
Prometheus metrics endpoint port. |
max_memory_mb |
u64 |
— | Soft cap. Logs a warning when the managed agent's VmRSS exceeds this. Use cgroups or systemd MemoryMax= for hard limits. |
AI coding-agent policy
When agent.type is coding-agents, the supervisor does not manage a single agent binary. Instead it runs a loopback gateway the coding-agent runtimes export to, plus an events pipeline that redacts each event by policy. The supervisor.gateway block enables it:
agent:
type: coding-agents
supervisor:
state_dir: /var/lib/ampbase
gateway:
enabled: true
http_port: 4319 # loopback port the runtimes export to
reduction:
enabled: false
events:
enabled: true
Intelligence export uses this same gateway — on any agent type, with no config. The loopback gateway is also the receiver for reduced-telemetry export (Fluent Bit / Vector / OTel Collector / Telegraf, not just coding-agents). You do not set
gateway.enabledfor that: when a channel enables intelligence export in the dashboard, Ampbase sends anampbase.intelligence_exportoffer over OpAMP and the supervisor starts the receiver on demand on the first offer, forwarding the reduced stream to Ampbase. Setgateway.enabled: trueonly to eager-start the receiver at boot — which coding-agent fleets need because their events pipeline must be listening before the runtimes launch.
The channel config for this type is not agent YAML — it is a small policy document, authored in the dashboard and delivered verbatim over OpAMP. This is the one canonical schema: Ampbase renders it and the supervisor parses it, and a contract test pins the two sides so they cannot drift.
version: 1
# Which runtimes to wire. A scalar bool is sugar for
# {enabled: <bool>, metrics: true, logs: true}. `false` removes existing
# wiring; an omitted runtime is left untouched.
runtimes:
claude-code: true
codex-cli: true
cursor: false
gemini-cli: false
vscode-copilot: false # cooperative-mode only — see below
# Events-pipeline policy: redaction tier, local-retention bounds, and
# forwarding gates. Versioned, rolled back, and canaried like any config.
events:
tier: metadata-only # metadata-only | no-content | full
retention:
max_megabytes: 100
max_age_days: 7
forwarding:
ampbase_ingest: false # forward redacted events to Ampbase ingest
customer_otlp: false # forward to a customer SIEM (the gate)
# The customer SIEM endpoint is authored with the policy but reaches the
# supervisor over OpAMP `other_connections` (ampbase.events_customer_otlp),
# NOT this document — the supervisor ignores otlp_endpoint here.
otlp_endpoint: ""
Defaults are local-first: tier metadata-only, both forwarding gates off, retention 100 MB / 7 days. Nothing leaves the workstation until an admin opts in. Unknown events.* fields are ignored (logged at debug), not rejected, so a newer dashboard can add fields without breaking older supervisors.
| Field | Values | Meaning |
|---|---|---|
events.tier |
metadata-only | no-content | full |
How much of each event survives redaction. no-content guarantees no prompt text leaves the device. |
events.retention.max_megabytes |
int | Size cap on the local JSONL event log. Default 100. |
events.retention.max_age_days |
int | Age cap on the local JSONL event log. Default 7. |
events.forwarding.ampbase_ingest |
bool | Forward redacted events to Ampbase ingest. Default false. |
events.forwarding.customer_otlp |
bool | Forward redacted events to a customer SIEM. Default false. The endpoint is delivered over OpAMP, not this document. |
events.log_dir |
path | Where the events log and status.json are published. ampbase enroll sets /var/log/ampbase for an enforced install so ampbase status and ampbase events tail work without sudo; unset keeps both in state_dir. |
What the tier stamps on each record
Every forwarded record carries ampbase.redaction.location, saying where redaction ran for that record. A SIEM consuming records from more than one path needs it: without it, a record that was never redacted device-side is indistinguishable from one that was.
| Value | Set by | Means |
|---|---|---|
source+gateway |
supervisor, under metadata-only and no-content |
Two layers. The runtime's own content flags are pinned off at the source, and the gateway's field-wise redaction ran on top. |
gateway |
supervisor, under full |
One layer. Content flags are pinned on — prompt text flows from the runtime by design — and only the gateway's hard-secret denylist ran. |
none |
Ampbase, on the cloud-agent OTLP route | No device-side redaction at all. There is no supervisor on that path, so no tier applies. |
Read it as a scale: each step up is one more redaction layer between the runtime and the stream. The supervisor never stamps none — only the ingest route can know that device-side redaction never ran, so the value is set by the side that actually knows.
Codex CLI: managed configuration
Enabling codex-cli wires the [otel] exporters into /etc/codex/managed_config.toml — Codex CLI's native managed configuration layer (one Unix path for Linux and macOS; the enroll-generated service unit grants the supervisor write access to /etc/codex). Codex reads this file on every run and applies it above the developer's ~/.codex/config.toml and CLI --config overrides, so the managed telemetry wiring cannot be shadowed by per-user settings. No environment variables or extra steps are required.
Notes:
- The supervisor manages
otel.exporter,otel.metrics_exporter, andotel.log_user_promptin that file; other admin-managed Codex settings are preserved.otel.log_user_promptis driven by the policy tier:trueunderfull, explicitlyfalseotherwise, so a developer's own enablement cannot ship prompt content into a restrictive channel's gateway (source-side redaction beneath the gateway's per-tier redaction). - On macOS MDM fleets, Codex also accepts managed config via a device profile (
com.openai.codex,config_toml_base64), which takes precedence over the file — avoid pushing an[otel]table that way, or it will shadow the supervisor's. - For user-context or CI installs where
/etcis unwritable, redirect the writer with$CODEX_HOME(it then edits$CODEX_HOME/config.toml) orAMPBASE_CODEX_CONFIG_PATH. - On Codex releases predating managed configuration, the managed file is ignored — Codex shows as wired but silent in the fleet view; upgrade Codex or use the
AMPBASE_CODEX_CONFIG_PATHredirection. - Workstations enrolled before supervisor v0.6.0 need a re-enroll (or regenerated unit) to pick up the
/etc/codexwrite grant.
Gemini CLI: system settings
Enabling gemini-cli writes a telemetry block into Gemini CLI's system settings file — /etc/gemini-cli/settings.json on Linux, /Library/Application Support/GeminiCli/settings.json on macOS (the enroll-generated unit grants the supervisor write access to /etc/gemini-cli). Gemini reads this file natively at the highest precedence of any settings file — above the user's ~/.gemini/settings.json and any project .gemini/settings.json — so the managed telemetry wiring cannot be shadowed by per-user settings. Verified against google-gemini/gemini-cli v0.49.
Notes:
- The supervisor owns
telemetry.enabled,target,useCollector,otlpEndpoint,otlpProtocol, andlogPrompts; other settings (includingtelemetry.tracesandtelemetry.outfile) are preserved.logPromptsis driven by the policy tier —trueunderfull, explicitlyfalseotherwise. Gemini's vendor default islogPrompts: true, so the explicit off-pin is what keeps a developer's own (or the vendor's default) enablement from shipping prompt content into a restrictive channel's gateway. - Gemini has a single
enabledtelemetry switch covering metrics and logs together (there is no per-signal split;tracesis separate and left alone). A metrics-only or logs-only policy therefore turns on both. - The default OTLP/HTTP wiring reaches the gateway on Gemini >= v0.34.0 — no per-runtime change needed. Older Gemini (< v0.34.0) mis-posted OTLP/HTTP to the endpoint root instead of the
/v1/{signal}paths (google-gemini/gemini-cli#15581), fixed upstream in v0.34.0 (2026-03-17). There is no per-runtime transport override today — the policy'sotlp.endpoint/otlp.protocolare shared by every runtime, and moving them to a gRPC port would break Cursor's hook relay (it POSTs to that same endpoint) — so the fix for a pre-v0.34.0 Gemini is to upgrade Gemini, not to switch the fleet transport. GEMINI_TELEMETRY_*environment variables and a lower-scopetelemetry.outfilesit above what a settings file can control (env overrides every file; a mergedoutfilediverts telemetry to disk). These launch-environment surfaces are documented, not defeated.- For user-context or CI installs where
/etcis unwritable, redirect the writer withAMPBASE_GEMINI_CLI_SETTINGS_PATH, or enroll with--user(it targets~/.gemini/settings.json).
VS Code Copilot Chat: cooperative-mode only
Enabling vscode-copilot wires the GitHub Copilot Chat extension's native OpenTelemetry export by owning its github.copilot.chat.otel.* keys in VS Code's user settings.json (~/.config/Code/User/settings.json on Linux, ~/Library/Application Support/Code/User/settings.json on macOS). Verified against microsoft/vscode-copilot-chat's monitoring docs (2026-07). The supervisor owns otel.enabled, otel.exporterType, otel.otlpEndpoint, and otel.captureContent; every other setting is preserved.
This runtime is cooperative-mode-only. Unlike the others, VS Code exposes no admin-layer surface the supervisor can own to make the wiring non-shadowable: its enterprise-policy layer (/etc/vscode/policy.json) covers only the fixed set of settings VS Code declares as policies, and — as of the current policy reference — none of the github.copilot.chat.otel.* settings are among them (extensions cannot self-register policies). We therefore ship it cooperative-mode-only rather than counterfeit the enforced guarantee with a user-scope write:
ampbase enroll --userwires it in the developer's ownsettings.json.- In an enforced enrollment it is not wired:
ampbase status, the fleet chip, andpipeline_statusreport it as cooperative-mode-only (a documented, deferred runtime — not a fault), and no user file is written. It will graduate to enforced automatically if VS Code grows an enforceable policy/managed-settings surface for the OTel keys.
Notes:
- Copilot Chat has a single
enabledtelemetry switch covering metrics and logs together (tracesis separate and left alone), like Gemini. The default OTLP/HTTP wiring reaches the gateway (the exporter posts to the spec/v1/{signal}paths).captureContentis tier-driven —trueunderfull, explicitlyfalseotherwise. COPILOT_OTEL_*/OTEL_EXPORTER_OTLP_*environment variables override the settings file, and an org GitHub policy can disable Copilot telemetry above the supervisor entirely.- VS Code
settings.jsonmay be JSONC (comments / trailing commas). This writer edits it as strict JSON and refuses (degrades, never clobbers) a file it cannot parse as strict JSON, rather than stripping a developer's comments. Redirect non-standard installs (Insiders, portable) with the enroll--vscode-settings-pathseam.
CLI flags
| Flag | Description |
|---|---|
--config, -c |
Path to the supervisor config. Default: supervisor.yaml. |
--insecure |
Disable TLS certificate validation. Development only. Intentionally a CLI flag (not a config field) to prevent accidental production use. |
--version |
Print version and exit. |
CLI subcommands
With no subcommand the binary runs the supervisor daemon. The subcommands below support the AI coding-agent flow.
ampbase enroll
One-shot enrollment for a coding-agent workstation: writes supervisor.yaml (agent.type: coding-agents), applies the initial runtime wiring, and installs + starts the platform-native service — a systemd unit on Linux, a launchd job on macOS.
sudo ampbase enroll --key agent_… --endpoint https://<org>.ampbase.io/channels/<channel>/v1/opamp
| Flag | Default | Description |
|---|---|---|
--key |
required | Channel-scoped agent key. |
--endpoint |
required | OpAMP control-plane endpoint for this org + channel. |
--user |
— | Cooperative (user) mode: no sudo anywhere. See below. |
--config |
/etc/ampbase/supervisor.yaml (~/.config/ampbase/supervisor.yaml with --user) |
Where to write the generated config. |
--state-dir |
/var/lib/ampbase (~/.local/share/ampbase with --user) |
Supervisor state directory. |
--gateway-http-port |
4319 (allocated + persisted with --user) |
Loopback gateway HTTP port the runtimes export to. |
--runtimes |
claude-code |
Comma-separated runtimes to wire. |
--transport |
http |
OpAMP transport (http or websocket). |
--skip-service |
— | Write files and wire runtimes but don't install/start the service (for MDM base images). |
Cooperative mode: ampbase enroll --user
--user enrolls with no sudo anywhere: the wiring targets the developer's own configs (~/.claude/settings.json, ~/.codex/config.toml, ~/.cursor/hooks.json, ~/.gemini/settings.json), the service installs per-user (systemd --user on Linux, a LaunchAgent on macOS; hosts without a user service manager — devcontainers, sandboxes — still enroll and get the foreground run line), and the loopback gateway ports are allocated once and persisted in the state dir so two users on one machine never collide and re-enrolls don't churn the runtime configs.
The trade is explicit and badged: in cooperative mode the developer can edit or stop everything — removal's only signal is the workstation going stale — so the fleet view, agent drawer, channel rollup, and ampbase status all label the workstation cooperative (enforced installs carry no badge). Policies can target the mode via the mode agent attribute, and a channel can refuse cooperative workstations outright with the Workstation modes: enforced-only channel setting (rejected connections read cooperative-disallowed). A cooperative supervisor that detects enforced (admin-layer) wiring for a runtime on the same machine stands its own writer down for that runtime and reports "enforced elsewhere" — so installing the system supervisor later never produces duplicate Cursor hook events, and graduation from cooperative to enforced is incremental.
ampbase status
Shows what this workstation collects and where it goes. Reads local files only — works offline, never touches the network.
ampbase status [--config <path>]
Prints the enrolled runtimes, the active tier, what stays on-device vs. what forwards, and the local log path. When the host isn't enrolled it says so.
Both commands colour their output when stdout is a terminal, and print plain text when it is redirected or piped, when NO_COLOR is set, or under TERM=dumb. events tail --json is never coloured.
Neither command needs sudo. That is the point of them: the developer being observed can see what is collected, on any enrollment, from their own account.
Without --config, both read this machine's enrollment: the per-user config a cooperative (--user) enrollment writes if there is one, else /etc/ampbase/supervisor.yaml. On an enforced workstation neither of those files is readable by a developer — supervisor.yaml carries the agent key, state.json the per-instance credential, and both stay root-only. So the supervisor publishes the answer instead of the sources:
| Path | Mode | Holds |
|---|---|---|
/var/log/ampbase/status.json |
0644 |
what the status screen prints — tier, forwarding, wired runtimes, policy version, log path |
/var/log/ampbase/events.jsonl |
0644 |
the post-redaction events log |
/var/lib/ampbase/state.json |
0600 |
the credential and the applied config — never published |
Both files are rewritten on every config apply, and once at enrollment so a freshly enrolled workstation answers before its first apply. status.json carries only what the screen already shows: no key, no destination URL, no header.
On a shared enforced host this means any local user can read the coding-agent activity of any other. If that is not the trade you want for a particular fleet, point supervisor.gateway.events.log_dir at a directory with the ownership you need — the supervisor writes both files there instead, and the commands fall back to sudo.
A cooperative (--user) enrollment keeps both files in the per-user state directory, which the developer already owns.
ampbase events tail
Prints the most recent post-redaction events from the local JSONL log — precisely what would leave the device, not a summary of it.
ampbase events tail [-n 20] [--json] [--config <path>]
| Flag | Default | Description |
|---|---|---|
-n, --lines |
20 |
Number of trailing events to show. |
--json |
— | Print raw JSONL lines instead of the formatted one-line view. |
State directory
The supervisor persists state under state_dir as two files:
state.json— last-applied config hash, the effective config and the previous config (both inline as base64-encoded payloads keyed by filename), the managed agent's PID for re-adoption on supervisor restart, and the restart counter. On startup the supervisor reapplieseffective_configso a brief outage on the Ampbase side never leaves agents config-less;previous_configis the rollback target if a future apply fails.instance_uid— the 26-character ULID that identifies this supervisor instance to OpAMP. Persisted so a restart re-adopts the same agent record rather than registering as a new one.
The directory is created with mode 0700 on first start; do not relax those permissions. state.json is written 0600 — it carries the per-instance credential this workstation authenticates with.
The events log does not live here on an enforced install: it and status.json are published to events.log_dir (/var/log/ampbase) so a developer can read them without sudo. See ampbase status.
Component inventory
The supervisor reports which components are compiled into the agent binary it manages — the receivers, exporters, plugins and sinks that build actually has. Ampbase uses it to decide whether a config version can run on a given agent before routing it there (see Feature flags, under Build eligibility).
Requires supervisor 0.14.0 or newer. Older supervisors don't report an inventory at all, and Ampbase says so rather than guessing.
What runs
Each agent type is enumerated with its own command, on the binary at agent.binary_path:
| Agent type | Command |
|---|---|
otelcol |
otelcol components |
fluent-bit |
fluent-bit --help-json |
vector |
vector list --format json |
telegraf |
telegraf plugins |
refinery, tetragon and coding-agents have no build-time component axis — all builds are equivalent and configuration decides everything — so nothing is enumerated for them, and they are never held back for a missing component.
Components are identified as {kind}/{name} in the agent's own vocabulary: exporter/kafka on a collector, output/kafka on a Fluent Bit, inputs/cpu on a Telegraf. Those are unrelated identifiers, never translated into a shared naming scheme — a config written for one agent's vocabulary is only ever compared against a build of that same agent.
When it runs
- At supervisor start, so the first message Ampbase receives already carries the inventory.
- After each successful config apply. A failed apply is rolled back, so the agent is still running the config the stored inventory was read against and nothing is re-read.
The result ships on the next check-in rather than with the apply itself, so it lands one cycle behind.
Re-reading after an apply is what makes a fresh enrollment converge. Fluent Bit's inventory depends on its config (below), so an agent that has just enrolled and been given nothing yet has nothing to enumerate. Its agent page reads Not yet reported until its first config applies, then fills in on the following check-in. That's the expected sequence on enrollment day, not a fault.
A binary swapped underneath a running supervisor without a config apply isn't noticed — nothing watches the file — so the reported inventory stays as last measured until the next restart or apply.
Fluent Bit: external plugins are included
Fluent Bit's --help-json enumerates only what's loaded, and it doesn't read your config. So the supervisor reads the plugin paths your config declares and passes them to the enumeration itself, which means config-declared external plugins appear in the inventory alongside the built-ins. Duplicate paths are collapsed first, so a plugin declared twice is listed once.
A plugin path that fails to load makes the whole enumeration fail — which is the right answer, because it's the same failure Fluent Bit itself would hit when starting with that config.
When enumeration fails
If the command errors, times out, or produces output the supervisor can't parse, the result is "we could not look" — never an empty inventory:
- If the supervisor has read the build successfully before, it keeps that inventory and reports nothing new. A binary that hasn't changed is still described by the last good reading.
- If it has never succeeded, nothing is reported, and the agent shows as Not yet reported in Ampbase.
Either way the reason is logged on the host at warn level, which is where to look first. Ampbase counts an agent it can't describe as unknown — neither able nor unable to run a given config — and unknown is stated everywhere it applies rather than folded into either answer.
The consequence to know: an agent whose build Ampbase can't establish is held back from flag variants that require components, and keeps receiving the channel default.
Metrics
The supervisor exposes Prometheus metrics on metrics_port. Key series:
opamp_supervisor_connected— 1 when the OpAMP session is open.opamp_supervisor_config_updates— config-update counter.opamp_supervisor_agent_health— last health-check result (gauge).opamp_supervisor_agent_restarts— agent restart counter.
Pull these into the same scrape that grabs your collector's metrics for end-to-end visibility.
Full example
version: 1
server:
endpoint: wss://acme.ampbase.io/channels/clickhouse/v1/opamp
api_key: agent_01hxj7k9_8f2c4a7e9b1d3a5f7e2c4a7e9b1d3a5f7e2c4a7e9b1d3a5f
transport: websocket
max_message_size_kb: 64
agent:
type: otelcol
binary_path: /usr/local/bin/otelcol
config_path: /etc/otelcol/config.yaml
health_check_url: http://localhost:13133/
stop_timeout_seconds: 15
config_format: yaml
attributes:
env: production
region: us-east-1
role: edge-collector
supervisor:
state_dir: /var/lib/ampbase
log_level: info
log_filter: "ampbase::transport=debug,info"
health_check_delay_ms: 2000
startup_delay_ms: 1000
reload_grace_period_ms: 5000
metrics_port: 9464
max_memory_mb: 512