Don't trust the tool. Read it.
An auditor's job is to verify evidence, not to trust tools on faith. So the verifier is licensed Apache-2.0, rebuilds byte for byte from its source, and makes no network calls. The CLI cannot verify receipts for audit purposes if auditors cannot verify the CLI itself.
- Current version
- v1.7.2
- Public release
- On GitHub, with signed checksums
- Licence
- Apache-2.0
- Standard
- DSR/1.0
- Platforms
- linux · macOS · windows
- Network calls
- 0
- Third-party dependencies
- 0
- Account required
- none
- Build
- reproducible, byte for byte
The auditor's actual question.
The repository keeps its own trust-model document, docs/auditor-trust-model.md, versioned with the tool.
What it verifies
Recomputed locally from the file in front of you.
- Key authority
- The key ID the receipt names matches the key ID declared in the file you supplied. It runs first, so the wrong key is reported as the wrong key rather than as a bad signature. Where either side declares no ID, the comparison is skipped.
- Signature validity
- The signature is checked over the receipt's canonical bytes: Ed25519, or RSA-PSS or ECDSA over SHA-256 for organisations that bring their own key. Every primitive is Go's standard library.
- Sealed receipts
- Where a receipt carries a SHA-256 of its canonical bytes instead of a signature, the verifier recomputes it and compares in constant time. The R0, R2-F and R2-R receipts issued before 27 October 2026 are sealed rather than signed, and this verifier does not check them: their hashes cover a form it does not rebuild. Déjà's Ledger export lists them in checked-online.json, for deja.dev/verify to check.
- Required fields
- The receipt parses and carries every envelope field its type requires. Identifiers inside it, such as a commit hash or a pull-request reference, are covered by the signature. They are not checked for format.
- Bundle integrity
- For a bundle, the manifest's signature, and that its sequence numbers run without a gap. A resolution receipt that points at an attribution receipt missing from the bundle is reported. It does not fail the run on its own.
What it trusts
Stated plainly, because a trust model with no trusted inputs is a lie.
- The public key you were given
- The CLI never fetches a key. It reads a PEM file, a base64 Ed25519 key, or the JSON that Déjà's public signing-key endpoint returns, and always from a local path. Where that file came from is a question the tool cannot answer for you.
- The bundle you were handed
- It verifies what is in front of it. A receipt that was never issued cannot be missed by a tool that never saw it.
- The release, or your toolchain
- Run the published binary and you are trusting the release: its checksums are GPG-signed, and every platform's binary rebuilds from the tagged source byte for byte. Build it yourself and you are trusting your Go toolchain instead.
What it does not do
The list that makes the other two credible.
- Prove an incident occurred
- It proves a signal was received and sealed. What the signal meant is a question for the systems that sent it.
- Attest to time
- A signature establishes content and key authority, never when something happened. Issuance timestamps are asserted by Déjà.
- Decide what a failure means
- It counts failures by receipt and by check, and describes how they are distributed. It totals signature failures on a line headed Tampered and, past ten anomalies, names the pattern they are consistent with. Both describe the shape of the failures. The cause is the auditor's finding to make.
- Guess
- A receipt in a canonical form this release does not implement, or an export missing fields, is reported as cannot-verify, exit 5. It is never counted as a failure.
- Contact Déjà
- Not for keys, not for telemetry, not for updates. net/http is not in the binary's import graph. The bare net package is, because Go's X.509 parser uses it to read addresses in certificates. Nothing in the verifier calls it.
The third column is the one worth reading twice. A verifier that claimed to establish everything would be establishing nothing you could rely on.
Nothing in it asks who you are.
No account, no licence key, no registration, no telemetry. Install it, point it at a bundle, and verify against the key you were given.
dsr-verifier-cli v1.7.2 is publicly released. Download the binary for your platform from its release page, with the checksums in SHA256SUMS and their signature in SHA256SUMS.asc.
Every release ships binaries for macOS on both architectures, Linux on amd64 and arm64, and Windows on amd64, each listed with its SHA-256 checksum in a GPG-signed SHA256SUMS file. If you'd rather not run our binary at all, the build is reproducible: check out the tag, use the Go version its go.mod pins, run the command in BUILD.md, and compare hashes.
There are no third-party dependencies. The go.mod declares the module and the Go version and nothing else, and every cryptographic primitive comes from the standard library. That is a deliberate constraint rather than a boast: a supply chain is a trust dependency, and this tool exists to have as few as possible.
gpg --verify SHA256SUMS.asc SHA256SUMS sha256sum --check --ignore-missing SHA256SUMS
dsr-verifier-cli verify <receipt> --key <pubkey> dsr-verifier-cli verify <receipt> --byok-key <pubkey> dsr-verifier-cli verify-bundle <bundle> --key <pubkey> dsr-verifier-cli info <receipt>
go list -deps ./cmd/dsr-verifier-cli | grep net/http # prints nothing
Keys are a local file: PEM, a base64 Ed25519 key, or the signing-key JSON. Never fetched. --byok-key is for receipts signed with your organisation's own RSA or ECDSA key.
Runs the same on an air-gapped machine.
$ dsr-verifier-cli verify-bundle Q2-2026.dsr.bundle --key vault.pub --no-color
┌─ DSR Verifier · v1.7.2 · Bundle Verification ───────────────────────────┐
│ Bundle: Q2-2026.dsr.bundle │
│ Using key: vault.pub │
└─────────────────────────────────────────────────────────────────────────┘
Bundle: Q2-2026.dsr.bundle (137.6 KB)
Vault: vlt_bundle_test
Period: 2026-05-01 to 2026-05-31
Frameworks: SOC 2
Verifying 60 receipts...
R1 (Attribution) 60/60 passed
✓ Verifying bundle signature .......................................... OK
Bundle signed by: key_bundle_test
Bundle signature: valid
✓ Verifying sequence integrity ........................................ OK
Sequence range: 1–60 (no gaps detected)
✓ Verifying causal chain consistency .................................. OK
No cross-receipt causal references found
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✓ Bundle verified · all 60 receipts cryptographically valid
Summary:
Pass rate: 60 / 60 (100%)
Tampered: 0
Missing: 0
offline · zero network calls
Output: verification report written to Q2-2026.verification.json
── Anomaly Pattern Analysis ────────────────────────────────────────────────
Anomaly pattern analysis skipped (fewer than 10 anomalies)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━Receipts and the signed manifest travel in one file. The key does not, deliberately: it should reach you by a different route than the bundle did. Disconnect the machine entirely and the output is identical, which is the property that makes this evidence rather than a service.
Counts are per receipt type. Most receipts are signed; the receipts of three classes issued before those classes were signed are sealed with a content hash instead. The receipt-types table says which. Each is checked by its own method rather than collapsed into one reassuring tick.
Every run writes an unsigned JSON report beside the bundle, for your workpapers. It is unsigned deliberately: a verification report signed by us would reintroduce exactly the dependency the tool exists to remove. The only other file it writes is an append-only verifier.log in the working directory, one line per run, and --no-log turns it off.
The line reading “offline · zero network calls” is printed on every run. It is a statement, not a measurement; the evidence for it is the import graph.
The verifier reports. You conclude.
$ dsr-verifier-cli verify-bundle Q2-2026-altered.dsr.bundle --key vault.pub --no-color
┌─ DSR Verifier · v1.7.2 · Bundle Verification ───────────────────────────┐
│ Bundle: Q2-2026-altered.dsr.bundle │
│ Using key: vault.pub │
└─────────────────────────────────────────────────────────────────────────┘
Bundle: Q2-2026-altered.dsr.bundle (128.5 KB)
Vault: vlt_bundle_test
Period: 2026-05-01 to 2026-05-31
Frameworks: SOC 2
Verifying 56 receipts...
R1 (Attribution) 48/56 passed
✓ Verifying bundle signature .......................................... OK
Bundle signed by: key_bundle_test
Bundle signature: valid
✗ Verifying sequence integrity ...................................... FAIL
Error class: malformed_receipt
Missing sequences (first 4): [40 41 42 43]
✓ Verifying causal chain consistency .................................. OK
No cross-receipt causal references found
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✗ Bundle verification FAILED · 2 check(s) failed
Failure: Sequence integrity · malformed_receipt
The bundle has 4 missing sequence number(s) in the range 1–60. A
complete bundle must include every receipt in its declared sequence
range.
Receipt failures:
seq 11 · rcpt_0011 · signature_invalid
seq 14 · rcpt_0014 · signature_invalid
seq 17 · rcpt_0017 · signature_invalid
seq 20 · rcpt_0020 · signature_invalid
seq 23 · rcpt_0023 · signature_invalid
... and 3 more failure(s)
Recommended actions for auditor:
• Do NOT treat this bundle as verified evidence
• Request a fresh bundle directly from the issuing organization
• Document this discrepancy in your audit findings
Summary:
Pass rate: 48 / 56 (85%)
Tampered: 8
Missing: 0
offline · zero network calls
Output: verification report written to Q2-2026-altered.verification.json
── Anomaly Pattern Analysis ────────────────────────────────────────────────
Pattern: consistent_with_isolated_corruption (confidence 0.68)
Confidence: 0.996 (zone detected (moderate confidence, 2 tests))
Anomalies: 12
Zone: payments (100% of anomalies, <0.001)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━A failing run lists what did not verify, by check and by receipt, and describes how the failures are distributed. It exits 1, which a script can tell apart from exit 5, a verdict the verifier could not reach. The full table is in the system documentation.
One line in this output is wrong, and it is left in rather than avoided. Missing: 0 sits beneath four missing sequence numbers because that row counts receipts that failed to parse, not gaps. It is a labelling defect in v1.7.2. The sequence check above it is correct, and so is the exit code.
verifier.log is only ever appended to. A later passing run is written beneath a failed one, never over it.
- Reported
Every failure
By sequence number and receipt ID. The terminal lists five and says how many more. The JSON report carries all of them, so nothing is a summary you have to take on trust.
- Reported
The distribution
Past ten anomalies, whether they concentrate in one zone or one window of time, whether they cascade, and the pattern they are consistent with, with a confidence. Concentration is a fact about the data. What produced it is not.
- Labelled, not concluded
Tampered
The summary totals signature failures on a line headed Tampered. It means the signature did not verify over the bytes in front of it. Whether anyone tampered is the finding you write, not one the tool makes.
You were sent a bundle. Now what.
- 01
Install, or build from source
Download the release binary for your platform and check it against its GPG-signed checksums, or build it yourself from the tagged source and compare hashes.
- 02
Verify the bundle
One command, against the bundle and the public key you were given, ideally by a different route than the bundle. Disconnect the machine first if your engagement requires it. The output will be identical.
- 03
Keep your own copy
Retain the bundle and the verification report. A copy held outside the vendor's infrastructure is the strongest control in this whole model: a later export that disagrees with an earlier one is itself the finding.
- 04
Ask us things directly
Protocol questions, a test bundle to evaluate against, or a walkthrough before your first engagement. There's no sales process attached to any of that.
Where the detail lives.
- DSR/1.0 specification →Envelope, canonicalisation, signing scheme and verification procedure.
- Receipt classes →What each class records, how it is signed, and which earlier receipts are sealed with a content hash instead.
- Trust model →The repository's own trust-model document, versioned with the tool.
- Troubleshooting →The most common verification failures and what each usually means.
- Cryptographic design →For a security reviewer: primitives, constant-time comparison, the import-graph audit.
- Déjà's own posture →How the platform that issues the receipts is secured.