Repository navigation
Move analytics and cache header names out of core - #1389
Open
jwrosewell wants to merge 53 commits into
Open
jwrosewell wants to merge 53 commits into
jwrosewell wants to merge 53 commits into
Conversation
Core read `[analytics]` itself, with one vendor's settings typed into settings.rs, their checks beside them and the module that sends the rows built by the Fastly adapter alone. Every other module type is a section core reads generically and a crate that supplies the module, and analytics now follows. A registration declares the sink it supplies with `with_analytics_module`, the registry resolves `[analytics] module` against the sinks on offer as it resolves `[geo] module`, and `IntegrationRegistry::analytics_module` gives the one selected, or none, so core's no-op sink stands. A section selecting two names is refused. The Tinybird module, its settings, its checks and its sink move to crates/analytics/tinybird. It reads its table through `module_config`, declares the token as a secret setting so the token is looked up and checked as every module's secret is, and is offered by trusted-server-modules, so every adapter that builds from that list links it. The Fastly adapter asks the registry for the sink. The settings a deployment writes do not change. A table missing its host or token, an unknown key and a reserved setting turned on are refused when the deployment starts and by `ts config validate` rather than as the settings load. A `[tinybird]` table at the top level is refused as the section of a module type nothing supplies, where it was refused with directions that named the vendor in core.
Core wrote a shared cache policy in one of four headers it named itself, two of them one platform's own, and stripped all four from every response it made private. It now knows the two headers every platform may read, `Surrogate-Control` from the Edge Architecture Specification and `CDN-Cache-Control` from RFC 9213, and asks the running platform for the rest. An adapter declares its platform's headers with `platform::declare_platform_cache_headers` as it builds its state, once per process, and `cache_policy::edge_cache_header_names` lists the two core reads and then the declared ones. That list is what core strips from a response it makes private, skips among an operator's response headers on an uncacheable response, and treats as a policy it does not read when it decides whether an origin page may be kept. The header an adapter writes the policy in is `EdgeCacheHeader::Platform(name)`, which replaces the variant that named one platform. The Fastly adapter declares `fastly-surrogate-control` and writes `Surrogate-Control`. The Cloudflare adapter declares `cloudflare-cdn-cache-control` and writes it. The Axum and Spin adapters declare nothing. Responses carry the headers they carried. Not yet built: this commit was formatted and checked for leftover names but no cargo check, clippy or test has run on it.
The modules crate is gone, so the analytics module is listed by each adapter and the ts command after the client hints middleware, and the hook order tests name it there. The registry keeps both kinds of module a registration may now declare, and the settings lose both the consent validation and the analytics normalization to the crates that read them.
# Conflicts: # CHANGELOG.md
Clippy refuses the closure that only called it.
The guide lets a section's selector name a module with or without its type folder, as analytics.tinybird or tinybird in [analytics], and its table is keyed by the name without it. The type sections' table check and a module's own table lookup now shorten a written full name first, and the shipped module test gives a module's declared secrets a value so the unknown key is what the module refuses.
The Tinybird crate's README and the telemetry guide still said the deleted modules crate offers the module. Each adapter and the ts tool list its builder among the modules they link.
Core strips the two standard cache headers and the ones the running adapter declares, so the test declares an example platform header as an adapter would and checks all three are stripped, in place of two platforms' own headers that no adapter declares in this crate's tests.
…end telemetry Review round 1 found that the design named a composition crate that no longer exists, said nothing changes for an operator when each adapter now strips a different set of cache headers, left out that only the Fastly adapter sends auction telemetry, and did not list the core change that lets a section name a module with its own type folder. The design now says each of these, puts the narrowing of the stripped headers as an open question, and gives the defaults, the removed interfaces, the rollout, the paths with no test and the review log. The changelog and the telemetry guide no longer repeat the same claims.
Review round 2 found that the full name of the analytics module, analytics.tinybird, fails ts config validate and the load of a stored configuration, because the deploy check and the load look for the module table at the name the selector wrote, and that the selection test reaches the full name through the settings load and the module registration only. The design now says so plainly, lists the code fix under the open question, adds a rollout note for a deployment that writes the full name, and adds that core reads fastly-ssl as a scheme signal and removes it before calling the origin. The design never named the closed specs pull request, so no reference changed.
… its type folder With `[analytics] module = "analytics.tinybird"`, the deploy check looked for the token at `[analytics.analytics.tinybird]` and refused an empty reference, and the load of a stored configuration cleared the token from `[analytics.tinybird]` as unused, so the module refused a missing token. The short name `tinybird` worked through both. The deploy check of a module's secret settings and `module_secrets::clear_unused` now shorten a name written with the section's own type folder, as `check_tables` and `Settings::module_config` already do, so both use the table the module reads. A name under another type's folder is unchanged. Core tests a module of another type selected by its full name through the deploy check, the clearing of secrets not in use and the load, and the Tinybird crate tests its full name through the deploy check of its token and the load of a stored configuration.
…ponse With the platform header names out of core, each adapter declared only the header its own platform reads. A Fastly service then kept `cloudflare-cdn-cache-control` on a response core made private, a Cloudflare service kept `fastly-surrogate-control`, and the Axum and Spin adapters kept both, where core stripped all four headers on every adapter before. A policy left in one of them could let a shared cache of that platform in front of the service keep a personalized response. Each adapter now declares both platform headers, so the list core strips from a private response, removes before writing its own policy, skips among operator headers on an uncacheable response and treats as a reason not to keep an origin response holds the same four names as before on every adapter. Core still names neither platform. A test in each of the four adapters builds the state and checks that a response core makes private loses both headers. The Fastly adapter's header constant moves above the doc comment of `build_state_with_registrations`, which it had split.
The design closes its open question. Every adapter declares both platform cache headers and strips the four names core stripped before, so nothing changes for an operator, and the deploy check and the load of a stored configuration read a module selected by its full name at the table without its type folder, so `analytics.tinybird` works as `tinybird` does. Section 4 lists the new tests, the untested paths drop what is now tested, and the revision record has a row for the fixes. The changelog says each adapter strips the same four headers as before, and that a module named in full in its own type's section reads its table without the type folder.
…vate response" This reverts commit 6a22766. Each adapter named the other platform's cache header so that core would strip it, which breaks the rule that an adapter names only its own platform. Removing a header the origin sent is a fetch middleware's job that a deployment chooses, and the cache headers a response is served with follow the cache policy of the item as stored, so neither core nor an adapter strips a platform's header by name. The change that follows this one applies that.
Only a middleware changes an asset or a response, and the cache policy of an item as stored is model. So removing a header an origin sent is a fetch middleware's job that a deployment chooses, and the cache headers a response is served with mirror the item's policy in the headers core owns, being `Cache-Control`, the two standard headers and a platform header core wrote its policy in on that response. A new section 2.3 says what each use of the declared list becomes: core removes from a private response, and skips among an operator's headers, only the headers it owns; it removes the two standard headers before it writes a policy; and the declaration of each adapter's own platform header is read only to decide whether an origin response may be kept. It says what a deployment configures, a fetch middleware none of which exists yet, needing a core change that lets a fetch middleware remove a header and one crate per platform under `crates/middleware/`, and it lists every place in this row where core or an adapter still writes or removes cache headers outside a middleware.
Core removed every header an adapter declared from a response it made private, skipped them among an operator's headers and removed them before writing its own policy, so removing a platform's header that an origin sent was core's job and an adapter had to name headers for core to strip. Removing what an origin sent is a fetch middleware's job that a deployment chooses, and the cache headers a response carries follow the policy of the item as stored, so core now writes or removes only the headers it owns: `Cache-Control`, `Surrogate-Control`, `CDN-Cache-Control` and, on one response, a platform header it wrote its policy in. `CachePolicy::apply_to_response` records that header on the response as a `WrittenPlatformCacheHeader`, and `remove_owned_cache_headers` and `is_owned_cache_header_name` read it, so the privacy steps clear the policy core wrote there and nothing else. A private policy now leaves nothing in the header it is written in. `EDGE_CACHE_HEADER_NAMES` lists the two standard headers again, in place of `edge_cache_header_names()`. An adapter still declares its own platform's header, and core reads the declaration only to keep an origin response with a policy in that header out of the stored copies. Each adapter holds the header it writes its policy in as `EDGE_CACHE_HEADER`, and the Fastly adapter's header constant moves out of the doc comment of `build_state_with_registrations`, which it had split. Core's tests cover a declared header left on private and cookie responses, a policy written in a platform header cleared and not restored by an operator header, and a private policy emptying its header. Each adapter tests that it declares its own platform's header alone, and that a response core makes private carries the private policy, loses the policy core wrote and keeps the header the origin sent.
…owns Section 2.2 says each adapter declares only its own platform's header, read only to decide whether an origin response may be kept, and what changes for an operator: `fastly-surrogate-control` and `cloudflare-cdn-cache-control` stay on a private response where an origin or an operator wrote them, and only the declaring adapter keeps an origin response with a policy in its header out of the stored copies. Section 2.3 drops a line 2.2 now carries, says which headers the keep decision reads and adds the no-store helper to the places that still alter the response. Section 3 names the fetch middleware and the move into a serve middleware as later work, section 4 lists the new tests, and the revision record corrects the row for the reverted change and adds one for this change. The changelog entry says the same, and the configuration and Didomi guides say which cache headers are removed and which policies bypass the template cache.
This was referenced Oct 17, 2026
jwrosewell
marked this pull request as ready for review
October 19, 2026 20:24
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/analytics/tinybirdcrates/trusted-server-core/srcdocs/superpowerscrates/trusted-server-adapter-cloudflare/srccrates/trusted-server-adapter-fastly/srccrates/trusted-server-adapter-axum/srccrates/trusted-server-adapter-spin/srcdocs/guideCargo.lockcrates/analytics/README.md.cargo/config.tomlCHANGELOG.mdCargo.tomlcrates/ad-tag/googlecrates/trusted-server-cli/srccrates/trusted-server-adapter-axum/Cargo.tomlcrates/trusted-server-adapter-cloudflare/Cargo.tomlcrates/trusted-server-adapter-fastly/Cargo.tomlcrates/trusted-server-adapter-fastly/README.mdcrates/trusted-server-adapter-spin/Cargo.tomlcrates/trusted-server-cli/Cargo.tomlCloses
Part of #1341, which stays open for the three names this pull request leaves in core.
Spec
Spec: last-names-in-core
Test plan
CI on this pull request at 252c068 passed all 26 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 the OpenRTB series (#1388). Issue: #1341.
What this adds
What changes.
Two of the issue's five rows: the Tinybird telemetry module lives in the crate named for its vendor, and each adapter declares the headers its platform reads a cache policy from.
How it works.
crates/analytics/tinybirdholds the settings, the checks and the sink that core's settings and the Fastly adapter held, selected by[analytics]as today. Core gains the analytics module kind on a registration, resolved by the registry, and the adapter asks the registry for the module it links. The token is declared as a secret setting through the builder, so the generic secret handling covers it. The vendor's previous top-level table is refused as any section nothing supplies is, with no direction naming the vendor in core.platform/cache_headers.rsonce per process, as the adapters already declare their registered integrations. Core's cache policy, the response privacy rules, the proxy and the publisher read the declared list.Why
Core is the interfaces, the routing, the origin fetch, the caches and their tests. Each name here was a place where a change by one vendor or one platform was a change in core.
What breaks
Three refusals of the analytics settings move from settings load to the registry's build and to
ts config validate, because the module reads its table as every module does. A deployment's settings do not change.The commits
tscommand list the analytics module after it, where the removed modules crate listed it, and the registry keeps both kinds of module a registration may now declare.What is not in this pull request
The issue's other three rows: the platforms' store names and the removed store ids in the settings and the signing code, the JA4 fingerprint class in the Edge Cookie device classification, and the JWKS key format in request signing. The design page records where each goes.