Skip to main content

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 up with --profile local and no service list in day-to-day startup, because that profile includes db-init (destructive reset).

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 caseExisting 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 CIop run --env-file env.1password -- bash -lc 'cd /Users/swilco/01-DEVELOPMENT/Evalium && EXPORT_STORAGE=minio make ci'
Non-destructive migrationsop 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 caseShort 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 CIop run --env-file env.1password -- bash -lc 'EXPORT_STORAGE=minio make ci'
Non-destructive migrationsENV_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-docker
  • vps-dev
  • staging
  • prod

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 cloudflared when switching sides.
  • Treat cloudflared as 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=1 unless you intentionally want to delete DB/MinIO data.
  • Stable default mode uses the existing built web:3000 runtime; HMR mode adds docker-compose.hmr.yml to swap back to Vite on web:5173.
  • Local browser path uses proxy/Caddy; tunnel path is cloudflared -> web/api directly.
  • A fixed COMPOSE_PROJECT_NAME means 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 signin again.
  • Compose warns about missing env vars → you likely ran docker compose without op run.
  • Env file missing in copied workspace → add env.1password (or set ENV_FILE=/path/to/file).
  • Do not commit secrets: only the op:// references live in env.1password (or legacy .env.1password if you intentionally keep the compatibility fallback).