Content Hash — Definition in NakedPnL's Verification
A content hash is the SHA-256 digest of a record's canonicalized contents. NakedPnL stores one per venue response so any later edit is detectable.
- A content hash is the SHA-256 digest of a record's canonical byte representation.
- NakedPnL computes it as SHA-256(canonicalize(rawResponse)) on every raw venue response.
- It is the input to the per-trader hash chain and, transitively, to the daily Bitcoin-anchored Merkle root.
Definition
A content hash is a deterministic cryptographic digest of the contents of a single record. In NakedPnL, the formula is exact: `contentHash = SHA-256(canonicalize(rawResponse))`. The `canonicalize` step serializes the raw venue response as RFC 8785 JCS (JSON Canonicalization Scheme) bytes so that the same logical object always produces the same byte string, and therefore the same digest, regardless of the order or whitespace the venue returned.
Why canonicalization is required
Two JSON documents can be semantically identical but textually different: keys in a different order, different whitespace, different number formatting. SHA-256 hashes bytes, not meaning, so two textually different but semantically identical inputs produce two different digests. Canonical JSON solves this by defining a single, deterministic byte representation. NakedPnL canonicalizes every raw venue response before hashing so that any verifier who fetches the same record can independently reproduce the same contentHash.
How NakedPnL uses it
Every snapshot fetched from a venue (Binance, Bybit, OKX, IBKR, Kalshi, or Polymarket) is stored alongside its contentHash in `lib/calculation/audit-hash.ts`. The contentHash is then fed into the chain link as `chainHash = SHA-256(previousChainHash + contentHash)`. The contentHash also appears in the row-level audit data emitted by the TWR engine (`lib/calculation/twr-engine.ts`) so that each individual TWR computation can be tied back to the specific raw response it was derived from. This is what makes the registry independently re-verifiable: a third party can re-fetch (where the venue allows it), re-canonicalize, re-hash, and check.
Worked example
import { createHash } from "node:crypto";
// Pseudocode canonicalize — a real RFC 8785 implementation should be used.
function canonicalize(value: unknown): string {
if (value === null || typeof value !== "object") return JSON.stringify(value);
if (Array.isArray(value)) return "[" + value.map(canonicalize).join(",") + "]";
const keys = Object.keys(value as object).sort();
return "{" + keys.map(k =>
JSON.stringify(k) + ":" + canonicalize((value as Record<string, unknown>)[k])
).join(",") + "}";
}
const rawResponse = {
account: "spot",
balances: [{ asset: "USDT", free: "10250.00" }],
updateTime: 1747008000000,
};
const canonical = canonicalize(rawResponse);
const contentHash = createHash("sha256").update(canonical).digest("hex");
// contentHash is identical no matter what key order the venue returned.What a contentHash does and does not prove
A contentHash proves that a specific record, byte for byte after canonicalization, was committed at a specific position in the chain. It does not prove that the venue's response was honest. If a venue returns wrong data, the contentHash faithfully records the wrong data. NakedPnL mitigates this with read-only API keys, server-side fetches, and, where available, on-chain reconciliation against subgraph data (for example for Polymarket).
Related terms
- Canonical JSON — RFC 8785 byte-stable serialization used inside contentHash.
- SHA-256 — the digest function used by contentHash.
- Hash chain — uses contentHash as the per-record input to chainHash.
- Net asset value — the field most often present in the rawResponse being hashed.