Skip to content

Let a device module score a person and describe the device - #1369

Open
jwrosewell wants to merge 52 commits into
feature/27-prefix-modulefrom
feature/28-device-signals
Open

jwrosewell wants to merge 52 commits into
feature/27-prefix-modulefrom
feature/28-device-signals

Conversation

@jwrosewell

@jwrosewell jwrosewell commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Changes

Area Change
crates/trusted-server-core/src 13 files, 1283 lines added, 8 removed
crates/trusted-server-adapter-axum/tests 1 file, 47 lines added, 0 removed
crates/testing/seam-probe 1 file, 27 lines added, 1 removed
crates/auction/prebid-server 1 file, 9 lines added, 0 removed
docs/guide 2 files, 8 lines added, 7 removed
CHANGELOG.md 1 file, 1 line added, 0 removed
crates/ad-server/mock 1 file, 1 line added, 0 removed

Closes

Closes #1286

Spec

Spec: device-signals

Test plan

CI on this pull request at 9cdbc96 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 9cdbc96

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 the prefix module (#1368).

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 device module test services carry the sample policy.

Kept current

What this pull request does

Two answers a device module may give beyond the coarse browser-or-bot signals the Edge Cookie gate uses.

DeviceSignals::human_confidence, one of High, Medium, Low and Unknown, is how confident the module is that a person is looking at the page. A module that scores the request writes it and every other module leaves it Unknown, which is kept apart from Low because a request alone cannot establish a person. Nothing reads it yet. It is written only on Fastly, the one adapter that calls detect, and to_kv_device drops it, so it is not stored in the identity graph. looks_like_browser gates the Edge Cookie as it did.

DeviceAttributes is what a bidder is told about the device, being the OpenRTB device type, make, model, operating system and version, and screen size, every field optional because every source is partial. The module gives the device type as OpenRTB's own code, so core holds no detection vendor's names. DeviceModule::advertising_attributes answers it, with a default of nothing so no existing module changes. attach_device_attributes asks the selected module after each auction request is built, at three points: the auction endpoint, the publisher's server-side path and handle_page_bids. The consent-denied path makes no outbound call and does not ask. The answer goes on the request's device object, which the OpenRTB request carries as devicetype, make, model, os, osv, w and h, each absent where nothing resolved it. A module that resolves nothing adds no device object.

The attributes are attached under no permission check, as ua and ip are and unlike EIDs.

The signals answer whether the request looks like a browser. The attributes answer what a bidder prices and are stored nowhere.

The seam probe's device module scores the request Medium and answers fixed attributes naming a tablet. The Axum adapter's test calls the module directly and drives no auction, so it proves the probe answers through the call core hands a device module, not that the answers reach a bid request through core. Core's own tests carry the attributes to the OpenRTB device object.

Why

A device module that reaches a detection service knows far more than whether the request looks like a browser, and a bidder prices inventory on the device it is told about. Without a place for that answer in core, a module's knowledge stops at the gate, and a deployment that wants its bid requests to carry the device has to patch core.

How it was verified

Not exercised. No module outside the stock build scores a person or resolves a device. The probe's module is the one answer, and the Axum test calls it directly and drives no auction.

References #1286.

jwrosewell and others added 17 commits October 11, 2026 20:31
`DeviceSignals` gains `human_confidence`, one of `High`, `Medium`, `Low`
and `Unknown`, which a device module that scores the request writes and
every other module leaves `Unknown`. `Unknown` is kept apart from `Low`
because a request alone cannot establish a person, so a first-time
visitor is not called a machine. Nothing in core gates on the answer;
`looks_like_browser` still gates the Edge Cookie as it did, and the
answer is there for a module that reads it.
`DeviceAttributes` carries what a bidder is told about the device, being
the OpenRTB device type, make, model, operating system and version, and
screen size, every field optional because every source is partial. It is
kept apart from `DeviceSignals`, which answer whether the request looks
like a browser and are stored in the identity graph, because these
answer what a bidder prices and are stored nowhere.

`DeviceModule::advertising_attributes` answers them from the module call
`detect` is handed, with a default of nothing so no existing module
changes. `attach_device_attributes` asks the selected module after each
auction request is built, on the auction endpoint and the publisher's
server-side path and not the consent-denied path, which makes no outbound
call. A module that resolves nothing adds no device object, because an
absent object and an empty one say the same to a bidder and the absent
one is smaller. The OpenRTB device object carries the attributes, each
absent where nothing resolved it, and `device_type_from_name` maps a
detection name to the OpenRTB code and maps nothing for a name it does
not know, because a wrong type is worse for a bidder than an absent one.
The probe's device module scores the request `Medium` and answers fixed
attributes, a tablet by name, so the Axum adapter proves from outside
core that the module `[device] module` resolves answers both through the
call it is handed, and that a tablet maps to OpenRTB device type 5.
The integration guide's table of what a module is handed gains the
device module's second answer, the attributes for the bid request, and
the configuration guide's device section says a module may score the
request and tell a bidder what the device is. The changelog records the
addition.
Core no longer maps one vendor's device type names, so a device module gives the OpenRTB code itself. The unused as_str and is_machine are deleted, the scoring narrative and the claim of one lookup are cut, and the reasoning for attaching device attributes is stated once. The configuration guide no longer contradicts itself about bid enrichment.
human_confidence is written where the device module runs, is not stored in
the identity graph with the other signals, and nothing reads it yet. The
guide, the changelog and the field's own doc say so, and the guide says
that the redirect middlewares acting on it follow this set of changes.
…nal/28-device-signals

# Conflicts:
#	docs/guide/configuration.md
@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.

A device module cannot say how sure it is that a person is there, nor what the device is

2 participants