You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
What broke: Environment variables defined in a repo-local .env file are silently unavailable to workflows run via the Archon CLI. The dotenv log output reports injecting env (0) from .env which reads like "the file was empty," but the vars actually were parsed and then deliberately deleted from process.env by stripCwdEnv(). Additionally, there is no repo-scoped archon-owned location (e.g. <repo>/.archon/.env) to put workflow-time env vars — the only working path is ~/.archon/.env, which is neither discoverable from the logs nor documented.
When it started (if known): Since packages/paths/src/strip-cwd-env.ts was introduced.
Run any CLI command, e.g. bun run cli workflow list --json.
Observe the dotenv preamble output:
[dotenv@17.3.1] injecting env (0) from .env
[dotenv@17.3.1] injecting env (0) from .env.local
[dotenv@17.3.1] injecting env (0) from .env.development
[dotenv@17.3.1] injecting env (0) from .env.production
[dotenv@17.3.1] injecting env (3) from ../../../../../.archon/.env
Inside a workflow node (or any code reading process.env.SLACK_WEBHOOK), the var is not present.
Expected vs Actual
Expected:
Logs clearly state that CWD env keys were deliberately stripped (with the concrete path and files named) — not "injecting (0)".
There is an archon-scoped repo-local location (<repo>/.archon/.env) that the operator can use for per-project workflow secrets and that DOES load.
Actual: the misleading injecting env (0) from .env makes operators believe their env file was parsed but empty. The only loadable location is ~/.archon/.env (user-wide), which is not discoverable from the logs or docs. Users who put secrets in <repo>/.env lose them silently to stripCwdEnv.
User Flow
operator Archon CLI workflow node
──────── ────────── ─────────────
edits .env ─────────────▶ bun auto-loads .env into process.env
adds SLACK_WEBHOOK=…
strip-cwd-env-boot [first import]:
parses .env to discover keys
[X] Reflect.deleteProperty(process.env, 'SLACK_WEBHOOK')
logs: "injecting env (0) from .env"
(0 because processEnv:{} is used)
runs workflow ──────────▶ cli.ts loads ~/.archon/.env
workflow node reads process.env.SLACK_WEBHOOK
→ undefined
→ "Slack post skipped"
operator confused ◀────────────────────────────── no error, no actionable log line
Environment
Platform: CLI (also affects any entry point that imports @archon/paths/strip-cwd-env-boot)
Database: N/A
Running in worktree? No
OS: macOS 25.3.0, but reproduces anywhere stripCwdEnv runs
Logs
Abbreviated CLI startup (run from repo root):
[dotenv@17.3.1] injecting env (0) from .env ← SLACK_WEBHOOK was parsed + deleted here
[dotenv@17.3.1] injecting env (0) from .env.local
[dotenv@17.3.1] injecting env (0) from .env.development
[dotenv@17.3.1] injecting env (0) from .env.production
[dotenv@17.3.1] injecting env (3) from ../../../../../.archon/.env ← only loaded source
The strip pass calls config({ path: filepath, processEnv: {} }) — parsing the file into a throwaway object. dotenv's "injecting (N)" log reflects processEnv writes (always 0 because it's a throwaway), NOT keys parsed from the file. The subsequent Reflect.deleteProperty(process.env, key) loop is what actually makes keys disappear, but it emits nothing.
Impact
Affected workflows/commands: any CLI command reading workflow-time env vars (e.g. the repo-triage workflow relying on SLACK_WEBHOOK to post a digest to Slack).
Reproduction rate: Always.
Workaround: put the var in ~/.archon/.env (user-level) or export it via shell before running. Neither is discoverable from the logs or docs.
Scope by directory convention, not by filename..archon/ (whether at ~/ or at <repo>/) is the archon-owned namespace. Anything else is the user's.
Path
strip-cwd-env
Archon loads?
Why
<repo>/.env
strips (keeps guard)
never
User's project env — may contain unrelated secrets (their own ANTHROPIC_API_KEY for their app's LLM calls, random tool secrets, etc.) that must not leak into Archon
<repo>/.archon/.env
untouched
yes (repo scope)
Archon-owned directory — user explicitly put these vars here for archon
~/.archon/.env
untouched
yes (user scope)
Archon-owned directory — user-wide archon config
Directory ownership is the security boundary, not the filename.
Load order
1. ~/.archon/.env (user-wide defaults; override: true over shell)
2. <cwd>/.archon/.env (repo-scoped overrides; override: true over #1)
— strip-cwd-env runs BEFORE #1, still deletes <repo>/.env keys as today
Specific wins over general — a user can set a default SLACK_WEBHOOK in ~/.archon/.env for personal use, then override per-project with a different #gh-digest webhook in <repo>/.archon/.env.
Clearer log output
Replace the current misleading dotenv@17.3.1 preamble with Archon-owned lines that name the exact CWD path and the exact files, and only print when there's something to report.
Output when there is a repo-local env with keys that get stripped, plus a repo-scoped archon config loaded:
[archon] stripped 4 keys from /Users/rasmus/Projects/cole/Archon (.env, .env.local) to prevent target repo env from leaking into Archon processes
[archon] loaded 3 keys from ~/.archon/.env
[archon] loaded 2 keys from /Users/rasmus/Projects/cole/Archon/.archon/.env (repo scope, overrides user scope)
Output when running from a directory with no CWD env files and no repo-scoped archon config:
[archon] loaded 3 keys from ~/.archon/.env
Rules:
Use the CWD path directly — no owner/repo git lookup. Simpler, no boot-time git call, works anywhere Archon runs (CI, Docker, arbitrary dirs).
First line only prints when > 0 keys were stripped. Silent in the common case (no target-repo .env). When it does print, the parenthetical names every affected file.
Second line only prints when ~/.archon/.env exists and loaded > 0 keys.
Third line only prints when <cwd>/.archon/.env exists and loaded > 0 keys. The "(repo scope, overrides user scope)" annotation reminds operators of precedence.
Pass { quiet: true } to the underlying dotenv.config() calls so the existing [dotenv@17.3.1] injecting env (N) from … output is suppressed. These Archon-owned lines become the only env-loading signal an operator sees.
Call sites to update
packages/paths/src/strip-cwd-env.ts:41-66 — aggregate the 4 files into one log line after the delete loop (emit only when cwdKeys.size > 0); suppress per-file dotenv logging with quiet: true.
packages/cli/src/cli.ts:22-31 — replace silent load with a count log after config() returns (emit only when result.parsed has entries); add a second load for <cwd>/.archon/.env with override: true.
packages/server/src/index.ts (and any other entry point that loads ~/.archon/.env) — mirror the same two-scope load + log format.
Same underlying design decision. #1303 is the write side (where can archon setup write?); this issue is the read side (where does the CLI load from?). They must agree on the same three paths for the model to be coherent.
bug(cli/setup): archon setup silently overwrites repo .env and loses secrets #1303 (this repo): archon setup should write to ~/.archon/.env (home scope) OR <repo>/.archon/.env (project scope), selected by the user — never to <repo>/.env. Merge-only writes (no overwrite of existing keys). Backup before write. Related to our user-facing .archon/config.yaml which already has the same home/project scoping.
Summary
.envfile are silently unavailable to workflows run via the Archon CLI. The dotenv log output reportsinjecting env (0) from .envwhich reads like "the file was empty," but the vars actually were parsed and then deliberately deleted fromprocess.envbystripCwdEnv(). Additionally, there is no repo-scoped archon-owned location (e.g.<repo>/.archon/.env) to put workflow-time env vars — the only working path is~/.archon/.env, which is neither discoverable from the logs nor documented.packages/paths/src/strip-cwd-env.tswas introduced.minor(UX / developer experience — not a data loss risk in isolation; compounds with bug(cli/setup): archon setup silently overwrites repo .env and loses secrets #1303 for users who discovered<repo>/.envas the "obvious" place to put archon env vars and then ranarchon setup)Steps to Reproduce
.env:bun run cli workflow list --json.process.env.SLACK_WEBHOOK), the var is not present.Expected vs Actual
<repo>/.archon/.env) that the operator can use for per-project workflow secrets and that DOES load.injecting env (0) from .envmakes operators believe their env file was parsed but empty. The only loadable location is~/.archon/.env(user-wide), which is not discoverable from the logs or docs. Users who put secrets in<repo>/.envlose them silently tostripCwdEnv.User Flow
Environment
@archon/paths/strip-cwd-env-boot)Logs
Abbreviated CLI startup (run from repo root):
Relevant code:
packages/paths/src/strip-cwd-env.ts:41-66.The strip pass calls
config({ path: filepath, processEnv: {} })— parsing the file into a throwaway object. dotenv's "injecting (N)" log reflectsprocessEnvwrites (always 0 because it's a throwaway), NOT keys parsed from the file. The subsequentReflect.deleteProperty(process.env, key)loop is what actually makes keys disappear, but it emits nothing.Impact
repo-triageworkflow relying onSLACK_WEBHOOKto post a digest to Slack).~/.archon/.env(user-level) or export it via shell before running. Neither is discoverable from the logs or docs.<repo>/.envand then runsarchon setuploses them permanently.Proposed fix — unified design
Scope by directory convention, not by filename.
.archon/(whether at~/or at<repo>/) is the archon-owned namespace. Anything else is the user's.strip-cwd-env<repo>/.envANTHROPIC_API_KEYfor their app's LLM calls, random tool secrets, etc.) that must not leak into Archon<repo>/.archon/.env~/.archon/.envDirectory ownership is the security boundary, not the filename.
Load order
Specific wins over general — a user can set a default
SLACK_WEBHOOKin~/.archon/.envfor personal use, then override per-project with a different#gh-digestwebhook in<repo>/.archon/.env.Clearer log output
Replace the current misleading
dotenv@17.3.1preamble with Archon-owned lines that name the exact CWD path and the exact files, and only print when there's something to report.Output when there is a repo-local env with keys that get stripped, plus a repo-scoped archon config loaded:
Output when running from a directory with no CWD env files and no repo-scoped archon config:
Rules:
owner/repogit lookup. Simpler, no boot-time git call, works anywhere Archon runs (CI, Docker, arbitrary dirs).> 0keys were stripped. Silent in the common case (no target-repo.env). When it does print, the parenthetical names every affected file.~/.archon/.envexists and loaded> 0keys.<cwd>/.archon/.envexists and loaded> 0keys. The "(repo scope, overrides user scope)" annotation reminds operators of precedence.{ quiet: true }to the underlyingdotenv.config()calls so the existing[dotenv@17.3.1] injecting env (N) from …output is suppressed. These Archon-owned lines become the only env-loading signal an operator sees.Call sites to update
packages/paths/src/strip-cwd-env.ts:41-66— aggregate the 4 files into one log line after the delete loop (emit only whencwdKeys.size > 0); suppress per-file dotenv logging withquiet: true.packages/cli/src/cli.ts:22-31— replace silent load with a count log afterconfig()returns (emit only whenresult.parsedhas entries); add a second load for<cwd>/.archon/.envwithoverride: true.packages/server/src/index.ts(and any other entry point that loads~/.archon/.env) — mirror the same two-scope load + log format.Relation to #1303
Same underlying design decision. #1303 is the write side (where can
archon setupwrite?); this issue is the read side (where does the CLI load from?). They must agree on the same three paths for the model to be coherent.archon setupshould write to~/.archon/.env(home scope) OR<repo>/.archon/.env(project scope), selected by the user — never to<repo>/.env. Merge-only writes (no overwrite of existing keys). Backup before write. Related to our user-facing.archon/config.yamlwhich already has the same home/project scoping.(0) from .envinstead ofstripped N keys#1302 (this issue): load from the same two archon-scoped paths. Keep stripping<repo>/.env— the guard still does real work by protecting against target-repo var leaks (e.g., the user's ownANTHROPIC_API_KEYfor their unrelated app).Landing either without the other leaves the system half-coherent. Ideally they ship together, or #1303 lands on top of #1302.
Scope
paths,cli,server(any entry point that callsstripCwdEnv/ loads~/.archon/.env)paths:strip-cwd-env,cli:cli.ts,server:index.ts