The TMP Router
The TMP Router is infrastructure that sits between publishers and buyer agents. It handles request fan-out, response merging, and privacy enforcement. It does not make decisions — it routes requests and aggregates responses. The publisher configures which providers the router calls.What the Router Does
- Fans out requests: Sends Context Match requests to all configured providers with
context_matchcapability. Sends Identity Match requests to all configured providers withidentity_matchcapability. - Merges responses: Combines offers, enrichment signals, and eligibility results from multiple providers into unified responses.
- Enforces separation: Context and identity code paths are structurally separate — the context path never accesses identity data and vice versa.
- Manages latency: Applies adaptive timeouts and deprioritizes providers that consistently exceed the latency budget.
Single Binary, Separate Code Paths
The router is a single Go binary with two structurally separate code paths: one for context match, one for identity match.Provider Registration
Publishers configure which providers the router calls. This is an operational relationship — the publisher trusts the provider to participate in their ad decisioning. Provider registrations follow theprovider-registration schema (/schemas/tmp/provider-registration.json).
Discovery models
Provider registration typically comes from the page configuration — the publisher declares providers in their Prebid module config or surface-specific setup. This is the standard path and works well for publishers with a stable set of providers. Static configuration (Prebid config, YAML file, infrastructure-as-code):- Publisher declares providers at deploy time
- Router reads config at startup and on config reload
- Changes require a config update and reload/redeploy
- Appropriate for most deployments — provider lists change infrequently
- Publisher manages providers through an admin interface
- Router polls a discovery endpoint or watches for configuration changes
- Changes take effect within one refresh cycle (recommended: 30 seconds)
- Appropriate for publishers managing many providers or needing runtime updates without redeploys
- Dynamic registration endpoints MUST validate that provider
endpointURLs are external HTTPS addresses. Implementations MUST reject private (RFC 1918), link-local (169.254.x.x), and cloud metadata IP ranges to prevent SSRF through provider registration. See Provider registration security in the specification for the full normative requirements — endpoint URL validation (with DNS re-resolution), dynamic registration endpoint authentication, router-to-provider auth minimum bar, and/healthendpoint guidance.
Registration fields
| Setting | Type | Required | Description |
|---|---|---|---|
provider_id | string | Yes | Stable identifier for this provider. Used in logs, metrics, and cache keys. |
endpoint | URL | Yes | Provider’s base URL. The router appends /context or /identity when dispatching. |
context_match | bool | No | Provider handles Context Match requests. At least one of context_match or identity_match must be true. |
identity_match | bool | No | Provider handles Identity Match requests. At least one of context_match or identity_match must be true. |
countries | List<string> | Conditional | ISO 3166-1 alpha-2 country codes. MUST be present when identity_match is true. |
uid_types | List<string> | Conditional | Identity types this provider resolves. MUST be present when identity_match is true. |
timeout_ms | integer | No | Per-provider timeout. Must be ≤ the router’s latency_budget_ms. Default: 50. |
priority | integer | No | Merge conflict resolution order (lower = higher priority). Default: 0. |
status | enum | No | active, inactive, or draining. Default: active. |
properties | List<UUID> | No | Property RIDs this provider serves. When absent, the provider serves all properties. |
context_match or identity_match must be true — a provider that handles neither operation is invalid. When identity_match is true, countries and uid_types are required — the router cannot route Identity Match requests without them. The router MUST reject invalid provider registrations and SHOULD log a warning identifying the misconfigured provider.
Provider lifecycle
Providers have three lifecycle states:- Active: Provider receives requests normally.
- Draining: Provider stops receiving new requests. In-flight requests complete normally. Use when taking a provider offline for maintenance.
- Inactive: Provider is skipped entirely. Use to disable a provider without removing its configuration.
Provider health
Providers SHOULD exposeGET /health at their base URL. The router uses this for:
- Pre-flight checks: On startup or config reload, verify each provider is reachable before including it in fan-out.
- Periodic monitoring: Check provider health on a configurable interval (recommended: 30 seconds). Providers that fail consecutive health checks MAY be temporarily excluded from fan-out and automatically re-included when health recovers.
/healthz endpoint reflects overall router health, not individual provider status.
Providers MAY support any combination of context_match and identity_match. A context-only provider handles enrichment or contextual targeting. An identity-only provider handles frequency capping — the publisher evaluates context locally from the media buy’s targeting rules and calls the buyer only for identity checks.
All communication uses JSON over HTTP/2. TMP messages are small (200-600 bytes) — at these sizes, serialization format is less than 1% of total latency.
Integration
Prebid integration
Publishers with Prebid Server or Prebid.js add a TMP module that replaces vendor-specific RTD modules. The TMP module sends Context Match and Identity Match requests to the router and returns the merged response as targeting signals and package activation data. The publisher’s ad server (GAM, etc.) receives targeting key-values and activates the corresponding line items.Non-Prebid surfaces
For AI assistants, mobile apps, CTV, and retail media, the router provides a direct HTTP/2 API. Any platform that can make HTTP/2 POST requests can integrate. The request and response schemas are the same regardless of surface.SSP and DSP integration
SSPs and DSPs integrate as TMP providers — they expose an endpoint that the router calls during fan-out. This is the same pattern as existing RTD integrations.Identity tokens
Identity tokens come from existing providers (ID5, LiveRamp, UID2, etc.) that are already present on the page or in the app. TMP does not specify token lifecycle — it consumes tokens that the publisher’s identity stack already produces.Fan-Out and Response Merging
Context Match fan-out
When the publisher sends a Context Match request:- The router identifies all providers configured for the request’s
property_ridwithcontext_matchcapability. - It sends the request to all matching providers in parallel over HTTP/2.
- It waits for responses up to the latency budget (default: 50ms).
- It merges responses:
- Offers are collected from all providers. If two providers return offers for the same
package_id(uncommon — packages are typically provider-specific), the router keeps the first response received. Duplicatepackage_idacross providers is a configuration error; the router SHOULD log a warning. - Enrichment signals are concatenated. Segments from all providers are combined into a single list. Targeting key-values from different providers are namespaced to prevent collisions.
- Offers are collected from all providers. If two providers return offers for the same
- It returns the merged response to the publisher.
Identity Match fan-out
The router filters Identity Match providers by country and identity type:- The router reads the
countryfield from the request (a routing directive, not an identity signal). - It selects providers whose
countrieslist includes that country code. - It further filters to providers whose
uid_typeslist overlaps with anyuid_typein the request’sidentitiesarray. - For each selected provider, it filters the
identitiesarray to the intersection of the request’s identities and that provider’s declareduid_types. Providers MUST NOT receive identity tokens for types they did not declare — this enforces minimum-necessary-data as a structural privacy property, not an operational one. The router MUST NOT add, substitute, or transform identity tokens; the forwarded set MUST be a subset of the publisher-originidentitiesarray. - If the intersection is empty, the router MUST skip that provider entirely. An empty
identitiesarray is not a valid IMR payload (schema enforcesminItems: 1), and emitting skip-vs-forward as distinguishable telemetry would leak which identity types each user had available. - It strips the
countryfield before forwarding the request to the buyer agent. - Because the per-provider payload differs from the inbound request, the router re-signs each per-provider forward using the canonical
identities_hashof the filtered set. Providers verify signatures against the router’s public key. - It fans out to all matching providers in parallel, merges eligibility results, and returns a unified response.
package_id across providers is a configuration error — packages come from media buys and are provider-specific. If it occurs, the router applies conservative merging: the package is only eligible if it appears in eligible_package_ids from both providers. The router uses the minimum serve_window_sec across providers and SHOULD log a warning.
Timeout handling
The router manages two distinct timeout values:- Overall latency budget (
latency_budget_ms): The total time the router has to fan out, collect responses, and merge. Default: 50ms. This is the end-to-end budget the publisher allocates to TMP within their ad serving pipeline. - Per-provider timeout (
timeout_mson the provider registration): The maximum time the router waits for a single provider. Must be ≤ the overall latency budget. Default: 50ms (equal to the budget for single-provider setups).
- Single provider timeout: Skip that provider, log its latency percentile, proceed with responses from remaining providers. The skipped provider’s packages are treated as “not activated” for this request.
- All providers timeout: Return an empty response — no offers for Context Match, no eligibility for Identity Match. The publisher falls back to existing demand sources (Prebid open auction, direct-sold, etc.).
- Adaptive timeout: The router tracks per-provider latency percentiles (p50, p95, p99) and adjusts allocation over time. Consistently slow providers receive smaller timeout allocations or are preemptively skipped. Higher-priority providers (lower
priorityvalue) receive a larger share of the budget when adaptive allocation is active. This is an operational decision, not a protocol requirement.
Latency Budget
TMP targets sub-50ms end-to-end latency: publisher sends request, router fans out, providers respond, router merges, publisher receives response. This is achievable because:- Small messages: TMP requests are 200-600 bytes of JSON — roughly 10-20x smaller than a typical OpenRTB bid request. Serialization is sub-microsecond.
- No price computation: Packages are pre-negotiated. The provider evaluates targeting criteria, not auction dynamics.
- Parallel fan-out: All providers are called simultaneously. The total latency is the slowest provider’s response time, not the sum.
- Stateless router: No database lookups in the hot path. The router’s only job is forwarding and merging.
- Connection reuse: HTTP/2 multiplexing allows concurrent requests to each provider over a single connection.
Comparison to Vendor RTD Modules
The TMP Router generalizes what vendor-specific RTD modules do today. A single-vendor RTD module evaluates packages against content in real time, but it is locked to one provider, one surface (Prebid), and sends the full OpenRTB BidRequest. The TMP Router replaces this with a multi-provider, multi-surface, protocol-standard alternative:| Vendor RTD Module (today) | TMP Router | |
|---|---|---|
| Providers | Single vendor | Any provider declaring TMP capabilities |
| Discovery | Publisher configuration | Publisher configuration |
| Surfaces | Web (Prebid Server) | Web, AI, mobile, CTV, retail media |
| Request format | Full OpenRTB BidRequest (~2-10KB JSON) | TMP ContextMatchRequest (~200-600 bytes JSON) |
| Privacy | Data masking before sending | Structural separation (TEE-ready) |
| Identity handling | User ID in bid request | Separate Identity Match operation |
Relationship to TEE Auction Infrastructure
TEE-based auction infrastructure (encrypted bids, attestation proofs, verifiable winner selection) is complementary to TMP. When a publisher wants competitive selection among activated packages from multiple buyers:- TMP Router collects Context Match responses (which packages each buyer wants to activate).
- Publisher submits the activated packages (with their pre-negotiated prices) to a TEE auction.
- The TEE enclave selects the winner and produces an attestation proof.
- Publisher activates the winning package.
Deployment
The TMP Router is a single Go binary built on adcp-go. It reads a configuration file listing providers and their capabilities. Each provider exposes two path-based endpoints under its base URL —POST /context and POST /identity — and the router dispatches by path.
Configuration
Container deployment
/healthz.
Capacity planning
Each router instance handles approximately 10,000 requests per second on a 2-vCPU container. Memory usage scales linearly with the number of concurrent connections to providers, not with request volume. For web publishers, one router instance per point of presence (PoP) is typical. For AI platforms, a centralized deployment with regional failover is sufficient since the router adds < 5ms to end-to-end latency.Monitoring
The router exposes Prometheus metrics at/metrics:
| Metric | Description |
|---|---|
tmp_context_match_duration_ms | Context Match end-to-end latency histogram |
tmp_identity_match_duration_ms | Identity Match end-to-end latency histogram |
tmp_provider_duration_ms | Per-provider response time histogram |
tmp_provider_timeout_total | Per-provider timeout counter |
tmp_provider_error_total | Per-provider error counter |
tmp_offers_total | Total offers returned across all providers |
tmp_provider_timeout_total increasing — a provider consistently exceeding its timeout budget degrades match quality for all requests that include it.