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.
15 KiB
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: todotostatus: in-progressand setowner. When done: change tostatus: doneand 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
/healthzor/readyzprobes" as still-open work. v1.13.0 already added/health/liveand/health/readywith 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/healthzand/readyzare missing — fresh users copy-pasting ahealthcheck:block from k8s docs ordocker-compose.ymlexamples online get connection refused. Even worse:src/docker/app-templates.js:316references"/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/healthzand/readyzaliases that point to the same handlers, deprecate the/api/v1/healthduplicate (keep root/healthas canonical), document the probes with a copy-pastedocker-compose.ymlhealthcheck block in the user-guide. - result: Added
/healthzand/readyzas root-level aliases for/health/liveand/health/readyso fresh users can copy-pastehealthcheck: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 withchecksobject. 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,/readyzinto a single handler block insrc/app.js(DRYed the duplicated handler bodies). Removed the dead/api/v1/health*routes that were registered inPUBLIC_ROUTES+ CSRF lists but never actually mounted on the apiRouter — anyone probing/api/v1/healthnow 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 betweensrc/app.jsmount list andsrc/utilities/middleware.jsallowlist. 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.jsto 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) inroutes/services.jshad 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
maindropped the DC-001 route-prefix fix.routes/services.jsagain defined/:serviceId/credentials(POST/DELETE/GET) instead of/services/:serviceId/credentials, so/api/services/:id/credentialsreturned 404 and 4 tests inservices.routes.test.jsfailed. 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 latentReferenceError: those same validation branches calledctx.errorResponse()butctxis never defined in this module (the factory destructures deps); replaced with the importederrorResponsehelper 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 tagbackup-pre-origin-reset) and restored BACKLOG.md.
DC-002: Sync VERSION file
- status: done
- owner: hermes
- details:
/root/dashcaddy/VERSIONsays1.13.0butpackage.jsonsays1.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.shto write bothdashcaddy-api/package.jsonAND rootVERSIONon 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.jsandtest-security-fixes.jsare 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 insrc/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 insrc/app.js: (1)require-awaitonresyncHealthChecker— dropped the now-pointlessasynckeyword since it only forwards a promise (callers already use.catch()); (2)+(3) twomax-depthviolations in the/api/v1/network/ipshandler — extracted the interface-enumeration logic into adetectInterfaceIps()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 undersrc/(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-improvementsbranch (879/879 tests passing on branch). Merged into main via commit283121eafter resolving 24 conflicts. Post-merge regression check surfaced one additional latent path bug from DC-005:src/monitoring/health-checker.jsstill hadrequire('./platform-paths')(relative tosrc/monitoring/), butplatform-paths.jslives at top level — fixed in commit9688e64torequire('../../platform-paths'). Without that fix, 59 cascading test failures inhealth-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 nonexistentroutes/src/) — undocumented, ~15 occurrences forresponsesandlogging; (C)routes/apps/restore.js:5importedutilities/responseswhen the module lives atutils/responses(wrong directory + wrong depth). All 67 fixed to'../../src/...'(or'../../src/utils/responses'for the class-C case).routes/auth/totp.jswas 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 realotplibfor code generation (real TOTP math), mockscredentialManager/session/totpConfig/saveTotpConfigonly. 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.jshad broken require paths from the DC-005 refactor ('../../../src/utilities/errors'was 3 levels up fromroutes/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 stale1.0to current1.13.4and 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 remainingres.json(in route handlers and convert to response helpers. - result: All bare
{success: true, ...}envelopes across route files now go throughsuccess()(orok()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.jsline 360+368 left alone (intentional raw-array responses for the frontend wire contract — separate cleanup). Error-pathres.status(4xx/5xx).json({success:false, error:...})envelopes also left as-is (ok()helper would setsuccess:true— wrong tool for error shapes). Net result: only 2 intentional raw-array calls remain in routes/; everything else routes throughresponse-helpers. 750/750 tests pass at every checkpoint.
Coordination Rules
- Always
git pullbefore starting work. - Claim a task by editing BACKLOG.md: set
status: in-progressandowner: hermesorowner: krystie. - Commit BACKLOG.md claim first, then start coding.
- Run tests before pushing:
cd dashcaddy-api && npx jest --passWithNoTests - Push to
main— usehttp://sami7777:<token>@100.98.123.59:3000/sami7777/dashcaddy.git - Update BACKLOG.md when done: set
status: done, add brief result under the task. - Never work on a task another bot has claimed (status: in-progress).
- Quality bar: this is a public-release product. No hacks, no env-var workarounds, no per-machine patches. Fixes go in the shared codebase.
- VERSION bump: when a batch of tasks is done, bump patch version in package.json + VERSION file, update CHANGELOG, tag.