Drop one or more decision-head audit logs here and read them as a trail. Each line shows the answer to one question and how sure the reading was. Rotated files verify as one series, and the page names the first row, or the first file boundary, that stops matching its seal.
Runs in this browser tab. The page has no server, and the logs you drop or paste never leave this machine. The fonts are its one network request. Your screen settings stay in this browser.
The log
nothing loaded
Drop audit.jsonl files here with their .chain seals. Several rotated files read as one series, ordered by their first timestamp.
A generated sample log; no deployed system wrote it. tools/make_sample.py writes it in the documented row format.
Hash chain
File boundaries
How the chain is checked
decision-head writes no chain today. Its audit-log page says rows are not chained or signed, and chained row hashes sit on the README's planned list. This reader checks a chain in either of two forms.
A seal file holds a running hash for every row: h[i] = sha256(h[i-1] + "\n" + row i). A first file starts from 64 zeros. A rotated file starts from the previous file's last hash, written on the seal's # start: line, so the files check as one series and a missing or reordered file shows at its boundary. This page or tools/seal.py writes seals.
If the rows carry a prev_hash field, the form the 2026-10-06 security review proposed (SHA-256 of the previous line), the reader checks every link, across files too. An edit to row k shows up at row k+1. An edit to the very last row cannot show up at all.
With neither, the reader checks that each line parses as a JSON object and says so.
Look at these
Cases
rows grouped by request_hash
A case is every answered row that shares a request_hash: the same state and questions sent to the same model. A trailing -deliberate is set aside so fast and deliberate answers to one case sit together. Refused requests hash without the model id, so they stay out of this view.
Calibration by question
yes/no questions, fast and learned rows
The log holds no outcomes. This table shows how a question's answers spread across probability bins, so it cannot tell whether a 0.7 comes true seven times in ten. Read it as a distribution, and use the before and after columns to see what a calibration change did to that spread.
Charts
hover a mark for its numbers
Calibration drift
rolling mean of the top probability, and the share of answers flagged low_confidence, over the last N answered rows
Answer mix by day
share of each answer per day, for the chosen question
Probability by question
yes/no questions plot P(yes); choice and score questions plot the top option's probability. Count of answered rows per bin of 0.1.
Trail
The table scrolls down and sideways. Only the rows in view are drawn.
Row
Time (UTC)
Question
Answer
Probability
Band
Mode
backend · calibration
Chain
Export
files are written in this tab and saved by your browser
Redaction check
Before anything leaves the page, the reader scans every text value and every key in the loaded rows for email addresses, phone numbers, IBANs that pass their checksum, and runs of capitalised words that look like names. It keeps counts and nothing else. The 2026-10-06 security review notes that free text placed in question_id or in option names lands in the log, so those fields are scanned too. The name pattern also catches product and place names.
Flagged rows as CSV
one line per entry under "Look at these"
The whole trail as sanitised JSONL
keeps only the fields you tick; unreadable lines are left out; the result no longer matches any seal
check The export may carry personal data
Keeping the seal
where the .chain file should live
A seal helps only if the account that writes the log cannot also rewrite the seal. decision-head's 2026-10-06 security review lists the audit log as an asset that "must be complete, attributable and free of secrets and documents". It also says the files the service writes "are operator-controlled but not integrity-checked", and that without a chain "a tampered or truncated log is also undetectable".
Anything running under the service's account can edit both files when they sit side by side. That covers the service, a script, and an agent with shell access. So the seal goes somewhere that account cannot write.
Another machine or account. A one-way job copies each .chain file there. The log host gets no write access back.
Append-only or retention-locked storage. decision-head's audit-log page already sends the log there: "ship it to append-only storage if you need that guarantee". Seals can share that store.
The last hash, written down. A seal's final line commits to every row before it. Paste it into a ticket, an email or a signed note and you can check the whole file later, even if the seal file is lost.
Rotation. Close a file with tools/seal.py rotate. The new seal starts from the previous file's last hash, so a dropped or reordered file breaks at its boundary.
Re-seal on purpose only. A fresh seal over an edited log verifies cleanly. A seal written after an edit vouches for the edit.
Quotes from the decision-head repository: docs/security-review-2026-10-06.md (threat model, asset A4 and boundary TB4; finding 8); docs/audit-log.md ("What the audit log does not record"). The storage advice is this build's.