# Verification hashes

> The normalization, SHA-256, evidence metadata, fingerprint, and blockchain rules used to verify Agreebase artifacts.

Audiences: developers, agents, users
Kind: reference
Updated: 2026-07-12

Agreebase uses SHA-256 hashes to make record and content identity independently checkable. The correct input depends on the record type.

## Ordinary agreement, declaration, or witnessed record body hash

Normalize the body in this order:

1. Unicode NFC normalization.
2. Replace CRLF and CR with LF.
3. Strip leading and trailing whitespace.
4. Encode as UTF-8.
5. Compute SHA-256 as lowercase hexadecimal.

Pseudocode:

```text
text = unicode_nfc(raw_body)
text = replace(text, "\\r\\n", "\\n")
text = replace(text, "\\r", "\\n")
text = strip(text)
body_hash = sha256_hex(utf8(text))
```

The database commonly stores the digest without a prefix. Evidence metadata and blockchain records use `sha256:<64 lowercase hex characters>`.

## Notarization content hash

For a file notarization, hash the exact file bytes. Do not hash its filename, rendered PDF appearance, or metadata.

For a text notarization, apply the same NFC/LF/trim/UTF-8 process before hashing. The server checks that the submitted digest matches the normalized text.

For a hash notarization, the caller supplies an existing SHA-256 digest. The server does not infer or reconstruct the source content.

Notarizations do not use the agreement fuzzy fingerprint.

## Evidence metadata hash

At seal time, Agreebase builds an `agreebase.evidence_metadata.v1` package. It contains stable record facts, the body/content hash, completed current-version participants, redacted and scoped contact evidence, and signature-method evidence. A notarization package also includes its notarization payload and content hash.

To compute `metadata_hash`:

1. Copy the metadata object.
2. Remove `metadata_hash` if present.
3. Serialize JSON with keys sorted at every level, compact `,` and `:` separators, and UTF-8 output.
4. Compute SHA-256.
5. Compare `sha256:<digest>` with the package and blockchain value.

The metadata hash is separate from the signed PDF/document hash.

## Fuzzy fingerprints

An exact body hash only matches text that's identical down to the last character. That's the right check most of the time, but it means a single retyped word, a copy-paste that dropped a line break, or a PDF-to-text export that mangled some whitespace will fail to match even though the substance is clearly the same document. The fuzzy fingerprint exists for that situation: a secondary, near-complete-text match that can succeed where the exact hash can't.

It works by treating the normalized body as a set of overlapping short character sequences, encoding them alongside a large number of decoy values so the stored fingerprint reveals nothing about the text itself, and requiring a high proportion of a candidate's sequences to line up with the stored ones before it counts as a match. That threshold is intentionally strict; it tolerates small, incidental differences, not a different document that happens to share some wording.

Some ordinary agreement, declaration, and witnessed-record bodies carry a fuzzy fingerprint; notarizations do not use one at all, since a notarization's whole purpose is proving identity of exact content. A fuzzy fingerprint match is always reported as a fuzzy match, never as an exact one; treat it as "this is very likely the same text," not as cryptographic proof that it is.

## Blockchain record format

Ordinary hash-only records contain the AGR reference, body hash, metadata hash, and completion time. Full-text records contain the selected body text instead of the body-hash line.

Notarization records contain the AGR reference, content hash, content type, hash algorithm, selected advisory metadata, metadata hash, and completion time. A text notarization can include normalized text only when public text publication was enabled.

## Independent verification

A verifier can use the public verifier, a retained PDF/email, the evidence metadata package, and the transaction hash. For hash-only ordinary records, the verifier needs candidate text. For file notarizations, the verifier needs the original file bytes or its independently computed digest.

See [Comparing document hashes](/docs/users/comparing-document-hashes) for a user-oriented procedure.
