Skip to content

Dev-mode inline notice when ?reload=true is refused (empty reloadPassword / missing or wrong password) #3311

Description

@bpamiri

Problem

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):

  1. reloadPassword is empty — the headline case. Message along the lines of:

    Reload requested but URL-based reload is disabled because reloadPassword is empty. Set set(reloadPassword=env("WHEELS_RELOAD_PASSWORD", "")) in config/settings.cfm and pass &password=.... (Fail-closed since 4.0.4, Reload-password contract drift: empty password leaves ?reload=true open to anonymous restarts, warm-app wrong-password attempts are never logged or rate-limited, and the boot warning misstates behavior #3062.)

  2. password parameter missing while a password is configured — "add &password=...".
  3. 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

References

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions