Repository navigation
fix(cli): source reloadPassword from .env in scaffold; reconcile on WHEELS_RELOAD_PASSWORD - #2857
Conversation
…HEELS_RELOAD_PASSWORD
`wheels new` hard-coded the generated reload password as a literal into
config/settings.cfm (a committed file) and echoed it again in a comment,
leaking a secret into version control. It also wrote the value to .env
under RELOAD_PASSWORD while docs/deploy templates used WHEELS_RELOAD_PASSWORD,
so copy-pasting the documented env() snippet into a fresh app resolved to ""
and tripped the "reloadPassword is empty" warning.
Scaffold:
- config/settings.cfm and both app/snippets/*.txt now read the password via
`set(reloadPassword=env("WHEELS_RELOAD_PASSWORD", ""))`; the random value
the generator creates lives only in .env (git-ignored). Comment no longer
contains the literal.
- _env now emits WHEELS_RELOAD_PASSWORD, consistent with WHEELS_ENV /
WHEELS_DATASOURCE and the `wheels deploy` secrets contract (so the same
env() lookup works unchanged from laptop .env to production deploy).
CLI / console:
- detectReloadPassword() and the e2e mirror accept `(?:WHEELS_)?RELOAD_PASSWORD`
so apps generated before the rename keep working. Added a CLI spec asserting
WHEELS_RELOAD_PASSWORD detection.
- Console + reload hint strings now name WHEELS_RELOAD_PASSWORD.
Docs (standardize on bare env() + WHEELS_RELOAD_PASSWORD):
- core-concepts/environments-and-configuration and the 5 deployment guides
(production-config, security-hardening, docker-deployment, vm-deployment,
dev-server) reconciled to the bare env() accessor and the prefixed name.
- Replaced an insecure docker example that used
`Server.System.getEnv("RELOAD_PASSWORD") ?: "changeme"` (wrong accessor,
unprefixed, guessable fallback) with `env("WHEELS_RELOAD_PASSWORD", "")`.
- Refreshed the stale security-hardening note that claimed the template
hard-codes {{reloadPassword}}, and the Global.cfc line reference.
Follow-up: 13 `application.wo.env` usages remain in 4 non-deploy guides
(database-and-multiple-datasources, sending-email, authentication-patterns,
testing) — deferred to a separate docs-accessor sweep.
Reported in #2854
Signed-off-by: Peter Amiri <peter@alurium.com>
There was a problem hiding this comment.
Wheels Bot — Reviewer A
TL;DR: The PR correctly identifies and fixes the primary security issue — the scaffold template was committing a random literal reload password to version control via config/settings.cfm and its snippets. The CLI rename (RELOAD_PASSWORD → WHEELS_RELOAD_PASSWORD), docs sweep, and backwards-compatible regex are all well-executed. However, two files that directly exhibit the exact same vulnerability are left unchanged: examples/starter-app/config/settings.cfm still has set(reloadPassword="changeme"), and cli/src/commands/wheels/config/check.cfc still generates a sample .env with the old variable name. Requesting changes on those two, plus a minor test message nit and a missing CHANGELOG entry.
Correctness
examples/starter-app/config/settings.cfm:26 — hardcoded literal survives
// Reload your application with ?reload=true&password=changeme
set(reloadPassword="changeme");This is the same committed-literal pattern the PR exists to eliminate. The starter-app is a reference implementation that developers copy from. Leaving "changeme" here undermines the PR's goal: a user who scaffolds from the starter-app or follows it as a template will commit a guessable password. Fix:
// Reload your application with ?reload=true&password=<your WHEELS_RELOAD_PASSWORD>
// The secret is read from .env (git-ignored) so it never lands in version control.
set(reloadPassword=env("WHEELS_RELOAD_PASSWORD", ""));The starter-app's .env.example (examples/starter-app/.env.example) should also gain a WHEELS_RELOAD_PASSWORD= entry so users know what variable to populate.
cli/src/commands/wheels/config/check.cfc:374 — auto-repair generates mismatched variable name
"RELOAD_PASSWORD=your_reload_password",wheels config check --fix auto-creates a sample .env using the old variable name. Any new app's config/settings.cfm now calls env("WHEELS_RELOAD_PASSWORD", ""), so a user who runs wheels config check --fix gets a .env with RELOAD_PASSWORD — which the framework will not find, silently producing an empty reload password and the "reloadPassword is empty" boot warning. (The CLI's backwards-compatible detectReloadPassword() regex helps password detection in the CLI itself, but env("WHEELS_RELOAD_PASSWORD", "") in config/settings.cfm uses an exact key lookup via application.env["WHEELS_RELOAD_PASSWORD"] — there is no fallback alias at the CFML runtime layer.)
Fix: change line 374 to "WHEELS_RELOAD_PASSWORD=your_reload_password".
Tests
cli/tests/specs/e2e/ServerCommandsTest.cfc:171 — stale assertion message
expect(envPos).toBeGT(0, "Should check .env for RELOAD_PASSWORD");The test checks that Module.cfc source contains RELOAD_PASSWORD — which it still does (in the backwards-compat regex comment and pattern). The test passes, but the message now reads as an assertion about the old name being canonical rather than a legacy-path check. Suggest updating to:
expect(envPos).toBeGT(0, "Should check .env for WHEELS_RELOAD_PASSWORD (or legacy RELOAD_PASSWORD)");Docs
No CHANGELOG.md [Unreleased] entry
The PR renames the scaffolded .env variable from RELOAD_PASSWORD to WHEELS_RELOAD_PASSWORD. Existing apps that already have a .env with RELOAD_PASSWORD=... are unaffected (their config/settings.cfm still has the old literal form), but anyone who regenerates the scaffold or copies a snippet from the new guides will need to know to update their .env key. A short note under [Unreleased] ### Fixed or ### Changed documents the migration path and the backwards-compatible detection in detectReloadPassword().
Minor nit (no change required)
The two snippet files ConfigDataSourceH2Content.txt and ConfigReloadPasswordContent.txt remain byte-for-byte identical after the change (they were identical before too — pre-existing duplication, out of scope here).
The Dockerfile example in docker-deployment.mdx:61 still bakes WHEELS_RELOAD_PASSWORD=change-me into the image via ENV. The rename is correct; the guessable placeholder is a pre-existing documentation choice and the compose file's ${WHEELS_RELOAD_PASSWORD:?required} form is the authoritative pattern. Worth a doc note somewhere that the ENV default should be overridden before shipping, but not blocking.
Wheels Bot — Reviewer B (round 1)A's review is well-grounded. Every finding traces to actual file content, the verdict is consistent, and there is no sycophancy. I verified all three claims independently and find no false positives. Aligned on request-changes. SycophancyNone detected. A requested changes and cited specific file paths and line numbers for every finding. False positivesNone detected.
Missed issuesNone that rise to blocking. One minor downstream note A does not call out explicitly: Verdict alignmentA's ConvergenceAligned. A's findings are all confirmed, no false positives to retract, and the |
… .env name
Reviewer A (wheels-bot) findings:
- examples/starter-app/config/settings.cfm committed a guessable
`set(reloadPassword="changeme")` — the same vuln class this PR removes
from the scaffold. Now reads `env("WHEELS_RELOAD_PASSWORD", "")` (the
starter-app's Application.cfc already loads .env), and its .env.example
gains a WHEELS_RELOAD_PASSWORD entry.
- cli/src/commands/wheels/config/check.cfc (`wheels config check --fix`,
still shipped as the legacy CommandBox wheels-cli module) generated a
sample .env with the old RELOAD_PASSWORD key. Since env() does an exact
key lookup at the CFML layer (no WHEELS_ alias there — only the CLI's
detection regex has it), a new app's config/settings.cfm would not find
it. Renamed to WHEELS_RELOAD_PASSWORD (now consistent with the WHEELS_ENV
line already in the same sample).
- Updated the stale ServerCommandsTest assertion message to name
WHEELS_RELOAD_PASSWORD (or legacy RELOAD_PASSWORD).
- Added a CHANGELOG [Unreleased] Fixed entry documenting the rename and the
RELOAD_PASSWORD -> WHEELS_RELOAD_PASSWORD .env migration path.
Not changed (intentional): the docker ENV `change-me` default is a
documented placeholder and the compose `${WHEELS_RELOAD_PASSWORD:?required}`
form is authoritative (non-blocking nit); the tweet example already uses
`set(reloadPassword="")` (no secret); the root demo app + tools/build/base
keep their known dev passwords (test/build infrastructure).
Signed-off-by: Peter Amiri <peter@alurium.com>
|
Thanks @wheels-bot — all four addressed in a7b5cd7:
On the non-blocking nits: left the docker |
Wheels Bot — Address Review (round 1)Applied consensus findings from Reviewer A and Reviewer B's convergence (round 1):
Skipped (not part of the consensus):
Tests: not re-run for this round. The branch is not The new commit will trigger a fresh Reviewer A run on the updated SHA. Convergence loop continues until reviewers align on |
There was a problem hiding this comment.
Wheels Bot — Reviewer A
TL;DR: The author has addressed every finding from the previous Reviewer A pass - starter-app literal, check.cfc sample .env, test message nit, and CHANGELOG entry are all fixed. The core security change (removing committed reload secrets from scaffold templates) is correct, the backwards-compat regex is sound, and the docs sweep is thorough. One correctness regression was introduced: tools/test-onboarding.sh uses grep -E '^RELOAD_PASSWORD=' (anchored, no WHEELS_ prefix) in six places to read the reload password from the scaffold-generated .env, but the scaffold _env template now emits WHEELS_RELOAD_PASSWORD= - so every reload the onboarding harness attempts falls back to "wheels" and fails. Requesting changes on that one file.
Correctness
tools/test-onboarding.sh - six anchored greps will silently miss the renamed key
The scaffold _env template now writes:
WHEELS_RELOAD_PASSWORD={{reloadPassword}}
But tools/test-onboarding.sh reads the password at all six reload points with:
local_password=$(grep -E '^RELOAD_PASSWORD=' "$APP_DIR/.env" 2>/dev/null | cut -d= -f2 || echo "wheels")
(lines 407, 487, 563, 665, 805, 1049)
The ^ anchor means ^RELOAD_PASSWORD= does not match a line that starts with WHEELS_RELOAD_PASSWORD=. The grep returns empty, the shell takes the || echo "wheels" branch, and every subsequent curl "/?reload=true&password=wheels" call is sent against an app whose actual reload password is a random 32-hex-char string. All harness phases that touch the reload endpoint fail.
Note: tools/test-cli-e2e.sh:505 uses the unanchored grep RELOAD_PASSWORD (no -E, no ^) which correctly matches WHEELS_RELOAD_PASSWORD=… as a substring - that file is unaffected.
Fix for all six lines in tools/test-onboarding.sh:
local_password=$(grep -E '^(WHEELS_)?RELOAD_PASSWORD=' "$APP_DIR/.env" 2>/dev/null | cut -d= -f2 || echo "wheels")
Tests
cli/tests/specs/e2e/ServerCommandsTest.cfc:919 - test setup fixture uses legacy key (non-blocking)
The createTestServer() helper at line 919 writes RELOAD_PASSWORD=testapp to its fixture .env. The backwards-compat regex (?:WHEELS_)?RELOAD_PASSWORD\s*=\s*(.+) in detectReloadPasswordFromEnv still matches this, so no test breaks today. Worth updating to WHEELS_RELOAD_PASSWORD=testapp in a follow-up for consistency, but not blocking.
cli/tests/specs/commands/ConfigCommandsTest.cfc:190 - multi-env fixture uses old key (non-blocking)
The createTestEnvironment() helper writes RELOAD_PASSWORD=... to .env.$environment (not .env), so it is isolated from the main reload-password flow. Still worth a pass once the test-onboarding.sh fix lands.
Minor nit (no change required)
The six tools/test-onboarding.sh lines are the only blocking item. Everything else in the PR - scaffold templates, starter-app, check.cfc, docs, consoleeval.cfm, backwards-compat CLI regex, and CHANGELOG - is correct and complete.
|
|
Wheels Bot — Reviewer B (round 1)A's review is accurate and well-grounded on the new SHA. The single blocking finding — six anchored greps in SycophancyNone detected. A requested changes with a specific file, six concrete line numbers, and a ready-to-apply fix. False positivesNone detected.
Missed issuesNone that rise to blocking. A's two non-blocking notes ( Verdict alignmentA's ConvergenceAligned. A's single blocking finding is confirmed accurate, no false positives to retract, and |
…d grep missed renamed key) The reload-password rename (RELOAD_PASSWORD -> WHEELS_RELOAD_PASSWORD in the scaffold _env template) broke tools/test-onboarding.sh, which read the password at six reload points with an anchored `grep -E '^RELOAD_PASSWORD='`. The `^` anchor does not match a line starting with `WHEELS_RELOAD_PASSWORD=`, so the grep returned empty, the shell fell back to `|| echo "wheels"`, and every `?reload=true&password=wheels` call ran against an app whose real password is the random 32-hex value from .env — failing every harness phase that touches the reload endpoint. Anchored pattern is now `^(WHEELS_)?RELOAD_PASSWORD=`, matching both the new prefixed key and the legacy unprefixed one. tools/test-cli-e2e.sh is unaffected (it uses an unanchored `grep RELOAD_PASSWORD` that matches the prefixed key as a substring). Caught by Reviewer A round 2 on #2857. Signed-off-by: Peter Amiri <peter@alurium.com>
Wheels Bot — Address Review (round 2)Applied consensus findings from Reviewer A and Reviewer B's convergence on
Skipped (not part of the consensus):
Tests: not re-run for this round. The branch is The new commit will trigger a fresh Reviewer A run on the updated SHA. Convergence loop continues until reviewers align on |
This one is probably fine since we are seeding the admin password for the Lucee server but it should probably then be .gitignored. Let me analyze this one and the ramifications. |
|
Fixed the blocking item in 78d4a94, @wheels-bot — genuinely good catch, this was a regression I introduced by renaming the key without sweeping all consumers.
I also did the full-repo sweep I should have run when renaming:
On the two non-blocking test fixtures ( |
There was a problem hiding this comment.
Wheels Bot — Reviewer A
TL;DR: All blocking findings from both previous review rounds are resolved. The core security fix is complete and correct — scaffold templates no longer commit a reload-password literal to version control, the backwards-compat detection regex is sound, the starter-app guessable literal is gone, check.cfc generates the renamed key, the CHANGELOG is updated, and all six tools/test-onboarding.sh anchored-grep lines now handle the renamed key. Two minor test-fixture nits remain (flagged as non-blocking in round 2) but are genuine follow-up candidates. Approving.
Tests
cli/tests/specs/e2e/ServerCommandsTest.cfc:919 — createTestServer() fixture still writes legacy key (non-blocking)
"APP_NAME=testapp" & nl & "PORT=8080" & nl & "RELOAD_PASSWORD=testapp" & nl
The backwards-compat regex (?:WHEELS_)?RELOAD_PASSWORD\s*=\s*(.+) continues to match this, so no test breaks. Worth updating to WHEELS_RELOAD_PASSWORD=testapp in a follow-up for consistency, but not blocking.
cli/tests/specs/commands/ConfigCommandsTest.cfc:190 — createTestEnvironment() writes to .env.$environment, not .env (non-blocking)
The fixture writes RELOAD_PASSWORD= to .env.production / .env.development, which is isolated from the main reload-password detection path. Same follow-up opportunity as above.
Minor observation (no change required)
tools/test-cli-e2e.sh:429 and :505 use unanchored grep RELOAD_PASSWORD — these already correctly match WHEELS_RELOAD_PASSWORD= as a substring and were not touched by this PR. No action needed.
Wheels Bot — Reviewer B (round 1)A's review is accurate and well-grounded on the current SHA. Every approval claim traces to actual diff content I independently verified. No sycophancy, no false positives, no missed issues. Aligned on approve. SycophancyNone detected. A's approval cites specific file paths, line numbers, and diff evidence for every resolved blocking item. The two non-blocking nits are correctly categorised rather than glossed over. False positivesNone detected.
Missed issuesNone detected. The diff covers all surfaces called out in prior rounds (scaffold template, Verdict alignmentA's ConvergenceAligned. All blocking findings from rounds 1 and 2 are confirmed resolved, no false positives to dispute, and the approve verdict is correct. The PR is review-clean for this SHA. |
teleotic noted on #2857 that `wheels new` also embedded the same generated random value into `lucee.json` as the Lucee Server Admin password — a second committed-secret path independent of config/settings.cfm, and arguably higher impact (the admin gates the Lucee Server Admin UI). The scaffold `lucee.json` admin password now reads `#env:WHEELS_RELOAD_PASSWORD#` instead of the literal. LuCLI resolves `#env:VAR#` against the project `.env` (combined with system environment) when it loads lucee.json at server start (LuceeServerConfig.substituteField applies to admin.password, which is not a protected zone; envFile defaults to `.env`). So the random value lives only in the git-ignored `.env`, the committed `lucee.json` carries no secret, and its shareable config (ports, mappings, datasources) stays version-controlled — no fresh-clone breakage that git-ignoring the whole file would cause. Reuses the existing WHEELS_RELOAD_PASSWORD value (the scaffold already used one value for both the reload password and the Lucee admin password); this just stops committing it. Decoupling into a distinct WHEELS_LUCEE_ADMIN_PASSWORD would be a separate enhancement. Reported by @teleotic on #2857. Signed-off-by: Peter Amiri <peter@alurium.com>
|
Analyzed — good catch, @teleotic, and you're right that it's the same class of leak (and arguably higher-impact, since the Lucee Server Admin password gates the admin UI). Confirmed Rather than git-ignore the whole "admin": { "enabled": true, "password": "#env:WHEELS_RELOAD_PASSWORD#" }The random value now lives only in the git-ignored |
There was a problem hiding this comment.
Wheels Bot -- Reviewer A
TL;DR: The previous round-3 approval was dismissed to pick up this 4th commit, which extends the secret-elimination work to lucee.json -- the Lucee Server Admin password was being committed as a literal alongside the CFML reload password. The fix is directionally correct, and the commit message is unusually detailed about the LuCLI mechanism. One open question about fallback behavior when WHEELS_RELOAD_PASSWORD is absent, and no dedicated test for the new interpolation path. Posting a comment (not requesting changes) because neither concern is blocking, but both are worth the author confirming before merge.
Correctness
cli/lucli/templates/app/lucee.json -- unverified fallback when WHEELS_RELOAD_PASSWORD is unset
The commit message asserts that LuCLI's LuceeServerConfig.substituteField resolves #env:VAR# in admin.password at server start from the project .env. That claim is plausible and the author cites a specific internal class -- but the diff alone does not confirm what happens when WHEELS_RELOAD_PASSWORD is absent from both .env and the process environment (e.g., a bare clone before wheels new, or a CI pipeline that does not inject the var).
Two possible outcomes:
- LuCLI substitutes an empty string: Lucee starts with no admin password (undesirable, but not an open credential)
- LuCLI leaves the literal
#env:WHEELS_RELOAD_PASSWORD#as the password: admin UI is gated behind the interpolation string itself, which is surprising but at least not guessable
For the wheels new happy path this is a non-issue: the generated .env always contains WHEELS_RELOAD_PASSWORD=<random>. The risk is narrow (bare-clone or template-copy without scaffolding), but it is worth a sentence in the PR body or a comment in lucee.json clarifying the expected fallback behavior.
Tests
No test covers the #env:WHEELS_RELOAD_PASSWORD# substitution in lucee.json
The existing e2e spec at cli/tests/specs/e2e/ServerCommandsTest.cfc verifies detectReloadPassword() and password detection from .env / settings.cfm. There is no test that scaffolds an app (or uses a fixture lucee.json containing the interpolation syntax), starts the server, and confirms the Lucee admin panel is accessible with the value from .env.
This is a higher-effort integration gap and acceptable to defer. The onboarding harness (tools/test-onboarding.sh) would naturally exercise this end-to-end, so it may already be covered implicitly -- worth noting in the PR body if so.
Minor observations (no change required)
cli/tests/specs/e2e/ServerCommandsTest.cfc:919 and cli/tests/specs/commands/ConfigCommandsTest.cfc:190 still write the legacy RELOAD_PASSWORD key in test fixtures. These were flagged as non-blocking in round 2 -- the backwards-compat regex covers them. Worth a follow-up pass once this lands.
The docker-deployment.mdx Dockerfile example still has WHEELS_RELOAD_PASSWORD=change-me baked into the ENV directive. The compose file's required-substitution form is the authoritative pattern; the Dockerfile default is a documented placeholder, not introduced by this PR.
Everything from the previous three review rounds is addressed. The core security fix -- no committed reload-password literal in scaffold templates, starter-app, or (now) lucee.json -- is complete. The CHANGELOG entry, backwards-compat detection regex, onboarding harness fix, and guide sweep are all correct. The two open items above are non-blocking.
|
Thanks @wheels-bot — both non-blocking points confirmed, no code change needed. Fallback when replacement = getEnvVar(varExpression);
if (replacement == null) {
replacement = matcher.group(0); // keep the literal "#env:WHEELS_RELOAD_PASSWORD#"
}
Test coverage: yes, exercised implicitly by The two test-fixture nits ( |
Wheels Bot — Reviewer B (round 1)A's review is accurate and cites real concerns, but both are explicitly characterised as non-blocking. The template processing mechanism is safe (verified from source), and the author's prior PR comment already supplies the mechanism detail A marks as unverified from the diff. The COMMENT verdict is defensible but too conservative; B would approve. Not aligned on verdict — flagging for A to reconsider. SycophancyNone detected. A deliberately chose COMMENTED rather than approve, and every observation is backed by a specific file reference or diff assertion. False positivesNone detected.
Missed issuesNone that rise to blocking.
Verdict alignmentA's COMMENTED verdict is internally consistent — choosing COMMENTED over approve when you have open questions you want the author to confirm before merge is a reasonable stance. The inconsistency is that A labels both concerns "non-blocking" in the same comment. If neither concern blocks merge, COMMENTED (which holds the loop in a non-converged state) is harder to justify than approve. ConvergenceNot aligned. A said COMMENTED; B would approve. A's own characterisation that "neither concern is blocking" is the crux: non-blocking observations are documentation improvements, not merge gates. The author (who maintains LuCLI) explained the mechanism in their PR comment before this review ran, and the template-processing path is verified safe. A should reconsider whether COMMENTED is warranted when the two open items are (a) a PR-body note about a corner case the normal flow cannot hit, and (b) an acknowledged higher-effort test gap that A itself calls "acceptable to defer." If A agrees both are below the approve threshold, this PR is review-clean. |
There was a problem hiding this comment.
Wheels Bot — Reviewer A response (round 1)
B is correct on the verdict-alignment point. Both items I raised in my round-4 review were explicitly labelled "non-blocking" and "acceptable to defer" in my own text — choosing COMMENT rather than approve was internally inconsistent. B's verification and the author's inline PR comment have also resolved the factual uncertainty I cited as my reason for not approving outright. Conceding the verdict and updating to approve.
Conceded points
- COMMENT verdict was inconsistent with my own finding characterisation. I marked both the fallback-behavior question and the test-coverage gap as "non-blocking" in the same review body, then posted COMMENT. B correctly identifies the contradiction: if neither concern gates merge, COMMENT holds the loop in a non-converged state without justification.
- Fallback behavior was already documented, not an open question. bpamiri's PR comment (posted before my review ran) explained the exact
LuceeServerConfig.replaceLucliVars()no-default branch: the literal#env:WHEELS_RELOAD_PASSWORD#is kept as-is when the env var is absent — not empty string. That was the information I said the diff did not confirm, and it was already present in the thread. - Template-processing risk is resolved. B's proactive verification of
copyTemplateDir()→processPlaceholders()→replace()/fileWrite()confirms CFML does not evaluate#-delimited expressions in strings held in variables or written viafileWrite. The interpolation string is written verbatim to the generatedlucee.json.
Defended points
None. Both concerns in my COMMENT-state review were accurately characterised as non-blocking by me at the time. B's evidence closes the remaining factual uncertainty and demonstrates the verdict should have been approve.
Updated verdict
Approve. Every blocking finding across four rounds is resolved, both non-blocking nits are explicitly acknowledged as follow-up candidates, and the two concerns behind my COMMENT verdict are resolved — one by B's independent verification and one by the author's already-present PR-body explanation. This PR is review-clean.
Replace the remaining `application.wo.env(...)` calls in four v4-0-0
guides with the bare `env(...)` form, in both `{test:compile}` code
fences and inline-code prose. Bare `env()` is the same public function
(vendor/wheels/Global.cfc:597) and is already the canonical, shipping
pattern in the migration guide (upgrading/3x-to-4x.mdx) under
{test:compile}.
Accessor-style only — no env-var names changed (DB_USER, SMTP_PASSWORD,
JWT_SECRET, POSTMARK_TOKEN, WHEELS_DATASOURCE, etc. all preserved).
Deferred follow-up from #2857, which converted the scaffold plus the
deployment/ and core-concepts env() guides; these four files were the
remainder it did not touch.
Files:
- basics/database-and-multiple-datasources.mdx
- digging-deeper/sending-email.mdx
- digging-deeper/authentication-patterns.mdx
- command-line-tools/wheels-commands/testing.mdx
verify:docs (4 touched files): 35 passed, 0 failed.
Signed-off-by: Peter Amiri <peter@alurium.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…-argument Resolve CHANGELOG.md [Unreleased]/Fixed conflict by keeping both the --no-* flag fix entry (#2855) and develop's #2835/#2840/#2857 entries. cli/lucli/Module.cfc auto-merged cleanly — the PR's argsFromCollection one-liner sits in a different region than develop's CLI changes. Signed-off-by: Peter Amiri <peter@alurium.com>
Why
Discussion #2854 reported that
wheels newhard-codes the generated reload password as a literal inconfig/settings.cfm— a committed file — and echoes it again in a comment, so the secret lands in version control. The docs also disagreed on the variable name and accessor, so copy-pasting a documented snippet into a fresh app could resolve to""and trip the "reloadPassword is empty" warning.Tracing the code confirmed:
config/settings.cfmset(), asettings.cfmcomment, and.env..envusedRELOAD_PASSWORDwhile the deploy templates + upgrade guide usedWHEELS_RELOAD_PASSWORD.application.wo.env(...); the migration guide called bareenv()the "canonical pattern";production-config.mdxcalledapplication.wo.env(...)"the canonical accessor." Genuinely split.What changed
Canonical form going forward:
set(reloadPassword = env("WHEELS_RELOAD_PASSWORD", "")), with the secret living only in the git-ignored.env.Scaffold (
cli/lucli/templates/app/)config/settings.cfm+ bothapp/snippets/*.txtnow read the password viaenv("WHEELS_RELOAD_PASSWORD", ""). The generator's random value is written only to.env; the literal is gone from the committed file and its comment._envnow emitsWHEELS_RELOAD_PASSWORD— consistent withWHEELS_ENV/WHEELS_DATASOURCEand thewheels deploysecrets contract, so the sameenv()lookup works unchanged from a laptop.envto a production deploy.CLI / console
detectReloadPassword()(and its e2e mirror) now match(?:WHEELS_)?RELOAD_PASSWORD, so apps generated before the rename keep working. Added a spec assertingWHEELS_RELOAD_PASSWORDdetection.wheels reloadhint strings now nameWHEELS_RELOAD_PASSWORD.Docs — standardized on bare
env()+WHEELS_RELOAD_PASSWORDcore-concepts/environments-and-configurationand all 5 deployment guides (production-config,security-hardening,docker-deployment,vm-deployment,dev-server).Server.System.getEnv("RELOAD_PASSWORD") ?: "changeme"(wrong accessor that bypasses.env, unprefixed name, guessable fallback) — withenv("WHEELS_RELOAD_PASSWORD", "").security-hardeningnote that claimed the template hard-codes{{reloadPassword}}, plus a staleGlobal.cfcline reference.Scope note
The audit found the drift was wider than the two docs cited in the discussion — hence the 5 deploy-guide edits. Deferred to a follow-up: 13
application.wo.envusages remain in 4 non-deploy guides (database-and-multiple-datasources,sending-email,authentication-patterns,testing) for a separate accessor sweep.application.wo.env→env()in the 4 non-deploy guidesVerification
(?:WHEELS_)?RELOAD_PASSWORD\s*=\s*(.+)matches bothWHEELS_RELOAD_PASSWORD=and legacyRELOAD_PASSWORD=and captures the value.reloadPassword="..."literal remains.{test:compile}doc snippets use the bare<cfscript> set(…=env(…))form already proven to compile in the migration guide.deploy-ci.yml; the docs harness needsnpm ci+ a CFML backend; the onboarding harness can't reliably test worktree templates locally due to the brew-wrapperLUCLI_HOMEgotcha). Not claimed as locally green.🤖 Generated with Claude Code