Skip to the content.

CLI Reference

conduit [OPTIONS] [COMMAND]

Table of Contents


Global options

These flags are accepted by every command.

Flag Short Default Description
--config FILE -c auto-discovered (see below) Config file path
--version -V Print version and exit
--help -h Print help and exit
--kubernetes-namespace NS Read config from Kubernetes ConduitSite CRDs instead of a file. "*" watches all namespaces. Requires --features kubernetes. See Kubernetes CRD mode.

Config file discovery

When no -c flag is given, Conduit resolves the config in this order:

  1. conduit.json — if this file exists, use it
  2. conduit.yaml — fallback if conduit.json is absent
  3. conduit.yml — fallback if neither of the above exists

Auto-discovery only applies when no -c flag is given. When you pass -c some-other-name.json, that exact path is used with no fallback.

Both JSON and YAML are supported everywhere -c is accepted.


Server commands

start (default)

Start the reverse proxy server.

conduit                       # reads conduit.json (or .yaml / .yml)
conduit -c /etc/conduit.yaml  # explicit config path

On startup, Conduit:

  1. Loads and validates the config — exits 1 with a field-level error message on failure
  2. Binds all configured ports
  3. Starts the Admin API on global.admin.bind (only if global.admin is configured)
  4. Begins serving traffic

If hotReload is enabled, Conduit watches the config file and reloads it automatically when it changes — no restart needed.


validate

Parse and validate the config file, then exit. Does not start the server.

conduit validate
conduit validate -c staging.yaml

On success (exit 0):

Config is valid — 2 sites, 5 routes.

On failure (exit 1):

error at proxy./api.retry.attempts: must be > 0
error at rateLimit.windowSecs: missing required field

2 errors found.

Error messages include the exact JSON path to the invalid field, making it easy to locate the problem even in large config files.

Use in CI:

conduit validate -c conduit.yaml && echo "Config OK"

fmt

Normalize and pretty-print the config, preserving the input format: .yaml / .yml files stay YAML, .json files stay JSON.

# Print to stdout (useful for diffing)
conduit fmt
conduit fmt -c conduit.yaml   # → pretty YAML
conduit fmt -c conduit.json   # → pretty JSON

# Overwrite the file in place
conduit fmt --write
conduit fmt --write -c conduit.yaml

--write rewrites the config file in place. Useful for normalizing key order and whitespace after manual edits.


init

Interactive wizard that generates a starter config in YAML or JSON. All questions can be skipped with flags — useful for scripts and CI pipelines.

# Interactive (default)
conduit init

# Non-interactive: accept all defaults
conduit init --yes
conduit init -y

# Non-interactive with overrides
conduit init -y --port 3000 --proxy http://localhost:4000
conduit init -y --format yaml --port 443 --tls-acme admin@example.com -o prod.yaml

# Format inferred from -o extension
conduit init -o conduit.yaml   # YAML
conduit init -o conduit.json   # JSON

--yes defaults:

Setting Default
format yaml
port 8080
static ./dist (enabled)
proxy disabled
TLS disabled
health check enabled
log format dev

All flags:

Flag Short Description
--output FILE -o Output file path (format inferred from extension)
--yes -y Non-interactive: accept all defaults, no prompts
--format <yaml\|json> Output format (overrides extension inference)
--port N Port number [default: 8080]
--static-dir DIR Serve static files from DIR
--no-static Disable static file serving
--proxy URL Proxy requests to upstream URL
--no-proxy Disable proxy
--log <dev\|json\|combined\|none> Log format [default: dev]
--no-health Disable /__health__ endpoint
--tls-cert FILE TLS certificate file (enables manual TLS)
--tls-key FILE TLS private key file (required with --tls-cert)
--tls-acme EMAIL ACME email (enables Let’s Encrypt auto-TLS)

When both --yes and individual flags are given, the flags override the defaults. Any setting not covered by a flag is silently set to its default without prompting.


probe

Send a HEAD request to every upstream URL defined in the config and print a latency table. All upstreams are probed in parallel.

conduit probe
conduit probe -c production.yaml

Example output:

Probing 4 upstream(s) in parallel...

URL                         Status  Latency
────────────────────────────────────────────
✗ http://api-3:4000         timeout  10001 ms
✓ http://api-1:4000         200      12 ms
✓ http://api-2:4000         200      14 ms
✓ https://payment-svc:8443  200      31 ms

3/4 upstreams healthy

Results are sorted so failures appear first. Exits 1 if any upstream is unhealthy (status ≥ 500 or connection failure), 0 if all pass.

Notes:


Admin commands

These commands talk to the running server’s Admin API. See docs/admin.md for the full HTTP API reference, authentication details, and all endpoint request/response schemas.

The Admin API HTTP server only starts when global.admin is configured. Without it, admin CLI commands will fail to connect.

The admin address (where the CLI connects to) is resolved in this order:

  1. --admin ADDR flag
  2. CONDUIT_ADMIN environment variable
  3. Default: 127.0.0.1:2019

The default 127.0.0.1:2019 is only the CLI connection target. The server does not open this port unless global.admin.bind is set in config.

When global.admin.token is set on the server, authenticate CLI commands via the CONDUIT_ADMIN_TOKEN environment variable:

export CONDUIT_ADMIN_TOKEN="my-secret-token"
conduit status     # token sent automatically
conduit reload     # token sent automatically

For direct curl calls, pass the token as a Bearer header:

curl -H "Authorization: Bearer $CONDUIT_ADMIN_TOKEN" http://localhost:2019/status

reload

Hot-reload the config file without restarting the server.

conduit reload
conduit reload --admin 127.0.0.1:2019

The server re-reads the config file from disk and applies all hot-reloadable changes immediately. Fields that require a cold restart (port, TLS cert/key, workers) are ignored — the server logs a warning for each one.

Hot-reloadable: proxy routes, static paths, auth config, rate limits, middleware, logging, cache, CORS, security headers, transforms, fault injection.

Not hot-reloadable (restart required): port, tls.cert/key, tls.versions/ciphers, workers, backlog, global.admin.bind.


status

Print server status as JSON. Add --upstream to show a table of upstream health.

conduit status
conduit status --admin 127.0.0.1:2019

# Show upstream health table
conduit status --upstream

Default output (JSON):

{
  "version": "1.1.2",
  "uptime_secs": 3600,
  "inflight": 42,
  "sites": 2
}

--upstream output (table):

URL                      Healthy  Latency     Ejected  5xx
──────────────────────────────────────────────────────────
✓ http://api-1:4000      ✓        12 ms       no       0
✗ http://api-2:4000      ✗        —           yes      5

1/2 upstreams healthy
Flag Description
--admin ADDR Admin API address
--upstream Show upstream health table instead of server JSON

shutdown

Gracefully stop the server.

conduit shutdown
conduit shutdown --admin 10.0.0.1:2019

Conduit stops accepting new connections, waits for all in-flight requests to complete (up to global.shutdownTimeoutSecs), then exits.


upstreams

Manage upstream targets at runtime. Changes are in-memory only and are lost when conduit reload is run or the server restarts.

List upstreams

conduit upstreams
conduit upstreams --admin 127.0.0.1:2019

Prints all upstreams with health status, latency, and weight:

[
  {
    "route": "/api",
    "url": "http://api-1:4000",
    "healthy": true,
    "latency_ms": 12,
    "weight": 1,
    "ejected": false
  }
]

Add an upstream

conduit upstreams add --route /api --target http://api-3:4000
conduit upstreams add --route /api --target http://api-3:4000 --weight 2
conduit upstreams add --route /api --target http://api-3:4000 --site api.example.com:443
Flag Required Description
--route PATH Route path (e.g. /api)
--target URL Upstream URL to add
--weight N Weight for weighted-round-robin (default: 1)
--site LABEL Limit to a specific site (e.g. api.example.com:443). Omit to apply to all sites with this route

Remove an upstream

conduit upstreams remove --route /api --target http://api-3:4000
conduit upstreams remove --route /api --target http://api-3:4000 --site api.example.com:443
Flag Required Description
--route PATH Route path
--target URL Upstream URL to remove
--site LABEL Limit to a specific site

Change upstream weight

Only effective when the route uses strategy: weighted-round-robin.

conduit upstreams weight --route /api --target http://api-1:4000 --weight 3
conduit upstreams weight --route /api --target http://api-2:4000 --weight 1
Flag Required Description
--route PATH Route path
--target URL Upstream URL
--weight N New weight value
--site LABEL Limit to a specific site

Tooling

completions

Generate shell completion scripts.

# Bash
conduit completions bash >> ~/.bashrc
source ~/.bashrc

# Zsh
conduit completions zsh >> ~/.zshrc

# Fish
conduit completions fish > ~/.config/fish/completions/conduit.fish

# PowerShell
conduit completions power-shell >> $PROFILE

# Elvish
conduit completions elvish >> ~/.config/elvish/rc.elv

Supported shells: bash, zsh, fish, power-shell, elvish.

Completions cover all subcommands, flags, and (where statically known) their accepted values.


man

Generate a man(1) page in roff format and write it to stdout.

conduit man > conduit.1
man ./conduit.1

# Install system-wide (Linux)
conduit man | gzip > /usr/share/man/man1/conduit.1.gz
mandb

Environment variables

Variable Default Description
RUST_LOG warn Log level for the server process. Format: error\|warn\|info\|debug\|trace or per-crate: conduit=debug,pingora=warn
CONDUIT_ADMIN 127.0.0.1:2019 Admin API address used by reload, status, shutdown, and upstreams commands
CONDUIT_ADMIN_TOKEN Bearer token sent with every Admin API request from the CLI. Set when the server has global.admin.token configured.
CONDUIT_ACME_EXTRA_ROOT Path to a PEM CA file trusted for ACME HTTP client. For CI environments using test ACME servers (e.g. Pebble) with self-signed certificates

Config files also support $VAR interpolation — any environment variable can be referenced in field values:

tls:
  cert: $TLS_CERT_PATH
  key: $TLS_KEY_PATH
apiKey:
  keys: ["$API_KEY_1", "$API_KEY_2"]

Unknown variables are left as-is (the literal $VAR_NAME string). Variables are expanded at startup; hot-reload re-expands them from the current environment.


Exit codes

Code Meaning
0 Success
1 Error — config parse failure, validation error, upstream probe failure, Admin API unreachable, or other fatal error

The server process itself only exits with 1 on startup errors. Once running, it exits cleanly (0) after a graceful shutdown triggered by conduit shutdown or SIGTERM.


Kubernetes CRD mode

Requires --features kubernetes at compile time.

# Watch ConduitSite CRDs in a specific namespace
conduit --kubernetes-namespace default

# Watch all namespaces (requires cluster-wide RBAC)
conduit --kubernetes-namespace "*"

Instead of reading a config file, Conduit connects to the Kubernetes cluster (via KUBECONFIG or in-cluster service account), reads all ConduitSite custom resources, and starts serving. Changes to CRDs are hot-applied automatically — no restart needed.

When --kubernetes-namespace is set, the -c flag is ignored.

See deployment.md — ConduitSite CRD for the CRD schema and kubectl apply instructions.


Build features

Conduit uses compile-time feature flags to keep the binary lean and the attack surface small. cargo build --release with no flags produces the minimal build (default = []): core reverse proxy, TLS, static files, rate limiting, basic/API-key auth, compression, hot-reload, Prometheus metrics, health checks, and the Admin API. It’s the right choice for embedding Conduit in a larger system or for the smallest footprint.

The binaries and Docker images published as “standard” (the un-suffixed conduit-<target> downloads and the :latest image — see deployment.md) add the standard feature bundle on top of that: jwt + consumers + forward-auth + cache + acme, the auth/cache/ auto-TLS stack most self-hosted reverse-proxy / API-gateway deployments need. Reproduce it from source with --features standard.

# Minimal build (default = []) — embed-friendly, smallest footprint
cargo build --release

# Standard build — matches the published "conduit-<target>" binaries
cargo build --release --features standard

# Single optional feature
cargo build --release --features jwt
cargo build --release --features rhai
cargo build --release --features acme

# Full build — every feature enabled
cargo build --release --features full

# Select combination
cargo build --release --features "jwt,rhai,redis"

Feature overview

Feature What it enables Dependencies added
jwt JWT Bearer-token auth + JWKS URL jsonwebtoken
consumers Per-consumer credentials + rate limits
forward-auth ForwardAuth subrequest middleware
rhai Rhai scripting middleware (type: "script") rhai
wasm WASM plugin middleware (type: "wasm") wasmtime
tcp TCP passthrough proxy (type: "tcp" site)
upload File upload handler (upload: site config) multer
redis Redis-backed rate limiting & caching redis
cache Response caching (proxy.*.cache)
disk-cache Disk-backed cache store (cache.store: "disk:/…")
acme Auto-TLS / Let’s Encrypt (tls.acme) instant-acme, rcgen
fault-injection Fault injection for chaos testing
otlp OpenTelemetry OTLP tracing opentelemetry stack
kubernetes Kubernetes CRD config provider kube, k8s-openapi
standard Bundle: jwt + consumers + forward-auth + cache + acme (typical self-hosted reverse-proxy / API-gateway set) — used by the published “standard” binaries/images bundle, no extra deps of its own
full All of the above all of the above

When a feature is off but its config field is set, Conduit logs a warning at startup and continues with that feature disabled (fail-open, no crash).


jwt — JWT Bearer-token authentication

Enables jwtAuth site config. Supports HS256 (shared secret) and RS256/ES256 (remote JWKS URL). When disabled, jwtAuth config is ignored with a warning.

jwtAuth:
  jwksUrl: "https://auth.example.com/.well-known/jwks.json"
  issuer: "https://auth.example.com/"
  audience: ["my-api"]

Dependencies: jsonwebtoken = "9"


rhai — Rhai scripting middleware

Enables type: "script" middleware entries. Scripts run in a sandboxed Rhai engine with resource limits (500 000 operations, 1 MiB string, 65 536 array).

middleware:
  - type: script
    path: ./scripts/api-gate.rhai
    config: { api_key: "$SECRET" }
  - type: script
    phase: response
    path: ./scripts/response-enricher.rhai

Dependencies: rhai = "1" (sync feature)


upload — File upload handler

Enables upload: site config for multipart file uploads. Starts a loopback Axum server on a random port; the Pingora proxy forwards matching requests to it.

upload:
  path: /files
  dir: ./uploads
  maxFileSizeBytes: 10485760   # 10 MB
  allowedMimeTypes: ["image/jpeg", "image/png", "application/pdf"]

Files are saved with UUID v4 names. The success response includes name, originalName, size, and mimeType.

Dependencies: multer = "3"


cache — Response caching

Enables proxy.*.cache route config for in-memory response caching via Pingora’s cache layer. Supports TTL, skip-paths, cookie exclusion, Vary headers, stale-while-revalidate, and stale-if-error.

proxy:
  /api:
    targets: ["http://backend:4000"]
    cache:
      store: memory
      ttlSecs: 60
      staleWhileRevalidateSecs: 300

For Redis-backed shared cache add --features redis. For disk-backed persistent cache add --features disk-cache.


disk-cache — Disk-backed cache

Enables cache.store: "disk:/path". Cache entries are written atomically (.tmp → rename) and survive restarts. Useful for large response bodies or when Redis is not available.

proxy:
  /assets:
    targets: ["http://assets:4000"]
    cache:
      store: "disk:/var/cache/conduit"
      ttlSecs: 86400   # 1 day

Requires --features cache (depends on cache).


acme — Auto-TLS / Let’s Encrypt

Enables tls.acme site config for automatic certificate provisioning via the ACME protocol (Let’s Encrypt). Certificates are fetched at startup, cached to disk, and renewed automatically 30 days before expiry.

The domain is taken from the site’s host field — no separate domain: field exists.

host: api.example.com   # ← domain used for the certificate
port: 443
tls:
  acme:
    email: "admin@example.com"
    storage: "./certs"
    challenge: http-01
  httpRedirectPort: 80

Dependencies: instant-acme = "0.8", rcgen = "0.14"


redis — Redis-backed rate limiting and caching

Enables rateLimit.store: "redis://..." and cache.store: "redis://...". Falls back to in-memory on connection failure (fail-open, logged as warning).

rateLimit:
  windowSecs: 60
  limit: 1000
  store: "redis://localhost:6379"

Dependencies: redis = "1" (tokio-comp + rustls features)


tcp — TCP passthrough proxy

Enables tcp: site config for raw TCP forwarding (no HTTP parsing). Useful for databases, MQTT, custom binary protocols.

port: 5432
tcp:
  targets: ["db-primary:5432", "db-replica:5432"]
  strategy: round-robin
  connectTimeoutMs: 3000

consumers — Consumer model

Enables consumers: site config for named API clients with per-consumer credentials (API key, Basic Auth, JWT), rate limits, and custom upstream headers. The identified consumer’s username is injected as X-Consumer-ID.

consumers:
  consumers:
    - username: mobile-app
      apiKey: "$MOBILE_KEY"
      rateLimit: { windowSecs: 60, limit: 500 }
      headers: { X-Tier: mobile }
    - username: partner
      basicAuth: { password: "$PARTNER_PASS" }

JWT consumers (V2/V3) additionally require --features jwt.


forward-auth — ForwardAuth middleware

Enables forwardAuth: site config to delegate every authentication decision to an external HTTP service. 2xx = allow (inject response headers into upstream request); 4xx/5xx = deny; unreachable = fail closed.

forwardAuth:
  url: "http://auth-service:9000/verify"
  requestHeaders: [Authorization, Cookie]
  responseHeaders: [X-User-ID, X-Role]
  timeoutMs: 3000
  skipPaths: [/__health__]

fault-injection — Chaos testing

Enables faultInjection: site config. Do not use in production. Used to test circuit-breaker, retry, and timeout behaviour by injecting artificial delays and errors into a fraction of requests.

faultInjection:
  abort:
    percent: 5      # 5% of requests return 503
    status: 503
    body: '{"error":"injected"}'
  delay:
    percent: 10     # 10% of requests are delayed
    ms: 500

---

### `otlp` — OpenTelemetry distributed tracing

Enables `global.otlp` configuration block. Conduit exports distributed traces
to any OpenTelemetry-compatible backend (Grafana Tempo, Jaeger, Honeycomb,
OpenTelemetry Collector) via gRPC OTLP.

```yaml
global:
  otlp:
    endpoint: "http://tempo:4317"
    serviceName: "my-api"
    sampleRate: 0.1 # sample 10 % in production
    timeoutMs: 5000

Each span includes: method, path, status, duration_ms, upstream_url, request_id. 5xx responses set span status to ERROR.

When the binary is built without --features otlp, the global.otlp config block is silently ignored — no error, no traces.

Dependencies added: opentelemetry, opentelemetry_sdk, opentelemetry-otlp


wasm — WebAssembly plugin middleware

Enables type: "wasm" entries in the middleware array. Plugins are .wasm files that export on_request() -> i32 and a memory memory export.

middleware:
  - type: wasm
    path: ./plugins/my-plugin.wasm

Conduit uses Wasmtime as the WASM runtime. Plugins run in order alongside Rhai scripts (type: "script"). Plugin failures are fail-open — if a plugin panics or returns an error, Conduit logs a warning and continues processing.

Plugins may export two hooks:

Export Signature Phase Notes
on_request () → i32 Before upstream Required. 0 = continue, 1 = abort
on_response (status: i32) → i32 After upstream response Optional. 0 = continue, 1 = replace response

17 host functions are available across both phases: read/set/remove headers, get URI/method/IP/request-id, list header names, set response body/status, redirect, read plugin config, log.

See contrib/wasm/README.md for the full ABI reference and examples in Rust, C, and WAT.

When the binary is built without --features wasm, type: "wasm" entries log a startup warning and are skipped (fail-open).

Dependencies added: wasmtime (Cranelift JIT)


kubernetes — Kubernetes CRD config provider

Enables the --kubernetes-namespace startup flag. Conduit reads configuration from ConduitSite custom resources instead of a file, and watches for changes.

conduit --kubernetes-namespace default
conduit --kubernetes-namespace "*"    # all namespaces

Each ConduitSite spec mirrors the Conduit JSON schema — any field valid in conduit.yaml is valid in the CRD spec. Multiple resources in a namespace are combined into a multi-site config.

Install the CRD before use:

kubectl apply -f contrib/k8s/conduitsite-crd.yaml

When the binary is built without --features kubernetes, the --kubernetes-namespace flag does not exist in the binary.

Dependencies added: kube (runtime + derive), k8s-openapi, schemars, futures