Steward
All docs

Docs Your data

Run your own studio

Your studio runs in your own cloud account, with your own keys. Steward the company never gets a copy. One deployment is one studio.

You need three things: the Steward repository, a place to run one small server with a disk, and a web address for it.

What every host needs

The health check is GET /health. The container listens on PORT (hosts set it for you).

Railway

  1. Make a new project from the repository (or from the Steward template, once it's published).
  2. In the service's Settings, set Config as code to deploy/railway.json. It builds the Dockerfile, checks /health and keeps one replica.
  3. Add a volume to the service, mounted at /data.
  4. Variables: PUBLIC_URL (your Railway domain or your own) and SETUP_TOKEN.
  5. Deploy. Open PUBLIC_URL/setup.

Template variables, if you publish this as a Railway template: PUBLIC_URL (required, default https://${{RAILWAY_PUBLIC_DOMAIN}}), SETUP_TOKEN (required, generated with ${{secret(32)}}), and a volume at /data.

Fly.io

fly.toml is in the repository.

fly launch --copy-config --no-deploy # choose your own app name fly volumes create steward_data --size 1 fly secrets set PUBLIC_URL=https://<app>.fly.dev SETUP_TOKEN=<a long random string> fly deploy

Open https://<app>.fly.dev/setup. The config keeps one machine running all the time, since the schedule runs every minute.

Any server with Docker

docker-compose.yml is in the repository.

cp deploy/env.example .env # fill in PUBLIC_URL and SETUP_TOKEN docker compose up -d --build docker compose logs steward

Put HTTPS in front of port 3800 with Caddy, nginx or a tunnel. With Caddy, two lines are enough:

studio.example.com { reverse_proxy localhost:3800 }

The first run

Open PUBLIC_URL/setup. Enter the setup token, the studio's name, your handle, your name and a passcode. You're signed in as the first owner, and setup closes for good. Invite everyone else from Settings, then People.

The token works once. If the server made it, it's in the log, and each restart before setup makes a new one.

SEED_PEOPLE still works instead of /setup: SEED_PEOPLE=ana:Ana,ben:Ben creates those people on the first boot and prints an invite link for each.

Where each optional key goes

All of these go in the host's variables (Railway Variables, fly secrets set, or .env). None is needed to start. deploy/env.example lists them with notes.

WhatVariablesWithout them
@steward on an API keySTEWARD_BRAIN=api and ANTHROPIC_API_KEY or another provider's key (models)@steward thinks through an owner's connector
Nightly backups to your bucketBACKUP_S3_BUCKET, BACKUP_S3_ACCESS_KEY_ID, BACKUP_S3_SECRET_ACCESS_KEY, plus BACKUP_S3_ENDPOINT for R2, B2 or MinIO; optional BACKUP_S3_REGION, BACKUP_S3_PREFIX (steward/), BACKUP_RETENTION_DAYS (30), BACKUP_HOUR_UTC (10)Off. You can still download everything from Settings
Update noticesUPDATE_FEED_URL, a public JSON file like { "latest": "0.7.0", "url": "https://...", "notes": "..." }Off
Plan and billing reportsBILLING_URL, BILLING_CUSTOMER_ID, BILLING_KEY, from your Steward accountOff, and nothing is sent even with them until an owner opts in
Web pushVAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY, VAPID_SUBJECTOff
iPhone pushAPNS_KEY_ID, APNS_TEAM_ID, APNS_KEYOff
GitHub, Railway, email, Bluesky, StripeSee Integrations and trustEach is off
Time zone for the scheduleSTUDIO_TZ (default America/Los_Angeles)Pacific time
Behind a proxy (rate limits by visitor address)TRUST_PROXY=1 (detected on Railway and Fly)The socket's address is used, and forwarded headers are ignored
The key that seals two-step secretsSTEWARD_SECRET_KEYA key file the hub makes once, DATA_DIR/secret.key
Checks that may reach your own networkALLOW_PRIVATE_FETCH=1Uptime, health and site checks reach only the public internet
More push servicesPUSH_EXTRA_HOSTS (hosts, comma-separated)Only the browsers' own push services
Passkeys that survive a move to a subdomainPASSKEY_RP_ID, a parent domain of PUBLIC_URL's host (like example.com for app.example.com)Passkeys belong to PUBLIC_URL's host, and a new address needs new passkeys
A public site on another domain (how stewardstudio.io runs)SITE_HOSTS (hosts that get the landing page and public docs, comma-separated; default stewardstudio.io,www.stewardstudio.io) and APP_URL (where those hosts send app pages)Your hub's own address serves the app only

Back up and move

Updates

Settings, then Version shows the version you run. With UPDATE_FEED_URL set, it also says when a newer release is out. The server only reads that file; it sends nothing. To update, redeploy from the newer code. The database upgrades itself on start.