Expand description
The cheapest deterministic check there is: does this output match its declared shape?
A step that promises { "severity": "high" | "low", "files": [string] } and
delivers prose has failed, and it has failed in a way that no downstream
step can recover from — it will either crash on deserialisation or, worse,
silently read a field that is not there. Catching it here costs microseconds
and needs no tools, no sandbox, and no model.
§A subset, and unknown keywords are refused
This validates a documented subset of JSON Schema, listed on
SUPPORTED_KEYWORDS. Anything outside it — $ref, pattern, format,
if/then/else — makes Schema::compile fail.
Refusing is the whole design, and the alternative is worse than it sounds.
A validator that ignored the keywords it did not implement would hand a
schema author a Pass on a payload their schema said was invalid, and
nothing anywhere would report that a constraint had been skipped. That is a
silent downgrade of a security property, which is the failure mode Goal G5
exists to prevent. A build error naming the keyword is a cheap failure; a
check that quietly stopped checking is not.
§Why it cannot hang
Two independent bounds.
The schema is a finite tree. $ref is not supported, so a schema cannot
refer to itself, and schema depth is capped at compile time
(MAX_DEPTH). That is a stronger property than it first looks: validation
descends only where the schema descends, so recursion is bounded by the
schema and not by the instance. A payload nested a thousand levels deep
against a two-level schema is walked two levels deep.
Validation has a step budget. A finite schema still multiplies work:
oneOf re-validates the same subtree once per alternative, and against a
large array that compounds. So every comparison decrements a counter, and
running out is Verdict::Inconclusive —
never Pass, and never Fail either, because “we ran out of budget” is not
the same claim as “your payload is wrong”.
What this module does not defend against is a payload so deeply nested
that operating on it at all overflows the stack — enum and uniqueItems
compare values structurally, and comparison recurses over the instance. That
hazard is real but it is not created here and cannot be fixed here: by the
time a Condensate exists, something has already built the value, and
whatever built it would have overflowed first. Capping payload depth belongs
where payloads are produced.
§What a Pass actually guarantees
That the payload was representable as JSON and satisfied the schema. Both halves matter: the checker converts the condensate’s CBOR payload into JSON before validating, so a payload that could not survive that trip fails here rather than three steps later. A downstream step deserialising into a type that matches the schema will therefore succeed.
Structs§
- Schema
- A compiled schema: a finite tree of constraints, plus its content address.
- Schema
Checker - Rejects a condensate payload that does not match a declared schema.
- Violation
- One way in which a value did not match its schema.
Enums§
- Schema
Error - A schema could not be compiled.
- Validation
- What validating a value produced.
Constants§
- DEFAULT_
BUDGET - How many comparisons a validation may make before giving up.
- MAX_
DEPTH - How deeply a schema may nest.
- SUPPORTED_
KEYWORDS - The keywords this validator implements.