English | 简体中文
Oxidase is a declarative HTTP Service program compiler and runtime written in Rust.
Gateway configuration is treated as source code. Oxidase resolves imports and references, validates the complete program, compiles patterns, expressions, templates, and Oxista sites, prepares shared resources, then publishes an immutable runtime snapshot. Each listener binds network traffic to any root Service.
- Listener owns transport metadata and points to a root Service.
- Service Program composes terminal (
Respond,Redirect,Site,Proxy), wrapper (Transform,Observe,Timeout,RequestBodyLimit,ConcurrencyLimit,RateLimit,Recover), and composition (Route,Fallback,Reenter) nodes. - Resource Registry owns reusable state such as validated Secret, Trust Store, and Certificate material, compiled Site snapshots, and prepared Clusters. Resources are not Services.
- Router DSL is optional source syntax lowered to ordinary Service IR before execution; the runtime has no privileged Router.
- Oxista compiles
.oxsite,.oxr, and.oxtsources into an immutable Site index. Request handling never parses these files. - Portable Bundle (
oxidase.bundle/v1) stores stable Service, transport, Resource, Site, and Asset sections without serializing Rust runtime objects.
Every Service returns one of Handled(response), Declined, or Failed(error).
Fallback advances only on Declined; HTTP 404 and 500 responses are still handled
responses. Request overlays and route bindings are lexical, so a declined branch
cannot leak captures or rewrites into its siblings.
The runnable inbound data plane supports cleartext HTTP/1.1 plus HTTPS over TLS 1.2/1.3 with ALPN-selected HTTP/1.1 or HTTP/2. Every current Service node runs on either selected protocol. Proxy remains streaming over pooled HTTP/1.1, HTTPS, and upstream HTTP/2 connections. Assets are streamed from async files and support quality-weighted identity/Brotli/gzip selection, representation-specific ETags, correct validator precedence, If-Range, and single byte ranges. Range applies only to GET: a valid single bytes range uses identity when acceptable, while HEAD, unknown/malformed units, multipart requests, and an identity-excluded range fall back to the normal full-representation path.
Clusters can select auto, forced http1, or forced h2 upstream pools. From an
H2 downstream to an explicit H2 Cluster, Proxy preserves DATA and trailer frames.
The current TLS/H2 integration fixture verifies request/response trailers and transparent
multi-message application/grpc, including grpc-status and grpc-message in the
terminal trailer frame. Oxidase does not parse protobuf, reinterpret gRPC status, or
implement gRPC-Web. Additional socket fixtures verify HTTP/1 chunked request trailers
crossing to H2 and declared H2 response trailers crossing to an accepting HTTP/1
client; undeclared trailers fail the stream instead of being silently dropped.
Clusters are prepared runtime Resources rather than URL lists interpreted per request. Named endpoints support deterministic round robin, smooth weighted round robin, or weighted least-requests selection. Optional active checks and passive ejection control eligibility; Cluster and per-endpoint semaphores bound admission before the body is consumed. Retry is disabled by default and is allowed only for explicit methods and pre-response-head causes/statuses with an empty body or an explicitly bounded replay buffer. Runtime health/counters survive only a compatible endpoint reload identity. HTTPS Clusters can use system roots, a custom Trust Store, or both, fix an exact DNS/IP verification identity, and present a prepared Certificate Resource as the upstream client identity. The TLS policy is part of Proxy and health-check pool compatibility; verification cannot be disabled.
Inbound governance is explicit at both transport and Service boundaries. Listener
limits bound total and per-kernel-peer connections, idle progress, Header bytes and
count, and requests per HTTP/1 connection or accepted HTTP/2 streams. The wrapper
Services request_body_limit, concurrency_limit, and rate_limit respectively
enforce a streaming byte ceiling, cancellation-safe admission held through the
response body or trusted tunnel, and a bounded monotonic token bucket keyed only by
the actual peer IP or a named lexical binding. No forwarded Header is trusted as a
client identity, and runtime keys never become metric labels.
Listener programs share one immutable ServiceGraph; normal requests do not clone
the graph or collect explain traces. Every handled response passes through one
framing finalizer, and Gateway/Oxista source cannot set hop-by-hop or framing
headers. HEAD, informational, 204, 205, and 304 body rules are covered by wire
tests.
Observe is a production wrapper rather than explain-only syntax. It records a
bounded, structured service-to-response-head scope for handled, declined, failed,
timeout, and nested executions without enabling full traces. A separate streaming
body adapter records emitted bytes, completion, errors, idle timeouts, and downstream
cancellation. Metric labels come only from configured Observe names and fixed enums;
request URLs, queries, and Header values are never labels.
Oxista response headers execute in source order (global defaults, logical extension,
profiles, then local OXR). Ordinary and OXR-backed assets share extension defaults.
External OXT files inherit Site output/autoescape defaults; custom 404 templates are
validated as zero-argument calls and retain their effective metadata. Template
budget failures are classified separately for Recover without exposing details to
clients.
OXT includes have typed call contracts:
{% include "_templates/card.oxt" with item=item only %}. Arguments are ordinary
compiled expressions, required/unknown/constant-type errors fail preparation, and
dynamic values are checked before rendering. Normal include inherits caller locals;
only keeps the read-only request, bindings, site, resource, and page
roots while dropping caller template/loop/with scopes. Render budgets are charged
before each expression, loop body, include, and output write, so exactly the stated
limit is allowed and the next operation does not execute.
Correctness identities use complete SHA-256 digests with domain-separated,
length-prefixed structured encoding. Strong Asset validators are
"sha256-<64 lowercase hex>" over the selected representation bytes. One
SiteSourceIndex scan supplies Site reuse identity, representation ETags, cached
Oxista source text, and compilation metadata; large Asset bytes are not retained.
Gateway and Oxista semantic diagnostics carry exact byte and line/column ranges,
secondary labels, related definitions, and import/include reference chains. Every
compiling CLI command accepts --diagnostic-format human|json; JSON uses the
versioned oxidase.diagnostics/v1 envelope and keeps stdout machine-readable.
Request expression views are frame-local and lazy, so effective Headers, query
values, bindings, and the request namespace are built once per unchanged frame.
Portable .oxb Bundles can be built, inspected, verified, diffed, signed, and
served without rereading Gateway or Oxista YAML. The default embed Asset mode
stores deduplicated raw blobs and keeps range responses streaming from an anonymous
verified spool, isolating both path replacement and in-place source-file writes;
reference records an explicit absolute/deployment-root path, length, and SHA-256
digest that must match before activation, then serves an anonymous verified copy
rather than the mutable source inode. Stable versioned DTOs are
reparsed into the same Service graph and runtime plans, so Bundle startup is not a
second data plane. Unknown required capabilities or an incompatible minimum runtime
version fail closed.
Ed25519 signatures cover the domain-separated canonical content digest and support
multiple verification keys for rotation. Public certificate chains may be bundled,
but Secret values and certificate private keys never are: only typed runtime file
references cross the format boundary, inspection/debug output redacts them, and
their targets are revalidated during prepare-before-commit. The Bundle format and
CLI are alpha, are not an executable
snapshot of live connections or Cluster/limiter state, and do not yet constitute
the authenticated staged control plane. See docs/bundles.md.
Secrets are bounded file-only Resources with redacted formatting and best-effort
final-owner zeroization; they are not general expression/template values. Strict,
certificate-only Trust Store Resources provide roots for inbound client
authentication and upstream private PKI. Certificates are prepared as Resources:
PEM/X.509 structure, one supported private key, key/certificate consistency, SNI
certificate compatibility, and all listener settings are validated before
publication. HTTPS Listeners support none, optional, or required client
authentication and expose only bounded, rustls-verified leaf metadata under
request.tls.client. A retained listener socket loads the current immutable
TLS/HTTP plan for each new connection, so valid certificate or trust rotation is
atomic without rebinding; existing connections keep their old TLS state. Each
HTTP/2 stream pins the snapshot current when that request starts, and listener
retirement sends graceful shutdown before the drain deadline.
HTTP/1 Proxy now has a server-local trusted Upgrade path: ordinary Respond/OXR/
Transform output cannot forge its 101 response, and validated tunnels use
connection-owned bidirectional streaming with bounded metrics. Its parser, matching
101 validation, partial byte accounting, and in-memory copy/cancellation have focused
tests. Socket fixtures cover plain/TLS HTTP/1 handshakes, WebSocket-style bytes in
both directions, either peer closing, reload with the old snapshot pinned, Listener
drain timeout, trusted-capability isolation, and bounded metrics. Oxidase transparently
tunnels WebSocket traffic rather than parsing its frames. HTTP/2 extended CONNECT,
arbitrary CONNECT, cleartext h2c, gRPC-Web, ACME, OCSP/CRL revocation, automatic
certificate-to-role mapping, and user-selected cipher suites are not implemented.
OXT extends/block remains unsupported.
Atomic last-known-good reload is available with serve --watch; health, bounded
metrics, and read-only /api/v1/clusters status are available on an explicit
separate --admin-bind. See
docs/implementation-status.md for exact status.
This workspace is 0.3.0-alpha.1; the Gateway API remains
oxidase.dev/v1alpha1 and Oxista remains v1. It is not described as
production-ready or API-stable. Release changes are recorded in
CHANGELOG.md.
cargo run -p oxidase-cli -- check examples/basic-gateway/oxidase.yaml
cargo run -p oxidase-cli -- test examples/basic-gateway/oxidase.yaml
cargo run -p oxidase-cli -- explain examples/basic-gateway/oxidase.yaml \
--request examples/basic-gateway/requests/home.yaml
cargo run -p oxidase-cli -- serve examples/basic-gateway/oxidase.yaml
cargo run -p oxidase-cli -- serve examples/basic-gateway/oxidase.yaml --watch
cargo run -p oxidase-cli -- serve examples/basic-gateway/oxidase.yaml --watch \
--admin-bind 127.0.0.1:7590
cargo run -p oxidase-cli --locked -- \
check examples/secure-resilient-gateway/oxidase.yamlThe example demonstrates:
/: compiled OXT page;/about.html: sibling asset governed by OXR headers;/old-page: Oxista redirect;/feed.json: structured JSON response;/legacy: Service-level redirect;- a missing resource declining from Site into an explicit Respond 404;
- an outer response Transform applied to every handled branch.
The /api/* route proxies to an upstream on 127.0.0.1:3000. Without that fixture
upstream it returns a safe 502; explain can inspect the rewrite and Cluster
selection without making the network request.
api_version: oxidase.dev/v1alpha1
kind: gateway
services:
public:
type: transform
response:
headers:
set:
X-Content-Type-Options: nosniff
service:
type: fallback
services:
- type: site
site: web
- type: respond
status: 404
body:
text: Not Found
listeners:
- name: public-http
bind: 127.0.0.1:7589
service:
ref: publicThe v1alpha1 YAML boundary is shared by Gateway and every Oxista format. Unknown or
duplicate keys, anchors, aliases, merge keys, custom tags, tab indentation, and flow
mappings fail; flow sequences and literal/folded block scalars are allowed. Imports/
references are cycle checked, and parsed-but-inert field values are rejected with
migration guidance. check and serve use the same compiler and Site preparation
path.
Inbound transport configuration is documented in
docs/configuration/tls.md and
docs/configuration/http2.md. An HTTPS listener
defaults to versions: [h2, http1]; a cleartext listener defaults to http1 and
rejects h2 rather than implying h2c support.
File-backed Secret handling is documented in
docs/configuration/secrets.md. Custom Trust Store,
inbound mTLS, verified request metadata, and upstream TLS/mTLS policy are documented
in docs/configuration/mtls.md. mTLS authenticates a
certificate chain but does not by itself authorize a request; no
dangerous_skip_verify option exists.
Listener ingress limits and protection wrappers are defined in
ADR 0009. Defaults remain finite:
10,000 total connections, 100 connections per peer IP, a 2-minute connection-idle
deadline, 30-second request/response body-idle deadlines, 64 KiB and 100 decoded
Headers, and 1,000 requests/streams per connection. These are alpha defaults rather
than capacity recommendations; deployments must select limits for their workload.
Prepared Cluster policy is documented in
docs/configuration/clusters.md. Supported
protocol bridges and their framing limits are documented separately for
gRPC and
HTTP/1 Upgrade/WebSocket. The
secure-resilient-gateway example combines
these plans with explicitly test-only certificate material.
oxidase check <config>
oxidase serve <config>
oxidase explain <config> --request <request-file> [--listener <name>]
oxidase compile <config> --output <manifest.json>
oxidase test <config>
Append --diagnostic-format json to check, compile, test, serve, or a
failing explain invocation for deterministic machine-readable diagnostics. See
docs/diagnostics.md for the alpha schema and position
conventions.
compile continues to write a deterministic inspection manifest. Portable runtime
artifacts use the separate Bundle workflow documented in
docs/bundles.md; a Bundle does not preserve live process state.
serve --watch watches imported configuration and compiled Site dependencies.
Reload compiles and prepares the complete candidate, prebinds new listeners, reuses
unchanged resources, and atomically commits only on success. Blocking preparation
runs off Tokio workers. Failed-candidate imports and invalid certificate rotations
remain watched while the last-known-good state stays active. Retired HTTP/1
connections receive graceful shutdown and retired HTTP/2 connections receive
GOAWAY: idle connections close promptly while active requests/streams drain on their
pinned snapshot. A trusted HTTP/1 tunnel also pins its original snapshot and is owned
by that connection task; retained Listeners leave it running, while retirement gives
it the normal drain window before forced cancellation. This lifecycle is implemented,
and its reload/new-Listener/drain-timeout behavior is covered by a socket fixture.
Failed Site candidates also retain scanned
OXT/OXR/assets, missing declared paths, template roots, precompressed candidates,
and their parent directories in the watcher dependency set.
cargo +1.88.0 check --workspace --all-targets --all-features --locked
cargo +1.88.0 test --workspace --locked
cargo fmt --all -- --check
cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
cargo test --workspace --locked
RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps --locked
cargo deny check
cargo check --manifest-path fuzz/Cargo.toml --bins --locked
cargo build --workspace --release --lockedThe HTTP end-to-end tests bind ephemeral loopback ports. Sandboxed environments may need permission to run those tests.
Architecture starts at ARCHITECTURE.md. The v0.1 prototype is
described in docs/legacy/v0.1.md and remains available in
Git history.
Oxidase is licensed under the MIT License.