- Remove tests for createSuccessResponse and ErrorMessages which were
removed/made private during the type system refactoring (15 failures)
- Fix RalphConfigSchema to accept 'full' string for reset field,
matching the route handler's fullReset() code path
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update scorecard with before/after scores, mark phases 3-7 as
complete with verified line counts and file inventories. Fix 3
CLAUDE.md inaccuracies: static cache 1h→1y, route count ~160→~113,
remove stale src/tui reference.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Route modules (13→12), type domain files (14→13), SSE events (~80→~100),
total route handlers (~110→~160), and all per-group API route counts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds ephemeral single-use QR tokens for passwordless tunnel login.
Scanning the QR auto-authenticates; bare tunnel URL requires Basic Auth.
Backend:
- TunnelManager: 60s token rotation, 90s grace, rejection-sampled 6-char
base62 short codes, Map-based O(1) lookup, SVG caching, global rate limit
- Auth middleware: /q/ bypass, separate qrAuthFailures counter, enhanced
AuthSessionRecord with device context (ip, ua, createdAt, method)
- Routes: GET /q/:code (consume + cookie + redirect), POST /api/tunnel/qr/
regenerate, POST /api/auth/revoke, updated GET /api/tunnel/qr with cache
- SSE: tunnel:qrRotated, tunnel:qrRegenerated, tunnel:qrAuthUsed events
- Audit: qr_auth lifecycle log entries
Frontend:
- Auto-refresh QR via inline SVG in SSE (fallback fetch if absent)
- 60s countdown indicator on QR badge
- Regenerate QR button
- QRLjacking detection toast with [Revoke All] action button (10s duration)
- showToast enhanced with optional duration and action button support
Fixes:
- /api/logout now invalidates server-side session token (was only clearing
browser cookie, leaving token valid for replay)
Tests: 20 new tests in test/qr-auth.test.ts covering token lifecycle,
bias check, rate limiting, SVG caching, and full server integration.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Root cause: toggleTunnelFromWelcome() sent the entire settings object
back to PUT /api/settings, but the Zod schema uses .strict() which
rejects unknown fields (lastUsedCase, localEchoEnabled, etc.). The PUT
silently failed, so the tunnel never started.
Fix: send only {tunnelEnabled: true/false} instead of the full blob.
Also added polling fallback for tunnel status and server-side re-broadcast
when tunnel is already running.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Re-broadcast tunnel:started SSE event when tunnel is already active
and user toggles the setting, so the client receives the URL.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* add project setup
* add tools to eslint
* remove contributing.md
* update claude md
* chore: simplify CI to single Node.js version (22)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* use node 20
* adjust formatting
* fix linting
* format
* fix layout
* fix: align CI node version to .nvmrc (22), remove redundant gotcha
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: arkon <arkon.85@hotmail.com>