Human Seal

Developers

Implement and verify the Human Seal

Built entirely on proven standards: Ed25519 signatures (RFC 8032), sha256 content binding (FIPS 180-4), and the W3C did:web trust root. Nothing here is a new cryptographic primitive.

The receipt

A Human Seal is a small JSON object. The fields below are signed; personal data never appears (the approver is a stable reference, never a name or email).

FieldMeaning
action_idStable identifier of the approved action.
action_typeThe class of action (used for the authority check).
content_hashsha256: of the exact canonical action. Binds the Seal to that action.
tenantThe organization whose model the approval was checked against.
approver_refStable identity reference of the approver. Never personal data.
decisionapproved or refused.
decided_atUTC timestamp (ISO 8601).
issuerThe did:web trust root, e.g. did:web:humanseal.world.
levelThe assurance level (2 = Signed).
oversightOptional (Level 2+): the decision conditions (context hash, decision latency, pre-AI decision, risk tier, rationale), so oversight quality is provable.
signature_ed25519Ed25519 signature over the canonical core. Excluded from the signed bytes.
public_keyThe verification key, base64url. Confirmable against the did document.

The verification algorithm

Small on purpose, so anyone can reimplement it. If any step fails, the Seal is invalid, and it fails in a way that names the reason.

  1. Remove signature_ed25519 and public_key; the rest is the signed core.
  2. Canonicalize the core: JSON with keys sorted and no insignificant whitespace.
  3. Verify the Ed25519 signature over those bytes against public_key.
  4. Independently resolve the key from the did document (did:web to https to /.well-known/did.json) and confirm it matches.
  5. If you have the action, recompute sha256 of its canonical form and confirm it equals content_hash.

Verify in the browser (client side)

// canonical JSON: keys sorted, no spaces (matches the issuer)
function canon(o){
  if(Array.isArray(o)) return '['+o.map(canon).join(',')+']';
  if(o&&typeof o==='object') return '{'+Object.keys(o).sort()
      .map(k=>JSON.stringify(k)+':'+canon(o[k])).join(',')+'}';
  return JSON.stringify(o);
}
async function verify(receipt){
  const core={}; for(const k in receipt)
    if(k!=='signature_ed25519'&&k!=='public_key') core[k]=receipt[k];
  const msg=new TextEncoder().encode(canon(core));
  const key=await crypto.subtle.importKey('raw', b64u(receipt.public_key),
      {name:'Ed25519'}, false, ['verify']);
  return crypto.subtle.verify({name:'Ed25519'}, key,
      b64u(receipt.signature_ed25519), msg);
}

Trust root and interoperability