Local stack — env resolution and measured budgets

Local stack — env resolution and measured budgets

How mise run dev:fake sources env vars and how much memory the local stack
actually uses. Plan 1 (.agents/plans/2026-10-04-dev-fake-no-infisical.md)
§2 D4 for the design; BRI-398 for the implementation.

Env resolution (scripts/dev-env.ts)

resolveDevEnv() picks one of two sources for the apps/web process:

  • Infisical (canonical, unchanged): the CLI is on PATH and authenticated
    (INFISICAL_TOKEN set, or infisical user get token exits 0) and
    BRIOSO_INFISICAL is not 0. apps/web is wrapped in
    infisical run --env=dev --recursive -- exactly as before.

  • Local files (fallback): anything else. Layered, lowest precedence first:

    layer file role
    defaults apps/web/.env.example committed defaults; empty and <placeholder> values count as unset
    overrides apps/web/.env.local gitignored per-developer overrides
    generated .brioso/dev-secrets.env WORKER_SHARED_SECRET, SESSION_SECRET — generated once, persisted, 0600

    Plus defaults applied unless .env.local or the shell sets them:
    PUBLIC_ORIGIN=http://localhost:3000, NODE_ENV=development,
    EMAIL_PROVIDER=file, MAIL_OUTBOX_FILE=<repo>/.brioso/mail/outbox.jsonl.
    Non-empty shell env vars always win.

    EMAIL_PROVIDER=file currently means “no provider configured” — the app
    warns and skips emails exactly like an unset provider until BRI-399 lands
    the file/mailpit adapters that read MAIL_OUTBOX_FILE. The generated
    WORKER_SHARED_SECRET is what resolveWorkerSecret() returns, so web and
    the fake runner always agree.

Force the local path even with a working CLI: BRIOSO_INFISICAL=0.

Measured memory (Linux x64, bun 1.3.14, vite dev)

Measured with ps -o rss ~60 s after mise run dev:fake was fully up, after
hitting /dev/login?persona=free-new, POST /jobs (Free job → paid), and
/spaces:

process RSS plan budget (D2)
web: Vite dev + Nitro SSR (one node proc) ~1.75 GiB (1,796,700 KiB) 1.5 GB est.
fake runner (bun) + dev-fake orchestrator ~114 MiB combined 0.2 GB est.
garage (docker, dxflrs/garage:v1.0.1) idle ≪ 128m limit 0.13 GB limit

The web process runs ~0.25 GiB over the D2 estimate. Total for the default
dev:fake loop (web + fake runner + garage) is ≈ 1.9 GB of the 8 GB budget —
still fits, but the headroom is thinner than the estimate suggested. The
heavier combinations in D2 (+audio.cpp ≈ +2 GB, +Playwright ≈ +1 GB) should be
re-measured in U7/U6 rather than trusted blindly.

Verified locally (BRI-398 acceptance, 2026-10-04)

  • mise run dev:fake boots on a box with no infisical on PATH — no
    ERR_MODULE_NOT_FOUND (the predev intl compile runs inside bun run dev).
  • GET /dev/login?persona=free-new → 302 + brioso_session cookie.
  • Free job (POST /jobs) → fake runner claims, encodes, uploads to Garage →
    state paid in ~4 s.
  • Boot audit warns (non-fatal, expected): EMAIL_FROM, BREVO_API_KEY,
    RUNPOD_*, R2_CATALOG_* are unset under the local source — the fake path
    needs none of them.