docs: README updated for Overlord Agent — host-side service, security stack, deploy flow
Adds an AI Assistant section covering: - Architecture (host-side vs Docker, dedicated overlord-agent user) - The 12 MCP tools available to the model - 10-layer security stack (cookie auth, rate limit, audit log, allowed/disallowed tools, settings.json deny, system prompt, SQL parser, RO PG role, systemd hardening, dedicated UID) - Full file inventory under agent/ - Routing via nginx /api/agent/* - Cost / quota notes (subscription auth, reactive only) Plus features-section blurb, agent env vars table (/etc/overlord/agent.env), and deploy-flow recipes (code-only restart, requirements update, unit file change, first-time install).
This commit is contained in:
parent
0633865598
commit
6de4bfe03e
1 changed files with 113 additions and 8 deletions
119
README.md
119
README.md
|
|
@ -17,6 +17,7 @@ all driven by WebSocket events from the companion [MosswartMassacre](https://git
|
||||||
- [WebSocket Contract](#websocket-contract)
|
- [WebSocket Contract](#websocket-contract)
|
||||||
- [HTTP API Reference](#http-api-reference)
|
- [HTTP API Reference](#http-api-reference)
|
||||||
- [Frontend](#frontend)
|
- [Frontend](#frontend)
|
||||||
|
- [AI Assistant (Overlord Agent)](#ai-assistant-overlord-agent)
|
||||||
- [Database Schema](#database-schema)
|
- [Database Schema](#database-schema)
|
||||||
- [Operations & Health](#operations--health)
|
- [Operations & Health](#operations--health)
|
||||||
- [Contributing](#contributing)
|
- [Contributing](#contributing)
|
||||||
|
|
@ -38,7 +39,7 @@ The frontend is a React + Vite app served at `/` with a live map, draggable wind
|
||||||
│ WebSocket /ws/position (authenticated)
|
│ WebSocket /ws/position (authenticated)
|
||||||
▼
|
▼
|
||||||
┌────────────────────────────────────────────────────────┐
|
┌────────────────────────────────────────────────────────┐
|
||||||
│ dereth-tracker (FastAPI) │
|
│ dereth-tracker (FastAPI, Docker) │
|
||||||
│ • main.py — WS routing, analytics, broadcasts │
|
│ • main.py — WS routing, analytics, broadcasts │
|
||||||
│ • idle/death detection → Discord webhook │
|
│ • idle/death detection → Discord webhook │
|
||||||
│ • combat stats delta/lifetime accumulation │
|
│ • combat stats delta/lifetime accumulation │
|
||||||
|
|
@ -49,18 +50,29 @@ The frontend is a React + Vite app served at `/` with a live map, draggable wind
|
||||||
▼ ▼ ▼
|
▼ ▼ ▼
|
||||||
┌──────────┐ ┌──────────────────┐ ┌──────────────────┐
|
┌──────────┐ ┌──────────────────┐ ┌──────────────────┐
|
||||||
│ Browsers │ │ inventory-svc │ │ Discord bot │
|
│ Browsers │ │ inventory-svc │ │ Discord bot │
|
||||||
│ (React) │ │ (FastAPI) │ │ (rare monitor) │
|
│ (React) │ │ (FastAPI, Docker)│ │ (rare monitor) │
|
||||||
└──────────┘ └────────┬─────────┘ └──────────────────┘
|
└────┬─────┘ └────────┬─────────┘ └──────────────────┘
|
||||||
|
│ ▼
|
||||||
|
│ ┌──────────────┐
|
||||||
|
│ │ inventory-db │
|
||||||
|
│ └──────────────┘
|
||||||
|
│
|
||||||
|
│ /api/agent/* (host-side, OUTSIDE Docker)
|
||||||
▼
|
▼
|
||||||
┌──────────────┐
|
┌────────────────────────────────────────┐
|
||||||
│ inventory-db │
|
│ overlord-agent (FastAPI, systemd) │ ← runs as dedicated unprivileged user
|
||||||
└──────────────┘
|
│ • shells out to `claude -p ...` │ /var/lib/overlord-agent home,
|
||||||
|
│ • MCP server: live-state Q&A tools │ strict settings, no /home/erik
|
||||||
|
└────────────────────────────────────────┘
|
||||||
|
|
||||||
┌──────────────┐
|
┌──────────────┐
|
||||||
│ dereth-db │ ← TimescaleDB (telemetry, spawns, rares, portals)
|
│ dereth-db │ ← TimescaleDB (telemetry, spawns, rares, portals)
|
||||||
└──────────────┘
|
└──────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
All services run via Docker Compose.
|
Most services run via Docker Compose. **`overlord-agent` is host-side**
|
||||||
|
(systemd) because it shells out to the `claude` CLI which depends on
|
||||||
|
host-side credentials — see [AI Assistant](#ai-assistant-overlord-agent).
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
|
|
@ -86,6 +98,12 @@ All services run via Docker Compose.
|
||||||
- Plugin broadcasts its own vitals and consumes peer vitals
|
- Plugin broadcasts its own vitals and consumes peer vitals
|
||||||
- In-game `DxHud` overlay shows peer health/stamina/mana bars with direction arrows
|
- In-game `DxHud` overlay shows peer health/stamina/mana bars with direction arrows
|
||||||
|
|
||||||
|
### AI Assistant
|
||||||
|
- 🤖 chat window in the dashboard backed by `claude -p` running headless on the server
|
||||||
|
- Read-only access to live game state via 12 MCP tools (live players, inventory cross-search, combat stats, quests, suitbuilder, read-only SQL, etc.)
|
||||||
|
- Per-browser persistent session, "New Chat" button, history rehydration on reload
|
||||||
|
- Hardened: dedicated unprivileged Linux user, systemd lockdown, strict tool whitelist, audit log, rate limit. See [AI Assistant section](#ai-assistant-overlord-agent) for the full security stack.
|
||||||
|
|
||||||
### Discord Integration
|
### Discord Integration
|
||||||
- **Rare Monitor Bot** — posts rares (split by common/great) to configured channels
|
- **Rare Monitor Bot** — posts rares (split by common/great) to configured channels
|
||||||
- **Death Alerts** — webhook to `#alerts` when a character's vitae goes from 0 → >0 (rate-limited to one per character per 5 min)
|
- **Death Alerts** — webhook to `#alerts` when a character's vitae goes from 0 → >0 (rate-limited to one per character per 5 min)
|
||||||
|
|
@ -149,6 +167,18 @@ All secrets go in `.env`:
|
||||||
| `ACLOG_CHANNEL_ID` | Discord channel ID for the rare bot's status/vortex messages |
|
| `ACLOG_CHANNEL_ID` | Discord channel ID for the rare bot's status/vortex messages |
|
||||||
| `MONITOR_CHARACTER` | Which character's chat the bot monitors |
|
| `MONITOR_CHARACTER` | Which character's chat the bot monitors |
|
||||||
|
|
||||||
|
The Overlord Agent has its own env file at `/etc/overlord/agent.env` (root:overlord-agent 0640) so it doesn't share the tracker's secrets:
|
||||||
|
|
||||||
|
| Variable | Purpose |
|
||||||
|
|---|---|
|
||||||
|
| `SECRET_KEY` | Same value as the tracker — validates browser session cookies |
|
||||||
|
| `AGENT_DB_DSN` | Read-only connection string `postgresql://overlord_agent_ro:<pw>@127.0.0.1:5432/dereth` |
|
||||||
|
| `TRACKER_URL` | Loopback to the tracker container (default `http://127.0.0.1:8765`) |
|
||||||
|
| `AGENT_RATE_MAX` | Per-user rate limit (default 60/hour) |
|
||||||
|
| `AGENT_RATE_WINDOW_S` | Rate-limit window in seconds (default 3600) |
|
||||||
|
| `AGENT_AUDIT_LOG` | Path to audit JSONL (default `/var/log/overlord-agent/audit.jsonl`) |
|
||||||
|
| `CLAUDE_TIMEOUT_S` | Max seconds per `claude -p` invocation (default 240) |
|
||||||
|
|
||||||
## Deploying Changes
|
## Deploying Changes
|
||||||
|
|
||||||
Live backend host: `overlord.snakedesert.se` (SSH user `erik`, key-based auth).
|
Live backend host: `overlord.snakedesert.se` (SSH user `erik`, key-based auth).
|
||||||
|
|
@ -187,6 +217,34 @@ ssh erik@overlord.snakedesert.se "cd /home/erik/MosswartOverlord && \
|
||||||
|
|
||||||
`BUILD_VERSION` is displayed in the sidebar of the live frontend. Format is CalVer: `YYYY.M.D.HHMM-gitshorthash`.
|
`BUILD_VERSION` is displayed in the sidebar of the live frontend. Format is CalVer: `YYYY.M.D.HHMM-gitshorthash`.
|
||||||
|
|
||||||
|
### Overlord Agent deploy
|
||||||
|
|
||||||
|
Code changes to `agent/` only:
|
||||||
|
```bash
|
||||||
|
ssh erik@overlord.snakedesert.se "cd /home/erik/MosswartOverlord && \
|
||||||
|
git pull --ff-only origin master && \
|
||||||
|
sudo systemctl restart overlord-agent"
|
||||||
|
journalctl -u overlord-agent -f # tail logs to verify
|
||||||
|
```
|
||||||
|
|
||||||
|
`agent/requirements.txt` changed (new pip deps):
|
||||||
|
```bash
|
||||||
|
ssh erik@overlord.snakedesert.se "cd /home/erik/MosswartOverlord && \
|
||||||
|
git pull --ff-only origin master && \
|
||||||
|
agent/.venv/bin/pip install -r agent/requirements.txt && \
|
||||||
|
sudo systemctl restart overlord-agent"
|
||||||
|
```
|
||||||
|
|
||||||
|
systemd unit changed:
|
||||||
|
```bash
|
||||||
|
ssh erik@overlord.snakedesert.se "cd /home/erik/MosswartOverlord && \
|
||||||
|
git pull --ff-only origin master && \
|
||||||
|
sudo cp agent/overlord-agent.service /etc/systemd/system/ && \
|
||||||
|
sudo systemctl daemon-reload && sudo systemctl restart overlord-agent"
|
||||||
|
```
|
||||||
|
|
||||||
|
First-time install: `bash agent/install.sh` — see `agent/README.md` for the full bootstrap procedure (creating the `overlord-agent` user, copying claude auth, granting filesystem access, populating `/etc/overlord/agent.env`).
|
||||||
|
|
||||||
## WebSocket Contract
|
## WebSocket Contract
|
||||||
|
|
||||||
### `/ws/position` (plugin → backend)
|
### `/ws/position` (plugin → backend)
|
||||||
|
|
@ -253,6 +311,53 @@ See `EVENT_FORMATS.json` for event schemas. Major HTTP endpoints:
|
||||||
### Classic v1 (preserved at `/classic`)
|
### Classic v1 (preserved at `/classic`)
|
||||||
The original vanilla JS frontend with element-pooling optimization is kept for fallback and reference.
|
The original vanilla JS frontend with element-pooling optimization is kept for fallback and reference.
|
||||||
|
|
||||||
|
## AI Assistant (Overlord Agent)
|
||||||
|
|
||||||
|
A draggable chat window in the dashboard (🤖 Assistant button). Powered by `claude -p` running headless on the server, with read-only access to live game state via an MCP server.
|
||||||
|
|
||||||
|
### Architecture
|
||||||
|
- **Host-side service** (`agent/`, systemd unit `overlord-agent`) runs OUTSIDE Docker because the `claude` CLI binary lives on the host (`/home/erik/.local/bin/claude`) and depends on host-side authentication credentials.
|
||||||
|
- **Dedicated UNIX user** (`overlord-agent`, system account, `/var/lib/overlord-agent` home, no shell) — kernel-level isolation from the operator's `erik` account. Cannot read `/home/erik/.claude`, `~/.ssh`, `.bash_history`, `.env`, etc.
|
||||||
|
- **MCP stdio server** (`agent/mcp_overlord.py`) exposes 12 tools that wrap the tracker's HTTP endpoints + read-only DB queries. Claude only sees these tools; no `Bash`, `Read`, `Write`, etc.
|
||||||
|
- **Frontend** (`AgentWindow.tsx`) — per-browser session UUID in localStorage, "New Chat" button, on-mount rehydration from `/agent/sessions/{id}/history`.
|
||||||
|
|
||||||
|
### MCP tools available to the assistant
|
||||||
|
`get_live_players`, `get_player_state`, `get_combat_stats`, `get_equipment_cantrips`, `get_inventory`, `get_inventory_search`, `search_items` (cross-character), `get_recent_rares`, `get_quest_status`, `get_server_health`, `query_telemetry_db` (read-only SQL via sqlglot parser + GRANT-SELECT-only PG role), `suitbuilder_search`. Plus `WebFetch(domain:acpedia.org)` for AC info lookups.
|
||||||
|
|
||||||
|
### Security stack (defense-in-depth)
|
||||||
|
1. **Cookie auth** on `/agent/ask` (same session cookie the tracker issues)
|
||||||
|
2. **Per-user rate limit** (60 req/h default) and **concurrency cap** (1 in-flight)
|
||||||
|
3. **JSONL audit log** at `/var/log/overlord-agent/audit.jsonl` (every prompt + result)
|
||||||
|
4. **CLI flags** — `--allowed-tools` (just our 12 MCP tools), `--disallowed-tools` (Bash, Write, Read, Edit, Agent, ToolSearch, Monitor, scheduling, Gmail/Drive/Calendar, etc.), `--permission-mode dontAsk`
|
||||||
|
5. **`/var/lib/overlord-agent/.claude/settings.json`** — strict deny rules (server-side only, NOT in repo)
|
||||||
|
6. **System-prompt scope rules** in `CLAUDE.md` — instruct the model not to probe, not to suggest workarounds
|
||||||
|
7. **SQL parser** (`sqlglot`) rejects any non-SELECT statement on `query_telemetry_db`
|
||||||
|
8. **Read-only PG role** `overlord_agent_ro` (GRANT SELECT only) — even a parser bypass can't mutate
|
||||||
|
9. **systemd hardening** — `ProtectSystem=strict`, `ProtectHome=read-only`, `InaccessiblePaths=/etc/shadow,/root,~/.ssh,…`, `NoNewPrivileges=true`, `CapabilityBoundingSet=` (empty), `PrivateTmp=true`, `PrivateDevices=true`, `RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6`, `SystemCallFilter=@system-service ~@privileged ~@reboot ~@mount`, `MemoryMax=512M`, `TasksMax=128`
|
||||||
|
10. **Secrets out of /home** — `/etc/overlord/agent.env` (root:overlord-agent 0640) for SECRET_KEY + AGENT_DB_DSN
|
||||||
|
|
||||||
|
### Files
|
||||||
|
|
||||||
|
| Path | What |
|
||||||
|
|------|------|
|
||||||
|
| `agent/service.py` | FastAPI app: `/agent/health`, `/agent/sessions/new`, `/agent/ask`, `/agent/sessions/{id}/history` |
|
||||||
|
| `agent/auth.py` | Session cookie validation (mirrors `main.py:1013-1019`) |
|
||||||
|
| `agent/claude_wrapper.py` | `asyncio.create_subprocess_exec("claude", "-p", …)` with allowed/disallowed-tools |
|
||||||
|
| `agent/tools.py` | Pure tool implementations |
|
||||||
|
| `agent/mcp_overlord.py` | MCP stdio server registering tools |
|
||||||
|
| `agent/sql/0001_overlord_agent_ro.sql` | Read-only PG role |
|
||||||
|
| `agent/overlord-agent.service` | systemd unit (the hardening directives) |
|
||||||
|
| `agent/install.sh` | venv + systemd setup |
|
||||||
|
| `agent/README.md` | Operator's deeper reference |
|
||||||
|
| `.mcp.json` (repo root) | Project-level MCP config Claude Code auto-loads |
|
||||||
|
| `CLAUDE.md` "Overlord Assistant Mode" section | System-prompt briefing |
|
||||||
|
|
||||||
|
### Routing
|
||||||
|
nginx forwards `/api/agent/*` to `127.0.0.1:8767` (the host-side service) with a 300s read/send timeout (suitbuilder runs can be slow). Other `/api/*` continues to the dereth-tracker container at `127.0.0.1:8765`.
|
||||||
|
|
||||||
|
### Cost / quota
|
||||||
|
Subscription auth (no API key); per-call cost is informational only. Each `/agent/ask` invocation = one `claude -p` subprocess with shared session cache. Reactive only — no background polling, no scheduled tasks.
|
||||||
|
|
||||||
## Database Schema
|
## Database Schema
|
||||||
|
|
||||||
### Telemetry DB (`dereth`, TimescaleDB)
|
### Telemetry DB (`dereth`, TimescaleDB)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue