Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.steward.fi/llms.txt

Use this file to discover all available pages before exploring further.

Self-Hosting Steward

Steward is designed to be self-hosted. This guide covers deploying Steward on your own infrastructure.

Requirements

  • Runtime: Bun v1.0+
  • Database: PostgreSQL 15+ (or Neon for serverless)
  • Node.js: 18+ (for build tooling)
  • OS: Linux (Ubuntu 22.04+ recommended), macOS, or Windows WSL2

Quick Start

Environment Variables

Core (Required)

# Master encryption password — derives the AES-256-GCM key for all wallet/secret encryption
# CRITICAL: If lost, all encrypted keys are permanently inaccessible
STEWARD_MASTER_PASSWORD=your-strong-master-password-32-chars-min

# PostgreSQL connection string (not needed in embedded mode)
DATABASE_URL=postgres://user:pass@localhost:5432/steward

API Server

STEWARD_PORT=3200                           # API port (default: 3200)
STEWARD_BIND_HOST=0.0.0.0                   # Bind address
STEWARD_JWT_SECRET=separate-jwt-signing-key  # Defaults to master password if not set
STEWARD_PLATFORM_KEY=your-platform-admin-key # For platform-level management
STEWARD_EMBEDDED=false                       # Set true for PGLite mode

Blockchain

RPC_URL=https://mainnet.base.org             # Default EVM chain RPC
CHAIN_ID=8453                                # Default chain ID
SOLANA_RPC_URL=https://api.mainnet-beta.solana.com

Authentication

# Passkeys (WebAuthn)
PASSKEY_RP_ID=myapp.com                      # Relying party ID (your domain)
PASSKEY_ORIGIN=https://myapp.com             # Expected origin for ceremonies
PASSKEY_RP_NAME="My App"                     # Display name in passkey dialogs

# Email magic links
RESEND_API_KEY=re_xxxxxxxxxxxx               # Resend.com API key
EMAIL_FROM=auth@myapp.com                    # Verified sender address
APP_URL=https://myapp.com                    # Used for magic link URLs

# OAuth providers (optional, enable any combination)
GOOGLE_CLIENT_ID=xxxx.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-xxxx
DISCORD_CLIENT_ID=1234567890
DISCORD_CLIENT_SECRET=xxxx
TWITTER_CLIENT_ID=xxxx
TWITTER_CLIENT_SECRET=xxxx

Database Setup

Run the Drizzle migrations to set up the database schema:
cd packages/db
bun run drizzle-kit push

# Or generate and run migrations
bun run drizzle-kit generate
bun run drizzle-kit migrate

Running the API

# Development
cd packages/api
bun run dev

# Production
bun run start
Verify it’s running:
curl http://localhost:3200/health
# {"ok":true,"timestamp":"2026-03-26T12:00:00Z"}

Creating Your First Tenant

curl -X POST http://localhost:3200/tenants \
  -H "Content-Type: application/json" \
  -d '{
    "id": "my-org",
    "name": "My Organization",
    "apiKeyHash": "your-api-key"
  }'
If you pass a non-hash string as apiKeyHash, Steward will SHA-256 hash it for you. The raw API key is returned once on creation.

Production Deployment

Systemd Service

[Unit]
Description=Steward API
After=network.target postgresql.service

[Service]
Type=simple
User=steward
WorkingDirectory=/opt/steward
ExecStart=/usr/local/bin/bun run packages/api/src/index.ts
Restart=always
RestartSec=5
Environment=NODE_ENV=production
EnvironmentFile=/opt/steward/.env

[Install]
WantedBy=multi-user.target

Reverse Proxy (Nginx)

server {
    listen 443 ssl http2;
    server_name api.myapp.com;

    ssl_certificate /etc/letsencrypt/live/api.myapp.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.myapp.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:3200;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Production Checklist

  • Set a unique STEWARD_MASTER_PASSWORD (32+ chars, random)
  • Set a separate STEWARD_JWT_SECRET
  • Use TLS for all connections (API + database)
  • Restrict database access to the Steward server only
  • Set STEWARD_PLATFORM_KEY for admin operations
  • Configure PASSKEY_RP_ID and PASSKEY_ORIGIN to match your domain
  • Set up email provider (Resend) for magic link auth
  • Run as non-root user
  • Enable firewall (only expose ports 443 and SSH)
  • Back up the database regularly