Signed audit anchor
A signed audit anchor is a per-length audit-chain commitment carrying a cryptographic signature, so a client verifies the chain's integrity against a pinned public key instead of trusting the serving API. Caisson's audit-worm package signs every anchor at mint with a dedicated Ed25519 key (domain-separated from the license-issuer key) stored alongside the existing WORM anchor.
In code
const entries = await loadEntries(tx, accountId);
const anchor = anchorChain(entries);
// Sign the anchor's CANONICAL CORE bytes at mint when a signer is configured.
// `sig`+`keyId` are stored ALONGSIDE the core (additive optional fields), so legacy unsigned
// anchors stay structurally valid and the signed core stays byte-identical to the unsigned form.
let anchorToStore: AuditChainAnchor = anchor;
if (this.signer !== undefined) {
const sigBytes = await this.signer.sign(encodeAnchor(anchor));
anchorToStore = {
...anchor,
sig: Buffer.from(sigBytes).toString("base64"),
keyId: this.signer.keyId,
};
}
// The trusted commitment lands in WORM under a LENGTH-keyed, write-once key.
await this.store.put(
anchorKey(accountId, anchor.length),
encodeStoredAnchor(anchorToStore),
{ retainUntil, contentType: "application/json" },
);How it holds
A dedicated key, domain-separated from the license issuer
The anchor-signing identity is a separate Ed25519 keypair from the license-issuer key, loaded from its own env var and held as an opaque KeyObject that never enumerates, logs, or JSON-serializes: an anchor-key compromise can't forge a license, and rotating the license key can't invalidate anchor-verification history.
Signed additively, legacy anchors stay valid
sig and keyId are stored alongside the existing {length, tipHash} core as optional fields, never a chain-format break: an anchor minted before a signer was configured stays structurally valid, and the signed core is byte-identical to the unsigned form.
Verified against a pinned key, never the serving API
The client and offline pack verifier check the signature with WebCrypto against a public key baked into the bundle out-of-band (never read from the row response) so a compromised or malicious API can forge a self-consistent payload/hash/anchor triple but can't forge a signature that verifies against that pinned key.
Fail-safe on 'can't check', never on 'didn't check'
A missing signature, no pinned key, or a keyId mismatch resolves to na, not fail, an unchecked signature never earns the tamper flag. Only a signature that positively fails to verify against the pinned key classifies the row tampered, the same fail-closed direction as the other two verification legs.