Skip to content
Warrantv0.1

Build a custom verifier

Compose the eight clause kinds. There is no plugin API.

A "custom verifier" is a combination of the eight built-in clause kinds — the registry is closed, and a clause with any other kind is rejected at the API boundary.

{
  "clauses": [
    { "kind": "http.status", "equals": 200 },
    { "kind": "json.jsonpath", "path": "$.invoice.total", "equals": "84.00" },
    { "kind": "data.freshness", "maxAgeSeconds": 60 }
  ]
}

No plugin API

You cannot ship code that runs inside the engine. There is no webhook the engine calls to ask your opinion, and no way to register a ninth kind.

The closed registry is what makes a spec provable: spec_hash is meaningful because everyone evaluating it runs the same code. A plugin would make the hash a reference to whatever your server happened to return that day.

The eight kinds

kindFieldsPasses when
http.statusequalsThe status equals it exactly.
json.schemaschemaThe body validates against that named schema.
json.jsonpathpath, equals?The path resolves; if equals is given, it matches.
file.sha256digestThe bytes hash to that digest.
data.freshnessmaxAgeSecondsThe payload is no older than that.
uptime.windowwindowSeconds, minRatioComing soon. Reads a supplied uptimeRatio.
chain.eventtopicComing soon. Reads supplied emittedTopics.
llm.judgerubric, minScoreComing soon. Reads a supplied judgeScore.

Design the spec against the bench first

POST /bench/run evaluates clauses against a deliverable you describe. No key, nothing held, nothing recorded.

curl -sS https://api.gowarrant.xyz/bench/run \
  -H "Content-Type: application/json" \
  -d '{
    "clauses": [
      { "kind": "http.status", "equals": 200 },
      { "kind": "json.jsonpath", "path": "$.invoice.total", "equals": "84.00" }
    ],
    "deliverable": {
      "reached": true,
      "status": 200,
      "raw": "{\"invoice\":{\"total\":\"84.00\"}}"
    }
  }'

The deliverable is described, not fetched: reached, status, raw (up to 128 kB), generated_at, uptime_ratio, emitted_topics, judge_score. now overrides the clock so a freshness clause is reproducible.

Every clause is reported

The engine evaluates the whole list rather than stopping at the first failure. Three clauses where the second fails returns three details — you learn the status was fine, the schema matched, and the data was stale. That is the difference between a useful escalation and a shrug.

score is the share that passed. On the bench it is a fraction 0–1; everywhere it is stored it is basis points 0–10000. Half passing is 0.5 here and 5000 on an escalation.

Aim for clauses that fail informatively

Two clauses that each check one thing beat one clause that checks two, because the failure tells you which half broke. http.status plus data.freshness separates "the endpoint is down" from "the endpoint is serving yesterday's data", and those want different policy outcomes — the first is usually alwaysEscalate, the second is often a partial release.

Saved verifiers

verifier_id on a warrant references a saved verifier instead of inline clauses. Saved verifiers are created in the console; there is no POST /v1/verifiers. Most integrations never need one — clauses travel inline and are hashed into spec_hash either way.

Amounts

Clauses carry none, but the warrant does: an integer string of the smallest unit, ^[0-9]+$.

"8400000"    8.40 USDC
8400000      rejected — a JSON number
"8.40"       rejected — not the smallest unit