package
Inspect and verify .treeship session packages offline.
.treeship packages are portable Session Receipt bundles written by treeship session close. The package subcommands let you read and verify them without talking to any hub. See the Session Receipts concept page for the format.
treeship package inspect
Pretty-print the full contents of a Session Receipt.
treeship package inspect .treeship/sessions/ssn_42e740bd9eb238f6.treeshipArgument
| Argument | Description |
|---|---|
<path> | Path to a .treeship package directory |
Output includes:
- Session metadata: id, name, mode, status, started, ended, duration
- Participants: agent count, spawned sub-agents, handoffs, max delegation depth, hosts
- Agent graph: every agent instance with role, host, depth, and tool call count
- Side effects summary: files read, files written, tool invocations, processes, ports, network
- Timeline: up to 20 events with timestamp, event type, agent name, and detail
- Merkle: leaf count, root, inclusion proof count
- Artifacts: chain entries with content ids and digests
Example:
session receipt
type: treeship/session-receipt/v1
id: ssn_42e740bd9eb238f6
name: smoke-test
mode: AutoWorkspace
status: Completed
started: 2026-04-09T06:57:56Z
ended: 2026-04-09T07:04:38Z
duration: 401658ms
participants
agents: 1
spawned: 0
handoffs: 0
max depth: 0
hosts: 1
root agent: operator
agent graph
treeship-cli (operator) depth=0 tools=0 [active] @host_MacBookPro
side effects
files read: 0
files written: 0
tool calls: 0
processes: 0
ports opened: 0
network conns: 0
timeline
events: 2
06:57:56 session.started treeship-cli Session started
07:04:38 session.closed treeship-cli smoke test session
merkle
leaves: 4
root: mroot_effc020c67548f53
proofs: 4treeship package verify
Run local verification checks against a .treeship package. All checks are offline; no hub, no network. Since 0.31.2 the checks go past the Merkle tree into the signed envelopes the package carries: every sealed artifact's Ed25519 signature, its content-addressed id re-derived from the signed bytes, the parent links inside the signatures, and whether the signing keys are pinned trust roots.
treeship package verify .treeship/sessions/ssn_42e740bd9eb238f6.treeshipArgument
| Argument | Description |
|---|---|
<path> | Path to a .treeship package directory |
Options
| Option | Description |
|---|---|
--strict | Promote approval-evidence warnings, the unpinned-signer warning (signer_trust), and the sealed-set warnings (chain_completeness, receipt_binding, session_window) to failures. |
--structural | Check structure and approvals only; do not require or verify the envelopes. For packages built before 0.31.2, which carry no envelopes and otherwise fail at envelopes. The verdict line reads structural-pass: structure and approvals verified, signatures not checked, never verified. |
--format json | The same checks as a JSON array, for a script. |
Checks performed
| Check | What it validates |
|---|---|
receipt.json | File exists and parses as a valid Session Receipt |
type | Receipt type is treeship/session-receipt/v1 |
determinism | Re-serializing the parsed receipt byte-matches the on-disk file |
merkle_root | Recomputing the Merkle tree over the artifact ids produces the root stored in the receipt |
inclusion:<id> | Each stored inclusion proof verifies against the recomputed root |
envelopes | Only emitted when it fails: the package carries no artifacts/ envelopes (built before 0.31.2) or no readable keys.json. WARN under --structural |
signature:<id> | The envelope in artifacts/<id>.json verifies under a key in keys.json, its art_ id re-derives from the signed bytes, and the receipt's digest column matches |
chain_linkage | Every chained artifact names the previous one as parentId inside the signature, in the order the receipt lists them |
chain_completeness | WARN naming sealed artifacts that were signed during the session without --parent (marked unchained in the receipt): verifiable on their own, their order is the signer's unsigned claim |
signer_trust | PASS when every signing key is a pinned trust root or one of this ship's own keys, and the detail says which ("this ship's own" on the producer's machine is not a third party's decision); otherwise WARN with the treeship trust add … --yes command for each key, ready to paste in a script (FAIL under --strict) |
receipt_body_binding | PASS when the close record in record.json binds receipt.json: the timeline, side effects, tool usage and narrative are then under the producer's signature, and editing any of them fails receipt_binding. They remain the producer's own account of the session; the artifacts are the evidence. WARN on a package with no close record (built before 0.31.4) |
| receipt_binding | The close record in record.json verifies under a key in keys.json, names this session, and its signed receipt_digest equals the SHA-256 of receipt.json. Rewriting the sealed set after close fails here even when every per-artifact row passes. Missing record.json: WARN on a package with no artifact envelopes at all (nothing else to bind) or under --structural; otherwise FAIL -- a package that carries envelopes but no record is either a 0.31.2 or 0.31.3 build (both wrote envelopes with no record) or one that had record.json removed, and nothing signed says which |
| session_window | Every sealed artifact's signed timestamp falls inside the session's started_at to ended_at window, with two minutes of skew. WARN naming the artifacts outside it (FAIL under --strict) |
| network_scope | Only when the session declared a network scope (treeship declare --network): PASS with the count of recorded connections when every destination matched a declared pattern, WARN naming the destinations outside it. No scope declared, no row: the connections stand in side_effects.network_connections unjudged. Not promoted by --strict |
| coverage | PASS naming the sealed coverage.v1 receipt with its declared level, the harnesses and connection modes, and the observed event count; WARN when the package carries none (sealed before 0.31.6). A declared level is the harness's potential, not proof of what happened outside it. Never promoted by --strict |
| retries | Only when a sealed action names an earlier attempt (attest action --retry-of): PASS with the chain count when every chain is consistent (same action and actor, attempts count up, idempotency keys agree, the retried attempt is in the package, no two attempts each report a distinct effect); WARN naming the attempts that are not, for example two attempts with different output digests, which is the shape of a duplicated ticket rather than a recovered one. Never promoted by --strict |
| judgements | Only when the sealed set carries judgement.v1 receipts: PASS with the count and the judges named; WARN naming any judgement whose outcome is acted while its answer fell below its own declared threshold, or that names no threshold at all. A judgement is the caller's attestation of what a judge returned; the row does not re-run the judge. Never promoted by --strict |
| leaf_count | merkle.leaf_count matches the artifact count in the receipt |
| timeline_order | Timeline entries are in deterministic (timestamp, sequence_no, event_id) order |
Example output, from a session with four chained actions and one signed without --parent, on a ship whose key is not yet a pinned trust root:
package verification
package: .treeship/sessions/ssn_048b0184f2abd3e0.treeship
PASS receipt.json -- Parses as valid Session Receipt
PASS type -- Correct receipt type
PASS determinism -- receipt.json round-trips identically (structural, NOT a signature)
PASS receipt_body_binding -- timeline/side-effects/narrative are bound: the close record (record.json) signs the digest of the whole receipt.json ...
WARN receipt_body_binding -- timeline/side-effects/narrative are NOT signed in this package — only the artifacts and Merkle root are cryptographically bound. ...
PASS coverage -- art_9c1d2e3f4a5b6c7d: declared high (claude-code via native-hook+mcp+git-reconcile); 14 events observed across 4 types; 1 stated gap(s). ...
PASS merkle_root -- Merkle root matches recomputed value
PASS inclusion:art_7f536ce61435c17afd16c3af6484d4eb -- Inclusion proof valid
... (one line per artifact)
PASS signature:art_7f536ce61435c17afd16c3af6484d4eb -- Ed25519 signature by key_ba973a7083d45f8d verifies; id and digest re-derived from the signed bytes
... (one line per artifact)
PASS chain_linkage -- 4 chained artifacts each name the previous one as parent, inside the signature
WARN chain_completeness -- 1 sealed artifact(s) were signed during the session but never chained onto it (art_cd9bf1738355ae7d9ee10125cdf24359); signed and sealed, but their order relative to the chain is the signer's claim only
WARN signer_trust -- signature(s) verify for the key(s) the package names, but 1 of them are not pinned trust roots here: key_ba973a7083d45f8d. Pin what you have decided to trust: treeship trust add key_ba973a7083d45f8d ed25519:tyFBDCyMLbUV2NtlPaBaTiZvktwXxxOcI2j7ME9GSrw --kind cert_issuer --yes
PASS leaf_count -- Leaf count matches artifact count
PASS timeline_order -- Timeline is correctly ordered
17 passed, 0 failed, 3 warnings
✓ package verifiedThe last line is the verdict, and the word is chosen carefully: package verified only when every signature verifies under a key you have pinned (or this ship's own key); signatures-pass when every signature verifies but no signing key is pinned here, with --strict turning that into a failure; structural-pass under --structural; package verification failed otherwise. --format json returns one object: status (ok, warning, error), verdict, message, passed, failed, warnings, signer_pinned, and checks, an array of {name, status, detail} for every row above.
Any FAIL exits nonzero and prints a warning summary. WARNs (for example, signer_trust on a key you have not pinned) are non-fatal. A package built before 0.31.2 fails at envelopes because there is nothing in it to check a signature against; run it with --structural to read its structure and approvals, and verify its artifacts from a bundle or the hub with treeship verify.
Local verification is the trust anchor. A hub serving a receipt at https://treeship.dev/receipt/<session_id> is a convenience layer; the cryptographic trust comes from running treeship package verify on the downloaded package, reading every row, and pinning the signer's key yourself.
Package format
A .treeship package is a directory containing:
<session_id>.treeship/
├── receipt.json canonical Session Receipt v1
├── merkle.json standalone copy of the Merkle section
├── render.json Explorer render hints
├── preview.html static preview you can open in a browser
├── keys.json public keys the envelopes were signed with, by key id (0.31.2+)
├── record.json the signed session.v1 close record, over the digest of receipt.json (0.31.4+)
├── artifacts/ the signed DSSE envelope of every sealed artifact (0.31.2+)
└── proofs/
├── art_xxx.proof.json
└── ...receipt.json is the authoritative artifact. Everything else is derived from it and can be regenerated deterministically.
Sharing packages
You can share a .treeship package in three ways:
- Upload to a hub via
treeship session report, which prints a public URL likehttps://treeship.dev/receipt/<session_id>that stays up until youtreeship hub unpublishit. - Tar and send the directory directly:
tar czf ssn_xxx.treeship.tar.gz .treeship/sessions/ssn_xxx.treeship/. Recipients extract and runtreeship package verify. - Commit the directory to git or upload to any object store. The format is deterministic so two identical inputs produce byte-identical packages.