Skip to content

Make auction telemetry a module that [analytics] selects - #1357

Open
jwrosewell wants to merge 39 commits into
feature/15-diagnostic-headersfrom
feature/16-analytics
Open

jwrosewell wants to merge 39 commits into
feature/15-diagnostic-headersfrom
feature/16-analytics

Conversation

@jwrosewell

@jwrosewell jwrosewell commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Changes

Area Change
crates/trusted-server-core/src 9 files, 912 lines added, 112 removed
docs/guide 6 files, 87 lines added, 39 removed
crates/trusted-server-adapter-fastly/src 2 files, 65 lines added, 28 removed
crates/trusted-server-adapter-cloudflare/src 1 file, 36 lines added, 0 removed
crates/trusted-server-adapter-spin/src 1 file, 36 lines added, 0 removed
crates/trusted-server-adapter-axum/src 1 file, 34 lines added, 0 removed
trusted-server.example.toml 1 file, 11 lines added, 8 removed
tinybird/README.md 1 file, 6 lines added, 3 removed
CHANGELOG.md 1 file, 1 line added, 0 removed

Closes

Closes #1274

Spec

Spec: analytics-module

Test plan

CI on this pull request at 48e36bd passed all 20 checks.

  • cargo test-fastly && cargo test-axum
  • cargo clippy-fastly && cargo clippy-axum
  • cargo fmt --all -- --check
  • JS tests: cd crates/trusted-server-js/lib && npx vitest run
  • JS format: cd crates/trusted-server-js/lib && npm run format
  • Docs format: cd docs && npm run format
  • WASM build: cargo build --package trusted-server-adapter-fastly --release --target wasm32-wasip1
  • Manual testing via fastly compute serve
  • Other: the full workflow set of this repository, run on this pull request at 48e36bd

Checklist

  • Feature PRs: spec linked above, ending in a review log of two or more review rounds
  • Changes follow AGENTS.md conventions
  • No unwrap() in production code (clippy denies unwrap_used in CI)
  • Uses log macros as AGENTS.md asks (not println!)
  • New code has tests
  • No secrets or credentials committed

Stacks on headers that describe the deployment (#1356).

What changed after the 51Degrees PR review

Agreed at the review on 23 October, point 1. No configuration or permission policy is compiled into the WASM, and a WASM without a policy does not start. Since #1345 a loaded domain carries its permission policy and every services graph needs one, so the tests below give theirs the sample policy.

  • The analytics startup tests carry the sample policy.

Kept current

What this pull request does

# Before
[tinybird]
enabled = true
api_host = "api.example.com"
auction_token_secret = "tinybird_auction_append_token"

# After
[analytics]
module = "tinybird"

[analytics.tinybird]
api_host = "api.example.com"
auction_token_secret = "tinybird_auction_append_token"
  • Settings::analytics_module() returns the analytics module's settings only when the section selects it. The Fastly sink is built from that, so it has no enabled flag of its own, and settings that select nothing build the sink that sends nothing.
  • module takes tinybird or the same name in full, analytics.tinybird, as every other selector takes a short or a full name.
  • The secret reference is analytics.tinybird.auction_token_secret. The references of a table nothing selects are cleared before secrets are resolved, so what an operator sees is the refusal below and not a failed secret lookup.
  • An empty or absent token reference is refused as the settings load and again at deploy by the declared secret field, so the deploy validation's own duplicate check of it is removed, and both refusals are tested.
Configuration Refused with
[analytics] with no module "[analytics] selects no module", in the words every module type's section uses
module naming something else "[analytics] selects …, which no module this deployment supplies", and the analytics modules it supplies, in the registry's words
A key [analytics.tinybird] does not know, enabled included The key's name
[tinybird] Where the table went, and what replaces enabled

Why

After the seam (#1348) a vendor's module is selected by name. A section named for the job says which module does it, and the module's settings sit in a table at its name. Auction telemetry did not follow that. Its section was named after its vendor, and a flag inside the vendor's own table switched it on.

With this change a second place to send auction events is a second name [analytics] module can take, and nothing about the section has to change to add one.

What breaks

  • A configuration that carries [tinybird] is refused with directions.
  • An earlier version wrote the [tinybird] table into every stored configuration, whether telemetry was on or off. So a stored configuration from before this change is refused by a build with it, and the configuration is pushed again with this version's ts config push, which writes no such table. The removal of administration from the publisher's domain (Remove administration from the publisher's domain #1354) already needs the new configuration and the new build to go live together, so this adds no step to that upgrade.
  • A build from before this change does not know [analytics], so a configuration that selects the module cannot be rolled back to it unchanged.

How it was verified

Not exercised. No event has been sent to Tinybird. The sink's tests send to a recording stand-in, as before this change.

References #1274.

jwrosewell and others added 10 commits October 10, 2026 00:39
`[tinybird]` is now `[analytics.tinybird]`, and `[analytics] module =
"tinybird"` selects it, which replaces the table's `enabled` line. Every
other thing a deployment can switch on is selected that way, and telemetry
was the one section named after its vendor and switched on by a flag.

- `Settings::telemetry()` returns the module's settings only when the
  section selects it, and the Fastly sink is built from that, so the sink
  has no enabled flag of its own.
- A section that selects nothing, or a module this build does not have, is
  refused when the settings load, and the table refuses a key it does not
  know.
- The secret reference is `analytics.tinybird.auction_token_secret`. The
  references of a table nothing selects are cleared before secrets are
  resolved, so the refusal is what an operator sees and not a failed lookup.
- A configuration that still carries `[tinybird]` is refused with
  directions. An earlier version wrote the table into every stored
  configuration, on or off, so the message says to push the configuration
  again with this version.
- The messages about the table name it through one constant.
- An empty or absent token reference is refused as the settings load, where
  an empty one becomes absent as they normalize, and at deploy by the
  declared secret field, so the deploy validation's own check of the token
  is removed. Both refusals are tested.

The example configuration carries the new block, which a test uncomments and
loads.
The configuration guide's Tinybird section becomes the analytics section,
with the selector, what is refused and where `[tinybird]` went. The
telemetry guide, the configuration rules and the Tinybird project's README
name the settings by their new table, and the changelog records the change
as breaking.
# Conflicts:
#	docs/guide/configuration-rules.md
The constant, accessors, helpers and tests this pull request added say analytics where they said telemetry, and the Tinybird test helper no longer carries the removed enabled key. The two [analytics] refusals use the words of the generic section and the registry. Comments that restated a rule, described a crates folder that does not exist yet, or gave upgrade advice the changelog already gives are cut.
…ment

Every auction telemetry row describes the reader, with the complete
User-Agent, the country, the region and the page path, and rows went out
on every path the sink was on, including an auction the consent gate
skipped as consent_denied. The observation now records whether the
reader's permissions include MeasureAdPerformance, the Data Use TCF
Purpose 7 maps to, and build_auction_events returns no row without it.
Every path builds its rows there, so the /auction endpoint, the
navigation and page-bids paths and the abandoned and collected auctions
all check it. No new policy is needed, because the permission model
already resolves the permission from each place's rules and the reader's
signals.

New tests drive the /auction endpoint, a navigation and page-bids with a
reader the consent gate refuses and who has set no permission, and each
sends no row. A unit test shows the builder gives no row for a skipped or
a completed auction without the permission and a row with it alone, and
the /auction consent test now gives its reader that permission, so a
refused reader who permits measurement is still recorded. The test
contexts with the gate open also permit measurement, so the existing
telemetry tests keep their rows.
The Axum, Cloudflare and Spin adapters build no telemetry sink, so a
module [analytics] selects loaded, resolved its token and sent nothing,
with nothing logged. Each now calls warn_once_when_analytics_is_unsent as
it builds its state, which logs one warning naming the adapter when the
section selects a module, unless the services a caller supplied carry a
sink that sends. The adapter's own flag keeps it to one line however
often the state is built. The behaviour is otherwise unchanged.

Core tests capture the log on their own thread and show one warning
naming the adapter after two calls, and none when nothing is selected or
a supplied sink sends. Each adapter's test builds its state from settings
with the documented [analytics] block and shows startup logged. The
documented_block test helper gives a test that block on its own, so no
adapter names the module.
The changelog entry, the telemetry guide and the Tinybird project's README
say that a row is sent only for a reader whose permissions include
analytics.ad_reporting.measure_ad_performance, whatever the auction's
outcome, and the telemetry guide says that every row carries the reader's
complete User-Agent, country, region and page path. The changelog and the
guide say that the Axum, Cloudflare and Spin adapters log a warning at
startup when [analytics] selects a module.
@jwrosewell
jwrosewell removed this pull request from stack #1408 October 18, 2026 20:15
@jwrosewell
jwrosewell added this pull request to stack #1418 October 19, 2026 12:18
@jwrosewell
jwrosewell removed this pull request from stack #1418 October 19, 2026 16:30
@jwrosewell
jwrosewell added this pull request to stack #1421 October 19, 2026 17:38
@jwrosewell
jwrosewell removed this pull request from stack #1421 October 19, 2026 18:29
@jwrosewell
jwrosewell added this pull request to stack #1422 October 19, 2026 19:20
@jwrosewell
jwrosewell marked this pull request as ready for review October 19, 2026 20:23
@jwrosewell
jwrosewell removed this pull request from stack #1422 October 28, 2026 11:47
@jwrosewell
jwrosewell added this pull request to stack #1465 October 28, 2026 18:28

This branch has not been deployed

No deployments
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.

Auction telemetry is switched on by a flag inside its vendor's table, where every other job selects its module by name

2 participants