Proof that history hasn't changed.
GATillNotary is an append-only Merkle transparency log and timestamping notary. You submit an opaque hash; you get back a signed inclusion proof and a witness-cosigned checkpoint — a receipt that this exact value existed at this exact moment, in a log whose history is tamper-evident: rewriting it breaks every proof already issued. Proofs verify on your machine against keys you pin, so you never have to take the server’s word for anything.
A receipt, not a promise
Every entry you submit becomes a leaf in a Merkle tree that only ever grows. Appending a leaf changes the tree’s root hash; the log signs each new state as a checkpoint (origin, tree size, root hash, timestamp). When you submit, you get back three things in one response:
- Your leaf’s position — the
leaf_indexin the log. An entry’s identity is (log, index), forever, and each value appears at most once per log, so the value finds its entry too. - An inclusion proof — a short chain of hashes showing your leaf is inside the tree with that root. See Inclusion & consistency.
- A signed checkpoint — the log’s signature over the new state. Witnesses add their cosignatures once they have checked it, usually on their next pass; strict verification waits for the threshold you pinned. See Checkpoints & signatures.
The receipt is self-contained. Store it next to the artifact it describes and anyone can verify it later — including fully offline, with no network and no live server.
You never trust the server
The trust model is deliberately blunt: the server is not trusted, it is checked. Three mechanisms make that real:
- Client-side verification against pinned keys. The CLI and SDK verify every proof and every signature locally, against public keys you pinned once (and confirmed out of band). A response that doesn’t verify is an error, not data — the SDK throws
NotaryVerificationErrorrather than return anything a lying server produced. - Consistency proofs. Given two tree sizes you’ve seen, the log must prove the newer tree is an append-only superset of the older one. If any past leaf were rewritten, some consistency proof fails — and there is no rewrite that passes it. Tampering isn’t prevented by decree; it is made detectable by anyone holding an earlier head.
- Witnesses. A witness holds its own key and cosigns a checkpoint only after verifying consistency with everything it cosigned before. New logs are cosigned by the platform witness; attach one you run, or one a party you trust runs, and require it, so no checkpoint completes without a key TillDev doesn’t hold. Presented with a rewritten history, a witness refuses to cosign and signs a refusal that anyone can check against its key. See Witnesses & refusals.
- Mirrors.
tilldev notary mirrorkeeps a full copy of a log and checks every entry against the signed roots. If the log ever shrinks, forks or rewrites, the mirror stops with an alarm and keeps the two conflicting signed heads as evidence. See the CLI.
Log, witnesses, mirrors
Submitters append; the log signs; witnesses cosign or refuse; mirrors and monitors check. Checkpoints are also published in a standard signed-note format (GET /v1/logs/{slug}/note), so anyone can exchange them — two parties who compare checkpoints will catch a log that shows different histories to different people (a split view).
you (submitter)
│ POST an opaque hash
▼
┌──────────────────────────────┐ signed checkpoints ┌─────────────────────┐
│ TillNotary log │ ──────────────────────▶ │ Witnesses │
│ append-only Merkle tree │ │ ours, plus yours │
│ hybrid-signed checkpoints │ ◀────────────────────── │ each with its key │
└──────────────┬───────────────┘ cosignature — or a └──────────┬──────────┘
│ signed REFUSAL │
▼ ▼
receipt: inclusion proof + mirrors and monitors re-check
signed checkpoint every head; two signed heads
│ that disagree are the evidence
▼
verified CLIENT-SIDE against keys YOU pin
(a lying server is detected, never obeyed)The REST surface lives at https://notary.tilldev.dev/v1 (machine-readable spec at /v1/openapi.json), with tokens shaped tnot_… and three scopes — read < submit < admin. Public logs serve reads with no token at all.
What to anchor
Anything whose history must be provable belongs in a transparency log. The common shapes:
| Use case | What you submit | What the receipt proves |
|---|---|---|
| Audit-chain anchoring | The rolling hash of your internal audit log, on a cadence | Your audit trail can’t be quietly rewritten after the fact — each anchor pins everything before it. |
| Release / build provenance | The hash of each artifact (or its signed attestation) | This exact binary existed at this time, before any incident — and was never swapped since. |
| Backup manifests | The hash of every backup manifest as it’s taken | A restore verifies against a manifest that provably predates the disaster. |
| Document timestamping | The hash of a contract, report, or dataset | Proof of existence at a point in time, without disclosing the document. |
Opaque by default, public by choice
By default, a leaf is an opaque hash — 64 hex characters on a SHA-256 log, 96 on a SHA-384 log. Your content never leaves your machine, and the log reveals nothing about it: not the filename, not the size, not the type. Anyone reading the log sees only that some value was committed at some time.
When you want the content itself to be public — a signed release attestation, a published policy — you can submit raw bytes as a public attestation. That path is a deliberate double opt-in (the bytes and an explicit content_class), because those bytes become publicly readable. Details in Logs & entries.
Hybrid signatures, fixed per log
Every checkpoint is hybrid-signed: a classical Ed25519 signature and a post-quantum ML-DSA signature, and both must verify. The checkpoint therefore holds as long as either algorithm survives — a break of one doesn’t forge history. A log’s suite is chosen at creation and fixed for its lifetime, so a proof from year one verifies identically in year ten.
| Suite | Hash | Signatures | Profile |
|---|---|---|---|
sig-ed25519-mldsa65-sha256.v1 | SHA-256 | Ed25519 + ML-DSA-65 | Standard — the default for most logs |
sig-ed25519-mldsa87-sha384.v1 | SHA-384 | Ed25519 + ML-DSA-87 | Government — CNSA 2.0 algorithm levels |
What's in the box
- Quickstart — create a log, pin trust, submit a hash, and hold a verified receipt in five minutes.
- Logs & entries — log fields and lifecycle, the two submission modes, leaf identity, caps and freezing.
- Inclusion & consistency — what the two proofs actually establish, how to verify them, and the heartbeat habit that catches tampering.
- Checkpoints & signatures — the signed head, hybrid signatures, cosignatures and the signed-note format.
- Witnesses & refusals — the platform witness, running your own, thresholds, and what a refusal means.
- Evidence bundles — the portable file that verifies offline, and how to hand one to an auditor.
- Service tokens — scopes, pinning a token to one log, expiry, and rate limits.
- CLI reference and Node SDK — every command and method, with real output.
Start with the Quickstart. TillNotary is part of TillDev — one workspace, one login, one audit log.