The problem
Two records are supposed to describe the same thing. A custodian's subledger and a transfer agent's register. A distributed ledger and an internal book. A fund administrator's shadow record and the administrator of record. Somebody has to establish whether they agree.
Reconciliation today means one party sends the other a file containing positions and holder identities. Sending it creates a data protection question, a competitive question, and an operational question about what happens to the copy afterward. Many pairs of institutions that should reconcile daily reconcile monthly, or by exception, because of what the exchange costs them.
When they do reconcile, the output is a spreadsheet produced by whichever side did the work. That side chose which fields to compare, chose the cut off times, and produced the verdict. The other side reads a verdict about its own book that it did not compute.
Name the parties. Operator A asserts its record is right. Operator B asserts the same. The party that ran the comparison asserts an outcome. An auditor asked to rely on the reconciliation asserts that a reconciliation was performed, which is a statement about process rather than about agreement. A regulator asking whether client assets are properly recorded gets all of the above.
The failure has a shape that is easy to miss. Two records observed at different moments will differ for reasons that have nothing to do with either being wrong. A transfer that settled between the two observations produces a difference that looks exactly like an error. Reconciliation processes handle this by hand waving about cut offs, and the hand waving is where the arguments live. If the comparison does not record how far apart the two observations were, every stale comparison is indistinguishable from a real break.
The second failure is that a match result is worth exactly as much as the honesty of whoever produced the digests. If one operator computes both sides, the comparison is one party talking to itself, and reconciliation processes rarely encode any requirement that two genuinely different parties attested.
The third failure is leakage in the artifact itself. Whatever record survives tends to carry a residue: a balance in a comment field, an account number in a record name, a wallet address in a configuration identifier. That residue is exactly what neither side agreed to share. When one or both books are maintained by autonomous systems, records update continuously, so a reconciliation instrument that cannot express its own staleness is useless.
What the receipt binds
The schema is at https://thehiveryiq.com/.well-known/schemas/ledger-parity-v1.json.
| Field | Meaning |
|---|---|
receipt_type | Fixed to ledger.parity. |
schema | Fixed to r1.0.0. |
parity_id | Identifier matching p_ plus a label, a unix seconds value and eight hex characters. |
tenant_id | The owning tenant. |
subject | What is compared: an uppercase instrument_label, an identifier_scheme from national numbering, internal registry, contract address, composite or undisclosed, an identifier_digest_sha256 and an optional asset_class. |
commitment | How the records were committed: a method, a function from hmac-sha256, hmac-sha512, kmac128 or kmac256, a commitment_key_id, a window_salt_digest_sha256, and an encoding_profile fixed to sorted-keys-no-whitespace-utf8. |
agreed_fields | One to 32 lowercase field names both sides committed over. |
ledger_a and ledger_b | The two records. Each carries name, record_kind, cursor_kind, cursor_value, state_digest_sha256, observed_at, attestor_key_id, attestor_sig_b64u and an attestor_algorithm fixed to ed25519. |
comparison | The outcome: window_tolerance_ms, observed_gap_ms, a result of match, divergence or window_exceeded, and a divergence object or null. |
attestors | The quorum: required and signed. |
anchor | The time reference: time_source, anchor_time and drift_bound_ms up to 60000. |
sequence | Chain position: prev_hash_sha256 and ledger_seq. |
evaluated_at | The UTC instant of evaluation. |
boundary | The fixed non attestation string. |
A divergence object carries field, a magnitude_basis from absolute count, absolute minor units, relative parts per million, set symmetric difference or not quantified, a magnitude_class from a fixed ladder running 0 through gt-1000000 plus unquantified, a direction of a_greater, b_greater or incomparable, and a boolean additional_fields_differ.
What is deliberately not in the signed body
No position, no balance, no holder identity, no account identifier. Each record appears as state_digest_sha256, a keyed digest over the declared agreed_fields under a commitment key that never appears. The window_salt_digest_sha256 commits to the salt for the comparison window without revealing it. The instrument identifier appears as a digest under a declared scheme, with a short uppercase label for human reference.
Magnitude is a bucket, never a number. A divergence reports which ladder rung the difference falls on and under what basis. The gate states the rule directly: a precise difference is never a permitted label. A receipt can say two books differ by something in the range 101 to 1000 minor units without saying either book's contents.
NO_RAW_STATE_LEAK enforces the absence. It requires every searched signed string to be free of a quantity, an account identifier and a ledger address, and requires every exempt digest field to hold its own hexadecimal form. The quantity pattern targets a grouped, fractional or labelled numeric value, which is how a balance reaches a free text field in practice.
The receipt envelope uses the shared construction in src/typed/canon.js: canonical JSON with keys sorted recursively, no whitespace, non ASCII escaped as lowercase \uXXXX, lowercase hex SHA-256 over the UTF-8 bytes, and Ed25519 over the ASCII string hive-receipt <receipt_id> <payload_sha256> <ts>.
The attestor signatures use a second signing string. For a slot, the text is
parity-attest <parity_id> <slot> <state_digest_sha256> <cursor_kind>:<cursor_value> <observed_at> <window_salt_digest_sha256>
joined with single spaces, where slot is the literal a or b. The slot letter stops a signature being moved between sides. The window salt digest stops replay into a later comparison. The state digest and the observed instant are inside the string, so neither can change after attestation.
The chain uses a fixed genesis value. GENESIS_PREV_HASH_SHA256 is the SHA-256 of the ASCII string ledger.parity:genesis, so a chain starting at position zero has exactly one correct previous hash.
The gates
Verification runs in this order and stops at the first failure.
- SCHEMA. Validates the envelope, including the fixed encoding profile and the closed enums. A failure means the artifact is malformed.
- ISSUER_KEY_MATCH. Requires the envelope key to resolve in the trusted keyring and to belong to this tenant. A failure means the signer is untrusted or belongs elsewhere.
- PAYLOAD_DIGEST, RECEIPT_ID, SIGNATURE and SIGNER_IDENTITY. The shared envelope layer. The canonical digest of
signed_bodyis recomputed againstpayload_sha256, the identifier has to parse and match the declared type, the Ed25519 signature has to verify, and SIGNER_IDENTITY fails when only a caller supplied key verified. - BOUNDARY_CONSTANT. Requires the signed boundary to equal the schema constant byte for byte. A failure means the non attestation was edited.
- LEDGER_DISTINCTNESS. Requires two differently named records observed at two different cursors. A failure means a record was compared against itself.
- CURSOR_BINDING. Requires each cursor kind to be permitted for its record kind and each cursor value to take the form that kind requires. A failure means the observation point is not a real observation point for that kind of record.
- AGREED_FIELDS_DECLARATION. Requires a non empty, unique, ascending, well formed field list that contains any named divergence field. A failure means the comparison basis is malformed or the reported divergence names a field nobody compared.
- ATTESTOR_KEY_DISTINCTNESS. Requires two registered attestor keys with different identifiers and different key material, each bound to the record it signs for. A failure means one operator produced both attestations, possibly under two identifiers.
- ATTESTOR_QUORUM. Requires exactly two record objects carrying a well formed signature, a
signedcount equal to that number, and arequiredvalue fixed at two for this schema version. A failure means one side did not attest or the counts were misstated. - ATTESTOR_SIGNATURE_VALID. Requires each slot signature to verify over that slot's own signing string, which no other slot and no other comparison reproduces. A failure means a signature was moved, replayed or made obsolete by a later edit.
- TIME_ANCHOR_DRIFT_BOUND. Requires a drift bound no wider than the tolerance and both observations near the declared anchor reading. A failure means the timing claim is looser than the comparison it supports.
- OBSERVATION_WINDOW_TOLERANCE. Requires the recorded gap to be the true gap between the two observation instants, and requires an over tolerance gap to be recorded as
window_exceededwith no divergence. A failure means staleness was understated or a stale pair was reported as decisive. - COMMITMENT_EQUALITY_RECOMPUTE. Requires two well formed committed digests and a recorded result equal to the result derived from them. The window test dominates the digest test, so a pair observed too far apart never reports match and never reports divergence. A failure means the stated result is not the derived one.
- DIVERGENCE_CONSISTENCY. Requires a described divergence for a divergence result and a null divergence for every other result. A failure means the outcome and its description disagree.
- MAGNITUDE_CLASS_ENUM. Requires a bucket label, a basis that admits that label, and an incomparable direction under any unordered basis. A failure means a precise difference was smuggled in as a label, or a direction was claimed where none exists.
- NO_RAW_STATE_LEAK. Requires every searched signed string to be free of a quantity, an account identifier and a ledger address, and every exempt digest to hold its own hexadecimal form. A failure names the leak.
- CHAIN_LINKAGE_AND_SEQUENCE. Requires the previous hash to be the recomputed digest of the supplied preceding entry, or the fixed genesis digest at position zero. A failure means the chain is broken or the predecessor was withheld.
The boundary
This receipt attests that two named records, each observed at a named cursor and at a named instant, and each committed by a distinct registered attestor key to a keyed digest computed over the same declared list of fields, produced the comparison outcome that this service recomputed from those two committed digests and those two instants against the declared window tolerance. It does not disclose any position, balance, holder identity, or account identifier. It does not attest that either committed digest is a correct digest of the record it names, because confirming that requires read access which this receipt does not confer. It does not decide which record is correct when the two records diverge, assigns no fault to either operator, does not decide whether the underlying settlement, transfer, or register update was proper, and does not effect or reverse any settlement. A window_exceeded outcome records only that the two observations were too far apart for the comparison to be decisive.
BOUNDARY_CONSTANT compares that text against the schema constant byte for byte.
The third sentence is the trade the whole instrument makes. Nobody had to give anybody read access, which is why this can run daily between institutions that would never exchange files. The price is that neither operator's digest is confirmed against its own record. Saying so is what makes the trade legible: both sides know they are relying on the other's honest commitment, and both know a divergence result is a signal to go look rather than a finding.
Refusing to say which record is correct is the second thing that makes this usable. A comparison instrument that assigned fault would be litigated by whichever side it went against, and no operator would agree to participate. A comparison instrument that only reports disagreement is one both sides can sign up to before knowing which way it will come out.
The window sentence closes the argument that otherwise never ends. Without it, every stale comparison becomes a fight about cut off times. With it, window_exceeded is a first class outcome that says plainly the two observations were too far apart to be decisive, and nobody gets to characterise it as either agreement or a break.
Adversarial cases
From test/ledger-parity.test.mjs.
One record compared against itself fails LEDGER_DISTINCTNESS, and an identical cursor pair on both sides fails too. Self reconciliation is the easiest clean match to produce.
A block height cursor value of latest fails CURSOR_BINDING, and a block hash cursor on a book entry register fails. A cursor has to name a fixed observation point of a kind that record kind actually has. A repeated compared field fails AGREED_FIELDS_DECLARATION, and a divergence naming an uncompared field fails.
Both sides signed by one attestor fails ATTESTOR_KEY_DISTINCTNESS. So does a second identifier of the same operator, because the gate compares resolved public key material rather than identifier strings. One counted signature fails ATTESTOR_QUORUM, and a required threshold other than two fails, since this schema version fixes the requirement at two.
Side A signature copied into side B fails ATTESTOR_SIGNATURE_VALID. The slot letter is inside the signing string. An attestation replayed from an earlier comparison fails the same gate, because the parity identifier and window salt digest are also inside it, and a digest changed after attestation fails since the state digest is covered.
A drift bound wider than the tolerance fails TIME_ANCHOR_DRIFT_BOUND, and an observation far from the anchor reading fails. Timing uncertainty cannot exceed the window the comparison relies on.
An understated observed gap fails OBSERVATION_WINDOW_TOLERANCE. This is the stale comparison dressed as a fresh one, and the gate recomputes the gap from the two instants. An over tolerance pair declared as a match fails, and a window_exceeded result carrying a divergence object fails.
Unequal digests declared as a match fails COMMITMENT_EQUALITY_RECOMPUTE, and equal digests declared as a divergence fails. The result is derived, never read. A divergence result with no description fails DIVERGENCE_CONSISTENCY.
An unquantified basis with a numeric class fails MAGNITUDE_CLASS_ENUM, and a set difference basis claiming a direction fails. You cannot claim precision your basis does not support, and you cannot claim one side is greater under an unordered basis.
An instrument label carrying a quantity fails NO_RAW_STATE_LEAK. So does a record name carrying an account identifier, and a commitment key identifier carrying a ledger address. All three are schema valid.
A previous hash that does not resolve fails CHAIN_LINKAGE_AND_SEQUENCE, a withheld preceding entry fails, and a genesis position carrying a foreign previous hash fails. A genesis position with the declared genesis digest verifies. A signed body mutated after signing fails PAYLOAD_DIGEST, and a shortened boundary text fails BOUNDARY_CONSTANT.
Three positive cases anchor the outcomes: a clean matching pair verifies, a genuine divergence with a valid basis and class pair verifies, and a stale pair with equal digests verifies as window_exceeded. That last one matters most. Equal digests are not enough. If the observations were too far apart, the honest answer is that the comparison was not decisive, and the instrument says so rather than claiming a match it did not earn.
Performance
From hive-verifier-api/benchmarks/results-latest.json, measured at 2026-08-09T22:28:01.099Z at commit e167719f7d815809c6f55c5dde4495edf1c5615f over 200 iterations:
| Measure | Value |
|---|---|
| Mint p50 | 7.698 ms |
| Mint p95 | 10.84 ms |
| Verify p50 | 5.686 ms |
| Verify p95 | 8.554 ms |
| Artifact size | 3299 bytes |
Local harness measurement on one machine and one Node version, recorded on Node v20.20.1, linux/x64, two Intel Xeon cores at 2.90 GHz. It is a reproducible measurement of this code, not a production latency guarantee. Verify here includes two attestor signature verifications on top of the envelope signature, which is where most of the cost sits.
How to run it
- Host:
https://thehiveryiq.com/v1 - Verify route:
/verify/ledger-parity, no credentials required - Mint route:
/mint/ledger/parity, requiresAuthorization: Bearer $HIVE_TYPED_MINT_TOKEN
curl -sS -X POST https://thehiveryiq.com/v1/verify/ledger-parity -H 'content-type: application/json' -d '{"receipt": <a receipt of this type>}'
Mint fails closed with 401 when the token is missing.
Where it sits in the canon
Family: settlement evidence. Category: typed receipt contract.
It chains to itself through sequence, so a running reconciliation between two books is a hash linked series with a fixed genesis rather than a folder of unrelated reports. Each entry commits to the recomputed digest of the entry before it. It does not replace either operator's books, a reconciliation platform, or an audit.
Status
production_deployed.