Skip to main content

Self-Hosting

Run your own Treeship instance for maximum control and trust minimization.

Architecture

Treeship has three components. You can self-host all of them or just the API.
┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   Agents    │────▶│  Treeship   │────▶│ PostgreSQL  │
│  (SDK/CLI)  │     │    API      │     │  Database   │
└─────────────┘     └─────────────┘     └─────────────┘

                    ┌──────┴──────┐
                    ▼             ▼
             ┌───────────┐ ┌───────────┐
             │  Next.js  │ │ Verifiers │
             │  Frontend │ │ (public)  │
             └───────────┘ └───────────┘
ComponentWhat it doesRequired?
API (treeship-api/)Signs attestations, stores data, verifies proofsYes
Frontend (root Next.js)Verification pages, approval pages, dashboardOptional
DatabasePostgreSQL (production) or SQLite (development)Yes

Quick Start

Option 1: Setup Script

git clone https://github.com/zerkerlabs/treeship.git
cd treeship
chmod +x setup.sh
./setup.sh
This generates your signing key, creates .env files, and installs dependencies. Follow the printed instructions to start.

Option 2: Docker Compose

git clone https://github.com/zerkerlabs/treeship.git
cd treeship

# Generate a signing key first
cd treeship-api
pip install cryptography
python3 -c "
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from cryptography.hazmat.primitives import serialization
import base64
key = Ed25519PrivateKey.generate()
pem = key.private_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PrivateFormat.PKCS8,
    encryption_algorithm=serialization.NoEncryption()
)
print(base64.b64encode(pem).decode())
"
cd ..

# Set your signing key and start
export TREESHIP_SIGNING_KEY="paste-base64-key-here"
export TREESHIP_API_KEY="ts_live_$(openssl rand -hex 24)"
docker compose up
This starts the API, frontend, and PostgreSQL. The API is at localhost:8000, frontend at localhost:3000.

Option 3: Manual Setup

# 1. API
cd treeship-api
pip install -r requirements.txt
cp .env.example .env
# Edit .env with your values (see Environment Variables below)
uvicorn main:app --reload --port 8000

# 2. Frontend (new terminal)
cd ..
npm install
cp .env.example .env.local
# Edit .env.local
npm run dev

Environment Variables

API (treeship-api/.env)

VariableRequiredDescription
DATABASE_URLYesDatabase connection string
TREESHIP_SIGNING_KEYYesBase64-encoded Ed25519 PEM key (signs all attestations)
TREESHIP_API_KEYRecommendedAdmin API key for bootstrapping
TREESHIP_WEB_URLNoBase URL for verification links (default: https://treeship.dev)
Optional features:
VariableFeatureWhere to get it
WORLD_ID_APP_IDHuman authorization (proof-of-personhood)developer.world.org/apps
WORLD_ID_ACTIONWorld ID action name (default: authorize-attestation)Set in World ID dashboard
SINDRI_API_KEYZK proof generation (Groth16)sindri.app
RESEND_API_KEYEmail verification for API key managementresend.com

Frontend (.env.local)

VariableRequiredDescription
NEXT_PUBLIC_API_URLYesURL of your Treeship API (e.g. http://localhost:8000)
Optional features:
VariableFeatureWhere to get it
WORLD_ID_SIGNING_KEYSigns World ID proof requests (hex, starts with 0x)developer.world.org/apps
NEXT_PUBLIC_WORLD_ID_RP_IDWorld ID Relying Party identifierSame dashboard
The World ID signing key is a server-side secret that generates rp_context for the IDKit widget. It is NOT exposed to users. If you don’t need human authorization, skip all World ID variables.

Accounts & Services

Here’s every external service Treeship can use, and which are required:
ServicePurposeRequired?Free Tier?
PostgreSQLDatabaseYes (or SQLite for dev)Many free options
NoneCore attestation signingYesBuilt-in (Ed25519)
World IDHuman authorization (proof-of-personhood)NoYes
SindriZK proof generationNoYes (limited)
ResendEmail verification codesNoYes (100/day)
To run Treeship with just attestation + verification, you need zero external accounts. Everything else is opt-in.

Generate a Signing Key

The signing key creates Ed25519 signatures for all attestations. Generate one:
python3 -c "
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from cryptography.hazmat.primitives import serialization
import base64, hashlib

key = Ed25519PrivateKey.generate()
pub = key.public_key()

pem = key.private_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PrivateFormat.PKCS8,
    encryption_algorithm=serialization.NoEncryption()
)

pub_bytes = pub.public_bytes(
    encoding=serialization.Encoding.Raw,
    format=serialization.PublicFormat.Raw,
)

print('TREESHIP_SIGNING_KEY=' + base64.b64encode(pem).decode())
print('Key ID: ' + hashlib.sha256(pub_bytes).hexdigest()[:16])
"
Store the TREESHIP_SIGNING_KEY value in your API’s .env file. The Key ID is for reference only.

Create Your First API Key

After starting the API, create an API key:
# Using the admin API key set in TREESHIP_API_KEY
curl -X POST http://localhost:8000/v1/keys \
  -H "Content-Type: application/json" \
  -d '{"email": "admin@example.com", "name": "Admin Key"}'
Or set TREESHIP_API_KEY in your .env — this acts as a master key that always works.

Test Your Instance

# Health check
curl http://localhost:8000/health

# Create an attestation
curl -X POST http://localhost:8000/v1/attest \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_slug": "my-agent",
    "action": "Hello from self-hosted Treeship",
    "inputs_hash": "test123"
  }'

# Verify it (use the attestation_id from the response)
curl http://localhost:8000/v1/verify/ATTESTATION_ID

# Check the public key
curl http://localhost:8000/v1/pubkey

Database

SQLite (Development)

DATABASE_URL=sqlite+aiosqlite:///./treeship.db
Zero setup. Database file created automatically. Not recommended for production.

PostgreSQL (Production)

DATABASE_URL=postgresql+asyncpg://user:pass@host:5432/treeship
Tables are created automatically on first startup. Migrations run automatically via migrate.py.

Deploy to Production

Railway

npm install -g @railway/cli
railway login
cd treeship-api
railway up
Set environment variables in the Railway dashboard.

Docker

cd treeship-api
docker build -t treeship-api .
docker run -p 8000:8000 \
  -e DATABASE_URL=postgresql+asyncpg://... \
  -e TREESHIP_SIGNING_KEY=... \
  -e TREESHIP_API_KEY=... \
  treeship-api

Any Platform

Treeship API is a standard Python ASGI app. Deploy anywhere that runs Python:
  • Render, Fly.io, Heroku, AWS ECS, Google Cloud Run, etc.
  • Entry point: uvicorn main:app --host 0.0.0.0 --port $PORT
  • Health check: GET /health

Scaling

The API is stateless except for the signing key. Scale horizontally by:
  1. Using a shared PostgreSQL database
  2. Setting the same TREESHIP_SIGNING_KEY on all instances
  3. Load balancing across instances

Key Rotation

  1. Generate a new signing key
  2. Update TREESHIP_SIGNING_KEY in your environment
  3. Restart the service
  4. Old attestations remain verifiable — verifiers can check against the old public key stored with each attestation

World ID Setup (Optional)

To enable human authorization with proof-of-personhood:
1

Create a World ID app

Go to developer.world.org/apps and create a new app. Note your app_id and rp_id.
2

Create an action

In the app settings, create an action called authorize-attestation (or any name you prefer).
3

Get the signing key

Copy the RP signing key (hex format, starts with 0x).
4

Set environment variables

API (treeship-api/.env):
WORLD_ID_APP_ID=app_your_app_id
WORLD_ID_ACTION=authorize-attestation
Frontend (.env.local):
WORLD_ID_SIGNING_KEY=0x_your_signing_key
NEXT_PUBLIC_WORLD_ID_RP_ID=rp_your_rp_id
Verification methods supported: Passport, Government ID, Orb (iris scan).