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 is not an anti-fraud report, 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, 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.