Configuration
The operator reads its configuration from a TOML file, environment variables, and CLI flags. This page documents every configuration field, its type, default value, and behavior.
Loading order
Section titled “Loading order”Configuration is merged using Figment with the following precedence (highest wins):
- CLI flags —
--bind,--wg-port, etc. - Environment variables — prefixed with
AIRDRESS_ - Config file — TOML at the path given by
--configorAIRDRESS_CONFIG - Compiled defaults — built into the binary
The default config file path is /etc/airdress/operator.toml. Override it with:
airdress-operator -c ./my-config.toml serveValidate the resolved configuration at any time:
airdress-operator config # print merged resultairdress-operator config --check # validate without printing (exit 0 = OK)Full annotated example
Section titled “Full annotated example”This example shows every section with representative values. Fields set to their defaults are commented.
# Top-level settings# bind = "127.0.0.1:8080" # HTTP listener address# wg_port = 51820 # WireGuard UDP port# name = "airdress-operator" # Instance name (appears in logs)
[dns]enabled = true# bind_udp = "0.0.0.0:53"# bind_tcp = "0.0.0.0:53"zone = "humans.airdress.co"
[dns.soa]mname = "ns1.airdress.co"rname = "noc.airdress.co"
[dns.cache]# max_entries = 10000# default_ttl = 60
[dns.ratelimit]# queries_per_second = 1000# burst = 200
[[dns.records]]name = "myapp.humans.airdress.co"type = "A"rdata = "192.168.1.10"ttl = 300
[[dns.records]]name = "api.humans.airdress.co"type = "A"rdata = "192.168.1.11"# ttl = 60
[auth]issuer = "https://auth.airdress.co"jwks_uri = "https://auth.airdress.co/.well-known/jwks.json"audience = "airdress-operator"
[database]# url = "postgresql://user:pass@localhost:5432/airdress" # omit for embedded# data_dir = "./var/postgres"# version = "=18.3.0"# shared_buffers_mb = 64# max_connections = 32# pool_max_size = 16# pool_acquire_timeout_secs = 5
[enrollment]bootstrap_token = "change-me-on-first-run"# operator_base_url = "http://127.0.0.1:8080"
[logging]# level = "info"# format = "pretty" # pretty | compact | json# target = "stderr"# debug_file = "/var/log/airdress/debug.log"# service_name = "airdress-operator"# service_version = "0.x.y"# deployment_environment = "production"
[proxy]enabled = true# bind_http = "0.0.0.0:8081"# via_token = "secret-proxy-token"# rate_limit_rps = 100# rate_limit_burst = 20
[proxy.health]# enabled = true# interval_secs = 5# timeout_secs = 1# unhealthy_threshold = 3# healthy_threshold = 2
[proxy.circuit_breaker]# enabled = true# failure_threshold = 5# success_threshold = 1# cool_down_secs = 10
[chat]enabled = true# store = "postgres" # memory | postgres
[ingress]enabled = true# buffer_size = 100# metrics_interval_secs = 30
[[ingress.endpoints]]slug = "github-webhook"url = "http://localhost:3000/webhooks/github"
[apps]enabled = true# apps_dirs = ["/etc/airdress/apps"]# workdir = "./var/apps"# zone = "humans.airdress.co"
[miniapps]enabled = true# zone = "humans.airdress.co"# bundle_root = "./var/bundles"# max_bundle_size = 52428800# render_cache_assets = true# render_cache_ttl_secs = 3600# miniapps_dirs = ["/etc/airdress/miniapps"]
[plugin_db.secrets]# key_file = "/etc/airdress/secrets.key"# Or set AIRDRESS_SECRETS_KEY env var with base64-encoded keyTop-level fields
Section titled “Top-level fields”| Field | Type | Default | Description |
|---|---|---|---|
bind | string | "127.0.0.1:8080" | HTTP listener address and port |
wg_port | integer | 51820 | WireGuard UDP port |
name | string | "airdress-operator" | Instance name, included in log output |
Built-in authoritative DNS server. When enabled, the operator answers DNS queries for records defined in the config or added at runtime via airdress-operator dns add.
| Field | Type | Default | Description |
|---|---|---|---|
enabled | boolean | false | Enable the DNS server |
bind_udp | string | "0.0.0.0:53" | UDP listener address |
bind_tcp | string | "0.0.0.0:53" | TCP listener address |
zone | string | "humans.airdress.co" | Zone name for served records |
[dns.soa]
Section titled “[dns.soa]”SOA record fields for the zone.
| Field | Type | Default | Description |
|---|---|---|---|
mname | string | Primary nameserver hostname | |
rname | string | Hostmaster email (dot-encoded) |
[dns.cache]
Section titled “[dns.cache]”In-memory record cache.
| Field | Type | Default | Description |
|---|---|---|---|
max_entries | integer | 10000 | Maximum cached entries |
default_ttl | integer | 60 | Default TTL in seconds for records without an explicit TTL |
[dns.ratelimit]
Section titled “[dns.ratelimit]”Response Rate Limiting to mitigate DNS amplification.
| Field | Type | Default | Description |
|---|---|---|---|
queries_per_second | integer | 1000 | Sustained query rate per source IP |
burst | integer | 200 | Burst allowance above the sustained rate |
[[dns.records]]
Section titled “[[dns.records]]”Static DNS records loaded at startup. Each entry is a table in the TOML array.
| Field | Type | Default | Description |
|---|---|---|---|
name | string | Fully qualified record name | |
type | string | Record type: A, AAAA, CNAME, TXT, MX, SRV | |
rdata | string | Record data | |
ttl | integer | 60 | Time to live in seconds |
[auth]
Section titled “[auth]”OIDC-based authentication for the operator API.
| Field | Type | Default | Description |
|---|---|---|---|
issuer | string | OIDC issuer URL | |
jwks_uri | string | JWKS endpoint for token verification | |
audience | string | Expected JWT aud claim |
[database]
Section titled “[database]”PostgreSQL connection and tuning. When url is omitted, the operator runs an embedded PostgreSQL child process using the settings below. When url is set, the operator connects to the external database and ignores data_dir, version, shared_buffers_mb, and max_connections.
| Field | Type | Default | Description |
|---|---|---|---|
url | string | (none) | External PostgreSQL connection URL. Omit for embedded mode. |
data_dir | string | "./var/postgres" | Data directory for embedded PostgreSQL |
version | string | "=18.3.0" | Required PostgreSQL version (embedded mode) |
shared_buffers_mb | integer | 64 | Shared buffer size in MB (embedded mode) |
max_connections | integer | 32 | Max connections (embedded mode) |
pool_max_size | integer | 16 | Connection pool maximum size |
pool_acquire_timeout_secs | integer | 5 | Timeout in seconds when acquiring a pool connection |
Embedded vs external PostgreSQL
Section titled “Embedded vs external PostgreSQL”The operator supports two database modes:
Embedded (default) — when database.url is absent, the operator downloads and manages a PostgreSQL child process. Data is stored in data_dir. This is the simplest setup for single-node deployments and local development.
External — when database.url is set (e.g. postgresql://user:pass@db.example.com:5432/airdress), the operator connects via sqlx::PgPool. Use this for production deployments where you want a managed database, replication, or shared state between operator instances.
# Embedded (just omit url)[database]data_dir = "/var/lib/airdress/postgres"
# External[database]url = "postgresql://airdress:secret@db.internal:5432/airdress"pool_max_size = 32The AIRDRESS_DATABASE_URL environment variable is the most common way to provide the connection string without writing it to the config file.
[enrollment]
Section titled “[enrollment]”Device enrollment settings.
| Field | Type | Default | Description |
|---|---|---|---|
bootstrap_token | string | Secret token for the first device enrollment | |
operator_base_url | string | Base URL the operator advertises to enrolling devices |
[logging]
Section titled “[logging]”Structured logging configuration.
| Field | Type | Default | Description |
|---|---|---|---|
level | string | "info" | Log level: trace, debug, info, warn, error |
format | string | "pretty" | Output format: pretty, compact, or json |
target | string | "stderr" | Log target |
debug_file | string | (none) | Path to an additional debug log file |
service_name | string | "airdress-operator" | Service name in structured log fields |
service_version | string | Version string in structured log fields | |
deployment_environment | string | Environment tag (e.g. production, staging) |
[proxy]
Section titled “[proxy]”Reverse proxy for forwarding traffic to local services.
| Field | Type | Default | Description |
|---|---|---|---|
enabled | boolean | false | Enable the reverse proxy |
bind_http | string | HTTP listener for proxied traffic | |
via_token | string | Token for the Via header (upstream authentication) | |
rate_limit_rps | integer | 100 | Requests per second per client |
rate_limit_burst | integer | 20 | Burst allowance above the sustained rate |
[proxy.health]
Section titled “[proxy.health]”Health checking for proxy backends.
| Field | Type | Default | Description |
|---|---|---|---|
enabled | boolean | true | Enable backend health checks |
interval_secs | integer | 5 | Seconds between health probes |
timeout_secs | integer | 1 | Probe timeout in seconds |
unhealthy_threshold | integer | 3 | Consecutive failures to mark unhealthy |
healthy_threshold | integer | 2 | Consecutive successes to mark healthy |
[proxy.circuit_breaker]
Section titled “[proxy.circuit_breaker]”Circuit breaker for proxy backends. When a backend exceeds the failure threshold, the circuit opens and requests are rejected immediately until the cool-down period expires.
| Field | Type | Default | Description |
|---|---|---|---|
enabled | boolean | true | Enable the circuit breaker |
failure_threshold | integer | 5 | Failures before the circuit opens |
success_threshold | integer | 1 | Successes during half-open to close the circuit |
cool_down_secs | integer | 10 | Seconds before retrying after the circuit opens |
[chat]
Section titled “[chat]”Chat subsystem.
| Field | Type | Default | Description |
|---|---|---|---|
enabled | boolean | false | Enable the chat subsystem |
store | string | "memory" | Message store backend: memory or postgres |
The chat section also supports limits, sse, and federation subsections for advanced tuning. Federation is feature-gated and may not be available in all builds.
[ingress]
Section titled “[ingress]”Inbound event endpoints (webhooks, callbacks).
| Field | Type | Default | Description |
|---|---|---|---|
enabled | boolean | false | Enable inbound event processing |
buffer_size | integer | 100 | In-memory event buffer size |
metrics_interval_secs | integer | 30 | Interval for emitting ingress metrics |
[[ingress.endpoints]]
Section titled “[[ingress.endpoints]]”Each entry defines a named inbound endpoint that receives events and forwards them to a local URL.
| Field | Type | Default | Description |
|---|---|---|---|
slug | string | URL-safe endpoint identifier | |
url | string | Local URL to forward events to |
[apps]
Section titled “[apps]”Mini-app runtime.
| Field | Type | Default | Description |
|---|---|---|---|
enabled | boolean | false | Enable the app runtime |
apps_dirs | string[] | Directories to scan for app definitions | |
workdir | string | "./var/apps" | Working directory for app data |
zone | string | DNS zone for app subdomains |
[miniapps]
Section titled “[miniapps]”Mini-app serving and registry.
| Field | Type | Default | Description |
|---|---|---|---|
enabled | boolean | false | Enable mini-app serving |
zone | string | DNS zone for mini-app subdomains | |
bundle_root | string | "./var/bundles" | Directory for downloaded bundles |
max_bundle_size | integer | 52428800 | Maximum bundle size in bytes (default 50 MB) |
render_cache_assets | boolean | true | Cache rendered assets |
render_cache_ttl_secs | integer | 3600 | Asset cache TTL in seconds |
miniapps_dirs | string[] | Directories to scan for mini-app definitions |
The miniapps section also supports registry, tokens, capability, and upgrade subsections for registry authentication, capability grants, and upgrade policies.
[plugin_db]
Section titled “[plugin_db]”Encrypted storage for plugin secrets.
[plugin_db.secrets]
Section titled “[plugin_db.secrets]”| Field | Type | Default | Description |
|---|---|---|---|
key_file | string | (none) | Path to a file containing the base64-encoded 32-byte master key |
Alternatively, set the AIRDRESS_SECRETS_KEY environment variable with the base64-encoded key directly. Generate a key with airdress-operator plugin-db init-key.
Environment variable mapping
Section titled “Environment variable mapping”Config fields map to environment variables with the AIRDRESS_ prefix. Nested sections use double underscores as separators.
| Config path | Environment variable |
|---|---|
bind | AIRDRESS_BIND |
wg_port | AIRDRESS_WG_PORT |
database.url | AIRDRESS_DATABASE_URL |
auth.issuer | AIRDRESS_AUTH__ISSUER |
auth.jwks_uri | AIRDRESS_AUTH__JWKS_URI |
auth.audience | AIRDRESS_AUTH__AUDIENCE |
enrollment.bootstrap_token | AIRDRESS_ENROLLMENT__BOOTSTRAP_TOKEN |
logging.level | AIRDRESS_LOG |
chat.federation.keystore_passphrase | AIRDRESS_CHAT__FEDERATION__KEYSTORE_PASSPHRASE |
plugin_db.secrets (key value) | AIRDRESS_SECRETS_KEY |