VASTlint

VAST validator for Elixir and Erlang

Short answer: add the vastlint hex package and call Vastlint.validate/1. You get in-process VAST XML validation on the BEAM with the same rule catalog as the web validator and CLI, with no sidecar and no network hop.

A lot of programmatic infrastructure runs on the BEAM (Elixir and Erlang). If your bidder, SSAI stitcher, or ad server is one of them, you can validate creatives where they already live instead of shelling out to an external service.

Two integration modes

  • OTP port (recommended for ad delivery): runs vastlint-cli as a supervised OS process. A crash is isolated; the supervisor restarts it. ~10–50 µs IPC overhead.
  • DirtyCpu NIF: sub-microsecond, but a crash takes down the BEAM node. Use it for internal tooling and batch jobs only.

Install (Elixir)

# mix.exs
defp deps do
  [
    {:vastlint, "~> 0.4"}
  ]
end

Install (Erlang / rebar3)

%% rebar.config
{deps, [
    {vastlint, "0.3.3"}
]}.

The hex package ships precompiled NIFs for major platforms, so no Rust toolchain is required under normal circumstances. Set VASTLINT_BUILD=true to build from source.

Minimal example

xml = File.read!("creative.xml")

case Vastlint.validate(xml) do
  {:ok, %Vastlint.Result{valid: true}} ->
    :ok

  {:ok, %Vastlint.Result{summary: summary, issues: issues}} ->
    Logger.warning("VAST invalid: #{summary.errors} errors")

    for issue <- issues do
      Logger.warning("[#{issue.severity}] #{issue.id}: #{issue.message}")
    end

    {:error, :invalid_vast}

  {:error, reason} ->
    {:error, reason}
end

Vastlint.validate!/1 returns the %Vastlint.Result directly and raises Vastlint.ValidationError on failure if you prefer the bang form.

Validating tags in batch

# Validate a list of tags; each entry is {:ok, result} or {:error, reason}.
[xml1, xml2, xml3]
|> Vastlint.validate_batch()
|> Enum.with_index()
|> Enum.reject(fn {{:ok, r}, _} -> r.valid; _ -> false end)
|> Enum.each(fn {_, i} -> Logger.warning("tag #{i} failed validation") end)

Why validate VAST in-process on the BEAM

A broken VAST tag is an impression that gets billed and never renders: the player loads, the auction clears, the publisher is charged, and the viewer sees nothing. If your ad delivery runs on the BEAM, the validation needs to keep up with it: a per-creative call out to a remote validator adds a network hop and a failure mode to a path measured in microseconds, and it scales with the same concurrency pressure as everything else on the node.

Running validation in-process keeps it inside the same supervision tree as your delivery logic. The trade-off the BEAM forces you to make explicit is isolation: a native validation crash must not take ad requests down with it. That is why the OTP port mode is the recommended shape for delivery: the Rust process is supervised and restarts transparently, so one malformed creative can never panic the node and drop every in-flight request.

Where it lands in an SSP, DSP, or SSAI service

On the BEAM, validation belongs at the request and pipeline boundaries, wherever a creative enters from an untrusted source.

  • SSP / exchange (Phoenix or Plug): validate the VAST in each seatbid.bid.adm inside the bid handler before forwarding the winning bid, and fall through to the next bid if it fails.
  • DSP / bidder: validate the creative you are about to bid with so you never win an impression you cannot render.
  • Creative QA pipeline (Broadway / GenStage): use validate_batch/1 to score tags in bulk from a demand feed and route failures to a dead-letter stage, all under back-pressure.
  • SSAI / ad insertion: gate the stitcher so a malformed creative never reaches the manifest, which is critical when one stream serves many viewers at once.
  • Per-partner monitoring: aggregate error rates per demand partner from validation results to enforce SLAs.

When not to use the BEAM binding

If you just want a quick manual answer, use the web validator. If you only have a live tag URL, use the tester. If the real problem is wrapper depth or redirect chains, jump to the inspector.

Related reading