Files
dashcaddy/CLAUDE.md
T
Hermes 4a66962f19
CI / Test & Lint (push) Has been cancelled
CI / Security audit (push) Has been cancelled
DC-008: add Linux deployment section to CLAUDE.md + fix stale version field
Inserts a comprehensive Linux (DNS2 / Contabo VPS) section between the existing Windows docs and the Project Info footer. The new section documents:
- Production paths (/opt/dashcaddy/, /var/www/dashcaddy-status/, /etc/dashcaddy/)
- Container mount points with the /app/data/ auto-resolve fallback
- The three-filesystem frontend trap (source vs live vs build-context)
- Common admin commands (Caddyfile reload, logs, rebuild, services.json)
- Windows-vs-Linux differences table
- Four Linux-specific gotchas (Caddy network_mode host, credentials.json perms, CORS_ORIGINS, TS_AUTHKEY)

Also corrects the stale 'Version: 1.0' field to current 1.13.4 and adds the Linux-side default TLD (.home). All existing Windows content preserved verbatim per the LITERAL COPY RULE.
2026-06-25 15:48:09 -07:00

13 KiB

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 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:

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):

# 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)

# 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.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)