Security
Treeship's security properties and threat model.
Cryptographic primitives
| Primitive | Usage |
|---|---|
| Ed25519 | All artifact signatures |
| SHA-256 | Content-addressed artifact IDs, subject digests |
| DSSE | Envelope format for all signed statements |
| PAE | Pre-Authentication Encoding for deterministic signing |
| DPoP (RFC 9449) | Proof-of-possession for Hub authentication |
Key management
- One Ed25519 keypair per Treeship
- Private key encrypted at rest
- Key never leaves the local machine unless explicitly exported
- No key escrow, no key recovery service
Root access to the machine breaks all guarantees. The trust boundary is the machine itself. If someone has your private key and passphrase, they can forge artifacts.
Hub connection model: enrollment and operation
Two phases, two different security questions:
| Phase | Question | Mechanism |
|---|---|---|
Enrollment (treeship hub attach) | Should this machine be allowed to register a new hub connection (dock) with the Hub? | Human-in-the-loop device authorization (browser at treeship.dev/hub/activate, single-use challenge, atomic consumption, mandatory nonce on finalize). The CLI generates a fresh Ed25519 keypair for Hub / DPoP use and registers ship + dock public keys with the Hub. |
Operation (every hub push, session report, etc.) | Does this request come from someone who right now holds the private key for this connection? | DPoP (RFC 9449) -- a short-lived proof JWT bound to HTTP method and URL, signed with the connection private key. No long-lived bearer token for writes. |
Good user experience (no device code on every push) is compatible with this model: the code is only for bootstrapping trust (enrollment). Ongoing auth is cryptographic proof-of-possession, not repeated human proof.
What counts as a "device"?
The Hub identifies a hub connection by a dock_id (returned as hub_id in the CLI) and registered public keys. That is cryptographic device identity, not hardware attestation (no TPM / Secure Enclave binding in the default path). Copying ~/.treeship with valid hub connection material to another host is, from the Hub's perspective, the same connection -- the same as copying an SSH private key.
Standards alignment
- RFC 8628 (OAuth 2.0 Device Authorization Grant) -- same class of flow as "CLI / constrained client can't do a browser redirect."
- RFC 9449 (DPoP) -- proof-of-possession for API requests, aligned with modern sender-constrained access patterns (contrast with replayable bearer secrets for every call).
- Ed25519 -- widely deployed signature scheme for new systems.
Threats and mitigations (Hub path)
| Threat | Mitigation | Residual risk |
|---|---|---|
| Stolen DPoP proof from a log | Proofs are bound to method + URL and use jti / time window; replay should fail | Misconfiguration or bugs could weaken binding -- treat as implementation risk |
| Stolen connection private key (filesystem exfil) | Same class as stolen SSH key; use OS protections, FDE, least privilege, treeship hub kill to revoke the connection on the Hub | Until revoked, attacker can act as that connection without a new device code |
| Rogue enrollment | Device flow requires human approval; challenge is single-use and consumed atomically (see PLATFORM.md / Hub changelog for hardening history) | Phishing a user into approving a rogue attach -- partial mitigation is user education and clear UI on the activate page |
| Hub compromise | Does not rewrite past offline-verifiable receipts; may affect availability and published URLs | Out of scope for client-only verification; run verifiers offline for evidence |
Operational best practices
- Revoke lost or decommissioned machines:
treeship hub kill(or equivalent) so the Hub stops accepting that dock's DPoP. - Named connections (
treeship hub attach --name ...) for multiple tenants or lifecycles. - CI and automation: prefer short-lived or federated provisioning when you add it; avoid long-lived shared secrets in plain environment variables where OIDC or a vault is available.
- Be explicit if you add a separate bearer or API-key path: document it as a different trust tier, not a silent substitute for DPoP.
What Treeship does not protect against
- A compromised signing key (if someone steals your private key and passphrase, they can forge artifacts)
- Actions taken outside of Treeship (unwrapped commands leave no trace)
- Content confidentiality (Treeship signs metadata and digests, not content, but metadata can still be sensitive)
What Treeship does protect against
Treeship provides strong guarantees against retroactive tampering, unauthorized agent actions, broken chains of custody, and replay attacks.
- Retroactive tampering of action records
- Unauthorized agent actions (missing or expired approval)
- Broken chain of custody (missing parent links)
- Replay attacks (nonce binding, content-addressed IDs)
- Forged verification results (client-side WASM verifier runs independently of the Hub)
Revocation
The Hub publishes a signed revocation list at:
GET /.well-known/treeship/revoked.jsonThis list contains revoked artifact IDs and key fingerprints. Verifiers that have network access check this list. Offline verifiers skip it and rely solely on cryptographic validity.
Privacy
Treeship never stores the content of actions. It stores:
- Actor URIs
- Action names
- Timestamps
- Subject digests (SHA-256 hashes of content)
- Metadata you explicitly provide
To attest an action on sensitive data without revealing the data:
treeship attest action \
--actor agent://clinical-ai \
--action clinical.note.summarize \
--input-digest sha256:e3b0c44298fc1c149afb4c8996fb924The digest proves which document was processed. The content never enters the artifact.