gRPC and conduitctl
This page is the connection and command reference for the optional control plane: enabling gRPC, pointing conduitctl at a server, and invoking each subcommand. For when to reload, apply an overlay, or export, see Reload and export. For how config layers merge, see Configuration model.
Enabling the control plane
Conduit starts the gRPC listener only when the process starts with a control: block that sets listen_address (for example 127.0.0.1:5199). Without it, DNS still runs but conduitctl apply, export, reload, trace, health, and live acl check are unavailable — use SIGHUP or a process restart to reload from disk instead. Offline validate --file and acl check --file still work.
Adding or changing control: via reload updates the stored config but does not start or rebind the listener today. Restart conduit after enabling or moving the control address.
Config fields: Reference: control.
Connecting
Every remote conduitctl subcommand shares one connect helper. Settings resolve in this order: CLI flags → environment variables → optional YAML client config file → built-in defaults. A missing client file is not an error.
Offline commands (validate --file, acl check --file) do not need the client file or a running control plane.
Client configuration file
Default path (YAML):
| Platform | Path |
|---|---|
| Unix (XDG) | $XDG_CONFIG_HOME/conduit/conduitctl.yaml, or ~/.config/conduit/conduitctl.yaml |
| Windows | %APPDATA%\conduit\conduitctl.yaml |
Override the path with --config or CONDUITCTL_CONFIG.
Example file:
endpoint: https://conduit.example:5199
api_key_file: ~/secrets/conduit-api-key # prefer a path over inline api_key
tls:
ca: ~/tls/ca.pem
cert: ~/tls/client.pem # mTLS when the server requires a client cert
key: ~/tls/client-key.pem
# insecure_skip_verify: true # opt-in only — see TLS below
Flags, env, and file fields
| Setting | Flag | Env | Client file |
|---|---|---|---|
| Endpoint | --endpoint |
CONDUIT_CONTROL |
endpoint |
| API key (inline) | --api-key |
CONDUIT_API_KEY |
api_key (discouraged) |
| API key file | --api-key-file |
CONDUIT_API_KEY_FILE |
api_key_file |
| Trust CA / bundle | --tls-ca |
CONDUIT_TLS_CA |
tls.ca |
| Client cert (mTLS) | --tls-cert |
CONDUIT_TLS_CERT |
tls.cert |
| Client key (mTLS) | --tls-key |
CONDUIT_TLS_KEY |
tls.key |
| Skip verify | --tls-insecure |
CONDUIT_TLS_INSECURE |
tls.insecure_skip_verify |
| Client config path | --config |
CONDUITCTL_CONFIG |
— |
Built-in default endpoint: http://127.0.0.1:5199. Use http:// for plain TCP; use https:// when control.tls is configured on the server.
TLS verification (HTTPS)
For https:// endpoints, conduitctl by default:
- Validates the server certificate chain against the configured trust store (
--tls-ca/tls.ca, otherwise the client’s normal trusted roots), and - Verifies that the certificate identity matches the endpoint hostname (DNS SAN / usual TLS rules, or IP SAN when the host is an IP).
Present a client certificate and key when the server sets control.tls.client_ca_path (mTLS). Server-side setup: mTLS.
Skip-verify (--tls-insecure / CONDUIT_TLS_INSECURE / tls.insecure_skip_verify) disables chain and hostname verification. It is a supported opt-out for self-signed server certificates when you do not distribute a CA to every client host. Prefer pinning a CA when practical. Skip-verify is never the default and is not implied by using https:// or setting other TLS fields. Treat it as a weakened transport authentication mode.
On success, mutating commands print ok to stdout (and may print generation=… when the server returns it). Failures exit non-zero with error text on stderr.
Authentication
| Server config | Client requirement |
|---|---|
control.api_keys empty |
No credentials (anonymous access to control RPCs) |
control.api_keys non-empty |
Valid key via Authorization: Bearer … or header x-api-key — conduitctl uses Bearer (--api-key / CONDUIT_API_KEY) |
control.tls.client_ca_path set |
Server requires a client certificate (mTLS) in addition to any API key rules |
Config control vs runtime control
Two families share the same gRPC listener and conduitctl connect settings:
| Family | What it changes | Examples | Shows up in export? |
|---|---|---|---|
| Config control | Effective configuration (file layer and/or overlay) through the Configurator | apply, reload, export, typed primitives (pool, backend, orchestrator, …) |
Yes — after a successful mutation |
| Runtime control | Observed operator state outside effective config | health freeze / drain / resume |
No — health runtime state is separate |
Changing a backend’s weight is config control (conduitctl backend set-weight or an overlay apply). Drain / freeze is runtime control (conduitctl health) and is not a substitute for a weight change. Details: Backend health.
Commands
| Command | Needs server? | Purpose |
|---|---|---|
conduitctl apply |
Yes | Patch the in-memory overlay (apply modes) |
conduitctl export |
Yes | Print effective config as YAML |
conduitctl reload |
Yes | Reload from disk; clear overlay |
conduitctl validate --file |
No | Offline YAML validation and runtime snapshot compile (Rhai, data sources, forward) |
conduitctl acl check |
Yes (default) | Dry-run client ACL for an IP against the live snapshot |
conduitctl acl check --file |
No | Same dry-run against a local config file (twin of validate) |
conduitctl trace |
Yes | Fetch pipeline trace events for a transaction id |
conduitctl health |
Yes | Per-backend health show, freeze, set up/down (drain), resume automatic |
conduitctl pool / backend |
Yes | Typed pool list/get; backend set-weight / remove |
conduitctl orchestrator |
Yes | Get / set overlay-hot orchestrator limits |
conduitctl data-source / data-source-limits |
Yes | List/get/upsert/remove data sources; get/set limits |
conduitctl events |
Yes | Get events; get/set filters and emit on existing sinks |
conduitctl rhai |
Yes | Get / set Rhai sandbox limits |
conduitctl metrics |
Yes | Get / patch metrics plan (deep merge) |
conduitctl cache |
Yes | List/get; set hot cache knobs (max_entries, LMDB policy, …) |
RPC methods and messages: Reference: gRPC and CLI.
Document apply vs typed primitives
Both paths update the same effective config and runtime snapshot:
- Document workflow — author sparse YAML and
conduitctl apply(merge / replace / clear), or edit the startup file andreload. Best when configuration management owns patches or you need a multi-field change in one file. - Primitive workflow — typed
conduitctlsubcommands (and the matching config-primitive gRPC services) for overlay-hot settings. Best for surgical automation (set one weight, adjust a limit). Internally the server mutates effective config, synthesizes a full overlay replacement, then validates and swaps — so interleaved document apply and primitives keep unrelated overlay fields.
Primitives apply only where overlay is allowed and the change takes effect without process restart (including live reconcile after snapshot swap). They do not expose restart-pending-only knobs (for example listener bind, orchestrator.txn_table_capacity, event sink add/remove / queue_depth, caches[].memory.shard_count). Prefer document apply or a restart for those.
export always shows effective config (no remove-marker tombstones). Mutating apply/reload/primitive responses include generation (correlates with conduit_config_generation) and optional status notes.
apply
conduitctl apply --file patch.yaml # default: merge into overlay
conduitctl apply --merge --file patch.yaml # explicit merge
conduitctl apply --replace --file patch.yaml # replace entire overlay
conduitctl apply --clear # clear overlay; no --file
| Flag | Conflicts with | Behavior |
|---|---|---|
| (default) | — | Merge patch into accumulated overlay |
--merge |
--replace, --clear |
Explicit merge (same as default) |
--replace |
--merge, --clear |
Replace overlay; schema_version-only patch clears overlay |
--clear |
--merge, --replace, --file |
Clear overlay without re-reading startup file |
--file is required for merge and replace; omit it for --clear.
Patch files are sparse YAML — only keys you include are sent. Overlays must not include rules: or tracing:; apply is rejected if those sections are present. metrics: is allowed (deep merge). Semantics, examples, and overlay scope: Reload and export — apply modes. To remove a pool or backend via overlay, set remove: true on the matched entry — see Configuration model — remove marker.
export
conduitctl export # stdout
conduitctl export --output PATH # write file
Returns effective config as YAML (file layer + overlay, defaults normalized). See Reload and export — export.
reload
conduitctl reload
Re-reads the config path from process startup and clears the overlay. Same semantics as SIGHUP when gRPC is enabled. See Reload from disk.
validate
conduitctl validate --file PATH
Runs locally — no control plane connection. Validates YAML structure, then builds the same runtime snapshot Conduit uses at startup and reload: Rhai scripts are read and compiled, data sources are loaded, and forward settings are compiled. Paths resolve relative to the config file directory (or as absolute paths). Failures print prefixed errors (for example script '…': …, rule '…': …, data source '…': …) to stderr and exit non-zero.
The server also exposes ValidateConfig over gRPC for automation that already talks to the control plane; the CLI does not call it today.
acl check
Dry-run client ACL evaluation for one IP. Prints pretty JSON to stdout. Exit code is 0 whenever the check itself succeeds (connect/load/evaluate); the JSON decision fields carry admit / drop / refuse / tag. The check is read-only — it does not bump ACL metrics or emit denial logs.
conduitctl acl check 203.0.113.50
conduitctl acl check 203.0.113.50 --listener public
conduitctl acl check 10.1.2.3 --file /path/to/conduit.yaml
conduitctl acl check 10.1.2.3 --file /path/to/conduit.yaml --listener public
| Mode | Flag | What is evaluated |
|---|---|---|
| Live (default) | (none) | Running process snapshot + in-memory CIDR tables (includes overlay) via CheckAcl |
| File | --file PATH |
Local compile of that YAML (same path resolution as validate) — no control plane |
Omit --listener to return one result object per listener. Filter with --listener NAME (resolved listener name, including the default protocol:address form). Unknown listener or invalid IP exits non-zero.
JSON shape:
{
"ip": "10.1.2.3",
"source": "live",
"results": [
{
"listener": "public",
"decision": "admit",
"matched": "corp_nets",
"action": "accept"
},
{
"listener": "internal",
"decision": "tag",
"tag": "corp",
"matched": "corp_nets",
"action": "tag"
}
]
}
source is live or file. decision is admit, drop, refuse, or tag (tag name in sibling tag). matched is the CIDR view name, or default when default_action applied. action is the matched rule action, or allow / deny for the default.
trace
conduitctl trace TXN_ID
Prints pipeline trace events when tracing captured the transaction. Exits non-zero if no trace was found.
health
Per-backend health inspection and operator controls. Requires the control plane. Behavior: Backend health. RPC reference: Reference: gRPC and CLI — BackendHealth.
conduitctl health show
conduitctl health show --pool default
conduitctl health show --pool default --backend resolver-a
conduitctl health freeze --global
conduitctl health freeze --pool default
conduitctl health freeze --pool default --backend resolver-a
conduitctl health set down --pool default --backend resolver-a
conduitctl health set up --pool default --backend resolver-a
conduitctl health resume --global
conduitctl health resume --pool default --backend resolver-a
| Subcommand | Purpose |
|---|---|
show |
Print observed/applied health, scope, eligibility, latency EWMA per backend |
freeze |
Freeze — stop probe-driven changes to applied at the scope (--global, --pool, or --pool + --backend) |
set up\|down |
Manually set applied health and imply freeze (drain = set down) |
resume |
Resume automatic — unfreeze and snap applied to observed |
--backend accepts the configured backend name or host:port address. Prefer health resume over ad-hoc clear sequences while frozen — see Clear-while-frozen.
Config primitives (examples)
# Pools / backends
conduitctl pool list
conduitctl pool get edge
conduitctl backend set-weight --pool edge --backend primary --weight 50
conduitctl backend remove --pool edge --backend secondary
# Orchestrator / Rhai limits (hot fields only)
conduitctl orchestrator set-limits --max-attempts 5
conduitctl rhai set-limits --max-operations 20000
# Metrics plan (deep-merge patch)
conduitctl metrics patch --enabled false
# Cache hot knobs
conduitctl cache set-max-entries --name answers --max-entries 2500
# Event filters on an existing sink (not sink add/remove)
conduitctl events set-filters --name lab-tap --sample-percent 25
gRPC services for these commands: Reference: gRPC and CLI — Config primitive services.
Access logs
Every control RPC logs at info as control rpc (the transport line): gRPC method (rpc), peer address (peer), whether the connection used TLS (tls: true/false — transport encryption, distinct from requestor mtls), requestor identity (requestor: anonymous, API key, mTLS, or rejected), gRPC status (grpc_code, e.g. Ok, InvalidArgument), and latency (latency_ms). Request and response bodies are not logged.
Connections that never become an RPC — TCP accept errors, or TLS handshake failures (wrong protocol, bad/missing client certificate, etc.) — log at warn as control plane connection failed with tls, error, and peer when known.
Config RPCs (ApplyConfig, ValidateConfig, ReloadFromFile, and mutating config primitives) additionally emit a separate control rpc outcome line (the application line) with rpc, outcome (ok or rejected), error_count, and the joined errors. Successful outcomes (outcome=ok) log at info; rejections (outcome=rejected) log at warn so failed apply/validate/reload attempts stand out while the last-good snapshot stays active.
The two lines report different layers. A config that fails validation is rejected in-band — the RPC still succeeds at the transport layer — so it logs control rpc with grpc_code=Ok and a control rpc outcome at warn with outcome=rejected and error_count>0. conduitctl surfaces the same rejection as a non-zero exit with the validation messages.
gRPC reflection
When control.reflection_enabled: true, Conduit registers the standard gRPC server reflection service for dev and test tooling (for example discovering ConduitControl without a local copy of the proto). Leave reflection off in production unless you need it.
Related topics
- Reload and export — workflows, apply modes, export-before-clear
- Configuration model — merge rules and overlay scope
- Config file — startup path used by reload
- Reference: gRPC and CLI — RPC and message reference
- Glossary — overlay, conduitctl, control plane