Repository navigation
Run request filters and the device module on all adapters - #1410
Open
jwrosewell wants to merge 27 commits into
Open
jwrosewell wants to merge 27 commits into
jwrosewell wants to merge 27 commits into
Conversation
…e device module Each of the three adapters now runs the request cycle the Fastly adapter runs. After the modules' preparers, a named route other than the inspection endpoints, and the publisher fallback, classify the device with the module [device] selects (derive_device_signals), read the Edge Cookie context with that classification (build_ec_context), and run the integration request filters (run_pre_route_filters) before the route. A filter that answers stands in for the route, its request headers reach the route, and its response headers are left on the response for FinalizeResponseMiddleware, which writes them after the finalize headers and then restores the privacy guards (apply_terminal_response_effects). robots.txt is answered once the preparers have run and before the Edge Cookie steps and the filters, with an empty Edge Cookie context. Axum's named routes and fallback go through execute_named and execute_fallback where they went through execute_handler. Cloudflare and Spin register the same paths and methods as before, each handled by named_route_handler, and the fallback by execute_fallback. On Spin the discovery, signature verification and first-party routes and the page-bids preflight now run the preparers, which they skipped. These adapters supply no TLS or HTTP/2 signals, so [device] module = "platform" builds the built-in module there. src/app/request_cycle_tests.rs in each adapter checks, through the router its composition root builds and a stub origin, that a filter's answer stands in for the page and the origin is not asked, that a filter's request header reaches a module route and its response header is written after the operator's, that the selected device module's classification reaches a module route's call, and that robots.txt is answered with a filter that answers everything registered. cargo test-axum (1,357 passed), cargo test-cloudflare (61) and cargo test-spin (95) pass, and so do the Cloudflare wasm32 check, the Spin native and wasm32-wasip1 checks and its release build, clippy with warnings denied on the three adapters and cargo fmt.
…ry adapter docs/superpowers/specs/2026-10-17-request-filters-on-every-adapter-design.md sets out what the Fastly adapter runs that the Axum, Cloudflare and Spin adapters do not, the request cycle each of them now runs and the routes it covers, how device classification there differs from the Fastly entry point, the timeouts their HTTP clients give a filter's call, and what a deployment sees change.
…pter The changelog gains a line under Changed, and the 51Degrees client hints line no longer says only the Fastly adapter runs request filters. The 51Degrees, DataDome, permission model, configuration and attestation guides no longer limit the request filters, the order robots.txt is answered in or the order the attestation endpoint is answered in to Fastly, the 51Degrees guide gives what the other three HTTP clients wait for, and the Axum, Cloudflare and Spin guides say the filters and the device module run there and what platform classifies from.
# Conflicts: # docs/guide/spin.md
jwrosewell
added this pull request to stack #1408
October 18, 2026 01:31
…and Spin Four tests on each adapter fail when one of the guards that apply_terminal_response_effects runs after a filter's response headers is taken away: a response core marks private stays no-store, private, a filter's Set-Cookie keeps the response from shared caches, a shared cache header a filter adds to a private response is removed, and the validators an operator writes on a response core marks private are removed. Four more show that platform classifies with the built-in module, that an Edge Cookie context that cannot be read answers before the filters while a client's unreadable Cookie header is rebuilt by a linked preparer first, that robots.txt shows the device module none of a crawler's cookies, and that the inspection endpoints and the attestation pages skip the filters.
robots.txt is answered with an empty Edge Cookie context, so its Edge Cookie and consent are not read and no device module is shown its cookies, but the publisher path still parses its Cookie header, as for every request.
…n every adapter Every adapter now classifies a request with the device module [device] selects, where a module was asked only for bid request attributes off Fastly. DataDome's timeout_ms and the 51Degrees timeout_ms apply on the Fastly adapter only, DataDome checks /auction and both page-bids paths, the client tag suppression holds on every adapter, and a request filter's response header is written after X-Robots-Tag. The middleware readme no longer says only the Fastly adapter runs request filters.
The table of parts, the builder, the timeout_ms setting, consent, failure, rollout, section 3 and the tests no longer say only the Fastly adapter runs request filters, and a table gives what bounds the call on each adapter. The review log moves after the revision record, as in the other designs.
It says what a selected device module did on Axum, Cloudflare and Spin before, what DataDome's filter sends and sets with no permission gating it, why no time limit is added to a filter's call, that the Fastly adapter answers all three inspection endpoints before the preparers, which routes stay out of the cycle, which of a crawler's cookies robots.txt reads, when the Cookie check is reached, the configuration, the new tests, the open questions, and ends with its review log.
… Axum, Cloudflare and Spin A new test on each adapter sends every named route the cycle runs, being discovery, /verify-signature, /auction, GET and OPTIONS on both page-bids paths and the four first-party paths, to a filter that answers every request, and asserts that each is the filter's answer with its effect header. The spoiling preparer now also spoils the discovery route, so the context test shows the preparers and the Edge Cookie context run for a named route as well as for a page. Taking run_pre_route_filters out of any adapter's execute_named now fails the new test alone, and taking build_ec_context out of it, or on Spin the preparers, fails the context test alone. The context test's comment says the linked preparer drops a Cookie header value it cannot read, rather than rebuilding the header from the values it can.
build_ec_context answers with an error for a Cookie header that HeaderValue::to_str refuses, being one holding any byte outside visible ASCII, an accented letter in UTF-8 among them, and not only one that is not valid UTF-8, as the comment said on Axum, Cloudflare and Spin.
A device module a registration offers is shown the client's IP address as well as the User-Agent and the cookies, in a module context that carries the request's method, scheme, host, path and query.
Section 2.11 lists apply_terminal_response_effects on all four adapters, at the Fastly entry point and in the other adapters' FinalizeResponseMiddleware, and section 1 no longer calls the Fastly adapter the only one that calls detect.
…sts skip On Axum a service that cannot be reached fails within the client's 5 second connection limit and a refused connection fails at once, so only a service that accepts the connection and then stalls holds the call for up to 30 seconds. No alias runs the two 51Degrees crates' tests for the WebAssembly target of any adapter, not only the Fastly adapter's, because cargo test-cloudflare and cargo test-spin test the adapter crate alone.
The review of the change that has every adapter run the filter counts as this design's third round, found nothing blocking and was answered in e7417c8.
It lists the test that sends every named route in the cycle through the filters and the context test's discovery request, names the tests each removal fails, names the IP address and the request's address a device module is shown and the subresources it classifies, says the diagnostics preparer drops a Cookie header value whole, names the two DataDome designs whose scope it lifts, says what the guards do with a platform's own cache header with an open question on it, gives a true reason no shipped filter is tested through an adapter, and logs the round.
# Conflicts: # CHANGELOG.md
jwrosewell
added this pull request to stack #1418
October 19, 2026 12:18
jwrosewell
removed this pull request from stack #1418
October 19, 2026 16:30
jwrosewell
added this pull request to stack #1421
October 19, 2026 17:38
jwrosewell
removed this pull request from stack #1421
October 19, 2026 18:29
jwrosewell
added this pull request to stack #1422
October 19, 2026 19:20
This was referenced Oct 19, 2026
jwrosewell
marked this pull request as ready for review
October 19, 2026 20:24
13 of 14 tasks
19 tasks done
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Changes
crates/trusted-server-adapter-spin/srccrates/trusted-server-adapter-cloudflare/srccrates/trusted-server-adapter-axum/srcdocs/superpowersdocs/guidecrates/middleware/51degreesCHANGELOG.mdCloses
Part of #777
Spec
Spec: request-filters-on-every-adapter
Test plan
CI on this pull request at 18e8765 passed all 27 checks.
cargo test-fastly && cargo test-axumcargo clippy-fastly && cargo clippy-axumcargo fmt --all -- --checkcd crates/trusted-server-js/lib && npx vitest runcd crates/trusted-server-js/lib && npm run formatcd docs && npm run formatcargo build --package trusted-server-adapter-fastly --release --target wasm32-wasip1fastly compute serveChecklist
unwrap()in production code (clippy deniesunwrap_usedin CI)logmacros as AGENTS.md asks (notprintln!)Stacks on ts config migrate (#1409).
What this pull request does
The design is request-filters-on-every-adapter. The modules' request filters and the device module ran on the Fastly adapter alone. The Axum, Cloudflare and Spin adapters now run them in the order Fastly does: the request preparers, robots.txt, the device module, the Edge Cookie context with the device result on it, the request filters, then the route. A filter's answer stands in for the page or the named route, a filter's request header reaches the route, and its response header is written last, after the finalize headers, without making a private response less private. robots.txt, the inspection endpoints and the attestation pages are still answered without the filters. So the 51Degrees client hints middleware and DataDome run on every adapter.
/verify-signature, the first-party routes and the page-bids preflight now run the modules' preparers, which they skipped before.Cookievalue that is not UTF-8 is dropped whole by a preparer every adapter links, the Edge Cookie and consent cookies in it included, which is new on the Spin routes above.User-Agentand its cookies, and on these adapters it classifies every request the fallback proxies, subresources included.[device] module = "platform"builds the built-in module on these adapters, because they supply no TLS or HTTP/2 signals.timeout_msto a filter's outbound call, and the design and the 51Degrees guide say what bounds the call on each.How it was verified
Locally on Windows at 076f4ad:
cargo test-axum(1,368),cargo test-cloudflare(72) andcargo test-spin(106) passed, with clippy refusing warnings on the three adapters andcargo fmt --all -- --check. On each of the three adapters, four tests fail when one of the guards that keep a filter's response headers from making a private response less private is taken away, and one test sends all 13 named route and method pairs in the cycle to a filter that answers, so takingbuild_ec_contextorrun_pre_route_filtersout of the adapter'sexecute_namedfails a test. Each new adapter test fails with its change reverted. At f778984 the cross-adapter parity tests (17) and the Cloudflare and Spin WebAssembly checks passed.