TILLNOTARY · QUICKSTART

A verified receipt in five minutes.

GA

Create a log, mint a token, pin the keys once, then submit a file hash and hold a witness-cosigned receipt that verifies on your machine — online and fully offline.

01Create

Create a log in the dashboard

Go to /<org>/notary/logs in the dashboard and create a log. Pick a slug (it becomes part of the log’s permanent origin, e.g. notary.tilldev.dev/acme-releases) and a signature suite — the standard SHA-256 suite unless you need the government profile. The suite is fixed for the log’s lifetime, so choose deliberately. All fields are explained in Logs & entries. From a terminal: tilldev notary logs create acme-releases.

02Token

Mint a submit token

Go to /<org>/notary/tokens and mint a token with the submit scope. Tokens are shaped tnot_<32 hex> and scopes are strictly ordered: read < submit < admin. Give CI a submit token, not an admin one — submitting is the only thing a pipeline should be able to do. If your log is public, reads need no token at all. From a terminal: tilldev notary tokens create --name ci --scope submit --log acme-releases prints the token once.

03Pin trust

Pin the keys — once, carefully

Everything downstream — every verify, every receipt, every offline check — is verified against the keys in your local trust file. Pin them once:

bash
# One-time setup per machine.
npm i -g @tillstack/cli          # or: pnpm add -g @tillstack/cli

# Fetch the log and witness public keys and pin them in a local trust file.
tilldev notary trust-init acme-releases --out notary-trust.json
OK Pinned acme-releases into notary-trust.json
origin                         notary.tilldev.dev/acme-releases
log key fp                     1388 3e7f fc10 01f9 32c0 e322 13f6 aba7
witness tillnotary-witness/w1  29ac d643 062e 5a23 ed8c 7c94 ea41 bc43
required                       1
! TRUST ON FIRST USE: new keys came from the log itself. Verify the fingerprints out of band (dashboard, ops channel) before relying on this pin — a pinned lie stays a lie.
Verify the fingerprints out of band
trust-init fetches keys over the network, so on its own it only proves you talked to a server. Before relying on the pin, compare the printed fingerprints against a second channel — the log’s page in the dashboard, or a fingerprint your team published elsewhere. Once confirmed, the trust file is the anchor: from then on a substituted key or forged proof is detected, not obeyed.

The CLI finds your pin and token via --trust / --token flags or the TILLDEV_NOTARY_TRUST_FILE, TILLDEV_NOTARY_TOKEN, and TILLDEV_NOTARY_URL environment variables.

04Submit

Submit a hash, get a receipt

submit --file hashes the file locally with the log’s suite hash and sends only the digest. The response — leaf index, inclusion proof, cosigned checkpoint — is verified against your pin before the CLI prints a single line of it.

bash
# Hash the file locally and submit only the digest: the file never
# leaves your machine.
export TILLDEV_NOTARY_TOKEN=tnot_…        # the submit token you minted
export TILLDEV_NOTARY_TRUST_FILE=notary-trust.json

tilldev notary submit acme-releases --file release.tar.gz --out bundle.json
OK Logged at index 41 of notary.tilldev.dev/acme-releases — receipt VERIFIED locally
leaf          c53d57898e94df8d3fd05e41456bb17357992da9c36bab87edda8fe75f9acafb
origin        notary.tilldev.dev/acme-releases
tree size     42
root          d49832b2ac4e981bce2a74ea576ac48905fefd2639ec5e74652639b9b0f03e7d
timestamp     2026-09-27T07:03:41.478Z
cosignatures  tillnotary-witness/w1
status        COMPLETE (witness-cosigned)
OK Evidence bundle written to bundle.json

# Submitting the same value again returns the original receipt, not a new entry.
tilldev notary submit acme-releases --file release.tar.gz
OK Already logged at index 41 of notary.tilldev.dev/acme-releases — original receipt, VERIFIED locally

# Already have the digest? Submit it directly:
tilldev notary submit acme-releases \
  --hash 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824

Keep bundle.json with the artifact it notarizes. It is the portable, self-contained evidence: leaf, proof, checkpoint, cosignatures.

The log waits a few seconds for its witnesses before it answers, so a receipt is normally COMPLETE. If a witness is slow, the status reads incomplete — awaiting witness: the entry is logged and its proof is valid, and verify passes once the witness cosigns a head that includes it.

05Verify

Verify it — online, then offline

Re-check any entry against the live log:

bash
# Fetch the entry, its inclusion proof and the newest cosigned checkpoint,
# and check them all against your pinned keys.
tilldev notary verify acme-releases 41
OK VERIFIED — leaf 41 of notary.tilldev.dev/acme-releases
leaf           c53d57898e94df8d3fd05e41456bb17357992da9c36bab87edda8fe75f9acafb
content class  hash-only
origin         notary.tilldev.dev/acme-releases
tree size      43
root           2f45debf01c9c04b5004461162ae5bdd38d3bc68e8bec462e272651b2011bf66
timestamp      2026-09-27T07:03:42.215Z
cosignatures   tillnotary-witness/w1
status         COMPLETE (witness-cosigned)

# Lost the index? Look the entry up by its value:
tilldev notary lookup acme-releases --file release.tar.gz
OK FOUND and VERIFIED — leaf 41 of notary.tilldev.dev/acme-releases

And the stronger claim — the receipt stands on its own, with no server in the loop:

bash
# No network, no server, no token: the bundle and your trust file are enough.
tilldev notary verify bundle.json
OK VERIFIED (offline) — leaf 41 of notary.tilldev.dev/acme-releases
leaf          c53d57898e94df8d3fd05e41456bb17357992da9c36bab87edda8fe75f9acafb
origin        notary.tilldev.dev/acme-releases
tree size     42
root          d49832b2ac4e981bce2a74ea576ac48905fefd2639ec5e74652639b9b0f03e7d
timestamp     2026-09-27T07:03:41.478Z
cosignatures  tillnotary-witness/w1
status        COMPLETE (witness-cosigned)
Why offline matters
The moment you most need a notary receipt — an incident, a dispute, an audit — is exactly the moment you shouldn’t have to depend on any service being up or honest. The bundle plus your pinned trust file is sufficient evidence, forever.
06SDK

The same loop from Node

@tillstack/sdk-notary-node has the same posture as the CLI: verification is not a method you call, it is always on. Every response is proven against your pinned keys before it is returned.

ts
import { NotaryClient, parseTrustFile, trustFromFile, verifyEvidence } from '@tillstack/sdk-notary-node'
import { createHash } from 'node:crypto'
import { readFile, writeFile } from 'node:fs/promises'

// Keys YOU pinned with `tilldev notary trust-init` and verified out of band,
// never keys the server hands you at call time.
const logs = trustFromFile(parseTrustFile(await readFile('./notary-trust.json', 'utf8')))
const notary = new NotaryClient({
  baseUrl: 'https://notary.tilldev.dev',
  token: process.env.TILLDEV_NOTARY_TOKEN, // tnot_… with the submit scope
  logs,
})

const hash = createHash('sha256')
  .update(await readFile('./release.tar.gz'))
  .digest('hex')

// Verification is always on: the receipt is checked BEFORE it is returned.
// A lying server throws NotaryVerificationError — it never returns data.
const receipt = await notary.submit('acme-releases', hash)
console.log(receipt.leafIndex, receipt.checkpoint.treeSize)

// The receipt carries a portable evidence bundle; store it next to the artifact…
await writeFile('./release.evidence.json', JSON.stringify(receipt.evidence))

// …and anyone can verify it later, offline, with just the pinned keys:
verifyEvidence(receipt.evidence, logs['acme-releases'])
07Raw API

Straight HTTP, if you need it

The full REST surface is under https://notary.tilldev.dev/v1 (spec at /v1/openapi.json). Every error uses one envelope — {error, code, request_id} — and every response carries an x-request-id header to quote when you contact support.

bash
# Submit a hash (submit scope): 64 hex characters on a SHA-256 log, 96 on SHA-384.
curl -s https://notary.tilldev.dev/v1/logs/acme-releases/leaves \
  -H "authorization: Bearer $TILLDEV_NOTARY_TOKEN" \
  -H "content-type: application/json" \
  -d '{"leaf":"d7439bee24773bcbfa2d0a97947ee36227b10d1022b1a55847e928965bb6bfde"}'
# 201 for a new entry, 200 with "duplicate": true if the log already holds the value:
{
  "leaf_index": 43,
  "leaf": "d7439bee24773bcbfa2d0a97947ee36227b10d1022b1a55847e928965bb6bfde",
  "duplicate": false,
  "inclusion_proof": { "leaf_index": 43, "tree_size": 44, "path": ["07636ca8…", "ebf62045…", "40487d6d…", "a2ee9134…"] },
  "checkpoint": {
    "origin": "notary.tilldev.dev/acme-releases",
    "tree_size": 44,
    "root_hash": "a947f0647ece35bcbe53f1bd5fb925e380920c0be9db0855faab3cc022f66669",
    "timestamp": "2026-09-27T07:03:43.107Z",
    "suite": "sig-ed25519-mldsa65-sha256.v1",
    "key_name": "tillnotary-log/acme-releases",
    "signature": "eWZSL4fl…",
    "cosignatures": [
      { "witness": "tillnotary-witness/w1", "observed_at": 1790492623, "signature": "rD8ZqWe0…" }
    ],
    "complete": true
  },
  "complete": true
}

# An entry, a fresh inclusion proof and the best checkpoint (read scope; no token on a public log):
curl -s https://notary.tilldev.dev/v1/logs/acme-releases/leaves/43

# The newest witness-cosigned checkpoint:
curl -s https://notary.tilldev.dev/v1/logs/acme-releases/checkpoint
Raw HTTP means verification is your job
The API hands you leaves, proofs, and checkpoints — it cannot verify them for you on your behalf without defeating the point. If you consume the raw API, verify the proofs and signatures against your pinned keys yourself; otherwise you are simply trusting the server, which is exactly what TillNotary exists to make unnecessary. The CLI and SDK do this for you.
08Next

What's next

  • Logs & entries — log fields, hash-only vs public-attestation submissions, caps, and freezing.
  • Inclusion & consistency — what your receipt actually proves, and the periodic consistency check that catches tampering.
  • Run your own witness — require a cosignature from a key you hold, so no checkpoint completes without you.
  • Keep a mirror — a full copy of the log, every entry checked against the signed roots.
  • Overview — the trust model and where TillNotary fits.