Replay levels
What each replay-check level actually proves — and what it doesn't.
When treeship verify or treeship package verify runs against a package that contains an action consuming an Approval Grant, it can speak to up to four replay levels. Each level is a separate check, emitted as its own row, with its own evidence requirement. The output reports the strongest level it actually achieved — never overclaims, never silently downgrades.
✓ approval binding nonce matched a signed approval
✓ approval scope actor / action / subject matched approval scope
✓ replay package-local no duplicate approval use inside package
✓ replay local-journal local Approval Use Journal passed, use 1/1
✓ replay checkpoint included journal checkpoint verified
✓ replay hub-org cp_42 signed by hub://zerker-org verifies; covers 1 use(when no Hub-signed checkpoint is embedded, the last row reads - replay hub-org not checked (no Hub checkpoint in package) instead).
The four levels
| Level | What it proves | What it doesn't | Available in |
|---|---|---|---|
package-local | No duplicate use inside this package | Says nothing about uses on this machine outside the package, or on other machines | Always |
local-journal | The recorded use is within max_uses per the workspace's local journal | Says nothing about other machines | Verifier has Treeship workspace |
included-checkpoint | An embedded JournalCheckpoint's record_digest verifies offline | Doesn't bind to your workspace journal; only proves the checkpoint records weren't tampered after sealing | Package carries checkpoints |
hub-org | A signed Hub checkpoint validates global single-use across machines | — | Available since v0.9.9 (consumer side). Verifies an embedded Hub-signed JournalCheckpoint and its coverage. The Hub server itself is out of scope for v0.9.9. |
How each level is checked
package-local (always offline, always available)
Reads the package's embedded approvals/uses/*.json files. Counts uses sharing (grant_id, nonce_digest) and compares to the use record's max_uses snapshot.
- Pass: every grant's recorded uses are within
max_uses. Two legitimate uses of amax_uses=2grant pass. - Fail: uses exceed
max_uses, or two records carry the sameuse_id(always a corruption — never legitimate).
This is the v0.9.6 behavior, preserved.
local-journal (CLI-side, workspace-aware)
Calls journal::find_use_for_action(grant_id, nonce_digest, max_uses_hint) against the workspace's <config>/journals/approval-use/. Asks the verify-time question — "does the recorded use's use_number stay within bounds?" — distinct from consume-time's "would the next use exceed?".
- Pass: the journal has a record for this
(grant_id, nonce_digest)and itsuse_number ≤ max_uses. - Fail: journal record exists but exceeds
max_uses. - Warn: no journal in this workspace; falls back to package-local. Bare-package verification in someone's inbox doesn't error here.
included-checkpoint (offline)
Reads the package's embedded approvals/checkpoints/*.json. Recomputes each record_digest from canonical form and compares.
- Pass: every checkpoint's stored digest matches the recompute.
- Fail: any checkpoint was tampered after sealing.
A checkpoint is itself a Merkle commitment over a contiguous range of journal records. The Hub-signed variant (checkpoint_kind: hub-org) carries an embedded Ed25519 signature over the canonical signing bytes; that signature is what the hub-org row checks.
hub-org (consumer side, since v0.9.9)
A JournalCheckpoint with checkpoint_kind: hub-org carries five extra fields: hub_id, hub_public_key, hub_signature, signed_at, and covered_use_ids. The verifier emits replay-hub-org PASS only when every gate clears:
- Signature verifies. The Ed25519 signature decodes against the embedded public key over
canonical_hub_signing_bytes()(every signed field except the signature itself). - Coverage is explicit. Every
use_idin the package appears incovered_use_ids. A checkpoint that verifies but doesn't cover a particular use cannot promote the row for that use. - All required fields are populated.
hub_id,hub_public_key,hub_signature,signed_atnon-empty.
Anything short of all three: the row is ✗ (warning by default, fail under --strict). When no Hub-kind checkpoint is embedded at all, the row reports not checked (no Hub checkpoint in package) — absent evidence is reported as absent, never as a silent pass.
Out of scope for v0.9.9: the Hub server itself — the thing that signs checkpoints — lives in a separate release. v0.9.9 ships only the consumer-side verifier. Until you have a Hub signing checkpoints and embedding them in packages, the row will keep reading not checked.
The honesty rule, baked in: Treeship will never print "global single-use enforced" or "hub-org passed" without a real Hub checkpoint that signs cleanly AND covers every use. The check exists to fire when evidence exists; absent evidence stays explicit about what's missing.
Reading the report
treeship package inspect renders each level as a discrete line under the Approval Authority panel:
approval authority (1 use from 1 grant)
human://piyush approved agent://deployer (deploy.production)
grant_id: art_2a325283550936d0c32a15ba
subject: env://production max_uses: 1 uses recorded: 1
use 1/1 use_id=use_cf0cb9fea3540e92
✓ package-local no duplicate use inside package
✓ local-journal local Approval Use Journal passed, use 1/1
- included-checkpoint no journal checkpoint included in package
- hub-org not checked (no Hub checkpoint in package)When the package embeds a Hub-signed checkpoint that covers each use, the bottom two rows promote:
✓ package-local no duplicate use inside package
✓ local-journal local Approval Use Journal passed, use 1/1
✓ included-checkpoint cp_42 verified offline
✓ hub-org use use_cf0cb… signed by hub://zerker-orgA ✓ means the level was actually checked and passed. A ✗ means it was checked and failed. A - means the level was not checked — usually because the evidence wasn't present, not because the check was skipped.
Strict mode
treeship package verify --strict promotes approval-evidence warnings to failures. Useful in CI:
treeship package verify --strict ./session.treeship
echo "exit code: $?" # non-zero on any approval anomalyExisting receipt-determinism and event-log warnings stay warnings. Only the replay-* and approval-use-integrity rows are promoted.
Decision Cards
The "Key decisions" section under package inspect includes a Replay-warning card when no verified Hub-signed checkpoint covers every use in the package:
⚠ Replay posture: no verified Hub coverage
Verifiers without access to your workspace's local journal can
only check package-local replay (duplicate uses inside this package).
Hub-org replay is not asserted -- a global single-use guarantee
requires a signed Hub checkpoint that covers every use_id in this
package. v0.9.9 supports verifying such checkpoints when present;
the Hub signer itself is out of scope for this release.
evidence:
approval uses: 1 (see approval authority panel above)
hub checkpoints: 0 embedded (none)
verify rows: replay-package-local, replay-local-journalWhen a Hub-signed checkpoint is embedded and verifies, the card stays silent — the Approval Authority panel's ✓ hub-org row already tells the story.
Every Decision Card carries evidence pointers (grant_id, approval_use_id, action_artifact_id, verify check rows). No LLM, no invented intent — the narrative is generated mechanically from the receipt.
See also
- Approval Authority — the wider model
- Approval Use Journal — the local store
- Approvals — minting, consuming, verifying flows
- Trust fabric — how this fits the rest of the system