Created by Aleksander Sekowski.
High-performance, in-process VAST XML validation for Ruby backends.
Ruby & Rails guide: vastlint.org/docs/best-vast-validator-ruby · Rule reference: vastlint.org/docs/rules · Web validator: vastlint.org/validate · Test a live tag URL: vastlint.org/tester · Inspect wrapper chains: vastlint.org/inspect
This gem wraps the existing vastlint-ffi C API, which is backed by the same Rust core used by the CLI, Go binding, Erlang NIF, MCP server, and web validator. The intended use case is a DSP, SSP, ad server, or trafficking backend that needs to validate VAST XML and return structured linting results to a React or other web frontend.
- No subprocess management in your Ruby app
- No network hop to an external validation service
- Stable JSON-compatible result shape for backend-to-frontend responses
- Same rule coverage and behavior as the rest of the vastlint ecosystem
This repository provides workflows for GitHub Packages and RubyGems. Use the registry where the selected version has been published. The GitHub Packages installation path below requires authentication.
Authenticate Bundler against GitHub Packages:
bundle config https://rubygems.pkg.github.com/aleksUIX USERNAME:TOKENThen add the package source in your application:
source "https://rubygems.org"
source "https://rubygems.pkg.github.com/aleksUIX" do
gem "vastlint"
endThen install it:
bundle installThe gem expects a platform-matched libvastlint shared library.
For development, it will also try the sibling vastlint/target/debug and vastlint/target/release outputs.
For a distributable gem release, vendor the release libraries into lib/vastlint/native/* with:
bash scripts/fetch-libs.shYou can also point the gem at an explicit shared library path:
export VASTLINT_LIB_PATH=/absolute/path/to/libvastlint.dylibThe RubyGems workflow requires a configured RUBYGEMS_API_KEY. Publishing to GitHub Packages does not also publish to RubyGems.
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_jsonclass VastValidationsController < ApplicationController
def create
result = Vastlint.validate(
params.require(:xml),
wrapper_depth: params[:wrapper_depth].to_i,
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
endThe response shape is stable and frontend-friendly:
{
"version": "4.2",
"issues": [
{
"id": "VAST-2.0-inline-impression",
"severity": "error",
"message": "<InLine> must contain at least one <Impression>",
"path": "/VAST/Ad[0]/InLine",
"spec_ref": "IAB VAST 2.0 §2.2.1",
"line": 4,
"col": 3
}
],
"summary": {
"errors": 1,
"warnings": 0,
"infos": 0,
"valid": false
}
}Need a link instead of raw output? The vastlint CLI can upload a validation report (rule IDs, severities, and locations; never the input XML) and print a public URL you can paste into Slack, tickets, or PRs:
vastlint check tag.xml --share
# https://vastlint.org/r/<id>Vastlint.validate(xml, wrapper_depth: 0, max_wrapper_depth: 5, rule_overrides: nil)
Vastlint.versionrule_overrides accepts a hash of rule IDs to levels:
result = Vastlint.validate(
vast_xml,
rule_overrides: {
"VAST-2.0-mediafile-https" => "error",
"VAST-4.1-mezzanine-recommended" => "off"
}
)core-version.txt records the bundled public engine release. The native manifest.json records each file's SHA-256 and release URL. The binding's package version and engine version are reported separately. Refresh all four platform libraries with bash scripts/fetch-libs.sh, or pass an explicit engine tag. Run python3 scripts/check-native.py before packaging. No sibling checkout is required for the bundled runtime.
Vendored release libraries belong at:
lib/vastlint/native/darwin_arm64/libvastlint.dyliblib/vastlint/native/darwin_amd64/libvastlint.dyliblib/vastlint/native/linux_arm64/libvastlint.solib/vastlint/native/linux_amd64/libvastlint.so
The shared libraries come from the vastlint-ffi-* tarballs attached to each vastlint GitHub Release.
If this gem lives in its own GitHub repository, you do not need to add a separate publish token just to push to GitHub Packages. GitHub injects GITHUB_TOKEN automatically, and the workflow only needs packages: write permission.
The included workflow at .github/workflows/publish-github-packages.yml:
- derives the gem version from the tag or
lib/vastlint/version.rb - downloads the
vastlint-ffi-*shared libraries from the public engine release pinned incore-version.txt - runs the Ruby tests
- builds the gem
- pushes it to
rubygems.pkg.github.com/<owner>usingGITHUB_TOKEN
That means the normal release path can be fully automated from CI.
Publish to GitHub Packages with a classic personal access token that has write:packages.
export GITHUB_PACKAGES_OWNER=aleksUIX
export GITHUB_PACKAGES_TOKEN=YOUR_CLASSIC_PAT
./scripts/publish-github-packages.shThe script:
- builds
vastlint-<version>.gemif needed - writes a temporary
~/.gem/credentialswith:github: Bearer TOKEN - pushes to
https://rubygems.pkg.github.com/$GITHUB_PACKAGES_OWNER
The publish script also accepts GITHUB_TOKEN, so the exact same script works inside GitHub Actions without adding a separate secret.
Install from the registry with:
gem install vastlint \
--clear-sources \
--source https://USERNAME:TOKEN@rubygems.pkg.github.com/aleksUIX/Apache 2.0.