Repository navigation
Add the module documentation set and finish the decomposition - #1047
Open
jwrosewell wants to merge 231 commits into
Open
jwrosewell wants to merge 231 commits into
jwrosewell wants to merge 231 commits into
Conversation
jwrosewell
force-pushed
the
split/5-response-hook-docs
branch
3 times, most recently
from
August 25, 2026 10:51
7ebce99 to
700c913
Compare
jwrosewell
force-pushed
the
split/5-response-hook-docs
branch
4 times, most recently
from
August 27, 2026 05:37
c17a7ea to
5b63f48
Compare
jwrosewell
force-pushed
the
split/5-response-hook-docs
branch
from
August 27, 2026 15:10
5b63f48 to
0bab4c0
Compare
jwrosewell
added a commit
to jwrosewell/trusted-server
that referenced
this pull request
Aug 27, 2026
The five-PR series (IABTechLab#1043 to IABTechLab#1047) opens the identity, device and geo seams. The nine vendor integrations already in core sit behind the integration registry instead, which is a private table, so none of them can move out until that table is opened. This spec defines the one core change that opens it: public registration builders with a second input on IntegrationRegistry, browser JavaScript carried on the registration, startup validation as a hook, the same treatment for auction providers and the bid renderer contract, and neutral replacements for the two places where a vendor reaches into core. It then sets out the migration of all nine existing integrations, one PR each. The change is complete in itself: after it, no vendor move needs a core change. Written against the series' tree with the file and line references for every claim about the current code. Documentation only.
jwrosewell
force-pushed
the
split/5-response-hook-docs
branch
2 times, most recently
from
August 31, 2026 12:50
3cfe393 to
45acb97
Compare
jwrosewell
added a commit
to jwrosewell/trusted-server
that referenced
this pull request
Aug 31, 2026
The five-PR series (IABTechLab#1043 to IABTechLab#1047) opens the identity, device and geo seams. The nine vendor integrations already in core sit behind the integration registry instead, which is a private table, so none of them can move out until that table is opened. This spec defines the one core change that opens it: public registration builders with a second input on IntegrationRegistry, browser JavaScript carried on the registration, startup validation as a hook, the same treatment for auction providers and the bid renderer contract, and neutral replacements for the two places where a vendor reaches into core. It then sets out the migration of all nine existing integrations, one PR each. The change is complete in itself: after it, no vendor move needs a core change. Written against the series' tree with the file and line references for every claim about the current code. Documentation only.
jwrosewell
added a commit
to jwrosewell/trusted-server
that referenced
this pull request
Aug 31, 2026
The review of IABTechLab#1043 asked that spec changes land before the code that implements them, so a divergence is a decision taken in review rather than a ratification of something already merged. PRs IABTechLab#1043 to IABTechLab#1047 each carried the design document for their own step, and IABTechLab#1043 carried a 607-line spec describing device providers, geo providers, the permission model and the browser resolve endpoint, none of which is in that PR. Move all six series documents here, so this PR carries the complete normative set and no code: - 2026-07-30-pluggable-providers-design.md (from IABTechLab#1043) - provider-code-registry.md (from IABTechLab#1043) - 2026-07-30-permission-model-design.md (from IABTechLab#1045) - 2026-07-30-client-cycle-ec-resolve-design.md (from IABTechLab#1046, later revised by IABTechLab#1047) - 2026-07-30-integration-response-header-hook-design.md (from IABTechLab#1047) - 2026-07-30-provider-migration-rollout-design.md (from IABTechLab#1047) Each file is taken verbatim at the tip of the stack, so the later revisions are preserved: the provider-switching continuity section, the geo requires-signal floor, and the code-envelope paragraph IABTechLab#1047 added to the client-cycle spec. The revision-record tables are unchanged. No document's substance was edited. The only edits are to this spec's own status line, which said the PR adds one document and that the series specs land with IABTechLab#1047, and a revision-record row recording the move.
Collaborator
This was referenced Aug 31, 2026
jwrosewell
added a commit
to jwrosewell/trusted-server
that referenced
this pull request
Sep 1, 2026
The four series specs (client-cycle EC resolve, permission model, pluggable providers, migration and rollout) each carried a Status line saying they were implemented. The code they describe is only in PRs IABTechLab#1043 to IABTechLab#1047 and none of those is merged, so the line read as shipped behavior. Each now says Proposed, names the PR that carries the implementation and states that it is not yet on main, keeping the existing revision dates and notes. The integration provider seam spec carried counts and line references that do not hold on main at d516a9e. Corrected against that commit: - Section 4 said migration_guards.rs embeds "the thirteen vendor files". The directory holds 23 .rs files (2 infrastructure, 6 in nextjs/, 2 in datadome/, 13 top-level integration modules), the guard embeds 20 of them and 9 of those 20 belong to the nine vendors, with osano.rs and the two datadome/ files absent. builders() registers 13 integrations, which is a different 13 from the file count. - Section 3.5 gave no counts for the prepare and finalize calls. There are nine production prepare_request call sites across the four adapters and a tenth in core, and the single production finalize_response call site is in core rather than in any adapter. - Section 8 item 3 described a proxy resolving geo twice, which does not happen on main. The real double resolution is the adapter EC context build against handle_auction on POST /auction. - Section 8 item 5 understated the Spin gap and misdescribed Cloudflare. Cloudflare covers every route it registers and has no health route, while Spin skips its first-party bindings as well as its inline admin stubs. - Line references: settings.rs:166 to :215, auction/mod.rs:49 to the list at :51 to :53, publisher.rs:4361 to :4369. Section 6 now requires the round trip to be proven on the Fastly adapter, the primary deployment target, rather than on any adapter, because Fastly has no library target and the round trip otherwise only runs on the Axum dev server.
jwrosewell
force-pushed
the
split/5-response-hook-docs
branch
6 times, most recently
from
September 1, 2026 23:04
98764db to
d48c98d
Compare
Brings the naming module, so a module is named by its crate folder, and the Edge Cookie side resolving a written name within its type folder. The host-signal module's key and the type folder sit side by side.
Brings the naming module from the Edge Cookie seam, so a module is named by its crate folder.
Each of the five modules takes its name from its crate folder through `module_name!()`, so `crates/permission-signal/gpp` is `gpp` and `crates/permission-signal/us-privacy` is `us-privacy`, where the two had called themselves `gpp_sale_opt_out` and `us_privacy` by hand. The page is told a signal's source by the short name, without the type folder. The section is `[permission-signal]`, named exactly as its folder, and its key is `modules`, a list, because signals compose where `[ec]`, `[geo]` and `[device]` each name one module. A name in the list may be written in full, as `permission-signal.gpp`, or with the type folder left off, and the refusal of a name this build does not offer lists the short names. A configuration still carrying `[permission_signal]`, a `module` key or the retired `sources` key is refused with directions to the new spelling.
Brings the naming module and the permission signal modules named by their crate folders, with [permission-signal] modules. The Edge Cookie module file keeps the client_fixed key beside the type folder, and the agent guide's capability table keeps this branch's rows with the permission signal selector as [permission-signal] modules.
Brings the naming module, the permission signal modules named by their crate folders and [permission-signal] modules, with their guides.
A name written in `[ec] module`, an `[ec.<name>]` block or an `implementation` line is parts joined by `.`, each of lower case letters, digits, `_` or `-`, in place of snake_case, and a module from a crate may be written in full, as `edgecookie.<name>`, or with that type folder left off. The sections table names `[permission-signal]`.
This was referenced Oct 7, 2026
Comments on the adapters and two test notes in core described what the code did before it changed. Each now says what the code does and why. - The resolved Edge Cookie module held on the Cloudflare, Fastly and Spin application state is resolved when the state is built, because resolving reads no request data. - The three tests that a selected module the adapter cannot build fails the request say what a default context would do. - The Spin state builder reads its settings from the config store, and its test says why the failure has to be the absence of a config store. - The test that keeps the internal header list in step with the Edge Cookie response headers, and the test for an unknown key in a module block, say what they guard. No code changes.
- The Cloudflare region test says why the region header has to reach the privacy outcome. - The Fastly geo module's re-export and the geo selector's default are described as they are. - The integration fixture's note on the geo selector no longer promises a setting that a later change does not add. No code changes.
- The integration fixture carried two notes on its geo selector that contradicted each other. The one that stays says that Viceroy maps the loopback client to US/CA, so the platform geo module resolves a real place. - That fixture, the Viceroy template, the template cache harness and the permission model guide no longer mention a default country setting, which no released version had. - Five code comments say what the code does without saying what it did before. No code changes.
Core builds the identity-graph key from the module's code and whatever `normalize_id_for_kv` returns, so the method decides what two visits must have in common to be treated as the same visitor. Its documentation described normalization only and advised a module with a case-sensitive identifier to return the value unchanged. That is right for a stable identifier and wrong for one that carries a signature, a nonce or a timestamp, because each reissue then gets a row of its own and the identity does not survive it. The trait documentation now says that the returned value is the identity two visits must share, and that a module whose identifier has a part reissued each time must return the stable part. It also says that core asks `accepts_id` about the returned string as well as about the identifier as issued, so a module must accept its own canonical form, or no row is read or written for it. `two_issues_of_one_identity_share_one_identity_graph_key` drives a fixture module whose identifier is a stable part and a part that changes on each issue through `AcceptedModules::canonical_kv_key`. Two identifiers that differ only in the reissued part reach one key, and a different stable part reaches another. The test fails for a module that returns the value unchanged. No behavior changes.
The Edge Cookie guide still said that an unknown country falls back to a configured default country, and that only the storage permission is resolved from a TCF signal. Neither is how the permission model works. - When no country is identified, or neither the region nor the country has a rule, the baseline on the top node of the rules tree applies. - Every permission is resolved from that baseline and the signals, each signal read by its own permission signal module in the configured order, with the Model Terms for Marketing preference among them. - A location lookup that fails resolves at the requires-signal floor. The setup guide describes the bare identifier form by what it is, a value with no module prefix, rather than by when it was issued.
…ords `KvNetwork` said a low cluster count "indicates an individual or household". A count of connections is a fact about a network, and reading a low one as an individual describes a device record as a record about a person. It now says a small network, such as a home connection. `KvEntry::ids` now says what it holds, which is each partner's identifier for the same browser on the same device, never an identifier for a person. Comments only, with no change in behavior.
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.
Stacks on the client-set Edge Cookie value path (#1046), and depends on #1043
to #1046 together, because this pull request documents what they build. The
stack has six pull requests (#1043, #1044, #1045, #1046, #1047, #1094), each
targeting
main, with this one fifth and the last of the five that decomposethe module work in #838, as requested in the #986 review.
Compare
split/4-client-resolvewithsplit/5-response-hook-docsto see only this pull request's change.
The design specs for the series are carried by the spec pull request (#1084),
including the two this pull request used to carry:
2026-07-30-provider-migration-rollout-design.md,the migration and rollout draft revised against the implemented series. Its
sign-off ledger is kept intact, with each row the series implements marked
with its pull request, so the task force can ratify rows rather than argue
them again.
2026-07-30-integration-response-header-hook-design.md,kept as the design bar for the response-header hook. The hook itself is not
in this series.
What this pull request does
IntegrationResponseMutatorresponse-header hook that an earlier versionof this pull request added is not in the series. The hook had no consumer,
and the spec set's own rule is against speculative surface. The hook returns
with the first integration that needs one, and its spec is the starting bar
for that design.
docs/guide/configuration.mdgains the
[ec] moduleselector with its[ec.hmac]and[ec.host_signals]tables, the[device]and[geo]selectors, theimplementationline, theassume_single_jurisdictionacknowledgment, therequires-signal floor on a failed geo lookup, the bare-origin rule for
resolve_allowed_origins, and a section on the country and region rules inpermissions.yaml. It also says what stops a deployment, being a table theselector does not name, a name this build does not have, a name that is
not a module name, and a setting the module does not know.
(
docs/guide/ec-setup-guide.md)is rewritten around modules and the permission model, including the narrow
withdrawal rules, the hardened resolve endpoint and the resolved-marker
cookie. The setup guide, API reference, error reference, Fastly guide and
key-rotation guide are updated to match.
trusted-server.example.tomladds a commented
[ec.host_signals]block and rewrites the[device]and[geo]comments to say what each default does and how to override it..github/workflows/test.ymlon
windows-latestas well asubuntu-latest, and the Axum job also runsthe core library's unit tests natively, because on the WebAssembly targets
the test harness stops at the first failing test and reports the rest as
never run.
crates/trusted-server-core/src/ec/mod.rschecks that a module receives the empty string as the client IP when the
host cannot determine one, and
crates/trusted-server-core/src/evidence.rscorrects its module documentation and moves the
HostSignalstrait to thetop of the file.
What happens to #838
Once the five pull requests that decompose #838 merge, #838 is closed. It stays
open as a draft reference for the review period only.
How it was verified
The head this pull request shows now is
3c7ef00e7, which merges #1046 ate7623de78and, through it,mainat 182fdf4 into1f31ba53e. The merge brings thets dev lint domainsandts dev install-hookscommands (#733) and a design document (#930), and changes none of this pull request's own code. No dependency of core or of an adapter changed version, and the one line of core the merge touches gains a comment. On3c7ef00e7these ran locally on Windows:cargo fmt --all --check, a locked dependency check (cargo tree --locked) and the core suite natively (3,140 tests).1f31ba53eadds to93af3ea2btwo merges of #1046, which bring comment and guide wording, the identity
graph's comments, one doc comment and one test, and one commit to two guide
pages. On
1f31ba53ethese ranlocally on Windows:
cargo fmt --all --check, Clippy with warnings denied onall four adapters and the five permission signal crates, the core suite
natively (3,140 tests), the five permission signal crates' own tests
(55 tests), and the docs lint, Prettier (inside
docs/and over the Markdownoutside it) and VitePress build. The wider set last ran locally on
9a3772871, the head of #1046 that the earlier merge brought, being the coresuite under Viceroy (3,377 across the test binaries the Fastly alias runs),
the Axum (123 tests, the five permission signal crates included), Cloudflare
(56) and Spin (90) adapter suites, the cross-adapter parity suite (17) and
the permissions inspector's wasm build, and CI runs all of them on this
head. The CLI tests, the browser integration tests and the Fastly Edge
Cookie lifecycle test run only in CI.
CI on
3c7ef00e7: every check passes, being Run Tests, Run Format, Integration Tests, Permissions Inspector and CodeQL Advanced, with the Fastly Edge Cookie lifecycle test in the integration run. There are 22 checks rather than the 20 the earlierpull requests run because this one puts the Axum test job and the Cloudflare
check job on a
[ubuntu-latest, windows-latest]matrix.References #777 and #778. Decomposes #838. Spec baseline from #986.