Skip to content

fix: NANOCLAW_* flags set in .env never reach process.env under launchd/systemd#2730

Open
sturdy4days wants to merge 1 commit into
nanocoai:mainfrom
sturdy4days:fix/env-flags-service-env
Open

fix: NANOCLAW_* flags set in .env never reach process.env under launchd/systemd#2730
sturdy4days wants to merge 1 commit into
nanocoai:mainfrom
sturdy4days:fix/env-flags-service-env

Conversation

@sturdy4days

Copy link
Copy Markdown

Bug

Several modules read operational flags straight from process.env — most notably egress-lockdown.ts, which gates on process.env.NANOCLAW_EGRESS_LOCKDOWN in a top-level const, and whose docs (docs/SECURITY.md) tell users to "set NANOCLAW_EGRESS_LOCKDOWN=true". But nothing loads .env into the process environment: readEnvFile deliberately returns values without exporting them (so secrets stay out of child-process envs), and services started by launchd/systemd get no shell environment.

The result: putting the flag in .env — the place every other setting lives — silently does nothing. The host spawns agents on the default bridge network while the operator believes lockdown is on. Fail-open, and invisible until you audit docker inspect output. Found this live: lockdown was "enabled" for days with zero effect.

Related symptom family: #1934, #2134 (flags behaving differently under the service manager vs pnpm run dev from a shell, where exported vars happen to exist).

Fix

promoteEnvFlags() in src/env.ts: copies NANOCLAW_-prefixed entries from .env into process.env.

  • Scope: NANOCLAW_ prefix only — by convention those are feature flags and tuning knobs, never credentials. Secrets keep going through readEnvFile and stay out of child-process environments (the existing design, unchanged).
  • Precedence: real environment values win; .env never overrides a var the service manager set explicitly.
  • Ordering: called at the top of src/config.ts, which the import graph evaluates before any flag reader (verified: every importer of egress-lockdown.tscontainer-runner.ts, host-sweep.ts — imports config.js first, as does index.ts).
  • Hermetic tests: no-op under VITEST, so a developer's live-install .env can't steer test behavior.

Testing

  • New src/env.test.ts: 5 tests (promotion, comment/prefix filtering, no-override precedence, quote stripping, missing-file no-op, VITEST guard) — all pass
  • tsc clean
  • Full vitest suite: failure set byte-identical to stock main in the same environment (the pre-existing failures are unrelated to this change); the new test file passes
  • Verified live under launchd: with this change, NANOCLAW_EGRESS_LOCKDOWN=true in .env takes effect on service start

🤖 Generated with Claude Code

…hd/systemd

Several modules read operational flags straight from process.env — e.g.
egress-lockdown.ts gates on process.env.NANOCLAW_EGRESS_LOCKDOWN at module
evaluation, and docs/SECURITY.md tells users to "set
NANOCLAW_EGRESS_LOCKDOWN=true". But nothing loads .env into the process
environment: readEnvFile deliberately returns values without exporting
them (so secrets stay out of child-process envs), and services started by
launchd/systemd get no shell environment. The result is that putting the
flag in .env — the place every other setting lives — silently does
nothing: the host spawns agents on the default bridge network while the
operator believes lockdown is on. Fail-open, and invisible until audited.

Fix: promoteEnvFlags() in env.ts copies NANOCLAW_-prefixed entries (flags
and tuning knobs by convention, never credentials) from .env into
process.env, with real environment values taking precedence. Called at
the top of config.ts, which the import graph evaluates before any flag
reader. No-op under vitest so a developer's live .env can't steer tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant