# Opal-Web Deployment Guide ## Overview This guide covers deploying the Opal Task Manager PWA to production on `opal.example.com` with Caddy as the web server. ## Prerequisites - Server with Caddy installed - Domain: `opal.example.com` (DNS configured) - Authentik OAuth configured at `auth.example.com` - Opal Go API server binary built ## Architecture ``` Browser → Caddy (opal.example.com) ├─ / → Static PWA (SvelteKit build) └─ /api/* → Go API Server (:8080) ``` ## Step 1: Build the Frontend ### Local Build ```bash cd opal-web # Install dependencies if needed bun install # Build for production bun run build # Output will be in: build/ ``` The build creates a static site ready to deploy. ## Step 2: Deploy Static Files ### Option A: Direct Copy ```bash # On your local machine rsync -avz --delete build/ user@server:/var/www/opal/ ``` ### Option B: Server-Side Build ```bash # On the server cd /opt/opal-web git pull bun install bun run build cp -r build/* /var/www/opal/ ``` ### Set Permissions ```bash sudo chown -R www-data:www-data /var/www/opal sudo chmod -R 755 /var/www/opal ``` ## Step 3: Configure Backend ### Create Environment File ```bash sudo mkdir -p /etc/opal sudo nano /etc/opal/opal.env ``` Add: ```bash # Server SERVER_ADDR=:8080 # Database OPAL_DB_PATH=/var/lib/opal/opal.db # OAuth (from Authentik setup) OAUTH_ENABLED=true OAUTH_ISSUER=https://auth.example.com/application/o/opal/ OAUTH_CLIENT_ID=your_client_id_from_authentik OAUTH_CLIENT_SECRET=your_client_secret_from_authentik OAUTH_REDIRECT_URI=https://opal.example.com/auth/callback # JWT (generate with: openssl rand -hex 32) JWT_SECRET=your_generated_jwt_secret_here JWT_EXPIRY=3600 # Refresh Token REFRESH_TOKEN_EXPIRY=604800 ``` ### Create SystemD Service ```bash sudo nano /etc/systemd/system/opal-api.service ``` ```ini [Unit] Description=Opal Task API Server After=network.target [Service] Type=simple User=opal Group=opal WorkingDirectory=/var/lib/opal EnvironmentFile=/etc/opal/opal.env ExecStart=/usr/local/bin/opal server start --addr :8080 --db /var/lib/opal/opal.db Restart=always RestartSec=5 # Security hardening NoNewPrivileges=true PrivateTmp=true ProtectSystem=strict ProtectHome=true ReadWritePaths=/var/lib/opal [Install] WantedBy=multi-user.target ``` ### Setup Database and User ```bash # Create user sudo useradd -r -s /bin/false opal # Create directories sudo mkdir -p /var/lib/opal sudo chown opal:opal /var/lib/opal sudo chmod 750 /var/lib/opal # Build and install binary cd opal-task go build -o opal main.go sudo cp opal /usr/local/bin/ sudo chmod 755 /usr/local/bin/opal # Generate first API key (optional - for CLI access) sudo -u opal opal server keygen --name "Admin CLI" --db /var/lib/opal/opal.db # Save the generated key! # Start service sudo systemctl daemon-reload sudo systemctl enable opal-api sudo systemctl start opal-api sudo systemctl status opal-api ``` ## Step 4: Configure Caddy ### Create Caddyfile ```bash sudo nano /etc/caddy/Caddyfile ``` Add: ```caddy opal.example.com { # Root directory for static PWA root * /var/www/opal # API proxy handle /api/* { uri strip_prefix /api reverse_proxy localhost:8080 } # Static file serving with SPA fallback handle { try_files {path} /index.html file_server } # Security headers header { # HTTPS only Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" # Prevent clickjacking X-Frame-Options "SAMEORIGIN" # XSS protection X-Content-Type-Options "nosniff" # CSP (adjust as needed) Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' https://auth.example.com;" } # Logging log { output file /var/log/caddy/opal.log format json } # Compression encode gzip } ``` ### Test and Reload Caddy ```bash # Test configuration sudo caddy validate --config /etc/caddy/Caddyfile # Reload sudo systemctl reload caddy # Check status sudo systemctl status caddy ``` ## Step 5: Verify Deployment ### Check Services ```bash # API server sudo systemctl status opal-api sudo journalctl -u opal-api -n 50 # Caddy sudo systemctl status caddy sudo journalctl -u caddy -n 50 ``` ### Test Endpoints ```bash # Health check curl https://opal.example.com/api/health # OAuth login URL curl https://opal.example.com/api/auth/login # Static files curl -I https://opal.example.com/ ``` ### Browser Testing 1. Navigate to `https://opal.example.com` 2. Should see the Opal login page 3. Click "Login with OAuth" 4. Should redirect to Authentik 5. After login, should redirect back to task list 6. Test creating a task 7. Test PWA install prompt (mobile/desktop) ## Step 6: Update Deployment (Future) ```bash # Frontend update cd opal-web git pull bun install bun run build rsync -avz --delete build/ server:/var/www/opal/ # Backend update cd opal-task git pull go build -o opal main.go scp opal server:/tmp/ ssh server "sudo systemctl stop opal-api && sudo cp /tmp/opal /usr/local/bin/ && sudo systemctl start opal-api" ``` ## Troubleshooting ### API Not Responding ```bash # Check if running sudo systemctl status opal-api # Check logs sudo journalctl -u opal-api -f # Test locally curl http://localhost:8080/health ``` ### OAuth Not Working - Verify redirect URI in Authentik matches exactly - Check OAuth client ID and secret in env file - Ensure JWT secret is set - Check Caddy is proxying /api/* correctly ### PWA Not Installing - Ensure HTTPS is working - Check manifest.json is accessible - Verify service worker is registered (Browser DevTools → Application) - Icons must be served correctly ### Static Files 404 - Check file permissions in /var/www/opal - Verify Caddy root directive points to correct path - Ensure index.html exists in root ## Monitoring ### Logs ```bash # API logs sudo journalctl -u opal-api -f # Caddy logs sudo tail -f /var/log/caddy/opal.log # System logs dmesg | grep opal ``` ### Database Backup ```bash # Backup script #!/bin/bash BACKUP_DIR="/var/backups/opal" DATE=$(date +%Y%m%d_%H%M%S) mkdir -p "$BACKUP_DIR" sudo -u opal sqlite3 /var/lib/opal/opal.db ".backup '$BACKUP_DIR/opal_$DATE.db'" # Keep last 7 days find "$BACKUP_DIR" -name "opal_*.db" -mtime +7 -delete ``` Add to crontab: ```bash 0 2 * * * /usr/local/bin/backup-opal.sh ``` ## Security Checklist - ✅ HTTPS enforced (Caddy auto) - ✅ OAuth2 authentication configured - ✅ JWT tokens with expiry - ✅ API key hashing (bcrypt) - ✅ SystemD service hardening - ✅ Database file permissions (750) - ✅ Security headers (CSP, HSTS, etc.) - ✅ Regular backups configured - ⬜ Fail2ban for API rate limiting (optional) - ⬜ Log monitoring/alerting (optional) ## Performance Tips - Caddy handles compression and HTTP/2 - Service worker caches static assets - API responses cached by service worker - Consider CDN for static assets (future) ## Support - Check logs first - Verify all environment variables - Test OAuth flow in incognito - Check network tab in DevTools