Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
020dfbe
The device signals carry the confidence that a person is there
jwrosewell Sep 21, 2026
e20d190
A device module may tell the bidder what the device is
jwrosewell Sep 15, 2026
ec96c12
The seam probe's device module answers a confidence and the device
jwrosewell Oct 11, 2026
8e9a458
The guides describe what a device module may answer
jwrosewell Oct 11, 2026
732f665
Merge branch 'review/27-prefix-module' into final/28-device-signals
Jamesr51d Oct 12, 2026
30fee77
The device type a module gives is OpenRTB's own code
Jamesr51d Oct 12, 2026
48d4348
The score is written and read by nothing yet
jwrosewell Oct 12, 2026
b9678f9
Merge remote-tracking branch 'origin/split/27-prefix-module' into fin…
Jamesr51d Oct 12, 2026
83792f3
Merge remote-tracking branch 'origin/split/28-device-signals' into fi…
Jamesr51d Oct 12, 2026
0f46891
Merge 27-prefix-module into 28-device-signals
Jamesr51d Oct 12, 2026
a00f3f6
Merge 27-prefix-module into 28-device-signals
jwrosewell Oct 13, 2026
de5fc48
Merge 27-prefix-module into 28-device-signals
jwrosewell Oct 13, 2026
94b8145
Merge split/27-prefix-module into split/28-device-signals
Jamesr51d Oct 13, 2026
98b59df
Merge split/27-prefix-module into split/28-device-signals
Jamesr51d Oct 13, 2026
288d0f6
Merge 27-prefix-module into 28-device-signals
jwrosewell Oct 13, 2026
45e4a8f
Merge 27-prefix-module into 28-device-signals
jwrosewell Oct 13, 2026
b680d07
Merge 27-prefix-module into 28-device-signals
jwrosewell Oct 13, 2026
77c717d
Merge split/27-prefix-module into split/28-device-signals
jwrosewell Oct 16, 2026
379b6cf
Merge split/27-prefix-module into split/28-device-signals
jwrosewell Oct 16, 2026
c15bb89
Merge split/27-prefix-module into split/28-device-signals
jwrosewell Oct 16, 2026
2fcd447
Merge split/27-prefix-module into split/28-device-signals
jwrosewell Oct 16, 2026
61653cb
The device module's attributes call carries the request's permissions
jwrosewell Oct 16, 2026
ccaab61
A backend the device module registers waits no longer than the auction
jwrosewell Oct 16, 2026
72ad78b
Core drops device values a bidder cannot read
jwrosewell Oct 16, 2026
42ee324
A request the consent gate denies never reaches the device module
jwrosewell Oct 16, 2026
4badbda
Two device comments say only what the code does
jwrosewell Oct 16, 2026
ba49d9d
Merge split/27-prefix-module into split/28-device-signals
jwrosewell Oct 16, 2026
86ad459
Merge split/27-prefix-module into split/28-device-signals
jwrosewell Oct 16, 2026
b8a31b6
Merge the review fixes into split/28-device-signals
jwrosewell Oct 16, 2026
96d0471
Merge split/27-prefix-module into split/28-device-signals
jwrosewell Oct 16, 2026
b74f39d
Merge split/27-prefix-module into split/28-device-signals
jwrosewell Oct 16, 2026
8d90ac3
Merge split/27-prefix-module into split/28-device-signals
jwrosewell Oct 16, 2026
87a6b4c
Merge split/27-prefix-module into split/28-device-signals
jwrosewell Oct 16, 2026
b439eb9
Merge split/27-prefix-module into split/28-device-signals
jwrosewell Oct 16, 2026
bc2ac64
Merge split/27-prefix-module into split/28-device-signals
jwrosewell Oct 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- A device module may answer the confidence that a person is looking at the page, `DeviceSignals::human_confidence`, which nothing reads yet, which is not stored in the identity graph with the other signals, and which stays `Unknown` for every module that does not score the request, and may tell a bidder what the device is through `DeviceModule::advertising_attributes`, which reaches the bid request's device object as its type, make, model, operating system and screen. That call carries the permissions resolved for the request, a backend the module registers during it waits no longer than the auction's timeout, and core drops a device type outside the AdCOM 1.0 device types list, a screen size that is not positive and a blank string. A module that resolves nothing adds no device object.
- A prefix module, selected by `[prefix] module`, gives the start of every name the browser holds for a publisher and of every key the publisher's entries take in a shared store. Core's own module gives `ts` and `ts.<publisher.domain>` and runs when nothing is selected, so a default deployment's names are unchanged. The page's bundle tag tells the client script the public prefix as `data-ts-public-prefix`, and a template stored under one prefix is never served under another.
- `[shared.<vendor>]`, one table per vendor holding what every module of that vendor shares, read by the vendor's crates through `Settings::shared_config` and by nothing in core. A builder marked with `IntegrationBuilder::with_shared_settings` runs when its vendor's table is present and is selected by no section, a table no builder reads is refused when the registry is built, a table whose name is not lower case letters, digits, `_` or `-` is refused when the settings load, a refusal of a module nothing supplies names the absent table, and a secret in the table is declared, resolved and checked at push to name a key as a module's own are. A shared table, or a module's own, that cannot be read or fails its validation is refused naming the setting at fault and never its value.
- `robots_txt`, core's own middleware for the file `[robots-txt]` describes, named in a `[[fetch]]` entry for `text/plain` on `/robots.txt`. Its preparation makes the file from the selected modules through their holding, and its action replaces the origin's file with it. The file is made in place of any other answer the origin gives to a `GET` or `HEAD`, such as a `404`, a page, an error or a redirect. The origin is asked for its whole file, without a crawler's conditional or range headers, and the middleware works on `/robots.txt` alone. A module that cannot answer with nothing held refuses the request with `503` and `Retry-After` before any document is processed. A section with no such entry, or with an entry naming the middleware on another path or media type, is refused when the settings load.
Expand Down
1 change: 1 addition & 0 deletions crates/ad-server/mock/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,7 @@ mod tests {
user_agent: Some("Mozilla/5.0".to_string()),
ip: Some("192.168.1.1".to_string()),
geo: None,
attributes: None,
}),
site: None,
context: HashMap::new(),
Expand Down
9 changes: 9 additions & 0 deletions crates/auction/prebid-server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3181,6 +3181,7 @@ assume_single_jurisdiction = true
user_agent: Some("test-agent".to_string()),
ip: Some("203.0.113.42".to_string()),
geo: None,
attributes: None,
});
let settings = make_settings();
let request = build_test_request();
Expand Down Expand Up @@ -3361,6 +3362,7 @@ assume_single_jurisdiction = true
region: None,
asn: None,
}),
attributes: None,
});

let settings = make_settings();
Expand Down Expand Up @@ -3455,6 +3457,7 @@ assume_single_jurisdiction = true
region: Some("NY".to_string()),
asn: None,
}),
attributes: None,
});

let settings = make_settings();
Expand Down Expand Up @@ -3494,6 +3497,7 @@ assume_single_jurisdiction = true
region: Some("NY".to_string()),
asn: None,
}),
attributes: None,
});

let settings = make_settings();
Expand Down Expand Up @@ -3791,6 +3795,7 @@ assume_single_jurisdiction = true
user_agent: Some("TestAgent".to_string()),
ip: None,
geo: None,
attributes: None,
});

let settings = make_settings();
Expand Down Expand Up @@ -3819,6 +3824,7 @@ assume_single_jurisdiction = true
user_agent: Some("TestAgent".to_string()),
ip: None,
geo: None,
attributes: None,
});

let settings = make_settings();
Expand Down Expand Up @@ -3852,6 +3858,7 @@ assume_single_jurisdiction = true
user_agent: Some("TestAgent".to_string()),
ip: None,
geo: None,
attributes: None,
});

let settings = make_settings();
Expand Down Expand Up @@ -3928,6 +3935,7 @@ assume_single_jurisdiction = true
region: Some("NY".to_string()),
asn: None,
}),
attributes: None,
});

let settings = make_settings();
Expand Down Expand Up @@ -4712,6 +4720,7 @@ assume_single_jurisdiction = true
user_agent: Some("test-agent".to_string()),
ip: None,
geo: None,
attributes: None,
}),
site: None,
context: HashMap::new(),
Expand Down
28 changes: 27 additions & 1 deletion crates/testing/seam-probe/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ use trusted_server_core::auction::demand::{
};
use trusted_server_core::auction::provider::{AuctionProvider, ProviderRequestOutcome};
use trusted_server_core::auction::types::{AuctionContext, AuctionRequest, AuctionResponse};
use trusted_server_core::ec::device::{DeviceModule, DeviceSignals};
use trusted_server_core::ec::device::{
DeviceAttributes, DeviceModule, DeviceSignals, HumanConfidence,
};
use trusted_server_core::ec::module::{EdgeCookieModule, GeneratedEdgeCookie, ModuleCode};
use trusted_server_core::error::TrustedServerError;
use trusted_server_core::evidence::RequestInfo;
Expand Down Expand Up @@ -393,6 +395,30 @@ impl DeviceModule for SeamProbeDevice {
looks_like_browser: true,
ja4_class: None,
h2_fp_hash: None,
// A scoring module's answer, which core's own modules never give.
human_confidence: HumanConfidence::Medium,
}
}

async fn advertising_attributes(&self, _call: ModuleCall<'_>) -> Option<DeviceAttributes> {
Some(SeamProbeDevice::attributes())
}
}

impl SeamProbeDevice {
/// What the probe tells a bidder about the device, fixed so a test can
/// read it back.
#[must_use]
pub fn attributes() -> DeviceAttributes {
DeviceAttributes {
// A tablet, as the module gives `OpenRTB`'s own code.
device_type: Some(5),
make: Some("Probe".to_owned()),
model: Some("P-1".to_owned()),
os: Some("ProbeOS".to_owned()),
os_version: Some("1.0".to_owned()),
screen_width: Some(800),
screen_height: Some(1280),
}
}
}
Expand Down
47 changes: 47 additions & 0 deletions crates/trusted-server-adapter-axum/tests/seam_probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1023,6 +1023,53 @@ async fn a_requests_services_carry_the_selected_modules_prefixes() {
);
}

/// The device module `[device] module` resolves answers the confidence that
/// a person is there and what a bidder is told about the device, through the
/// call it is handed, so both reach core from a module it does not know.
#[tokio::test]
async fn the_resolved_device_module_answers_a_confidence_and_the_devices_attributes() {
let settings = settings_selecting_module(
r#"
[device]
module = "testing.seam-probe"
"#,
);
let registry = registry_with_probe(&settings);
let module = registry
.device_module()
.expect("`[device] module = \"testing.seam-probe\"` should resolve the module's module");
let request = ModuleRequest::new(
&axum::http::Method::GET,
"www.publisher.example",
"https",
"/",
);
let context = ModuleContext::new(request).with_settings(&settings);

let signals = module
.detect(context.call(module.id(), module.required_permissions()))
.await;
let attributes = module
.advertising_attributes(context.call(module.id(), module.required_permissions()))
.await;

assert_eq!(
signals.human_confidence,
trusted_server_core::ec::device::HumanConfidence::Medium,
"the scoring module's answer should reach core unchanged"
);
assert_eq!(
attributes,
Some(seam_probe::SeamProbeDevice::attributes()),
"the attributes the module resolved should reach core unchanged"
);
assert_eq!(
attributes.and_then(|found| found.device_type),
Some(5),
"a tablet is OpenRTB device type 5"
);
}

/// `[ec] module` naming a module resolves a module that, when driven
/// through `generate`, produces an identifier the built-in HMAC module can
/// never make.
Expand Down
Loading
Loading