Audience: an outsider with no access to the authors. If any step below requires asking someone, that is a defect — file it as an issue (or a dispute, step 7). This document is the SPEC#7 AC-7.5 gate: the project is not settlement-grade until a stranger succeeds with nothing but this page.
Current-state caveats (honest): the repository is not yet published to a public remote (backlog A-17) and no official Monday prints have been produced yet (the weekly CI job still runs the reproducibility gate only). Until then, this procedure verifies the committed reference computation — the same path official prints will use.
A fixing is one epoch's settlement value plus complete provenance (methodology version, snapshot sha256 hashes, source URLs), hashed over a canonical rendering. Two independent parties agree when their 64-hex fixing hashes are string-equal. Nothing else needs to be exchanged.
pytest and ruff for the gates: pip install -e '.[dev]').git and ~1 GB disk if you refetch the large snapshots.git clone <repo-url> tly && cd tly git log --oneline -1 # record the commit you verified
python -m pytest tests/test_manifest_schema.py tests/test_snapshot_immutability.py -q
Both must pass: every snapshot file matches its manifest sha256, and history contains no rewrites. If this fails, STOP — the tree you have is not the tree that was published.
python -m tly.pipeline 2026-08-17T12:00:00+00:00 > my_print.json python -m tly.pipeline 2026-08-17T12:00:00+00:00 > my_print_2.json diff my_print.json my_print_2.json # must be empty (determinism)
The full P5 gate (two cold processes, different hash seeds):
python -m pytest tests/test_p5_reproducibility.py -q
Every input file's source_url, sha256, and retrieval timestamp is in
data/snapshots/<date>/manifest.json. Refetch any URL yourself and hash
it. NOTE: upstreams revise; a hash mismatch against a fresh fetch
proves revision, not tampering — tampering is a mismatch between the
committed file and the committed manifest (step 3 catches that).
Files marked "in_git": false are large; their manifest rows are the
committed record, and your own fetch lets you verify content
independently.
from pathlib import Path
from tly.archive import PrintArchive
from tly.fixings import settle_from_archive
from tly.pipeline import build_settlement_print
archive = PrintArchive(Path("./my_archive"))
archive.append(build_settlement_print("2026-08-17T12:00:00+00:00"))
fixing = settle_from_archive(
archive, "2026-08-17T12:00:00+00:00", Path("data/snapshots")
)
print(fixing.fixing_hash)
Compare your 64-hex hash with the published one (once official fixings
publish, they live in the static API tree under api/v1/ and in the
archive's chain.json). String-equal = agreement.
If you would rather not manage a Python environment:
docker build -t tly-recompute . docker run --rm --network=none tly-recompute > my_print.json
--network=none enforces at the container level what the code enforces
internally (offline compute over hash-verified snapshots). Verified at
E-13: two container runs are byte-identical, and container output is
byte-identical to a host run on a DIFFERENT OS and Python version
(Linux/CPython 3.12 container vs macOS/CPython 3.13 host) — the P5
property holds across platforms, not just across runs.
Archived prints carry the methodology version THAT PRODUCED them. If
governance has advanced since (the registry is append-only), your
recomputation under HEAD will stamp a newer version — that is NOT a
divergence. The contract (enforced by the public outsider-sim CI job,
tly/outsider_sim.py): all VALUE fields byte-identical excluding
provenance; the archived stamp must equal its version's immutable
registry entry; every archived snapshot citation must resolve into the
committed manifests (manifests grow append-only — the archived citation
set is a valid subset). Compare values, verify stamps against history.
Each archived epoch's chain record_hash is Bitcoin-timestamped:
stamps/<epoch>.hash holds the record_hash (compare it to chain.json
yourself), and stamps/<epoch>.ots is the proof over that hash file.
To verify independently: pip install opentimestamps-client, then
ots verify -f stamps/<epoch>.hash stamps/<epoch>.ots. A fresh proof is
calendar-pending until aggregated into a Bitcoin block (hours); run
ots upgrade on the proof after that, and note full verification wants
a local Bitcoin node (the client offers a trusted-explorer fallback).
CI blocks publish unless every epoch has a matching, existing proof
(python -m tly.ots_gate verify).
1. Re-run step 3 (is your tree intact?) and step 4 (is your run
deterministic on your machine?).
2. Diff your my_print.json against the published per-epoch artifact —
the first differing field localizes the disagreement.
3. File a dispute within 48 hours of the epoch (log-only; it alters
nothing and delays nothing, but it is permanently on the record):
claimant, claim, the epoch, the disputed fixing hash, and your
computed hash. Substantiated disputes are resolved through the
correction ledger (ledger/CORRECTIONS.md) in the NEXT epoch —
published history is never rewritten.