When config.json schema changes between versions, register a migration
function in src/config/migrations.js. On startup, loadSiteConfig() detects
the stored version, runs all migrations forward, and writes the result back.
Users never see the migration — it runs silently and the rest of the app
only ever sees the current schema.
Includes:
- v0 → v1: normalize dns from string to object
- v1 → v2: add dns.provider field (default 'technitium')
- Forward compat: configs from future versions left untouched
- Idempotent: re-running on already-migrated config is a no-op
- Safe: no user data is removed during migration
21 unit tests covering edge cases: null input, forward compat, corrupt
JSON, missing parent dirs, idempotency, full migration chain.
Cross-platform hardening — removes all hardcoded /app/ paths from route files
and routes them through platform-paths.js so the app works the same way
regardless of Docker layout (single-file mount vs consolidated data dir).
Changes:
- platform-paths.js: add generatedCertsDir, pkiDir, containerUpdatesDir,
containerFrontendDir, containerAssetsDir, resolveAssetsPath()
- self-updater.js: UPDATE_URL/MIRROR_URL/CHANNEL env var overrides
- routes/ca.js: use platformPaths for cert paths and generated certs dir
- routes/services.js: use platformPaths.pkiRootCert
- routes/themes.js: derive THEMES_DIR from platformPaths.servicesFile
- routes/config/assets.js + backup.js: use resolveAssetsPath() fallback
- routes/services.js + src/app.js: use platformPaths.pkiRootCert
- server.js: HOST env var support, parse PORT as int
- src/app.js: GET /api/v1/version (public, no auth), global request timeout,
disable x-powered-by, trust proxy
- pylon/dashcaddy-pylon.js: PYLON_HOST env var, graceful shutdown on SIGTERM/SIGINT
A fresh user can now deploy with a custom Docker layout (e.g. /opt/dc/data/
as a single volume mount) and the app finds its files automatically, no env
var configuration required.
- deploy.js: wrap logError/notification in try/catch so they never mask the original deploy error
- deploy.js: use optional chaining for error.message access
- logging.js: safeErrorMessage handles null/undefined error gracefully
- dns-providers/: adapter base class + registry with auto-discovery
- technitium.js: wraps existing Technitium API calls into adapter interface
- cloudflare.js: Cloudflare API v4 adapter (zones, records, credentials)
- rfc2136.js: RFC 2136 dynamic DNS via nsupdate (BIND, PowerDNS, etc.)
- manual.js: no-op adapter for external DNS management with instructions
- provider-dns.js: provider-aware DNS context, resolves active adapter from config
- Universal helper methods: universalCreateRecord/Delete/ResolveRecord
- All 7 route files updated to use universal methods instead of raw dns.call()
- Setup wizard: provider dropdown (Technitium, Cloudflare, RFC 2136, Manual)
- DNS template selector: added Cloudflare and External/Manual options
- Config schema: validates dns.provider field
- Capability gating on Technitium-specific endpoints (logs, restart, update)
- Backward compatible: no provider set = auto-detect (technitium if dns.ip exists)
- openClawRoutes was mounted at root causing /status vs /openclaw/status mismatch
- ctx.docker is a typed wrapper {client,pull,...} — all calls now use docker.client.*
- templates/deploy/removal/restore sub-routers had /apps/ hardcoded in inner routes
causing double-stacking when mounted under /apps (→ /apps/apps/templates etc)
- openclaw.js: GET /status, POST /deploy, GET/POST /proxy/*, DELETE /
- Set Domain=.sami on session + CSRF cookies so browsers send them to all subdomains
- This fixes Caddy forward_auth returning 401 for radarr/sonarr/prowlarr
- Fix login URL concatenation bug (radarr.samilogin -> radarr.sami/login)
- Fix getSetCookie() missing from _httpsFetch/_httpFetch response objects
- Fix array/string handling for set-cookie header in session-handlers fallback
- Refactor csrf-protection to createCSRFMiddleware() factory with cookieDomain support
- Pass renewCSRFToken through middleware deps chain to TOTP route
- Remove legacy /api/ mount; all routes now under /api/v1/ only
- Update path matchers (CSRF excludes, public routes, audit log, rate limits)
- Move standalone routes (/api/network/ips, /api/docs, /api/docs/spec) to v1
- Update openapi.yaml (110 paths), CA pages, and 4 lingering frontend files
- Add LICENSE (proprietary EULA), CHANGELOG.md (Keep a Changelog format)
- Add .gitea/workflows/ci.yml (test+lint and security audit jobs)
- Fix 9 pre-existing no-empty lint errors so CI starts green
- Drop ad-hoc scratch reports and *.bak files from repo root
All 739 jest tests pass. Lint is clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- monitoring.js: Added log dependency, replaced console.log with log.warn
- themes.js: Added log dependency, replaced console.error with log.error
- src/app.js: Pass log to monitoringRoutes and themesRoutes
This fixes error messages being lost to stdout instead of proper log 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>
The lightweight probe endpoint used by the dashboard for live status
checks had no Pylon integration. When DNS2 (Singapore) tried to probe
home network services directly, all probes timed out with 502. Now
falls back to the configured Pylon relay before the domain fallback.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The modular refactor changed function signatures to destructured deps but
left internal ctx.* references intact, causing "ctx is not defined" errors
on /api/config, /api/logo, and many other endpoints. Also implements
loadTotpConfig and saveTotpConfig which were left as stubs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Create src/config/paths.js for all file paths and env vars
- Create src/config/site.js for site configuration loading
- Create src/config/index.js as unified config export
- Prepare for server.js modularization (Phase 2.1)
Part of deslopification roadmap: break 1997-line server.js into layers