How it works

Status: being tested. Steps 1 to 7 below are built. The second record (step 7) is written to Base's test network for now, while it is tested.

1. A fingerprint of each file, made on your phone

Each photo or video is run through SHA-256, a standard fingerprint function. Change a single pixel and the fingerprint changes completely. The fingerprint cannot be turned back into the photo. The photos themselves stay on your phone.

2. One fingerprint for the whole set

The file fingerprints are combined into a Merkle tree (the format Certificate Transparency uses, RFC 9162). The result is one 32-byte value for the set. Later, any single photo can be shown to belong to the set without showing the others.

3. Timestamped where nobody can change it

That one value, and nothing else, goes from your phone to our server. The server adds 16 random bytes, hashes again, and sends the result to four public OpenTimestamps calendar servers (at least two must accept). They record it in the Bitcoin blockchain, usually within some hours. Until then the proof is “pending”; when you next open the app, it fetches the finished proof and keeps it with the set. The zip you save holds the proof as a standard .ots file, which the OpenTimestamps tools can check on their own. The value is also recorded a second way, on Base, in hourly batches (step 7). Neither record can be edited afterwards, by us or by anyone else, and both can be checked without asking us.

Why it is free

A Bitcoin transaction costs a fee, but one transaction can timestamp any number of fingerprints. Each OpenTimestamps calendar server collects every fingerprint it receives in a second into one Merkle tree, and only the top of that tree goes into Bitcoin (Peter Todd, “OpenTimestamps: scalable, trust-minimized, distributed timestamping with Bitcoin”, 2016). So one fee is shared by thousands of timestamps. The public calendars are free to use, need no account, and run on donations (opentimestamps.org, checked 2026-10-22).

Our own part is small: the phone sends one 32-byte value per set, and our server passes it on and keeps the proof. The photos are never sent, so there is nothing large to store or move. That is why the app can be free without adverts, accounts or payments.

4. Anyone can check

The checker runs in the browser, and the files are never uploaded. Given the zip (or receipt.json and the files), it:

For that last step it needs the block's 80-byte header. Our server fetches it from two public block explorers (mempool.space and blockstream.info), only once the block has at least six confirmations, and refuses if they disagree. The browser then checks the header itself: its double SHA-256 meets the proof-of-work target written in it; that target is not easier than a floor set well below the real difficulty for that height (so a header mined cheaply can't pass); and its Merkle root is exactly the value the proof arrives at. It does not download the chain from that block to today, as a full node would. For a check that needs no trust in us at all, run ots verify proofs/tree-head.json.ots in the unzipped folder, with your own Bitcoin node.

The time shown is the block's own timestamp. Bitcoin only requires it to be later than the median of the previous 11 blocks and less than two hours ahead of the network's time (Bitcoin wiki, “Block timestamp”, checked 2026-10-21), so the checker says the time can be out by an hour or two.

A proof still pending is reported as pending, never as failed. If the zip's proof is pending, the checker asks our server for the finished one, sending only the set's fingerprint, and offers it for saving next to the zip.

5. One photo on its own

The checker can save a proof for a single file: its own entry in the tree, its inclusion path to the root (RFC 9162), the tree head and the timestamp. With that and the file, anyone can check the file was part of the timestamped set without seeing the other files, the notes or the phone's times. The path's sibling values are hashes of other entries, which can't be turned back into them. One limit: the proof includes the set's random identifier, so someone who already had an exact copy of another photo from the set could confirm that it was in the set too.

6. The status link

A link of the form /r/<fingerprint>#n=<entries>&r=<root> shows whether a set is timestamped and when, checked in the viewer's browser in the same way. The part after # never leaves the browser; the page rebuilds the tree head from it and shows the number of files only if it gives the fingerprint in the link. The page is kept out of search engines.

7. A second record, on Base

Base is a low-cost network built on Ethereum. Once an hour, our server takes every set fingerprint that has arrived since the last batch, in the order received, and makes them the leaves of another RFC 9162 Merkle tree. Its tree head, {"format":"move-in-proof/batch-head","version":0,"tree_size":N,"root":…}, is hashed with SHA-256, and that one value goes on the chain as an attestation of the Ethereum Attestation Service, under our schema bytes32 batchHead,uint32 treeSize,string format,uint16 version, which can't be revoked. One transaction carries the whole batch, so the cost is the same however many sets are in it, and our server pays it: a fraction of a penny a batch. Only the batch's value and its size are written; no set's own fingerprint is.

Your phone then fetches its set's place in the batch and the inclusion path, and the zip keeps them in receipt.json under proofs.batch, with the attestation's UID and transaction. The checker follows the path from the set's recomputed fingerprint to the batch's root, hashes the batch head, and reads the attestation through our server from two public nodes (sepolia.base.org and publicnode.com), which must agree. In the browser it then checks that the attestation was made by our address (or by an earlier one of ours, before we retired its key), under our schema, is not revoked, and holds exactly that value and size. The time shown is the time of the block holding it, set by Base's sequencer, which makes a block about every two seconds.

For now the batches go to Base Sepolia, Base's test network, while this is tested. Records there are public and checkable in the same way, but a test network carries less weight than the real one. The Bitcoin timestamp in step 3 does not depend on this record, and this record does not depend on it.

What a receipt proves

A receipt proves that these exact files existed, unchanged, by the time shown on the receipt.

The time on your phone is shown as “your phone's clock”. The time you can rely on is the timestamp: it shows the files existed by then.