Self-hosting
6. Populate `.env.production`
The complete env-var reference for a self-hosted Sinatra instance.
Sinatra cascades env files at the repo root via dotenv-flow (later overrides earlier):
.env— committed cross-env defaults..env.local— gitignored cross-env local overrides..env.production— committed prod defaults (you create this)..env.production.local— gitignored prod-only secrets.
For a real deploy, secrets (*_SECRET, *_API_KEY, *_PRIVATE_KEY, SINATRA_KMS_KEY) belong in your secret manager (GCP Secret Manager, AWS Secrets Manager, Doppler, etc.) and are injected at deploy time — not committed. The pattern below shows the env-var shape; the storage mechanism is up to you.
Full template
# ────────────────────────────────────────────────────────────────────────
# Database & Temporal
# ────────────────────────────────────────────────────────────────────────
DATABASE_URL=postgresql://sinatra:CHANGE_ME@db.example.com:5432/sinatra
TEMPORAL_ADDRESS=temporal.example.com:7233
TEMPORAL_NAMESPACE=default
# TEMPORAL_API_KEY=... # only when using Temporal Cloud
# ────────────────────────────────────────────────────────────────────────
# API server
# ────────────────────────────────────────────────────────────────────────
HOST=0.0.0.0
PORT=8080
APP_BASE_URL=https://sinatra.example.com # your public URL (step 8)
LINEAR_REDIRECT_URI=https://sinatra.example.com/install/linear/callback
# ────────────────────────────────────────────────────────────────────────
# KMS — for envelope-encrypting tenant credentials
# ────────────────────────────────────────────────────────────────────────
# Production: GCP KMS resource name
SINATRA_KMS_KEY=projects/<proj>/locations/<loc>/keyRings/<ring>/cryptoKeys/<key>
# OR (single-tenant evaluation only) a 32-byte base64 dev key:
# SINATRA_LOCAL_DEV_KMS_KEY=<base64 32 bytes>
INSTALL_STATE_SECRET=<random hex> # signs OAuth state tokens
# ────────────────────────────────────────────────────────────────────────
# Linear App (from step 3)
# ────────────────────────────────────────────────────────────────────────
LINEAR_CLIENT_ID=lin_app_xxxxxxxxxxxx
LINEAR_CLIENT_SECRET=lin_app_secret_xxxxxxxxxxxx
LINEAR_WEBHOOK_SECRET=lin_wh_xxxxxxxxxxxx
# ────────────────────────────────────────────────────────────────────────
# GitHub App (from step 2)
# ────────────────────────────────────────────────────────────────────────
GITHUB_APP_ID=123456
GITHUB_APP_SLUG=sinatra-acme
GITHUB_APP_WEBHOOK_SECRET=<hex>
GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
MIIE...
-----END RSA PRIVATE KEY-----"
# ────────────────────────────────────────────────────────────────────────
# Sandbox (from step 4)
# ────────────────────────────────────────────────────────────────────────
SANDBOX_PROVIDER=daytona # or 'docker'
DAYTONA_API_KEY=daytona_xxxxxxxxxxxxxxxxxxxx # required if provider=daytonaGenerating the random secrets
# INSTALL_STATE_SECRET (hex)
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
# SINATRA_LOCAL_DEV_KMS_KEY (base64, evaluation only)
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"Verifying
After populating the env file, the API and worker should start without 4xx-ing on startup. The first request to /install/linear will validate Linear credentials; the first webhook delivery will validate GitHub.