TILLNOTARY · CHECKPOINTS

Checkpoints & signatures.

GA

A checkpoint is the log’s signed statement of its own state: a tree head committing to exactly {origin, tree_size, root_hash, timestamp}. Everything you will ever verify — an inclusion proof, a consistency proof, an evidence bundle — is ultimately checked against a checkpoint, so this page is worth reading closely.

01The signed head

What a checkpoint commits to

Every time the tree grows, TillNotary signs a fresh head. The four committed fields are the whole contract: origin names which log is speaking (so a signature for one log can never be replayed against another), tree_size says how many leaves the statement covers, root_hash is the Merkle root over exactly those leaves, and timestamp is when the log stood behind it.

json
GET https://notary.tilldev.dev/v1/logs/acme-releases/checkpoint

{
  "origin": "notary.tilldev.dev/acme-releases",
  "tree_size": 4096,
  "root_hash": "96b66e36e50a3a61fc37f08bf11e12d2b6ea922068183a8d24e1ae613b98402d",
  "timestamp": "2026-09-27T09:14:02.118Z",
  "suite": "sig-ed25519-mldsa65-sha256.v1",
  "key_name": "tillnotary-log/acme-releases",
  "signature": "mBhb35vfRMRqJBGq…",
  "cosignatures": [
    { "witness": "tillnotary-witness/w1", "observed_at": 1790500443, "signature": "4LNgxgE5WTXeLCRK…" },
    { "witness": "acme-witness/primary", "observed_at": 1790500444, "signature": "vv0siERecad3wp+q…" }
  ],
  "complete": true
}

The remaining fields are how you check the commitment: suite names the signature algorithms, key_name tells you which pinned key to verify signature with, cosignatures carries witness attestations, and complete says whether the witness threshold has been met. Like every response from the API, it arrives with an x-request-id header you can quote to support.

02Signatures

Hybrid: Ed25519 + ML-DSA, and both must verify

Every checkpoint signature is hybrid: an Ed25519 signature and an ML-DSA (FIPS 204) signature over the same bytes, and a verifier accepts only when both check out. That means a checkpoint stays sound as long as either algorithm survives:

  • If a future cryptanalytic or quantum break lands on Ed25519, the ML-DSA half still binds the statement.
  • If ML-DSA turns out to have an undiscovered flaw — it is the younger algorithm — Ed25519 still binds it.

This matters because notarisation is a long-horizon promise. A receipt you export today may need to convince an auditor or a court in a decade, against whatever cryptanalysis exists then. A single-algorithm signature ages with its algorithm; a hybrid one ages with the stronger of the two.

03Suites

Two suites, fixed for the life of the log

A log declares its signature suite at creation, and every checkpoint carries the suite id so a verifier always knows exactly what it is checking:

Suite idHashSignaturesIntended for
sig-ed25519-mldsa65-sha256.v1SHA-256Ed25519 + ML-DSA-65Standard — the default for most logs
sig-ed25519-mldsa87-sha384.v1SHA-384Ed25519 + ML-DSA-87Government / regulated work, at CNSA 2.0 algorithm levels
The suite cannot be changed later
A log’s suite is fixed for life — changing it would re-hash history and break every proof already exported against it. If any of your evidence may ever need CNSA 2.0 algorithm levels, create the log on the government profile from day one. There is no upgrade path from one suite to the other; there is only creating a new log.
04Trust gating

Signed immediately, trustable when complete

The log signs a checkpoint the moment the tree grows — but a log vouching for itself is not the standard TillNotary asks you to accept. A checkpoint is only trustable once it carries the required number of witness cosignatures; the complete flag tells you whether that threshold has been met.

  • complete: false — the log has signed, witnesses have not yet; they cosign on their next pass. The CLI and SDK will not treat this head as trusted unless you explicitly opt in (--latest in the CLI, allowIncomplete in the SDK), and evidence bundles are only ever built from complete heads.
  • complete: true — the witness threshold is met. This is the head you verify against, ship in evidence bundles, and record as your last-known-good.
05Time

Timestamps are monotonic — and fail closed

Checkpoint timestamps are RFC 3339 UTC and monotonic: a later checkpoint never carries an earlier time. Witnesses independently enforce this — a timestamp that regresses or sits in the future is grounds for a refusal, not a shrug.

The flip side of a timestamp you can rely on is that the service refuses to sign one it cannot stand behind. If trusted time is momentarily unavailable, TillNotary fails closed rather than emit a false time, and a submit can transiently return:

http
HTTP/1.1 502 Bad Gateway
x-request-id: 9a4e1c7b-3f20-4d86-b5e9-72c0a8f13d65

{
  "error": "Cannot establish a trustworthy time; refusing to sign a checkpoint.",
  "code": "UPSTREAM_UNAVAILABLE",
  "request_id": "9a4e1c7b-3f20-4d86-b5e9-72c0a8f13d65"
}
Correct client behaviour: retry
A 502 UPSTREAM_UNAVAILABLE on submit is transient and safe to retry — the entry itself is never lost. The CLI and SDK retry for you. What you are seeing is the service preferring a short delay over a timestamp it could not defend later.
06Interop

The signed note format

Alongside the JSON checkpoint, every log serves its head in the compact signed-note format used across the transparency-log ecosystem, at GET /v1/logs/{slug}/note — origin line, decimal tree size, base64 root hash, the signed timestamp, then one signature line per signer:

text
GET https://notary.tilldev.dev/v1/logs/acme-releases/note

notary.tilldev.dev/acme-releases
4096
lrZuNuUKOmH8N/CL8R4S0rbqkiBoGDqNJOGuYTuYQC0=
timestamp=2026-09-27T09:14:02.118Z

— tillnotary-log/acme-releases zZiKjZgYW9+b30TE…   (log signature, hybrid)
— tillnotary-witness/w1 9crJlQAAAABquK0s…          (witness cosignature)
— acme-witness/primary yRqFRgAAAABquK0t…           (witness cosignature)

The note and the JSON checkpoint commit to the same head; the note exists so third-party transparency tooling can consume TillNotary logs without speaking our API. Fetch it with tilldev notary note <log> — see the CLI reference.


Next: Witnesses & refusals — why cosignatures exist and what a refusal means — or Evidence bundles, the portable artifact built on top of a complete checkpoint.