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
Since the fail-closed reload gate shipped in 4.0.4 (#3062, PR #3140), hitting ?reload=true while URL reload is disabled — most commonly set(reloadPassword="") — is a complete silent no-op in the browser. The request serves normally with zero indication that a reload was requested and refused.
The only feedback channels today are:
the boot-time warning in wheels_security.log (vendor/wheels/events/onapplicationstart.cfc:368-371)
the per-attempt accept/reject lines in the same log
the debug-footer warning when reloadPassword is empty (vendor/wheels/events/onrequestend/debug.cfm:146)
None of these are in the developer's face at the moment they hit ?reload=true and nothing happens. Real-world impact: this has now produced at least two support reports shaped as "reload is broken, I have to restart the server" from long-time users whose v1–v3 muscle memory is ?reload=true with an empty password. The most recent (2026-07-14, community Slack): app configured with set(reloadPassword=""), user concluded reload no longer works in v4.
The fail-closed behavior itself is correct and should not change. The gap is purely discoverability at request time.
Proposal
In the development environment only, when a request arrives with url.reload present but the reload gate refuses to fire, surface an inline, browser-visible notice explaining why and what to do.
Cases to cover (all dev-only):
reloadPassword is empty — the headline case. Message along the lines of:
password parameter missing while a password is configured — "add &password=...".
Wrong password / rate-limited — a generic "reload refused; see wheels_security.log" notice. Must NOT distinguish wrong-password from rate-limited and must not echo anything about the configured password (keep the constant-time/no-oracle properties of the gate; in dev this is low-stakes, but parity is cheap).
Non-development environments: behavior unchanged — silent no-op + security log, exactly as today. No new response-surface in testing/production/maintenance.
Implementation sketch (open to alternatives)
The gate lives in app-template code (public/Application.cfc, 4 copies, pinned by the structural parity spec), but the notice logic should live in the framework so it's maintained in one place. Options, roughly in order of preference:
Debug-bar notice: the gate sets a request.wheels.reloadRefusedReason flag; events/onrequestend/debug.cfm renders a prominent banner when present. Zero new output path — rides the existing debug footer, which is already dev-only by default.
Flash-style banner injected into the rendered page independent of the debug bar (covers apps that turn showDebugInformation off in dev).
A dev-only Wheels.ReloadDisabled friendly error page instead of serving the request. Most visible, but interrupts the request and may be too aggressive for case 3.
Whichever shape: template public/Application.cfc copies only need to record the refusal reason; rendering stays framework-side. The parity spec should pin the new flag-setting lines across all four copies.
Acceptance criteria
Dev environment + ?reload=true + empty reloadPassword → browser-visible notice naming the setting and the fix; app serves the page normally otherwise
Dev environment + configured password + missing password param → notice saying the param is required
Dev environment + wrong password or rate-limited → single generic refusal notice (no oracle)
testing/production/maintenance → no behavioral change, no new output
All four public/Application.cfc copies updated; structural parity spec extended
Guides touched: upgrading/3x-to-4x troubleshooting entry updated to mention the new notice
Problem
Since the fail-closed reload gate shipped in 4.0.4 (#3062, PR #3140), hitting
?reload=truewhile URL reload is disabled — most commonlyset(reloadPassword="")— is a complete silent no-op in the browser. The request serves normally with zero indication that a reload was requested and refused.The only feedback channels today are:
wheels_security.log(vendor/wheels/events/onapplicationstart.cfc:368-371)reloadPasswordis empty (vendor/wheels/events/onrequestend/debug.cfm:146)None of these are in the developer's face at the moment they hit
?reload=trueand nothing happens. Real-world impact: this has now produced at least two support reports shaped as "reload is broken, I have to restart the server" from long-time users whose v1–v3 muscle memory is?reload=truewith an empty password. The most recent (2026-07-14, community Slack): app configured withset(reloadPassword=""), user concluded reload no longer works in v4.The fail-closed behavior itself is correct and should not change. The gap is purely discoverability at request time.
Proposal
In the development environment only, when a request arrives with
url.reloadpresent but the reload gate refuses to fire, surface an inline, browser-visible notice explaining why and what to do.Cases to cover (all dev-only):
reloadPasswordis empty — the headline case. Message along the lines of:passwordparameter missing while a password is configured — "add&password=...".wheels_security.log" notice. Must NOT distinguish wrong-password from rate-limited and must not echo anything about the configured password (keep the constant-time/no-oracle properties of the gate; in dev this is low-stakes, but parity is cheap).Non-development environments: behavior unchanged — silent no-op + security log, exactly as today. No new response-surface in testing/production/maintenance.
Implementation sketch (open to alternatives)
The gate lives in app-template code (
public/Application.cfc, 4 copies, pinned by the structural parity spec), but the notice logic should live in the framework so it's maintained in one place. Options, roughly in order of preference:request.wheels.reloadRefusedReasonflag;events/onrequestend/debug.cfmrenders a prominent banner when present. Zero new output path — rides the existing debug footer, which is already dev-only by default.showDebugInformationoff in dev).Wheels.ReloadDisabledfriendly error page instead of serving the request. Most visible, but interrupts the request and may be too aggressive for case 3.Whichever shape: template
public/Application.cfccopies only need to record the refusal reason; rendering stays framework-side. The parity spec should pin the new flag-setting lines across all four copies.Acceptance criteria
?reload=true+ emptyreloadPassword→ browser-visible notice naming the setting and the fix; app serves the page normally otherwisepasswordparam → notice saying the param is requiredpublic/Application.cfccopies updated; structural parity spec extendedReferences
?reload=trueopen to anonymous restarts, warm-app wrong-password attempts are never logged or rate-limited, and the boot warning misstates behavior #3062 — fail-closed reload gate (shipped 4.0.4, PR fix(public): empty reloadPassword disables URL reload; log and rate-limit warm-path attempts #3140)public/Application.cfc:277-347— warm-path reload gatevendor/wheels/events/onapplicationstart.cfc:368-371— boot warningvendor/wheels/events/onrequestend/debug.cfm:146— existing empty-password debug-footer warning