Treeship
CLI reference

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.treeship

Argument

ArgumentDescription
<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:  4

treeship 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.treeship

Argument

ArgumentDescription
<path>Path to a .treeship package directory

Options

OptionDescription
--strictPromote approval-evidence warnings, the unpinned-signer warning (signer_trust), and the sealed-set warnings (chain_completeness, receipt_binding, session_window) to failures.
--structuralCheck 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 jsonThe same checks as a JSON array, for a script.

Checks performed

CheckWhat it validates
receipt.jsonFile exists and parses as a valid Session Receipt
typeReceipt type is treeship/session-receipt/v1
determinismRe-serializing the parsed receipt byte-matches the on-disk file
merkle_rootRecomputing 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
envelopesOnly 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_linkageEvery chained artifact names the previous one as parentId inside the signature, in the order the receipt lists them
chain_completenessWARN 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_trustPASS 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_bindingPASS 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 verified

The 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:

  1. Upload to a hub via treeship session report, which prints a public URL like https://treeship.dev/receipt/<session_id> that stays up until you treeship hub unpublish it.
  2. Tar and send the directory directly: tar czf ssn_xxx.treeship.tar.gz .treeship/sessions/ssn_xxx.treeship/. Recipients extract and run treeship package verify.
  3. Commit the directory to git or upload to any object store. The format is deterministic so two identical inputs produce byte-identical packages.