Repository navigation
httpjet v0.0.1
httpjet v0.0.1 — September 2026: security hardening round + new subsystems
First tagged release, adopting semantic versioning.
This sync carries everything since the previous public snapshot (September 6): a set of new
opt-in subsystems, a transactional zero-downtime reload architecture, and a whole-codebase
security review whose twelve fixes are the centerpiece of this release. As always, every
subsystem below is opt-in — the default configuration serves exactly as before.
Security hardening round (whole-codebase review)
Five parallel review slices covered the request pipeline, transports, TLS and unsafe code, trust
and access control, backend gateways, cache and static serving, control surfaces, process
supervision, ACME, logging, and ops packaging. Twelve fixes shipped, all fail-closed:
Transport hardening
- HTTP/2: frame and request-body deadlines on the production loop. The production (monoio)
h2 loop never enforcedframe_read_timeout/request_body_timeout— those deadlines existed
only in a code path that doesn't run — so a peer trickling one byte per idle window could pin a
connection, its header block, and its body buffers indefinitely. The production loop now
mirrors the tokio-loop bookkeeping: partial frames must complete within the frame deadline
(GOAWAY PROTOCOL_ERROR otherwise), and requests with decoded headers but no END_STREAM are
reset with body accounting released after the body deadline. - HTTP/1.1: stalled
sendfileis bounded. A zero-window reader used to cause ~10k
sendfile(2)syscalls/s per connection on a pinned worker core, forever. Retries back off
exponentially (100 µs → 12.8 ms, reset on progress) and a transfer with zero progress for 60 s
closes the connection. - HTTP/1.1: duplicate identical
Content-Lengthlines collapse to one on the forwarded
header map, so a lenient upstream cannot re-parse two lines differently. - CPU pinning respects
CPU_SETSIZE—available_parallelism()can exceed the fixed
1024-bit affinity mask on pathological hosts.
Trust boundary
- PROXY protocol: claimed loopback/unspecified sources are refused at the parser. The
pp-claimed source was applied before the mandatory client-certificate gate, so on a
pp-enabled public listener a direct client could claim127.0.0.1and be treated as a trusted
internal peer: skipping the client certificate, reaching the loopback-gated cache purge/ready
endpoints, and bypassing the plaintext mTLS-gate redirect. Such headers are malformed in both
v1 and v2 (v4-mapped forms included) and the connection is closed. Real load balancers never
relay loopback client addresses. - Rewrite
[P]targets are screened away from the local trust plane.[P]URLs are
request-influenceable (.htaccessauthors;$Nbackrefs in the authority), and a same-host
dial presents a loopback raw peer — exactly what the purge/ready endpoints, the metrics
listener, and the admin listeners gate on. Rewrite-driven targets refuseunix:/uds:
transports outright and resolve the authority (bounded 3 s) to reject loopback, unspecified,
and link-local addresses — including/etc/hostspins, which bypass literal screens.
Config-declared proxy targets remain operator-trusted and unscreened. - CGI environment: directory-authored overrides of server-computed variables are denied
(shared by LSAPI and FastCGI). Rewrite[E=...]/SetEnvIf entries could previously re-point
SCRIPT_FILENAMEat an arbitrary readable file or forgeREMOTE_ADDR/HTTPS/SERVER_*.
SCRIPT_NAME,QUERY_STRING,REDIRECT_*(front-controller and ErrorDocument contracts) and
HTTP_*mirrors remain overridable.
Fail-closed defaults
.htaccess: unreadable or non-UTF-8 access files no longer degrade to "no rules." A
present-but-unreadable file, or a single non-UTF-8 byte, memoized the directory as unprotected
until the mtime changed. Non-UTF-8 files decode lossily (rules survive); any other read error
yields the deny-all sentinel, self-healing on mtime change — matching the existing parse-error
behavior.- lsphp: never-root is enforced on every credential path, and a setuid/setgid lsphp binary
refuses the spawn outright (after the supervisor's credential drop, executing such a binary
yields workers whose effective uid is root). - Isolation modes: out-of-range
setUIDMode/chrootModevalues fail the vhost load instead
of silently disabling isolation.
Cache integrity
- The page cache varies stored entries on
Originwhen the.htaccesschain reads it. An
origin-reflecting chain (SetEnvIf /RewriteCond %{HTTP:Origin}, typically for a reflected
Access-Control-Allow-Origin) produces origin-conditional responses, but the cache stored
whatever headers the first storer's render produced — every later visitor received that
request's CORS verdict. Origin now folds into the cache-key vary discriminant (bounded hash)
across lookup, store, stale-if-error, and capsule keys. Absent/emptyOriginkeeps the
historic base key, so entries persisted before this change stay hit-able; origin-bearing
requests (browser fetch/XHR) re-render once per URL per origin after upgrading.
Admin write API and packaging
useIpInProxyHeaderand the whole<security>block (trusted-CIDR access rules,
followSymlink, geo/ASN lists) are restart-required rather than silently publishable as
live "application" updates; unix-socket ext-processor addresses are authorized against
--admin-resource-root.- The OCI seccomp profile gates
ptrace/process_vm_*onCAP_SYS_PTRACE; the page-cache
integrity key is createdO_CREAT|O_EXCLat mode 0600 (no umask window, no symlink redirect).
New subsystems since the previous sync (all opt-in)
- FastCGI gateway — bounded wire protocol, connection pool, CGI response parsing, streaming
handler, and suffix routing. - WAF sidecar — request inspection before cache and backends with bounded aggregate memory;
when configured, every request funnels through inspection (fast paths decline). - ACME client — durable HTTP-01 issuance/renewal with TLS activation, plus scoped DNS-01
wildcard issuance with durable cleanup. - OCSP stapling — opt-in validated staple lifecycle with fail-closed "required" mode.
- Admin APIs — read-only loopback introspection (
GET /v1/status) and an opt-in write
transaction API: token-file auth, bounded control requests, JSON config submissions validated
against operator-declared resource roots, and revision-checked publications that leave the
current configuration serving on any failure. - Transactional zero-downtime reload — configuration changes are classified; TLS/cert and
trust-policy changes flow through prepared same-socket TCP successors with kernel-verified
socket-identity handoff, raced-accept transfer, activation gating, and generation-pinned
connection views. Failed candidates leave the currently serving generation untouched. - Reverse-proxy load-balancing groups — weighted selection, strict group configuration
(canonical unix syntax, validated ports), bounded active health probes, and generation-scoped
pool lifecycle. - OpenTelemetry — bounded OTLP trace/metric export with an explicit trust policy for inbound
trace parents; spans cover H1/H2/H3, WebSocket relays, LSAPI dispatch, and cache/rewrite
stages; export never blocks request handling. - kTLS — auto mode gated on real NIC TLS-hardware offload; H1 file bodies send via
sendfile
on plaintext and kTLS connections. - Logging — structured JSON error events with credential redaction, an optional syslog tap,
and a shared parser corpus replay. - Bounded request decompression for
Content-Encodingrequest bodies, and a hardened OCI
packaging example.
Dependency refresh
All dependencies bumped to their latest releases, including major versions: rustls
0.23.45 (closes RUSTSEC-2026-0285), tokio 1.53.1, quinn 0.11.12, sha2 0.11, hmac 0.13,
zstd 0.14, brotli 9, base64 0.23, bcrypt 0.19.3. The vendored monoio fork was ported to
io-uring 0.7.15 (upstream monoio 0.2.4 remains the latest release): the fork's multishot
accept machinery re-applies unchanged on the 0.7 Builder<S, C> defaults, and the dependency
graph dedups to a single io-uring version. The minimum supported Rust is now 1.98.
Upgrade notes
- PROXY-protocol listeners: connections whose PROXY header claims a loopback or unspecified
source are now closed by design. If a fronting load balancer sends such headers, fix the LB. - Rewrite
[P]rules targeting loopback, link-local, or unix sockets now return 403 — declare
same-host backends as config proxy contexts instead; operator configuration is trusted,
rewrite-driven targets are not. [E=SCRIPT_FILENAME]-style env overrides of server-computed CGI variables are ignored;
front-controller rewrites usingSCRIPT_NAME/QUERY_STRINGare unaffected.setUIDMode/chrootModevalues outside 0–2 abort the affected vhost config load instead of
silently disabling isolation.- Everything else is behavior-identical for conformant clients; new subsystems activate only via
their flags.
Validation
- Full workspace test suite (16 crates, ~1,500 tests) green; new regression tests cover every
security fix, including io_uring-driver tests that drive the real h2 deadline paths. - fast-memo parity gate: 192-case byte-identity matrix on a real 800-line
.htaccesschain —
zero differences. - Page-cache cross-URL, cross-scheme, and cross-host gates green; file-tier persistence gate
restored 148k entries across a restart; private-tier session-isolation gate green. - A live probe on an origin-reflecting chain confirms per-origin CORS caching: a no-Origin
request no longer replays the first storer'sAccess-Control-Allow-Origin.
Co-Authored-By: Claude Code noreply@anthropic.com