Skip to main content

orphograph

Bitcoin-anchored folder receipts that a relying party can verify without the issuer. Anchoring goes through the Orphograph service; verifying a file against its saved proof needs no server, no account, and no network — the service can be unreachable or gone.

  • License: MIT
  • Python: 3.9 or newer
  • Runtime dependencies: Python standard library only

Verify without the issuer

This is the command a relying party runs. It needs the file, the POSIX path the file had inside the anchored folder, the proof.json the anchoring party saved, and the Merkle root from the receipt. Nothing is fetched.

python -m orphograph verify-inclusion /path/to/sub/photo.jpg sub/photo.jpg proof.json <root_hex>
# {"ok": true, "root_hex": "…", "root_source": "argument"}
# exit 0 on a match, 1 on a mismatch, 2 on an I/O or parse error

root_hex is the root recorded on the receipt (and in its OpenTimestamps proof). Pin it. ok means "this file is in the tree with THAT root"; it says nothing about a root you did not check against the receipt.

Same thing from Python, reading the object inclusion-proof writes (root_hex and proof keys; the CLI additionally accepts a bare proof array when root_hex is passed explicitly):

import json
from orphograph import verify_inclusion

with open("proof.json") as f:
    saved = json.load(f)
ok = verify_inclusion(
    file_path="/path/to/sub/photo.jpg",
    rel_path="sub/photo.jpg",
    proof=saved["proof"],
    root_hex=saved["root_hex"],   # match this to the receipt's root first
)

verify_inclusion reads the file, recomputes its SHA-256, walks the proof up to the root, and compares. It takes no server_url and opens no socket. A missing local file raises FileNotFoundError rather than returning False, so an I/O problem is never mistaken for a "not included" verdict.

To hand a relying party what they need, the anchoring party saves the proof once, while the service is reachable:

python -m orphograph inclusion-proof <receipt_id> sub/photo.jpg > proof.json

proof.json carries root_hex and proof. The 4th argument may be omitted, in which case the root inside proof.json is used and the verdict reports "root_source": "proof_json". That root came from the same file as the proof, so a bundle is always self-consistent: compare the echoed root_hex to the receipt before treating ok as meaningful. An explicit root_hex always overrides the one in the file. When proof.json records a path that differs from rel_path, one JSON warning line goes to standard error; standard output and the exit code are unchanged.

The receipt's Bitcoin anchoring is an OpenTimestamps proof over the same root. The anchoring party exports it with the rest of their vault (GET /api/me/anchors.zip, authenticated; each receipt folder carries its receipt.json, .ots files and, for folder receipts, manifest.json) or per receipt (GET /api/receipt/<receipt_id>.zip, no login for public receipts) and hands the .ots alongside proof.json. ots info <file>.ots prints the attested Bitcoin block height with no service and no node; ots verify -d <root_hex> <file>.ots checks the root against the chain itself and needs a local Bitcoin node to do it (it exits 1 without one). Neither step depends on this service.

Privacy contract

The library does not transmit file contents. For each file the SDK reads the bytes locally, streams them through SHA-256 in one megabyte chunks, and commits the digest into an RFC 6962 Merkle leaf bound to the file's POSIX relative path. Only the resulting manifest (paths, per-file digests, leaf hashes, and the 32-byte root) is sent across the network. The verification path is symmetric: the root is recomputed locally from the folder on disk and compared to the root recorded in the receipt.

The Merkle module is a verbatim copy of the server's reference implementation, carrying the source SHA-256 in its header so divergence from the canonical algorithm is immediately visible.

Install

pip install orphograph

Anchor a folder

from orphograph import anchor_folder

result = anchor_folder("/path/to/folder")
# {
#   "receipt_id": "...",
#   "root_hex": "...",
#   "leaf_count": 42,
#   "calendars_ok": 5,
#   "calendars_total": 5,
# }

Optional arguments:

Argument Purpose
server_url Base URL of the service (default https://orphograph.com).
api_key Sent as X-Orpho-Api-Key when present.
client_label Short free-form label persisted with the receipt.
exclude Sequence of fnmatch patterns. None selects the default deny list (OS detritus, editor backups, build caches). Passing [] disables exclusion.

Verify a folder

from orphograph import verify_folder

ok = verify_folder("/path/to/folder", receipt_id="...")

The folder is walked locally, the Merkle root is recomputed, and the SDK returns True only if the recomputed root equals the root recorded in the receipt's manifest.

Inclusion proofs

A folder receipt can be queried for a proof that a single file belonged to the anchored tree. The proof is verified locally; no further network call is required to confirm it.

from orphograph import inclusion_proof, verify_inclusion

proof = inclusion_proof(receipt_id="...", path="sub/photo.jpg")
ok = verify_inclusion(
    file_path="/path/to/sub/photo.jpg",
    rel_path="sub/photo.jpg",
    proof=proof["proof"],
    root_hex=proof["root_hex"],
)

Command line

The package installs an orphograph console script and is also runnable as a module.

python -m orphograph verify-inclusion <file> <posix/rel/path> proof.json [root_hex]   # local only
python -m orphograph anchor /path/to/folder
python -m orphograph verify /path/to/folder <receipt_id>
python -m orphograph inclusion-proof <receipt_id> <posix/rel/path>

Each subcommand writes a single line of JSON to standard output. The verify and verify-inclusion subcommands exit with status 0 on a match and 1 on a mismatch. verify-inclusion ignores --server-url and --api-key; it never connects anywhere.

Environment variables:

Variable Purpose
ORPHO_SERVER_URL Default base URL.
ORPHO_API_KEY Default API key (sent as X-Orpho-Api-Key).

Algorithm

The Merkle construction is RFC 6962 with a domain-separated leaf (0x00 || rel_path_utf8 || 0x00 || file_sha256) and a domain-separated internal node (0x01 || left || right). Odd-level remainders are promoted, never duplicated, to avoid the second-preimage ambiguity of the duplicate-last construction. The algorithm tag orphograph-merkle-v1-rfc6962 is embedded in every manifest.

License

MIT. See LICENSE.

Metadata

Release files for orphograph 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for orphograph 0.1.1
File Size Uploaded
orphograph-0.1.1.tar.gz 27.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for orphograph 0.1.1
File Interpreter ABI Platform
orphograph-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 49.1 kB

Release files / orphograph-0.1.1.tar.gz

Download URL orphograph-0.1.1.tar.gz
Size 27.9 kB
Tags Source
SHA-256 checksum
How to use checksums
ca4f4138c0053109d31ed301fc3e8f780679da1002e57c5d0706da7f60dbefb5
BLAKE2b-256 checksum
How to use checksums
cac29e19f80e542d2e4fe7c399e0372e2296e58f145789b917dd1344dbd58091
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.9

Release files / orphograph-0.1.1-py3-none-any.whl

Download URL orphograph-0.1.1-py3-none-any.whl
Size 21.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c9897ee780557b52cda9be06f7cf4f8ab094a593a509403a6a31330c458196a3
BLAKE2b-256 checksum
How to use checksums
4f2268001fd6112720c43900a111230efa52eb52f872feab33e4264d414061c6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.9

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page