Embedding vastlint in an SSP, DSP, or ad server
vastlint is built for in-process validation inside ad tech infrastructure. Embed vastlint-core directly in your bid handler, SSAI stitcher, or ad server to validate every VAST response before committing the impression — no subprocess, no network round-trip, no external dependency to manage.
A typical OpenRTB bid cycle has 100–300 ms to work with. vastlint validates a 17 KB production VAST tag in 363 µs on a single core, adding less than 2.1% of the bid budget on the heaviest 44 KB tags. An SSAI platform doing 1,000 stitches/sec spends more time on DNS than on VAST validation.
Architectural diagram
VAST ad flow: wrapper chain to viewer
Why validate at bid time?
VAST errors are silent. A missing <Impression>, a malformed duration, a VPAID tag on a CTV device — none of these produce a player error you can act on. The impression is lost, the tracking fires (or doesn't), and a discrepancy surfaces in a report three days later. By the time you see it, the revenue is gone.
Validating inline, before the bid is committed, lets you:
- Reject invalid tags and serve a fallback in the same auction window
- Return structured rule IDs to the demand partner so they can fix the creative at the source
- Track per-partner error rates and escalate when they spike
- Filter by revenue-impact rules to avoid false positives on advisory-only issues
Rust — vastlint-core
vastlint-core is a zero-dependency Rust library. Three compile-time dependencies (quick-xml, url, phf), no runtime dependencies. Rules are compiled Rust functions — no regex engine, no schema interpreter, no allocator pressure beyond the parse.
# Cargo.toml
[dependencies]
vastlint-core = "0.4"Basic validation:
use vastlint_core::validate;
let result = validate(vast_xml);
if !result.summary.is_valid() {
for issue in &result.issues {
// issue.id — stable rule ID (e.g. "VAST-2.0-inline-impression")
// issue.severity — "error", "warning", or "info"
// issue.message — human-readable description
// issue.path — XPath location in the document
eprintln!("[{}] {} — {}", issue.severity, issue.id, issue.path);
}
}With per-deployment rule overrides (e.g. relax a CTV-only rule for web inventory):
use std::collections::HashMap;
use vastlint_core::{validate_with_context, RuleLevel, ValidationContext};
let mut overrides = HashMap::new();
overrides.insert("VAST-4.1-mezzanine-recommended", RuleLevel::Off);
overrides.insert("VAST-2.0-mediafile-https", RuleLevel::Error); // promote to error
let ctx = ValidationContext {
rule_overrides: Some(overrides),
..Default::default()
};
let result = validate_with_context(vast_xml, ctx);Full API documentation is on docs.rs/vastlint-core.
Go — vastlint-go (no Rust toolchain required)
vastlint-go wraps the same Rust core via CGo. Prebuilt static libraries are bundled in the module — you do not need a Rust toolchain on your build or production machines. Supported platforms: Linux (amd64, arm64), macOS (amd64, arm64).
go get github.com/aleksUIX/vastlint-goBasic validation:
import vastlint "github.com/aleksUIX/vastlint-go"
result, err := vastlint.Validate(xmlBytes)
if err != nil {
log.Fatal(err)
}
if !result.Valid {
for _, issue := range result.Issues {
log.Printf("[%s] %s: %s", issue.Severity, issue.ID, issue.Message)
}
}With wrapper depth cap and per-deployment overrides:
result, err := vastlint.ValidateWithOptions(xmlBytes, vastlint.Options{
MaxWrapperDepth: 5, // IAB-recommended maximum
RuleOverrides: map[string]string{
"VAST-4.1-mezzanine-recommended": "off", // web inventory, no SSAI
"VAST-2.0-mediafile-https": "error", // enforce HTTPS strictly
},
})
if err != nil || !result.Valid {
// Quarantine the tag. Return result.Issues to the demand partner.
}Elixir / Erlang — vastlint_erlang NIF
vastlint_erlang is a NIF (Native Implemented Function) for high-throughput BEAM pipelines. It uses the same compiled Rust core. Add it to your mix.exs:
# mix.exs
defp deps do
[
{:vastlint, "~> 0.4", github: "aleksUIX/vastlint-erlang"}
]
endcase Vastlint.validate(xml_string) do
{:ok, %{summary: %{errors: 0}}} ->
:ok
{:ok, %{issues: issues}} ->
errors = Enum.filter(issues, &(&1.severity == "error"))
{:reject, errors}
{:error, reason} ->
{:error, reason}
endProduction patterns
Filter by revenue impact before rejecting
Not every error warrants a hard rejection. Use the rules reference to identify which rule IDs map to impression-killing defects (missing <Impression>, bad <Duration> format, no <MediaFile>) versus advisory issues. Run two passes: hard-reject on errors, quarantine-for-review on warnings.
Return rule IDs to the demand partner
Every issue in the result includes a stable id field (e.g. VAST-2.0-inline-impression) and an XPath path pointing to the exact element. Return these in your bid response or partner notification — they are actionable without manual triage.
Wrapper depth cap
The IAB recommends a maximum wrapper chain depth of 5. Set MaxWrapperDepth: 5 (Go) or the equivalent context field in Rust to enforce this. The rule VAST-2.0-wrapper-depth fires when the depth is exceeded.
Self-hosting (air-gapped deployments)
All bindings run entirely in-process. There is no network code in vastlint-core — no callbacks, no telemetry, no license checks. For teams that also need the web validator or REST API on-premise, the Docker image is FROM scratch, under 5 MB, with a cold-start under 10 ms.
Performance reference
Benchmarked on Apple M4 (10-core), production-realistic VAST tags:
| Tag size | Single-core throughput | Single-core latency | 10-core throughput |
|---|---|---|---|
| 17 KB | 2,747 tags/sec | 363 µs | 15,760 tags/sec |
| 44 KB | 475 tags/sec | 2,104 µs | 2,635 tags/sec |
A typical OpenRTB bid cycle takes 100–300 ms; validation adds less than 2.1% of that budget even on the heaviest tags.
Enterprise support & integration consulting
Evaluating vastlint for production use in a DSP bid pipeline, SSAI platform, ad server, or CTV device? The author is available for integration consulting, SLA-backed enterprise support agreements, custom rule development, and on-site architecture review.
Typical engagements include:
- Architecture review for in-process bid-time validation
- Custom rule development for proprietary spec extensions or internal trafficking requirements
- Per-partner error rate dashboards and alerting integration
- Priority issue resolution and private disclosure channel
- Air-gapped or AWS Marketplace deployment support
Email: [email protected]
Or reach out through one of these channels:
- GitHub Discussions — questions, use-case sharing, and production feedback
- GitHub Issues — bug reports and feature requests
- Security Advisory — private disclosure channel, 48-hour acknowledgement SLA
- GitHub: @aleksUIX
Further reading
- Rules reference — all 118 rules with severity, category, and fix examples
- How to validate VAST XML — overview of all integration options
- MCP server — for agentic pipelines and AI buyer agents
- vastlint-core API docs
- vastlint-go README
- vastlint-erlang README