Norcaster · Trust center

Trace verification algorithm

The checks the offline verifier performs, in order, and what each one does and does not prove. This is the same text the verifier ships with.

Terminology

Tamper-evident is the right word: the ledger is designed so that post-hoc edits break hash links or external anchors. It makes changes detectable; it does not make them impossible.

Chain construction

  1. Each tenant has a SHA-256 hash chain (org_trace_chain_entries).
  2. Entry hash: SHA256(prev_hash + "\n" + canonical_json(payload)) with sorted JSON keys.
  3. Genesis entry uses prev_hash = 64 × "0".
  4. Runs sealed after cutover receive a run_seal entry linked to the prior head.

Merkle segments

Anchoring batches entries into segments. Segment Merkle root is computed over entry hashes (binary SHA-256 tree, duplicate last leaf if odd).

External anchoring

Segment roots are published to Sigstore Rekor (primary) and optionally OpenTimestamps. Rekor provides a public, independently queryable inclusion record.

Offline verification steps (norcaster-verify)

  1. Bundle parse — parse the proof JSON (no field validation beyond JSON well-formedness).
  2. Local chain proof — Confirm chain_proof.valid and entry_hash present.
  3. Merkle path — Recompute root from merkle_proof + entry_hash; compare to anchor.segment_merkle_root.
  4. RekorGET the rekor_url (or --rekor-url override); parse entry body; confirm embedded digest equals segment_merkle_root. For signed bundles, also confirm the entry embeds the same public key as anchor_signer and the same signature bytes as anchor.anchor_signature (Rekor validated that signature on ingestion).
  5. Entry signature (bundle_version ≥ 2) — verify signature.signature as ES256 (ECDSA P-256, DER) over SHA256("norcaster-trace-entry-v1\n" + chain_id + "\n" + chain_position + "\n" + entry_hash) with signature.public_key_pem; confirm signature.key_id == sha256(public_key_pem)[:16]. Reported as verified, invalid, or unsigned — an absent signature is never a pass.
  6. Root signature — verify anchor.anchor_signature over the segment root with anchor_signer.public_key_pem. Norcaster signs the raw root digest (what Rekor requires); Node can only verify ECDSA over a message it hashes itself, so this step uses the root's preimage recovered from the Merkle path and is reported skipped_single_leaf when the segment has one entry (step 4's key check still applies).

No Norcaster credentials or API access is required for steps 1–3, 5 and 6. Step 4 uses only the public Rekor transparency log.

What a signature proves (and its limit)

The key is specific to one tenant and its public half is committed to the chain (a signing_key_activated entry) and to Rekor. In the current release Norcaster holds the private key, encrypted at rest: a valid signature proves the entry was signed with the key Norcaster holds for that tenant. It rules out forgery by any other party and makes rewrites detectable before anchoring; it does not remove Norcaster from the trust base until customer-held keys ship.

Trust boundaries

You trustYou get
Bundle file + Rekor public logSegment was committed to Rekor with matching Merkle root, by the tenant key when signed
Bundle file alone (v2)Entry signature verifies under the tenant public key the bundle carries; the key's on-chain activation is named so it can be checked against the ledger and its anchor
Norcaster hourly integrity jobTenant-wide chain continuity (dashboard status)
Norcaster API at download timeBundle was assembled from live DB state

For maximum independence, archive proof bundles and verify with this CLI after download.

← Trace integrity · How proof works → · Verifier source →