Skip to content

Repository files navigation

Oxidase

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.

The model

  • 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 .oxt sources 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.

Current v0.3 alpha

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.

Try the vertical slice

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.yaml

The 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.

Configuration sketch

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: public

The 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.

CLI

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.

Development

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 --locked

The 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.

About

A super lite rust http server.

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages