Deployment and trust model
The server has no authentication. ox runtime serve consumes X-Tenant-Id / X-Principal-Id (and the other context headers) at face value — it does not verify who the caller is or whether they may act as the named tenant or principal. Tenant data is isolated at the storage layer (one tenant cannot read another’s individuals, verified), but the gate deciding who may claim a tenant is absent by design. A caller that sets X-Tenant-Id: acme reads and writes acme’s data regardless of any Authorization header.
This is an honest internal-service posture, not a bug — but it has a hard deployment consequence: the server MUST be fronted by a gateway and MUST NOT be directly reachable by untrusted clients. The default loopback-only bind (serve refuses a non-loopback host) enforces “behind a proxy on the same host or network namespace”; do not relax it without a fronting gateway.
The deployment contract:
- Front it with an authenticating gateway. A reverse proxy / API gateway terminates client auth (mTLS, OIDC, an API-key service — backend-specific), and only after authenticating injects the trusted
X-Tenant-Id/X-Principal-Idheaders. The runtime trusts these because the gateway is the only thing that can reach it. - Strip inbound trust headers at the edge. The gateway MUST overwrite (not pass through)
X-Tenant-Id/X-Principal-Id/X-Standpointfrom the client request, so a client cannot forge a tenant. Treat these as gateway-internal headers. - Network-isolate the runtime. Bind loopback (the default) or a private interface, and use network policy / security groups so only the gateway can open a connection. The runtime has no rate limiting or load-shedding beyond the per-request timeout, body-size cap, and result-row cap (Serving surface) — the gateway owns rate limiting and client back-pressure.
- Wire liveness/readiness to the orchestrator. Point the orchestrator’s liveness probe at
GET /healthz(restart on failure) and its readiness probe atGET /readyz(remove from the load-balancer pool on failure — e.g. when Postgres is unreachable). Do not gate liveness on storage: a storage outage should drain traffic (readiness), not restart pods (liveness). - Schema-change gate. Cross-version artifact loads over live data refuse by default (
ARGON_RUNTIME_SCHEMA_MISMATCH, R-B7); an operator opts into serving a changed schema over an existing scope withARGON_ACCEPT_SCHEMA_CHANGE=1. Treat that as a deliberate migration step, not a default.
forget (physical erasure) is refused outright over HTTP and there is no per-principal authorization inside the runtime (Capability surface, Serving surface) — all authorization is the gateway’s responsibility.