Installation
Set up Lumio using Docker Compose.
Hosted Environments
Lumio runs three environments per app. Production lives on lumio.vision; non-production on zaflun.dev. Use these strings when configuring OAuth callbacks, CORS, env-var defaults and CI deploy targets.
| App | Production | Production Preview | Staging |
|---|---|---|---|
| Webapp | lumio.vision | lumio.web.prod.zaflun.dev | lumio.web.staging.zaflun.dev |
| ID App | id.lumio.vision | lumio.id.prod.zaflun.dev | lumio.id.staging.zaflun.dev |
| Admin App | admin.lumio.vision | lumio.admin.prod.zaflun.dev | lumio.admin.staging.zaflun.dev |
| API | api.lumio.vision | lumio.api.prod.zaflun.dev | lumio.api.staging.zaflun.dev |
| Docs | docs.lumio.vision | lumio.docs.prod.zaflun.dev | lumio.docs.staging.zaflun.dev |
| Overlay external URL | overlay.lumio.vision | (single per-deployment external host; configurable via NEXT_PUBLIC_OVERLAY_URL on the webapp) | (same) |
Branch mapping: next → staging · main → production preview · tag 20* → production.
The rest of this guide covers a local development setup using Docker Compose.
Prerequisites
- Docker and Docker Compose
- A PostgreSQL 18 instance (or use the provided compose file)
- Redis 8
Docker Setup
# Start the dev stack (PostgreSQL, TimescaleDB, Redis)
docker compose -f dev-stack/db.docker-compose.yml up -d
# Start the web app
docker compose -f dev-stack/web.docker-compose.yml up -d
Environment Configuration
Copy .env.example to .env and configure:
cp .env.example .env
Key variables:
| Variable | Description |
|---|---|
DATABASE_URL | PostgreSQL connection string |
REDIS_URL | Redis connection string |
AUTH_SECRET | NextAuth session secret (ID app) |
AUTH_URL | ID app base URL |
NEXT_PUBLIC_WEB_URL
Absolute URL of the apps/web app. Used by apps/id and apps/admin to build absolute links back to the canonical /legal/* pages in the cookie-consent banner footer.
- Default in code:
https://lumio.vision(production) - Staging:
https://lumio.web.staging.zaflun.dev(set via Cloudflare Pages env settings) - Production-preview:
https://lumio.web.prod.zaflun.dev - Local development: put
NEXT_PUBLIC_WEB_URL=http://localhost:4000inapps/id/.env.localandapps/admin/.env.localso banner links resolve to your local web app instead of production.
apps/web itself does NOT need this variable — it uses relative /legal/* paths because it hosts the pages.
Environment Variable Naming
Every TOML key in apps/api/config/*.toml is overridable via an environment variable using the format LUMIO__<SECTION>__<KEY> — double underscore between the prefix and the first segment, and double underscore between every nested segment. Examples:
| TOML path | ENV var |
|---|---|
database.url | LUMIO__DATABASE__URL |
auth.token_encryption_key | LUMIO__AUTH__TOKEN_ENCRYPTION_KEY |
webhooks.youtube_secret | LUMIO__WEBHOOKS__YOUTUBE_SECRET |
youtube.innertube_observer.api_key_fallback | LUMIO__YOUTUBE__INNERTUBE_OBSERVER__API_KEY_FALLBACK |
The override priority is default.toml < {env}.toml < local.toml < ENV vars, so an env-var always wins.
YouTube Chat Transport
YouTube chat uses InnerTube as the primary transport (zero quota cost). gRPC and REST fallbacks are available but disabled by default:
| ENV var | Purpose | Default |
|---|---|---|
LUMIO__YOUTUBE__GRPC_FALLBACK_ENABLED | Enable gRPC streamList fallback when InnerTube fails | false |
LUMIO__YOUTUBE__REST_FALLBACK_ENABLED | Enable REST polling fallback when InnerTube and gRPC both fail | false |
YouTube InnerTube Settings
The InnerTube chat poller ships with sensible defaults — no configuration is required. For tuning or local debugging, the following ENV overrides map onto the [youtube.innertube_observer] block in apps/api/config/default.toml:
| ENV var | Purpose | Default |
|---|---|---|
LUMIO__YOUTUBE__INNERTUBE_OBSERVER__API_KEY_FALLBACK | Cold-boot fallback for the InnerTube API key (auto-rotated at runtime via watch-page scrape) | well-known WEB-client key |
LUMIO__YOUTUBE__INNERTUBE_OBSERVER__POLL_INTERVAL_FIRST_5MIN | Polling cadence (s) during the first 5 min of a stream | 60 |
LUMIO__YOUTUBE__INNERTUBE_OBSERVER__POLL_INTERVAL_5_TO_30MIN | Polling cadence (s) between 5 and 30 min | 120 |
LUMIO__YOUTUBE__INNERTUBE_OBSERVER__POLL_INTERVAL_AFTER_30MIN | Polling cadence (s) after 30 min | 300 |
LUMIO__YOUTUBE__INNERTUBE_OBSERVER__ON_DEMAND_MIN_INTERVAL | Min interval (s) for on-demand triggers per account | 60 |
LUMIO__YOUTUBE__INNERTUBE_OBSERVER__PARSE_ERROR_THRESHOLD | Watchdog trip threshold (0.0–1.0) over a 1 h window | 0.25 |
LUMIO__YOUTUBE__INNERTUBE_OBSERVER__CACHE_TTL_SECONDS | Member + tier-badge entry TTL in Redis | 1209600 (14 d) |
api_key_fallback is wrapped in a SensitiveString and never appears in logs or tracing output.
Frontend Environment & Logging
apps/web, apps/admin, and apps/id share two env-vars that drive the logger and the Sentry integration. Set both the server-side and the NEXT_PUBLIC_* mirror so the browser bundle picks them up too.
| Variable | Allowed values | Default | Purpose |
|---|---|---|---|
LUMIO_ENV / NEXT_PUBLIC_LUMIO_ENV | development | staging | production | development | Logger threshold default + Sentry environment tag |
LUMIO_LOG_LEVEL / NEXT_PUBLIC_LUMIO_LOG_LEVEL | debug | info | warn | error | per env (debug/info/warn) | Override the auto-threshold |
Sentry env-vars per app — leave blank locally to disable, fill them in CI / Cloudflare Pages env for staging + production:
| Variable | Where | Purpose |
|---|---|---|
NEXT_PUBLIC_SENTRY_DSN | Browser + server | Public DSN |
SENTRY_DSN | Server only | Optional override; falls back to NEXT_PUBLIC_SENTRY_DSN |
SENTRY_ORG | Build (CI) | Sentry org slug for source-map upload |
SENTRY_PROJECT | Build (CI) | Sentry project slug |
SENTRY_AUTH_TOKEN | Build (CI) | Auth token for source-map upload |
Each app also has a proxy.ts (Next.js 16's renamed middleware.ts) that prints one structured line per HTTP request to the SSR terminal — including the real client IP (x-real-ip / x-forwarded-for / cf-connecting-ip) with IPv4/IPv6 family classification. See Logging → Per-request access logging for details.
Verify Installation
Visit http://localhost:4000 to access the Lumio dashboard.