Proposal: TMP as a Peer to RTD in Prebid
Problem
Prebid has 61 RTD (Real-Time Data) modules. Each one follows the same pattern: fetch external data, enrich bid requests and/or set ad server targeting. Each vendor ships its own module with its own API format, configuration, and maintenance burden.What RTD modules actually do
RTD modules hook into the auction viagetBidRequestData (modify bid requests
to bidders) and getTargetingData (set ad server key-values). They fall into
five categories:
What they send
Most RTD modules send some combination of: full page URL, referrer, viewport dimensions, ad unit structure (bidders, sizes, params), existing user eIDs, and consent strings. Many send the complete OpenRTB BidRequest (2-10KB). The vendor API returns data that gets injected into:ortb2.user.data— user segments sent to all biddersortb2.site.ext— site-level extensionsortb2Imp.ext— per-impression extensions- Per-bidder
ortb2Fragments— bidder-specific targeting - Ad server targeting — GAM key-values
The problems
- 61 modules, same pattern. Each is a separate integration with its own configuration, testing, and Prebid PR cycle. New vendors require new modules.
- Large payloads. Sending the full BidRequest (2-10KB) when most modules only need page context and ad unit codes.
- No privacy separation. User identity and page context travel in the same request. Privacy depends on field-level masking — one missed field leaks data.
- No standard protocol. Vendors define their own request/response formats. Switching vendors means rewriting the integration.
What can migrate to TMP
Of the 61 modules, approximately 38 (contextual + audience + brand safety + bid enrichment) follow the “fetch data, enrich request” pattern that TMP standardizes. Those integrations are migration candidates; introducing TMP does not remove or subsume the RTD framework. Specifically:
Modules that TMP does not replace: real-time security monitoring (Confiant,
Human Security), client-side device detection (51Degrees, WURFL), auction
timeout control, and proprietary client-side engines that require their own
JavaScript runtime.
Proposal: target architecture
Add TMP (Trusted Match Protocol) as an optional Prebid.js module alongside the existing RTD module, and as a module in Prebid Server. In Prebid.js, TMP owns its protocol-specific lifecycle, privacy separation, temporal decorrelation, and local join. It does not register as an RTD data provider and does not change RTD’s provider contract. Publishers include the TMP module in their build and configure it withpbjs.setConfig(). Prebid Server operators
configure the corresponding module via YAML.
This peer architecture preserves a clean boundary:
- RTD remains RTD. Existing
realTimeData.dataProvidersconfiguration and RTD submodules continue to work unchanged. - TMP remains TMP. Top-level
tmpconfiguration is consumed only by the TMP module, whose two-request flow cannot be represented safely as a single RTD provider callback. - Publishers can run both. A publisher can compare or migrate an existing RTD integration without changing auction-wide RTD behavior.
realTimeData.dataProviders; it MUST
preserve TMP’s separate Context Match and Identity Match requests and local
join. Once the peer module is available, publishers move the same protocol flow
to top-level tmp configuration. The wire protocol and router do not change.
TMP is an open protocol (part of AdCP) that standardizes what RTD modules do
today. It defines two operations:
- Context Match: page context in, offers + targeting signals out. No user data.
- Identity Match: opaque user token in, per-package eligibility out. No page data.
What changes in Prebid.js
Prebid.js gains an optional TMP module at the same architectural level as the RTD module. A distribution must include that module (for example, through the publisher’s normal custom-build module list) before thetmp configuration is
used. TMP may use the same auction lifecycle hook points as RTD, but it does
not call RTD’s submodule registration API or appear in
realTimeData.dataProviders.
Present-day compatibility profile
Before that peer module lands, an implementation MAY expose TMP as an RTD submodule. This is the near-term integration described in the current web surface guide. It is intentionally a concession to the existing Prebid.js extension surface, not the final ownership boundary. Implementers must not infer from the RTD registration that TMP collapses its context and identity paths into a conventional single vendor-enrichment request. The compatibility form is:Configuration
Per-ad-unit configuration
What Prebid.js does internally
- On auction init: For each ad unit with
tmpconfig, resolve the per-impression identifier (tmp_impression_id) in priority order — (1) publisher-supplied value viaadUnit.tmp.impressionId, (2) reuse ofadUnit.transactionIdwhenenableTIDsis true, or (3) a fresh decision-layer mint (ULID, UUID, or any collision-resistant scheme). CallbuildContextMatchRequest()from@adcp/client/tmpand send to the router. - On context match response: Store offers and signals per ad unit.
- After temporal delay (randomized): Call
buildIdentityMatchRequest()with the user’s token (from existing identity module) and ALL active package IDs. Send to the router. - On identity match response: Call
joinResults()to intersect offers with eligibility. CalltoTargetingKVs()to flatten to key-values. - Set targeting: Apply key-values to the ad unit before bid requests go out,
including
tmp_impression_idso the buyer’s creative tracking URL can substitute it via%%PATTERN:tmp_impression_id%%. GAM line items match onadcp_pkgandadcp_seg. Signals also flow to bidders viaortb2.site.extandortb2.user.data— the same injection points RTD modules use today. Bidders see enriched bid requests without needing to know TMP exists.
enableTIDs reuse optimization, and the GAM creative URL pattern.
The @adcp/client/tmp package handles steps 1, 3, and 4 as pure functions. In
the target architecture, the Prebid.js TMP module handles the HTTP calls,
timing, and ad unit targeting, and RTD is neither a dependency nor an
intermediary. In the bridge profile, the TMP RTD submodule temporarily owns
those responsibilities while preserving the same protocol behavior.
Dependency: @adcp/client/tmp
- Zero dependencies, under 3KB gzipped
- Tree-shakeable — only the functions Prebid uses get bundled
- Types + pure functions — no network calls, no side effects
- Prebid already supports npm dependencies for optional modules
- Ed25519 request signing is handled by
@adcp/client/tmpwhen a signing key is configured (see Request signing below for the requirements that apply to both Prebid.js and PBS)
What changes in Prebid Server
YAML configuration
What Prebid Server does internally
Usesadcp-go/tmp/client for:
- Fan-out to configured providers in parallel over HTTP/2
- Per-provider timeouts with graceful degradation
- Response merging (offers concatenated, signals merged, eligibility conservative-merged)
- Ed25519 request signing (see Request signing below)
processed-auction-request hook stage — after imps are parsed and before bid
requests are dispatched — so the module can read per-imp tmp ext and set
targeting key-values without racing bidder fan-out.
Temporal decorrelation in a server-side embed
The goal of temporal decorrelation is to make pairing ambiguous to a buyer agent or network observer between router and buyer — not to add delay for its own sake. The spec’s 100-2000ms random delay is one lever; volume, batching, and cross-page caching are others. Publishers pick a profile that suits their traffic and latency budget. Delay applies to Identity Match, not Context Match. Context Match fires on the auction critical path — delaying it has no decorrelation value (correlation is about the relative timing of the pair, not their absolute arrival). Delaying both independently widens the observable window but doesn’t improve pairing ambiguity and doubles latency cost. Delaying both by a correlated random amount gains nothing at all. The deferrable side is Identity; that’s where randomization goes. Levers publishers combine:- Volume / k-anonymity. A high-traffic publisher firing hundreds or thousands of Identity Match requests per second across users on the same placement naturally produces an ambiguous pairing set. Short or zero explicit delay is defensible when traffic volume alone prevents individual-pair recovery. Low-traffic publishers have no such cover and need explicit delays toward the top of the recommended range.
- Cross-page caching. Cache Identity Match responses per
user_tokenfor the buyer’sttl_sec(typically 60s+). Cache hits produce no wire request, so most auctions have no pair to correlate. Cache misses still emit an Identity Match — publishers pair this with an explicit delay or rely on volume. Each cache refresh MUST generate a new Identity Matchrequest_id; reusing a cachedrequest_idon a new wire send violates the spec’s per-epoch dedup requirement. - Batching. Identity Match requests for multiple users bundled on the
wire within a short window destroy individual-pair timing. Spec permits
Publishers MAY batch Identity Match requests across multiple page views. - Explicit delay on Identity. Uniform random delay from an interval appropriate to traffic volume. 100-2000ms is a default that works for most publishers; smaller windows are defensible with high volume, larger with low volume. Context Match fires immediately.
- A large publisher with heavy traffic and aggressive caching may set Identity delay to 0 and rely on volume + caching. The trade-off is auditability: “we’re not delaying” needs a defense and a measurable k-anonymity target.
- A small or long-tail publisher picks a longer explicit delay (closer to 2000ms) and runs Identity on a background schedule or a client companion so the auction critical path stays under timeout budget.
- A hybrid deployment fires Context Match server-side on the auction critical path and defers Identity Match to a Prebid.js companion after a randomized post-auction delay. The decorrelation comes from the delay, not the origination source — the router still emits to the buyer from its own egress IP — but client origination defeats publisher-edge client-IP correlation and removes identity from the auction hot path.
adagents.json so auditors and users can see what temporal properties the
publisher is actually providing. “Pure parallel with no explicit delay and
no volume defense” is a valid configuration only if the publisher is willing
to surface it; it does not satisfy the spec’s temporal decorrelation SHOULD
on its own.
Request signing
Per the spec’s Request Authentication model, the router signs all TMP requests — Context Match and Identity Match — with Ed25519. Providers verify signatures using the publisher’s public key from the property registry. Providers typically sample-verify (e.g., 5% of requests) rather than verify every request to keep per-request cost under 30µs; sustained failures trigger property suppression. This prevents unauthorized parties from probing provider targeting logic by forging requests. Implementations usingadcp-go/tmp/client inherit outbound signing — the
client loads the publisher’s signing key at startup and signs every request.
Verification cost sits on the provider side and isn’t affected. Implementations
building against the TMP schemas directly without the SDK must implement:
X-AdCP-SignatureandX-AdCP-Key-Idheaders on every request.- Daily-epoch replay window (
floor(unix_timestamp / 86400)); see the signature envelope for per-message-type signed-field ordering. - Per-provider signatures. Context Match signatures are bound to
provider_endpoint_url, so the router generates one signature per fan-out target. Signature caches key on(placement_id, provider_endpoint_url), notplacement_idalone. - Signature invalidation on active-package-set change. Context Match
signatures cover the sorted
package_idslist; when the buyer’s active set changes, cached per-placement-per-provider signatures must be regenerated. Daily-epoch rollover alone isn’t sufficient. - Key rotation and
revocation via
agent-signing-key.json; providers cache keys with a 5-minute TTL and honor therevoked_atmarker.
- Signing key storage. The publisher’s Ed25519 private key is high-value
material — it authorizes forged Context Match and Identity Match requests
to the providers for which the router holds valid registrations, for the
remainder of the ~48-hour replay window if leaked. Store in HSM or KMS,
not a mounted file. The spec supports explicit revocation via
revoked_at, which propagates within the 5-minute cache TTL — but revocation does not retroactively invalidate signatures already captured before the revocation timestamp. Rotate and revoke proactively on any suspicion. - End-to-end signing verification before go-live. Per spec §Signature verification, providers SHOULD suppress a property for 24 hours on verification failure. Misconfigured signing is silent-then-catastrophic — run a signed probe against at least one provider before flipping traffic.
- 401 handling. Treat signature verification failures as non-retryable;
exclude the provider from the current auction and alert operations.
Sustained failures indicate key rotation drift, clock skew across the
epoch boundary, or a
provider_endpoint_urlmismatch between the router’s provider registration and the provider’s self-advertised endpoint.
Dependency: adcp-go/tmp
- Standard Go module, no CGO
- HTTP/2 client with connection pooling (stdlib
net/http) - Ed25519 signing (stdlib
crypto/ed25519) - No external dependencies beyond Go stdlib
Migration from Scope3 RTD module
Scope3 is the first TMP provider. Migration for publishers currently using the Scope3 RTD module:Step 1: Scope3 exposes TMP endpoint
Scope3 adds a TMP-compatible endpoint alongside their existing RTD API. The endpoint acceptsContextMatchRequest and returns ContextMatchResponse.
Scope3’s existing contextual targeting, content classification, and enrichment
signals map directly to TMP offers and signals.
Step 2: Publisher enables the TMP peer module and switches config
Before (Scope3 RTD module):realTimeData and tmp top-level
configuration blocks may coexist for an A/B comparison. Before the peer module
lands, the TMP RTD bridge can provide the same wire behavior, but its temporary
configuration remains inside realTimeData.dataProviders.
Step 3: Deprecate Scope3 RTD module
Once publishers have migrated, the vendor-specific module can be deprecated. Other vendors (DoubleVerify, IAS, etc.) can expose TMP endpoints and join the same config — no new Prebid modules needed.Benefits for Prebid
Fewer modules to maintain
One optional TMP peer module provides a migration path for up to 38 vendor RTD modules (the contextual, audience, brand safety, and bid enrichment categories). Each migrated vendor becomes a provider endpoint in the publisher’s router configuration. New TMP providers don’t require new Prebid modules, PRs, or releases. RTD stays available for integrations that have not migrated or do not fit TMP.Smaller payloads
Privacy by design
RTD modules send user identity and page context in the same request. Privacy depends on field-level masking — one missed field leaks data. TMP separates context and identity into different requests on different code paths. The context path never has access to identity data. This is structural, not policy-based. TEE attestation can make it independently verifiable.Open provider ecosystem
Any company can become a TMP provider by exposing a standard HTTP/2 endpoint. No Prebid module PR needed. No vendor-specific configuration format. Publishers add providers in config the same way they add bidder adapters.Aligns with Prebid’s direction
Prebid already standardized demand (bidder adapters) and identity (userId modules). TMP standardizes the remaining piece: real-time contextual and identity-based enrichment. The pattern is the same: define a protocol, let vendors implement it, publishers configure endpoints.Reference adapter: Prebid.js
This is a starting point for the Prebid team to adapt to Prebid.js internals. It uses@adcp/client/tmp for data transformation and Prebid’s hooks for
lifecycle integration.
Reference adapter: Prebid Server (Go)
Timeline
- SDK development — Build
@adcp/client/tmpandadcp-go/tmpagainst the TMP schemas shipping in AdCP 3.0. - Reference adapters — Working Prebid.js and Prebid Server adapters that the Prebid team can review and adapt.
- RTD bridge — If needed for an initial pilot, ship a compatibility RTD submodule that preserves TMP’s split-request and local-join invariants.
- Prebid proposal submission — Submit the peer-module architecture to Prebid.org with working code, performance benchmarks (payload size, latency), and Scope3 migration plan.
- Scope3 TMP endpoint — Scope3 ships TMP-compatible endpoint.
- Publisher pilot — One publisher runs TMP via Prebid alongside existing Scope3 RTD module, A/B comparison.
- Prebid module merge — Prebid team adapts the reference adapters to their codebase standards and merges TMP as an optional peer to RTD, not as an RTD provider or an always-on core subsystem.