# DashCaddy Project Guidelines for AI Assistants ## HARD RULE: Docker Storage on E: Drive **ALL Docker container data, volumes, bind mounts, and app configs MUST use `E:/dockerdata/` via bind mounts or CIFS volumes. No exceptions.** - E: is a network share (`\\Sami-pc\e_share`) shared across all home network computers - The ONLY thing allowed on C: is the Docker Desktop WSL engine VHD (`C:/dockerdata/DockerDesktopWSL/`) — this is the absolute bare minimum WSL2 requires (local NTFS). WSL2 cannot create VHDs on network shares. - Keep C: Docker usage under 5GB - When deploying new containers, always use `E:/dockerdata//` for bind mount paths - For CIFS volumes in docker-compose, use `//Sami-pc/e_share/dockerdata/...` as the device path ## CRITICAL: Production vs Development Paths ### Production Files (LIVE - what actually runs) ``` C:/caddy/ ├── Caddyfile # Active Caddy configuration ├── services.json # Services shown on dashboard ├── dns-credentials.json # DNS API credentials ├── config.json # DashCaddy configuration └── sites/ └── status/ # Dashboard frontend files └── assets/ # Logos, fonts, icons ``` ### Development Files (for editing/testing) ``` e:/CaddyCerts/sites/ ├── caddy-api/ │ ├── server.js # API server source code │ ├── app-templates.js # Docker app templates (52+ apps) │ ├── services.json # DEV ONLY - not used in production! │ └── ... └── status/ └── index.html # Dashboard UI source ``` ## Docker Container Mount Points The `caddy-api` container mounts production files: | Container Path | Host Path (Production) | |----------------|------------------------| | `/app/services.json` | `C:/caddy/services.json` | | `/app/dns-credentials.json` | `C:/caddy/dns-credentials.json` | | `/caddyfile` | `C:/caddy/Caddyfile` | | `/app/assets` | `C:/caddy/sites/status/assets` | ## When Making Changes ### To add/remove services from dashboard: Edit `C:/caddy/services.json` (NOT e:/CaddyCerts/sites/caddy-api/services.json) ### To modify Caddy reverse proxy rules: Edit `C:/caddy/Caddyfile`, then reload via: ```bash curl -X POST http://localhost:2019/load -H "Content-Type: text/caddyfile" --data-binary @"C:/caddy/Caddyfile" ``` ### To modify API server code: Edit `e:/CaddyCerts/sites/caddy-api/server.js`, then: 1. Copy to production: `C:/caddy/sites/caddy-api/` 2. Restart container: `docker restart caddy-api` ### To modify app templates: Edit `e:/CaddyCerts/sites/caddy-api/app-templates.js` (Templates are loaded at runtime, changes require container restart) ### To modify dashboard UI: Edit `e:/CaddyCerts/sites/status/index.html` Copy to `C:/caddy/sites/status/` for production ### To modify DashCA (CA certificate distribution): Edit files in `e:/CaddyCerts/sites/ca/`, then: 1. Regenerate certificate formats: `cd e:/CaddyCerts/sites/ca/scripts && bash generate-all.sh` 2. Copy to production: `cp -r e:/CaddyCerts/sites/ca/* C:/caddy/sites/ca/` 3. Reload Caddy if Caddyfile changes were made ## DashCA - Certificate Authority Distribution **Purpose**: Provides a one-click installation page for the root CA certificate, allowing users to easily trust *.sami domains on any device. **Access**: https://ca.sami (or https://ca.yourdomain for other installations) ### File Locations **Development (for editing):** ``` e:/CaddyCerts/sites/ca/ ├── index.html # Landing page ├── root.crt, root.der # Certificate formats ├── root.mobileconfig # Apple profile ├── intermediate.crt # Intermediate CA ├── cert-info.json # Certificate metadata ├── scripts/ │ ├── install.ps1 # Windows installer │ ├── install.sh # Linux/macOS installer │ ├── generate-cert-info.js # Extract cert metadata │ ├── generate-mobileconfig.js # Generate Apple profile │ └── generate-all.sh # Regenerate all formats └── assets/ # Icons, logos ``` **Production (served by Caddy):** ``` C:/caddy/sites/ca/ ├── index.html ├── root.crt, root.der ├── root.mobileconfig ├── install.ps1, install.sh └── assets/ ``` ### Certificate Source Caddy's built-in PKI generates certificates at: - **Root CA**: `C:/caddy/certs/pki/authorities/local/root.crt` - **Intermediate CA**: `C:/caddy/certs/pki/authorities/local/intermediate.crt` **Certificate Info:** - **CN**: Sami Home Network Root CA - **Algorithm**: ECDSA P-256 with SHA-256 - **Valid Until**: Dec 22, 2034 (~10 years) - **Fingerprint**: `08:98:A5:63:F5:A1:A2:58:5F:02:D7:A8:A2:54:87:E6:BC:33:96:21:29:0E` ### Deployment DashCA is a **static site** (not Docker-based), deployed via the app selector: 1. Navigate to App Selector in dashboard 2. Find "DashCA" in Security category 3. Click Deploy 4. System automatically: - Creates `C:/caddy/sites/ca/` directory - Copies files from development directory - Generates certificate formats (DER, mobileconfig) - Adds ca.sami block to Caddyfile - Reloads Caddy configuration - Registers service in `services.json` ### Updating Certificates When Caddy's CA certificate is renewed (every ~10 years): ```bash # 1. Regenerate all certificate formats cd e:/CaddyCerts/sites/ca/scripts bash generate-all.sh # 2. Update fingerprint in installation scripts # Edit install.ps1 - update $ExpectedFingerprint # Edit install.sh - update EXPECTED_FP # 3. Copy to production cp -r e:/CaddyCerts/sites/ca/* C:/caddy/sites/ca/ # 4. Notify users via dashboard or email ``` ### API Endpoints - **GET /api/ca/info** - Returns certificate metadata (name, fingerprint, expiration, etc.) - **GET /api/health/ca** - Returns CA expiration health status - `healthy`: >90 days remaining - `warning`: 30-90 days remaining - `critical`: <30 days remaining ### Caddyfile Configuration DashCA's Caddyfile block (auto-generated on deployment): - **Root**: `C:/caddy/sites/ca` - **TLS**: Internal (uses Caddy's local CA) - **MIME Types**: Proper headers for .crt, .der, .mobileconfig, .ps1, .sh files - **SPA Fallback**: Rewrites non-file requests to /index.html - **Cache Control**: Certificates cached for 24h, HTML not cached ### Supported Platforms - **Windows**: PowerShell installer (installs to LocalMachine\Root store) - **macOS**: .mobileconfig profile or command-line installer - **Linux**: Shell installer (Debian, RedHat, Arch) - **iOS**: .mobileconfig profile (requires manual trust in Settings) - **Android**: Direct .crt download (installs as user certificate) ### Landing Page Features - Automatic OS detection - QR code for mobile access - Certificate info display (loaded from `/api/ca/info`) - Platform-specific installation instructions - Copy-to-clipboard for fingerprint and commands - Download links for all certificate formats ### Troubleshooting **Issue**: Certificate fingerprint mismatch during installation **Cause**: CA certificate was renewed **Solution**: Regenerate certificates and update fingerprints in install scripts **Issue**: *.sami sites still show warnings after CA install **Cause**: Browser may have cached the untrusted state **Solution**: Clear browser cache, restart browser, or visit site in incognito mode **Issue**: iOS doesn't trust certificate after profile install **Cause**: iOS requires manual trust enablement **Solution**: Settings → General → About → Certificate Trust Settings → Enable trust ## Key Services | Service | Port | Description | |---------|------|-------------| | Caddy (HTTPS) | 443 | Reverse proxy | | Caddy Admin | 2019 | Caddy API (note: NOT 2021) | | DashCaddy API | 3001 | Dashboard backend | | DNS2 (Primary) | 100.74.102.61:5380 | Technitium DNS | | DNS1 (Secondary) | 192.168.254.204:5380 | Technitium DNS | ## Common Mistakes to Avoid 1. **Wrong services.json**: The API container reads from `C:/caddy/services.json`, not the development copy 2. **Caddy admin port**: It's 2019, not 2021 (check with `netstat` if unsure) 3. **DNS server**: DNS2 (100.74.102.61) is PRIMARY, DNS1 is secondary 4. **Caddyfile not reloaded**: After editing, must POST to /load endpoint or restart Caddy --- ## Linux Deployment (DNS2 / Contabo VPS) The Windows path sections above describe the **SAMI-PC** deployment. DashCaddy also runs as a Docker container on Linux (DNS2 = `194.233.88.206` / Tailscale `100.121.150.22`). The Linux deployment uses a different layout driven by `start.sh` and `docker run` bind mounts. ### Production paths (Linux) ``` /opt/dashcaddy/ ├── dashcaddy-api/ # Built image source (rebuilt on update) │ ├── Dockerfile │ └── ... ├── status/ # Dashboard frontend SOURCE (build context) ├── credentials.json # Encrypted credentials (mounted to /app/data) ├── .encryption-key # AES key (mounted to /app/data) └── services.json # Live service list (mounted to /app/data) /var/www/dashcaddy-status/ # Dashboard frontend LIVE (served by Caddy) # Built bundle output from status/ — NOT the source # tree, NOT the docker build context /etc/dashcaddy/ └── Caddyfile # Active Caddy configuration /root/.dashcaddy/ # Per-user state, credentials backup, license ``` ### Container mount points (Linux) | Container path | Host path | |---|---| | `/app/data/credentials.json` | `/opt/dashcaddy/credentials.json` | | `/app/data/.encryption-key` | `/opt/dashcaddy/.encryption-key` | | `/app/data/services.json` | `/opt/dashcaddy/services.json` | | `/caddyfile` | `/etc/dashcaddy/Caddyfile` | Note: the app must auto-resolve both `/app/data/...` AND the older `/app/...` layout (where files mounted directly to `/app/`). The `credential-manager.js` and `crypto-utils.js` modules handle this fallback. This is intentional — fresh installs get `/app/data/`, legacy installs keep working without env-var overrides. ### Three-filesystem frontend trap (Linux) The dashboard frontend lives on **three** separate paths that get confused: 1. **Source** — `/opt/dashcaddy/status/` — what you edit 2. **Live** — `/var/www/dashcaddy-status/` — what Caddy serves to browsers 3. **Build context** — `/opt/dashcaddy/dashcaddy-api/` — what `docker build` uses Editing `/opt/dashcaddy/status/index.html` and restarting the container does **nothing** visible until you run the build (which writes to `/var/www/dashcaddy-status/`). Always rebuild + container-recreate together. See the `dashcaddy` skill § Deploy cycle for the exact sequence. ### Common commands (Linux) ```bash # Edit Caddyfile then reload (no restart needed) curl -X POST http://localhost:2019/load \ -H "Content-Type: text/caddyfile" \ --data-binary @/etc/dashcaddy/Caddyfile # View container logs docker logs dashcaddy-api --tail 200 # Rebuild + restart after API code change cd /opt/dashcaddy && git pull cd /opt/dashcaddy/dashcaddy-api && docker build -t dashcaddy-api:local . docker stop dashcaddy-api && docker rm dashcaddy-api # (then re-run the container with the mount table above) # Edit a service in the live list vi /opt/dashcaddy/services.json # live-reloaded by the watcher ``` ### Differences from Windows | Concern | Windows (SAMI-PC) | Linux (DNS2) | |---|---|---| | Drive letter | `C:/`, `E:/` | `/opt/`, `/etc/`, `/var/www/` | | Network share for state | `\\Sami-pc\e_share` | (none — all local) | | Docker engine | Docker Desktop on WSL2 | Docker Engine on host | | Backend admin | PowerShell | bash + curl | | Caddyfile reload | POST to `localhost:2019/load` | POST to `localhost:2019/load` (same) | | Caddy admin port | 2019 | 2019 | | Self-update | host-side PowerShell updater | host-side bash updater (`start.sh`) | | Tailscale | Same `100.x.x.x` magic DNS | Same | | DNS server | DNS2 (100.74.102.61) primary | DNS2 (100.121.150.22 / 194.233.88.206) — **is** the primary | ### Linux-specific gotchas - **Caddy needs `network_mode: host`** (or `--network host`) so it can bind :80 and :443 directly. Bridge mode + port mapping also works, but `network_mode: host` is simpler for a single-host setup. - **`credentials.json` permissions matter** — file mode `0600`, owned by the same UID the container runs as. If the host root creates it but the container runs as `node` (uid 1000), the API will fail to read it. Either `chown 1000:1000` or run the container as `--user 0`. - **Don't use `localhost` in the API's CORS_ORIGINS** — it conflicts with the Tailscale IP. Use the actual `https://dashcaddy` URL. - **Tailscale cert provisioning** — set `TS_AUTHKEY` in `/etc/dashcaddy/tailscale.env` (mode 0600) before first start. Without it, the magic DNS hostname will resolve but TLS will fail. --- ## Project Info - **Name**: DashCaddy - **Version**: 1.13.4 (current; CHANGELOG.md `[Unreleased]` tracks the next bump) - **Purpose**: Unified management for Docker + Caddy + DNS - **Local TLD (Windows)**: `.sami` - **Local TLD (Linux, DNS2)**: `.home` (default; configurable via `siteConfig.tld`)