Start free

Redaction

Redaction keeps sensitive values out of the receipt and off our servers while still proving a field was present. HESO supports two modes: destructive and commit-and-reveal.

A receipt records the fields of an action so the verdict means something — you can see what amount was paid, which account was touched, which host was called. But some of those values should never leave your process: an API key, a member ID, a token. Redaction rewrites those fields before the receipt is signed, so HESO sees a commitment — a one-way fingerprint of the value — or nothing at all, yet the receipt still proves the field was part of the action.

Why redact

The fields on an action are part of the signed receipt, so they stick around, get replicated, and can be checked by anyone who holds the receipt. That is the point — but it is the wrong place for a secret. Redaction lets you keep the proof without keeping the cleartext.

  • You want a receipt for a vendor call but the call carried an api_key you must not persist.
  • You must record that a member record was changed without writing the member’s identifier into a log that outlives the action.
  • A reviewer needs to confirm a field existed and matched policy, but is not entitled to read its value.

Redaction always happens before signing. The values in action.fields on a finished receipt are the post-redaction values, and the action_hash covers exactly those — so the cleartext is never what gets signed, chained, or verified.

Destructive

In destructivemode the value is simply removed. The field still appears in the redaction record as a marker, but the marker’s commitment is the empty string — it is never omitted. There is no salt and no sidecar: the original is gone, and you cannot later reveal it. Use destructive mode when you never need the value back and only need to attest that the field was present.

receipt.json (excerpt)
"redaction": {  "mode": "destructive",  "markers": [    {      "field_path": "action.fields.member_id",      "algorithm": "blake3",      "commitment": ""    }  ]}
Note

An empty commitment is a real, well-formed marker — the verifier checks that markers are well-formed, and an empty string is the correct, expected value for destructive redaction. A missing commitment key is malformed and fails verification.

Commit-and-reveal

In commit-and-reveal mode the value is replaced by a BLAKE3 commitment — a 64-character lowercase hex digest hashed from the value plus a per-field salt(random bytes mixed in so the hash can’t be guessed). The salts are kept out of the receipt in a separate sidecar, held by an authorized party. Later, that party can take the original value, the salt, and the commitment, hash them again, and show the result matches — a reveal. A merkle_root over the markers ties them together, and it is present only in commit-and-reveal mode.

receipt.json (excerpt)
"redaction": {  "mode": "commit_and_reveal",  "markers": [    {      "field_path": "action.fields.api_key",      "algorithm": "blake3",      "commitment": "7d4a…b9f1"    }  ],  "merkle_root": "c1e8…02ab"}

The salt matters: without it, a small or predictable value (a member ID, a yes/no flag) could be recovered by guessing inputs and hashing each one. A fresh salt per field makes the commitment safe to publish while keeping the reveal exact.

The redaction record

When any field is redacted, the receipt’s content.redaction carries a RedactionRecord. It is part of the signed content, so the verifier checks its markers are well-formed as one of the verify gates.

mode"destructive" | "commit_and_reveal"required
Which redaction mode produced these markers.
markersRedactionMarker[]required
One marker per redacted field, in order.
merkle_rootstring
Merkle root tying the markers together. Present only for commit_and_reveal; omitted in destructive mode.

Each entry in markers is a RedactionMarker:

field_pathstringrequired
Dotted path to the redacted field, e.g. action.fields.member_id.
algorithm"blake3"required
The commitment hash function — always BLAKE3.
commitmentstringrequired
A 64-hex BLAKE3 digest in commit_and_reveal mode; the empty string in destructive mode. Never omitted in either mode.

Redacting in code

In Python the SDK does the work for you. Declaring redact=[…] on @heso.tool runs commit-and-reveal on those argument names automatically, before the receipt is signed.

agent.py
import heso heso.init() @heso.tool(redact=["api_key"])def call_vendor(api_key: str, endpoint: str) -> dict:    # api_key is committed (BLAKE3 + per-field salt) before the    # receipt is signed; the cleartext never reaches HESO.    return vendor.request(endpoint, key=api_key)

In Node you call the redaction primitives directly. Both take the fields as a JSON string and a list of field paths, and return the modified fields. redactCommitJs additionally takes one 32-byte Buffer salt per field and returns the record and the sidecar of salts. See the Node core reference for the full signatures.

redact.ts
import * as core from "@hesohq/core"import { randomBytes } from "node:crypto" const fields = JSON.stringify({ member_id: "m_4821", region: "eu" }) // Destructive: the value is gone — the marker commitment is "".const stripped = core.redactDestructiveJs(fields, ["member_id"]) // Commit-and-reveal: one 32-byte salt per redacted field.const salts = [randomBytes(32)]const out = core.redactCommitJs(fields, ["member_id"], salts)// out.fields  → fields JSON with the value replaced by a commitment// out.redactionRecord → the RedactionRecord to embed in the receipt// out.sidecar → the salts an authorized party keeps to later reveal

What stays private

Because redaction runs before signing, the cleartext never reaches HESO — not the API, not the audit chain, not a reviewer’s browser. What the receipt carries is a commitment (or, in destructive mode, nothing) plus a marker proving the field was part of the action. The reveal, when it happens, is between you and whoever holds the sidecar of salts.

What redaction proves — and what it doesn't

A redaction marker proves the field was present in the action and was redacted under a known mode. In commit-and-reveal it also lets an authorized holder later prove a specific value matches the commitment.

It does not reveal the value to anyone without the sidecar, and in destructive mode the value is unrecoverable by design — HESO never held it. As with every receipt, this records what was authorized — the redacted field included — not whether the action succeeded downstream.