qaitai docs

Environment variables

Every setting in .env.example, generated from it.

This page is generated from .env.example. Docker Compose users set these in docker/.env (created by sh docker/init-env.sh); Helm users put them in the Secret passed as existingSecret. Example values for secrets are not shown.

Core

VariableExampleWhat it does
DATABASE_URL(secret)The app connects as qaitai_app (no superuser/BYPASSRLS, DML only) so row-level security applies (packages/db/drizzle/0002_rls.sql, docker/sql/app-role.sql). Migrations use the owner role.
DATABASE_MIGRATION_URL(secret)
BETTER_AUTH_SECRETopenssl rand -base64 32
BETTER_AUTH_URLhttp://localhost:3000
PUBLIC_APP_URLhttps://app.tryqaitai.comcloud; self-hosters set their own
QAITAI_MASTER_KEY32 random bytes, base64. Wraps the data keys for BYOK keys and test secrets. Back it up; losing it loses those secrets. openssl rand -base64 32
QAITAI_DEFAULT_PLANself_hostedPlan for new workspaces: self_hosted (no limits, default) | free | pro | team | enterprise. Cloud sets free.
QAITAI_ADMIN_EMAILSInstance admins (verified emails, comma-separated). Empty = the first user who signed up.
QAITAI_ALLOW_SIGNUPfalseSelf-hosted sign-up is invite-only (first account + pending invitations). Set true to allow anyone who can reach this instance to create an account.
QAITAI_INTERNAL_SECRETShared by web + platform runners: they call POST /api/internal/run-completed with it. openssl rand -hex 32 (>= 32 chars)
QAITAI_EDITIONselfhostcloud = qaitai Cloud (tryqaitai.com): domain verification is always enforced and self-hosted runs need BYOK. selfhost (default) = your own instance.
QAITAI_REQUIRE_DOMAIN_VERIFICATIONtrueCloud-runtime runs may only target verified domains. Self-hosted instances may set false (ignored when QAITAI_EDITION=cloud). Default: true.
QAITAI_RUNNER_JOB_SECRETOptional: HMAC key for per-job runner tokens (qjt_). Defaults to a key derived from BETTER_AUTH_SECRET.
QAITAI_RATE_LIMIT_STOREmemoryRate-limit counters (runner API, /api/v1, media): memory (default, per web process) or postgres (shared across web replicas; set this when running more than one web instance, e.g. qaitai Cloud or Helm replicas > 1).

GitHub App (sign-in + repo access + PR checks)

VariableExampleWhat it does
GITHUB_CLIENT_IDEasiest: create it in-app (Settings → GitHub, /api/setup/github/manifest) or with scripts/create-github-app.mjs. Anything set here overrides the in-app config.
GITHUB_CLIENT_SECRET
GITHUB_APP_ID
GITHUB_APP_SLUG
GITHUB_APP_PRIVATE_KEYPEM, single line with \n escapes
GITHUB_WEBHOOK_SECRET
QAITAI_PREVIEW_HOST_SUFFIXESvercel.app,netlify.app,pages.dev,onrender.com,fly.dev,up.railway.appDeployments on these hosts from a connected repo count as verified preview domains.

Other sign-in

VariableExampleWhat it does
GOOGLE_CLIENT_ID
GOOGLE_CLIENT_SECRET
QAITAI_PASSWORD_BREACH_CHECKEmail + password accounts must verify their email before signing in when a mail transport is set up (always on qaitai Cloud). Reject passwords found in Have I Been Pwned (only a 5-char SHA-1 prefix is sent). Default: true when QAITAI_EDITION=cloud, false self-hosted.
VariableExampleWhat it does
EMAIL_PROVIDEREMAIL_PROVIDER: resend (qaitai Cloud) | smtp (self-hosted) | log (dev only: prints emails, refuses in production). Default: smtp when SMTP_URL/SMTP_HOST is set, else resend when RESEND_API_KEY is set, else log.
EMAIL_FROMdefault: qaitai <no-reply@BETTER_AUTH_URL host>
RESEND_API_KEY
SMTP_URLDev: pnpm infra:up runs Mailpit, SMTP on localhost:1026, inbox at http://localhost:8025 smtp://localhost:1026 for Mailpit; or the parts below
SMTP_HOST
SMTP_PORT587
SMTP_USER
SMTP_PASS
SMTP_SECUREtrue = implicit TLS (default when SMTP_PORT=465)
QAITAI_DISABLE_BOOTSTRAPfalseSelf-hosted, zero users: the web container prints a one-time admin sign-in link at start. true = don't.

Jobs (Hatchet)

VariableExampleWhat it does
HATCHET_CLIENT_TOKEN
HATCHET_CLIENT_TLS_STRATEGYnone

Runner

VariableExampleWhat it does
QAITAI_RUNNER_MODEplatformplatform = our own workers: Hatchet + DATABASE_URL + QAITAI_MASTER_KEY (everything above). remote = customer runners: only QAITAI_URL + QAITAI_TOKEN (qrt_... from the Workers page); no DB, no master key, no Hatchet token. Default: remote when QAITAI_TOKEN is set, else platform.
QAITAI_URLRemote mode only: the control plane's URL and this runner's token.
QAITAI_TOKEN
QAITAI_RUNTIMEcloudplatform mode: cloud | self_hosted (self_hosted also takes self-hosted runs)
QAITAI_SLOTS4
AI_GATEWAY_API_KEYManaged model keys (BYOK keys are stored per workspace instead). Set either; AI Gateway wins when both are set (provider fallbacks + spend observability), otherwise runs call Anthropic directly.
ANTHROPIC_API_KEY
QAITAI_WEB_URLhttp://localhost:3000Where the runner reaches the web app for run-completion callbacks (internal URL is fine).
QAITAI_PLATFORM_JOBStruefalse on customer-hosted runners: they execute runs only, not platform cron jobs (domain re-verification).
STEEL_BASE_URLhttp://localhost:3100Steel: set STEEL_BASE_URL for the local open-source instance (no key), or STEEL_API_KEY for Steel Cloud. Leave both empty to use local Chromium (npx playwright install chromium).
STEEL_API_KEY
STEEL_PUBLIC_URLBrowser-reachable base URL for the Steel session viewer on the Live run page, when it differs from STEEL_BASE_URL (how the runner reaches Steel), e.g. https://steel.example.com. Set on the runner.

Cost model (COGS in usage_events.cost_micros; docs/pricing-and-costs.md#cost-model)

VariableExampleWhat it does
STEEL_COST_PER_HOUR0.08Steel Cloud price per browser-hour on OUR STEEL_API_KEY. Zero cost for BYO browser keys, open-source Steel (STEEL_BASE_URL without our key), local Chromium and customer (remote) runners.
QAITAI_INFRA_COST_PER_MIN0.001Compute + storage + DB overhead per agent-minute, platform-mode runners only (0 on customer runners).

Notifications & issue trackers (docs/self-hosting.md#notifications-and-integrations)

VariableExampleWhat it does
SLACK_CLIENT_IDSlack app (OAuth "Add to Slack", threads, Rerun / Assign buttons). Without these, admins can still paste a Slack incoming-webhook URL (one message per run). Redirect URL: <app>/api/integrations/slack/callback; interactivity request URL: <app>/api/integrations/slack/interactions.
SLACK_CLIENT_SECRET
SLACK_SIGNING_SECRETverifies interactivity requests (Rerun / Assign)
LINEAR_CLIENT_IDLinear OAuth app (redirect URL: <app>/api/integrations/linear/callback). Without these, admins paste a Linear personal API key instead.
LINEAR_CLIENT_SECRET

Billing (qaitai Cloud only; docs/pricing-and-costs.md#billing-pipeline)

VariableExampleWhat it does
STRIPE_SECRET_KEYBilling runs only when QAITAI_EDITION=cloud AND STRIPE_SECRET_KEY is set. Self-hosted instances leave all of these empty: no Stripe calls, no plan limits, /api/stripe/webhook returns 404. Prices and meters are created by pnpm --filter @qaitai/billing stripe:setup and found by lookup key (<STRIPE_LOOKUP_KEY_PREFIX><plan><item>), so no price or meter ids go here. sk_test_... in dev/staging; restricted keys work (customers, checkout, portal, subscriptions, prices, meter events, invoices)
STRIPE_WEBHOOK_SECRETwhsec_... of the endpoint https://<app>/api/stripe/webhook (or stripe listen)
STRIPE_LOOKUP_KEY_PREFIXdefault qaitai
STRIPE_AUTOMATIC_TAXtrue = Stripe Tax on Checkout (configure tax settings in Stripe first)
STRIPE_API_BASEdev/tests only: http://localhost:12111 for stripe-mock

Public REST API /api/v1 (docs: API reference)

VariableExampleWhat it does
QAITAI_API_RATE_LIMITRequests per minute per API key; empty = plan default (Free 60, Pro 300, Team 600, Enterprise/self-hosted 1200). Counted per web process: behind N replicas a key gets up to N x this.
QAITAI_API_IP_RATE_LIMIT600Requests per minute per client IP before the key is checked (brute-force guard).

Storage

VariableExampleWhat it does
S3_ENDPOINThttp://localhost:9000
S3_BUCKETqaitai
S3_ACCESS_KEY_ID(secret)
S3_SECRET_ACCESS_KEY(secret)
QAITAI_RECORDINGfullReplays (docs/self-hosting.md#recordings-replays): full | screenshots | off; retention unset = forever.
QAITAI_RECORDING_RETENTION_DAYS

Enterprise (ee/, commercial license; everything else works without these)

VariableExampleWhat it does
QAITAI_LICENSE_KEYSigned license key for self-hosted Enterprise features (SSO, SCIM, custom roles, audit export). Verified offline; see docs/self-hosting.md#enterprise.
QAITAI_SSO_TRUSTED_ORIGINSOrigins of identity providers on a private network that SSO may reach (comma-separated).
QAITAI_AUDIT_RETENTION_DAYSRunner: days to keep audit log entries for self-hosted workspaces (unset = forever).

Telemetry (docs/self-hosting.md#telemetry)

VariableExampleWhat it does
QAITAI_TELEMETRYEach integration starts only when its own endpoint/key is set. QAITAI_TELEMETRY=off disables ALL of them (OTLP, Sentry, Langfuse, PostHog) whatever else is set; =on also allows PostHog on self-hosted.
OTEL_EXPORTER_OTLP_ENDPOINTOpenTelemetry traces (web request → createRun → Hatchet → runner → agent steps → model → browser). e.g. http://localhost:4318 (Jaeger: docker compose -f docker/compose.dev.yml --profile observability up -d jaeger)
OTEL_EXPORTER_OTLP_TRACES_ENDPOINTfull traces URL, overrides the one above
OTEL_EXPORTER_OTLP_HEADERSk=v,k2=v2 (e.g. an API key header); secret
OTEL_SERVICE_NAMEdefault qaitai-web / qaitai-runner
QAITAI_TRACE_SAMPLE_RATE0..1 of new traces (default 1)
SENTRY_DSNSentry errors (web server, browser, runner); events are scrubbed of secrets, bodies, cookies, auth headers.
SENTRY_ENVIRONMENT
SENTRY_RELEASE
LANGFUSE_PUBLIC_KEYLangfuse LLM traces (model calls, tokens, cost; redacted prompts for managed runs and opted-in BYOK orgs).
LANGFUSE_SECRET_KEY
LANGFUSE_BASE_URLdefault https://cloud.langfuse.com (LANGFUSE_HOST also accepted)
LANGFUSE_SAMPLE_RATE0..1 of run traces (default 1)
POSTHOG_KEYPostHog product analytics (qaitai Cloud; self-hosted only with QAITAI_TELEMETRY=on). No session recording.
POSTHOG_HOSTdefault https://us.i.posthog.com

On this page