VASTlint

VAST validator for Ruby and Rails

Short answer: install the vastlint gem and call Vastlint.validate(xml). It validates VAST XML in-process and returns a stable result you can hand straight to your frontend.

The gem wraps the same Rust core used by the CLI, Go binding, and web validator through an FFI C API. No subprocess to manage, no network hop, a good fit for a DSP, SSP, ad server, or trafficking backend that validates creatives and renders structured results in a React UI.

Why use the Ruby gem

  • In-process validation, with no subprocess or external service
  • Same core rule coverage as the CLI, web app, and other bindings
  • Stable JSON result shape for backend-to-frontend responses
  • Per-call rule_overrides and wrapper-depth options

Install

# Gemfile: published to GitHub Packages
source "https://rubygems.org"

source "https://rubygems.pkg.github.com/aleksUIX" do
  gem "vastlint"
end

The gem expects a platform-matched libvastlint shared library; release builds are vendored under lib/vastlint/native/, or point at one with VASTLINT_LIB_PATH.

Minimal example

require "vastlint"

result = Vastlint.validate(vast_xml)

if result.valid?
  puts "clean tag"
else
  puts result.summary.errors
  puts result.issues.first.message
end

puts result.to_json

Rails controller

class VastValidationsController < ApplicationController
  def create
    result = Vastlint.validate(
      params.require(:xml),
      max_wrapper_depth: params[:max_wrapper_depth].presence&.to_i || 5,
      rule_overrides: params[:rule_overrides]
    )

    render json: result.as_json
  rescue ArgumentError, Vastlint::Error => error
    render json: { error: error.message }, status: :unprocessable_entity
  end
end

The response shape is stable and frontend-friendly: version, an issues array (each with id, severity, message, path, spec_ref, line, col), and a summary with error, warning, and info counts.

Why validate VAST in-process

A malformed VAST tag is a billed impression that never renders: the player loads, the auction clears, the publisher is charged, and the viewer sees a blank slot. The failure shows up in the player, not your Rails logs, so a bad tag can sit in rotation until someone traces a revenue dip back to it.

The gem calls the same Rust core the rest of the pipeline uses, in-process through FFI. No subprocess to spawn per request, no validation microservice to keep alive, and the XML never leaves your app. Because the result serialises to a stable JSON shape, the same call that gates a creative on the backend also feeds the reviewer's screen: the trafficker sees exactly what the system enforced.

Where it lands in an SSP, DSP, or ad server

Ruby and Rails tend to run the ad-ops and trafficking side of the stack rather than the microsecond bid path, so validation lands where humans and creatives meet the system.

  • Creative ingestion / upload: validate in the controller when a tag is submitted, and reject it with the specific issues before it is ever trafficked.
  • Trafficking and admin review: back a creative-QA screen so an ad-ops reviewer sees errors and warnings inline as they approve a tag.
  • Batch QA jobs: a nightly Sidekiq or rake task that re-validates every active tag and flags drift after upstream changes.
  • Demand-partner intake: validate sample adm payloads from a new SSP or DSP partner during onboarding and report error rates back as part of the SLA.

When not to use the Ruby gem

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