Captures the work done in this session (DC-033) and surfaces 9 follow-up
items that came out of the cross-check investigation:
P1: DC-034 (regenerate release tarball as 1.14.9), DC-035 (regression test
for getLocalVersion), DC-036 (delete dead root self-updater.js), DC-037
(move symlink creation into install script so fresh hosts don't repeat
the v1.14.4 failure mode).
P2: DC-038 (backup trigger.json/result.json), DC-039 (audit for other
__dirname antipatterns), DC-040 (audit whether post-deploy-patches.sh is
still needed), DC-041 (integration test for the auto-update pipeline).
Each ticket cites the specific files, commit SHAs, and evidence from
this session so future agents can pick up where this left off.
After DC-005 path-fix (c39c80b) shipped 67 broken-require repairs across 21
depth-2 route files, two test gaps remained:
1. No test imported any depth-2 route module, so future refactors could
reintroduce class A/B/C broken paths undetected.
2. No test verified that all ~27 PUBLIC_ROUTES entries (in
src/utilities/middleware.js) corresponded to actually-mounted routes.
DC-012 added a similar check for the 5 probe paths, but only those.
Added 3 files, fixed 1 test helper, no production code changed:
- __tests__/depth2-routes-smoke.test.js (new): discovers every .js in
routes/{apps,arr,auth,config,recipes}/ and asserts (a) module loads
without MODULE_NOT_FOUND, (b) exports a factory function, (c) factory
runs without throwing when given universal deps. Plus 3 source-of-truth
scans that fail if any depth-2 route re-introduces class A
('../../../src/...'), class B ('../src/...'), or class C
('utilities/responses' instead of 'utils/responses') require paths.
- __tests__/public-routes-drift.test.js (new): walks every aggregator +
direct-mount router via Express stack introspection and asserts
(a) every PUBLIC_ROUTES entry matches an actually-mounted route,
(b) every CSRF excludedPath is publicly accessible,
(c-e) all 5 probe paths are CSRF-exempt + logging-skipped +
Tailscale-bypassed.
- __tests__/test-helpers/universal-deps.js (new): Proxy + seed-object
shared by both suites. Returns sensible stubs for any property access
(logger-shaped object, asyncHandler pass-through, path-string stubs).
Supports Object.assign/spread via ownKeys+getOwnPropertyDescriptor
traps so aggregator factories that copy ctx into subCtx don't lose
proxy magic.
Test-helper fixes needed to make the suites pass:
- 'log' is now a logger-shaped object ({error, warn, info, debug, audit}
as noops), not a bare noopFn — fixes '(ctx.log || console).error(...)'
in routes/apps/index.js factory catch block.
- 'asyncHandler' seeded as own enumerable property — survives
Object.assign({}, ctx, { helpers }) used by routes/arr/index.js etc.
- Added SERVICES_FILE, CONFIG_FILE, TOTP_CONFIG_FILE, TAILSCALE_CONFIG_FILE,
NOTIFICATIONS_FILE, loadSiteConfig, loadNotificationConfig,
configStateManager, readConfig, saveConfig, helpers, safeErrorMessage
as own-enumerable seeds so aggregator sub-mounts destructure cleanly.
Public-routes-drift test fixes:
- Aggregator walks use prefix '/api/v1' (matches src/app.js's bare-mount
on apiRouter at /api/v1). Without this, the 6 TOTP routes registered by
routes/auth/index.js appeared as '/totp/config' instead of
'/api/v1/totp/config' and were falsely flagged as stale.
- Direct-mount walks use '/api/v1' + explicit prefixMap entry (same reason).
- Added routes/themes.js and routes/license.js to directMounts.
Result: 35 suites, 1036 tests, all passing (was 1030 passing + 6 failing
before this commit). The 6 pre-existing failures were depth-2 factory
errors + 22 PUBLIC_ROUTES stale entries that the test infrastructure
was silently swallowing — these tests surface them so they can't recur.
BACKLOG.md updated with full DC-017 entry (status: done, owner: krystie).
Picked up the four 'Still Open' standardization items from the audit doc
as Option B work. Audited each before starting implementation:
DC-013 (config schema migration) — src/config/migrations.js exists with
a versioned migration system (CURRENT_VERSION=2, v1 dns normalization,
v2 dns.provider field), loadAndMigrate() writes back to disk only when
version changes, called from src/config/site.js on every startup.
Guarded by 21 tests in __tests__/config-migrations.test.js.
DC-014 (monitoring endpoint opt-in) — MONITORING_PUBLIC env var +
config.monitoring.public both work via an IIFE in
src/utilities/middleware.js line 297. Routes are conditionally public
based on the flag. Default is 'true' for back-compat with existing
dashboards that pre-load widget data. Flipping the default to 'false'
is a fresh change with a real UX cost.
DC-015 (CSRF token path duplication) — grep confirms only
/api/v1/csrf-token exists. /api/v1/auth/csrf-token was never
implemented or was already cleaned up.
DC-016 (per-call fetchT timeouts) — src/utils/http.js defines
fetchT(url, opts, timeoutMs) with AbortSignal.timeout() in the
native branch and explicit timeout handlers in the http/https
raw-request branches. 5s default covers most calls; 8 of 77 sites
pass explicit overrides. 5min global request timeout is the backstop.
All four tasks reassigned from krystie → hermes because the work shifted
from 'implement' to 'verify and document'. No code changes in this commit
— only BACKLOG.md and CHANGELOG.md updated to reflect actual state.
This commit is the meta-example for Pitfall 20 (just added to the
standardization pitfalls reference): audit docs decay as fast as fixes
land. Always audit before implementing.
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.
The DC-005 src/ refactor left depth-2 route files (routes/auth/*,
routes/recipes/*, routes/apps/*, routes/arr/*, routes/config/*) with
broken require() paths. A filesystem-resolving scanner found 67 broken
requires across 21 files — three distinct bug classes:
A) '../../../src/...' (3 levels up, above package root) — Bug 7, ~49 occurrences
B) '../src/utils/...' (1 level up, resolves to nonexistent routes/src/) — ~15 occurrences
C) routes/apps/restore.js:5 used utilities/responses (wrong dir) — should be utils/responses
All fixed to '../../src/...' (or '../../src/utils/responses' for class C).
routes/auth/totp.js was already fixed in the DC-006 commit.
Post-fix: 922/922 tests pass, zero new ESLint warnings. No logic changes —
purely mechanical require() path corrections.
DC-006 marked done with 25-test result summary + 904/904 test note.
DC-005 annotated with two critical notes:
- Latent require-path bug in depth-2 routes (mechanical 3->2 fix needed in ~22 files)
- Branch state vs origin/main divergence (need coordinated merge, not silent FF)
Inserts a comprehensive Linux (DNS2 / Contabo VPS) section between the existing Windows docs and 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 (Caddyfile reload, logs, rebuild, services.json)
- Windows-vs-Linux differences table
- Four Linux-specific gotchas (Caddy network_mode host, credentials.json perms, CORS_ORIGINS, TS_AUTHKEY)
Also corrects the stale 'Version: 1.0' field to current 1.13.4 and adds the Linux-side default TLD (.home). All existing Windows content preserved verbatim per the LITERAL COPY RULE.
comprehensive-test.js and test-security-fixes.js are 875 lines of
ad-hoc security test scripts (not Jest tests). They have zero references
in code or docs. Moved to scripts/legacy/ to declutter repo root
without losing the content. All 759 Jest tests still pass.
- Updated root VERSION file from 1.13.0 → 1.13.4 to match package.json.
- scripts/release.sh now writes both files on every release bump, and
stages VERSION alongside package.json in the release commit.
- This prevents the drift that caused the stale VERSION in the first place.