Skip to content

CLI Usage & Flags

Synopsis

simple-ots [FLAGS]
<file-list-command> | simple-ots [FLAGS]

When stdin is a pipe, simple-ots reads file paths one per line.
When run interactively (no pipe), it walks the current directory, skipping .simple-ots/ and .git/.

Flags

FlagDefaultDescription
--no-otsfalseSkip OpenTimestamps stamping
--config PATH<out-dir>/config.tomlConfig file path, must exist if given
--out DIR./.simple-otsDirectory to write results under
--verbosefalsePrint each file as it is hashed
--versionn/aPrint version and exit
-h, --helpn/aPrint usage and exit

--output-dir is accepted as an alias for --out.

Environment variables

VariableDescription
SIMPLE_OTS_CONFIG_PATHConfig file path. --config takes precedence.
SIMPLE_OTS_OUT_DIROutput directory. --out takes precedence.
OTS_CMDFull ots invocation to use instead of the one on PATH.

In PowerShell:

powershell
$env:SIMPLE_OTS_CONFIG_PATH = "./config.toml"
git ls-files | simple-ots

The config file that was actually loaded is printed as the Config: line of the run summary. A path given through --config or SIMPLE_OTS_CONFIG_PATH must exist; if it does not, the run fails with config file not found rather than quietly using the defaults.

Exit codes

CodeMeaning
0Success
1Fatal error (no files, config error, I/O error)
2OTS step failed (root hash was written; only stamping failed)

Exit code 2 lets CI distinguish "tool broke" from "network/ots unavailable".

Examples

Standard patterns

bash
# All Git-tracked files (the most common invocation)
git ls-files | simple-ots

# Tracked files + new untracked files not in .gitignore
git ls-files --cached --others --exclude-standard | simple-ots

# Walk cwd (no pipe needed)
simple-ots

# Specific extensions
find . -name '*.go' -o -name '*.md' | simple-ots

# Everything under a subdirectory
find ./src -type f | simple-ots

Skip OTS

bash
# Compute and record hashes without network access
git ls-files | simple-ots --no-ots

# Useful in CI for hash recording; stamp separately on release
find . -type f | simple-ots --no-ots

Custom config

bash
# Use a shared team config
simple-ots --config /etc/simple-ots/team.toml

# Per-run config override (e.g. anonymous run, no DIDs)
git ls-files | simple-ots --config .simple-ots/anonymous.toml

Verbose output

bash
git ls-files | simple-ots --verbose
# hashing: go.mod
# hashing: main.go
# hashing: ots/leaf.go
# ...
# Root hash: 51167fb0...

Output structure

Every run creates a timestamped directory under .simple-ots/results/:

.simple-ots/
└── results/
    └── 20260626_120000/
        ├── manifest.jsonl
        ├── root.hash
        └── root.hash.ots         (absent with --no-ots)

manifest.jsonl

One JSON object per line, one line per leaf:

json
{"file_path":"src/main.go","content_sha256":"e3b0c4...","datetime":"2026-06-26T12:00:00Z","did":"did:web:example.com","path":"src/main.go","path_variant":"relative","leaf_hash":"a1b2c3..."}
{"file_path":"src/main.go","content_sha256":"e3b0c4...","datetime":"2026-06-26T12:00:00Z","did":null,"path":null,"path_variant":"null","leaf_hash":"d4e5f6..."}

Fields:

FieldTypeDescription
file_pathstringNormalized relative path (always present for reference)
content_sha256stringSHA-256 of the raw file content
datetimestringFile mtime, ISO 8601 UTC
didstring|nullDID bound to this leaf, or null
pathstring|nullPath value in this leaf, or null
path_variantstringWhich variant: "null", "filename", or "relative"
leaf_hashstringSHA-256 of the canonicalized JSON of the above fields

root.hash

The Merkle root over all leaf hashes, as a 64-character hex string followed by a newline.

root.hash.ots

OpenTimestamps binary receipt. Initially pending; confirmed after a Bitcoin block.

bash
# Check/upgrade after ~2 hours
ots upgrade .simple-ots/results/20260626_120000/root.hash.ots

# Verify against Bitcoin
ots verify  .simple-ots/results/20260626_120000/root.hash.ots

Selective disclosure workflow

After stamping, to prove one file to a third party:

  1. Find the leaf(es) for that file in manifest.jsonl (filter by file_path + desired path_variant + did).
  2. Collect the sibling hashes from the manifest to form a Merkle proof.
  3. Share: the file content, the leaf JSON, the proof path, root.hash, and root.hash.ots.

The recipient verifies by recomputing the leaf hash and walking up to the root, then running ots verify.

No other files or DIDs in the tree are revealed.

gitignore recommendation

Add to .gitignore to avoid committing run artifacts:

.simple-ots/results/

Or commit selectively, since the root.hash and root.hash.ots files are small and useful to keep.

Released under the Apache License 2.0.