Skip to content

docs(spec): one owned request-normalisation boundary across the five runtime shapes - #27

Draft
antstanley wants to merge 1 commit into
spec/harden-admin-planefrom
spec/runtime-parity-across-interfaces
Draft

docs(spec): one owned request-normalisation boundary across the five runtime shapes#27
antstanley wants to merge 1 commit into
spec/harden-admin-planefrom
spec/runtime-parity-across-interfaces

Conversation

@antstanley

@antstanley antstanley commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Note

Stacked PR 12 of 16 — part of stack #35, which targets main.
Base: #26 (spec/harden-admin-plane) · Followed by: #31

This PR's diff shows only its own change. Merge the stack bottom-up; do not merge this before its base.

Why it sits at position 12: Touches 06-configuration.md's [server] section and 04-http-api.md's bootstrap step 6, both also edited by #29, #24 and #26.

Scope of this PR

This PR currently carries the change spec. It will also carry the implementation plan and the build work for that spec — plan and code land as later commits on this same branch, so the spec, its plan, and its implementation review and merge as one unit.

Review order within the PR: spec first, then plan, then implementation.


Change spec proposing one owned request-normalisation boundary across the five runtime shapes.

Problem. The same service is published behind five request shapes — the Axum server, the Lambda runtime, the napi Node binding, and the PyO3 WSGI and ASGI adapters — and each reconstructs the request differently. The defects are not one bug repeated; they are the consequence of normalisation being re-implemented per host with no shared conformance definition: assert!s on host-supplied method/path that panic on a legal empty PATH_INFO, a synchronous napi handleRequest that block_ons the whole request and stalls the host process, uncapped body buffering on both Python adapters, two decoded-path reconstructions, two duplicate-header collapses, and three differing Lambda base-path strips.

Proposed delta. Hosts hand over raw percent-encoded path bytes, a separate query string, ordered header pairs, and a body already bounded against a published limits(). The FFI performs every decode, segment-aware base-path strip, header ordering, and body check exactly once, and handle becomes async and total — shaping failures return the same status the native server would, so an embedded host never returns 502 where the server returns 404.

Adds a differential conformance corpus replayed through all five shapes as a CI merge gate — this is what stops the drift recurring — plus an explicit server.max_request_body_bytes enforced before buffering on every host, and two-guard panic containment (inner CatchPanicLayer retained for x-request-id correlation, a second guard wrapping the base-path service, and an FFI-level catch_unwind).

A live bug confirmed in passing. The Some("/") base-path case is genuinely reachable: strip_prefix_at_segment_boundary("/", "/") returns Some(""), new_path becomes /, and the assert_ne! in base_path.rs then fires on every request to /.

Breaking change, stated plainly. The async signature breaks @oidc-exchange/node and PyPI oidc-exchange. One shared version string means 0.2.x → 0.3.0 across all three manifests, with sync entry points deprecated for one major cycle and a per-package migration table.

Open questions recorded rather than assumed: whether a panic across the napi extern "C" trampoline aborts Node for this build (catch-unwind is not enabled), and which WSGI/ASGI servers are used in practice and whether they drop underscore headers.

Pre-existing divergence found: server.base_path exists in crates/core/src/config.rs but was never added to 06-configuration.md — the merged implement_lambda_runtime.md proposed that block and it was not applied. The spec flags it and supplies the text.

Drafted with the spec-creator plugin from a deep security scan of the repository at 53cbdec9. Spec only — no source changes.

@antstanley
antstanley force-pushed the spec/runtime-parity-across-interfaces branch 2 times, most recently from 679c924 to 69d431c Compare August 5, 2026 08:22
@antstanley
antstanley changed the base branch from main to spec/harden-admin-plane August 5, 2026 08:22
@antstanley
antstanley marked this pull request as draft August 5, 2026 08:25
@antstanley
antstanley force-pushed the spec/runtime-parity-across-interfaces branch from 69d431c to ef1ce74 Compare August 5, 2026 09:00
@antstanley
antstanley force-pushed the spec/runtime-parity-across-interfaces branch from ef1ce74 to 52d4e49 Compare August 5, 2026 11:29
…runtime shapes

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@antstanley
antstanley force-pushed the spec/runtime-parity-across-interfaces branch from 52d4e49 to 289e8ee Compare August 5, 2026 12:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant