Files
dashcaddy/DC-PRODUCTION-GRADE-BACKLOG.md
T
Hermes bb20f02cbf
CI / Test & Lint (push) Canceled after 0s
CI / Security audit (push) Canceled after 0s
Expand production backlog: 19 → 39 tasks (DC-081–DC-100)
Deep audit additions:
  P2.5 Security: route validation gap (151/160 unvalidated), cmd injection
    surface in ca.js, 30 untested source files, no .dockerignore, Math.random IDs
  P3.5 Ops: error codes, SDK/types, log rotation, license rate limit, Node
    version pin, Dependabot, dependency health checks, workflow retry, audit trail
  P4 Advanced: multi-user RBAC, API keys, Prometheus/Grafana, changelog,
    migration system, service auto-discovery
2026-08-12 02:13:55 -07:00

28 KiB
Raw Blame History

DashCaddy Production-Grade Backlog (v2)

Generated 2026-08-12 from a full codebase audit. v1 items (P0-1 through P2-7) are ALL DONE. Current state: 1539 tests, 86.55% statement coverage, 0 ESLint errors, 173 warnings.

Current Health Snapshot

  • Tests: 1539 passing across 63 suites
  • Coverage: Statements 86.55% | Branches 72.14% (below 80% gate) | Functions 80.8% | Lines 90.67%
  • ESLint: 0 errors, 173 warnings (all pre-existing)
  • Remaining console. calls in src/:* 21 across 10 files
  • Dockerfile: Runs as root (documented — needs Docker socket), no resource limits
  • OpenAPI spec: Present but stale (says v1.0.0, actual is v1.15.0)
  • Unhandled rejection/exception handlers: Present in server.js ✓
  • Rate limiting: Present on auth + general routes ✓
  • npm audit: 4 remaining vulns (semver-major transitive deps, deferred)

P0 — Must Fix (blocks public release)

DC-062: OpenAPI spec is stale — update to match actual v1.15.0 API surface

  • status: pending
  • details: openapi.yaml says version: 1.0.0 and describes only a fraction of the API. Since DC-046/047 (auth providers), DC-053 (share), DC-055 (billing), DC-058 (share UI), and the tailscale-admin routes were added, the spec is significantly out of date. A stale spec is worse than no spec — it misleads API consumers and breaks any code generation from it. Fix: audit all route files (grep -rn 'router\.\(get\|post\|put\|delete\|patch\)' routes/), update openapi.yaml with every endpoint, bump version to 1.15.0, add it to the test suite (DC-017-style source-of-truth test that fails if a route exists but has no spec entry). Effort: ~3 hr.
  • impact: Public API trust. No paying customer can integrate against an undocumented API.

DC-063: Branch coverage at 72% — below the 80% gate

  • status: pending
  • details: Jest coverage report shows branches at 72.14% (303/420), failing the 80% threshold. The uncovered branches are concentrated in error-handling paths (catch blocks, fallback returns, edge-case conditionals). Fix: run npx jest --coverage --coverageReporters=text to identify the files with the lowest branch coverage, then add targeted tests for the uncovered conditional paths. Priority files: backup-manager.js (multiple catch blocks), health-checker.js (timeout/retry branches), tailscale-coord.js (API error branches). Effort: ~2 hr.
  • impact: Error paths are where production incidents hide. Every untested catch block is a potential crash.

DC-064: Dockerfile runs as root with no resource limits

  • status: pending
  • details: The Dockerfile has no USER directive and start.sh has no --memory or --cpus flags. While root is needed for Docker socket access, the container can still OOM the host. Fix: (1) Add --memory=512m --memory-swap=1g --cpus=1.5 to the docker run in start.sh. (2) Create a non-root user dashcaddy for the application process, and use a Docker socket proxy (like tecnativa/docker-socket-proxy) that exposes a limited subset of Docker API endpoints — the app only needs read access for monitoring + controlled container lifecycle. (3) Add --restart=unless-stopped if not already present. Effort: ~2 hr. Risk: medium — socket proxy may break some Docker API calls, needs testing.
  • impact: Without limits, a memory leak in the API can take down the entire host. This is a production safety issue.

P1 — Code Quality & Reliability

DC-065: Remaining 21 console.* calls — sweep to structured logger

  • status: pending
  • details: After DC-060 (update-manager) and P1-3 through P1-8, 21 console calls remain across 10 files: error-handler.js (2), email.js (1), dns-providers/registry.js (2), audit-logger.js (3), csrf-protection.js (3), config-drift-detector.js (1), auto-restart-manager.js (1), http.js (1), logging.js (6 intentional — the logger itself), routes/backups.js (1). The logging.js calls are fine (the logger IS console internally). The rest should route through log.info/warn/error. Some are fallbacks: ctx.logError || ((_c, err) => console.error(err)) — these fire when ctx isn't available, which is exactly when structured logging matters most. Effort: ~45 min.
  • impact: Consistency. The logger write to error.log and supports structured JSON — console does not.

DC-066: No API integration test for the billing flow end-to-end

  • status: pending
  • details: DC-057 shipped contract tests and unit tests for the Stripe bridge, but there is no test that exercises the full flow: pricing page → Stripe Checkout → webhook → license-key delivery → license activation → Pro unlock. Build a single integration test that mocks Stripe's API, walks the complete flow, and asserts the license works at the end. This is the revenue path — it must be tested as a chain, not just individual pieces. Effort: ~2 hr.
  • impact: Confidence in the revenue pipeline. A broken webhook or catalog mismatch silently loses sales.

DC-067: No graceful shutdown — SIGTERM kills in-flight requests

  • status: pending
  • details: server.js handles uncaughtException and unhandledRejection, but there is no SIGTERM handler that calls server.close() to drain connections. Docker stop sends SIGTERM (the Dockerfile has STOPSIGNAL SIGTERM), but without a handler the process exits immediately, dropping any in-flight API calls. Fix: add a SIGTERM handler in server.js that (1) stops accepting new connections via server.close(), (2) waits up to 10s for in-flight requests, (3) closes DB/file handles, (4) exits cleanly. Also emit a shutdown event so managers (health checker, SSL monitor, workflow engine) can stop their timers. Effort: ~1 hr.
  • impact: Zero-downtime deployments. Currently, every docker stop drops active requests.

DC-068: ESLint warnings sweep — 173 pre-existing warnings

  • status: pending
  • details: While there are 0 ESLint errors, 173 warnings remain. Top files: dns-providers/base.js (27), update-manager.js (14), backup-manager.js (10), keychain-manager.js (10), bundled-workflows.js (10), auth/providers/base.js (9), log-digest.js (8). Most are no-unused-vars, require-await, no-nested-ternary. Fix: sweep through the top 10 files, fix what's actionable (unused vars → remove, nested ternaries → extract to named variables, false-positive require-await → mark _ or restructure). Set a ceiling: warnings should never increase. Effort: ~2 hr.
  • impact: Clean codebase. 173 warnings is noise that hides real issues when new ones are added.

DC-069: Health check notification spam — add failure threshold + cooldown

  • status: pending
  • details: The workflow engine sends a notification on EVERY health check failure (every 15 min). If a service is down for a day, that's 96 identical notifications. There is no backoff, no deduplication, no "service recovered" message. Fix: (1) Only notify on state TRANSITIONS (up→down, down→up), not every failure. (2) Add a consecutiveFailures threshold (e.g., 2 failures before first alert) to avoid flapping noise. (3) Send a recovery notification when a service comes back up. (4) Optional: daily digest of uptime stats instead of per-failure alerts. Effort: ~1.5 hr.
  • impact: Operator sanity. The current notification volume is exactly why people mute alerting channels — and then miss real incidents.

P2 — Polish & Developer Experience

DC-070: No CI/CD pipeline — tests run manually

  • status: pending
  • details: There is no GitHub Actions / CI configuration. Tests are run manually before push. This means a bad commit can reach main if someone forgets to test. Fix: add .github/workflows/test.yml (or Gitea Actions equivalent) that runs npm ci && npx jest --coverage on every PR and push to main. Cache node_modules. Upload coverage report as artifact. Block merge on test failure or coverage decrease. Effort: ~1 hr.
  • impact: Automated quality gate. No bad commit reaches production.

DC-071: No error tracking / Sentry integration

  • status: pending
  • details: Errors go to error.log inside the container. If the container is recreated (DC-050 migration), the error log is lost. There is no external error tracking. Fix: add an optional Sentry (or GlitchTip for self-hosted) integration. If SENTRY_DSN env var is set, initialize Sentry before Express. Wrap async handlers to capture exceptions. The error-handler.js middleware should forward to Sentry before returning the generic error response. Make it opt-in (no DSN = no Sentry, zero behavior change). Effort: ~1 hr.
  • impact: Production visibility. Right now, errors are invisible unless someone SSHs in and reads the log.

DC-072: Frontend bundle has no source maps in production

  • status: pending
  • details: status/build.js uses esbuild but the production build doesn't emit source maps. When a frontend error occurs in production, the stack trace points to minified bundle lines — useless for debugging. Fix: add sourcemap: true to the esbuild production config. Serve .map files from Caddy (they're already in dist/). Optionally upload source maps to Sentry (DC-071). Effort: ~30 min.
  • impact: Frontend bug reports become actionable instead of "line 1 of core.js".

DC-073: No API request/response logging middleware for debugging

  • status: pending
  • details: While there is an audit logger for POST/PUT/DELETE, there's no request/response logging middleware for debugging purposes (like morgan or a custom equivalent). When an operator reports "the dashboard is slow" or "this endpoint returns 500 sometimes", there's no way to trace the request through the system. Fix: add an optional debug-level request logger that logs method, path, status, duration, and request ID. Gated behind LOG_LEVEL=debug so it's off in production by default. Effort: ~45 min.
  • impact: Drastically reduces time-to-resolution for production issues.

DC-074: Docker image is not multi-stage — build artifacts bloat the image

  • status: pending
  • details: The Dockerfile copies source files into a single stage based on node:20-alpine. The image includes devDependencies because npm install --production still installs some optional deps, and there's no .dockerignore (so __tests__/, .git/, node_modules/ from the host can leak in). Fix: (1) Add a .dockerignore file excluding __tests__/, .git/, node_modules/, *.md, coverage/. (2) Convert to multi-stage: build stage installs all deps, production stage copies only node_modules/ (production) + source. (3) Pin Node.js version: FROM node:20.10-alpine instead of node:20-alpine (floating). Effort: ~1 hr.
  • impact: Smaller image = faster pulls = faster deploys. Current image size carries unnecessary weight.

DC-075: No health check dashboard endpoint for operators

  • status: pending
  • details: The /api/v1/monitoring/stats endpoint returns container stats, but there's no single "is everything OK" endpoint that returns a human-readable system health summary. Fix: add GET /api/v1/system/health that returns { status: "healthy"|"degraded"|"unhealthy", checks: { database: "ok", diskSpace: "ok", memory: "ok", uptime: ..., activeServices: N/M, lastError: "..." } }. This is useful for uptime monitoring services (UptimeRobot, BetterStack) and for a quick operator glance. Effort: ~1 hr.
  • impact: Operators can plug DashCaddy into external monitoring without parsing container stats.

P3 — Future & Nice-to-Have

DC-076: WebSocket support for real-time dashboard updates

  • status: pending
  • details: The dashboard polls the API every N seconds for service status updates. For a "live" dashboard experience, WebSocket (or SSE) push would be better — status changes appear instantly without polling overhead. Fix: add a WebSocket server (using ws library) that pushes service status changes, health check results, and container events to connected dashboard clients. Keep polling as fallback for clients without WS support. Effort: ~3 hr.
  • impact: Dashboard feels "live". Reduces API load from polling.

DC-077: Multi-language (i18n) support

  • status: pending
  • details: All UI text is hardcoded English. For a public product, internationalization is a step toward wider reach. Fix: extract all user-facing strings into a locale file, add an i18n library (like i18next), provide at minimum an English + Arabic locale (Sami's audience). Effort: ~4 hr.
  • impact: Market expansion. Arabic-speaking homelab community is underserved.

DC-078: Backup and restore of DashCaddy's own configuration

  • status: pending
  • details: While DashCaddy can backup app data, there's no one-click "backup my entire DashCaddy setup" (services.json, config.json, health-config.json, credentials, Caddyfile, license) that could be restored on a fresh install. Fix: add GET /api/v1/system/export (returns a signed JSON bundle) and POST /api/v1/system/import (restores from bundle). The credentials file should be encrypted with a user-provided passphrase. Effort: ~2 hr.
  • impact: Migration story. "Moving DashCaddy to a new host" is currently a multi-hour manual process.

DC-079: Mobile-responsive dashboard improvements

  • status: pending
  • details: While the dashboard is somewhat responsive, it's not optimized for mobile use. For operators checking services on their phone, the experience should be touch-first. Fix: audit all dashboard pages on mobile viewport, fix any horizontal scroll, ensure buttons are touch-target sized (min 44px), add a mobile-specific layout for the service grid. Effort: ~3 hr.
  • impact: Operators check services on their phone. Current mobile experience is usable but not polished.

DC-080: Plugin/extension system for custom services

  • status: pending
  • details: DashCaddy supports a fixed set of service templates. A plugin system would allow community-contributed service definitions (e.g., "Home Assistant", "Vaultwarden", "Nextcloud") without modifying core code. Fix: define a plugin manifest schema (name, logo, health check URL pattern, config fields), load plugins from /data/plugins/, add a community plugin registry page. Effort: ~4 hr.
  • impact: Community growth. Extensibility is what makes a tool ecosystem vs. a product.


P2.5 — Security Hardening (Deep Audit Findings)

DC-081: 151 of 160 mutating routes have NO Joi input validation

  • status: pending
  • details: P1-1 added Joi validation to 8 routes, but a scan shows 151 out of 160 POST/PUT/PATCH/DELETE routes still accept raw req.body without schema validation. That's 94% of the mutation surface unvalidated. Routes like POST /api/v1/services/:id, PUT /api/v1/config, POST /api/v1/tailscale/*, POST /api/v1/health/config/:id all accept arbitrary input. Fix: extend src/utilities/validate.js with schemas for every mutating route, wire them in. This is the single highest-impact security improvement. Effort: ~4 hr (batch by route file).
  • impact: Input validation is the #1 defense against injection, abuse, and crashes. 94% gap is a P0 hiding as a P2.

DC-082: Command injection surface in ca.js — 5 execSync calls with interpolation

  • status: pending
  • details: routes/ca.js has 5 execSync() calls with template-string interpolation: lines 164, 175, 180, 203, 263. P0-2 fixed the password injection (execFileSync), but the remaining calls interpolate file paths and subjects (${certFile}, ${keyFile}, ${subject}, ${configFile}). If any of these contain user input (e.g., a service name with ; or backticks), it's command injection. Fix: convert ALL execSync(\...`)calls toexecFileSync('openssl', [...args])with no shell interpolation. Also fixsrc/docker/self-updater.js:717 (execSync(`tar xzf "${tarballPath}"...)) and src/utilities/backup-manager.js:8 (imported execSync). Effort: ~2 hr.
  • impact: Any execSync with interpolation is a potential RCE. This is the same class of bug P0-2 already fixed — finish the job.

DC-083: 30 source files have zero test coverage

  • status: pending
  • details: The test gap scan found 30 source files with NO corresponding test file, including critical paths: license-manager.js (534 lines, the entire revenue validation path), config-schema.js, middleware.js (the auth/rate-limit/CORS stack), startup-validator.js, all 7 DNS provider modules (technitium.js, cloudflare.js, rfc2136.js, manual.js, base.js, registry.js, email.js), docker-maintenance.js, config/migrations.js, event-workers.js, keychain-manager.js, event-store.js, host-registry.js. Fix: prioritize license-manager.js (revenue path) and middleware.js (security stack) first, then work through the rest. Effort: ~8 hr (can be done incrementally, 2-3 files per PR).
  • impact: license-manager.js validates Pro licenses — an untested bug there could silently break activation for every paying customer.

DC-084: No .dockerignore — test files and .git leak into Docker image

  • status: pending
  • details: There is no .dockerignore file. The Docker build context includes __tests__/ (hundreds of test files), any .git/ directory, coverage/, node_modules/ from the host, and markdown files. This bloats the image (currently 249MB) and can leak sensitive test fixtures. Fix: create .dockerignore with: __tests__/, .git/, node_modules/, coverage/, *.md, .eslintrc.js, jest.config.js, npm-debug.log*, .env*, openapi.yaml (only needed at build time if at all). Also add .dockerignore to the git repo. Effort: ~15 min.
  • impact: Faster builds, smaller images, no test fixture leaks.

DC-085: Math.random() used for security-sensitive IDs

  • status: pending
  • details: health-checker.js:352 generates incident IDs with Math.random().toString(36). resource-monitor.js:143 uses Math.random() for sampling. rfc2136.js:120 generates temp filenames with Math.random(). While these aren't crypto-level secrets, Math.random() is not collision-resistant and is predictable. Fix: use crypto.randomUUID() for incident IDs, crypto.randomBytes() for temp filenames, and a simple counter for sampling. Effort: ~30 min.
  • impact: Defense in depth. Predictable IDs can be exploited if they ever become user-facing.

P3.5 — Operational Maturity

DC-086: No structured error codes — errors are ad-hoc strings

  • status: pending
  • details: The HTTP status code audit shows only 6 distinct status codes used across routes (200, 201, 400, 401, 404, 429). Error responses are plain strings like "Invalid input" or "Unauthorized". There is no error code system (like INVALID_CONFIG, SERVICE_NOT_FOUND, LICENSE_EXPIRED). Fix: define a canonical error code enum in src/utilities/errors.js, return { error: { code: "SERVICE_NOT_FOUND", message: "..." } } in all error responses. This makes API integration programmable (consumers switch on code, not parse message). Effort: ~3 hr.
  • impact: API consumers can handle errors programmatically. Required for SDK generation and good DX.

DC-087: No API client SDK / type definitions

  • status: pending
  • details: There is no TypeScript definitions file (.d.ts) or client SDK. Anyone integrating against the API has to read the source code to understand request/response shapes. Fix: (1) Generate TypeScript types from the OpenAPI spec (once DC-062 updates it) using openapi-typescript. (2) Ship a @dashcaddy/api-types npm package or include a types/index.d.ts in the repo. (3) Optionally, a thin JS client wrapper. Effort: ~2 hr (after DC-062).
  • impact: Developer adoption. A typed SDK lowers the barrier to integration.

DC-088: No log rotation — error.log grows forever

  • status: pending
  • details: The logger has basic rotation (rename to .1 when it hits a size limit), but only keeps ONE rotated file. In production, error.log can grow rapidly during incident bursts. There's no retention policy, no compression, no date-based rotation. Fix: (1) Add a max-size threshold (e.g., 10MB) and keep N rotated files (e.g., 5). (2) Compress rotated files with gzip. (3) Add date-based naming so logs are greppable by date. (4) Add a GET /api/v1/system/logs endpoint so operators can view recent logs without SSH. Effort: ~1.5 hr.
  • impact: Prevents disk fill during incident storms. Makes logs accessible without SSH access.

DC-089: No rate limit on public license activation endpoint

  • status: pending
  • details: The rate limiter skip list includes req.path === '/api/v1/license/status' and req.path.startsWith('/api/v1/license/feature/') — meaning license checks bypass rate limiting. While these are GET endpoints, the license activation endpoint (POST /api/v1/license/activate) should have its own dedicated rate limit to prevent brute-force license key guessing. Fix: add a dedicated licenseLimiter with tighter limits (e.g., 10 attempts per 15 min per IP) on POST /license/activate. Effort: ~30 min.
  • impact: Prevents license key brute-forcing. Pro keys follow a predictable format (DC-XXX-XXXXX-XXXXXX) making them guessable without rate limiting.

DC-090: Node.js version drift — Dockerfile says 20, host runs 22

  • status: pending
  • details: Dockerfile uses FROM node:20-alpine (floating). The development machine runs Node v22.22.3. The container uses whatever node:20-alpine resolves to at build time. This version drift can cause "works on my machine" bugs (especially around fetch(), crypto, and structuredClone which changed between 20 and 22). Fix: (1) Pin the exact version: FROM node:20.10.0-alpine3.19. (2) Add .nvmrc or engines field to package.json specifying the minimum version. (3) Optionally upgrade to Node 22 across the board. Effort: ~30 min.
  • impact: Reproducible builds. No surprise behavior from Node version drift.

DC-091: No dependency update automation (Dependabot/Renovate)

  • status: pending
  • details: Dependencies are updated manually. The 21 production dependencies and 4 dev dependencies can fall behind silently. There's no automated PR for security patches or major version bumps. Fix: add either GitHub Dependabot config (.github/dependabot.yml) or Renovate config (renovate.json). Schedule weekly checks. Group minor/patch updates into one PR. Keep major updates separate for review. Effort: ~30 min.
  • impact: Security patches arrive automatically. No more manual npm audit sessions.

DC-092: No health check for DashCaddy's own dependencies (disk space, memory)

  • status: pending
  • details: The Dockerfile has a HEALTHCHECK that hits /health, but that endpoint only checks if the Express server responds. It doesn't check: disk space (if /app/data is on a full disk), memory pressure (Node heap near limit), Docker socket connectivity (if Docker daemon is down), Caddy admin API reachability. Fix: extend the health endpoint to include dependency checks: { diskSpace: { free: ..., total: ... }, memory: { heapUsed: ..., heapTotal: ..., rss: ... }, docker: { reachable: true/false }, caddy: { reachable: true/false } }. Return 503 if any critical dependency is down. Effort: ~1.5 hr.
  • impact: Catch systemic issues before they become outages. External monitoring can alert on 503.

DC-093: Workflow engine has no retry/backoff for failed actions

  • status: pending
  • details: When the workflow engine's health-check action fails, it logs the failure and moves on — no retry. If a service is temporarily down and recovers in 30s, the workflow reports it as failed for the entire 15-min cycle. Fix: add configurable retry logic to workflow actions (e.g., retry 2 times with 30s backoff before reporting failure). Also add a maxRetries config to the health-check workflow. Effort: ~1.5 hr.
  • impact: Fewer false-positive alerts. More resilient monitoring.

DC-094: No audit trail for config changes (who changed what, when)

  • status: pending
  • details: The audit logger (src/security/audit-logger.js) captures POST/PUT/DELETE events, but config changes (services.json, health-config.json, config.json) are made via file writes, not API calls. There's no record of who changed a service URL, disabled a health check, or modified a workflow. Fix: (1) Route all config mutations through API endpoints that log to the audit trail. (2) Add a GET /api/v1/system/audit-log endpoint for viewing the trail. (3) Include a diff of what changed in each audit entry. Effort: ~2 hr.
  • impact: Accountability. When something breaks, you can trace who changed the config and when.

P4 — Advanced Features

DC-095: No multi-user support — single-admin only

  • status: pending
  • details: DashCaddy has one admin user. For teams or homelab groups, there's no way to add a second admin or a read-only viewer. Fix: (1) Add a users.json with role-based access (admin, editor, viewer). (2) Add user management endpoints. (3) Add per-service permissions (editor can manage services but not billing). This is a significant feature, not a quick fix. Effort: ~6 hr.
  • impact: Multi-admin is a requirement for team/enterprise adoption.

DC-096: No API key management (create/revoke/scoped keys)

  • status: pending
  • details: API authentication uses session cookies or TOTP. There's no way to create scoped API keys for automation (e.g., a read-only key for monitoring, a key that can only manage one service). Fix: add POST /api/v1/api-keys (create with scopes), GET /api/v1/api-keys (list), DELETE /api/v1/api-keys/:id (revoke). Store hashed in credentials.json. Effort: ~2 hr.
  • impact: Enables automation and third-party integrations without sharing the admin password.

DC-097: No Prometheus / Grafana metrics export

  • status: pending
  • details: There's a basic /metrics endpoint, but it returns JSON, not Prometheus format. Fix: (1) Add prom-client dependency. (2) Instrument key metrics: HTTP request duration histogram, active WebSocket connections, health check pass/fail counter, container count gauge, API error rate. (3) Expose GET /metrics in Prometheus exposition format alongside the existing JSON endpoint. (4) Ship a Grafana dashboard JSON as a reference. Effort: ~2 hr.
  • impact: Industry-standard observability. Drop-in Grafana dashboard for operators.

DC-098: No changelog / release notes generation

  • status: pending
  • details: Releases are tracked via git commits and VERSION file, but there's no user-facing changelog. For a public product, customers need to know what changed between versions. Fix: (1) Add a CHANGELOG.md following Keep a Changelog format. (2) Auto-generate from conventional commits (if adopted) or git log. (3) Display "What's new" on the dashboard after updates. Effort: ~1.5 hr.
  • impact: Customer trust. Users won't update without knowing what changed.

DC-099: No automated database migration system

  • status: pending
  • details: Config migrations exist (src/config/migrations.js) but are ad-hoc. As the data schema evolves (new fields in services.json, config.json), there's no versioned migration system. Fix: (1) Add a schemaVersion field to config files. (2) Create a migration runner that applies migrations sequentially on startup. (3) Log each migration. (4) Support rollback on failure. Effort: ~2 hr.
  • impact: Safe upgrades. No more manual config patching after updates.

DC-100: No service discovery / auto-detect running containers

  • status: pending
  • details: Services are added manually by specifying URLs. DashCaddy doesn't auto-detect running Docker containers and suggest adding them as services. Fix: (1) Scan docker ps for containers with exposed ports. (2) Match against known app templates (Plex, Sonarr, etc.). (3) Show a "Detected services" panel with one-click add. (4) Periodically re-scan for new containers. Effort: ~3 hr.
  • impact: Zero-config onboarding. New users see their services auto-discovered.

Summary by Priority

Priority Count Effort Theme
P0 3 (DC-062064) ~7 hr Public release blockers
P1 5 (DC-065069) ~7 hr Reliability & code quality
P2 6 (DC-070075) ~5.5 hr Polish & DX
P2.5 5 (DC-081085) ~15 hr Security hardening (deep audit)
P3 5 (DC-076080) ~16 hr Future growth
P3.5 9 (DC-086094) ~14.5 hr Operational maturity
P4 6 (DC-095100) ~16.5 hr Advanced features
Total 39 ~81.5 hr