Treeship
Get started

How it works

Signing, chaining, actors, verification, Hub, and Merkle proofs explained.

This page explains the mechanics behind Treeship. If you have not run the quickstart yet, start there. Come back here when you want to understand what is happening under the hood.

Signing

Every artifact Treeship produces is an Ed25519 signature over a structured payload.

When you run treeship init, a new Ed25519 keypair — the ship key — is generated and encrypted at rest on your machine. Private keys never leave your device. The ship key is the default signer, and agents you register with their own key (treeship onboard, or treeship agent register --own-key) sign with per-agent keys, each bound to its agent:// URI by a ship-signed certificate. Anyone holding the matching public key (or a certificate chain to a key they pinned) can verify any artifact you have ever created.

The signature format follows DSSE (Dead Simple Signing Envelope):

DSSE envelope
├── payloadType: application/vnd.treeship.action.v1+json
├── payload (base64-encoded JSON):
│   {
│     type:          "treeship/action/v1",
│     timestamp:     "2026-03-26T21:00:00Z",
│     actor:         "agent://researcher",
│     action:        "document.analyze",
│     parentId:      "art_previousstep",
│     approvalNonce: "nce_7f8e9d0a",
│     meta:          { ... }
│   }
└── signatures:
    [{ keyid: "key_9f8e7d6c", sig: "base64url(ed25519_sig)" }]

The signature is computed over PAE(payloadType, payload) (Pre-Authentication Encoding), which prevents type confusion attacks where a valid signature for one payload type could be reused for another.

Chaining

Artifacts form an ordered chain through parentId references.

When you create a new artifact, Treeship automatically sets its parentId to the ID of the most recent artifact in your local store. The artifact ID itself is derived from sha256(PAE_bytes)[..16], which means the ID is a content hash. If anyone tampers with the payload, the ID no longer matches and verification fails.

This gives you two properties:

  1. Ordering. You can walk the chain from any artifact back to the first one.
  2. Tamper evidence. Modifying, inserting, or removing any artifact breaks the chain for every artifact that follows it.

Actors

An actor is any entity that performs work inside your Treeship. Actors are identified by URI:

PrefixMeaningExample
human://A human identityhuman://alice
agent://An AI agent or automationagent://deployer
system://An external system (used in receipts)system://stripe-webhook

URIs are freeform after the prefix. Use whatever naming convention fits your organization.

An actor URI comes in two trust grades, and the verifier always tells you which one you got:

  • asserted — the artifact was signed by the shared ship key, and the actor URI is a label inside the signed payload. The signature proves which trust domain produced the artifact, not which actor within it. This is the default for unregistered actors, and it keeps the key surface small when you don't need more.
  • proven (key-bound) — the actor is a registered agent with its own Ed25519 key, and the artifact is signed by that key. A ship-signed agent_cert.v1 certificate binds the key to the agent:// URI, so "who did this" is cryptographically established, not just claimed.

treeship onboard <agent> does the registration, certificate, and capability card in one command; the MCP and A2A bridges provision per-agent keys automatically on startup. A single Treeship often has multiple actors — a developer, a CI bot, several AI agents — contributing to the same chain of work, each verifiable at its own grade.

Actors appear in different roles depending on the attestation type:

AttestationFlagRole
Action--actorWho performed the action
Approval--approverWho authorized the action
Handoff--from / --toSender and receiver of work
Receipt--systemExternal system producing the receipt

Being referenced as an actor does not grant permission to act. To take a sensitive action, an agent still needs an approval from an authorized approver.

Verification

Verification is fully offline and deterministic. No network call, no server, no account.

treeship verify art_f7e6d5c4

The verifier performs the following checks:

Recompute the artifact ID

Derive the expected ID from sha256(PAE_bytes)[..16] and compare it to the stored ID. If they do not match, the payload has been tampered with.

Check the Ed25519 signature

Verify the signature in the DSSE envelope against the signer's public key — the ship key, or the agent's own key for registered agents. A bad signature means the artifact was not produced by the claimed signer. The verdict also grades the actor: actor proof: proven (key-bound) when a certified per-agent key signed it, asserted when the actor URI is a label under the ship key.

Validate the statement schema

Confirm the payload conforms to the expected Treeship statement schema for its declared type.

Walk the parent chain

Follow parentId links recursively, verifying each ancestor artifact the same way. A broken link at any point fails the entire chain.

Check approval binding, scope, and replay (if present)

If the artifact references an approvalNonce, confirm a matching signed approval exists; if the approval has a scope, confirm the action's actor / action / subject fall inside the signed allow-lists; observe whether the same nonce was consumed earlier inside this verified package. Verify reports each property as a separate line. Beyond the package, the Approval Use Journal (shipped) enforces replay at consume time: treeship attest action reserves a grant use before signing.

Return the result

Exit 0 means the full chain is intact. Exit 1 means something failed, with reasons printed.

The verifier is open source and Apache-2.0 licensed. You can audit it, embed it in CI, or run it in a browser. Trust the math, not the infrastructure.

Hub

Hub is the optional sharing layer. It adds discoverability and public verify URLs, but it never adds trust. The signatures are the trust.

Hub connections

A hub connection is a named link between your local Treeship and a workspace on Hub. Hub connections work like tmux sessions: you can attach, detach, and kill them independently. Your Treeship stays the same regardless of how many hub connections exist.

tmuxTreeshipWhat it does
tmux new -s worktreeship hub attach --name workCreate a new named connection
tmux attach -t worktreeship hub use workSwitch to an existing connection
tmux detachtreeship hub detachDisconnect without destroying (keys preserved)
tmux kill-session -t worktreeship hub kill workRemove the connection permanently
tmux lstreeship hub lsList all connections

Hub connections are about audiences, not environments. The Treeship key stays the same across all hub connections. Artifacts never change. Hub connections only control where artifacts appear on Hub.

Treeship (one keypair, one artifact store)
 |-- hub: default  -> hub.treeship.dev/alice/personal
 |-- hub: acme     -> hub.treeship.dev/acme-corp/projects
 +-- hub: client-x -> hub.treeship.dev/clientx/deliverables

Most developers need exactly one hub connection.

When you attach to Hub, your Treeship's public key is registered. If the same public key appears in multiple hub connections, Hub knows they belong to the same Treeship. This is how Hub links workspaces to identities without requiring accounts or emails.

Attach and detach

Hub uses a device-authorization flow. When you run treeship hub attach, the CLI prints a URL and a short code. You visit the URL in a browser, enter the code, and approve the connection. From that point on, the CLI can push artifacts to your Hub workspace.

Detach disconnects the active hub connection but preserves all keys locally. You can re-attach later without re-authenticating.

Kill removes the hub connection from local config permanently. Artifacts already pushed to Hub remain there: they are content-addressed and independently verifiable.

DPoP authentication

Hub connections use DPoP (Demonstration of Proof-of-Possession) tokens. Each API request includes a proof that the caller holds the private key associated with the connection. This prevents token theft: a stolen access token is useless without the corresponding private key.

Hub connection commands

CommandDescription
treeship hub attachConnect to Hub (creates new or reconnects)
treeship hub attach --name <name>Create or reconnect a named hub connection
treeship hub detachDisconnect active hub connection (keeps keys)
treeship hub lsList all known hub connections
treeship hub statusShow active hub connection details
treeship hub use <name>Switch active hub connection
treeship hub push <id>Push artifact to active hub connection
treeship hub push <id> --hub <name>Push to a specific hub connection
treeship hub push <id> --allPush to all hub connections
treeship hub pull <id>Pull artifact from Hub
treeship hub openOpen workspace in browser
treeship hub kill <name>Remove a hub connection

Hub connection config

Hub connections are stored in ~/.treeship/config.json:

{
  "hub_connections": {
    "default": {
      "hub_id": "hub_661e5463912d",
      "key_id": "key_9f8e7d6c",
      "endpoint": "api.treeship.dev"
    },
    "work": {
      "hub_id": "hub_a2b3c4d5e6f7",
      "key_id": "key_9f8e7d6c",
      "endpoint": "api.treeship.dev"
    }
  },
  "active_hub": "default"
}

Each hub connection has its own DPoP keypair for authenticating with Hub. Adding or removing a hub connection never changes your Treeship key.

Configs from earlier versions (flat hub object or docks format) are automatically migrated to the hub_connections format on first run. No manual action required.

Workspace URLs

When you push an artifact to Hub, it gets a public URL:

https://treeship.dev/verify/art_f7e6d5c4

Anyone who opens that URL can verify the full artifact chain in their browser. No account, no install, no CLI required. The browser-based verifier runs the same checks as the CLI verifier described above.

Merkle proofs

Checkpoints are created by the publisher, not by Hub — the Hub holds no Merkle tree and creates nothing (that is the load-bearing invariant of the whole design). You run treeship checkpoint to sign a Merkle root over your artifacts, and treeship merkle publish to push it with per-artifact inclusion proofs; the Hub stores and serves them verbatim. These checkpoints enable two additional properties:

Inclusion proofs

Given an artifact and a checkpoint, anyone can verify that the artifact was included in the checkpoint's Merkle tree. This proves the artifact existed at the time the checkpoint was created, even if Hub later tries to deny it.

Anti-backdating

Because checkpoints are published at known intervals, you can prove that an artifact existed before a certain time. If your artifact appears in checkpoint N, and checkpoint N was published at time T, then the artifact must have been pushed before T. This prevents anyone (including Hub) from inserting artifacts after the fact and claiming they were always there.

How it works in practice

Day to day you rarely touch proofs directly: treeship onboard --publish runs the checkpoint-and-publish steps for you, and the browser verifier checks inclusion proofs whenever they are available. The properties hold whether or not you think about them — but the checkpoints exist because a publisher signed them, never because the Hub vouched.

If you want to verify a proof manually:

treeship audit --hub https://api.treeship.dev agent://f7e6d5c4
✓ inclusion verified
  checkpoint: cp_20260326_2100
  root:       sha256:a1b2c3...
  proof:      3 intermediate hashes

This confirms the artifact was included in the specified checkpoint and that the Merkle path is valid from the artifact's leaf node to the checkpoint root.