Skip to main content

12. Operations

12.1 Commands​

bo-app start # HTTP server
bo-app start --worker # queue workers only (ingest, workflows)
bo-app start --server-and-worker # both, for small deployments
bo-app scheduler # runs the cron jobs in config/{env}.yaml
bo-app db migrate # apply migrations
bo-app task config_validate # load, validate and compile every definition, then exit
bo-app task schema_export [dir:schemas]
bo-app task retention_purge # drop parsed text older than policies.retention.parsed_markdown_days
bo-app task approval_expiry # mark pending approvals past expires_at as expired
bo-app task seed_demo [tenant:demo] [expiration:86400]

In development, prefix these with cargo run -p bo-app --. config_validate boots exactly as the server does, so CI uses it to prove every definition loads.

The scheduler runs retention_purge daily at 03:00 and approval_expiry every 15 minutes. Schedules have six fields, starting with seconds; bo-app scheduler --list shows what will run.

12.2 Production​

config/production.yaml reads everything from the environment. deploy/.env.example lists the variables:

VariableNotes
DATABASE_URLPostgreSQL with pgvector and pgcrypto
JWT_SECRETbase64 (openssl rand -base64 32); Loco decodes it
VLLM_CHAT_URL, VLLM_EMBED_URL, VLLM_API_KEYOpenAI-compatible model servers
DOCLING_URL, DOCLING_API_KEYdocling-serve
OTLP_ENDPOINTOpenTelemetry Collector (telemetry is on in production)
PUBLIC_URL, WEB_ORIGIN, DOCUMENTS_DIRserver URL, the web app's origin (CORS), the upload directory
ACCOUNTING_URL, ACCOUNTING_HMAC_SECRETfor a webhook accounting connector

deploy/docker-compose.yml describes the full stack: PostgreSQL, vLLM (chat and embeddings), docling-serve, the OpenTelemetry Collector, Jaeger, a migrate job, then server, worker, scheduler and web. config/ is mounted read-only, so changing a workflow means editing the file and restarting, never rebuilding. Optional profiles add Valkey and SeaweedFS for observability tools. These images have not been built and run end to end yet; treat the compose file as a starting point.

Production runs auto_migrate: false; run bo-app db migrate as a deploy step.

12.3 The web app in production​

cd web
bun install --frozen-lockfile --omit optional # leaves out sharp (LGPL), which nothing uses
bun run build
BO_AGENT_URL=http://server:8080 bun run start

deploy/Dockerfile.web does the same on Bun. It runs bun run license:check, which fails the build if any production dependency's license is outside license-policy.json.