VASTlint
Back to blog
Evaluation/8 min read

The vastlint Surface You Install Depends on Whether You Hold a Tag, a Repository, or the Response

Evaluating vastlint means picking a surface. A tag in a thread is the website. A folder of fixtures that must not regress is the CLI in CI, where vastlint check exits non-zero and can fail on warnings. A service that sees VAST before the player is an in-process check, including vastlint-go on each hop. None of these classify invalid traffic or prove a television played the file.

Author

Alex Sekowski

Published

September 27, 2026

Reading time

8 min read

VASTAd serverAd opsCI

The artifact decides the install

Install the surface that sees the tag at the moment you are allowed to reject it.

What you holdSurfaceWho runs itWhat it will not do
One tag in a thread or a ticketWebsite: validate, tester, inspectorA person, onceIt will not remember the next tag
A repository of tag fixturesCLI in CIThe buildIt will not fetch a production URL unless you ask a separate step to
The VAST response inside a serverIn-process libraryThe bid path, the ad server, or the stitcherIt will not play the creative on a device
An agent setting up campaignsMCPThe agent, as a tool callIt will not replace the human pass on a wrapper you have not fetched
Four ways to run vastlint: paste a tag on the website, fail a build in CI, validate inside the ad server, or call it from an agent. The artifact you hold picks the surface.
The same rules can run in four places. The place has to be where the tag exists before you are committed to serving it. Diagram by vastlint.org. An independent open-source project. The diagram restates figures already cited in this post.

Someone evaluating vastlint is usually one of four seats, and they are easy to mix because the rules are the same. A verification manager holds a tag in a thread. A trafficker holds a URL and an attachment. A publisher ad ops lead holds advertiser tags arriving all week. A platform engineer holds the response inside a process that will either forward it or drop it. The website satisfies the first two on a slow day. It does not satisfy the last two, because a person with a tab is not in the path.

Pick the surface from the artifact at the moment you can still reject it. If that moment is a ticket, use the website and write down what you checked. If that moment is a pull request, the CLI belongs in CI. The CI doc shows vastlint check on a path of XML files, with --fail-on-warning when a warning should fail the build, a GitHub Action that wraps the binary, and a container image for GitLab. No Rust toolchain is required to run the check. If that moment is the process that already fetched the demand or the advertiser tag, the library belongs in that process. The Go note walks a chain: fetch a hop, validate it, unwrap only when the wrapper is sound, stop at a depth limit. The ad server doc is the same idea for an SSP, a DSP, or a stitcher that cannot pay a network round trip for the check.

What the website is for

The website is a single tag, in front of a person who can read the result. Validate when you already have XML. Test when you have a URL and need the body, the media, and the trackers that URL returns now. Inspect when the body is a wrapper and the failure is on a later hop. That split matters. A tester result that looks fine on hop one is how a dropped verification node survives until the buyer complains.

Do not make the website the control for a team that ships tags daily. It has no memory of yesterday's tag, and it is not on the request path. It is the debugger for the tag the other surfaces rejected, and the only surface a non-engineer will open. The verification manager and the trafficker notes are that use. Send engineers elsewhere.

What CI is for

CI is a tree of files you are willing to store. Creative fixtures, golden wrappers, tags exported from a template. vastlint check exits non-zero when there are errors, so the build fails. --fail-on-warning is how you treat the advisory class as a break when you have decided those advisories cost money. The files in the repo are the contract. A production URL that changes under you is not in that contract unless a job fetches it and writes the body where the checker can see it.

This is the right install for a creative pipeline and for a publisher or an agency that wants a known-bad tag to fail review before a person trafficks it. It is the wrong install for a bid response that exists for a few hundred milliseconds and is never a file. Do not staff an engineer to commit every demand response so CI can look at it.

Get VAST spec updates, platform guides, and release notes in your inbox.

What in-process is for

In-process is the response you already have in memory. An ad server accepting programmatic demand, a stitcher about to insert, a DSP checking a creative before it bids. The Go integration validates each fetched hop instead of waiting for the player to discover a malformed body, and it can keep stable rule IDs so partner quality is a trend rather than a screenshot. The ad server page describes embedding the same core in Rust, Go, or Elixir, including an OTP port when the BEAM service must survive a bad response. The latency claim on that page is a measurement of the checker on stated tag sizes, not a promise about your whole bid. Read it as a budget inside the time you already spend, and measure your own tags.

In-process will not fetch a wrapper unless your code fetches it. Validating the first hop and forwarding a wrapper you did not open repeats the trafficker's mistake inside a service. If you unwrap, validate again. If you do not unwrap, do not claim you checked the inline.

An agent that sets up campaigns is a fourth seat. MCP is a tool call the agent can make, so the check is not a tab someone remembers. It is still only as good as the document or URL the agent passed. The ad ops MCP note is that workflow. It does not remove the wrapper walk.

What you should refuse to buy it for

None of the surfaces classify invalid traffic, score viewability, or decide brand suitability. A clean result is not a DoubleVerify report and not a Pixalate benchmark. None of them prove a television played the file. Device labs and the stitcher's own transcoder logs do that. Buying the embed because a slide said CTV fraud was 19 percent is a category error: the 19 percent is someone else's log, and the embed is a structural gate on your responses.

Evaluate with one hostile tag from your own traffic: a wrapper, an insecure media file, a missing verification node your buyers require, a VPAID file on a CTV fixture. Run it on the website so a non-engineer can see the result. Run the same file in CI so the build fails. If you are the service, run it in process on the hop you actually forward. If those three agree, you understand the install. If they do not, you are checking different documents and calling it one product.

Questions that pick the surface

  • At the moment we can still reject the tag, is it in a ticket, a git tree, or a running process?
  • Do we need the first body, or the inline at the end of the wrappers?
  • Should a warning fail the build, or only an error?
  • Are we promising a fraud number? If yes, this is the wrong tool.
  • Who debugs the reject? They get the website. The path that must not forget gets CI or the process.

Try the one-tag surface before you install anything

Paste XML or a live URL. If the result is the reject you wanted, the same rules are what CI and the in-process check run. Nothing is stored.

Paste the tag

XML opens the validator, and a live tag URL opens the tester.

Or test a live URL

Sources

GitHub Actions, GitLab, pre-commit, and Docker. vastlint check exits non-zero on errors.

Validate each fetched hop before you forward programmatic demand.

In-process checks for a service that already holds the response.

When the caller is an agent and the check has to be a tool call.

The one-tag seat that should stay on the website.

Related stories

All posts