Repository navigation
Call a geo module with the request - #1374
Open
jwrosewell wants to merge 51 commits into
Open
jwrosewell wants to merge 51 commits into
jwrosewell wants to merge 51 commits into
Conversation
locate_request on dyn GeoModule builds the call every geo call carries, the request as evidence from its headers and the client address, the settings when the caller has them, the services and the client, and the Edge Cookie path, the auction endpoint, the Didomi module and the seam probe call it. The Fastly adapter's two finalize paths call it without headers and say why: the module there is the host's own lookup or the disabled one, chosen by build_geo_module, which reads the address alone, so the middleware no longer clones the headers for it.
# Conflicts: # CHANGELOG.md
…the deployment selects The finalize step that writes the x-geo-* headers on every response built its geo module with build_geo_module alone, which answers the host's own lookup for "platform" and the disabled module for every other name, because it cannot see the registry. A deployment whose [geo] module names a module from the registry therefore got no location on that path, and every page carried x-geo-info-available: false, while the same module answered the request path. The adapter now hands the finalize step the registry's module when the selector names one, through finalize_geo_module, and the host's own lookup or the disabled module otherwise, as before. The step keeps the request's headers before the handler takes the request and hands them to the module as its evidence, so a module that reads the request, as a device module does, is called the way the request path calls it, and the answer it holds for the same evidence from earlier in the request is the one it gives. The note on locate_request no longer says the finalize paths never call a registry module. A test proves the finalize step hands a geo module the request's headers, with a module that records the user agent of the evidence it is called with.
Describes how a geo module is called with the request's module call, as a device module is, in the series that adds GeoModule, PlatformGeoModule and locate_request. Covers each adapter, the [geo] settings, what request data a geo module now sees, failure handling, rollout for crates outside core, and the tests, with the joins that have none. Written after the code, for draft pull request #1374, which answers issue #1321.
The page no longer says a geo module is called as a device module is. On Fastly a device module's detect is shown the client address, the User-Agent header and, for a registered module, the Cookie header, where five of the six geo callers hand the module every request header. The page now states that the Fastly router path keeps the request before the request preparers run, so its call can differ from the request path's call, and that the 401 rule stops only the second call. It also adds the services graph of the finalize steps, the response headers that no permission guards and the wait for the module, which the review found missing. The code changes the review points to are listed as open questions. The changelog entry names the builder types and the registry items it left out, and the integration guide's geo paragraph no longer splits the paragraph about module names.
The page now says why the Fastly entry-point step skips the registry, which responses carry the Edge Cookie state, what the Fastly device module reads and the type of the registration field, and names the permissions endpoint for the other three adapters. The changelog, the integration guide and four stale comments are listed under the open questions with their files and lines.
…erve middleware Core and the adapters stop writing the x-geo-* headers and stop calling a geo module when they finish a response. The serve middleware headers.geo, in crates/headers/geo, writes the headers from the location the request already resolved, on the responses a [[serve]] entry naming it covers. The design also lists the other changes a finalize step makes to a response, so each can move to a middleware later.
…eware Core and the four adapters no longer write any x-geo-* header, including x-geo-info-available, and the Fastly adapter no longer asks a geo module a second time when it finishes a response. The module headers.geo, in crates/headers/geo, supplies a serve middleware that writes the headers from the location the request already resolved, which its preparation takes from the module call, so the headers appear only on the responses a [[serve]] entry naming it covers. A document that selects the module and names it in no [[serve]] entry is refused. GeoInfo::set_response_headers and the HEADER_X_GEO_* constants move from core to the crate. The Fastly services builder that only the entry point's device module now uses is renamed for that use, and the request copy and the 401 rule that existed for the finalize lookup are removed with it.
The changelog marks as breaking that no response carries an x-geo-* header until a deployment selects headers.geo and names it in a [[serve]] entry. The configuration guide's geo section shows the two lines, the Fastly guide's client address check says where the headers appear, and AGENTS.md lists the headers type folder.
This branch hands the services a geo module, which is called with the request, where they held a platform geo lookup. The helper that builds the services for the three tests the demo Edge Cookie module fix brought up from split/4-client-resolve still handed it NoopGeo, so core's tests stopped compiling here. It now wraps NoopGeo in a PlatformGeoModule named "noop", as the noop services in test_support do.
`[geo] module = "platform"` selects the host's own geo lookup. The Axum dev server's lookup and Spin's resolve nothing, so on those two adapters the selection located no request and still counted as a geo module, which let a deployment running an Edge Cookie module skip the `assume_single_jurisdiction` acknowledgement that a deployment with no geo module must make. Each of the two adapters now refuses the selection at startup, naming the setting and saying the host has no geo lookup. Services a caller supplies carry a lookup of their own for `platform` to select, as the Next.js auction fixture's do, so the check applies only to the services the adapter builds. A test on each adapter checks the refusal. The integration tests' shared fixture selects `platform` for Viceroy, so the Axum dev server they start now has `[geo] module` unset and single-jurisdiction operation acknowledged, which locates no request as its own lookup did. The configuration guide says what `platform` selects on each adapter, and with the Cloudflare guide that Cloudflare places a visitor in a region only from the `cf-region-code` request header. The architecture table, the Spin guide, the changelog and the geo design record the refusal.
# Conflicts: # docs/guide/cloudflare.md
jwrosewell
marked this pull request as ready for review
October 19, 2026 20:23
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/headers/geocrates/trusted-server-core/srcdocs/superpowerscrates/trusted-server-adapter-fastly/srccrates/trusted-server-adapter-spin/testsdocs/guidecrates/trusted-server-adapter-axum/testscrates/trusted-server-adapter-spin/srccrates/trusted-server-adapter-cloudflare/testscrates/trusted-server-adapter-axum/srccrates/trusted-server-integration-tests/testscrates/cmp/didomicrates/testing/seam-probecrates/trusted-server-adapter-cloudflare/srcCargo.locktrusted-server.example.toml.cargo/config.tomlcrates/trusted-server-cli/srccrates/trusted-server-modules/srcCHANGELOG.mdcrates/auction/prebid-servercrates/loading-cache/51degreesCargo.tomlAGENTS.mdcrates/trusted-server-modules/Cargo.tomlCloses
Closes #1321
Spec
Spec: geo-module-request
Test plan
CI on this pull request at 79a0421 passed all 29 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!)On top of a module declares the caches it loads through (#1373). Issue: #1321.
What this adds
A geo module is a
GeoModule, called with a module call, the type a device module is called with, aslocate(call), so it sees the request's evidence, the settings and the services, and a module whose one service answers location and device from the same request can answer both from one call. The evidence is wider than a device module'sdetectis shown, because five of the six callers pass every header of the request they serve. A module that resolves from the client address alone keeps implementingPlatformGeoand is offered throughPlatformGeoModule::new(name, module), which hands the platform lookup the address the call carries and asks nothing when the call carries no services.RuntimeServices::geoandIntegrationRegistration::with_geo_moduletake anArc<dyn GeoModule>,build_geo_modulereturns one (platformwraps the host's lookup, anything else the disabled one), and the registry resolves[geo] moduleto aGeoModule. Core builds the call in one place,locate_requestondyn GeoModule, from the request's view, its headers, the settings and the services. The Edge Cookie path, the auction endpoint, the Didomi module and the seam probe call it with the request's headers. The Fastly adapter writes thex-geo-*headers on every response in two steps. The one in the router's middleware chain keeps the request's headers as the request arrived and calls the module the deployment selects, being the registry's module when[geo] modulenames one, and otherwise the host's own lookup or the disabled one, asbuild_geo_modulechooses. The entry-point step, for a response that did not pass through the middleware chain, calls the host's own lookup or the disabled one with no headers, because the entry point keeps only the request's view.Why
A geo module was called as
lookup(client_ip, services), with the address and nothing else of the request, while a device module is called with the request's evidence. A vendor whose one service answers location and device from the same request could not make the geo answer from the same call as the device answer, so either the two were asked separately or the geo answer depended on the device module having run first on the same adapter. The adapters resolved geo in four places with the request in hand and handed the module only the address.What breaks
Crates outside core that passed a
PlatformGeotowith_geo_moduleorRuntimeServices::geowrap it inPlatformGeoModule::new(name, module), or implementGeoModuleto see the request. The Didomi module's geo call is rewritten to go throughlocate_request, and reads the resolvedGeoInfoas before. Nothing in a deployment's documents changes. On Fastly, a deployment whose[geo] modulenames a module from the registry now gets that module's location in thex-geo-*headers of every response the router's middleware finishes, where those responses carriedx-geo-info-available: falsebefore. The headers carry the module's city, coordinates, metro code and region as well as its country, and no permission guards them. Responses finished by the entry-point step are unchanged. The middleware also copies the request's method, headers and URI before the handler runs, on every request.The commits
The other 13 commits merge the branch below into this one, and through it Tech Lab main at 6ef07ae.
What is not in this pull request
PlatformGeoand are wrapped. Fastly's resolves the client address, Cloudflare's reads the platform'scf-*request headers, and Axum's and Spin's resolve nothing.