AdPilot Proxy
AdPilot Proxy is a transparent FreeWheel proxy that sits between the VAST caller (DAI/GAM) and FreeWheel. It receives the exact same FreeWheel-formatted ad tag request, optionally enriches or modifies parameters via the Rules Engine (EvalScript), then forwards to FreeWheel and returns the VAST/VMAP response.
Why
Two problems drove the need for this:
- Google interpolation bugs – DAI/PodServing proxy interpolation has broken in the past (e.g., sorted slot data). A proxy lets us bypass Google’s parameter handling entirely.
- Parameter harvesting for SqueezeBacks – For live events like UEFA, both SqueezeBacks and DAI need to control the stream. The proxy harvests ad tags from the client without changing the client, and without DAI releasing them.
In both cases, Bakery serves as a proxy for FreeWheel requests, capable of passing through all ad tags and of inserting, modifying, and deleting ad tags on the fly per client/per request.
Architecture
FreeWheel-formatted query string
VAST Caller --------------------------------------------------------> AdPilot Proxy
(DAI / GAM) |
1. Reject extra path after /adpilot/adproxy
2. Parse FW sections (;-separated)
3. Collect EvalScript names from global rule sets (filter = es(name))
4. Run each EvalScript (Rules Engine), apply st() overrides in order
5. Rebuild FW URL
|
v
FreeWheel
|
VAST/VMAP
|
VAST Caller <-----------------------------------------------------+
URL Format
/adpilot/adproxy?{global_params};{kv_params};{slot1};{slot2};...
The path must be exactly /adpilot/adproxy (optional trailing slash). A legacy URL such as /adpilot/adproxy/UEFA/... returns 404.
The query string uses the standard FreeWheel ad tag format with semicolon-separated sections:
| Section | Contents | Example |
|---|---|---|
| 0 (global) | Request-level params | prof=520311%3Ahylda&nw=520311&flag=...&csid=...&caid=...&resp=vmap1%2Bvast4 |
| 1 (key-value) | Targeting params | _fw_us_privacy=1YNN&gr=2&ge=2&tfcd=0&sb=14&_fw_coppa=0 |
| 2+ (slots) | Ad slot definitions | ptgt=a&cpsq=1&mind=15&tpos=0&tpcl=MIDROLL&maxd=15 |
Examples
Passthrough (no enrichment):
/adpilot/adproxy?prof=520311%3Ahylda&nw=520311&...;kv_params;slot_params
With EvalScript enrichment:
EvalScript names are not in the URL. Configure global=1 rows in the rule_set table whose filter is es(name) with a single identifier ([a-zA-Z0-9_]+, case-insensitive es), e.g. es(UEFA) or es(my_script). Use one row per script; AdPilot loads those global rows and runs each script in order (duplicates deduped). Scripts typically gate on Header(Host), URI, and FreeWheel tags via CustomEvaluators before emitting st('tag','value') overrides.
GAM ad tag URL example (no path segment for script name):
https://bakery-host/adpilot/adproxy?prof=520311%3Ahylda&nw=520311&...
If no global row applies es(name), or every script short-circuits / yields no st() filters, the proxy forwards the request unchanged (aside from normal URL handling).
Rules Engine Integration
The Rules Engine uses the existing EvalScript infrastructure (configured via VPUI):
Building Blocks
CustomEvaluator (CE) – a gate condition. Reads a value from the request and compares it to expected values. If the condition fails, the script short-circuits.
| Source | What it reads |
|---|---|
URI | Request URL path |
Header(name) | HTTP header (e.g., Header(X-Geo-Asn)) |
Tag(name) | FreeWheel parameter from the incoming request (e.g., Tag(gr) for age bracket) |
UTCTime | Current UTC time |
MediaTitle | Asset title |
MediaLength | Video duration |
DevCap(name) | Device capability |
Operations: EQUALS, IN, STARTSWITH, <, >, !=, NOTSET
RuleSet (RS) – a filter action. Contains a filter expression like sport_type(soccer) that sets a FreeWheel parameter.
EvalScriptStep – chains CEs and RSs in sequence. Steps execute in sequence_id order.
Evaluation Flow
for each step in script (ordered by sequence_id):
if step is ActionString (e.g., "sport_type(soccer)"):
-> add to overrides
if step is RuleSet:
-> add RS.Filter to overrides
if step is CustomEvaluator:
-> evaluate condition
-> if TRUE: continue to next step
-> if FALSE: STOP (short-circuit), discard collected overrides
Example: Set sport_type Based on Geo
This script sets sport_type=soccer for U.S. viewers:
| Step | Type | Configuration |
|---|---|---|
| 1 | RuleSet | alias=uefa, filter=sport_type(soccer) |
| 2 | CustomEvaluator | name=Soccer vs. Futbol, source=Header(X-Geo-Asn), operation=STARTSWITH, match=US:* |
- Step 1 collects the filter
sport_type(soccer)as a pending override. - Step 2 checks if the viewer is in the U.S. If yes, the override is applied. If no, the script short-circuits and no override is applied.
Example: Add caid When Missing
This script adds a default caid when the VAST caller doesn’t send one:
| Step | Type | Configuration |
|---|---|---|
| 1 | CustomEvaluator | source=Tag(caid), operation=NOTSET |
| 2 | ActionString | caid(c_4Zqs9CbdzcPf5w9EqRXmlJRz7Ji0MT) |
- Step 1 checks if
caidis absent from the request. If absent, continue. - Step 2 injects the default
caidvalue into the key-value section.
Where Overrides Are Applied
Overrides from the eval script are applied to section 1 (key-value targeting params) of the FreeWheel URL. If a parameter already exists in section 1, its value is replaced. If it doesn’t exist, it’s appended.
Slot sections (2+) are never modified – they pass through as-is.
GAM Configuration
To use AdPilot Proxy, configure the stream in GAM:
- In the GAM LiveStreamEvent, set the ad tag url to point at AdPilot instead of FreeWheel (path is
/adpilot/adproxyonly — no script name in the path):https://bakery-host/adpilot/adproxy?prof=520311%3Ahylda&nw=520311&...(The domain must be whitelisted by Google for DAI – this is already handled for Bakery AdPilot.)
-
In VPUI, create
globalrule sets whosefilteris exactlyes(your_eval_script)(one script per row; identifier[a-zA-Z0-9_]+). Pair with Custom Evaluators / EvalScripts as needed. - Done. The VAST caller sends the same FreeWheel params it always has; AdPilot enriches and forwards.
API Endpoint
GET /adpilot/adproxy
| Component | Required | Description |
|---|---|---|
| Path | Yes | Must be /adpilot/adproxy or /adpilot/adproxy/ only. Extra segments → 404. |
| Query string | Yes | Full FreeWheel-formatted ad tag parameters (;-separated sections). |
| EvalScripts | — | Driven by global=1 rule_set rows with filter = es(name) (single identifier). |
Responses
| Status | Description |
|---|---|
| 200 | VAST/VMAP XML from FreeWheel |
| 404 | Invalid path (e.g. /adpilot/adproxy/foo) |
| 502 | FreeWheel request failed |
Response Headers
| Header | Value |
|---|---|
Content-Type | application/xml |
Cache-Control | no-cache, no-store, must-revalidate |
Related Files
| File | Purpose |
|---|---|
handlers/adpilot_handler.go | Proxy handler implementation |
evaluators/evaluate_script.go | EvalScript evaluation engine |
repository/models/eval_script_step.go | EvalScriptStep model and ActionWrapper |
repository/models/custom_evaluator.go | CustomEvaluator model |
repository/models/rule_set.go | RuleSet model |
handlers/freewheel_proxy_handler.go | Original FW proxy (used by FastBreak) |