The launch journal

No `eval` at validation time.

31 July 2026 · the launch journal

JSON Schema validators in JavaScript are, under the hood, code generators. Ajv takes a schema and writes a function — a string of JavaScript compiled with new Function() — because interpreted schema-walking is an order of magnitude slower. That design is fine right up until you ask two questions a security reviewer always asks: what code is executing, and when was it decided?

Compile the schema at runtime and the answers are uncomfortable. The code that validates every inbound document is synthesized inside the production process, from whatever schema object happens to be in memory at that moment — a dynamic-code-execution surface that CSP policies and hardened runtimes exist to forbid. The Workers runtime this gateway targets forbids it outright: new Function() from strings throws EvalError: Code generation from strings disallowed. And even where it is allowed, you pay the compiler on every cold start, before the first request can be answered.

This gateway's answer is structural: validators are generated ahead of time, from the pinned schemas, into standalone eval-free code — and a freshness gate fails the build if the generated artifact drifts from the pins.

The pipeline

The generator is one auditable script, scripts/generate-validators.mjs, and its header states the policy:

Precompile the pinned GS1 schemas into a standalone ESM validator module.
Compiling here — at generate time, with the SAME Ajv engine and options the
tests use — keeps one validation semantics everywhere while shipping
eval-free code.

It reads exactly two inputs — vendor/gs1/epcis-json-schema.json and vendor/gs1/query-schema.json, the artefacts pinned by sha256 in the pins table — and emits src/gs1/validators.generated.js: plain functions, committed to the repository, containing no eval, no new Function(), and no require() (the script rewrites Ajv's runtime-helper requires into static imports and throws if any survive: "unrewritten require() left in generated validator module"). The Worker bundles those functions like any other module. At validation time there is no compiler in the building.

Two gates keep the generated artifact honest:

  • Freshness: a drift test regenerates the module from the pinned schemas and fails if the committed artifact differs — so the code that ships can never silently lag the schema on record. Change a pinned schema without regenerating, and CI stops you.
  • Provenance: the pins themselves change only by journaled ruling, the discipline described in the harness entry. Chain the two together and there is no moment where the schema in memory differs from the schema on record: pinned artefact → generated code → drift gate → the digest every validate --json result carries back to the caller.

The numbers

Measured on this repository, against the golden corpus, 2026-07-31 (Node 22, Apple silicon):

measurementresult
load precompiled validator module14 ms, once
validate the seven valid-standard corpus documents × 200 (1,400 validations)17 ms total
per-document validation~0.012 ms (12 µs)
Ajv runtime compile of the same pinned EPCIS schema, before the first validation73 ms

The comparison is the point. The full EPCIS 2.0.1 schema — five event types, the conditional "then" towers, the CBV value enumerations — costs 73 milliseconds to compile at runtime before any document is validated. Precompiled, that cost was paid at build time, and the marginal cost of a validation is twelve microseconds. In a serverless runtime that cold-starts per isolate, runtime compilation puts the compiler on the latency path of real requests; precompilation removes the compiler from production entirely.

The questionnaire lines this closes

Security reviews of a validation gateway reliably ask some version of: Does the service execute dynamically generated code? No — the runtime forbids it and the shipped validators contain none; the generation step runs at build, from pinned inputs, and its output is committed and diffable. How do you know the deployed validator matches the claimed schema version? The drift gate, plus the digest in every result: validate --json returns "schema": { "artefact": "epcis-json-schema.json", "version": "2.0.1", "sha256": "0f46ff69…" }, which a reviewer can check against GS1's published artefact at ref.gs1.org without trusting this origin at all. What is your schema update process? A pin change by journaled ruling, then regeneration, then the gate — three artifacts, all in the open repository.

None of this is exotic. It is the same move the estate makes everywhere: decide the normative input once, pin it, derive the runtime artifact mechanically, and gate the derivation — spec texts, validator code, and the corpus alike. The unusual part is only that it is written down where a reviewer can check it, which is the difference between a security posture and a security paragraph.

Validate something against it now — npx epcis.dev validate <file> runs the same generated functions the gateway runs, and the corpus is in the tarball if you want the twelve-microsecond version of a decade of committee work.

Sources: scripts/generate-validators.mjs and src/gs1/validators.generated.js (open repository); timings measured on this repo 2026-07-31; pinned artefact digests at /conformance/ and ref.gs1.org.

We answer in writing. We take at most five conversations a month, only when you ask for one, and only after you already have the written read.