P2-1: VERSION file 1.14.9→1.15.0 (matches package.json), CLAUDE.md 1.13.4→1.15.0 P2-2: git rm dashcaddy-api/scripts/legacy/comprehensive-test.js + test-security-fixes.js P2-3: .eslintrc.js add no-empty rule with allowEmptyCatch:true (3 errors→0) P2-4: routes/auth/session-handlers.js:39 fix no-useless-escape (\- → .- in char class) 1539/1539 tests pass. ESLint errors eliminated.
252 lines
12 KiB
Markdown
252 lines
12 KiB
Markdown
# 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/<app-name>/` for bind mount paths
|
|
- For CIFS volumes in docker-compose, use `//Sami-pc/e_share/dockerdata/...` as the device path
|
|
|
|
## CRITICAL: Production is on DNS2 (not this machine)
|
|
|
|
DashCaddy runs on **DNS2** (`100.121.150.22` via Tailscale / `194.233.88.206` public).
|
|
SSH in with: `ssh root@100.121.150.22`
|
|
|
|
### Production Layout on DNS2
|
|
|
|
```
|
|
/opt/dashcaddy/ # git repo (auto-updated)
|
|
├── dashcaddy-api/
|
|
│ ├── *.js # API server source
|
|
│ └── data/
|
|
│ ├── services.json # LIVE services list
|
|
│ ├── config.json # LIVE DashCaddy config
|
|
│ ├── dns-credentials.json # DNS API credentials
|
|
│ └── credentials.json # Encrypted app credentials
|
|
├── status/ # Dashboard frontend (built)
|
|
│ ├── index.html
|
|
│ ├── dist/ # Bundled JS (core/features/onboarding/init)
|
|
│ ├── js/ # Source JS (also served statically)
|
|
│ ├── css/
|
|
│ └── assets/
|
|
├── ca/ # DashCA static site
|
|
├── updates/ # Auto-updater staging + history
|
|
└── start.sh # Container launch script (run by @reboot cron)
|
|
```
|
|
|
|
### Docker Container
|
|
|
|
- **Name**: `dashcaddy-api`
|
|
- **Image**: `dashcaddy-dashcaddy-api:latest`
|
|
- **Port**: `127.0.0.1:3001` (Caddy proxies to it)
|
|
- **Started by**: `/opt/dashcaddy/start.sh` via root `@reboot` cron
|
|
|
|
Key container mounts:
|
|
| Container path | Host path |
|
|
|---|---|
|
|
| `/app/data/` | `/opt/dashcaddy/dashcaddy-api/data/` |
|
|
| `/app/assets` | `/opt/dashcaddy/status/assets` |
|
|
| `/caddyfile` | `/etc/caddy/Caddyfile` |
|
|
| `/app/backups` | `/opt/dashcaddy/backups` |
|
|
|
|
### Caddy
|
|
|
|
- **Config**: `/etc/caddy/Caddyfile` (git-guarded — edit then run `caddy-apply`)
|
|
- **Admin API**: `http://localhost:2019` (NOT 2021)
|
|
- **TLS storage**: `/var/lib/caddy/`
|
|
- **Static files**: Caddy serves `/opt/dashcaddy/status/` for `status.sami`
|
|
|
|
### Development Files (for editing)
|
|
|
|
```
|
|
e:/CaddyCerts/sites/
|
|
├── dashcaddy-api/ # API server source (NOT caddy-api/)
|
|
│ ├── server.js
|
|
│ ├── src/app.js # Express app factory
|
|
│ ├── routes/ # Route handlers
|
|
│ ├── middleware.js
|
|
│ └── ...
|
|
└── status/ # Dashboard frontend source
|
|
├── index.html # HTML template (~853 lines)
|
|
├── js/ # Source JS modules
|
|
├── css/
|
|
├── dist/ # Built output (run node build.js)
|
|
└── build.js # Build script (uses esbuild)
|
|
```
|
|
|
|
## When Making Changes
|
|
|
|
### To add/remove services from dashboard:
|
|
Edit `/opt/dashcaddy/dashcaddy-api/data/services.json` on DNS2 directly,
|
|
OR use the dashboard UI at `https://status.sami`.
|
|
|
|
### To modify Caddy reverse proxy rules:
|
|
```bash
|
|
ssh root@100.121.150.22
|
|
# Edit /etc/caddy/Caddyfile
|
|
caddy-apply "reason for change" # validates + reloads + git commits
|
|
```
|
|
|
|
### To modify API server code:
|
|
1. Edit `e:/CaddyCerts/sites/dashcaddy-api/` locally
|
|
2. `scp` changed files to `root@100.121.150.22:/opt/dashcaddy/dashcaddy-api/`
|
|
3. Rebuild container: `ssh root@100.121.150.22 "bash /opt/dashcaddy/start.sh"`
|
|
|
|
### To modify dashboard frontend:
|
|
1. Edit source in `e:/CaddyCerts/sites/status/js/` or `status/index.html`
|
|
2. Build: `cd e:/CaddyCerts/sites/status && node build.js`
|
|
3. Deploy: `scp -r dist/ index.html sw.js root@100.121.150.22:/opt/dashcaddy/status/`
|
|
|
|
### To modify DashCA:
|
|
Edit files in `e:/CaddyCerts/sites/ca/`, then:
|
|
1. Regenerate: `cd e:/CaddyCerts/sites/ca/scripts && bash generate-all.sh`
|
|
2. Deploy: `scp -r e:/CaddyCerts/sites/ca/* root@100.121.150.22:/opt/dashcaddy/ca/`
|
|
|
|
## DashCA - Certificate Authority Distribution
|
|
|
|
**Purpose**: One-click CA cert install page so *.sami domains are trusted on all devices.
|
|
**Access**: `https://ca.sami`
|
|
|
|
**Certificate Info:**
|
|
- **CN**: Sami Home Network Root CA
|
|
- **Algorithm**: ECDSA P-256 with SHA-256
|
|
- **Valid Until**: Dec 22, 2034
|
|
- **Fingerprint**: `08:98:A5:63:F5:A1:A2:58:5F:02:D7:A8:A2:54:87:E6:BC:33:96:21:29:0E`
|
|
|
|
**Certificate Source** (on DNS2):
|
|
- Root CA: `/etc/ssl/sami-ca/root.crt`
|
|
- Intermediate CA: auto-generated by Caddy at `/var/lib/caddy/pki/authorities/local/`
|
|
|
|
### API Endpoints
|
|
- `GET /api/ca/info` — certificate metadata
|
|
- `GET /api/health/ca` — CA expiration health (`healthy` / `warning` / `critical`)
|
|
|
|
## Key Services
|
|
|
|
| Service | Where | Port | Notes |
|
|
|---------|-------|------|-------|
|
|
| Caddy (HTTPS) | DNS2 | 443 | Reverse proxy |
|
|
| Caddy Admin | DNS2 | 2019 | Caddy API |
|
|
| DashCaddy API | DNS2 | 3001 | Dashboard backend (container) |
|
|
| Technitium DNS (primary) | DNS2 | 5380 | `100.121.150.22` |
|
|
| Technitium DNS (secondary) | DNS1 (this PC) | 5380 | `100.71.97.12` |
|
|
|
|
## SSO Architecture
|
|
|
|
`import dashcaddy_auth <serviceId>` in the Caddyfile expands to a `forward_auth` gate that:
|
|
1. Checks the DashCaddy TOTP session (cookie domain `.sami` — shared across all `*.sami`)
|
|
2. Injects credentials (API key, Basic Auth, app cookies) into upstream request headers
|
|
|
|
For client-side auto-login (chat, Plex, Jellyfin, Emby):
|
|
- Caddy redirects `path /` to `/dashcaddy-login`
|
|
- `/dashcaddy-login` proxies to `GET /api/v1/auth/login-page?service=<id>` on the API
|
|
- That page's JS fetches `/dashcaddy-api/api/auth/app-token/<id>` and stores the token in `localStorage`
|
|
|
|
## Common Mistakes to Avoid
|
|
|
|
1. **Wrong API source dir**: It's `dashcaddy-api/`, NOT `caddy-api/` (old name, no longer exists)
|
|
2. **Wrong services file**: Edit the one in `/opt/dashcaddy/dashcaddy-api/data/` on DNS2, not the dev copy
|
|
3. **Caddyfile edits without caddy-apply**: Always use `caddy-apply` — it validates, reloads, and git-commits
|
|
4. **Caddy admin port**: It's 2019, not 2021
|
|
5. **Frontend changes without build**: Edit JS source, then `node build.js`, then deploy `dist/`
|
|
6. **DNS2 Tailscale IP**: `100.121.150.22` (NOT the old `100.104.4.5` or `100.74.102.61`)
|
|
|
|
---
|
|
|
|
## 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<your-tld>` 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.15.0 (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`)
|
|
- **Repo**: `/opt/dashcaddy/` on DNS2 (git, auto-updated by self-updater)
|