Add sourcemap: 'both' to esbuild.transform — emits inline + external .map
files for production debugging. Stack traces now point to real source lines.
DC-090: Already resolved — Dockerfile pins node:20.11.1-alpine3.19 (specific).
New module status/js/auth-gate.js owns the Caddy ?auth=required flow.
On load it queries GET /api/v1/auth/login/methods to discover which
AuthProviders are configured. Three branches:
* 0 providers → legacy TOTP overlay (delegates to window._showTotpOverlay)
* 1 provider (totp only) → legacy TOTP overlay (delegates, no UI change)
* 2+ providers → provider selector with 'Sign in with …' buttons
Email provider challenge is a single email input + 'Send sign-in link'
button. POST to /api/v1/auth/login/email/initiate. On success the UI
shows 'check the server logs' message if deliveredVia == 'dev-console'
(production hosts without SMTP fall back gracefully) or 'check your
inbox' when SMTP is configured.
TOTP button just calls window.location.reload() — simplest path because
totp-auth.js wires the 6-digit input handlers at module-load time, and
a reload re-runs all IIFEs with the original markup. Same behavior as
the legacy single-provider path.
Coordination with totp-auth.js: auth-gate.js sets window.__dc_049_handled
= true at IIFE entry. totp-auth.js's top-level ?auth=required check
reads that flag and skips its own UI when set — eliminates the flicker
in multi-provider installs. Single-provider installs still work because
the legacy code path is unchanged (auth-gate delegates to it).
Bundle order in build.js: auth-gate.js BEFORE totp-auth.js so the flag
is set in time.
Webpack-style bundle markers verified offline: __dc_049_handled,
auth-gate-email-input, provider-btn, _showAuthGate, totp_redirect all
present in dist/core.js (now 20 files, 248KB raw / 153KB min). New SW
cache hash dashcaddy-shell-680e230383 (was 743f9c17b0).
Introduces a unified security event store and HTTP API that ingests events
from any of the configured sources (API audit, Caddy access log, fail2ban,
shared_bans, future remote agents) and surfaces them in the dashboard.
New files:
src/security/event-store.js JSONL-backed store + in-memory query index
src/security/host-registry.js Registered hosts with per-host API keys
src/security/event-workers.js Tail-followers for Caddy/fail2ban/shared_bans logs
routes/security.js Events, hosts, ingest, SSE stream endpoints
status/js/security-center.js Dashboard modal with Overview/Events/Hosts tabs
SECURITY-FEATURE.md Full feature documentation
DEAD-CODE.md, DUP-CODE.md, HARDENING.md Prior audits
Modified:
src/app.js Mount /api/v1/security/*
src/utilities/middleware.js Add ingest endpoints to PUBLIC_ROUTES
src/security/audit-logger.js Mirror audit events into security store
server.js Start security workers on boot
status/build.js Bundle security-center.js
status/index.html Add Security button to nav
- status/js/totp-recovery.js: NEW. Wires up recovery panel on the TOTP
gate. Pastes Base32 -> /api/v1/totp/setup -> /verify-setup -> session.
Exposes window._refreshRecoveryLink() called by totp-auth.js.
- status/js/totp-auth.js: showTotpOverlay() now calls
_refreshRecoveryLink() so the recovery link hides when TOTP is healthy
and appears when it's broken.
- status/js/totp-settings.js: removed setupSection.style.display='none'
so 'Import existing secret' is always visible; added 'Download backup
file' button after setup that exports the Base32 + recovery
instructions as JSON.
- status/index.html: added 'Lost access? Recover with saved Base32
key ->' link to the TOTP overlay plus the recovery panel itself;
added title tooltip to the auth card reminding users to save the
Base32 on first setup.
- status/build.js: include JS('totp-recovery.js') in the core bundle
after totp-auth.js (since recovery registers a hook auth calls).
Three logical changes grouped:
1. Widget bundle rebuild + sami-files logo (from previous session)
- status/dist/{init,core,features,onboarding}.js rebuilt from latest source
- status/sw.js cache bumped to dashcaddy-shell-594ec75648 to force SW refresh
- status/assets/sami-files.png added (Sami Files service card logo)
2. status/build.js: include monitoring-widgets.js in bundle
- The original build.js was missing monitoring-widgets.js from its JS()
bundle list — that's why the System Overview widget never showed up
in the live init.js until we ran the live /var/www/dashcaddy-status/
build.js. Now consistent.
3. dashcaddy-api/scripts/dashcaddy-update.sh restart_container(): preserve
TOTP secret across container recreates
- Was only setting SERVICES_FILE; container fell back to image-local
/app/credentials.json + /app/.encryption-key (auto-generated fresh
every recreate), which broke TOTP for the bind-mounted secret at
/app/data/credentials.json
- Added CREDENTIALS_FILE + ENCRYPTION_KEY_FILE env vars pointing at
/app/data/ so the container reads from the bind-mounted host data dir
- See skill: software-development/dashcaddy/references/totp-and-system-overview-pitfalls.md §9
4. Auto-updater integration (pulled from upstream release):
- dashcaddy-api/VERSION: dev → c64bbe2
- dashcaddy-api/health-checker.js, middleware.js, package.json,
routes/backups.js, src/app.js: new release code (bundled workflows,
/api/auth/ → /api/v1/ back-compat rewrite, backup storage limits)
When the build runs on a Windows checkout, fs.readFileSync returns the
file with CRLF intact, and the hash of the inline bootstrap script's
body reflects those CRLFs. The release tarball / git transport / Linux
file system strip CRLF on the publishing host, so the browser sees the
LF-only version and computes a different sha256. CSP then blocks the
script — disabling the version widget, theme switcher, and any other
DOM bindings set up in that inline block.
Normalize CRLF -> LF before computing the hash (the on-disk file keeps
its native line endings; only the hash input is normalized). The CSP
allowlist now matches whatever Caddy actually serves.
Three merge-fallout bugs that combined to leave the services grid empty
and most UI inert:
1. error-handler.js was bundled into onboarding.js (loaded 3rd), but
globals.js in core.js (loaded 1st) does `const errorHandler = new
ErrorHandler()` at top level. ErrorHandler was undefined when core.js
ran -> ReferenceError -> globals.js stopped, so window.APPS,
_showTotpOverlay, loadServices, etc. were never set, and init.js
blew up on every call into core's exports.
Moved error-handler.js to the start of the core.js bundle so the
class is on window before any other script touches it.
2. setup-wizard.js also declared `const errorHandler = new ErrorHandler()`
at top level. Classic scripts share the document's top-level lexical
environment, so this collided with globals.js's declaration ->
redeclaration SyntaxError in features.js. Removed setup-wizard.js's
copy; it picks up the global one.
3. tooltip-definitions.js closed its `(function(window){...})(window);`
IIFE at line ~171 ("Validation module loaded"), then the TOOLTIP_
DEFINITIONS array, getter helpers, window.TooltipDefinitions export,
and final `debug(...)` log all sat at top level — outside the IIFE,
where `debug` was no longer in scope. Removed the early close and
added one at EOF so the whole file is in one IIFE.
The service worker uses staleWhileRevalidate on /dist/*, so after a
release it would serve old bundles from cache indefinitely (cache is
only wiped when the cache *name* changes, which was hardcoded to
'dashcaddy-shell-v10'). Result: dashboard appears unchanged after a
self-update until the user manually unregisters the SW.
build.js now hashes the concatenated dist bundles and writes
`dashcaddy-shell-<10-hex-chars>` into sw.js. Any change in dist/
produces a fresh cache name; on the next page load the SW's activate
handler deletes all older caches and the new bundles are fetched.
- Service Filter Bar: search by name, filter by status (online/offline)
- Batch Operations: multi-select containers for start/stop/restart
- Container Snapshots: create and manage Docker checkpoints
- Added filter bar and batch action bar to index.html
- Added snapshot button to Admin tools section
- New JS modules: service-filter.js, batch-operations.js, snapshot.js
- Updated build.js to include new modules in bundle
- New container-logs.js module for viewing Docker container logs
- Integrated with existing API endpoints (/logs/containers, /logs/container/:id, /logs/stream/:id)
- Features:
- Select container from dropdown
- View logs with stdout/stderr color coding
- Real-time log streaming via SSE
- Search/filter within logs
- Download logs as text file
- Line count and filter indicators
- Added '📜 Container Logs' button to Tools section in index.html
- Added to features.js bundle via build.js
- Rebuilt dist files
- Container exec/shell via WebSocket + xterm.js (subtle >_ button on cards)
- Live dashboard updates via SSE (resource alerts, health changes, update notices)
- Docker Compose import with YAML parsing, preview, and dependency-ordered deploy
- Volume & network management modal with disk usage overview
- CPU/memory resource limits on deploy and live update
- Email SMTP notifications (nodemailer) alongside Discord/Telegram/ntfy
- Scheduled auto-update scheduler with maintenance windows (daily/weekly/monthly)
New deps: ws, js-yaml, nodemailer
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Full codebase including API server (32 modules + routes), dashboard frontend,
DashCA certificate distribution, installer script, and deployment skills.