Resonance
Resonance is Adbustr's own decisioning path: it enriches the request, decides whether the traffic is worth an auction, picks the demand that can actually serve it, runs the fan-out inside tmax, and clears the winner. Written in Go, co-located with the auction, driven by configuration rather than code.
Enrich · Filter · Select · Clear
The thesis
An exchange is judged on one interval: the few hundred milliseconds between an ad request arriving and a cleared price leaving. Everything that decides the outcome — what the request is, whether it deserves demand, which partners see it, how the winner is priced — happens inside that interval, in one process.
Resonance is that process. It is not a scoring service bolted onto someone else's stack: enrichment, quality filtering, demand selection, fan-out, clearing and settlement are stages of the same request path, and each stage records why it did what it did.
How it works
Every request — a format handler call or a raw OpenRTB BidRequest posted to /rtb/{uid} — is resolved to the real client IP and enriched with GeoIP (ISO2 country, region, city, coordinates) and with browser, OS and device class derived from the user-agent. Enrichment degrades gracefully: a missing asset narrows targeting precision, it never drops the request.
Eligibility is decided before any demand partner is contacted. device.ip and device.ua are mandatory; the user-agent must resolve into the browser allow-list; IAB25, IAB25-3 and IAB26 are cut on mainstream endpoints. Each rejection is written to the funnel log with its reason code. Resonance does not run IVT scoring, proxy or datacenter detection, bot filters, environment checks, or automated validation of counterparty supply files — see the Anti-Fraud Policy.
The request is normalised into a single OpenRTB 2.5 BidRequest: exactly one imp, a site or app object, device, user, source.ext.schain (SupplyChain 1.0), badv, imp.secure, tmax, cur and at. Video impressions carry fixed mimes, protocols [2,3,5,6], api [1,2], 1–150 s duration and 10–9000 kbps bitrate bounds, so partners receive one predictable shape instead of per-publisher dialects.
Demand is chosen per request, not per integration: partner active and public flags, geo list, permitted device types, zone allow and block lists, and a synced buyeruid for partners that only serve matched users. ReqFilter sets what share of eligible requests each partner actually sees, and per-partner request adjustments are applied before the call leaves the process.
Selected partners are queried in parallel inside one window — tmax, 300 to 500 ms by default and configurable per publisher and zone, with a 500 ms per-partner timeout. Responses that arrive after the window are discarded rather than held. Every outcome is instrumented per partner: requests, bids, timeouts, errors, latency.
Bids are normalised to the base currency before they are compared: Norm = price × currency_rate × BidRate. Non-200 responses, empty bodies, unparseable JSON, price ≤ 0 and anything below the partner bidfloor are discarded. The winner clears at second price by default (at=2); first price (at=1) is available per partner. The clearing price is bounded below by the partner bidfloor and above by the winner's own bid, and the price actually recorded for each request is exposed in the impression-level logs. Settlement substitutes the price macros, calls the winner's nurl server-side and fires lurl on every valid losing bid with its loss code.
Auction parameters
Economic levers — bidfloor, BidRate, ReqFilter and revenue share with per-country overrides — are read from the database and served from the configuration snapshot. They are configuration, not constants in the binary.
Reason codes
A silent 204 is the least useful answer an exchange can give. Resonance writes a per-request funnel — verdict and reason — into ClickHouse alongside impression and click events, so an integration issue is diagnosed from the log rather than guessed at over email. A GET to /rtb/{uid} is treated as a connectivity ping, not a bid request.
Defensibility
Resonance is not a wrapper around someone else's pipeline. The exchange core is our own Go service, and the decisioning path runs inside the same process as the auction — no network hop between the decision and the bid it governs.
Behaviour is driven by a configuration snapshot in Redis, projected from the database and hot-reloaded through a version and reload channel: floors, bid rates, throttling, revenue share, demand routing and the endpoint live toggle change without a rebuild and without a restart. The endpoint toggle is fail-safe off — when demand is switched away, requests are still accepted and logged.
Everything that path decides is observable: per-partner request, bid, timeout, error and latency metrics in Prometheus and Grafana, and a per-request funnel with verdict and reason in ClickHouse. What we publish about the exchange, we can read back out of it — see the Transparency Center.