notary #
A content notary built on the AT Protocol.
notary timestamps the digest of any content into your atproto repository. Since
every atproto commit is signed by your identity key, a stamp becomes a
tamper-evident, publicly verifiable attestation that you asserted "this
content existed" at a given time — and anyone can check it without trusting the
PDS that serves it.
You never upload the content itself, only its sha256 digest. The thing you
notarize stays private; only the proof is public.
$ notary stamp contract.pdf
notarized
digest : sha256:b9dc3966…fadf0
subject: contract.pdf
uri : at://did:plc:zzk6…/coffee.a-cup-of.notary.stamp/3mt37oktj522n
$ notary verify contract.pdf at://did:plc:zzk6…/coffee.a-cup-of.notary.stamp/3mt37oktj522n
signature valid, and stamp included in the signed repository.
MATCH: this content was notarized by did:plc:zzk6…
Install #
The code is hosted on a Tangled knot, which does not serve
Go's module-resolution protocol, so there is no go install. Clone the repository
and build the binary:
go build -o notary .
Usage #
Stamp #
Notarize a file (writes one record to your repo):
notary stamp --handle you.example.com --password <app-password> contract.pdf
Credentials can also come from the environment:
export NOTARY_HANDLE=you.example.com
export NOTARY_PASSWORD=<app-password> # create an *app password*, not your main one
notary stamp contract.pdf
notary stamp --hash sha256:b9dc39… # notarize a digest you computed elsewhere
Flags: --subject (label, defaults to the filename), --note (free text),
--pds (host URL, otherwise resolved from your handle).
Verify #
Verification needs no credentials — it only reads public data:
notary verify contract.pdf at://did:plc:…/coffee.a-cup-of.notary.stamp/3mt37oktj522n
notary verify --hash sha256:b9dc39… at://… # verify a digest you already have
Exit code is 0 on a match, non-zero on a mismatch or a failed proof.
List #
notary list you.example.com # or a did:plc:…
How verification works #
notary verify proves three independent things, none of which require trusting
the server that hands you the data:
- Authentic identity key. The DID is resolved through the PLC directory (or
did:web), giving the account's public signing key from its DID document — published independently of the PDS. - Signed commit.
com.atproto.sync.getRecordreturns a CAR proof: the signed repo commit, the Merkle Search Tree nodes on the path to the record, and the record block. The commit signature is checked against the key from step 1. - Inclusion + integrity. The record is walked from the signed MST root using
only the blocks in the proof (so it really is in the signed tree), then its
hashfield is compared to the digest of your content.
If a malicious PDS altered the record, changed the tree, or forged a commit, one of these checks fails.
What it does and does not prove #
- ✅ Attribution & integrity. Identity
Xasserted digestH, and the record is genuinely part ofX's signed repository. Tampering with either the content or the record is detected. - ✅ Privacy of the content. Only the digest leaves your machine.
- ✅ Server-independence. Anyone can verify from the public DID + a proof CAR; the PDS is untrusted. A third party who keeps the proof CAR retains evidence even if the record is later deleted.
- ⚠️ Time is author-asserted.
createdAtand the record key (a TID) are set by the author, who could backdate them. For a time attestation a third party trusts, anchor the commit — the relay firehose observes every commit at wall-clock time, so a witnessed firehose cursor (or cross-notarizing the commit CID elsewhere) upgrades "owner-asserted" to "witnessed". This is a natural next feature, not yet implemented. - ⚠️ Everything is public. atproto repositories are fully public; the stamp
records (digests, subjects, notes) are visible to anyone. Never put secrets in a
--subjector--note.
Lexicon #
Records use the coffee.a-cup-of.notary.stamp lexicon (see
lexicons/). If you fork this
under a domain you control, change the reverse-domain NSID accordingly (the
prefix is defined once in main.go).
License #
MIT — see LICENSE.