---
title: Security
description: Treeship's security properties and threat model.
---

import { Callout } from 'fumadocs-ui/components/callout';

## 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

<Callout type="warn">
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.
</Callout>

## 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

<Callout type="success">
Treeship provides strong guarantees against retroactive tampering, unauthorized agent actions, broken chains of custody, and replay attacks.
</Callout>

- 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.json
```

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

```bash
treeship attest action \
  --actor agent://clinical-ai \
  --action clinical.note.summarize \
  --input-digest sha256:e3b0c44298fc1c149afb4c8996fb924
```

The digest proves which document was processed. The content never enters the artifact.
