VASTlint

vastlint-grpc

vastlint-grpc is the network surface of the same Rust catalog the CLI, WASM validator, and MCP server already use. It serves openadtech.vastlint.v1 over HTTP/2 with server reflection and grpc.health.v1. Image: aleksuix/vastlint-grpc:0.13.0, FROM scratch.

A validator that can only be called from a shell runs in CI and nowhere else. This is the transport for callers that need a verdict inside a request budget, on a language that cannot load the core in-process, or on a pipeline that already speaks gRPC.

Which surface to pick

CallerSurfaceWhy
Go / Rust / Python / ErlangIn-process (ad server guide)No network hop. Bid-path QPS.
JVM (Prebid Server Java, Spring, Vert.x)gRPC + vastlint-javaNo JNI on the event loop. Sidecar owns the catalog.
Chat / IDE agents (Claude, Cursor)MCPTool calling, hosted SSE at vastlint.org/mcp.
ARTF host, SSAI stitcher, bulk cataloggRPC Validate / ValidateStreamAlready on gRPC. Streaming for thousands of tags.
CI / pre-commitCLIExit code, no daemon.

Run it

Published image, no local Rust toolchain:

docker run --rm -p 50051:50051 -p 9090:9090 aleksuix/vastlint-grpc:0.13.0

Local build from the repo:

docker compose up grpc
# from https://github.com/aleksUIX/vastlint

Kubernetes starter: deploy/k8s/vastlint-grpc.yaml. Readiness and liveness are native gRPC probes on 50051. Metrics on 9090.

Reflection is on, so grpcurl needs no local proto:

grpcurl -plaintext localhost:50051 list
grpcurl -plaintext localhost:50051 describe openadtech.vastlint.v1.VastlintService
grpcurl -plaintext -d '{"document":"<VAST version=\"4.1\"></VAST>"}' \
  localhost:50051 openadtech.vastlint.v1.VastlintService/Validate
grpcurl -plaintext -d '{}' localhost:50051 grpc.health.v1.Health/Check

RPCs

RPCShapeUse
ValidateUnaryOne document. Bid, stitch, upload gate.
FixUnaryDeterministic repairs (HTTP to HTTPS, dropped deprecated attrs).
ListRulesUnaryThe 228-rule catalog. Cache it; it is static per binary.
ValidateStreamBidi streamBulk. Responses can arrive out of order; correlate on request_id.

Contract: proto/openadtech/vastlint/v1/vastlint.proto. buf breaking runs against main on every PR. Rule IDs are strings, not a proto enum, so a new rule is not a wire-contract change. Unknown IDs in rule_overrides return INVALID_ARGUMENT, not a silent no-op. Every verdict carries Provenance (catalog version, digest, engine).

Java

Go embeds the core through CGo. Java does not. A JNI load on Vert.x or Netty is a crash domain. vastlint-java is a blocking stub over this server.

implementation("io.openadtech:vastlint:0.13.0")

GitHub Packages: io.openadtech:vastlint:0.13.0. JitPack: com.github.aleksUIX:vastlint-java:v0.13.0. Setup: vastlint-java README.

try (VastlintClient client = VastlintClient.connect("localhost:50051")) {
    Verdict verdict = client.validate(xml);
    if (!verdict.getValid()) {
        // reject the bid, quarantine the creative, or return rule IDs upstream
    }
}

Default deadline is 5 seconds. Against a load balancer that terminates TLS, use VastlintClient.connectTls(target). The server itself speaks plaintext; put TLS on the ingress.

ARTF and agent pipelines

ARTF (Agentic Real Time Framework) puts a container beside an SSP or exchange and lets an agent mutate the OpenRTB auction object before any buyer sees it. Named uses: identity, deal and segment activation, fraud pre-impression. That hop is a different document from VAST. RTBlint checks the envelope and the applied OpenRTB. VASTlint does not.

After the auction clears, the creative still has to play. adm, a wrapper chain, an SSAI stitch, a trafficking upload: those are VAST documents. An agent that already speaks gRPC into the host can call VastlintService.Validate on that hop with the same catalog the trafficker used in CI. MCP stays the right interface for chat agents; gRPC is the right interface for the pipeline that already has a channel, a deadline, and a sidecar mesh.

ARTF host
  OpenRTB bid request  →  RTBlint gRPC  →  forward or drop
  VAST in adm / stitch →  vastlint-grpc  →  quarantine or serve

Chat / IDE agent
  validate_vast        →  vastlint-mcp (SSE or stdio)

Full agentic setup (MCP, AAMP Buyer SDK, AdCP): Agentic ad delivery.

Load behaviour

Adaptive concurrency (AIMD) sheds with RESOURCE_EXHAUSTED rather than queueing. Health and reflection are exempt, so a busy instance does not look dead to the load balancer. Client grpc-timeout is honoured. Streams are admitted per message, not per call: a long-lived ValidateStream must not occupy one concurrency slot for its whole lifetime.

Method, ramp, and SLO: LOAD-TEST.md. Operator notes: crate README.

See also