Skip to content

didvc/simple-ots

simple-ots

Hash your files. Anchor to Bitcoin. Prove any single file later — without revealing the rest.

CI License: Apache 2.0 Go Report Card Docs

Full documentation →

simple-ots builds a SHA-256 Merkle tree over a set of files and stamps the root to Bitcoin via OpenTimestamps. For each file it generates one leaf per (path variant, DID) combination, so you can later prove a specific file to a third party with a short Merkle proof — without exposing anything else in the tree.

No ZKP. No external indexing. Just SHA-256 and a Merkle tree.

Install

# Requires Go 1.22+
go install github.com/didvc/simple-ots@latest

Stamping requires the OpenTimestamps client:

pip install opentimestamps-client

To build from source:

git clone https://github.com/didvc/simple-ots
cd simple-ots
./install.sh --local

Quick start

# Hash all Git-tracked files and stamp to Bitcoin
git ls-files | simple-ots

# Walk the current directory (no pipe needed)
simple-ots

# Offline — skip OTS
git ls-files | simple-ots --no-ots

Output lands in .simple-ots/results/YYYYMMDD_HHMMSS/:

File Contents
manifest.jsonl One JSON line per leaf (file × path-variant × DID)
root.hash Merkle root, hex SHA-256
root.hash.ots OpenTimestamps receipt

Selective disclosure

Each file gets multiple leaf hashes — one per (path_variant, DID) pair. To prove a file later, hand the recipient that leaf's JSON plus its Merkle path (roughly 16 hashes). They verify up to the root, then run ots verify. Everything else in the tree stays invisible.

Put a config in .simple-ots/config.toml:

dids           = ["did:web:example.com"]
path_variants  = ["null", "filename", "relative"]
include_no_did = true

Leaves per file = len(path_variants) × (len(dids) + 1). The example above gives 6 per file.

See Configuration and Config Examples for more.

Verify

After a Bitcoin block confirms (~2 hours):

ots upgrade .simple-ots/results/TIMESTAMP/root.hash.ots
ots verify  .simple-ots/results/TIMESTAMP/root.hash.ots

Flags

Flag Default Description
--no-ots false Skip OpenTimestamps stamping
--config PATH .simple-ots/config.toml Config file
--verbose false Print each file as it's hashed
--version Print version and exit
-h, --help Show usage

Set OTS_CMD to override the ots invocation. Useful with pyenv:

export OTS_CMD="env PYENV_VERSION=3.9.9 ots"

How it works

Paths are sorted alphabetically. For each file: compute content_sha256 (raw bytes) and read mtime. For each (path_variant, DID) combo, build a JSON object with four keys (content_sha256, datetime, did, path), serialize with sorted keys, and SHA-256 the result. All leaf hashes go into an in-memory Merkle tree. The root is 32 bytes regardless of how many files are in the tree.

Full write-up

Man page

man simple-ots

The man page is installed automatically by install.sh --local if a writable man directory is found.

Contributing

See CONTRIBUTING.md.

Citation

Use the Cite this repository button on the GitHub repo, or see CITATION.cff.

License

Apache 2.0


Related projects

  • c2pa — C2PA examined from the standpoint of artistic authorship