Especificação dos formatos humanproof
8. O recibo de acesso humanproof-acesso/1
Mesma canonicalização, hash, assinatura e chaves das seções 3 a 5. Schema em humanproof-acesso-1.schema.json, também fechado.
| Membro | Conteúdo |
|---|---|
formato |
"humanproof-acesso/1" |
acesso_id, ato_id |
o acesso e o ato cuja evidência foi aberta |
tenant |
{ id, nome_publico } |
ator |
{ papel, id }, com id opaco, nunca nome nem e-mail |
motivo_hash |
hash do JCS da string do motivo, isto é, do texto entre aspas; o texto não entra |
componentes |
um por registro tocado: registro, registro_id e desfecho (aberto, apagado, nunca_houve, negado ou erro) |
desfecho |
do acesso: concedido, negado ou erro |
ts_acesso |
RFC 3339 em UTC, com Z |
prev_hash |
hash do acesso anterior do tenant, ou o gênesis de acessos |
kid, alg, assinatura |
seção 5 |
Papéis e prefixos do ator.id:
papel |
prefixo | quem |
|---|---|---|
cliente |
usuario: |
usuário do painel do tenant |
operador_hummand |
operador: |
operação da Hummand |
revisor_hummand |
revisor: |
revisão humana da Hummand |
integracao |
apikey: |
integração do tenant |
terceiro |
compartilhamento: |
quem abriu, sem conta, um dossiê que o cliente compartilhou por link; o id é o do compartilhamento, nunca o da pessoa |
Coerência que o verificador DEVE aplicar:
concedidoexige ao menos um componente e nenhumnegado.negadoexige todos os componentesnegado; a lista vazia é aceita, porque a recusa pode vir antes de tocar registro.erroadmite qualquer mistura.ator.papelconhecido eator.idcom o prefixo do papel, seguido de pelo menos um caractere.ts_acessoem RFC 3339 comZ;prev_hashemotivo_hashna forma de hash;kidnão vazio.
O recibo de acesso cobre a abertura pelo caminho oficial, que é o único que o emissor enxerga.
9. A cadeia
Cada tenant tem uma cadeia de atos e uma de acessos, independentes, cada uma com o próprio gênesis. Um lote é verificado assim:
- Agrupar os objetos por
(formato, tenant.id). Cada grupo é uma cadeia. Um verificador que recebe um arquivo como uma cadeia só DEVE recusar o que mistura tenants ou formatos. - Calcular o hash de cada objeto e ligar cada um ao objeto cujo hash é o seu
prev_hash. A ordem de entrada não importa. - A cadeia é contínua quando há exatamente um objeto cujo
prev_hashnão é hash de outro objeto do lote, e cada objeto tem no máximo um sucessor. - Ler o início: se esse primeiro objeto aponta para o gênesis do formato e do tenant, a cadeia está verificada desde o gênesis; se aponta para outro hash, é contínua dentro do lote, de origem externa, e DEVE ser apresentada assim, nunca como completa.
- Problemas que o verificador DEVE relatar: mais de um início (buraco no meio ou objeto de outra história), dois objetos com o mesmo
prev_hash(bifurcação), ciclo e objeto repetido.
A cadeia não promete ordem de tempo: um ato expirado conclui na data de expiração, que pode ser posterior à de atos emitidos depois dele.
O que a cadeia não acusa. Remover os últimos objetos deixa uma cadeia contínua desde o gênesis, e nada dentro do lote diz quantos deveriam existir. Para isso existe o ponto de conferência.
10. O ponto de conferência
humanproof-ponto/1, só da cadeia de atos:
{
"formato": "humanproof-ponto/1",
"tenant": { "id": "..." },
"total": 128,
"ultimo_hash": "sha256:...",
"ts": "2026-09-13T21:00:00Z",
"kid": "...", "alg": "ES256", "assinatura": "..."
}
humanproof-ponto/2, das duas cadeias, ambas obrigatórias:
{
"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 é quantos objetos a cadeia tinha até ultimo_hash, inclusive. Cadeia vazia tem total 0 e ultimo_hash igual ao gênesis daquela cadeia. Assinatura como na seção 5. O /1 continua aceito e não afirma nada sobre acessos: conferir acessos contra ele DEVE ser recusado, nunca aprovado por omissão.
Conferir um lote contra um ponto, por cadeia:
| Situação | Leitura |
|---|---|
| o ponto não confere (assinatura) ou é de outro tenant | contradiz |
total 0 e ultimo_hash igual ao gênesis |
confere: o ponto afirma que nada havia |
o lote contém ultimo_hash e, se começa no gênesis, na posição total |
confere |
o lote começa no gênesis e contém ultimo_hash em outra posição |
contradiz |
o lote começa no gênesis e não contém ultimo_hash |
contradiz: cortado, bifurcado ou reescrito, ou é anterior ao ponto |
o lote não começa no gênesis e não contém ultimo_hash |
inconclusivo: pode ser uma janela anterior ao ponto |
O verificador DEVERIA mostrar as duas contagens, a do lote e a do ponto, e não só a leitura.
O ponto vale pela distância no tempo. Um ponto pedido junto com o lote vem da mesma versão da história e não prova nada; o que prova é o ponto que o auditor guardou antes e compara depois.
11. Procedimento completo e vetores
Para cada objeto, nesta ordem, relatando cada etapa em separado:
- Ler como I-JSON, recusando nome duplicado, e identificar
formato. Formato desconhecido DEVE ser recusado. Identificar também a revisão (seção 16): semrevisao, revisão 1; comrevisao2, as regras das seções 16 a 20; outro valor DEVE ser recusado. - Validar contra o schema do formato.
- Selecionar a chave pelo
kide conferiralgcontra ela (seção 5). - Conferir a assinatura sobre o payload.
- Aplicar a coerência do formato (seções 7 e 8).
Um objeto é válido quando passa nas cinco. Para um lote, depois disso, verificar cada cadeia (seção 9) e, se houver ponto guardado, conferir cada cadeia contra ele (seção 10).
Os vetores acompanham este documento. Foram assinados com chaves de teste derivadas de rótulos públicos, sem valor fora dos testes: o escalar P-256 é o SHA-256 da string humanproof/1:chave-de-teste:p256 (a segunda, humanproof/1:chave-de-teste:p256:2), a semente Ed25519 é o SHA-256 de humanproof/1:chave-de-teste:ed25519, e as assinaturas são determinísticas (RFC 6979). Os arquivos de chave pública são jwks.json, jwks-rotacao.json e jwks-eddsa.json, e revisao-2/jwks.json para os vetores da revisão 2, com revisao-2/jwks-chave-vencida.json e revisao-2/jwks-chave-comprometida.json, que trazem a mesma chave com validade declarada e recusam o recibo válido.
indice.jsonlista cada vetor com a operação a aplicar e o resultado esperado.recibo: o objeto sozinho.cadeia: o array deve formar uma cadeia de um formato e um tenant.lote: o array agrupado por formato e tenant.ponto: o array conferido contra o ponto indicado.ancoragem: a prova conferida com o JWKS e a raiz confiável indicada emraizes.esperado.falhasdiz em que etapas a verificação falha (leitura, quando o texto não é I-JSON;schema,assinatura,coerencia,cadeia,ponto; na ancoragem,assinaturado ponto,caminhoecarimbo); vazio é vetor válido.esperado.validoé verdadeiro exatamente quandoesperado.falhasé vazio.esperado.inicioégenesisouexterno;esperado.pontoéconfere,inconclusivooucontradiz.canonicalizacao.json: pares de entrada e saída da seção 3.genesis.json: os gênesis dos tenants de teste e ummotivo_hashde exemplo.ancoragem/: as provas válidas, os carimbos de uma ACT de teste (RSA com SHA-512 e ECDSA P-256, emitidos pelo OpenSSL) e os certificados DER das raízes, a de teste e uma estranha.revisao-2/: os vetores da revisão 2 (seções 16 a 20), com a própriajwks.json: o recibo do ato, um recibo com duas assinaturas, um recibo adulterado, os inválidos da coerência nova (a dupla aprovação com o mesmo operador nas duas posições, uma extensão crítica desconhecida emcrit, um motivo do registro que não cabe no resultado) e um recibo da revisão 1 concluído depois da data de corte, o recibo de acesso, um lote conferido contra o ponto (lote.jsoneponto.json) e as provas de ancoragem de ponto e de recibo, com o carimbo e a cabeça que elas usam.
Uma implementação conforme dá, para cada entrada do índice, exatamente as falhas, o início e a leitura declarados. O mesmo vetor pode aparecer com operações diferentes: o lote cortado no fim é uma cadeia válida desde o gênesis e contradiz o ponto, e é isso que mostra para que o ponto existe.