The problem
An agent is told to buy something. The instruction contains conditions. Some of those conditions can be checked against a receipt afterwards: the price, the merchant, the quantity, the delivery window. Others cannot be checked by anybody: the best one, something really nice, a good deal. When the purchase goes wrong, whether a payout happens turns on which kind of condition was broken.
Name the parties. The person who gave the instruction says the agent got it wrong and wants their money back. The agent operator says it followed what it was told. The party holding the loss says the instruction was too vague to have been broken in the first place, so nothing is payable. Everyone is arguing about a document that was written before the loss, using a standard that was applied after it.
The failure is that the standard has no artifact. Nothing anywhere records what counted as a checkable condition, or when that rule was written down. A refusal reason of subjective or non verifiable is a real reason and a fair one. It is also decided by the party whose money is at stake, after that money is at stake, against wording fixed before it. There is nothing for the other side to check the refusal against.
This is not primarily a bad faith problem. Classification rules genuinely get written after somebody sees a class of dispute they did not expect, because that is what makes the category visible. Then the rule is applied to instructions that predate it, and no artifact distinguishes that case from a rule published in advance. A dispute over the rule collapses into a dispute over two internal timestamps in two different systems.
There is a second failure, quieter and in the opposite direction. Nobody wants to hand over the instruction to prove anything about it. It is personal, it may name third parties, and it often reveals commercial intent the person would not publish. Any instrument that needs the text in order to be checked will not be used, so an instrument that wants adoption has to classify the instruction without reading it.
The third failure is silent widening. Suppose a classification is produced by walking a list of condition kinds and looking each one up. What happens to a kind the rule never mentioned? Left alone, an unrecognised kind falls into whichever bucket the code happens to default to, and in every incentive structure that bucket is the favourable one. An instruction full of conditions nobody has classified will present itself as fully checkable.
When the acting party is autonomous, all three get worse together. A human buyer negotiates each purchase and remembers what was said. An operator running a fleet holds thousands of standing instructions and nothing else, and the rule that will be used to judge them may not be written yet.
What the receipt binds
The schema is at https://thehiveryiq.com/.well-known/schemas/intent-verifiability-v1.json. The signed body has receipt_type, schema, the boundary, and one object, intent_verifiability.
| Field | Meaning |
|---|---|
assessment_id | Identifier matching iv_ plus 16 to 64 lowercase hex characters. |
assessed_at | The UTC instant this assessment was made. |
intent_ref.intent_id | The intent, as in_ plus 16 to 64 hex characters. |
intent_ref.commitment_sha256 | Digest of the opaque intent commitment object. |
intent_ref.fixed_at | The instant the intent was fixed. |
ruleset_ref.ruleset_id | The classification ruleset, as rs_ plus lowercase letters, digits or underscores. |
ruleset_ref.ruleset_digest | Digest of the ruleset object. |
ruleset_ref.committed_at | The instant the ruleset digest was committed. |
predicate_kind_set_digest | Digest over the sorted set of pseudonym and kind pairs. |
predicate_count | How many predicates the intent declared, 1 to 10000. |
machine_checkable_predicate_count | How many the ruleset called machine checkable. |
subjective_predicate_count | How many the ruleset called subjective. |
unclassified_predicate_count | How many kinds the ruleset never named. |
verifiability_class | One of machine_checkable, partially_checkable, subjective or unclassifiable. |
precedence_class | Either ruleset_precedes_intent or ruleset_follows_intent. |
evidence_access_class | One of confidential_independent, issuer_only or public_commitment_only. |
salt_commitment | Commitment to the 32 byte assessment salt. |
Note that the three class counts are separate integers and the total is separate again. The gate that checks them requires all four to agree, so an unclassified predicate cannot be hidden by shrinking the total.
What is deliberately not in the signed body
No intent text, no predicate identifier, no assessment salt, no ruleset content. What the receipt holds is two digests, a set digest, four counts and three classes.
Three constructions in src/typed/intent-verifiability.js do the work.
The salt commitment is a plain SHA-256 over the UTF-8 label intent.verifiability/salt/v1, a zero byte, and the raw 32 salt bytes.
The predicate pseudonym is HMAC-SHA256 keyed by the raw salt bytes, over a message built from the domain label intent.verifiability/predicate/v1 followed by a zero byte and then the canonical JSON of an object holding just the predicate identifier. Because the salt is per assessment, the same predicate identifier gives one stable pseudonym inside an assessment and an unrelated one under a different salt.
The predicate kind set digest is a canonical set digest under the label intent.verifiability/predicate-kind-set/v1. Each entry is a pseudonym and its declared kind. The entries are sorted by their canonical JSON bytes before hashing, so one set of predicates gives one digest regardless of the order the evidence arrived in. Canonical JSON here is the shared rule: keys sorted recursively, arrays kept in order, no whitespace, and every code unit above U+007F escaped as a lowercase \uXXXX sequence.
The intent commitment object is required to hold exactly intent_id, fixed_at and content_digest, and content_digest is required to be 64 lowercase hex characters. That is the enforcement point for the promise that the text never arrives: there is no field in the accepted object that could hold it, and the one field that references it must be a digest.
The classification is deterministic. Each declared kind is looked up in the ruleset. A kind the ruleset assigned to machine_checkable counts there, a kind it assigned to subjective counts there, and a kind it never named counts as unclassified. Any unclassified predicate makes the whole class unclassifiable. With none unclassified, no subjective gives machine_checkable, no machine checkable gives subjective, and a mix gives partially_checkable.
The receipt envelope uses the shared Ed25519 construction over the ASCII string hive-receipt <receipt_id> <payload_sha256> <ts>, with hive-receipt as the domain separation label.
The gates
Verification runs in this order and stops at the first failure.
- SCHEMA. Validates the envelope against the intent verifiability v1 schema, including the boundary constant and the closed class enums. A failure means the artifact is malformed.
- ISSUER_KEY_MATCH. Resolves
envelope.key_idin the trusted keyring, checks the key is authorised for this receipt type, and checks it was usable at the envelope timestamp. A failure means the signer is not trusted here. - PAYLOAD_DIGEST_VALID. Recomputes the canonical digest of
signed_bodyagainstpayload_sha256. A failure means the body was altered after signing. - SIGNATURE_VALID. Requires Ed25519 and verifies the signature over the signing string using the resolved key. A failure means that key did not produce it.
- ASSESSMENT_INSTANT_ORDERED. Requires
intent_ref.fixed_atandruleset_ref.committed_atboth to be at or beforeassessed_at, and all three to be valid UTC instants. A failure means the assessment claims to have looked at something that had not happened yet. - SALT_BINDING. Requires the disclosed salt to be 64 lowercase hex characters and to recompute to
salt_commitment. A failure means the salt does not open the published commitment, so the pseudonyms cannot be checked. - INTENT_REF_INTEGRITY. Requires the supplied intent commitment to hold exactly the three allowed fields in the allowed forms, to agree with the receipt on identifier and fixed instant, and to digest to
intent_ref.commitment_sha256. A failure means the receipt names an intent commitment that was not the one supplied. - RULESET_REF_INTEGRITY. Requires the supplied ruleset to hold exactly
ruleset_id,ruleset_version,committed_atand a non emptyclassificationsarray with no repeated kind, to agree with the receipt on identifier and committed instant, and to digest toruleset_ref.ruleset_digest. A failure means the classification rule is not the rule committed. - RULESET_PRECEDENCE. Recomputes
precedence_classfrom the ruleset committed instant against the intent fixed instant, and then fails outright when the recomputed class isruleset_follows_intent. A failure means the rule was fixed after the instruction, so no class can rest on it. - PREDICATE_KIND_SET_INTEGRITY. Requires a non empty predicate array with no repeated identifier, derives a pseudonym for each, compares the count to
predicate_count, and compares the recomputed set digest topredicate_kind_set_digest. A failure means the predicate set is not the set committed. - PREDICATE_CLASSIFICATION_RECOMPUTE. Recomputes all three class counts from the pseudonym and kind pairs against the ruleset, compares each one, and requires their sum to equal
predicate_count. A failure names the count that disagrees. - VERIFIABILITY_CLASS_RECOMPUTE. Derives the verifiability class from the three recomputed counts and compares it to the declared class. The declared value is never an input to the derivation.
- NO_INTENT_CONTENT_LEAK. Walks every string in the signed body against its approved opaque form: identifiers against their patterns, digests against 64 lowercase hex, classes against their enums. A failure means something readable reached a field that must carry only an identifier, a digest or a class.
- INTENTVERIFIABILITY_BOUNDARY_PRESENT. Requires the boundary text to equal the fixed string. A failure means the non attestation was edited or removed.
Mint refuses every field this list recomputes, including the two nested digests, and it refuses to sign at all when the ruleset was committed after the intent was fixed.
The boundary
This receipt attests only that a named observer recomputed a verifiability class from the declared predicate kinds of one committed intent, against a classification ruleset whose digest was committed at or before the intent was fixed. It does not attest that the intent was reasonable, that the Card Member understood it, that the declared predicate kinds are complete or honestly declared, that the predicates are the ones a court or regulator would consider material, that any claim is payable or deniable, or that any party is entitled to rely on the class. This service does not read the intent text and never receives it.
That string is a schema constant inside the signed bytes and it is checked again by its own gate.
The clause about the declared kinds not being complete or honestly declared is the one that costs something to write. The operator declares the predicate kinds. If it leaves one out, or labels a vague condition with a kind the ruleset treats as checkable, the receipt will classify what it was given and say nothing about what it was not given. That is a real limit and the instrument states it.
Stating it makes the receipt more useful when a dispute starts. The reader knows where to spend effort. Nobody has to re-derive the class, because the class is recomputed from the committed set and the committed rule. What is worth arguing about is whether the declared kinds describe the instruction faithfully, and that argument can happen with the arithmetic already settled.
The clause about payability does the same job in the other direction. A class of subjective is a fact about a rule applied to a declaration. Whether it means a claim fails is a question about a policy, and the receipt leaves that untouched so nobody can wave it around as a denial.
Adversarial cases
These are the real cases in test/intent-verifiability.test.mjs, which holds 38 tests, all passing.
A ruleset committed after the intent was fixed fails RULESET_PRECEDENCE. This is the rule written with the instruction already in view. The gate recomputes the precedence class and then refuses the receipt outright, rather than reporting a hindsight class and letting a reader miss it. Mint refuses the same case, so the receipt cannot be produced. A forged precedence class also fails this gate, because writing ruleset_precedes_intent over a late ruleset trips the recomputation first.
A hidden unclassified predicate fails PREDICATE_CLASSIFICATION_RECOMPUTE. This is the silent widening attack. Present a kind the ruleset never named and report it as checkable. The gate recomputes all three counts and requires them to sum to the declared total. A forged machine checkable count and a forged subjective count fail the same gate, so moving a predicate between buckets is caught too.
A forged verifiability class fails VERIFIABILITY_CLASS_RECOMPUTE. The simplest forgery is writing machine_checkable and hoping nobody re-derives it. The declared value is only ever compared against.
Intent text supplied in place of a digest fails INTENT_REF_INTEGRITY. The commitment object rejects anything in content_digest that is not 64 lowercase hex characters, so the field cannot be repurposed to carry the instruction. An edited intent commitment and a swapped intent identifier fail the same gate.
An edited classification row fails RULESET_REF_INTEGRITY, and a duplicated classification kind fails the same gate. A ruleset with two answers for one kind is refused rather than resolved by iteration order.
An added predicate, a repeated predicate identifier, a swapped predicate kind and a forged predicate count all fail PREDICATE_KIND_SET_INTEGRITY. The set digest and the count are both recomputed from the supplied evidence.
A substituted salt fails SALT_BINDING, which is the first attack on a pseudonym scheme. An intent fixed after the assessment and a ruleset committed after the assessment both fail ASSESSMENT_INSTANT_ORDERED.
Raw intent text in a signed field fails NO_INTENT_CONTENT_LEAK, and a boundary mismatch fails INTENTVERIFIABILITY_BOUNDARY_PRESENT. Both are named as defense in depth in the tests, which is accurate: the earlier gates should already have caught these.
Structural tests confirm the design. The minted envelope contains neither the salt, nor a predicate identifier, nor the intent content digest. Predicate pseudonyms never repeat across salts. The two classifiers are pure functions of their inputs. Mint refuses every computed field, a repeated predicate identifier, and an empty predicate list.
Performance
From hive-verifier-api/benchmarks/results-latest.json, measured at 2026-08-10T08:00:47.634Z at commit a22b4d88f6e041d6f593381b6e310a8d527d5b4b over 200 iterations:
| Measure | Value |
|---|---|
| Mint p50 | 2.792 ms |
| Mint p95 | 3.129 ms |
| Verify p50 | 1.999 ms |
| Verify p95 | 2.285 ms |
| Artifact size | 1929 bytes |
Local harness measurement on one machine and one Node version. The run recorded Node v20.20.1 on linux/x64 with two Intel Xeon cores at 2.90 GHz. It is a reproducible measurement of this code, not a production latency guarantee. These figures sit among the cheapest in the set, which follows from a verification path that verifies no nested receipt. The schema permits up to 10000 predicates and the classification is linear in that count, so a large intent will cost more than the benchmark fixture.
How to run it
This type has no mint route. It is produced as an export and only verified here, so there is nothing to authenticate against. The verify route takes no credentials.
The verify route is live on the production host. It is open, needs no account, and costs nothing to call.
- Verify route:
/verify/intent-verifiability - Demonstration route:
/demo/intent-verifiability, carrying the keysmachine_checkable,partially_checkable,subjectiveandunclassifiable
curl -sS -X POST https://thehiveryiq.com/v1/verify/intent-verifiability -H 'content-type: application/json' -d '{"receipt": <a receipt of this type>}'
Where it sits in the canon
Family: risk evidence. Category: typed receipt contract.
It sits before every other instrument in an agent purchase. The Intent Affirmation Receipt binds the summary a person affirmed to the summary that was transmitted. The Mandate Conformance Receipt compares one transaction to the authority in force. The Fault Attribution Receipt decides whose precommitment the observations broke. This receipt answers the question those three all assume an answer to: was the instruction the kind of thing that could be checked at all.
It composes with the Screening Attestation Receipt in shape, because both bind a ruleset by digest and both refuse to let the version be restated afterwards.
It does not replace the instruction record. If you want to know what was asked for, read the instruction. This receipt classifies declared kinds and nothing else.
It does not replace a policy decision. Patent pending.
Status
production_deployed.