Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 16 additions & 10 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,31 @@ ENCRYPTION_KEY= # AES-256-GCM for OAuth token encryption (dashboard)
# ─── Database Passwords ──────────────────────────────────────────────────────
# Used by Docker Compose for the DB containers.
#
# !! The `yavio_dev` values below are LOCAL DEVELOPMENT PLACEHOLDERS and are
# !! published in a public repository. Never run a deployment that can be
# !! reached by anything you do not control on these values — yavio_service is
# !! a Postgres superuser that bypasses RLS, and the ClickHouse `default` user
# !! is unrestricted.
# !! EVERY password in this section ships BLANK, and compose refuses to start
# !! rather than substitute one. They used to default to `yavio_dev`, which is
# !! published in this public repository — including for the two most
# !! privileged accounts in the system: yavio_service is the Postgres
# !! superuser, and the ClickHouse `default` user is unrestricted and can
# !! manage access. A missing password is a startup error you fix in a minute;
# !! a published one is a credential anyone can read on GitHub.
#
# ./scripts/setup-env.sh generates strong random values for every password in
# this section, and rewrites DATABASE_URL / CLICKHOUSE_URL to match. If you are
# filling this in by hand, generate each with: openssl rand -hex 24 — and note
# that CLICKHOUSE_INGEST_PASSWORD and CLICKHOUSE_DASHBOARD_PASSWORD are applied
# to their ClickHouse users by `pnpm migrate:clickhouse`, not by compose.

POSTGRES_SERVICE_PASSWORD=yavio_dev # yavio_service role: table owner, bypasses RLS
POSTGRES_SERVICE_PASSWORD= # yavio_service role: the Postgres SUPERUSER — owns the tables,
# bypasses RLS, and can run COPY ... FROM PROGRAM. No default.
POSTGRES_API_PASSWORD= # yavio_api role: the app connects as this (NOT a superuser).
# Deliberately has NO default: compose refuses to start rather
# than fall back to a published value. setup-env.sh generates it.
POSTGRES_APP_PASSWORD= # yavio_app role: RLS enforced. Applied by `pnpm migrate` — blank so an
# upgrade cannot reset the role to a published value.
CLICKHOUSE_PASSWORD=yavio_dev # default user: runs migrations
# upgrade cannot reset the role to a published value. Set this: left
# unset, the role keeps the published 'yavio_dev' from migration 0001
# and holds DML on every table.
CLICKHOUSE_PASSWORD= # default user: unrestricted, runs migrations and manages access.
# No default — see the warning above.
CLICKHOUSE_INGEST_PASSWORD= # yavio_ingest user: INSERT only. Applied by `pnpm migrate:clickhouse`
# — blank so an upgrade cannot reset the user to a published value.
CLICKHOUSE_DASHBOARD_PASSWORD= # yavio_dashboard user: SELECT only, row policies. Applied by
Expand All @@ -55,8 +61,8 @@ CLICKHOUSE_ERASER_PASSWORD= # yavio_eraser user: ALTER DELETE on de
# ─── Database Connection URLs ────────────────────────────────────────────────
# localhost for local dev; Docker Compose overrides these with container hostnames.

DATABASE_URL=postgres://yavio_service:yavio_dev@localhost:5432/yavio
CLICKHOUSE_URL=http://default:yavio_dev@localhost:8123
DATABASE_URL=postgres://yavio_service:REPLACE_WITH_POSTGRES_SERVICE_PASSWORD@localhost:5432/yavio
CLICKHOUSE_URL=http://default:REPLACE_WITH_CLICKHOUSE_PASSWORD@localhost:8123

# ─── Dashboard ───────────────────────────────────────────────────────────────

Expand Down
7 changes: 5 additions & 2 deletions docker-compose.test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ services:
POSTGRES_USER: yavio_service
POSTGRES_PASSWORD: test
ports:
- "${TEST_POSTGRES_PORT:-5433}:5432"
# Loopback only. The dev compose file has bound its datastores this way
# since #42; this one was publishing a password-`test` Postgres to the
# whole LAN from every developer's machine.
- "127.0.0.1:${TEST_POSTGRES_PORT:-5433}:5432"
tmpfs:
- /var/lib/postgresql/data
healthcheck:
Expand All @@ -33,7 +36,7 @@ services:
CLICKHOUSE_PASSWORD: test
CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT: 1
ports:
- "${TEST_CLICKHOUSE_PORT:-8124}:8123"
- "127.0.0.1:${TEST_CLICKHOUSE_PORT:-8124}:8123"
tmpfs:
- /var/lib/clickhouse
healthcheck:
Expand Down
18 changes: 12 additions & 6 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ services:
environment:
POSTGRES_DB: yavio
POSTGRES_USER: yavio_service
POSTGRES_PASSWORD: ${POSTGRES_SERVICE_PASSWORD:-yavio_dev}
POSTGRES_PASSWORD: ${POSTGRES_SERVICE_PASSWORD:?POSTGRES_SERVICE_PASSWORD is not set. Run ./scripts/setup-env.sh, or add it to .env — it must never fall back to a published default. This role is the Postgres superuser.}
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
Expand All @@ -31,7 +31,7 @@ services:
image: clickhouse/clickhouse-server:24.3
restart: unless-stopped
environment:
CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD:-yavio_dev}
CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD:?CLICKHOUSE_PASSWORD is not set. Run ./scripts/setup-env.sh, or add it to .env — it must never fall back to a published default. This user is unrestricted and can manage access.}
CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT: 1
volumes:
- clickhouse_data:/var/lib/clickhouse
Expand Down Expand Up @@ -62,13 +62,19 @@ services:
context: .
dockerfile: packages/db/Dockerfile
environment:
DATABASE_URL: postgres://yavio_service:${POSTGRES_SERVICE_PASSWORD:-yavio_dev}@postgres:5432/yavio
CLICKHOUSE_URL: http://default:${CLICKHOUSE_PASSWORD:-yavio_dev}@clickhouse:8123
DATABASE_URL: postgres://yavio_service:${POSTGRES_SERVICE_PASSWORD:?POSTGRES_SERVICE_PASSWORD is not set. Run ./scripts/setup-env.sh, or add it to .env — it must never fall back to a published default. This role is the Postgres superuser.}@postgres:5432/yavio
CLICKHOUSE_URL: http://default:${CLICKHOUSE_PASSWORD:?CLICKHOUSE_PASSWORD is not set. Run ./scripts/setup-env.sh, or add it to .env — it must never fall back to a published default. This user is unrestricted and can manage access.}@clickhouse:8123
# Applied to their ClickHouse users by migrate-clickhouse.ts after
# migrations run — migration 0007 creates them with a published literal.
CLICKHOUSE_INGEST_PASSWORD: ${CLICKHOUSE_INGEST_PASSWORD:-}
CLICKHOUSE_DASHBOARD_PASSWORD: ${CLICKHOUSE_DASHBOARD_PASSWORD:-}
CLICKHOUSE_ERASER_PASSWORD: ${CLICKHOUSE_ERASER_PASSWORD:-}
# Applied to the yavio_app role by migrate.ts after migrations run.
# Compose-injected environment is NOT inherited from .env, so omitting this
# left yavio_app on the published 'yavio_dev' from 0001 even for operators
# who ran setup-env.sh and followed every documented step — the host-side
# `pnpm migrate` reads .env directly and repaired it, this path did not.
POSTGRES_APP_PASSWORD: ${POSTGRES_APP_PASSWORD:-}
# Applied to the yavio_api role by migrate.ts after migrations run.
POSTGRES_API_PASSWORD: ${POSTGRES_API_PASSWORD:?POSTGRES_API_PASSWORD is not set. Run ./scripts/setup-env.sh, or add it to .env — it must never fall back to a published default.}
networks:
Expand All @@ -86,7 +92,7 @@ services:
restart: unless-stopped
environment:
DATABASE_URL: postgres://yavio_api:${POSTGRES_API_PASSWORD:?POSTGRES_API_PASSWORD is not set. Run ./scripts/setup-env.sh, or add it to .env — it must never fall back to a published default.}@postgres:5432/yavio
CLICKHOUSE_URL: http://default:${CLICKHOUSE_PASSWORD:-yavio_dev}@clickhouse:8123
CLICKHOUSE_URL: http://default:${CLICKHOUSE_PASSWORD:?CLICKHOUSE_PASSWORD is not set. Run ./scripts/setup-env.sh, or add it to .env — it must never fall back to a published default. This user is unrestricted and can manage access.}@clickhouse:8123
# This service narrows the ClickHouse username; when set it uses that
# user's own password instead of the default user's.
CLICKHOUSE_INGEST_PASSWORD: ${CLICKHOUSE_INGEST_PASSWORD:-}
Expand Down Expand Up @@ -114,7 +120,7 @@ services:
restart: unless-stopped
environment:
DATABASE_URL: postgres://yavio_api:${POSTGRES_API_PASSWORD:?POSTGRES_API_PASSWORD is not set. Run ./scripts/setup-env.sh, or add it to .env — it must never fall back to a published default.}@postgres:5432/yavio
CLICKHOUSE_URL: http://default:${CLICKHOUSE_PASSWORD:-yavio_dev}@clickhouse:8123
CLICKHOUSE_URL: http://default:${CLICKHOUSE_PASSWORD:?CLICKHOUSE_PASSWORD is not set. Run ./scripts/setup-env.sh, or add it to .env — it must never fall back to a published default. This user is unrestricted and can manage access.}@clickhouse:8123
# This service narrows the ClickHouse username; when set it uses that
# user's own password instead of the default user's.
CLICKHOUSE_DASHBOARD_PASSWORD: ${CLICKHOUSE_DASHBOARD_PASSWORD:-}
Expand Down
26 changes: 17 additions & 9 deletions packages/docs/content/docs/04-self-hosting/02-configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -38,24 +38,32 @@ These must be set before starting the platform. Each should be a unique random s

## Database Passwords

| Variable | Default | Description |
**None of these have a default.** They used to fall back to `yavio_dev`, which is published in the public repository. `./scripts/setup-env.sh` generates all seven; if you fill them in by hand, use `openssl rand -hex 24`.

| Variable | Applied by | Role |
| --- | --- | --- |
| `POSTGRES_SERVICE_PASSWORD` | `yavio_dev` | PostgreSQL `yavio_service` role (table owner, bypasses RLS) |
| `POSTGRES_APP_PASSWORD` | `yavio_dev` | PostgreSQL `yavio_app` role (RLS enforced) |
| `CLICKHOUSE_PASSWORD` | `yavio_dev` | ClickHouse `default` user (runs migrations) |
| `CLICKHOUSE_INGEST_PASSWORD` | `yavio_dev` | ClickHouse `yavio_ingest` user (INSERT only) |
| `CLICKHOUSE_DASHBOARD_PASSWORD` | `yavio_dev` | ClickHouse `yavio_dashboard` user (SELECT only, row policies) |
| `POSTGRES_SERVICE_PASSWORD` | Compose | PostgreSQL `yavio_service` — the **superuser**: owns the tables, bypasses RLS |
| `POSTGRES_API_PASSWORD` | `pnpm migrate` | PostgreSQL `yavio_api` — what the application actually connects as (not a superuser) |
| `POSTGRES_APP_PASSWORD` | `pnpm migrate` | PostgreSQL `yavio_app` — RLS enforced |
| `CLICKHOUSE_PASSWORD` | Compose | ClickHouse `default` — unrestricted; runs migrations and manages access |
| `CLICKHOUSE_INGEST_PASSWORD` | `pnpm migrate:clickhouse` | ClickHouse `yavio_ingest` — INSERT only |
| `CLICKHOUSE_DASHBOARD_PASSWORD` | `pnpm migrate:clickhouse` | ClickHouse `yavio_dashboard` — SELECT only, row policies |
| `CLICKHOUSE_ERASER_PASSWORD` | `pnpm migrate:clickhouse` | ClickHouse `yavio_eraser` — `ALTER DELETE` on the events table only; cannot read what it erases |

<Callout type="warn">
Change all default passwords before exposing the platform to a network. The defaults are only safe for local development.
Compose refuses to start when `POSTGRES_SERVICE_PASSWORD`, `POSTGRES_API_PASSWORD` or `CLICKHOUSE_PASSWORD` is missing, rather than substituting a value. That is deliberate: a startup error takes a minute to fix, a published default is a credential anyone can read on GitHub.

The four applied by a migration are skipped when unset, so a deployment that shares one password across users keeps working — but each one left unset means that account keeps whatever it was created with.
</Callout>

## Database Connection URLs

| Variable | Default | Description |
| --- | --- | --- |
| `DATABASE_URL` | `postgres://yavio_service:yavio_dev@localhost:5432/yavio` | PostgreSQL connection string |
| `CLICKHOUSE_URL` | `http://default:yavio_dev@localhost:8123` | ClickHouse HTTP endpoint |
| `DATABASE_URL` | `postgres://yavio_service:<POSTGRES_SERVICE_PASSWORD>@localhost:5432/yavio` | PostgreSQL connection string |
| `CLICKHOUSE_URL` | `http://default:<CLICKHOUSE_PASSWORD>@localhost:8123` | ClickHouse HTTP endpoint |

These embed a password, so `setup-env.sh` rewrites them whenever it generates one. Editing a password by hand means editing these too.

In Docker Compose, these are overridden with container hostnames (`postgres`, `clickhouse`).

Expand Down
31 changes: 25 additions & 6 deletions scripts/setup-env.sh
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,32 @@ CLICKHOUSE_ERASER_PASSWORD=$(generate_db_password)
# Replace values in .env. The trailing-comment form in .env.example
# (`KEY=value # note`) is intentionally dropped for the secrets: a comment
# after a value is fragile to parse and has already caused one outage.
#
# The value travels through the ENVIRONMENT, never through argv. This used to be
# `sed -i "s|^${key}=.*|${key}=${value}|"`, which puts every secret on a command
# line — and a command line is world-readable through `ps` and
# /proc/<pid>/cmdline, while /proc/<pid>/environ is readable only by the owner.
# Passing them on argv handed any local user the entire datastore credential set
# and made the chmod 600 above pointless (CWE-214).
#
# Using awk instead of sed also removes the BSD/GNU `sed -i` split and the
# escaping question entirely: awk prints the value literally, so a `|`, `&` or
# backslash from some future generator cannot break the substitution or inject a
# second line. Today's values are base64/hex and cannot contain those — this is
# about not depending on that.
set_var() {
local key="$1" value="$2"
if [[ "$OSTYPE" == "darwin"* ]]; then
sed -i '' "s|^${key}=.*|${key}=${value}|" "$ENV_FILE"
else
sed -i "s|^${key}=.*|${key}=${value}|" "$ENV_FILE"
fi
local key="$1"
# umask, because `mv` keeps the temp file's mode: created under the default
# umask it would be world-readable and would silently widen .env.
(
umask 077
SET_VAR_KEY="$key" SET_VAR_VALUE="$2" awk '
BEGIN { key = ENVIRON["SET_VAR_KEY"]; value = ENVIRON["SET_VAR_VALUE"] }
index($0, key "=") == 1 { print key "=" value; next }
{ print }
' "$ENV_FILE" > "$ENV_FILE.tmp"
)
mv "$ENV_FILE.tmp" "$ENV_FILE"
}

set_var NEXTAUTH_SECRET "$NEXTAUTH_SECRET"
Expand Down
Loading