1Password CLI (local dev + Docker)
This repo uses 1Password CLI to inject secrets at runtime (no secrets committed to git).
Install + sign in
brew install --cask 1password-cli
op signin
Confirm access:
op whoami
Env reference file (env.1password)
We use a reference-only env file with op:// values so Compose never stores secrets.
For this repo/workspace, use env.1password at repo root in direct commands and examples.
Some helper scripts still accept .env.1password as a compatibility fallback when ENV_FILE is not set, but new guidance in this repo should prefer env.1password explicitly.
Example:
POSTGRES_USER=op://Evalium/local-docker/POSTGRES_USER
POSTGRES_PASSWORD=op://Evalium/local-docker/POSTGRES_PASSWORD
POSTGRES_DB=op://Evalium/local-docker/POSTGRES_DB
APP_DB_USER=op://Evalium/local-docker/APP_DB_USER
APP_DB_PASSWORD=op://Evalium/local-docker/APP_DB_PASSWORD
MINIO_ROOT_USER=op://Evalium/local-docker/MINIO_ROOT_USER
MINIO_ROOT_PASSWORD=op://Evalium/local-docker/MINIO_ROOT_PASSWORD
EXPORT_STORAGE=minio
EXPORT_BUCKET=op://Evalium/local-docker/EXPORT_BUCKET
EXPORT_PREFIX=op://Evalium/local-docker/EXPORT_PREFIX
SESSION_SECRET=op://Evalium/local-docker/SESSION_SECRET
JWT_SECRET=op://Evalium/local-docker/JWT_SECRET
WEB_ORIGIN=op://Evalium/local-docker/WEB_ORIGIN
Run Compose with secrets injected
Recommended stable startup (default workflow: built web runtime + proxy + cloudflared):
op run --env-file env.1password -- \
docker compose -f docker-compose.yml -f docker-compose.local.yml up -d --build \
db minio minio-init api web proxy cloudflared
Optional reduced infrastructure-only startup (when you intentionally do not need the full frontend stack):
op run --env-file env.1password -- \
docker compose -f docker-compose.yml -f docker-compose.local.yml up -d --build \
db minio minio-init api
Explicit HMR/Vite opt-in (keeps tunnel + proxy, but swaps web to the Vite runtime):
op run --env-file env.1password -- \
docker compose -f docker-compose.yml -f docker-compose.local.yml -f docker-compose.hmr.yml up -d --build \
db minio minio-init api web proxy cloudflared
Important:
- Avoid
upwith--profile localand no service list in day-to-day startup, because that profile includesdb-init(destructive reset).
Convenience scripts (recommended)
If you prefer short commands, use the scripts in scripts/ (they handle op signin and op run):
./scripts/op-compose-up.sh
./scripts/op-compose-ps.sh
./scripts/op-db-init.sh
./scripts/op-db-migrate.sh
./scripts/op-compose-down.sh
op-compose-up.sh now defaults to the stable full stack (db minio minio-init api web proxy cloudflared) so the normal workflow includes the built frontend runtime and tunnel.
Use FRONTEND_MODE=hmr when you explicitly want Vite/HMR instead:
FRONTEND_MODE=hmr ./scripts/op-compose-up.sh
If you prefer not to run op signin manually, set OP_ACCOUNT once:
OP_ACCOUNT=your-team.1password.com ./scripts/op-compose-up.sh
You can also supply a fully custom sign-in command:
OP_SIGNIN_CMD='op signin --account your-team.1password.com' ./scripts/op-compose-up.sh
Set PURGE_VOLUMES=1 to remove volumes on shutdown:
PURGE_VOLUMES=1 ./scripts/op-compose-down.sh
Reset and re-init the database (Docker)
This runs backend/reset-and-reinit.sh inside the db-init container (destructive; drops and rebuilds schema):
op run --env-file env.1password -- \
docker compose -f docker-compose.yml -f docker-compose.local.yml --profile local run --rm db-init
Frontend Authentication (Local Dev)
Use this sequence to generate a magic-link login for your local browser while maintaining 1Password secret injection:
op run --env-file env.1password -- bash -lc '
source backend/tests/auth_login_helper.sh
login_as_owner
ELEVATE_PERMISSIONS=0 USER_ID="$AUTH_USER_ID" scripts/frontend_dev_auth_login.sh
'
Mobile Browser Login (QR)
Install qrencode once:
brew install qrencode
Generate a verify URL for phone login and print a terminal QR code:
op run --env-file env.1password -- bash -lc '
source backend/tests/auth_login_helper.sh
login_as_owner
OPEN_BROWSER=0 PRINT_ONLY=1 SHOW_QR=1 ELEVATE_PERMISSIONS=0 USER_ID="$AUTH_USER_ID" scripts/frontend_dev_auth_login.sh
'
If qrencode is not installed, the script still prints the raw verify URL; open it manually on the phone.
Rebuild API container (Docker)
Rebuild and restart only the API service:
op run --env-file env.1password -- \
docker compose -f docker-compose.yml -f docker-compose.local.yml up -d --build api
Force a no-cache API image rebuild, then start API:
op run --env-file env.1password -- \
docker compose -f docker-compose.yml -f docker-compose.local.yml build --no-cache api
op run --env-file env.1password -- \
docker compose -f docker-compose.yml -f docker-compose.local.yml up -d api
Run full local CI (Go tests + smoke suites)
op run --env-file env.1password -- \
bash -lc 'cd /Users/swilco/01-DEVELOPMENT/Evalium && EXPORT_STORAGE=minio make ci'
Workspace-root short commands for this copied workspace
If you are already in /Users/swilco/intent/workspaces/head-swan/evalium, use env.1password and the shorter forms below.
Existing longer-form commands on this page
| Use case | Existing command |
|---|---|
| Stable full stack (default) | op run --env-file env.1password -- docker compose -f docker-compose.yml -f docker-compose.local.yml up -d --build db minio minio-init api web proxy cloudflared |
| HMR full stack (opt-in) | op run --env-file env.1password -- docker compose -f docker-compose.yml -f docker-compose.local.yml -f docker-compose.hmr.yml up -d --build db minio minio-init api web proxy cloudflared |
| Full local CI | op run --env-file env.1password -- bash -lc 'cd /Users/swilco/01-DEVELOPMENT/Evalium && EXPORT_STORAGE=minio make ci' |
| Non-destructive migrations | op run --env-file env.1password -- docker compose -f docker-compose.yml -f docker-compose.local.yml --profile migrate run --rm db-migrate |
Equivalent short commands from this workspace root
| Use case | Short command |
|---|---|
| Stable full stack (default) | ENV_FILE=env.1password ./scripts/op-compose-up.sh |
| HMR full stack (opt-in) | ENV_FILE=env.1password FRONTEND_MODE=hmr ./scripts/op-compose-up.sh |
| Full local CI | op run --env-file env.1password -- bash -lc 'EXPORT_STORAGE=minio make ci' |
| Non-destructive migrations | ENV_FILE=env.1password ./scripts/op-db-migrate.sh |
Run migrations only (non-destructive)
op run --env-file env.1password -- \
docker compose -f docker-compose.yml -f docker-compose.local.yml --profile migrate run --rm db-migrate
DB-backed backend verification
Use this exact command form for backend checks that need DB/auth secrets:
op run --env-file env.1password -- bash -lc 'cd backend && <command>'
- Agents may execute DB/auth-backed verification with this command form when it is the smallest proportionate check needed, and should report the exact command run and result.
Bootstrap a new environment
Use the bootstrap script to create/update the 1Password item with fresh secrets:
OP_VAULT=Evalium OP_ITEM=local-docker ./scripts/op-bootstrap-env.sh
Defaults can be overridden by setting env vars (e.g. WEB_ORIGIN, POSTGRES_DB, EXPORT_BUCKET).
Why hex secrets?
We generate hex strings to avoid URL-unsafe characters in connection strings and JWT/session secrets.
Naming per environment
Use a clear, unique item name per environment, for example:
local-dockervps-devstagingprod
This makes it obvious which secrets belong to each environment and avoids accidental cross-use.
Two local workspaces on one machine (main + copied workspace)
If you keep two checked-out copies of this repo on the same machine (for example your main workspace and an Augment-copied workspace), you can switch the full Docker app context between them.
Recommended:
- Use one stable Compose project name in both workspaces so service names and networks stay consistent.
- Recreate
db minio minio-init api web proxy cloudflaredwhen switching sides. - Treat
cloudflaredas part of the normal stack, not an optional afterthought.
Set a shared project name in each shell session:
export COMPOSE_PROJECT_NAME=evalium-dev
Switch to workspace A:
cd /path/to/workspace-a
./scripts/op-compose-down.sh
./scripts/op-compose-up.sh
Switch to workspace B:
cd /path/to/workspace-b
./scripts/op-compose-down.sh
./scripts/op-compose-up.sh
One-command switch helper (run from either workspace):
./scripts/op-workspace-switch.sh /path/to/target-workspace \
--project evalium-dev
HMR/Vite opt-in during a switch:
./scripts/op-workspace-switch.sh /path/to/target-workspace \
--project evalium-dev \
--frontend-mode hmr
Notes:
- Do not run both workspace stacks simultaneously on the same ports.
- Do not pass
PURGE_VOLUMES=1unless you intentionally want to delete DB/MinIO data. - Stable default mode uses the existing built
web:3000runtime; HMR mode addsdocker-compose.hmr.ymlto swap back to Vite onweb:5173. - Local browser path uses
proxy/Caddy; tunnel path iscloudflared -> web/apidirectly. - A fixed
COMPOSE_PROJECT_NAMEmeans persistent volumes/state are shared across workspace switches unless removed.
Read a single value (sanity check)
op read "op://Evalium/local-docker/POSTGRES_USER"
Troubleshooting
op: command not found→ install the CLI (brew install --cask 1password-cli).- Permission/lock errors → run
op signinagain. - Compose warns about missing env vars → you likely ran
docker composewithoutop run. - Env file missing in copied workspace → add
env.1password(or setENV_FILE=/path/to/file). - Do not commit secrets: only the
op://references live inenv.1password(or legacy.env.1passwordif you intentionally keep the compatibility fallback).