humanproof format specification
8. The access receipt humanproof-acesso/1
Same canonicalization, hash, signature and keys as sections 3 to 5. Schema in humanproof-acesso-1.schema.json, also closed.
| Member | Content |
|---|---|
formato |
"humanproof-acesso/1" |
acesso_id, ato_id |
the access and the act whose evidence was opened |
tenant |
{ id, nome_publico } |
ator |
{ papel, id }, with an opaque id, never a name or e-mail |
motivo_hash |
hash of the JCS of the reason string, that is, of the quoted text; the text does not go in |
componentes |
one per record touched: registro, registro_id and desfecho (aberto, apagado, nunca_houve, negado or erro) |
desfecho |
of the access: concedido, negado or erro |
ts_acesso |
RFC 3339 in UTC, with Z |
prev_hash |
hash of the tenant’s previous access, or the accesses genesis |
kid, alg, assinatura |
section 5 |
Roles and ator.id prefixes:
papel |
prefix | who |
|---|---|---|
cliente |
usuario: |
user of the tenant’s dashboard |
operador_hummand |
operador: |
Hummand operations |
revisor_hummand |
revisor: |
Hummand human review |
integracao |
apikey: |
the tenant’s integration |
terceiro |
compartilhamento: |
whoever opened, without an account, a dossier the customer shared by link; the id is the share’s, never the person’s |
Coherence the verifier MUST apply:
concedidorequires at least one component and nonenegado.negadorequires every componentnegado; the empty list is accepted, because the refusal can come before any record is touched.erroaccepts any mix.- Known
ator.papelandator.idwith the role’s prefix, followed by at least one character. ts_acessoin RFC 3339 withZ;prev_hashandmotivo_hashin hash form; non-emptykid.
The access receipt covers opening through the official path, which is the only one the issuer sees.
9. The chain
Each tenant has an acts chain and an accesses chain, independent, each with its own genesis. A batch is verified like this:
- Group the objects by
(formato, tenant.id). Each group is a chain. A verifier that receives a file as a single chain MUST reject one that mixes tenants or formats. - Compute each object’s hash and link each one to the object whose hash is its
prev_hash. Input order does not matter. - The chain is continuous when there is exactly one object whose
prev_hashis not the hash of another object in the batch, and each object has at most one successor. - Read the start: if that first object points to the genesis of the format and tenant, the chain is verified from genesis; if it points to another hash, it is continuous within the batch, of external origin, and MUST be presented as such, never as complete.
- Problems the verifier MUST report: more than one start (a gap in the middle or an object from another history), two objects with the same
prev_hash(fork), cycle and repeated object.
The chain does not promise time order: an expired act concludes at its expiry date, which can be later than that of acts issued after it.
What the chain does not catch. Removing the last objects leaves a chain continuous from genesis, and nothing inside the batch says how many there should be. That is what the checkpoint is for.
10. The checkpoint
humanproof-ponto/1, acts chain only:
{
"formato": "humanproof-ponto/1",
"tenant": { "id": "..." },
"total": 128,
"ultimo_hash": "sha256:...",
"ts": "2026-09-13T21:00:00Z",
"kid": "...", "alg": "ES256", "assinatura": "..."
}
humanproof-ponto/2, both chains, both required:
{
"formato": "humanproof-ponto/2",
"tenant": { "id": "..." },
"cadeias": {
"atos": { "total": 128, "ultimo_hash": "sha256:..." },
"acessos": { "total": 7, "ultimo_hash": "sha256:..." }
},
"ts": "2026-09-24T21:00:00Z",
"kid": "...", "alg": "ES256", "assinatura": "..."
}
total is how many objects the chain had up to ultimo_hash, inclusive. An empty chain has total 0 and ultimo_hash equal to the genesis of that chain. Signature as in section 5. /1 remains accepted and states nothing about accesses: checking accesses against it MUST be refused, never approved by omission.
Checking a batch against a checkpoint, per chain:
| Situation | Reading |
|---|---|
| the checkpoint does not verify (signature) or belongs to another tenant | contradicts |
total 0 and ultimo_hash equal to the genesis |
matches: the checkpoint states there was nothing yet |
the batch contains ultimo_hash and, if it starts at genesis, at position total |
matches |
the batch starts at genesis and contains ultimo_hash at another position |
contradicts |
the batch starts at genesis and does not contain ultimo_hash |
contradicts: truncated, forked or rewritten, or earlier than the checkpoint |
the batch does not start at genesis and does not contain ultimo_hash |
inconclusive: it may be a window earlier than the checkpoint |
The verifier SHOULD show both counts, the batch’s and the checkpoint’s, not just the reading.
A checkpoint is worth the distance in time. A checkpoint requested together with the batch comes from the same version of history and proves nothing; what proves is the checkpoint the auditor kept before and compares later.
11. Full procedure and vectors
For each object, in this order, reporting each step separately:
- Read as I-JSON, rejecting duplicate names, and identify
formato. An unknown format MUST be rejected. Also identify the revision (section 16): withoutrevisao, revision 1; withrevisao2, the rules of sections 16 to 20; any other value MUST be rejected. - Validate against the format’s schema.
- Select the key by
kidand checkalgagainst it (section 5). - Check the signature over the payload.
- Apply the format’s coherence rules (sections 7 and 8).
An object is valid when it passes all five. For a batch, after that, verify each chain (section 9) and, if there is a kept checkpoint, check each chain against it (section 10).
The vectors come with this document. They were signed with test keys derived from public labels, worthless outside tests: the P-256 scalar is the SHA-256 of the string humanproof/1:chave-de-teste:p256 (the second one, humanproof/1:chave-de-teste:p256:2), the Ed25519 seed is the SHA-256 of humanproof/1:chave-de-teste:ed25519, and the signatures are deterministic (RFC 6979). The public key files are jwks.json, jwks-rotacao.json and jwks-eddsa.json, and revisao-2/jwks.json for the revision 2 vectors, with revisao-2/jwks-chave-vencida.json and revisao-2/jwks-chave-comprometida.json, which carry the same key with a declared validity and reject the valid receipt.
indice.jsonlists each vector with the operation to apply and the expected result.recibo: the object alone.cadeia: the array must form one chain of one format and one tenant.lote: the array grouped by format and tenant.ponto: the array checked against the checkpoint given.ancoragem: the proof checked with the JWKS and the trusted root given inraizes.esperado.falhassays at which steps verification fails (leitura, reading, when the text is not I-JSON;schema,assinatura,coerencia,cadeia,ponto; in anchoring, the checkpointassinatura,caminhoandcarimbo); empty means a valid vector.esperado.validois true exactly whenesperado.falhasis empty.esperado.inicioisgenesisorexterno;esperado.pontoisconfere(matches),inconclusivoorcontradiz(contradicts).canonicalizacao.json: the input and output pairs of section 3.genesis.json: the geneses of the test tenants and a samplemotivo_hash.ancoragem/: the valid proofs, timestamps from a test TSA (RSA with SHA-512 and ECDSA P-256, issued by OpenSSL), a full TSA response and the DER certificates of the roots, the test one and a foreign one.revisao-2/: the revision 2 vectors (sections 16 to 20), with their ownjwks.json: the act receipt, a receipt with two signatures, a tampered receipt, the invalid ones for the new coherence (the dual approval with the same operator in both positions, an unknown critical extension incrit, a registry reason that does not fit the result) and a revision 1 receipt concluded after the cutoff date, the access receipt, a batch checked against the checkpoint (lote.jsonandponto.json) and the checkpoint and receipt anchoring proofs, with the timestamp and the head they use.
A conforming implementation gives, for each index entry, exactly the failures, start and reading declared. The same vector can appear with different operations: the batch truncated at the end is a valid chain from genesis and contradicts the checkpoint, and that is what shows what the checkpoint is for.