TAMPER
SIGNAL
← all posts

Data you can take with you

A matte charcoal parcel lifting away, a glowing green wax-sealed tag still attached to it

Every BI tool has a Download button. You click it, a CSV lands in your downloads folder, and the proof stays behind. Whatever the dashboard knew about where those numbers came from, the chain of code they survived, the totals that matched at every step, none of it makes the trip. The file you carry out is an unattested blob, indistinguishable from one someone typed by hand. The trust ended at the button.

That is the seam this whole project exists to close, and it should not reopen the moment data leaves the page. So the rule for Tamper Signal is the obvious one: if the data is verified on the dashboard, it should still be verifiable after you take it. The proof travels with the data, or it was never really yours to take.

The download button drops the proof at the door

A bare export is a claim with no way to check it. You hand a colleague a spreadsheet, they open it, and they have exactly your word that it is the data the dashboard showed. They cannot tell a faithful export from one that lost a row on the way out, because there is nothing attached that would know. The number sits in a cell looking like fact, the same way it looked on the chart, with the same silence underneath it.

A receipt fixes that at the register: you do not take the total on faith, you read the line items. A verified export should work the same way. The data leaves with the receipts that prove it, and the person who receives it checks for themselves rather than trusting the sender.

A bundle the recipient can check

So that is what receipts export --bundle writes: a verified bundle, a zip holding the data file plus chain.json and its receipts, kept byte for byte. There is no service to call and no account to make. Whoever receives it unzips it and runs the same verification the dashboard ran, on their own machine, offline.

receipts export --bundle
# the recipient, anywhere, with no access to your dashboard:
unzip report-verified.zip && receipts verify chain.json

In the browser it is a button. The verified Data tab grows a "Take your data" control that builds the bundle client-side from the rows it already proved, or hands you a bare rows-only file when you only want the data. The bundle is offered only when the light is green or yellow, because a bundle is a claim of verifiability and the tab will not make that claim about data that does not hold up. A rows-only file is the data without its receipt, and the tab says so where you pick it, so the unverified one is never the one you reach for by accident.

The quietly nice part is the format. Tamper Signal's fingerprint is taken over the data, not the bytes, so it is the same whether the data is a CSV, a TSV, JSON, or NDJSON. Export your report as a CSV, hand it to someone who re-ingests it as JSON, and the hash matches and the light stays green. The proof does not care what shape you carry the data in, which is the entire point of a format you are allowed to leave with.

Take it, change it, bring it back

Portability that only goes one direction is half a feature. If you can take the data out, fix a row, and re-verify it, you should be able to bring it back in as the new source of truth. receipts ingest --as replace does that: it re-signs a fresh chain from the corrected file and archives the old one, so the prior chain is preserved rather than quietly overwritten. --as period does the gentler thing for a recurring report: it records the file as the next period and judges it against the run history through the tolerance band you already declared, so a normal refresh stays green and a settled number that moved earns a yellow caveat.

Bringing data back is where honesty matters most, so re-attestation is never silent. Importing signs under your key and records who re-attested and when. Continuing a chain's history works only under a signer that chain already trusts; an unfamiliar key is refused rather than waved through, because the alternative is letting anyone graft their data onto someone else's green history and call it continuous. You can take the data anywhere. You cannot launder it back in.

What the round trip cannot carry

The honest part, because skipping it would make this the kind of copy this project exists to mock. A bundle proves continuity, not correctness. It proves the data you are holding is the data the chain signed, unchanged, and it lets the recipient confirm that for themselves. It says nothing about whether the source was right to begin with. Garbage exported faithfully is still garbage, and it will verify green the whole way.

Two smaller caveats, stated plainly. The browser export reconstructs the data from what it verified, so the file you get is the attested data in a real, openable format, but it is sorted and normalized rather than a byte-for-byte copy of your original upload. And because the fingerprint reads numeric text as the number it is, a round trip collapses 030 to 30 and 30.00 to 30; if a leading zero is load-bearing, it is an identifier, not a number, and it belongs in a column that reads as text.

Data you can take with you

None of this needs a server. You export a bundle the way you mounted the light: one command, or one button, pointed at the chain file the rest of Tamper Signal already writes. MIT licensed, Python 3.11+ or Node 18.17+, and the data your dashboard verified becomes data you can hand to anyone and they can verify too. If you want to watch a CSV come out one side and re-verify green as JSON on the other, the live demo does exactly that.

Take the data. The proof comes with it.