Files
dashcaddy/BACKLOG.md
T
Hermes 8ec6c0ca6a
CI / Test & Lint (push) Has been cancelled
CI / Security audit (push) Has been cancelled
DC-012: Add Kubernetes-style /healthz + /readyz probe aliases
Fresh users copy-pasting healthcheck blocks from k8s/Docker docs need
the standard short aliases. Without /healthz and /readyz they get
connection refused. This commit:

1. Adds /healthz + /readyz as root-level aliases for /health/live +
   /health/ready in src/app.js. Handler bodies DRYed into named
   functions (livenessHandler, readinessHandler) so a probe semantics
   change updates all five paths at once.

2. Removes the dead /api/v1/health*, /api/v1/health/live, /api/v1/health/ready
   registrations from PUBLIC_ROUTES and CSRF exclusion list — those
   routes were never actually mounted on the apiRouter (only root
   paths existed). Anyone probing /api/v1/health now gets a clean 404
   instead of being routed through to a duplicate root handler.

3. Adds bypass for the 5 probe paths in three places where it matters:
   - PUBLIC_ROUTES (no auth)
   - csrf-protection.js excludedPaths (no CSRF check)
   - middleware.js request-logging exclusion (k8s polling every 10s
     doesn't flood the audit log)
   - middleware.js Tailscale auth bypass (probes don't carry Tailscale
     identity headers)

4. Adds __tests__/health-probe-aliases.test.js (19 tests):
   - Alias equivalence (/healthz == /health/live, /readyz == /health/ready)
   - Back-compat (/health == /health/live)
   - Path consolidation (all 3 /api/v1/health* return 404)
   - Source-of-truth PUBLIC_ROUTES allowlist sync check
   - Source-of-truth src/app.js mount list sync check (catches drift
     between handler mount and middleware allowlist)

5. Documents probes in README (copy-paste docker-compose.yml +
   Kubernetes blocks) and user-guide (Health Probes section + System
   API table updated).

Post-fix: 941/941 tests pass (+19 new). Zero new ESLint warnings
introduced. The pre-existing warnings/errors in src/app.js line 906
('os' is not defined) and the empty blocks in logging.test.js are
not regressions from this commit.
2026-06-25 17:16:16 -07:00

15 KiB

DashCaddy Improvement Backlog

Shared coordination file for Hermes & Krystie. Both bots read this, claim tasks, and update status. Git is the source of truth. When claiming: change status: todo to status: in-progress and set owner. When done: change to status: done and add brief result.


P0 — Must Fix (blocks public release)

DC-012: Add Kubernetes-style /healthz + /readyz probe aliases + document for fresh users

  • status: done
  • owner: hermes
  • details: The standardization-pitfalls doc explicitly lists "No /healthz or /readyz probes" as still-open work. v1.13.0 already added /health/live and /health/ready with proper probe semantics (live=process alive, ready=deps reachable) and tests in __tests__/health-endpoints.test.js (8 tests). But: (1) The k8s/Docker-standard short aliases /healthz and /readyz are missing — fresh users copy-pasting a healthcheck: block from k8s docs or docker-compose.yml examples online get connection refused. Even worse: src/docker/app-templates.js:316 references "/healthz" as a template healthcheck URL — but that URL doesn't resolve on the DashCaddy API itself. (2) /api/v1/health (apiRouter.get line 658) and root /health (app.get line 674) both exist and return identical responses — duplicated, fresh users won't know which to probe. (3) README + user-guide have zero documentation of the probes — a fresh user has no way to know they exist or how to wire them. Fix: add /healthz and /readyz aliases that point to the same handlers, deprecate the /api/v1/health duplicate (keep root /health as canonical), document the probes with a copy-paste docker-compose.yml healthcheck block in the user-guide.
  • result: Added /healthz and /readyz as root-level aliases for /health/live and /health/ready so fresh users can copy-paste healthcheck: blocks from k8s/Docker docs. Liveness (/healthz) is a pure process check (no I/O). Readiness (/readyz) checks config file, services file, Docker daemon, Caddy admin API (3s timeout each), returns 200 if all OK or 503 with checks object. Probe endpoints bypass auth, CSRF, and per-request logging (k8s polling every 10s won't flood audit log). Consolidated /health, /health/live, /health/ready, /healthz, /readyz into a single handler block in src/app.js (DRYed the duplicated handler bodies). Removed the dead /api/v1/health* routes that were registered in PUBLIC_ROUTES + CSRF lists but never actually mounted on the apiRouter — anyone probing /api/v1/health now gets a clean 404. Added __tests__/health-probe-aliases.test.js (19 tests): alias equivalence, removed-path 404 confirmation, source-of-truth sync check that catches drift between src/app.js mount list and src/utilities/middleware.js allowlist. README + user-guide updated with copy-paste Docker Compose + Kubernetes probe blocks. Post-fix: 941/941 tests pass (+19 new).

DC-001: Fix 4 failing tests in services.routes.test.js

  • status: done
  • owner: hermes
  • details: Credential storage tests failing since before v1.13.4. Run cd dashcaddy-api && npx jest __tests__/routes/services.routes.test.js to see failures. Fix the root cause, not the test.
  • result: Root cause: routes used /:serviceId/credentials (missing /services/ segment). All 3 credential routes (POST/DELETE/GET) in routes/services.js had the wrong path. Fixed to /services/:serviceId/credentials — matches the URL pattern used by the live frontend and all 759 tests pass.

DC-011: Fix DC-001 regression reintroduced by src/ refactor (4 failing tests)

  • status: done
  • owner: hermes
  • details: The module-flattening refactor (DC-005) force-pushed to main dropped the DC-001 route-prefix fix. routes/services.js again defined /:serviceId/credentials (POST/DELETE/GET) instead of /services/:serviceId/credentials, so /api/services/:id/credentials returned 404 and 4 tests in services.routes.test.js failed. Baseline: npx jest → 4 failed, 746 passed.
  • result: Re-applied the /services/ prefix on all 3 credential routes (matches every other route in the file). Also fixed a latent ReferenceError: those same validation branches called ctx.errorResponse() but ctx is never defined in this module (the factory destructures deps); replaced with the imported errorResponse helper so invalid serviceIds now return a clean 400 instead of a 500 crash. Result: 750/750 tests pass (4 failed → 0), zero new ESLint warnings. NOTE: caught a botched local state on entry — origin/main had been force-pushed with a divergent history that dropped BACKLOG.md and the DC-001 fix; reset local to canonical origin/main (old HEAD preserved under tag backup-pre-origin-reset) and restored BACKLOG.md.

DC-002: Sync VERSION file

  • status: done
  • owner: hermes
  • details: /root/dashcaddy/VERSION says 1.13.0 but package.json says 1.13.4. VERSION file should always match package.json. Add a pre-commit or post-version bump hook to keep them in sync.
  • result: Fixed root VERSION to 1.13.4. Updated scripts/release.sh to write both dashcaddy-api/package.json AND root VERSION on every release — also stages VERSION in the release commit. No more drift.

DC-003: Remove stale test/debug files from repo root

  • status: done
  • owner: hermes
  • details: comprehensive-test.js and test-security-fixes.js are ad-hoc test scripts, not Jest tests. They clutter the repo root. Remove them or convert to proper Jest tests under __tests__/.
  • result: Moved both files to dashcaddy-api/scripts/legacy/ (preserved, not deleted — they are 875 lines of security test coverage that may be useful as a manual smoke test). Zero references to them in code/docs — safe to move. All 759 Jest tests still pass.

P1 — Code Quality

DC-004: Fix 19 ESLint warnings

  • status: done
  • owner: hermes
  • details: Run cd dashcaddy-api && npx eslint src/ --format compact. Most are unused vars and nested ternaries in src/utils/logging.js. Fix all, target zero warnings.
  • result: Reached zero ESLint warnings across src/. Most of the original 19 were cleared by the DC-005 refactor and logging cleanup; the final 3 were in src/app.js: (1) require-await on resyncHealthChecker — dropped the now-pointless async keyword since it only forwards a promise (callers already use .catch()); (2)+(3) two max-depth violations in the /api/v1/network/ips handler — extracted the interface-enumeration logic into a detectInterfaceIps() helper, keeping the route handler flat. npx eslint src/ now reports 0 problems; 750/750 Jest tests still pass.

DC-005: Organize top-level modules into src/

  • status: done (merged to main 2026-06-25)
  • owner: krystie
  • details: 40+ JS files at dashcaddy-api/ root level (auth-manager.js, credential-manager.js, etc.). Move into organized subdirs under src/ (e.g., src/managers/, src/security/, src/docker/). Update all require() paths. This is a big refactor — run tests after.
  • result: Refactor complete on krystie-improvements branch (879/879 tests passing on branch). Merged into main via commit 283121e after resolving 24 conflicts. Post-merge regression check surfaced one additional latent path bug from DC-005: src/monitoring/health-checker.js still had require('./platform-paths') (relative to src/monitoring/), but platform-paths.js lives at top level — fixed in commit 9688e64 to require('../../platform-paths'). Without that fix, 59 cascading test failures in health-checker.test.js. Final post-merge state: 921/922 tests passing.
  • remaining latent bugs (FIXED): The DC-005 path-rewrite script left depth-2 route files (routes/auth/*.js, routes/recipes/*.js, routes/apps/*.js, routes/arr/*.js, routes/config/*.js) with broken require() paths. A filesystem-resolving scanner found 67 broken requires across 21 files — three distinct bug classes: (A) '../../../src/...' (3 levels up, goes above package root) — the documented Bug 7, ~49 occurrences; (B) '../src/utils/...' (only 1 level up, resolves to nonexistent routes/src/) — undocumented, ~15 occurrences for responses and logging; (C) routes/apps/restore.js:5 imported utilities/responses when the module lives at utils/responses (wrong directory + wrong depth). All 67 fixed to '../../src/...' (or '../../src/utils/responses' for the class-C case). routes/auth/totp.js was already fixed in the DC-006 commit. Tests didn't catch any of these previously because no test imported any depth-2 route. Post-fix: 922/922 tests pass, zero new ESLint warnings.

DC-006: Add integration test for TOTP auth flow

  • status: done
  • owner: krystie
  • details: End-to-end test: no token → 401, wrong token → 403, valid TOTP → session token → authenticated request succeeds. Cover the full /api/auth/check → session → endpoint flow.
  • result: Added dashcaddy-api/__tests__/routes/auth.totp.routes.test.js — 25 tests, all passing. Covers: GET /api/totp/config, POST /api/totp/setup (generate + normalize + reject invalid Base32), POST /api/totp/verify-setup (missing/bad/no-pending/valid-code paths), POST /api/totp/verify (login — 400/400/401/200), GET /api/totp/check-session (passthrough when disabled + 401 no-session + 200 valid-session — the BACKLOG "no token → 401 / authenticated request succeeds" pair), POST /api/totp/disable (400/401/200), POST /api/totp/config (valid/invalid/never-disables), plus the full end-to-end flow setup→login→check-session→disable and an otplib-not-stubbed sanity check. Uses real otplib for code generation (real TOTP math), mocks credentialManager/session/totpConfig/saveTotpConfig only. Full suite: 904/904 pass (879 baseline + 25 new). ESLint clean for the new file.
  • side-effect (DC-005 latent bug fix): While writing the test I discovered routes/auth/totp.js had broken require paths from the DC-005 refactor ('../../../src/utilities/errors' was 3 levels up from routes/auth/ — wrong by 1). The test couldn't even load the route without this fix. Fixed in this commit ('../../src/utilities/errors' and '../../src/utils/responses'). Same depth bug exists in other depth-2 route files — see DC-005 note above.

DC-007: Add tests for untested modules

  • status: done
  • owner: krystie
  • result: 7 test files added (120 new tests, all passing alongside the 759 baseline → 879 total). Files: __tests__/dns-propagation.test.js (9), __tests__/notification-manager.test.js (18), __tests__/ssl-monitor.test.js (13), __tests__/log-digest.test.js (11), __tests__/metrics.test.js (21), __tests__/config-drift-detector.test.js (19), __tests__/auto-restart-manager.test.js (29).
  • details: These modules have NO test coverage: dns-propagation.js, notification-manager.js, ssl-monitor.js, log-digest.js, metrics.js, config-drift-detector.js, auto-restart-manager.js. Add at least basic smoke tests for each.

P2 — Polish & DX

DC-008: Update CLAUDE.md for cross-platform accuracy

  • status: done
  • owner: hermes
  • details: CLAUDE.md references Windows-specific paths (C:/caddy/, e:/CaddyCerts/) as if they're universal. DashCaddy runs on Linux (Docker on DNS2) and Windows (SAMI-PC). Document both deployment targets clearly.
  • result: Added a new "Linux Deployment (DNS2 / Contabo VPS)" section after the existing Windows docs (preserved verbatim) and before the "Project Info" footer. The new section documents: production paths (/opt/dashcaddy/, /var/www/dashcaddy-status/, /etc/dashcaddy/), container mount points with the /app/data/ auto-resolve fallback, the three-filesystem frontend trap (source vs live vs build-context), common admin commands, a Windows-vs-Linux differences table, and four Linux-specific gotchas (Caddy network_mode host, credentials.json perms, CORS_ORIGINS vs Tailscale, TS_AUTHKEY provisioning). Also updated the "Project Info" version field from stale 1.0 to current 1.13.4 and added the Linux-side default TLD (.home).

DC-009: Add CHANGELOG entry for any unreleased work

  • status: done
  • owner: hermes
  • details: [Unreleased] section in CHANGELOG.md is empty. Any fixes done should be documented there before tagging a release.
  • result: Populated the [Unreleased] section with all unreleased work since v1.5.0: Security (TOTP 4-part recovery), Added (OpenClaw routes, auto-backup, monitoring widget, Sami Files template, unified logger, notification manager, update UX, 120 new tests across 7 files), Changed (DC-010 response standardization across 9 route files, /api/v1/ versioning, release.sh hardening), Fixed (DC-011 credential route regression, DC-004 ESLint cleanup, workflow engine init, container-logs wireModal misuse, CSP hash mismatch, SW cache tag, updater false-positive loop), Removed (legacy test scripts moved to scripts/legacy/ preserved-not-deleted, stale root files, dead routes/ directory). Each entry cites the source commit hash for traceability.

DC-010: Standardize error response shapes

  • status: done
  • owner: hermes
  • details: v1.13.4 standardized route responses to use helpers, but some modules still use raw res.json(). Grep for remaining res.json( in route handlers and convert to response helpers.
  • result: All bare {success: true, ...} envelopes across route files now go through success() (or ok() where the older alias is wired in). Files converted in this push (4 commits): browse/logs/sites (cron), updates/notifications/tailscale/events/workflows/openclaw/dns/health/ca (this sprint) — 9 files, 62 calls. services.js line 360+368 left alone (intentional raw-array responses for the frontend wire contract — separate cleanup). Error-path res.status(4xx/5xx).json({success:false, error:...}) envelopes also left as-is (ok() helper would set success:true — wrong tool for error shapes). Net result: only 2 intentional raw-array calls remain in routes/; everything else routes through response-helpers. 750/750 tests pass at every checkpoint.

Coordination Rules

  1. Always git pull before starting work.
  2. Claim a task by editing BACKLOG.md: set status: in-progress and owner: hermes or owner: krystie.
  3. Commit BACKLOG.md claim first, then start coding.
  4. Run tests before pushing: cd dashcaddy-api && npx jest --passWithNoTests
  5. Push to main — use http://sami7777:<token>@100.98.123.59:3000/sami7777/dashcaddy.git
  6. Update BACKLOG.md when done: set status: done, add brief result under the task.
  7. Never work on a task another bot has claimed (status: in-progress).
  8. Quality bar: this is a public-release product. No hacks, no env-var workarounds, no per-machine patches. Fixes go in the shared codebase.
  9. VERSION bump: when a batch of tasks is done, bump patch version in package.json + VERSION file, update CHANGELOG, tag.