INTRO
meshcheck is a validation API for 3D assets. POST a GLB file and it returns a structured report: per-check verdicts across geometry, UV, materials, performance budgets and more, plus rendered screenshots, with an overall pass, warn or fail rollup instead of a score. It ships as a hosted API with billing and a versioned JSON schema, plus an MCP server, built for an agent or a pipeline to call directly rather than for a person to read one report at a time.
The decisions below are the ones with a real tradeoff behind them: a deterministic core that isolates the one non-deterministic feature into its own endpoint, a refusal to compute a single score, and a shared Rust-to-WASM core with a gate that compares the two builds' reports byte for byte across the whole test corpus.
SECTION
A deterministic core
The same input file and the same profile produce a byte-identical report once four fields are held fixed: the report id, its two timestamps, the storage URLs, and the timing block. The check runner takes a fixed identity and a zero-timing flag for exactly that, because those four are the only places wall-clock time and storage state get into the output. Most checks are pure measurement functions, reading their thresholds from versioned config rather than from a value hardcoded into the check itself; a handful, like the topology valence bands, still hardcode theirs. That guarantee has a cost: it rules out folding an AI vision pass, one that answers a question like "does this look like a barrel", into the same request as the deterministic checks, because a vision model does not reproduce its own output run to run. The vision endpoint is kept separate and optional, the one part of the system allowed to answer differently on a second call.

SECTION
No score, only per-check verdicts
A report returns a pass, warn or fail verdict for each check and an overall rollup, never a score out of 100. A single number hides which check failed, so meshcheck does not compute one. Checks are grouped into eight families, spec conformance, geometry, transform, UV, material, performance, render and topology, and each check carries a permanent ID such as UV-002 or SPEC-001, so a script or an agent can pin logic to a specific ID across schema versions. Retiring a check is a flag in the config rather than a deletion, which is what keeps the numbering from shifting. The rule against reusing a retired number is written down in the project conventions and nothing in the build enforces it.

SECTION
One Rust core, two runners
Checks are written once in Rust, in meshcheck-core, and that crate compiles to WebAssembly as the code path the hosted API calls from a Vercel function. The same crate also builds as a native binary, the local corpus and benchmark runner, so a local run and a hosted run apply the same check logic instead of two implementations that could drift apart. The two builds are not identical underneath: the native one carries rayon for parallelism and the WASM one is built without default features. That is why the parity gate compares their reports over the corpus rather than trusting that one crate implies one output. Thresholds and severities live in versioned TOML files under config/, not inside the check functions, so tuning a budget profile is a config change and a version bump, not a code change.
SECTION
A live report, not a mockup
These two screenshots are from an actual run: the site's drag-and-drop demo against a sample Duck.glb file, calling the real /v1/demo/validate endpoint and rendering the report it returned. That run came back WARN, with a table of real check verdicts (UV-002 warn, UV-004 warn, UV-005 warn, SPEC-001 skipped, and four checks passing) plus all six of the fixed camera angles rendered and watermarked by the API itself, three of which are shown here.


OUTCOME
Live at meshcheck.dev, with a hosted API running the Rust core compiled to WASM, a native binary built from the same Rust crate that runs the test corpus and the benchmarks locally, and an MCP server (npx meshcheck-mcp) that calls the hosted API rather than running checks itself. The schema is versioned so an agent can pin against a major version across releases.