Wire contracts

gRPC for the bid path.

MCP is for agents. These services are for anything that already speaks HTTP/2 and has a latency budget: an SSP, a DSP, a stitcher, a sidecar. Same Rust engine as the CLI. You run the server. There is no hosted endpoint.

Self-hosted only

VASTlint and RTBlint MCP servers are hosted. The gRPC servers are not. Reflection and grpc.health.v1 are on, so grpcurl works without a local copy of the proto. Pixellint has no gRPC service yet.

Which interface

An agent checking its own output

MCP

Hosted HTTP for two of the three. Structured tool results a model can branch on.

A service on the bid or stitch path

gRPC

Unary call, deadlines, health for a load balancer. Fits inside a bid window.

A human, a script, or CI

CLI

Same verdict. Fewer moving parts.

The two packages

Hand-written proto, not generated from Rust types. A minor engine release must not force a client regenerate. buf breaking runs against main on every pull request.

VASTlint

openadtech.vastlint.v1

VastlintService · localhost:50051 · VASTLINT_GRPC_ADDR

ValidateOne VAST, VMAP, or DAAST document.
ValidateStreamBulk. Responses may arrive out of order; correlate on request_id.
FixDeterministic repairs. Separate RPC so validate permission is not repair permission.
ListRulesThe catalog. Versioned independently of this contract.

RTBlint

openadtech.rtblint.v1

RtblintService · localhost:50061 · RTBLINT_GRPC_ADDR

ValidateOne bid request or bid response. kind is required; the server will not guess.
ValidatePairA response against the request it answers.
ValidateArtfEnvelopeAn ARTF RTBRequest and the OpenRTB payloads it carries.
ValidateArtfMutationsAn ARTF mutation set against that envelope. apply rewrites and revalidates.
ListVersionsOpenRTB snapshots this binary knows, and the default.

ARTF. The two ValidateArtf* RPCs exist because the IAB Tech Lab Agentic Real Time Framework requires gRPC at the extension point. An orchestrator checks an agent on the same transport the agent was called over, before forwarding anything.

Run and call

Binaries are not on crates.io. Clone the tool repo, then from that checkout:

Start a server
cargo run --release -p vastlint-grpc   # localhost:50051
cargo run --release -p rtblint-grpc   # localhost:50061
VastlintService on localhost:50051
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
RtblintService on localhost:50061
grpcurl -plaintext -d '{"document":"{}","kind":"PAYLOAD_KIND_BID_REQUEST"}' \
  localhost:50061 openadtech.rtblint.v1.RtblintService/Validate
grpcurl -plaintext -d '{}' localhost:50061 grpc.health.v1.Health/Check

What v1 promises

  • Fields are added, never renumbered or reused. Removed numbers go into reserved.
  • A breaking change is openadtech.*.v2 served beside v1 through a deprecation window.
  • Rule ids are strings, not proto enums. The catalog moves faster than the contract. An id, once published, is permanent. Unknown ids in overrides are rejected, not ignored.
  • Every verdict carries Provenance: catalog version, catalog digest, engine version. A result without the ruleset behind it is not reproducible.
  • OpenRTB version is a string, not an enum. A dated 2.6 revision must not require a new wire contract. An unknown version is INVALID_ARGUMENT, not a silent fallback.