# Nginx site config for overlord.snakedesert.se # # Lives on the host (not in the Docker stack) at: # /etc/nginx/sites-enabled/overlord # # This file is the source-of-truth copy committed to git. To deploy a change: # 1. Edit this file in the repo # 2. SSH to the host # 3. sudo cp /home/erik/MosswartOverlord/nginx/overlord.conf /etc/nginx/sites-enabled/overlord # 4. sudo nginx -t && sudo nginx -s reload # # Critical settings: # - proxy_read_timeout / proxy_send_timeout 1d on /websocket/ and / # WebSockets are long-lived; nginx's default 60s timeout drops idle clients. # Removing these timeouts caused all plugin connections to drop every # ~60s when no data flowed from backend to client (April 2026 incident). # - Bearer token in /grafana/ proxy_set_header is a Grafana service account # token used for anonymous panel embeds. Rotate when credentials leak. server { listen 443 ssl; server_name overlord.snakedesert.se; # Security hardening server_tokens off; add_header X-Frame-Options SAMEORIGIN always; add_header X-Content-Type-Options nosniff always; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header Referrer-Policy strict-origin-when-cross-origin always; add_header Permissions-Policy "geolocation=(), camera=(), microphone=()" always; # SSL certificates ssl_certificate /etc/letsencrypt/live/overlord.snakedesert.se/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/overlord.snakedesert.se/privkey.pem; include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # Plugin WebSocket ingest — `/ws/position` upstream location /websocket/ { proxy_pass http://tracker/ws/position; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header X-Plugin-Secret $http_x_plugin_secret; proxy_cache_bypass $http_upgrade; # Long-lived WebSocket: don't time out the proxy proxy_read_timeout 1d; proxy_send_timeout 1d; } # Overlord Agent — host-side service running OUTSIDE the Docker stack # because it shells out to `claude` which depends on host-side # ~/.claude credentials. Long timeout because agent calls can spin # while Claude Code chains tool invocations. location /api/agent/ { proxy_pass http://127.0.0.1:8767/agent/; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_pass_request_headers on; # Heavy tool calls (cross-char search, suitbuilder) can take a while; # the python wrapper caps each turn at 240s, so 300s gives some # headroom for the round trip. proxy_read_timeout 300s; proxy_send_timeout 300s; } # API endpoints (live, trails, history, stats) — short-lived HTTP location /api/ { proxy_pass http://tracker/; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_cache_bypass $http_upgrade; } # Frontend UI and browser WebSocket (`/ws/live` upstream) location / { proxy_pass http://tracker/; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_cache_bypass $http_upgrade; # Long-lived browser WebSocket (/ws/live): don't time out proxy_read_timeout 1d; proxy_send_timeout 1d; } # Grafana Dashboard UI (served under /grafana) location /grafana/ { proxy_pass http://grafana; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Authorization "Bearer glsa_AcDTcN5CUX9h5Bi2ipmVAs6g1FRTSIWk_8b81cf99"; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_cache_bypass $http_upgrade; } }