Context
The repo's monolithic .env aggregates credentials + configuration spanning multiple domains: Anthropic API key, OpenAI compatible endpoint, GitHub PAT, Chroma host/port, Memory Core flags, etc. Operators editing one domain's variable risk inadvertently overwriting adjacent variables (paste-into-wrong-line, partial-replace-not-isolated-replace, etc.).
This ticket is preventive operational substrate / MX hardening — it splits the monolithic .env into domain-scoped files so cross-domain edits become structurally isolated.
The Problem
Empirically, edits to one domain's variable can accidentally clobber another domain's variable in a monolithic .env. The blast radius scales with the number of variables in the file. With ~30+ env vars now in scope (GH_TOKEN, ANTHROPIC_API_KEY, NEO_OPENAI_COMPATIBLE_*, NEO_CHROMA_*, NEO_AUTH_*, ONEO_AUTH_*, GEMINI_API_KEY, etc.), any operator edit carries a real cross-contamination risk.
The Architectural Reality
dotenv package supports loading from explicit file paths.
- Many config consumers currently expect process.env to be pre-populated (no per-call .env load).
- A loader that reads multiple
.env.<domain> files at process start can populate process.env identically to a single .env, transparent to consumers.
- Domain-scoped files would be:
.env.github (GH_TOKEN, GH_TOKEN_NEO_*)
.env.anthropic (ANTHROPIC_API_KEY)
.env.openai (NEO_OPENAI_COMPATIBLE_*)
.env.gemini (GEMINI_API_KEY)
.env.chroma (NEO_CHROMA_HOST, NEO_CHROMA_PORT, NEO_CHROMA_UNIFIED)
.env.auth (NEO_AUTH_*, ONEO_AUTH_*)
.env.memory-core (NEO_MC_*, AUTO_*, REAL_TIME_MEMORY_PARSING, etc.)
The Fix
- Loader helper at process start: enumerate
.env.<domain> files in repo root + load each into process.env (later files don't override earlier — first-wins, intentional).
- Migration: split current
.env into the domain-scoped files. Existing .env retained as legacy fallback for one deprecation window, then removed.
.gitignore update: ensure all .env.* files are gitignored (verify before commit).
- Operator runbook: documents which domain file to edit for which variable. Lives in
learn/agentos/env-file-conventions.md.
- Bootstrap helper (
buildScripts/bootstrapEnv.mjs): generates the domain files from a template (.env.example.<domain>) on first checkout, mirroring the existing bootstrapWorktree.mjs pattern.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback / Edge Case |
Docs |
Evidence |
.env.<domain> files |
New env-loader helper |
Each domain file loaded into process.env at process start |
Missing domain file → that domain's vars absent (consumers handle defaults) |
learn/agentos/env-file-conventions.md (new) |
Unit + integration test: env-loader populates process.env from multiple files |
Legacy .env |
Backward-compat shim |
Loaded if present, with deprecation WARN |
Removed after deprecation window (separate hard-cut ticket) |
Same |
Unit test: legacy-file-warns-but-loads |
.gitignore |
Repo-level |
All .env.* files gitignored |
Templates .env.example.<domain> checked-in |
Same |
Verify in CI |
Acceptance Criteria
Out of Scope
- Per-process domain-file selection (e.g., MC daemon loads only
.env.memory-core + .env.chroma) — orthogonal optimization; v1 loads all
- Encrypted env files — out of scope
- Cross-machine env synchronization — out of scope; per-machine local files
Avoided Traps / Gold Standards Rejected
- Single .env with section headers — comments don't prevent paste-into-wrong-line; need physical file boundaries
- Symlinks from
.env to .env.<domain> for backward-compat — fragile; explicit loader is cleaner
- JSON config file replacing .env — different format paradigm; forks ecosystem tooling expectations; .env-shape preserved is the KISS choice
Related
- #10858 (per-agent credential namespacing) —
GH_TOKEN_NEO_OPUS_4_7 etc. would live in .env.github cleanly
- Pairs with the broader operator-credentials hygiene story; per-agent + per-domain together close the cross-contamination surface
Origin Session ID: 8b31fd62-6a53-40b5-aae2-c5288f8ced09
Retrieval Hint: "per-domain env file split monolithic .env cross-contamination credential isolation"
Context
The repo's monolithic
.envaggregates credentials + configuration spanning multiple domains: Anthropic API key, OpenAI compatible endpoint, GitHub PAT, Chroma host/port, Memory Core flags, etc. Operators editing one domain's variable risk inadvertently overwriting adjacent variables (paste-into-wrong-line, partial-replace-not-isolated-replace, etc.).This ticket is preventive operational substrate / MX hardening — it splits the monolithic
.envinto domain-scoped files so cross-domain edits become structurally isolated.The Problem
Empirically, edits to one domain's variable can accidentally clobber another domain's variable in a monolithic
.env. The blast radius scales with the number of variables in the file. With ~30+ env vars now in scope (GH_TOKEN, ANTHROPIC_API_KEY, NEO_OPENAI_COMPATIBLE_*, NEO_CHROMA_*, NEO_AUTH_*, ONEO_AUTH_*, GEMINI_API_KEY, etc.), any operator edit carries a real cross-contamination risk.The Architectural Reality
dotenvpackage supports loading from explicit file paths..env.<domain>files at process start can populate process.env identically to a single.env, transparent to consumers..env.github(GH_TOKEN, GH_TOKEN_NEO_*).env.anthropic(ANTHROPIC_API_KEY).env.openai(NEO_OPENAI_COMPATIBLE_*).env.gemini(GEMINI_API_KEY).env.chroma(NEO_CHROMA_HOST, NEO_CHROMA_PORT, NEO_CHROMA_UNIFIED).env.auth(NEO_AUTH_*, ONEO_AUTH_*).env.memory-core(NEO_MC_*, AUTO_*, REAL_TIME_MEMORY_PARSING, etc.)The Fix
.env.<domain>files in repo root + load each into process.env (later files don't override earlier — first-wins, intentional)..envinto the domain-scoped files. Existing.envretained as legacy fallback for one deprecation window, then removed..gitignoreupdate: ensure all.env.*files are gitignored (verify before commit).learn/agentos/env-file-conventions.md.buildScripts/bootstrapEnv.mjs): generates the domain files from a template (.env.example.<domain>) on first checkout, mirroring the existingbootstrapWorktree.mjspattern.Contract Ledger Matrix
.env.<domain>fileslearn/agentos/env-file-conventions.md(new).env.gitignore.env.*files gitignored.env.example.<domain>checked-inAcceptance Criteria
.env.<domain>files.gitignoreexcludes all.env.*(with!.env.example.*exception for templates).env.example.<domain>committed for: github, anthropic, openai, gemini, chroma, auth, memory-core.envcontinues to load with deprecation WARN (one-window backward-compat)learn/agentos/env-file-conventions.mddocumenting which variable belongs to which domain.envcontent into domain files (operator's local action; doc only)Out of Scope
.env.memory-core+.env.chroma) — orthogonal optimization; v1 loads allAvoided Traps / Gold Standards Rejected
.envto.env.<domain>for backward-compat — fragile; explicit loader is cleanerRelated
GH_TOKEN_NEO_OPUS_4_7etc. would live in.env.githubcleanlyOrigin Session ID: 8b31fd62-6a53-40b5-aae2-c5288f8ced09 Retrieval Hint: "per-domain env file split monolithic .env cross-contamination credential isolation"