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:

  1. 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.
  2. 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 caid is absent from the request. If absent, continue.
  • Step 2 injects the default caid value 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:

  1. In the GAM LiveStreamEvent, set the ad tag url to point at AdPilot instead of FreeWheel (path is /adpilot/adproxy only — 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.)

  2. In VPUI, create global rule sets whose filter is exactly es(your_eval_script) (one script per row; identifier [a-zA-Z0-9_]+). Pair with Custom Evaluators / EvalScripts as needed.

  3. 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
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)

This site uses Just the Docs, a documentation theme for Jekyll.