Here's the question I'd ask about your own release pipeline, and it isn't "is the timestamp signed."
It's this: if the service that issued your timestamps went dark tonight, what could you still prove in the morning?
The answer doesn't depend on what the service promised. It depends entirely on what you kept.
The obvious answer, and why it falls over
Obvious answer: you kept the verify link. Every timestamping service hands you one. You stash it in the release notes, you move on.
But a verify URL is a request to the issuer. That's the whole thing it is. If the issuer is gone, that URL is a 404, and what you're left holding is a screenshot of a page that used to load. A screenshot is not evidence of anything except that you once had a browser.
So the link is fine as a convenience. It is not the proof. Worth separating those two in your head before you go further.
What a timestamp actually consists of
Strip it down and there are two independent pieces.
The binding. A hash ties a specific sequence of bytes to a short digest. You run SHA-256 over the file, you get a digest, and anyone who has the same bytes gets the same digest. Forever. No service is involved in that step and none can be. This is the part you already control, and keeping it costs you nothing beyond keeping the file.
The anchor. Something append-only and expensive to rewrite says "this digest was known by this point." That's the part you're renting from somebody, and that's the part that goes away when they do.
So the anchor has to end up on your disk as data you hold, not as a pointer into someone else's database.
The part you have to store yourself
Here's what you actually need on your own disk for the anchor to survive the issuer:
- The digest. You can always recompute it from the file, but store it anyway, because it's tiny and it tells you which file you meant.
- Which chain the anchor went to.
- The transaction id on that chain.
- The path from your digest to whatever that transaction committed to.
Number four is the one that gets dropped, and it's the one that matters.
Anchoring one hash per transaction is expensive, so batching is normal. Many digests go into a Merkle tree, the root of that tree goes on chain, and the transaction commits to the root, not to your digest. Which means the on-chain record says nothing about your file on its own. Your digest plus the root is not a proof. You need the sibling hashes along the path from your leaf up to the root, in order, with the left/right position of each step.
With those, verification is arithmetic you can do yourself:
leaf = sha256(file_bytes)
node = leaf
for (sibling, side) in path:
node = sha256(sibling + node) if side == "left" else sha256(node + sibling)
assert node == root_in_transaction
That loop is the entire proof. It needs no network, no account, no vendor. If you're storing a verify URL instead of that path, you don't have a portable proof, you have a bookmark.
Two chains, one digest, and an honest caveat
ProofLedger, which is mine, sends the same digest to Polygon and to Bitcoin. Same hash, two independent records. The reason is redundancy of the anchor, not strength of the cryptography.
I want to be blunt about that second part because it's where this category gets oversold. OpenTimestamps gives you Bitcoin timestamping for free and the proof it produces is cryptographically just as valid as mine. There is no version of this where I have a better Bitcoin proof than a free tool. A hash committed to a chain is a hash committed to a chain. What differs between options is the platform wrapped around it: what you can query, what you can export, what the verification path looks like for someone who doesn't trust you. Anyone selling you a stronger proof is selling you an adjective.
Verification has to be boring, and it has to be public
The test of whether a timestamp is worth anything is whether the person relying on it can check it without asking you. Not without asking the vendor. Without asking you, the party with the motive.
That's why the verify endpoint on my API takes no auth at all:
H=$(shasum -a 256 release-artifact.tar.gz | cut -d' ' -f1)
curl "https://proofledger.io/api/v1/verify?hash=$H"
No key, no account, 120 requests per hour per IP. An auditor or opposing counsel can run that from a laptop. The whole v1 API is three endpoints: POST /api/v1/proof to submit a hash with a Bearer sk_ key, GET /api/v1/proof/:id for status plus chain_tx and bitcoin_txid, owner only, and that public verify. There's an OpenAPI spec at /openapi.json if you'd rather generate a client than read docs.
The file itself never goes anywhere. You hash locally, you send the digest. That's not a privacy feature bolted on, it's just what the engine does, because the engine only ever sees a digest. It has no idea what kind of file you hashed and there's no per-file-type pipeline behind it.
For the offline case there's a verify-proof package on PyPI that does the same check locally, and a GitHub Action so CI can do it on every build.
The limits, stated plainly
Reading a chain means reading a chain. Local verification confirms your bytes match your proof material without involving me, but confirming the anchor itself still means somebody queries a node or an explorer. That's a real dependency and I'd rather name it than let "offline" do work it can't do.
A timestamp proves existence by a point in time. It does not prove authorship, it does not prove the contents are true, and it does not prove you're the one who made the file. It proves the bytes existed. That's a narrow claim and narrow is what makes it hold.
And a tradeoff I took that I'm not thrilled about: the Merkle-inclusion download sits on the Business tier. Polygon anchoring is free and unlimited on every plan including the free one, and every plan has API access with a monthly cap, but that particular export is gated. That's a business decision, not a technical one, and I'd rather say so than have you find out later.
The test to run today
Pick an artifact you shipped and still care about. Then try to verify its timestamp using only files on your own disk and a public block explorer. No vendor login, no support ticket, no dashboard.
Can you get from the bytes to a confirmed on-chain record by yourself? If yes, you have a timestamp. If you have to authenticate to somebody first, you have an account with a company that has a timestamp.
Run that against whatever you're using now, including mine. proofledger.io, API docs at proofledger.io/api.html. I build and run it.
Genuine question for anyone doing this at scale already: what do you actually store next to your artifacts, just the digest and a txid, or the full inclusion path? I keep going back and forth on what belongs in the repo versus what belongs in cold storage, and I'd like to hear how other people landed on it.
Top comments (0)