API & SDK
Transparency log
Every credential Signata issues is appended as one entry in a single, global, append-only hash chain. That makes the set of issued credentials tamper-evident, and it lets provenance be recovered by content hash when a platform strips the embedded metadata (the soft binding).
What an entry commits to
Each entry wraps a record of the issued manifest. Only these fields are hashed into the chain. The canonical serialization is order-independent, so logically equal records always hash identically.
// The committed fields of a record. canonical(...) serializes them in a
// stable, order-independent form before hashing, so two equal records always
// produce the same recordHash.
interface LogRecord {
manifestId: string;
assetHash: string; // the content (hard-binding) hash, the recovery key
assetFormat: string;
format: "c2pa" | "signata";
signerName: string;
signerKeyId?: string;
signerThumbprint?: string;
aiGenerated: boolean;
aiEdited: boolean;
createdAt: string; // ISO timestamp
}The hashing scheme
It is a rolling hash chain: O(1) to append, O(n) to fully re-verify. The genesis link is 64 zero hex characters. Each entry hashes the previous entry’s hash together with the current record’s hash:
recordHash_n = SHA-256( canonical(record_n) )
entryHash_n = SHA-256( prevHash_n + ":" + recordHash_n )
prevHash_n = entryHash_{n-1} (prevHash_0 = GENESIS)
logRoot = entryHash of the last entry
GENESIS = "0" repeated 64 times (64 zero hex chars)The separator is a literal colon: entryHash = SHA-256(prevHash + ":" + recordHash). The logRoot is simply the last entry’s hash, a single value that fingerprints the entire log at a point in time.
How the links form
seq: 0 1 2 3
┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐
prev: GENESIS ──▶ entry₀ ──▶ entry₁ ──▶ entry₂ ──▶ …
│ rec₀ │ │ rec₁ │ │ rec₂ │ │ rec₃ │
└────────┘ └────────┘ └────────┘ └────────┘
entry₀ = H(GENESIS : H(rec₀))
entry₁ = H(entry₀ : H(rec₁))
entry₂ = H(entry₁ : H(rec₂)) logRoot = entry₃Why it is tamper-evident
Because each entryHash feeds the next one’s prevHash, the chain is a cascade. Mutating any past record changes its recordHash, which changes that entry’s hash, which no longer matches the next entry’s prevHash, and so on through every later entry, all the way to the logRoot.
Pinpointing the break
Anyone holding a previously published logRoot can detect a fork: if the log no longer reproduces that root for its prefix, it has been altered. This is the same append-only, hash-chained idea behind certificate transparency, scaled to a single workspace registry.
Soft-binding recovery
The assetHash in each record is the asset’s content (hard-binding) hash. When embedded metadata is stripped in transit, you can recover provenance by looking that hash up:
curl -sS https://app.signata.dev/api/v1/provenance/b1946ac9...d8e0f3a2 \
-H "x-signata-api-key: pk_live_xxx"A hit surfaces as recovered_via: "transparency_log" on a verification. Recovery is best-effort: it works only for credentials Signata issued, and only while the bytes still hash to the same value. A re-encoded or cropped copy hashes differently and will not match. That is the honest limit, not a bug. See the threat model.
Re-verifying the whole chain
GET /api/v1/transparency/verify recomputes every record and entry hash in seq order and checks the links. It returns whether the chain is valid, its length, the current logRoot, and the index of the first inconsistent entry if any.
{
"valid": true,
"length": 4128,
"root": "9f2c...001a",
"broken_at": null
}