Zornux docs
Get started Spec

Data & Enterprise

Configuration

Settings in Zornux are typed and declared, not scattered through the code. A configuration block defines a schema with defaults; values arrive from data-only files, environment variables, and overrides — and secrets are redacted everywhere by default.

A typed schema

Each setting declares a type after as and a default after is. Instantiate the configuration with create … from and read settings as fields:

zornux
configuration AppConfig
    has host as text is "localhost"
    has listen_port as whole is 8080
    has debug as truth is false
    has api_key as secret is "dev-only-secret"
end

create settings from AppConfig
show settings.host          # localhost
show settings.listen_port   # 8080

Types are text, whole, number, truth, list, map, and secret. They're contextual words — text is still the built-in function everywhere else.

Secrets are redacted

A secret setting never prints in the clear — not with show, not in JSON. Reading its value is a single, explicit, auditable act: reveal.

zornux
show settings.api_key           # ******** (redacted)
show reveal(settings.api_key)   # the actual value — the one way in
Secrets stay out of committed files

Setting a secret in a checked-in config file is flagged (ZX2609). Put secrets in environment variables or the local, git-ignored file instead. Using a secret where plain text is required raises ZX2610 until you reveal it.

External secret providers

A secret's value can be a secret://<reference> pointer instead of a literal. At startup the host resolves it through the configured secrets_providervault, aws-secrets-manager, azure-key-vault, gcp-secret-manager, or http — so real secrets live in your secret store, never in a file. A secret:// reference with no provider configured stops startup rather than running unresolved.

Files, profiles & overrides

Configuration values are layered, from lowest to highest precedence:

LayerSource
Schema defaultsThe is … values in the configuration block.
Profile files.zxcfg data files, per environment profile.
EnvironmentZORNUX_* variables.
Overrides--config name=value on the command line.

The four profiles are Development, Testing, Staging, and Production, selected with --profile or ZORNUX_PROFILE. A .zxcfg file is data only — no code runs in it:

zxcfg
host is "0.0.0.0"
listen_port is 443
debug is false

Injecting configuration

A service reads settings through dependency injectionuse AppConfig makes the loaded configuration available like any other dependency:

zornux
service Greeter
    use AppConfig
    function greet
        give back AppConfig.greeting
    end
end

A service can even take its port from configuration: publish Api on port AppConfig.listen_port.

Where a host listens, and which hosts it serves

Two settings, because they answer two different questions. The listening port is neither of them — that comes from publish <Service> on port <N> in the program.

zxcfg
bind_address is "127.0.0.1"
accepted_hosts is "store.example.com, 127.0.0.1"
trusted_proxy_hops is 1
SettingMeansDefault
bind_address Where the process listens, written as an address. 127.0.0.1 is this machine only — what a replica behind a same-host proxy uses; 0.0.0.0 (or *, +, all) is every interface. A name is refused: localhost resolves to [::1] on an IPv6 host, which is exactly how a deployment ends up bound where its proxy is not connecting. localhost
accepted_hosts Which Host values the application serves, matched exactly after lower-casing and dropping the port and any trailing dot. Empty serves any; * serves any, spelled out. An unlisted host is answered 421 before it reaches your code. (empty)
trusted_proxy_hops How many reverse proxies sit in front. 0 ignores forwarding headers entirely. Behind the generated nginx, set 1. Never set it higher than the proxies you actually operate — the surplus lets a caller forge both their address and their host. 0
An unusable bind address stops start-up

It is reported as ZX2713 rather than quietly falling back, because a narrower fallback serves nobody and a wider one opens a port nobody asked for. IPv6 literals are refused with their real reason — this host listens on IPv4, and a proxy in front still serves IPv6 clients.

listen_host is superseded

listen_host still works and still means the address to bind. It used to also become the only Host the process accepted, so a replica bound to 127.0.0.1 answered 404 to everything a proxy forwarded. It no longer does. Setting it alongside bind_address is reported (ZX2715).

Behind a proxy, the Host on the wire is not the caller's: a same-host proxy sends the replica's bind address as Host and the caller's real host in X-Forwarded-Host. With trusted_proxy_hops set, Zornux reads the forwarded one, checks it against accepted_hosts, and presents it as request.host. A forwarded host that is present but unusable is a refusal — never a fall back to the address on the wire.

Inspecting configuration

Two CLI commands make configuration visible without running the program: zornux config show prints the resolved settings (secrets redacted), and zornux config validate checks the files against every declared schema.

Host settings are known too

Reserved host settings like log_level, log_output, log_file, job_workers, secrets_provider, and the OpenTelemetry endpoints traces_endpoint / metrics_endpoint are recognized by config validate — a bad value warns (and the run falls back) rather than being reported as an unknown setting. The web host adds bind_address, accepted_hosts, listen_host (superseded), max_request_body_bytes, request_timeout_seconds, header_read_timeout_seconds, idle_connection_timeout_seconds, shutdown_grace_period_seconds, trusted_proxy_hops, static_files, and spa_fallback.

Managed KMS keys

When a host arms the capability-gated key-management (kms) module, these settings configure it: kms_provider, kms_endpoint, kms_family, kms_region, kms_allowed_key_ids, kms_encrypt_purposes / kms_decrypt_purposes, and kms_workload_token. They stay inert until a provider is armed — fail-closed by default.

Diagnostics are ZX2600ZX2699. Next: shaping requests before they reach a route — Middleware.