verify
Verify a receipt by URL, file path, or local artifact ID. Optionally cross-verify against an Agent Certificate.
treeship verify accepts three target shapes and one optional cross-check:
- a URL to a receipt JSON document served by the Hub
- a file path to a local
.treeshipor.agentpackage directory - an artifact ID from local storage (the original v0.1 path; chain walk + signature checks)
Pair any of those with --certificate to also confirm the session stayed inside the envelope of an Agent Certificate.
Usage
treeship verify <url-or-path-or-artifact-id> [OPTIONS]Options
| Option | Description |
|---|---|
--certificate <path-or-url> | Cross-verify against an Agent Certificate (.agent package or URL) |
--no-chain | Verify only this artifact, do not walk the parent chain. Artifact-ID form only. |
--max-depth <N> | Maximum chain depth to walk (default 20). Artifact-ID form only. |
--full | Show full chain timeline with box-drawn cards. Artifact-ID form only. |
--format json | Global flag. Machine-readable output for CI pipelines. |
--quiet | Global flag. Exit code only, no output. |
Exit codes
| Code | Meaning |
|---|---|
0 | Verified |
1 | Verification failed (signature, Merkle, inclusion proof, determinism) |
2 | Cross-verification failed (cert mismatch, unauthorized tool call, expired cert) |
3 | Network or filesystem error (could not fetch URL or read file) |
Examples
treeship verify https://api.treeship.dev/v1/receipt/ssn_abc123 ✓ Downloaded receipt
✓ Merkle root verified
✓ 2/2 inclusion proofs passed
✓ Leaf count matches artifact count
✓ Timeline ordering verified
✓ Chain linkage intact
Verified. This receipt is authentic.
Session: ssn_abc123
Ship: ship_demo
Agent: researcher
Duration: 4m 22s
Actions: 28The human-readable mirror at treeship.dev/receipt/... is also accepted; the CLI rewrites /receipt/ to /v1/receipt/ automatically.
treeship verify .treeship/sessions/ssn_abc123.treeshipLoads the on-disk package, runs the full bag of checks (determinism, Merkle root, inclusion proofs, leaf count, timeline ordering), and prints the same checkmark output as the URL form.
treeship verify ./researcher.agentLoads the certificate from researcher.agent/certificate.json, verifies the embedded Ed25519 signature, and prints the certificate metadata.
treeship verify https://api.treeship.dev/v1/receipt/ssn_abc123 \
--certificate ./researcher.agentVerifies the receipt, then runs cross-verification:
✓ Certificate verified
✓ Ship IDs match
✓ All 12 tool calls authorized by certificate
Complete trust loop verified.Fails with exit code 2 if the receipt's ship_id does not match the certificate's identity.ship_id, the certificate is expired or not yet valid at the verify time, or any tool the session called is missing from the certificate's capabilities.tools.
treeship verify art_f7e6d5c4 --fullThe original local-artifact path. Walks the parent chain, verifies every Ed25519 signature, checks approval nonce binding, and prints a box-drawn timeline.
treeship verify $RECEIPT_URL --format json --certificate $CERT_PATH \
| jq -e '.cross_verify.ok == true'The JSON object includes outcome, per-check status, and (when --certificate is used) a cross_verify block with ship_id_status, certificate_status, the three tool-call lists, and a roll-up ok boolean. Pipe it to jq -e for a clean pass/fail gate.
What the verifier checks
The exact set of checks depends on what's available at the target:
- URL mode runs every check that's derivable from the receipt JSON alone: Merkle root recomputation, inclusion proofs, leaf count, timeline ordering, chain linkage. Per-artifact Ed25519 signature checks need the original envelope bytes, which only the local-storage and
.treeshippackage paths have. .treeshippackage mode runs everything URL mode runs plus determinism (the on-diskreceipt.jsonround-trips byte-identical) and any signature checks the package preserves..agentpackage mode verifies the certificate's Ed25519 signature against the public key embedded in the certificate. It does NOT chain to a trusted issuer; trust chaining is out of scope for v0.9.0.- Artifact-ID mode is the original local-storage path: walks the parent chain, verifies every signature, enforces nonce binding on approvals, checks expiry. See the chain-verification guide for full semantics.
The cross-verification path additionally verifies that the receipt's session.ship_id matches the certificate's identity.ship_id, the certificate was valid at verify time (issued_at <= now <= valid_until), and every tool the session called is present in the certificate's capabilities.tools list. See the cross-verification concept for the full semantics.
Pre-v0.9.0 receipts (before schema_version and session.ship_id were added) verify cleanly under the URL and package paths but cannot complete cross-verification because the receipt has no ship_id. The CLI reports Receipt has no ship_id (legacy receipt; cannot cross-verify) and exits 2.
Approval Authority replay rows (v0.9.10)
When the package contains evidence of consumed ApprovalUse records, verify emits one row per replay level — each row pinned to a specific invariant. The strongest level the package's evidence supports wins; nothing silently downgrades.
| Row | What it proves | Available when |
|---|---|---|
replay-package-local | No duplicate uses inside this package | Always |
replay-local-journal | The workspace's local journal has not exceeded max_uses for the recorded (grant_id, nonce_digest) | Verifier has Treeship workspace (treeship package verify from a workspace) |
replay-included-checkpoint | An embedded JournalCheckpoint's record_digest recomputes — record range wasn't tampered after sealing | Package carries one or more checkpoints |
replay-hub-org | A signed Hub checkpoint validates global single-use across machines | Package carries a kind: hub-org checkpoint that signature-verifies AND covers every embedded use_id. The Hub server itself is out of scope for v0.9.9-v0.9.10. |
Plus four bundle-level integrity rows added in v0.9.10:
| Row | What it proves |
|---|---|
approval-use-record-digest | Every embedded use record's record_digest recomputes from canonical form |
approval-use-nonce-binding | Each use's nonce_digest equals sha256(grant.nonce) for a content-addressed grant in the package |
approval-use-action-binding | Each consuming action's meta.approval_use_id resolves to a use in the package, and the action's approval_nonce hashes to that use's nonce_digest. The action envelope is content-addressed against its filename — substituted/forged envelopes fail this row. |
approval-use-chain-continuity | Embedded uses + checkpoints form a single connected linked list from one genesis: no forks, cycles, or disconnected subchains. |
The honesty rule
A row reports ✓ only when the matching evidence is present and verified. ✗ only when the evidence is present and a check failed. - (or absent) when the evidence isn't in the package at all — never silent pass. Treeship will physically refuse to print "global single-use enforced" or "replay-hub-org passed" without a real Hub-signed checkpoint that verifies cleanly AND covers every use_id.
Strict mode
treeship package verify --strict <pkg>Promotes every approval-evidence warning to a hard failure. replay-*, approval-use-record-digest, approval-use-nonce-binding, approval-use-action-binding, and approval-use-chain-continuity all participate. Existing receipt-determinism / event-log warnings stay warnings.
Useful in CI:
treeship package verify --strict ./session.treeship
echo "exit code: $?" # non-zero on any approval anomalyFor the full ladder and the v0.9.10 bypass paths the binding rows close, see Replay levels and Approval Authority.