Start free

Verdicts

Every verification result HESO can return, what it means in plain language, and which gate produces it. The verifier resolves at the first failing gate.

Verifying a receipt is a fixed sequence of checks. The verifier walks the gates in order and stops at the first one that fails, returning that gate’s verdict. If every gate passes, the verdict is valid. The same Rust core runs everywhere — Python, Node, and the browser all call it — so you get the same verdict no matter where you check. See Offline verification for the full walkthrough.

The gates

The verifier resolves at the first failing gate, in this order. Each gate maps to one verdict on failure.

  1. Algorithm recognized — the alg string is one this checker understands. On failure: wrong_algorithm.
  2. Version recognized — the action_version is one this checker supports. On failure: unsupported_version.
  3. Hash matches — the recomputed action_hash (BLAKE3 of the canonical bytes) equals the embedded value. On failure: hash_mismatch.
  4. Operator signature verifies— the operator’s Ed25519 signature checks out against its public key. On failure: invalid_signature.
  5. Approver signature verifies — if an approver co-signature is present, its Ed25519 signature checks out. On failure: invalid_approver.
  6. Redaction markers well-formed — every redaction marker is valid for its mode. On failure: redaction_malformed.
  7. Trust level re-derived — the trust level re-derived from which signatures actually passed equals the embedded trust_level. On failure: trust_mismatch.

When all seven gates pass, the verdict is valid. Note that trust is never trusted as written — it is re-derived from the signatures that verified. A receipt that claims L1 but carries only a passing operator signature fails at the last gate with trust_mismatch.

Verdict states

Every result the verifier can return, with the plain-language copy the console shows.

VerdictMeaning
validThe content matches its signature.
wrong_algorithmThe algorithm is not recognized, so this cannot be checked.
unsupported_versionThis version is newer than this checker understands.
hash_mismatchThe content changed after it was signed.
invalid_signatureThe content does not match the operator key.
invalid_approverThe approver co-signature does not check out.
redaction_malformedA redaction marker is not valid for its mode.
trust_mismatchThe receipt claims a higher trust level than its signatures support.
pendingUI only — the check is still running in the browser.
What a valid verdict proves — and what it doesn't

A valid verdict proves the bytes match their signature: the operator authorized this exact action under a known policy, and at L1 that a person approved it with a device-held key. It records what was authorized, not whether the action succeeded downstream.

Core outcome strings

The Rust core returns its own outcome strings. Each maps to one verdict state. Some carry a detail suffix after a colon — for example TrustLevelMismatch:embedded=L1,derived=L0 — so a failure tells you exactly what was claimed versus what the signatures supported.

Core outcomeVerdict state
Validvalid
WrongAlgorithmwrong_algorithm
Unsupportedunsupported_version
HashMismatchhash_mismatch
InvalidSignatureinvalid_signature
MalformedRedactionredaction_malformed
TrustLevelMismatchtrust_mismatch
MalformedA hard structural failure — the receipt could not be parsed.

The approver gate has no distinct core outcome string of its own; invalid_approver surfaces in the console as the verdict for a failed approver co-signature. The pending state never comes from the core — it exists only in the UI while the browser check is in flight.

Reading a failure

Because the verifier stops at the first failing gate, the verdict tells you more than one fact: it also tells you which gates already passed. A later verdict means every earlier gate succeeded.

For example, an invalid_signature verdict means the algorithm was recognized, the version was supported, and the action_hash recomputed correctly — the bytes are intact, but they were not signed by the operator key. A trust_mismatch verdict implies the most: every signature that was checked verified, the content is intact, and the only problem is that the receipt claimed a trust level higher than those passing signatures support.

This ordering is why a hash_mismatch never tells you anything about the signatures: the verifier stopped before it checked them. There is no point checking a signature over bytes that have already changed. Read the verdict, then treat the gates above it as proven. The full gate order, and why the math runs in your browser, lives in Offline verification.

Mapping verdicts back to the receipt

Each verdict points at a specific part of the artifact. Pair this page with the receipt schema to see exactly which field a gate reads — the alg string, the embedded action_hash, the signatures array, the redaction markers, and the embedded trust_level.

Next steps