DB Password Rotation
DB_PASSWORD is now required (no weak default) in investment-agent/docker-compose.yml
and docker-compose.sit.yml. Generate a strong value and set it in the relevant
.env / .env.sit before starting the stack:
# Use hex (not base64): the value is embedded in the DATABASE_URL DSN, and
# base64's +, /, = characters are URL-unsafe and would break the connection string.
openssl rand -hex 24
Caveat: an existing Postgres volume keeps its old password
POSTGRES_PASSWORD is only applied when the Postgres data directory is first
initialized. If the postgres_data (or sit_postgres_data) volume already
exists, changing DB_PASSWORD in .env alone will not update the database —
the app will then fail to authenticate. Pick one:
Option A — rotate in place (keeps data)
# 1. Change the password inside the running DB to the new value
docker exec -it cortex-postgres \
psql -U "$DB_USER" -d "$DB_NAME" \
-c "ALTER USER \"$DB_USER\" WITH PASSWORD '<new-password>';"
# 2. Set the SAME new value as DB_PASSWORD in investment-agent/.env
# 3. Recreate the app containers so they pick up the new DATABASE_URL
docker compose up -d --force-recreate agent-api scheduler
Option B — recreate the volume (destroys data)
Only when the data is disposable (e.g. a fresh SIT bootstrap). This wipes the DB:
docker compose down
docker volume rm <project>_postgres_data # or sit_postgres_data
# set the new DB_PASSWORD in .env, then re-bootstrap per docs/runbooks/sit-deploy.md
docker compose up -d
JWT secret rotation
JWT_SECRET_KEY is also required and must not be a placeholder (values containing
change-me, placeholder, etc. are rejected at boot; production additionally
requires ≥ 32 characters). Generate one with:
openssl rand -hex 32
Rotating it invalidates all existing sessions — users must log in again. This is expected and safe.