Tamper evidence for the published JSON
🔗 Integrity chain — can anyone check that our JSON was not altered after publication?
Every day, right after the last daily update, this site writes a list of the SHA-256 of every published JSON file, links it to the previous day's list (a hash chain), and submits the SHA-256 of that list to OpenTimestamps, which anchors it in the Bitcoin blockchain. Only the 32-byte digest leaves this site; no file content is sent anywhere. The chain starts on 2026-10-03; earlier days are not reconstructed, because a list written after the fact proves nothing.
This page judges nothing about the observed world. It only lets a third party recompute, from the published files alone, whether a JSON file is the one that existed on a given day.
Files
- ・/api/integrity/latest.json — the latest day: date, the day file, its file_sha256 (the digest that was timestamped), chain_hash, and the OpenTimestamps proof path and status (pending until a Bitcoin block includes it, then complete)
- ・/api/integrity/YYYY-MM-DD.json — the day's list: files[] (path, bytes, sha256; sorted by path), list_hash, prev (previous day's date, file digest and chain_hash), chain_hash
- ・/api/integrity/YYYY-MM-DD.json.ots — the OpenTimestamps proof for the SHA-256 of that day file, exactly as served
Excluded from the list: the integrity files themselves and files whose name starts with an underscore. HTML pages are not listed because the CDN injects an analytics script into them; the JSON files are served byte-for-byte.
How to verify (anyone can recompute this)
- 1. A single file. Download the JSON file you care about and the day file for the date you care about. Compute the file's SHA-256 and compare it with the sha256 of the same path in files[]. A match means the file is byte-identical to what existed when the list was written.
- 2. The list. Recompute list_hash = SHA-256 of the canonical JSON of files[]: keys sorted, separators , and : with no spaces, ASCII-escaped, UTF-8. In Python: hashlib.sha256(json.dumps(files, sort_keys=True, separators=(',',':'), ensure_ascii=True).encode('utf-8')).hexdigest().
- 3. The chain. Recompute chain_hash = SHA-256 of the UTF-8 string prev.chain_hash + list_hash (two lowercase hex strings concatenated; on the first day prev is null and the empty string is used). Then open the previous day file and check that its chain_hash equals prev.chain_hash, and that its SHA-256 equals prev.file_sha256. Repeat back to 2026-10-03.
- 4. The time. Verify the OpenTimestamps proof against the day file: ots verify YYYY-MM-DD.json.ots -f YYYY-MM-DD.json (client: pip install opentimestamps-client; a Bitcoin node or the public calendars are used to resolve the block), or upload both files at opentimestamps.org. A complete proof shows the Bitcoin block height; it establishes that the day file — and therefore every hash in it — existed before that block.
# Python 3 — steps 1 to 3 for the latest day
import hashlib, json, urllib.request
B = 'https://ai-lifeline.org'
get = lambda p: urllib.request.urlopen(B + p).read()
latest = json.loads(get('/api/integrity/latest.json'))
day = json.loads(get(latest['file']))
assert hashlib.sha256(get(latest['file'])).hexdigest() == latest['file_sha256']
lh = hashlib.sha256(json.dumps(day['files'], sort_keys=True, separators=(',', ':'), ensure_ascii=True).encode('utf-8')).hexdigest()
assert lh == day['list_hash']
prev = (day.get('prev') or {}).get('chain_hash') or ''
assert hashlib.sha256((prev + lh).encode('utf-8')).hexdigest() == day['chain_hash']
for f in day['files']: # every published JSON, byte for byte
assert hashlib.sha256(get(f['path'])).hexdigest() == f['sha256'], f['path']
print('ok', day['date'], day['file_count'], 'files')
If a file was changed by a later daily update, step 1 fails for that file against the older day file but passes against the newer one — compare against the list of the same day. If a file fails against every day file after its publication, it was altered outside the daily batch.
What this does and does not prove
- ・It proves that a given JSON file existed, byte for byte, no later than the Bitcoin block that includes the day's proof, and that the daily lists form an unbroken chain since 2026-10-03.
- ・It does not prove that the values inside are correct. Correctness is a question for the primary sources linked on every page and in /api/series.json.
- ・The list is written by the same batch that publishes the files. Someone with control of this site could alter a file and write a new list; what they cannot do is backdate it, because the proof's block height is public. Compare lists across days.
- ・If the daily step fails (calendars unreachable, transfer failed), the day has no list. The failure is recorded in the site's own health reporting and the next day's list continues the chain from the last successful day.
Policy: 運営者・編集方針 (Japanese; section 一次証拠の保全). Japanese version of this page: 改ざん検知の鎖.