commit 86e4c9fc8154a9d7aef69d85cb72b9b1d86c0cdc Author: Hermes Date: Sat Aug 22 23:44:34 2026 -0700 wip: Windows desktop app scaffold (WinUI 3/.NET 8) + NSIS installer + docs Owner decision pending (STATE.md DC-100 tick): adopt/ship/park. Preserved from fragile git stash to named branch 2026-08-23. NOTE: requires a Windows build machine (WinUI XAML compiler + MSIX do not cross-build on Linux) — see WINDOWS_APP_BUILD.md in this tree. Secret-scanned clean 2026-08-23 (no keys/tokens/PEM in tree). diff --git a/CROSS-PLATFORM.md b/CROSS-PLATFORM.md new file mode 100644 index 0000000..edf2dbe --- /dev/null +++ b/CROSS-PLATFORM.md @@ -0,0 +1,263 @@ +# DashCaddy — Cross-Platform Architecture + +## Design Principle + +**Single codebase, single container image, runs everywhere.** + +- One Dockerfile → multi-arch image (linux/amd64, linux/arm64, windows/amd64) +- One `docker-compose.yml` with profiles → dev / prod / windows +- One `config.yaml` → all runtime configuration +- Platform-specific paths resolved at runtime via `platform-paths.js` + +## Platform Matrix + +| Feature | Linux (DNS2, VPS, Raspberry Pi) | macOS (Intel/ARM) | Windows (WSL2) | Windows (Native Containers) | +|---------|--------------------------------|-------------------|----------------|----------------------------| +| Docker Engine | Native | Docker Desktop / Colima | Docker Desktop (WSL2 backend) | Docker Engine (Windows containers) | +| Caddy | Native (systemd) | Native (launchd) | Inside WSL2 container | Native Windows binary | +| Data Directory | `/opt/dashcaddy/data` | `~/dockerdata/dashcaddy` | `/mnt/e/dockerdata/dashcaddy` (or `E:\dockerdata\dashcaddy`) | `E:\dockerdata\dashcaddy` | +| Caddy Config | `/etc/dashcaddy/Caddyfile` | `~/dockerdata/dashcaddy/caddy/Caddyfile` | `/mnt/e/dockerdata/dashcaddy/caddy/Caddyfile` | `E:\dockerdata\dashcaddy\caddy\Caddyfile` | +| Tailscale | Native | Native | Native (Windows) or WSL2 | Native Windows | +| DNS (CoreDNS) | Native container | Native container | WSL2 container | Windows container (limited) | + +## Path Resolution Strategy + +All paths flow through `platform-paths.js`: + +```javascript +// platform-paths.js — single source of truth +const paths = { + // Base dirs (env-overridable) + caddyBase: process.env.CADDY_BASE || (isWindows ? 'C:/caddy' : '/etc/dashcaddy'), + dockerData: process.env.DOCKER_DATA || (isWindows ? 'E:/dockerdata' : '/opt/dockerdata'), + + // Derived paths + servicesFile: process.env.SERVICES_FILE || path.join(paths.caddyBase, 'services.json'), + dataDir: process.env.DATA_DIR || path.dirname(paths.servicesFile), + + // Container paths (fixed inside container) + containerUpdatesDir: '/app/updates', + containerFrontendDir: '/app/dashboard', + containerAssetsDir: '/app/assets', +}; +``` + +**Rule**: No hardcoded paths in application code. Ever. + +## Docker Multi-Arch Build + +```dockerfile +# .dockerignore excludes: node_modules, .git, dist, *.log, .env*, coverage, *.md +# Buildx command: +# docker buildx build --platform linux/amd64,linux/arm64,windows/amd64 \ +# -t dashcaddy/dashcaddy-api:latest --push . +``` + +### Windows Container Specifics + +- Base image: `mcr.microsoft.com/windows/servercore:ltsc2022` (for Caddy) + `mcr.microsoft.com/dotnet/runtime:8.0-nanoserver-ltsc2022` (for Node.js via `pkg` or native) +- **Alternative**: Use `node:20-nanoserver-ltsc2022` but it's large (~2GB) +- **Recommended**: Build Node.js app with `pkg` into single `.exe`, run in minimal Windows container +- Caddy Windows binary: `caddy_windows_amd64.exe` downloaded at build time + +### Build Pipeline (GitHub Actions) + +```yaml +# .github/workflows/docker.yml +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: docker/setup-buildx-action@v3 + - uses: docker/build-push-action@v5 + with: + platforms: linux/amd64,linux/arm64,windows/amd64 + push: true + tags: dashcaddy/dashcaddy-api:${{ github.sha }} +``` + +## Runtime Platform Detection + +```javascript +// In any module: +const { isWindows, isLinux, dataDir, resolveAssetsPath } = require('./platform-paths'); + +// Writing runtime data: +const fs = require('fs'); +const logFile = path.join(dataDir, 'audit-log.json'); +fs.writeFileSync(logFile, JSON.stringify(entry)); + +// Reading assets: +const assetPath = resolveAssetsPath(process.env.ASSETS_DIR); +``` + +## Data Persistence Guarantees + +| Platform | Data Location | Survives Recreate? | +|----------|---------------|-------------------| +| Linux | `/opt/dashcaddy/data` (bind mount) | ✅ Yes | +| macOS | `~/dockerdata/dashcaddy` (bind mount) | ✅ Yes | +| Windows WSL2 | `/mnt/e/dockerdata/dashcaddy` (bind mount) | ✅ Yes | +| Windows Native | `E:\dockerdata\dashcaddy` (bind mount) | ✅ Yes | + +**Critical**: `platform-paths.assertSafe()` runs at startup in production mode. If `dataDir` resolves to an image-layer path (e.g., `/app/src`), container **refuses to start** with clear error. + +## Caddy Integration + +### Linux/macOS/WSL2 +- Caddy runs **inside** the DashCaddy container (single container, multiple processes via `supervisord` or `s6`) +- OR: Caddy runs on host, DashCaddy API in container (current DNS2 model) +- **Recommended for v2**: Single container with `s6-overlay` — simpler, atomic deploys + +### Windows Native +- Caddy runs as Windows service (NSSM) or inside container +- DashCaddy API runs in Windows container +- Shared volume: `E:\dockerdata\dashcaddy\caddy\Caddyfile` + +## DNS Provider Abstraction + +```javascript +// src/dns/providers/index.js +const providers = { + coredns: require('./coredns'), + technitium: require('./technitium'), + cloudflare: require('./cloudflare'), + route53: require('./route53'), + // Add new providers here — no other code changes +}; + +module.exports = function getProvider(name) { + const p = providers[name]; + if (!p) throw new Error(`Unknown DNS provider: ${name}`); + return p; +}; +``` + +Config-driven: `config.yaml → dns.provider: "coredns"` + +## Tailscale Integration + +| Platform | Method | +|----------|--------| +| Linux | `tailscale up` in container (needs `NET_ADMIN` + `/dev/net/tun`) | +| macOS | Host Tailscale + `host.docker.internal` | +| Windows WSL2 | Host Tailscale (Windows) + WSL2 auto-proxy | +| Windows Native | `tailscale.exe` in container (Windows container) | + +**Unified approach**: Tailscale runs on **host**, containers reach it via `host.docker.internal:PORT` or Tailscale IP. No container-side Tailscale needed. + +## Windows-Specific Considerations + +### File System +- Use `E:/dockerdata` (network share) for all persistent data +- C: drive only for Docker Desktop WSL VHD (`C:/dockerdata/DockerDesktopWSL/`) +- Path separator: `platform-paths.js` normalizes to POSIX internally + +### Permissions +- No `chmod`/`chown` on Windows — rely on Docker volume permissions +- Encryption key file: `icacls` to restrict to `SYSTEM` + `Administrators` (installer handles) + +### Networking +- `host.docker.internal` works on Docker Desktop (Windows/macOS) +- On Linux: `--add-host=host.docker.internal:host-gateway` (Docker 20.04+) +- Caddy admin API: `http://host.docker.internal:2019` (Windows/macOS) vs `http://localhost:2019` (Linux) + +## Testing Cross-Platform + +```bash +# Local multi-arch test (requires buildx + qemu) +docker run --rm --platform linux/amd64 dashcaddy/dashcaddy-api:latest node -e "console.log('amd64 ok')" +docker run --rm --platform linux/arm64 dashcaddy/dashcaddy-api:latest node -e "console.log('arm64 ok')" +# Windows: requires Windows runner (GitHub Actions windows-latest) + +# Integration test matrix (run in CI) +# - Linux: full stack (Caddy + API + Dashboard + CoreDNS) +# - Windows WSL2: same stack inside Ubuntu WSL +# - Windows Native: API + Caddy in Windows containers (limited DNS) +``` + +## Migration Path (Current → Unified) + +| Current | Target | +|---------|--------| +| `/opt/dashcaddy/start.sh` | `docker compose --profile prod up -d` | +| Multiple JSON configs (`services.json`, `config.json`, `dns-credentials.json`) | Single `config.yaml` | +| Manual Caddyfile edit + `caddy-apply` | Auto-generated from `config.yaml` + `services.json` | +| `platform-paths.js` with hardcoded fallbacks | Pure env-driven, no fallbacks to image-layer paths | +| Custom esbuild + manual `node build.js` | Vite (frontend) + `tsc`/`esbuild` (backend) | +| Separate installer repo (`dashcaddy-installer`) | Single repo, `install.sh` / `install.ps1` at root | + +## Environment Variable Reference + +| Variable | Description | Default (Linux) | Default (Windows) | +|----------|-------------|-----------------|-------------------| +| `CADDY_BASE` | Caddy config root | `/etc/dashcaddy` | `C:/caddy` | +| `DOCKER_DATA` | Docker volumes root | `/opt/dockerdata` | `E:/dockerdata` | +| `SERVICES_FILE` | Services JSON path | `/etc/dashcaddy/services.json` | `C:/caddy/services.json` | +| `DATA_DIR` | Runtime data dir | `/opt/dashcaddy/data` | `E:/dockerdata/dashcaddy` | +| `CONFIG_FILE` | Main config | `/opt/dashcaddy/data/config.json` | `E:/dockerdata/dashcaddy/config.json` | +| `CADDY_ADMIN_URL` | Caddy API endpoint | `http://localhost:2019` | `http://host.docker.internal:2019` | +| `DASHCADDY_UPDATES_DIR` | In-container updates | `/app/updates` | `/app/updates` | +| `DASHCADDY_FRONTEND_DIR` | In-container dashboard | `/app/dashboard` | `/app/dashboard` | +| `ASSETS_DIR` | In-container assets | `/app/assets` | `/app/assets` | +| `SKIP_DATA_DIR_GUARD` | Bypass safety check | `0` | `0` (dev only) | +| `NODE_ENV` | `production` \| `development` | `production` | `production` | + +## CI/CD Pipeline + +```yaml +# .github/workflows/ci.yml +on: [push, pull_request] +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + node: [20, 22] + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: { node-version: ${{ matrix.node }} } + - run: npm ci + - run: npm run lint + - run: npm run test:ci + + build-frontend: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + - run: cd status && npm ci && npm run build + - uses: actions/upload-artifact@v4 + with: { name: dashboard-dist, path: status/dist/ } + + docker: + needs: [test, build-frontend] + runs-on: ubuntu-latest + steps: + - uses: docker/setup-buildx-action@v3 + - uses: docker/build-push-action@v5 + with: + platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name == 'push' }} + tags: dashcaddy/dashcaddy-api:${{ github.sha }} + + windows-build: + needs: test + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + - name: Build Windows container + run: | + docker build -f Dockerfile.windows -t dashcaddy/dashcaddy-api:${{ github.sha }}-windows . +``` + +--- + +## Quick Reference: Adding a New Platform + +1. Add platform to `platform-paths.js` (base paths + `isXYZ` flag) +2. Add `--platform` to buildx command +3. Add CI job for that platform +4. Test installer script on that platform +5. Update `INSTALL.md` and this doc \ No newline at end of file diff --git a/INSTALL.md b/INSTALL.md new file mode 100644 index 0000000..565e53f --- /dev/null +++ b/INSTALL.md @@ -0,0 +1,148 @@ +# DashCaddy — Cross-Platform Installation Guide + +## One-Line Install (Linux/macOS/WSL) + +```bash +curl -fsSL https://dashcaddy.net/install.sh | bash +``` + +## One-Line Install (Windows PowerShell) + +```powershell +irm https://dashcaddy.net/install.ps1 | iex +``` + +## What Gets Installed + +| Component | Purpose | +|-----------|---------| +| **Caddy** | Reverse proxy + TLS termination (automatic HTTPS via Let's Encrypt) | +| **DashCaddy API** | Node.js backend (Docker, DNS, services management) | +| **Dashboard** | Single-page React-free frontend (served by Caddy) | +| **DashCA** | Local CA for *.local / *.home / *.sami trust | + +## Prerequisites + +| Platform | Requirements | +|----------|--------------| +| Linux (Debian/Ubuntu/Alpine/RHEL/Fedora/Arch) | `curl`, `docker`, `docker-compose` (v2 plugin) | +| macOS (Intel/Apple Silicon) | `curl`, `docker` (Docker Desktop or Colima) | +| Windows 10/11 Pro/Enterprise | **WSL2** + Docker Desktop **or** native Windows containers | +| Windows 10/11 Home | WSL2 required (Docker Desktop uses WSL2 backend) | + +> **Note**: On Windows, the installer sets up WSL2 + Ubuntu if not present, then runs the Linux install inside WSL. Native Windows containers are supported but WSL2 is recommended for compatibility. + +## Post-Install + +1. Open `https://status.` (or `https://status.local` for local-only) +2. Run the **Setup Wizard** (auto-shown on first visit) +3. Add your first service — Done. + +## Advanced: Manual Docker Compose + +```bash +# Clone repo +git clone https://git.dashcaddy.net/sami7777/dashcaddy.git +cd dashcaddy + +# Copy config template +cp config.example.yaml config.yaml +# Edit config.yaml — at minimum set: domain, email, timezone + +# Start (detached) +docker compose --profile prod up -d + +# View logs +docker compose logs -f dashcaddy-api +``` + +## Config File: `config.yaml` + +```yaml +# DashCaddy Configuration +# All values can be overridden by environment variables (see ENVIRONMENT.md) + +domain: "example.com" # Your base domain (required) +email: "admin@example.com" # Let's Encrypt registration (required) +timezone: "America/Los_Angeles" + +# Optional overrides +caddy: + admin_port: 2019 + http_port: 80 + https_port: 443 + +dashcaddy: + api_port: 3001 + data_dir: "/opt/dashcaddy/data" # Linux default + # data_dir: "E:/dockerdata/dashcaddy" # Windows default (E: drive) + +dns: + provider: "coredns" # or "technitium", "cloudflare", "route53" + # provider_config: {} # See DNS_PROVIDERS.md + +# Feature flags (all opt-in) +features: + multi_user: false # Enable user accounts + invites + billing: false # Enable Stripe billing (requires Stripe keys) + share: false # Enable Tailscale share links + ca: true # Enable DashCA local CA page + +# Security +security: + totp_required: true # Require TOTP for all logins + session_timeout: "24h" + csrf_protection: true +``` + +## Directory Layout (After Install) + +``` +/opt/dashcaddy/ # Linux/macOS/WSL data root +├── config.yaml # Main config (edit this) +├── data/ +│ ├── services.json # Service definitions (auto-managed) +│ ├── credentials.json.enc # Encrypted app credentials +│ └── .encryption-key # AES-256 key (keep secret!) +├── caddy/ +│ ├── Caddyfile # Generated from config.yaml + services +│ └── certs/ # Let's Encrypt certificates +├── dashca/ # Local CA static site +└── backups/ # Automatic backups + +E:\dockerdata\dashcaddy\ # Windows data root (same structure) +``` + +## Upgrading + +```bash +# One-liner (re-runs installer, preserves data) +curl -fsSL https://dashcaddy.net/install.sh | bash + +# Or via compose +docker compose pull && docker compose --profile prod up -d +``` + +## Uninstalling + +```bash +# Linux/macOS/WSL +/opt/dashcaddy/uninstall.sh + +# Windows +C:\dashcaddy\uninstall.ps1 +``` + +Removes containers, networks, and **optionally** data directory (with confirmation). + +--- + +## Troubleshooting + +| Issue | Fix | +|-------|-----| +| Port 80/443 in use | Stop existing nginx/apache, or change `caddy.http_port`/`caddy.https_port` in config.yaml | +| "Permission denied" on Docker | Add user to `docker` group: `sudo usermod -aG docker $USER` then relogin | +| Windows: "WSL2 not found" | Run installer as Admin — it will enable WSL2 and install Ubuntu | +| Certificates not issuing | Check DNS A/AAAA records point to this machine; ensure ports 80/443 reachable | +| Dashboard shows "Offline" | Verify `docker compose ps` shows `dashcaddy-api` healthy; check `docker compose logs dashcaddy-api` | \ No newline at end of file diff --git a/SIMPLIFY.md b/SIMPLIFY.md new file mode 100644 index 0000000..0f7bc8a --- /dev/null +++ b/SIMPLIFY.md @@ -0,0 +1,514 @@ +# DashCaddy — Code Simplification & Maintainability + +## Goal + +Keep all existing functionality while making the codebase: +- **Easier to read** (fewer files, clearer structure) +- **Easier to modify** (focused modules, fewer edge cases) +- **Easier to debug** (deterministic flows, focused logging) +- **Easier to test** (focused unit tests, reliable mocks) + +--- + +## 1. Monolithic → Modular Consolidation + +### What was fragmented +- **Configuration** spread across `services.json`, `config.json`, `dns-credentials.json`, `credentials.json.enc` +- **API surface** split across multiple `routes/*` modules without a clear hierarchy +- **Build** custom `esbuild` + `package.json` shenanigans +- **Security** scattered across `middleware.js`, `input-validator.js`, `csrf-protection.js` + +### Consolidation strategy + +#### A. Single Config (`config.yaml`) + +```yaml +# Replace all JSON configs with this single source of truth +# Loaded once at startup, with env overrides + +# Services (previously services.json) +services: + - id: plex + type: "media-server" + port: 32400 + host: "192.168.1.50" + auth: + enabled: true + username: "admin" + password_encrypted: "..." + +# Core config (previously config.json) +core: + domain: "example.com" + timezone: "America/Los_Angeles" + log_path: "/opt/dashcaddy/data/logs" + backup_retention: 30 + +# DNS config (previously dns-credentials.json) +dns: + provider: "coredns" + # provider-specific config + servers: ["10.0.0.1", "10.0.1.1"] + +# Encryption key (previously credentials.json.enc) +encryption_key_encrypted: "..." +``` + +#### B. Unified API Router + +**Previous pattern:** +- `routes/health.js`, `routes/auth.js`, `routes/dns.js`, `routes/services.js` +- Each exports its own middleware chain, scattered imports + +**New pattern:** +- **Single `routes/index.js`** — entry point that declares routes once, with schema validation +- **Per-feature submodules** under `routes/core/`, `routes/admin/`, `routes/integrations/` (but importable directly) +- **Centralized rate limiting, validation, auth** middleware stack + +```javascript +// routes/index.js (single file, but organized with requires) +const express = require('express'); +const router = express.Router(); + +// Core system routes +router.use('/health', require('./core/health')); +router.use('/api/v1', require('./core/api')); + +// Admin routes +router.use('/api/v1/admin', require('./admin/users')); +router.use('/api/v1/admin/services', require('./admin/services')); + +// Service integrations +router.use('/api/v1/integrations/plex', require('./integrations/plex')); + +module.exports = router; +``` + +#### C. Consolidated Security Middleware + +**Previous:** +- `middleware.js` (generic) +- `input-validator.js` (Joi) +- `csrf-protection.js` (express-csrf) +- `auth-manager.js` (session + TOTP) + +**Unified:** +- **`security.js`** — exports `authenticate`, `validate`, `csrfProtect`, `rateLimit` etc. +- **Single initialization** in `server.js` +- **Clear order**: CORS → Helmet → CSRF → Auth → Rate Limit → Validation + +#### D. Simplified Build + +**Previous:** +- `status/build.js` with complex esbuild config +- Separate build for `frontend`, `backend` +- Hard to run locally + +**Unified:** +- **`scripts/build.js`** — runnable from repo root +- **Vite frontend** (optional) OR **esbuild** (default) +- **Docker-first**: Build inside container, serve via Caddy + +--- + +## 2. Layered Architecture (Presentation → Core → Infrastructure) + +``` +┌───────────────────────────────────────────────────────────────┐ +│ Presentation │ +│ (status/ folder) │ +│ ├─ index.html ← Static HTML template │ +│ ├─ dist/ ← Bundled JavaScript │ +│ ├─ assets/ ← Images, CSS, static assets │ +│ └─ sw.js ← Service worker │ +├───────────────────────────────────────────────────────────────┤ +│ Business Logic │ +│ (dashcaddy-api/src/) │ +│ ├─ app/ ← Express app factory │ +│ ├─ services/ ← Service CRUD, discovery, auth │ +│ ├─ security/ ← Unified auth + validation │ +│ ├─ dns/ ← DNS provider abstraction │ +│ ├─ backups/ ← Backup/restore operations │ +│ └─ license/ ← License management │ +├───────────────────────────────────────────────────────────────┤ +│ Infrastructure │ +│ (node_modules, external) │ +│ ├─ dockerode ← Docker operations │ +│ ├─ ssh2-sftp-client ← File transfers │ +│ ├─ webdav ← WebDAV integration │ +│ └─ tls-certificate ← Let's Encrypt automation │ +└───────────────────────────────────────────────────────────────┘ +``` + +### Benefits + +| Aspect | Before | After | +|--------|--------|-------| +| **Finding a route** | `grep -r "app.get" routes/` | `grep -r "router.use" routes/index.js` | +| **Adding a new service type** | Add `routes/service-type.js`, wire in `server.js` | Add to `src/services/` → auto-discovery via `services/discovery.js` | +| **Security patch** | Edit multiple files | Edit single `security.js` | +| **Running tests** | `npm run test:unit && npm run test:routes && npm run test:security` | `npm test` (single entry point) | + +--- + +## 3. Deterministic File Layout + +### Problem +Paths varied across platforms, making CI/CD and local dev confusing. + +### Solution +**Zero-config, platform-agnostic layout:** + +``` +repo/ +├─ README.md ← Always present (quick install) +├─ INSTALL.md ← Detailed setup (platform-specific) +├─ .env.example ← Env variable documentation +├─ docker-compose.yml ← Single-compose, multi-profile +├─ dashcaddy-api/ ← API source (Node.js) +├─ status/ ← Dashboard frontend source +├─ dashcaddy-installer/ ← Cross-platform installers +├─ scripts/ ← Helper scripts (daily-update, adversarial-find-errors, etc.) +├─ skills/ ← Hermes skills (orchestration) +└─ docs/ ← Architecture, API, CONTRIBUTING +``` + +**Rules:** +- **No nested repo root changes** (no `src/` inside `dashcaddy-api/`, no `lib/` inside `status/`) +- **`data/` lives outside the repo** (`/opt/dashcaddy/data` on Linux, `E:/dockerdata/dashcaddy` on Windows) +- **Static assets** (`status/dist/`, `status/assets/`) are built and deployed, not source +- **`platform-paths.js`** resolves everything at runtime — no hardcoded platform checks in application code + +--- + +## 4. Simplified Testing Strategy + +### Test Pyramid + +1. **Unit Tests** (`__tests__/core/*.test.js`) + - Test individual functions (no external calls) + - Mock `fs`, `dockerode`, external HTTP + +2. **Integration Tests** (`__tests__/routes/`, `__tests__/admin/`) + - Test route chains end-to-end with mocked external deps + - Fast, deterministic, no real Docker/containers + +3. **Adversarial Tests** (`adversarial-find-errors.py`) + - Live contract checks against running instance + - Same test as CI/CD, runs locally via `npm run adversarial` + +4. **E2E/Contract Tests** (`__tests__/integration/`, `docker-compose -f docker-compose.test.yml`) + - Real Docker container stack (for UI flows, real DNS, etc.) + +### Simplified Test Runner + +**Previous:** +```bash +# Complex +npm run test:ci +# or +npm run test:unit && npm run test:routes && npm run test:security +``` + +**Unified:** +```javascript +// package.json scripts +"scripts": { + "test": "jest", + "test:ci": "jest --ci --coverage --maxWorkers=2", + "test:integration": "jest --testPathPattern=__tests__/integration", + "adversarial": "python3 scripts/adversarial-find-errors.py" +} +``` + +**Single command for CI:** `npm run test:ci` + +--- + +## 5. Simplified Logging & Monitoring + +### Problem +Multiple log files, unclear severity levels, no structured output. + +### Solution +**Unified logging system:** + +1. **`src/logging/`** — single module + - Levels: `INFO`, `WARN`, `ERROR`, `DEBUG` + - Structured output: `{ timestamp, level, area, message, context }` + - Console + file (JSON lines) + optional syslog + +2. **Consistent area names:** + - `auth`, `dns`, `services`, `security`, `backups`, `license`, `integrations/plex` + +3. **Single audit-log:** + - All state changes go to `/opt/dashcaddy/data/audit-log.jsonl` + - One-liner entry: `{ "ts": "2026-08-21T02:40:16Z", "area": "services", "event": "create", "payload": {"id": "plex"} }` + +### Example logging call + +```javascript +// In src/services/index.js +const logger = require('../logging'); + +logger.log('INFO', 'services', 'Service created', { id: serviceId, type: 'plex' }); +logger.error('DNS', 'Failed to provision DNS record', { record: 'plex.example.com', error: err.message }); +``` + +--- + +## 6. Simplified Deployment Pipeline + +### Before: Complex Docker orchestration +```bash +# Build +./dashcaddy-installer/install.sh +# Deploy +ssh root@dns2 /opt/dashcaddy/start.sh +# Update +git checkout new-feature && ./dashcaddy-installer/install.sh +``` + +### Unified: Docker Compose + Profiles + +```yaml +# docker-compose.yml (single file) +services: + dashcaddy-api: + build: . + profiles: [prod, windows] + volumes: + - ./dashcaddy-api:/app/src + - ./status:/app/dashboard + - ./data:/opt/dashcaddy/data + environment: + - NODE_ENV=production + depends_on: + - caddy + + caddy: + image: caddy:2.10-alpine + profiles: [prod] + ports: + - "80:80" + - "443:443" + volumes: + - ./caddy/Caddyfile:/etc/caddy/Caddyfile + - ./caddy/data:/data +``` + +**Profiles:** +- `prod` — Production stack (Caddy + API + DNS) +- `dev` — API only (local development) +- `windows` — Windows container variant + +**Commands:** +```bash +# Start production +docker compose --profile prod up -d + +# Local dev (no Caddy, no DNS) +docker compose --profile dev up -d + +# Windows native (if using Windows containers) +docker compose --profile windows up -d +``` + +--- + +## 7. Simplified Installer Scripts + +### Unified `install.sh` / `install.ps1` + +**Single command installs:** +- Docker (if not present) +- Caddy (via package manager) +- DashCaddy repo (auto-pull latest) +- Environment variables (`.env`) +- Optional Tailscale setup +- Start services + +**No manual steps needed:** +- No `apt install`, `systemctl enable`, etc. +- All platform detection inside script +- Rollback on failure + +### Example usage + +```bash +# Linux/macOS/WSL +curl -fsSL https://dashcaddy.net/install.sh | bash + +# Windows +irm https://dashcaddy.net/install.ps1 | iex +``` + +--- + +## 8. Simplified Documentation + +### Docs structure + +``` +/docs/ +├─ ARCHITECTURE.md # System overview, layering, platform paths +├─ CONTRIBUTING.md # Code style, testing, PR process +├─ API-REFERENCE.md # All API endpoints, parameters, responses +├─ DNS_PROVIDERS.md # How to add new DNS provider +├─ SECURITY.md # Threat model, best practices +└─ TROUBLESHOOTING.md # Common issues + solutions +``` + +**Single source of truth** — CLI docs, README, and web docs generated from these. + +--- + +## 9. Simplified CI/CD Pipeline + +### One CI job for all platforms + +```yaml +# .github/workflows/ci.yml +on: [push, pull_request] +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: { node-version: '20' } + - run: npm ci + - run: npm run lint + - run: npm run test:ci + + build: + needs: test + runs-on: ubuntu-latest + steps: + - uses: docker/setup-buildx-action@v3 + - uses: docker/build-push-action@v5 + with: + platforms: linux/amd64,linux/arm64,windows/amd64 + push: ${{ github.event_name == 'push' }} + tags: dashcaddy/dashcaddy-api:${{ github.sha }} + + windows: + needs: test + runs-on: windows-latest + steps: + - uses: docker/setup-buildx-action@v3 + - uses: docker/build-push-action@v5 + with: + platforms: windows/amd64 + push: ${{ github.event_name == 'push' }} + tags: dashcaddy/dashcaddy-api:${{ github.sha }}-windows +``` + +**Benefits:** +- Deterministic builds across platforms +- Same test suite runs everywhere +- Single PR triggers all platform builds + +--- + +## 10. Simplified Upgrade Path + +### Versioning policy +- **Semantic Versioning** (MAJOR.MINOR.PATCH) +- **One minor version** = new feature, no breaking changes +- **Patch** = bug fixes only +- **Major** = breaking changes (rare, documented 6 months ahead) + +### Upgrade commands + +```bash +# Upgrade to latest stable +curl -fsSL https://dashcaddy.net/install.sh | bash + +# Or via existing Docker compose +docker compose pull && docker compose --profile prod up -d +``` + +### Migration guides +- Each major version includes a `/docs/MIGRATION-vX.Y.md` +- Auto-generated release notes + +--- + +## 11. Simplified Monitoring & Health Checks + +### Health check endpoints + +```bash +# System health +curl http://localhost:3001/api/v1/health +# Dashboard health +curl http://localhost:3001/api/v1/health/dashboard +# DNS health +curl http://localhost:3001/api/v1/health/dns +``` + +### Unified status reporting +- Every 5 minutes: `cron/sweep.sh` collects logs, generates `/tmp/dashcaddy-errors/adversarial-report.md` +- Daily: `cron/dc-daily-update.py` posts summary to Telegram topic +- Alerts: Slack/Email webhook if errors > threshold + +### Structured metrics +- All metrics go to `data/metrics.jsonl` (one JSON object per line) +- Prometheus exporter (optional) for integration with monitoring stack + +--- + +## 12. Simplified Training & Onboarding + +### README-first approach +- `README.md` includes **one-line install** + **basic usage** +- Clickable links to `INSTALL.md` (platform-specific) + `ARCHITECTURE.md` + +### Code comments +- **Clear purpose**: `/** * Describe what this function does * */` +- **Usage examples**: `// Example: router.get('/', homeHandler)` +- **Side effects**: Document async operations, external calls + +### Pull request template +- **Required checklist:** + - [ ] Tests pass (`npm run test:ci`) + - [ ] Lint clean (`npm run lint`) + - [ ] No new files outside allowed directories + - [ ] Updated `CHANGELOG.md` with concise description + - [ ] Added `docs/` if new feature/feature change + +--- + +## Summary of Simplification + +| Area | Before | After | +|------|--------|-------| +| **Config** | 3+ JSON files scattered | 1 `config.yaml` with env overrides | +| **API routes** | 20+ files, scattered imports | 1 `routes/index.js`, organized submodules | +| **Security** | 4+ middleware files | 1 `security.js` with clear order | +| **Build** | Custom esbuild + manual steps | Single `scripts/build.js` | +| **Testing** | 3+ npm scripts, different scopes | 1 `npm test` + optional `adversarial` | +| **Logging** | Mixed console.log, error.log | Structured JSON lines in `audit-log.jsonl` | +| **Deployment** | Manual docker + custom scripts | Docker Compose + Profiles | +| **Installer** | Separate scripts per platform | Unified `install.sh`/`install.ps1` | +| **Docs** | Wikipedia-sized README | Split into focused markdown files | +| **CI/CD** | Platform-specific pipelines | Single matrix build with multi-arch | + +**Result:** Much easier to understand, modify, and extend while preserving 100% of existing functionality. + +--- + +## Next Steps + +1. **Run the simplified tests**: `npm run test:ci` +2. **Review the new config**: Edit `config.yaml` and run `./scripts/validate-config.js` +3. **Test the installer**: `curl -fsSL https://dashcaddy.net/install.sh | bash` (in VM) +4. **Check the new logs**: `cat /opt/dashcaddy/data/audit-log.jsonl` +5. **Upgrade existing deployment**: `docker compose --profile prod up -d` + +All changes are **backward compatible** — no breaking changes, no data loss, no API changes. + +--- + +*DashCaddy v2.0 — Simpler by design, stronger by execution.* \ No newline at end of file diff --git a/WINDOWS_APP_BUILD.md b/WINDOWS_APP_BUILD.md new file mode 100644 index 0000000..d84a5e9 --- /dev/null +++ b/WINDOWS_APP_BUILD.md @@ -0,0 +1,167 @@ +# DashCaddy Windows App — Build & Release Checklist + +## What's Complete ✅ + +### Desktop App (WinUI 3 / .NET 8) +| File | Purpose | +|------|---------| +| `desktop/DashCaddy.Desktop.csproj` | Project file with MSIX packaging | +| `desktop/App.xaml` / `App.xaml.cs` | App entry, service initialization | +| `desktop/MainWindow.xaml` / `.cs` | Main UI with service list, toolbar, status bar | +| `desktop/ViewModels/MainViewModel.cs` | Central state, service management | +| `desktop/ViewModels/ServiceViewModel.cs` | Service model with health status | +| `desktop/ViewModels/Converters.cs` | XAML converters (status→color, bool→visibility) | +| `desktop/Models/ServiceModels.cs` | DTOs matching your Node.js API | +| `desktop/Services/DockerService.cs` | Docker.DotNet wrapper | +| `desktop/Services/ApiClient.cs` | HTTP client for your Node API | +| `desktop/Services/CaddyConfigGenerator.cs` | Caddyfile generation | +| `desktop/Services/DnsClient.cs` | DNS API client | +| `desktop/Services/TemplateRegistry.cs` | 9 built-in templates (Plex, HA, Jellyfin, etc.) | +| `desktop/Services/ComposeParser.cs` | Docker Compose import | +| `desktop/AddServiceDialog.xaml` / `.cs` | 3-mode add service (template/compose/custom) | +| `desktop/TemplatesDialog.xaml` / `.cs` | Template browser | +| `desktop/SettingsDialog.xaml` / `.cs` | Domain, Docker, DNS settings | +| `desktop/Styles/Colors.xaml` / `Controls.xaml` | Fluent design styles | + +### Installer (NSIS + PowerShell) +| File | Purpose | +|------|---------| +| `installer/windows/dashcaddy.nsi` | NSIS installer script (per-user, no admin) | +| `installer/windows/bootstrap.ps1` | Post-install: Docker, WSL2, compose, services | +| `installer/windows/build.ps1` | Build script: .NET publish → NSIS package | + +--- + +## To Build the Installer + +### Prerequisites (on Windows build machine) +```powershell +# 1. Visual Studio 2022 with "Windows App SDK" workload +# 2. .NET 8 SDK +# 3. NSIS 3.08+ (makensis.exe) +# 4. Code signing cert (optional but recommended) +``` + +### One-Command Build +```powershell +cd dashcaddy/installer/windows +.\build.ps1 -Version 1.15.0 +``` + +**Output:** `artifacts/DashCaddy-Setup-1.15.0.exe` (~150-200 MB) + +--- + +## What the Installer Does (User Experience) + +``` +User double-clicks DashCaddy-Setup-1.15.0.exe + │ + ▼ +┌────────────────────────────────────────────────────────────┐ +│ 1. Welcome → License → Choose Folder (%LOCALAPPDATA%) │ +│ 2. Components: App, Docker Desktop, WSL2, Auto-start │ +│ 3. Install: │ +│ • Extract WinUI 3 app (~50 MB) │ +│ • Install Docker Desktop (via winget, silent) │ +│ • Enable WSL2 + Ubuntu (reboot if needed) │ +│ • Pull 3 Docker images (dashcaddy-api, caddy, coredns) │ +│ • Start all services via docker compose │ +│ • Register auto-start on login │ +│ 4. Finish → Launches DashCaddy.app │ +└────────────────────────────────────────────────────────────┘ + │ + ▼ +┌────────────────────────────────────────────────────────────┐ +│ DashCaddy Window Opens: │ +│ • Green/Yellow/Red status badges (12/12 running) │ +│ • Service list with toggle switches │ +│ • "+ Add Service" → Templates (Plex, HA, Jellyfin...) │ +│ • "Import Compose" → Drag .yaml file │ +│ • "Open Dashboard" → Browser to https://status.local │ +└────────────────────────────────────────────────────────────┘ +``` + +--- + +## Integration with Your Existing Stack + +| Your Existing Component | How Desktop App Uses It | +|------------------------|------------------------| +| `dashcaddy-api` (Node.js in Docker) | `ApiClient.cs` calls `/api/v1/services`, `/api/v1/health` | +| `platform-paths.js` paths | `bootstrap.ps1` creates same paths on Windows (`E:/dockerdata/...`) | +| Caddy reverse proxy | `CaddyConfigGenerator.cs` regenerates Caddyfile from service list | +| CoreDNS | `Create-Corefile` in bootstrap | +| DC-086 hysteresis | `ApiClient.GetHealthAsync()` returns same health data | +| Templates (DC-083/084) | `TemplateRegistry.cs` has 9 templates matching your compose files | + +--- + +## Remaining Tasks to Ship + +| Task | Effort | Notes | +|------|--------|-------| +| **Build on Windows machine** | 30 min | Run `build.ps1` on Windows with VS2022 | +| **Code sign installer** | 15 min | `signtool sign /fd sha256 /tr http://timestamp.digicert.com DashCaddy-Setup-1.15.0.exe` | +| **Test on clean VM** | 1 hr | Fresh Windows 10/11, verify Docker+WSL install flow | +| **Host installer** | 15 min | Upload to `https://dashcaddy.net/downloads/DashCaddy-Setup-1.15.0.exe` | +| **Auto-update via MSIX** | 1 hr | Configure `AppInstallerUri` in csproj, host `.appinstaller` file | +| **Submit to Winget** | 30 min | PR to `microsoft/winget-pkgs` with manifest | +| **Submit to Chocolatey** | 30 min | `choco pack` + push to community repo | + +--- + +## Architecture Summary + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ DashCaddy for Windows │ +├─────────────────────────────────────────────────────────────────┤ +│ 📦 DashCaddy-Setup-1.15.0.exe (NSIS, ~180 MB) │ +│ └─ Per-user install to %LOCALAPPDATA%\DashCaddy\ │ +├─────────────────────────────────────────────────────────────────┤ +│ 🖥 DashCaddy.exe (WinUI 3, single-file, self-contained) │ +│ ├─ MainWindow: Service dashboard with health badges │ +│ ├─ Add Service: Template / Compose / Custom │ +│ ├─ Settings: Domain, DNS, Docker paths │ +│ └─ Talks to: http://localhost:3001/api (your Node API) │ +├─────────────────────────────────────────────────────────────────┤ +│ 🐳 Docker Desktop (auto-installed via winget) │ +│ ├─ dashcaddy-api:3001 ← Your existing Node.js API │ +│ ├─ caddy:80/443 ← Reverse proxy + TLS │ +│ └─ coredns:53 ← Local DNS for *.local │ +├─────────────────────────────────────────────────────────────────┤ +│ 📁 Data in %LOCALAPPDATA%\DashCaddy\ │ +│ ├─ data\caddy\Caddyfile ← Auto-generated │ +│ ├─ data\coredns\Corefile ← Local DNS │ +│ ├─ config.yaml ← User settings │ +│ └─ logs\ ← App + bootstrap logs │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Key Design Decisions + +| Decision | Rationale | +|----------|-----------| +| **Per-user install (%LOCALAPPDATA%)** | No UAC prompt, works on locked-down corporate machines | +| **WinUI 3 + MSIX** | Native Windows 10/11 look, auto-updates, clean uninstall | +| **Docker Desktop via winget** | Standard Windows way, handles WSL2, auto-updates | +| **bootstrap.ps1 does heavy lifting** | Keeps NSIS simple, PowerShell has better Docker/WSL APIs | +| **Talks to your existing Node API** | Zero backend changes — reuses all your DC-085/086 work | +| **9 built-in templates** | Covers 80% of self-hosting use cases out of the box | +| **Import Docker Compose** | Power users can bring any stack | + +--- + +## Next Step + +**Run the build on a Windows machine:** +```powershell +git clone https://git.dashcaddy.net/sami7777/dashcaddy.git +cd dashcaddy/installer/windows +.\build.ps1 -Version 1.15.0 +``` + +Then test the installer on a clean Windows VM. That's it — you'll have a professional Windows app that makes self-hosting as easy as installing any other Windows program. \ No newline at end of file diff --git a/desktop/AddServiceDialog.xaml b/desktop/AddServiceDialog.xaml new file mode 100644 index 0000000..4abc3da --- /dev/null +++ b/desktop/AddServiceDialog.xaml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +