The browser-trust fence verifies Origin == Host after the loopback pin;
with Host presented as loopback the browser's public Origin can never
match (403 on every /api POST). Strip the Origin header in the proxy so
the fence falls back to its Sec-Fetch-Site cross-site check, while
panel-auth's cookie auth remains the real gate.
DSH pins privileged /api methods (settings.*, credentials.*, agentPreset.*,
host.pickDirectory, ...) to loopback hosts by design. When a reverse proxy
fronts the panel with the public Host header, those methods return 403.
The supported deployment shape is forwarding Host as loopback upstream
(header_up Host 127.0.0.1 in Caddy). This change:
- skips the login origin check when the incoming Host is loopback (proxy
context), while still rejecting real cross-site posts on public hosts;
- documents the reverse-proxy requirement in the README;
- extends tests with raw-request coverage for non-loopback cross-site
rejection, port tolerance, and loopback-Host skip.
- Failures counted per client IP (X-Forwarded-For last hop behind the
proxy); after maxFailures within the window the IP is locked out,
doubling per repeat up to lockoutMaxSeconds.
- Locked IPs get 429 + Retry-After (login page / JSON for API), and the
scrypt verification is skipped entirely while locked (no CPU burn).
- Fixed failedLoginDelayMs delay on every bad credential attempt.
- Basic-auth path counts and clears identically; success resets the IP.
- All thresholds configurable; in-memory state only.
- Tests: lockout, expiry restore, basic-path counting, XFF last-hop key.
- Browser navigation now gets a self-contained login page (no JS, CSP
hardened, XSS-escaped, cross-origin POST rejected) instead of the native
Basic dialog; API clients keep 401 + WWW-Authenticate.
- Login/logout endpoints (/panel-auth/login, /panel-auth/logout) issue and
revoke the signed cookie, then 303 back to the original target.
- Structured audit log (login-ok/login-fail/logout/challenge/reject with
username, IP, UA, reason), default $DSH_HOME/panel-auth-audit.jsonl,
5MB rotation; X-Forwarded-For honored behind the reverse proxy.
- Function-plugin form, per-request config, fail-open when unconfigured.
- Tests extended to 15 flows including login page, login POST, logout,
audit assertions and XSS escaping.