Skip to content

Reconcile .env.example with the environment variables the server reads, and guard the drift - #5926

Merged
atomantic merged 1 commit into
mainfrom
claim/issue-5706
Sep 2, 2026
Merged

Reconcile .env.example with the environment variables the server reads, and guard the drift#5926
atomantic merged 1 commit into
mainfrom
claim/issue-5706

Conversation

@atomantic

Copy link
Copy Markdown
Owner

Summary

  • .env.example had drifted from what the server actually reads, in both directions. Every one of these variables is read lazily with a silent fallback, so an undocumented one is invisible — the feature just uses its default and nothing says otherwise.
  • Documented 16 missing variables, each with a one-line comment naming its reading module and its default. The clearest gap: signalSync.js reads SIGNAL_DIR, SIGNAL_CONFIG_PATH and SIGNAL_DB_PATH in one three-line block and only the middle one was documented, so relocating a Signal install configured half of it.
  • Removed one dead variable. PORTOS_UI_MAX_MEMORY was still advertised long after Configure portos-ui to restart before CoS memory warnings #5322 made the Vite ceiling a fixed constant — setting it did nothing.
  • Added server/envExampleDrift.test.js, a guard for both directions, registered in ALWAYS_RUN_TESTS (any server file can add a process.env read, and .env.example is not a scope the CI selector routes anywhere).

Beyond the 13 variables the issue named, the same scan also turned up PORT, PYTHON_BIN and SLOTSTREAM_MODEL_DIR; all three are documented here too.

How the guard avoids false positives

  • The forward scan blanks string and comment content first, so the process.env.X inside a vite config PortOS generates for a user is not mistaken for a PortOS setting.
  • INHERITED_ENV is a category allowlist with a reason per entry — OS-, toolchain- and test-harness-provided names a user would never put in .env — rather than a snapshot of today's diff, so it keeps meaning something as the tree grows.
  • The reverse scan is deliberately wide (every tracked code file, any language, anywhere in the repo) because a false "this is dead" would block CI over a real setting. Prose and test files are excluded so a doc mention cannot vouch for a key nothing reads.
  • Known blind spots are stated in the file header and pinned by fixture assertions, so they cannot quietly become wrong.

All added values are placeholders. No real host, path, or credential appears anywhere in the diff.

Test plan

  • cd server && npx vitest run envExampleDrift.test.js — passes in both directions (~0.5s).
  • Bypass probe: temporarily adding an undocumented process.env.PORTOS_PROBE_UNDOCUMENTED read and a # PORTOS_PROBE_DEAD_VAR= line makes the guard fail on exactly those two, naming the file for the first. Both probes reverted.
  • cd server && npm test — 37,674 passing. The 17 failures are pre-existing 10-second timeout flakes from running a large suite in a worktree under data/; all 9 affected files pass when run on their own.
  • scripts/repo-scan-guards.test.js and scripts/ci-test-plan.test.js confirm the new scanner is registered.

Closes #5706

…e drift (#5706)

.env.example is the only place a fresh install can discover what it can
configure, and every one of these variables is read lazily with a silent
fallback — so an undocumented one is invisible: the feature just uses its
default and nothing says otherwise. The file had rotted in both directions.

Forward: 16 variables the server reads were undocumented, including the two
that made the gap actively confusing. signalSync.js reads SIGNAL_DIR,
SIGNAL_CONFIG_PATH and SIGNAL_DB_PATH in one three-line block and only the
middle one was documented, so relocating a Signal install configured half of
it. Each new entry names its reading module and its default.

Reverse: PORTOS_UI_MAX_MEMORY was still advertised long after #5322 made the
Vite ceiling a fixed constant. Setting it did nothing; it is now removed.

server/envExampleDrift.test.js checks both directions so this cannot recur.
The forward scan covers server runtime modules with string and comment content
blanked, so a `process.env.X` inside a vite config PortOS GENERATES for a user
is not mistaken for a PortOS setting. The reverse scan is deliberately wide —
every tracked code file in the repo, any language — because a false "this is
dead" would block CI over a real setting; prose and tests are excluded so a
doc mention cannot vouch for a key nothing reads. Its INHERITED_ENV allowlist
is a category list with a reason per entry (OS-, toolchain- and test-harness-
provided names a user would never put in .env) rather than a snapshot of
today's diff, so it keeps meaning something as the tree grows.

Registered in ALWAYS_RUN_TESTS because any server file can add a process.env
read and .env.example is not a scope the selector routes anywhere.
@atomantic
atomantic merged commit 07c9bfd into main Sep 2, 2026
13 of 24 checks passed
@atomantic
atomantic deleted the claim/issue-5706 branch September 2, 2026 22:58
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.

.env.example has drifted from the environment variables the server actually reads

1 participant