interface / server.py
Browser-facing FastAPI proxy on port 8080. Handles auth, streaming, file I/O, log tailing, voice forwarding.
Endpoints
| Method · Path | Purpose |
|---|---|
| GET / | Auth gate (?key=…). Renders index.html and injects the API key into a script. |
| GET /sw.js | Service worker for offline / PWA. |
| WS /ws | Persistent websocket — log line broadcast. |
| POST /api/chat | Streaming or non-streaming chat. Returns task_id for streams. |
| GET /api/chat/resume/{task_id} | Resume after disconnect at offset. |
| POST /api/chat/cancel/{task_id} | Cancel an in-flight task. |
| POST /api/workspace/save · /load | Zip download / upload of state. |
| POST /api/upload · GET /api/download/{path} | File I/O against the agent. |
| POST /api/stt | Audio → text via the Raspberry Pi voice server. |
| POST /api/tts | Text → audio stream via the Raspberry Pi voice server. |
Streaming model
- Per-task state dict keyed by UUID in global
active_chat_tasks. - Background worker spawned on POST /api/chat with
stream=true, sharing a pooledhttpx.AsyncClient. - The worker reads upstream chunks and appends bytes to
task["buffer"]; anasyncio.Eventsignals new data. - The stream generator yields by chunk index (not byte index) and clears the event before draining to avoid races.
- Truncation is detected via
task["truncated"]and surfaces as an SSE error marker. - Resume endpoint accepts an offset for reconnection.
- A janitor task evicts done tasks past 10-min TTL or hard cap of 200.
Buffer & payload limits
- Per-task stream buffer cap 50 MB (
GHOST_INTERFACE_STREAM_CAP). - Request body: max 500 chat messages.
- Upload body: 100 MB hard cap via
_read_capped_upload(); 64 KB chunks; rejects non-bytes.
Log streaming
A log_streamer() task spawns tail -f on the agent log and broadcasts to connected websockets. Detects ERROR/Exception and tags is_error for client reactivity. Subprocess is cleaned up with timeout-then-kill on shutdown.
Auth
GHOST_API_KEYmandatory; the module fails to import without it.- All state-mutating endpoints depend on
verify_interface_key()which comparesX-Ghost-Key. - The page itself is gated by a query parameter check (browsers send keys in URL on first load only).
- CORS:
allow_origins=*,allow_credentials=False.
Concurrency
- asyncio throughout; one shared
httpx.AsyncClientpool. - FastAPI
Depends(verify_interface_key)for auth. - StreamingResponse with custom generator for back-pressure.
- Background tasks for upstream polling + log streaming.
Static assets
interface/static/ holds index.html (three.js, marked, dompurify, mermaid, papaparse, chart.js via CDN), app.js (auth wrapper that injects X-Ghost-Key on every /api/ fetch, voice queue management, monologue display, renderer iframe), style.css (cyan/magenta cyber theme), matrix_graph.js (three.js particle system), image_face.js, cyber_face.png, and sw.js (service worker).