diff --git a/CLAUDE.md b/CLAUDE.md index e2c27a673..546a3bf52 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,8 +6,9 @@ ## Project Overview Rust-based edge computing application targeting **Fastly Compute**. Handles -privacy-preserving Edge Cookie (EC) ID generation, ad serving with GDPR compliance, -real-time bidding integration, and publisher-side JavaScript injection. +Edge Cookie (EC) ID generation, ad serving with consent signal extraction +and enforcement, real-time bidding integration, and publisher-side +JavaScript injection. ## Workspace Layout diff --git a/crates/trusted-server-adapter-fastly/src/main.rs b/crates/trusted-server-adapter-fastly/src/main.rs index 8fec21435..39d35b198 100644 --- a/crates/trusted-server-adapter-fastly/src/main.rs +++ b/crates/trusted-server-adapter-fastly/src/main.rs @@ -611,7 +611,7 @@ mod tests { ); assert!( body.contains("h2_fp: unavailable"), - "should include H2 fingerprint fallback" + "should include H2 probabilistic identifier fallback" ); assert!( body.contains("cipher: unavailable"), diff --git a/crates/trusted-server-core/src/auction/types.rs b/crates/trusted-server-core/src/auction/types.rs index 14c7713f8..f34110daa 100644 --- a/crates/trusted-server-core/src/auction/types.rs +++ b/crates/trusted-server-core/src/auction/types.rs @@ -19,7 +19,7 @@ pub struct AuctionRequest { pub slots: Vec, /// Publisher information pub publisher: PublisherInfo, - /// User information (privacy-preserving) + /// User information (consent-aware) pub user: UserInfo, /// Device information pub device: Option, @@ -74,7 +74,7 @@ pub struct PublisherInfo { pub page_url: Option, } -/// Privacy-preserving user information. +/// Consent-aware user information. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct UserInfo { /// Stable EC ID (from cookie or freshly generated). diff --git a/crates/trusted-server-core/src/consent_config.rs b/crates/trusted-server-core/src/consent_config.rs index 3b5ff2570..465629a51 100644 --- a/crates/trusted-server-core/src/consent_config.rs +++ b/crates/trusted-server-core/src/consent_config.rs @@ -174,7 +174,7 @@ impl ConsentForwardingMode { /// The `applies_in` list is used for **observability and logging only** — it /// does NOT cause consent to be synthesized. When a user's country appears in /// this list, the system logs that GDPR applies, enabling publishers to -/// monitor compliance coverage. +/// monitor jurisdiction coverage. #[derive(Debug, Clone, Deserialize, Serialize)] #[serde(deny_unknown_fields)] pub struct GdprConfig { @@ -283,11 +283,11 @@ impl Default for ConflictResolutionConfig { #[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)] #[serde(rename_all = "lowercase")] pub enum ConflictMode { - /// Deny consent when signals disagree (most privacy-safe). + /// Deny consent when signals disagree (most restrictive). Restrictive, /// Use the newer signal based on timestamps. Newest, - /// Grant consent when signals disagree (requires legal review). + /// Grant consent when signals disagree (least restrictive). Permissive, } diff --git a/crates/trusted-server-core/src/constants.rs b/crates/trusted-server-core/src/constants.rs index ffcf4f034..acdd58af6 100644 --- a/crates/trusted-server-core/src/constants.rs +++ b/crates/trusted-server-core/src/constants.rs @@ -46,7 +46,7 @@ pub const HEADER_REFERER: HeaderName = HeaderName::from_static("referer"); /// /// These headers are used internally by Trusted Server for identity, geo-enrichment, /// debugging, and compression hints. Leaking them to external origins could expose -/// user tracking data and internal implementation details. +/// user identity, geo data, and internal implementation details. /// /// Uses `&str` slices because `HeaderName` has interior mutability and cannot appear /// in `const` context. diff --git a/crates/trusted-server-core/src/ec/device.rs b/crates/trusted-server-core/src/ec/device.rs index 7e540bcef..fbefa9586 100644 --- a/crates/trusted-server-core/src/ec/device.rs +++ b/crates/trusted-server-core/src/ec/device.rs @@ -33,7 +33,7 @@ pub struct DeviceSignals { /// Coarse OS family: `"mac"`, `"windows"`, `"ios"`, `"android"`, /// `"linux"`. pub platform_class: Option, - /// SHA256 prefix (12 hex chars) of raw H2 SETTINGS fingerprint. + /// SHA256 prefix (12 hex chars) of the raw H2 SETTINGS string. pub h2_fp_hash: Option, /// `true` = known browser, `false` = known bot, `None` = unknown. pub known_browser: Option, @@ -65,8 +65,8 @@ impl DeviceSignals { /// Returns `true` when the request looks like a real browser. /// /// Checks for the presence of recognizable signals rather than matching - /// against a hardcoded fingerprint allowlist. Real browsers always - /// produce a valid TLS fingerprint (`ja4_class`) and a recognizable UA + /// against a hardcoded signal allowlist. Real browsers always + /// produce a valid TLS probabilistic identifier (`ja4_class`) and a recognizable UA /// platform string (`platform_class`). Raw HTTP clients (curl, Python /// requests, Go net/http, headless scrapers) typically lack one or both. /// @@ -146,11 +146,11 @@ fn parse_platform_class(ua: &str) -> Option { None } -/// Extracts Section 1 from a full JA4 fingerprint. +/// Extracts Section 1 from a full JA4 string. /// /// JA4 format: `section1_section2_section3` separated by underscores. /// Section 1 identifies browser family (cipher count, extension count, -/// ALPN) without uniquely fingerprinting a device. +/// ALPN) without uniquely identifying a device. /// /// Returns `None` if the input is empty or has no underscore-delimited /// section. @@ -164,7 +164,7 @@ fn extract_ja4_section1(full_ja4: &str) -> Option { } /// Computes a 12-hex-char prefix of the SHA256 hash of the raw H2 -/// SETTINGS fingerprint string. +/// SETTINGS string. /// /// The raw string looks like `"1:65536;2:0;4:6291456;6:262144"`. #[must_use] @@ -175,7 +175,7 @@ fn compute_h2_fp_hash(raw_h2_fp: &str) -> String { hex::encode(&digest[..6]) } -/// Known browser fingerprint allowlist. +/// Known browser signal allowlist. /// /// Each entry is `(ja4_class, h2_fp_prefix, known_browser)`. /// `h2_fp_prefix` is the raw H2 SETTINGS string (not the hash) — we @@ -191,7 +191,7 @@ const KNOWN_BROWSERS: &[(&str, &str, bool)] = &[ ("t13d1717h2", "1:65536;2:0;4:131072;5:16384", true), ]; -/// Returns H2 fingerprint hashes for the known browser allowlist. +/// Returns H2 SETTINGS hashes for the known browser allowlist. /// /// Computed once on first call and cached via `OnceLock`. fn known_browser_h2_hashes() -> &'static Vec<(&'static str, String, bool)> { @@ -366,7 +366,7 @@ mod tests { assert_eq!( evaluate_known_browser(Some(ja4), Some(&h2_hash)), Some(true), - "Chrome fingerprint should be recognized" + "Chrome signals should be recognized" ); } @@ -377,7 +377,7 @@ mod tests { assert_eq!( evaluate_known_browser(Some(ja4), Some(&h2_hash)), Some(true), - "Safari fingerprint should be recognized" + "Safari signals should be recognized" ); } @@ -388,7 +388,7 @@ mod tests { assert_eq!( evaluate_known_browser(Some(ja4), Some(&h2_hash)), Some(true), - "Firefox fingerprint should be recognized" + "Firefox signals should be recognized" ); } @@ -538,7 +538,7 @@ mod tests { ); assert!( signals.looks_like_browser(), - "unknown fingerprint with valid JA4 + platform should pass" + "unknown signal combination with valid JA4 + platform should pass" ); assert_eq!(signals.known_browser, None, "should not match allowlist"); } @@ -554,7 +554,7 @@ mod tests { #[test] fn looks_like_browser_rejects_missing_ja4() { - // Real UA but no TLS fingerprint (e.g. HTTP/1.1 or missing SDK support) + // Real UA but no JA4 value (e.g. HTTP/1.1 or missing SDK support) let signals = DeviceSignals::derive(CHROME_MAC_UA, None, Some("1:65536")); assert!( !signals.looks_like_browser(), diff --git a/crates/trusted-server-core/src/ec/generation.rs b/crates/trusted-server-core/src/ec/generation.rs index af40c3ff4..2924b7692 100644 --- a/crates/trusted-server-core/src/ec/generation.rs +++ b/crates/trusted-server-core/src/ec/generation.rs @@ -1,7 +1,7 @@ //! Edge Cookie (EC) ID generation using HMAC. //! -//! This module provides functionality for generating privacy-preserving EC IDs -//! based on the client IP address and a secret key. +//! This module generates EC IDs from the client IP address and a configured +//! secret key. use std::net::IpAddr; @@ -65,9 +65,9 @@ fn generate_random_suffix(length: usize) -> String { /// Generates a fresh EC ID from a pre-captured client IP string. /// /// Uses only the client IP (not user-agent or other headers) intentionally: -/// EC IDs are meant to be simple, privacy-preserving identifiers — not -/// high-entropy fingerprints. The random suffix provides per-cookie -/// uniqueness for users behind the same NAT/proxy. +/// EC IDs are deterministic identifiers (HMAC base plus random suffix), +/// not high-entropy probabilistic identifiers. The random suffix provides +/// per-cookie uniqueness for users behind the same NAT or proxy. /// /// Creates an HMAC-SHA256-based ID using the configured secret key and /// the client IP address, then appends a random suffix for additional diff --git a/crates/trusted-server-core/src/ec/kv_types.rs b/crates/trusted-server-core/src/ec/kv_types.rs index 630c9e77d..589f280c1 100644 --- a/crates/trusted-server-core/src/ec/kv_types.rs +++ b/crates/trusted-server-core/src/ec/kv_types.rs @@ -51,7 +51,7 @@ pub struct KvEntry { /// Creation-time publisher property metadata. #[serde(default, skip_serializing_if = "Option::is_none")] pub pub_properties: Option, - /// Device class signals (TLS fingerprint, UA platform). + /// Device class signals (TLS handshake, UA platform). /// Written once on creation — never updated after. #[serde(default, skip_serializing_if = "Option::is_none")] pub device: Option, @@ -92,7 +92,7 @@ pub struct KvGeo { #[serde(default, skip_serializing_if = "Option::is_none")] pub asn: Option, /// DMA/metro code (e.g. `807` = San Francisco). - /// Market-level targeting signal; not personal data. + /// Market-level targeting signal. #[serde(default, skip_serializing_if = "Option::is_none")] pub dma: Option, } @@ -143,7 +143,7 @@ where } } -/// Coarse, non-PII device signals derived from TLS handshake and UA. +/// Coarse device signals derived from TLS handshake and UA. /// /// Used by the `/_ts/api/v1/identify` endpoint for cross-suffix propagation decisions /// and buyer-facing device quality scoring. Written once on @@ -151,7 +151,7 @@ where /// /// **Privacy:** `ja4_class` (Section 1 only) and `platform_class` are /// category signals, not unique device identifiers. The full JA4 -/// fingerprint (Sections 2–3) is never stored. +/// string (Sections 2–3) is never stored. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct KvDevice { /// Mobile signal: `0` = confirmed desktop, `1` = confirmed mobile, @@ -160,14 +160,14 @@ pub struct KvDevice { pub is_mobile: u8, /// JA4 Section 1 only — browser family class identifier. /// e.g. `"t13d1516h2"` = Chrome, `"t13d2013h2"` = Safari. - /// Never stores the full JA4 fingerprint. + /// Never stores the full JA4 string. #[serde(default, skip_serializing_if = "Option::is_none")] pub ja4_class: Option, /// Coarse OS family from UA: `"mac"`, `"windows"`, `"ios"`, /// `"android"`, `"linux"`. #[serde(default, skip_serializing_if = "Option::is_none")] pub platform_class: Option, - /// SHA256 prefix (12 hex chars) of the HTTP/2 SETTINGS fingerprint. + /// SHA256 prefix (12 hex chars) of the HTTP/2 SETTINGS string. /// Used alongside `ja4_class` for browser confirmation and bot detection. #[serde(default, skip_serializing_if = "Option::is_none")] pub h2_fp_hash: Option, diff --git a/crates/trusted-server-core/src/ec/mod.rs b/crates/trusted-server-core/src/ec/mod.rs index 408ea9b32..d17fcd519 100644 --- a/crates/trusted-server-core/src/ec/mod.rs +++ b/crates/trusted-server-core/src/ec/mod.rs @@ -20,7 +20,7 @@ //! - [`kv`] — KV Store identity graph operations (CAS, tombstones, debounce) //! - [`kv_backend`] — Platform-neutral KV primitives implemented by adapters //! - [`kv_types`] — Schema types for KV identity graph entries -//! - [`device`] — Device signal derivation (UA, JA4, H2 fingerprinting) +//! - [`device`]: Device signal derivation (UA, JA4, H2 SETTINGS) //! - [`partner`] — Partner validation helpers (ID format, pull sync config) //! - [`registry`] — In-memory partner registry built from config //! - [`rate_limiter`] — Rate limiting abstraction (implemented by adapters) diff --git a/crates/trusted-server-core/src/integrations/aps.rs b/crates/trusted-server-core/src/integrations/aps.rs index 60c22265d..7971fb1d7 100644 --- a/crates/trusted-server-core/src/integrations/aps.rs +++ b/crates/trusted-server-core/src/integrations/aps.rs @@ -107,7 +107,7 @@ struct ApsContextual { #[serde(default)] slots: Vec, - /// Event tracking host + /// Event collection endpoint #[serde(skip_serializing_if = "Option::is_none")] host: Option, @@ -119,7 +119,7 @@ struct ApsContextual { #[serde(skip_serializing_if = "Option::is_none")] cfe: Option, - /// Event tracking enabled + /// Event collection enabled #[serde(skip_serializing_if = "Option::is_none")] ev: Option, @@ -131,7 +131,7 @@ struct ApsContextual { #[serde(skip_serializing_if = "Option::is_none")] cb: Option, - /// Campaign tracking URL + /// Campaign attribution URL #[serde(skip_serializing_if = "Option::is_none")] cmp: Option, } diff --git a/crates/trusted-server-core/src/integrations/google_tag_manager.rs b/crates/trusted-server-core/src/integrations/google_tag_manager.rs index ff53e05f3..f9ed00d7c 100644 --- a/crates/trusted-server-core/src/integrations/google_tag_manager.rs +++ b/crates/trusted-server-core/src/integrations/google_tag_manager.rs @@ -1,7 +1,7 @@ //! Google Tag Manager integration for first-party tag delivery. //! //! Proxies GTM scripts and Google Analytics beacons through the publisher's -//! domain, improving tracking accuracy and ad-blocker resistance. +//! domain, improving measurement accuracy and ad-blocker resistance. //! //! # Endpoints //! diff --git a/crates/trusted-server-core/src/integrations/gpt.rs b/crates/trusted-server-core/src/integrations/gpt.rs index 4def0fe24..f142a9317 100644 --- a/crates/trusted-server-core/src/integrations/gpt.rs +++ b/crates/trusted-server-core/src/integrations/gpt.rs @@ -1,8 +1,8 @@ //! Google Publisher Tags (GPT) integration for first-party ad serving. //! //! This module provides transparent proxying for Google's entire GPT script -//! chain, enabling first-party ad tag delivery while maintaining privacy -//! controls. GPT loads scripts in a cascade: +//! chain, enabling first-party ad tag delivery. +//! GPT loads scripts in a cascade: //! //! 1. `gpt.js` – the thin bootstrap loader //! 2. `pubads_impl.js` – the main GPT implementation (~640 KB) diff --git a/crates/trusted-server-core/src/integrations/lockr.rs b/crates/trusted-server-core/src/integrations/lockr.rs index 2a6da600b..1f4f04b73 100644 --- a/crates/trusted-server-core/src/integrations/lockr.rs +++ b/crates/trusted-server-core/src/integrations/lockr.rs @@ -1,7 +1,7 @@ //! Lockr integration for identity resolution and advertising tokens. //! //! This module provides transparent proxying for Lockr's SDK and API, -//! enabling first-party identity resolution while maintaining privacy controls. +//! enabling first-party identity resolution. //! //! Lockr provides a dedicated trust-server SDK (`identity-lockr-trust-server.js`) //! that is pre-configured to route API calls through the first-party proxy, diff --git a/crates/trusted-server-core/src/integrations/permutive.rs b/crates/trusted-server-core/src/integrations/permutive.rs index 9667ff7b2..aa684c620 100644 --- a/crates/trusted-server-core/src/integrations/permutive.rs +++ b/crates/trusted-server-core/src/integrations/permutive.rs @@ -1,7 +1,7 @@ //! Permutive integration for first-party data collection and audience management. //! //! This module provides transparent proxying for Permutive's API and SDK, -//! enabling first-party data collection while maintaining privacy controls. +//! enabling first-party data collection. use std::sync::Arc; diff --git a/crates/trusted-server-core/src/proxy.rs b/crates/trusted-server-core/src/proxy.rs index 38c90c350..ea04d7578 100644 --- a/crates/trusted-server-core/src/proxy.rs +++ b/crates/trusted-server-core/src/proxy.rs @@ -552,7 +552,7 @@ fn origin_response_metadata( /// Apply image content-type header and log pixel heuristics. /// /// Sets a generic `image/*` content-type when the response has none, then logs -/// a warning if size or path heuristics suggest a tracking pixel. Both call +/// a warning if size or path heuristics suggest a pixel image. Both call /// sites pass the response through unchanged afterwards, so this returns /// nothing. fn apply_image_passthrough_metadata( diff --git a/crates/trusted-server-core/src/settings.rs b/crates/trusted-server-core/src/settings.rs index e193a70aa..a62aa3792 100644 --- a/crates/trusted-server-core/src/settings.rs +++ b/crates/trusted-server-core/src/settings.rs @@ -1870,10 +1870,10 @@ fn validate_tinybird_secret(value: &str, setting: &str) -> Result<(), Report