MosswartOverlord/nginx/overlord.conf
Erik 4ae18536be feat(agent): cross-char search_items tool + bump timeouts
Adds an MCP tool wrapping the inventory-service /search/items endpoint
with include_all_characters=true, so questions like 'find me a bracelet
with Legendary Acid Ward on any unequipped char' resolve in ONE tool call
instead of looping get_inventory over 60+ chars (which timed out at 120s).

- agent/tools.py: search_items_global wrapper
- agent/mcp_overlord.py: register new tool with detailed schema doc
- agent/claude_wrapper.py: include in --allowed-tools whitelist;
  bump timeout 120s -> 240s
- nginx/overlord.conf: bump /api/agent/ proxy timeout 180s -> 300s
- CLAUDE.md: brief Claude to USE search_items for cross-char searches
2026-04-25 21:13:26 +02:00

116 lines
5.3 KiB
Text

# 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;
}
}