Zornux docs
Get started Spec

Data & Enterprise

Enterprise Hardening

A production Zornux service validates itself at boot, degrades gracefully under failure, answers readiness probes, and shuts down cleanly. It's the difference between "it runs" and "an enterprise deploys it with confidence" — and it adds no new keywords.

Startup validation & fail-fast

An on startup hook runs before a service serves. A failed require aborts the publish, so a misconfigured instance never accepts a single request.

zornux
web Checkout
    use AppConfig
    on startup
        require AppConfig.api_key otherwise "the API key must be configured"
    end
end
require, anywhere

require value [otherwise "message"] fails fast (ZX3300) when the value is false or nothing. It's an ordinary statement — use it at startup, or anywhere you need a precondition. It's recoverable with try.

Resilience policies

Wrap unreliable work in a named policy — a timeout, a retry, and a circuit breaker — and apply it with protect.

zornux
resilience PaymentGateway
    timeout 5 seconds
    retry 3 times waiting 1 second
    break after 5 failures for 30 seconds
end

protect with PaymentGateway
    charge = Payments.charge(order)
otherwise error
    charge = 503
end
ClauseRole
timeout N secondsBound the work; over budget is a timeout (ZX3310).
retry N times [waiting M seconds]Re-run a recoverable failure or a timeout.
break after N failures for M secondsCircuit breaker: after N failed protects, reject calls for M seconds.
Deterministic by design

The protected body is an isolated attempt (a give back ends it), so results flow out by mutating outer variables. Timeouts and the breaker's open window are measured on the deterministic virtual timeline, so resilience is reproducible and tests never flake.

The circuit breaker's three states

  • Closed — calls run; consecutive failed protects are counted.
  • Open — after the threshold, calls are rejected without running (ZX3314), so the fallback handles them fast.
  • Half-open — once the window passes, the next call is a trial: success closes the circuit, failure re-opens it.

Graceful shutdown & readiness

A ready … end block registers GET /ready — leaner than the liveness /health, it answers whether traffic should come here. On the way out, the host drains and runs each service's on shutdown hook.

zornux
web Api
    ready
        give back ok
    end
    on shutdown
        log info "draining, goodbye"
    end
end
Stop routing to me

Once shutdown begins, /ready answers 503 {"status":"shutting down"} so load balancers drain traffic away — while /health can stay green through the drain. Liveness and readiness are different questions.

Three probes, and who answers them

PathAnswered byAsks
/livethe host itselfIs this process alive?
/healthyour health block, as a routeIs everything it depends on working?
/readyyour ready block, as a routeShould traffic come here right now?

The distinction matters to whatever is watching. /health and /ready are ordinary routes, so they queue behind the work in front of them; a process busy with a slow request can look dead to a short-timeout probe. /live is answered by the host, so it stays responsive regardless — which is why the generated container health check, the generated proxy, and zornux deploy status all probe it.

Production diagnostics

zornux
show uptime_seconds()   # seconds since start, on the injected clock
show memory_used()      # bytes of memory in use

API hardening

Hardening composes with what Zornux already enforces: a typed route auto-validates its body (400), and the pipeline offers rate limits (429), size limits (413), CORS, and catch errors. A route can also wrap slow work in protect with … for a per-route timeout, retry, and breaker.

Two of those defences run before a request costs you anything. A request for a hostname this deployment does not serve is answered 421 on the accept thread — it takes neither a queue slot nor any application time — and an over-large body is drained before its 413, so the caller reads the status instead of losing the connection mid-upload. Which hostnames count is accepted_hosts.

Diagnostics

ZX3300 failed precondition, ZX3310 protect timeout, ZX3312 unknown policy, ZX3314 open circuit — all ZX3300ZX3399, and the recoverable ones are handled by otherwise or a surrounding try.

Least-privilege runtime

A production instance should hold only the rights it needs to run. --no-migrate opens the database without migrate on open or lazy table creation, so the runtime login can be granted zero DDL rights — schema is applied ahead of it by a separate, privileged zornux db migrate step in the deploy pipeline.

Managed key access — the kms module

For certification-grade encryption, the capability-gated native kms module (encrypt, decrypt, reencrypt, describe_key, health, verify_access) reaches a managed KMS — commonly a private/VPC endpoint — without the general-purpose http module and without weakening anti-SSRF: a provider reaches only its one config-allowlisted endpoint through the guarded transport's scoped private-network allowance, and the cloud metadata address stays refused. It's off until a host arms a provider (fail-closed), so an ordinary program can't reach a KMS.

Built on background processing, concurrency, and the request pipeline.