The humanproof/1 format
A signed receipt that carries only hashes and that a third party verifies offline, with standard cryptography and per-client chaining, is enough for a regulator to check an act without trusting Hummand.
- Method
- Specification of the format on top of RFC 8785, ECDSA P-256 and SHA-256; a single implementation in Rust used by the server, by the command-line verifier and by this page; fixed test vectors checked on every change.
- Source
- Hummand specification and implementation (private repositories); verifier at labs.hummand.com.br/en/verificar; public key at gateway.hummand.com.br/.well-known/humanproof/keys.json.
- Tags
- humanproof · receipt · verification · format
Why a receipt
Hummand answers whether there is a real human being on the other side of a digital act: whether they are present, whether they are the same person as the reference, what they authorized. That answer is only worth something if it can be checked by someone who is not Hummand: the client’s auditor, a regulator, an expert witness. That is why every finished act produces a signed receipt, exportable and verifiable outside Hummand. Positive, negative, expired, cancelled and even technical failure get a receipt; the absence of a receipt is the only invisible state, and the chain is what makes it detectable, within the limit described below.
What the receipt carries, and what it never carries
A humanproof/1 receipt is a JSON object with the identifier of the act and of the receipt itself, the client (tenant), the type of act, the level reached, the result and the reason, the steps completed with their timestamps, the sources used with component and version, the guarantees asserted as booleans or nulls, the consents by version and the policy applied. Everything that concerns the person enters as a hash: the reference the client gave to the subject, the reference image when there was one, the context of the act, the text of each consent.
Biometrics, images, templates and context in the clear never enter. The format is closed: an unknown field is a verification error, so there is nowhere to put such content. The subject’s reference is an HMAC with a per-client secret, because the plain hash of a national ID number can be reversed by enumeration.
Canonicalization
Before signing, the object is serialized in the canonical form of RFC 8785, with one restriction: numbers only as safe integers. The receipt has no floating point, and so the serialization is identical in any language. Keys ordered by UTF-16 code units, strings escaped as in JSON.stringify, no whitespace.
Signature and key
The signed payload is the canonical form of the receipt without the assinatura field; kid and alg live inside it, covered by the signature, and there is no algorithm negotiation. The algorithm in production is ECDSA P-256 with SHA-256 (ES256), with the private key in a managed key module it never leaves. The signature goes in base64url without padding over r || s, as in JWS. The public key is published as a JWKS, selectable by kid, at gateway.hummand.com.br/.well-known/humanproof/keys.json. The format admits Ed25519 as an option for a future key.
Hash and chain
The hash of the receipt is the SHA-256 of the complete canonical form, signature included. Each receipt carries in prev_hash the hash of the previous receipt of the same client; the first points to a genesis derived from the client identifier, which is in the receipt itself. An auditor with a batch rebuilds the order from the hashes, in any input order, and exposes a gap in the middle of the series, insertion, reordering and forking, without seeing anything from another client. A batch that does not start at genesis is labelled as externally sourced, and does not pass as a complete chain. The chain does not promise time order: an expired act concludes on its expiry date, which may be later than that of acts issued after it. The cryptographic destruction of an act’s data, foreseen in the retention policy, does not break the chain, because the receipt carries no biometrics, no image and no identifier in the clear.
Verification
To verify is to check the signature against the key of the declared kid, the format, the internal coherence and, in a batch, the chain. Coherence is what an auditor would apply by hand: a positive result cannot have a negative step; a presence level requires a presence step and the living-human guarantee; a level with civil identity requires the corresponding guarantee; hashes in the right shape; no step after the conclusion.
The implementation is single, in Rust, and runs in three places: on the server that signs, in the humanproof-verify command-line verifier, distributed to clients with a checksum, and on this page, compiled to WebAssembly. Verification on the page happens entirely in the browser; nothing is sent anywhere. The format’s test vectors are generated by the code itself and checked on every change: any alteration in canonicalization, hash or signature shows up as a difference.
Limits
The receipt asserts what the declared sources sustain, and nothing beyond that. Living presence comes from a certified third-party liveness detection component; civil identity, when present, comes from a plugged-in source, labelled in the receipt; the level is the one actually reached, not the one requested. The receipt does not estimate risk or probability, and does not replace the client’s process: it records, verifiably, what happened.
Two limits deserve a name. The first belongs to the chaining: an isolated chain exposes a gap in the middle, because the next link points at a hash that is not in the batch, and does not expose a cut at the end, because what remains is still continuous from genesis. Numbering does not solve it, since a sequence from 1 to N does not reveal that N+1 existed. What solves it is a checkpoint kept outside, with the last known hash and the count, compared later.
The second belongs to the source. Living presence is the verdict of a certified third-party component, decided on a probabilistic criterion with a threshold; the receipt records the verdict and the component version, not a score. The comparison against the reference applies a threshold defined by Hummand. And the receipt does not prove the legitimacy of the reference the client sent: a good comparison against the wrong reference is high confidence in the wrong person, and the reference is the client’s responsibility.
This is version 1 of the format. A change of format is a change of version; version 1 is not edited.
Addendum of 2026-09-18: the checkpoint exists
When this note was written, the checkpoint described above was what would solve the cut at the end, and it did not exist. It exists since 2026-09-13: the gateway issues it on request, as a signed humanproof-ponto/1 object, with the same key as the receipts, stating how many receipts that client had at the moment of the request and what the last hash was.
The mechanics are the ones the note predicted. What the addendum adds is the auditor’s side, and that is the part that decides whether the mechanism is worth anything: the checkpoint is useless kept by us. A batch presented later that does not contain that hash, or that holds fewer receipts since the genesis, contradicts a signed statement by Hummand; it is comparing the chain against a checkpoint kept outside that makes the cut detectable, not the chain. That is why there is no table of issued checkpoints on our side: the only copy that serves to check is the auditor’s, and a checkpoint requested at the same moment as the check proves nothing.
The command-line verifier compares a batch against a stored checkpoint. Verification on this page remains receipt and chain.
The limits section was not edited, and neither was the format: version 1 of humanproof/1 does not change, and the checkpoint is a sibling object, with its own format, rather than a new field in the receipt.
Addendum of 2026-10-29: three descriptions corrected
Three sentences in this note described the receipt in a way the issuer does not follow. The format did not change; the description did, and it now matches specification 1.4.
- The consent hash is not the hash of the text. Each consent’s
texto_hashis the hash of a consent identifier (purpose, scope and version), not of the text shown to the person. The receipt says which version of the text was accepted; the text of that version is requested from Hummand. - The version in
fontesis that of Hummand’s software. The receipt identifies the third-party component that decided presence, but the version recorded next to it is that of the Hummand software that called it, not that of the component. - The reference secret stays with Hummand. The reference the customer gives the person goes in as an HMAC with a per-customer secret kept by Hummand; only Hummand recomputes that hash. The receipt carries no identifier of the person in clear, but it does carry the customer’s and the act’s identifiers in clear, which are opaque.
Alongside, a fix in the issuer on the same day: a human review decides the act, not presence. The live-presence guarantee is now always the component’s verdict, and a review done by the customer through the integration now appears in the receipt.
Addendum of 2026-10-30: revision 2
Since 2026-10-30, at 05:21:39 UTC, Hummand issues the receipt in revision 2 of humanproof/1. The format is the same. The revision changes what the receipt states and how it is checked:
- Each statement says who backs it: the technical component, a human review, the customer’s statement or an official registry. A human review decides the act, never presence.
- The consent points to the text the person saw.
texto_hashbecomes the hash of the text itself, and the texts are published, with version and hash, at hummand.com.br/en/consentimentos. This supersedes the first correction in the addendum of 2026-10-29, which applied to revision 1. - The customer checks on their own whose receipt it is. The person’s reference goes in as a commitment, computed with a customer key and a random value per receipt. With the key and the reference, the customer recomputes and checks, without depending on Hummand. Without the key, the commitment does not link two receipts of the same person; when there was a comparison, the reference image hash is the same across receipts that used the same image.
- The receipt states the level the policy required and the level reached, and carries the hash of the policy applied.
- Signatures per role, so that a key change, or a new signature in the future, does not invalidate what was already issued. The format allows per-key validity; the current key has no declared term.
- Chained checkpoints and anchorings: each points to the previous one, and the list of anchorings is public. Anchoring is not yet active in production; the list exists and stays empty until then.
Receipts concluded up to the cut-off follow revision 1 and remain checkable with the earlier rules. After the cut-off, a receipt without the revision mark is rejected. The specification describes both revisions, and the verifier on this page reads both. It also checks a batch against a checkpoint and the anchoring proof, which the addendum of 2026-09-18 did not yet say.
One sentence in the limits section lost its first clause on this date, which denied the receipt a product category it does not belong to; the rest of the sentence stayed as it was.