-
Notifications
You must be signed in to change notification settings - Fork 0
cli
conduit [OPTIONS] [COMMAND]
- Global options
- Config file discovery
- Server commands
- Admin commands
- Tooling
- Environment variables
- Exit codes
- Kubernetes CRD mode
- Build features
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. |
When no -c flag is given, Conduit resolves the config in this order:
-
conduit.json— if this file exists, use it -
conduit.yaml— fallback ifconduit.jsonis absent -
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.
Start the reverse proxy server.
conduit # reads conduit.json (or .yaml / .yml)
conduit -c /etc/conduit.yaml # explicit config pathOn startup, Conduit:
- Loads and validates the config — exits 1 with a field-level error message on failure
- Binds all configured ports
- Starts the Admin API on
global.admin.bind(only ifglobal.adminis configured) - Begins serving traffic
If hotReload is enabled, Conduit watches the config file and reloads it
automatically when it changes — no restart needed.
Parse and validate the config file, then exit. Does not start the server.
conduit validate
conduit validate -c staging.yamlOn 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"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.
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.
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.yamlExample 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:
-
https://upstreams are checked with a plain TCP connect — TLS is not negotiated and the certificate is not verified. An unreachable host still shows as a failure, but a host with an expired or self-signed cert will show as healthy. - The probe path defaults to
/— adjust the upstream URL if a different path is required. - Useful as a pre-deploy readiness check in CI:
conduit probe -c conduit.yaml || exit 1
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:
-
--admin ADDRflag -
CONDUIT_ADMINenvironment variable - Default:
127.0.0.1:2019
The default
127.0.0.1:2019is only the CLI connection target. The server does not open this port unlessglobal.admin.bindis 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 automaticallyFor direct curl calls, pass the token as a Bearer header:
curl -H "Authorization: Bearer $CONDUIT_ADMIN_TOKEN" http://localhost:2019/statusHot-reload the config file without restarting the server.
conduit reload
conduit reload --admin 127.0.0.1:2019The 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.
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 --upstreamDefault 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 |
Gracefully stop the server.
conduit shutdown
conduit shutdown --admin 10.0.0.1:2019Conduit stops accepting new connections, waits for all in-flight requests to
complete (up to global.shutdownTimeoutSecs), then exits.
Manage upstream targets at runtime. Changes are in-memory only and are
lost when conduit reload is run or the server restarts.
conduit upstreams
conduit upstreams --admin 127.0.0.1:2019Prints all upstreams with health status, latency, and weight:
[
{
"route": "/api",
"url": "http://api-1:4000",
"healthy": true,
"latency_ms": 12,
"weight": 1,
"ejected": false
}
]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 |
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 |
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 |
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.elvSupported shells: bash, zsh, fish, power-shell, elvish.
Completions cover all subcommands, flags, and (where statically known) their accepted values.
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| 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.
| 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.
Requires
--features kubernetesat 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.
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 | 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).
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"
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.rhaiDependencies: rhai = "1" (sync feature)
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"
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: 300For Redis-backed shared cache add --features redis.
For disk-backed persistent cache add --features disk-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 dayRequires --features cache (depends on cache).
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: 80Dependencies: instant-acme = "0.8", rcgen = "0.14"
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)
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: 3000Enables 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.
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__]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: 5000Each 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
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.wasmConduit 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)
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 namespacesEach 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.yamlWhen 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