Prove a file existed on a date, without a notary
A notary doesn’t read your document. They witness that it existed, and when. You can do that part yourself, for free, in about ten minutes.
It comes up more than you’d expect. A client says the brief changed after you quoted. Somebody else files something that looks a lot like the thing you designed in March. A contractor claims the spec they were given didn’t include the bit they skipped.
None of those are settled by having the file. Of course you have the file. You could have made it this morning, and the file’s modified date is whatever you’d like it to be, because it’s just a number on your own disk that you can change.
What you’re missing is a witness. Somebody outside the argument who can say the thing existed before the argument started.
That’s the whole job, and it doesn’t need a person. You take a fingerprint of the file, publish the fingerprint somewhere nobody can quietly rewrite, and keep the receipt. Later, the fingerprint either matches your file or it doesn’t.
A timestamp isn’t proof of what’s in the file. It’s proof of when.
Before you start
You need a terminal and a file worth proving. Python 3, which is already on most machines.
One thing worth knowing up front: the file never leaves your computer. The tool hashes it locally and sends only the hash. Nobody receiving it can read the document, or tell what kind of document it is.
The stamp itself takes a minute. Confirmation takes a few hours, so this is two short sittings rather than one. Step 4 explains what’s happening in the gap.
Pick the file, and freeze it
Choose one file that would matter in a disagreement. A signed scope, a design, a spec, a set of research notes.
Copy it somewhere it won’t be touched:
mkdir -p ~/stamped
cp scope-v3.pdf ~/stamped/
This matters more than it looks. The proof covers exactly the bytes you stamp. Open that PDF later, resave it, and the fingerprint changes, which means the proof no longer matches and you’ve quietly thrown it away. Stamp a copy you never open again.
Install the client
Install it into its own environment, in the folder you just made:
cd ~/stamped
python3 -m venv ots-env
./ots-env/bin/pip install opentimestamps-client
The tool is called OpenTimestamps, an open standard for anchoring a hash to the Bitcoin blockchain. It’s free, there’s no account, and no API key. The public calendar servers it talks to run on donations.
You’ll see pip3 install opentimestamps-client given as the install command in a lot of places, including the project’s own README. On a current Mac or Linux box that now fails with error: externally-managed-environment, because the system Python refuses to let you install into it. The three lines above sidestep that and don’t touch your system Python at all. Every ots command below runs as ./ots-env/bin/ots.
If you’d rather not install anything, opentimestamps.org has a browser version that does the same job. It hashes in your browser, so the file still doesn’t leave your machine.
Stamp it
./ots-env/bin/ots stamp scope-v3.pdf
You get a new file next to it called scope-v3.pdf.ots. That’s the proof, and it’s the thing you’re actually keeping.
What happened: the tool hashed your file and sent that hash to four public calendar servers. They’re collecting hashes from everyone, bundling them together, and committing the bundle to Bitcoin in a single transaction. That’s why it costs nothing. You’re sharing a transaction with everybody else stamping right now.
The .ots is small, well under a kilobyte. It’s a path from your file’s hash to a Bitcoin block, not a copy of anything.
Come back in a few hours
Run the verify now and you’ll see it’s not ready:
./ots-env/bin/ots verify scope-v3.pdf.ots
You’ll get a line per calendar saying Pending confirmation in Bitcoin blockchain. That’s expected and it isn’t a failure. The calendar has your hash, but the bundle hasn’t made it into a Bitcoin block yet.
Bitcoin produces a block roughly every ten minutes, and the calendars don’t submit a transaction per stamp, so a few hours is normal. Nothing is at risk while you wait. The proof file already exists and it completes itself once the block lands.
Go and do something else. Come back later today or tomorrow.
Verify it, and know what your check is worth
Once it’s confirmed, verification tells you the block and the date:
./ots-env/bin/ots verify scope-v3.pdf.ots
You don’t have to name the original file. The tool works it out from the proof’s name and tells you which one it assumed.
A confirmed proof reads like Success! Bitcoin block 358391 attests existence as of and then a date.
Now the part that decides how much this is worth, and it’s the reason to read this step rather than skim it.
Full verification needs your own Bitcoin node. The client’s own documentation is direct about it: you can create timestamps without one, but to verify them you need a local Bitcoin Core node, and a pruned node is fine. With a node, you’re checking the proof against a copy of the chain you hold. Nobody is telling you the answer. You’re reading it.
Without a node, the browser verifier at opentimestamps.org will check the proof for you. That’s a reasonable thing to do and it’s a different thing. You’re trusting their lookup of the block rather than doing your own.
For most people the practical answer is: use the browser verifier day to day, and understand that the proof’s real strength is that anyone can verify it independently, including the person arguing with you. That’s what makes it worth having. A receipt only you can read isn’t evidence.
Keep the two files together
The .ots on its own proves nothing. The original file on its own proves nothing. The pair is the evidence.
Store them together, somewhere you’d still find them in three years, and note in one line what the file was. A folder of .ots files nobody can match to a document is the version of this that wastes the whole exercise.
A boundary worth knowing about
This proves a file existed on a date. It doesn’t prove you made it, it doesn’t prove it’s true, and it doesn’t prove anyone agreed to it.
Somebody else could stamp the same file, and their proof would be just as valid as yours. If they stamped it earlier, theirs is better. So this is useful for I had this in March and useless for this is mine. Authorship is a different problem with different tools.
The other edge is the one in step 1. The proof is bound to exact bytes. Resave the file, re-export the PDF, let a sync tool rewrite metadata, and the hash moves and the proof stops matching. The copy you stamp has to be a copy you leave alone.
If you have staff
Decide what gets stamped, once, and make it boring. Something like every signed scope and every delivered spec, stamped on the day it goes out. A rule that runs on a schedule is worth more than a heroic effort during a dispute, which is always too late by definition.
The proofs need to outlive the person who made them. If they’re in one person’s home folder, you don’t have them. Put them where your contracts live.
The short version
- Copy the file somewhere you won’t touch it again
python3 -m venv ots-envthen./ots-env/bin/pip install opentimestamps-client, because plainpip3is refused by system Python now./ots-env/bin/ots stamp yourfile.pdf, which producesyourfile.pdf.ots- Wait a few hours for a Bitcoin block, pending isn’t a failure
./ots-env/bin/ots verify yourfile.pdf.ots. Your own node reads the chain, the browser verifier trusts theirs- Keep the file and the
.otstogether, with a line saying what it is
Sources
- OpenTimestamps — the standard, the free public calendars, and the browser stamper and verifier that hashes locally
- opentimestamps-client on GitHub —
pip3 install opentimestamps-client, theots stampandots verifycommands, the pending state, and that creating a timestamp needs no node while verifying one needs a local Bitcoin Core node
Written August 2026. Commands and the node requirement checked against the OpenTimestamps client README on 23 August 2026.
Prints to PDF from your browser — colours and all.