Skip to main content

Server configuration

This page is the complete reference for configuring bowrain-server and bowrain-worker. For the overall service topology (PostgreSQL + job queue + worker + blob storage) and a production walkthrough, see Self-Hosting.

Precedence

The server reads command-line flags first, then applies environment-variable overrides on top — so an environment variable wins over the same value passed as a flag. All knobs have an environment variable; only a subset are also exposed as flags. The worker is configured only through environment variables.

Storage

Bowrain Server requires PostgreSQL. There is no SQLite or file backend. The connection string must use the postgres:// or postgresql:// scheme — the server refuses to start otherwise. The schema is created automatically on first start, and migrations run on startup. The brand knowledge graph runs on the same stock PostgreSQL (no extension required) — see AD-006.

BOWRAIN_DATABASE_URL=postgres://bowrain:password@localhost/bowrain

The same BOWRAIN_DATABASE_URL must be given to the worker.

Server environment variables

All Bowrain variables use the BOWRAIN_ prefix; a few external integrations (Azure, Stripe, PostHog) use their vendor's conventional names.

Core

VariableDefaultDescription
BOWRAIN_DATABASE_URL(empty)PostgreSQL connection string (postgres://…) — required
BOWRAIN_DATABASE_AUTH(empty)azure to use Entra ID managed-identity tokens; otherwise password auth from the URL
BOWRAIN_PORT8080HTTP port to listen on (gRPC is multiplexed onto the same port)
BOWRAIN_HOST0.0.0.0Address to bind to
BOWRAIN_DATA_DIR(empty)Directory for temporary files during processing
BOWRAIN_QUEUE_BACKEND(empty)sqs selects the SQS job-queue backend; unset uses an in-process queue (single-instance development only)
SQS_ENDPOINT(empty)SQS endpoint override for SQS-compatible emulators (ElasticMQ, LocalStack); empty on AWS
BOWRAIN_SQS_QUEUE_PREFIX(empty)Optional name prefix applied to every job queue
BOWRAIN_EVENT_BACKEND(empty)redis runs the event bus on Redis Streams (requires BOWRAIN_REDIS_URL); unset uses the in-memory bus
BOWRAIN_REDIS_URL(empty)Redis URL for caching, session state, and the Redis Streams event bus
BOWRAIN_REDIS_PASSWORD(empty)Redis password (overrides any password in BOWRAIN_REDIS_URL)
BOWRAIN_MAX_PUSH_BYTES256MBMax total upload size per push
BOWRAIN_WEB_UI_DIR(empty)Path to built web UI static files (dev only; production serves the UI from a separate container)
BOWRAIN_PULSE_ENABLEDfalseMounts the public Pulse activity dashboard (/api/v1/pulse routes + the pulse subdomain SPA). Unmounted by default
BOWRAIN_LOG_FORMAT(empty)text or json
BOWRAIN_LOG_LEVEL(empty)debug, info, warn, error

Authentication

VariableDefaultDescription
BOWRAIN_JWT_SECRET(empty)JWT signing secret. When set, auth, OIDC login, and workspace management are enabled
BOWRAIN_OIDC_ISSUER_URL(empty)OIDC issuer URL (internal, used for token validation)
BOWRAIN_OIDC_PUBLIC_URL(empty)Browser-facing URL of the identity provider, when it differs from the issuer URL (for redirects)
BOWRAIN_OIDC_CLIENT_ID(empty)OIDC OAuth client ID
BOWRAIN_OIDC_CLIENT_SECRET(empty)OIDC OAuth client secret

The Keycloak admin API (used to write back email changes initiated from the UI) and the admin control plane are optional:

VariableDescription
BOWRAIN_KEYCLOAK_ADMIN_URLIn-cluster Keycloak admin URL (enables Bowrain-managed email change)
BOWRAIN_KEYCLOAK_REALMRealm name (default bowrain)
BOWRAIN_KEYCLOAK_ADMIN_CLIENT_IDService-account client with realm-management:manage-users
BOWRAIN_KEYCLOAK_ADMIN_CLIENT_SECRETService-account client secret
BOWRAIN_ADMIN_OIDC_ISSUER_URLIssuer URL for the /api/admin/* control plane
BOWRAIN_ADMIN_OIDC_CLIENT_IDAdmin control-plane client ID
BOWRAIN_ADMIN_OIDC_CLIENT_SECRETAdmin control-plane client secret
OIDC public URL

When your OIDC provider has a different internal hostname than the browser-facing URL (common in Docker), set BOWRAIN_OIDC_ISSUER_URL to the internal URL (e.g. http://keycloak:8080/realms/bowrain) and BOWRAIN_OIDC_PUBLIC_URL to the browser-facing URL (e.g. http://localhost:8180/realms/bowrain). Leave it unset when the two are the same.

Earlier versions of this page said the variable defaults to BOWRAIN_OIDC_ISSUER_URL. It never did, and it does not now — leaving it unset means "the browser reaches the provider at the issuer URL", which is what each redirect already assumes. It names the identity provider's address, not this application's: for the app's own origin, see BOWRAIN_APP_PUBLIC_URL below.

Origins and cookies

These decide which other origins may talk to the API, and whether session cookies are marked Secure.

VariableDefaultDescription
BOWRAIN_APP_PUBLIC_URL(empty)This application's browser-facing origin (e.g. https://app.bowrain.cloud). Used to build the CORS and WebSocket origin allowlists
BOWRAIN_PUBLIC_SITE_URL(empty)Marketing landing origin, allowed one credentialed cross-origin read (GET /api/v1/auth/whoami) so the landing can render a signed-in link
BOWRAIN_FORCE_SECURE_COOKIEStrue, or false in developmentMarks session cookies Secure regardless of the request scheme
BOWRAIN_ALLOW_INSECURE_DEVfalseMarks the process a development instance. Also relaxes startup configuration checks and enables direct device approval
Development mode is opted into, never inferred

A server is production unless BOWRAIN_ALLOW_INSECURE_DEV (or --allow-insecure-dev) says otherwise. In production, only BOWRAIN_APP_PUBLIC_URL and BOWRAIN_PUBLIC_SITE_URL may make credentialed cross-origin requests, and WebSockets are accepted only from the app's own origin. In development, any localhost origin is accepted as well.

Set BOWRAIN_APP_PUBLIC_URL on any deployment where a browser reaches the API from a different origin than the one it was served from. If it is unset, no cross-origin caller is allowed — which is the safe answer, not a broken one: same-origin requests never consult CORS.

Do not use BOWRAIN_ALLOW_INSECURE_DEV on anything reachable from the internet.

Secure cookies behind a proxy

Bowrain infers the request scheme from X-Forwarded-Proto, so behind a TLS-terminating proxy the Secure flag would otherwise depend on that header arriving. It is forced on by default outside development for exactly that reason. Development leaves it off, because browsers discard Secure cookies sent over plain HTTP.

Blob storage

Bowrain stores in-flight sync push payloads in a blob store, shared with the worker. The backend defaults to local.

VariableDefaultDescription
BLOB_STORAGE_BACKENDlocallocal or azure
BLOB_STORAGE_LOCAL_DIR$BOWRAIN_DATA_DIR/blobs (or a temp dir)Local blob storage directory (server)
AZURE_STORAGE_ACCOUNT_URL(empty)Azure Blob Storage account URL
AZURE_STORAGE_CONTAINERbowrain-assetsAzure Blob Storage container
AZURE_STORAGE_CONNECTION_STRING(empty)Azure connection string (dev / Azurite)

Email

Set BOWRAIN_SMTP_HOST + BOWRAIN_SMTP_FROM for an unauthenticated relay (local dev / Mailpit), add username/password for authenticated SMTP, or set BOWRAIN_RESEND_API_KEY to send via Resend instead.

VariableDescription
BOWRAIN_SMTP_HOSTSMTP server in host:port format (empty = email disabled)
BOWRAIN_SMTP_FROMSender email address
BOWRAIN_SMTP_USERNAMESMTP auth username (empty = no auth)
BOWRAIN_SMTP_PASSWORDSMTP auth password
BOWRAIN_SMTP_USE_TLStrue/1 for implicit TLS (SMTPS); otherwise STARTTLS
BOWRAIN_RESEND_API_KEYResend API key (used instead of SMTP when set; reuses BOWRAIN_SMTP_FROM)

Agent (@bravo)

The in-product agent runs in containers. When BOWRAIN_AGENT_RUNTIME is unset it falls back to local mock responses.

VariableDescription
BOWRAIN_AGENT_RUNTIMEdocker or aca (Azure Container Apps)
BOWRAIN_AGENT_IMAGEAgent container image
BOWRAIN_AGENT_MAX_CONCURRENTMax concurrent agent containers per workspace
BOWRAIN_AGENT_DOCKER_HOST / BOWRAIN_AGENT_DOCKER_NETWORKDocker runtime settings
BOWRAIN_AGENT_ACA_SUBSCRIPTION / _RESOURCE_GROUP / _ENVIRONMENT_ID / _LOCATIONAzure Container Apps settings
BOWRAIN_AGENT_MODEL_PROVIDER / _MODEL_NAME / _MODEL_API_BASE / _MODEL_API_KEYAgent model configuration

Billing, analytics, audit

VariableDescription
STRIPE_SECRET_KEY / STRIPE_WEBHOOK_SECRETStripe API + webhook secrets
STRIPE_PRO_PRICE_ID / STRIPE_TEAM_PRICE_ID / STRIPE_CREDIT_PRICE_IDStripe price IDs
POSTHOG_API_KEY / POSTHOG_HOSTPostHog analytics
BOWRAIN_AUDIT_RETENTION_DAYSPrune audit-log rows older than N days (0 = keep forever)
BOWRAIN_AUDIT_SIEM_WEBHOOK_URLForward every audit event as NDJSON to an external SIEM

Rate limiting

Abuse-prone endpoints carry per-IP rate limits. Each knob is an integer; when unset, unparsable, or non-positive, the compiled default applies. The tightest caps are on the unauthenticated and email-sending routes. All limits are requests per minute except the claim-email cap, which is per hour.

VariableDefaultLimits
BOWRAIN_RL_ANON_PER_MIN10Anonymous project creation (unauthenticated)
BOWRAIN_RL_ANON_BURST5Burst for the anonymous limiter
BOWRAIN_RL_CLAIM_EMAIL_PER_HOUR5Claim emails per client IP (hourly)
BOWRAIN_RL_AUTH_PER_MIN30Pre-auth token endpoints
BOWRAIN_RL_AUTH_BURST15Burst for the auth limiter
BOWRAIN_RL_INVITE_PER_MIN20Invite routes (may send email)
BOWRAIN_RL_INVITE_BURST10Burst for the invite limiter
BOWRAIN_RL_AI_PER_MIN20AI-consuming routes (AI translate, brand check, brand scan, @bravo)
BOWRAIN_RL_AI_BURST10Burst for the AI limiter

Azure integration

VariableDescription
AZURE_CLIENT_IDManaged-identity client ID (used when BOWRAIN_DATABASE_AUTH=azure)

Worker environment variables

The worker (bowrain-worker) shares the database and job queue with the server and runs the auto-translate-on-push automation.

VariableDescription
BOWRAIN_DATABASE_URLSame PostgreSQL connection string as the server
BOWRAIN_QUEUE_BACKEND / SQS_ENDPOINTSame job-queue selection as the server (the two must agree on the broker)
BOWRAIN_EVENT_BACKEND / BOWRAIN_REDIS_URLSame event-bus selection as the server
LOCAL_BLOB_DIRSync push payload dir — must point at the same shared volume as the server's BLOB_STORAGE_LOCAL_DIR
BOWRAIN_PLATFORM_PROVIDERTranslation provider: gemini, openai, anthropic, ollama, or demo (offline)
BOWRAIN_PLATFORM_API_KEYProvider API key (or a provider-specific variable such as GEMINI_API_KEY)
BOWRAIN_PLATFORM_MODELDefault model for the provider
BOWRAIN_PLATFORM_BASE_URLProvider API base URL (e.g. self-hosted Ollama)
BOWRAIN_OPENAI_ENDPOINTAzure OpenAI endpoint (hosted-cloud path; uses managed identity)
Blob directory env var differs by service

The server reads BLOB_STORAGE_LOCAL_DIR; the worker reads LOCAL_BLOB_DIR. Point both at the same shared volume.

Command-line flags

These flags are accepted by bowrain-server. Each maps to the corresponding BOWRAIN_ environment variable, which takes precedence.

bowrain-server \
--port 8080 \
--host 0.0.0.0 \
--database-url postgres://bowrain:password@localhost/bowrain \
--data-dir /tmp/bowrain \
--jwt-secret your-secret \
--oidc-issuer-url https://keycloak.example.com/realms/bowrain \
--oidc-client-id bowrain \
--oidc-client-secret your-client-secret \
--web-ui-dir /path/to/web/dist
FlagDefaultDescription
--port8080HTTP port to listen on
--host0.0.0.0Address to bind to
--data-dir(empty)Directory for temporary files
--database-url(empty)PostgreSQL connection string (postgres://…)
--jwt-secret(empty)JWT signing secret
--oidc-issuer-url(empty)OIDC issuer URL
--oidc-client-id(empty)OIDC OAuth client ID
--oidc-client-secret(empty)OIDC OAuth client secret
--web-ui-dir(empty)Path to built web UI static files

Next steps