api / routes.py
All HTTP routes mounted on the FastAPI app. Ollama-compatible plus Ghost-specific.
Auth
Every authed route validates the X-Ghost-Key header against request.app.state.args.api_key (which defaults to $GHOST_API_KEY or ghost-secret-123).
CORS
Wildcard origin (allow_origins=["*"]) with credentials disabled. No cookies — auth is header-only.
Endpoints
| Method · Path | Auth | Behaviour |
|---|---|---|
| GET / · HEAD / | — | Returns plain text "Ollama is running". Compatibility shim. |
| GET /api/version | — | {"version": "0.1.24"}. |
| POST /api/show | — | Ollama-compatible model metadata (family: qwen3, format: gguf). |
| GET /api/tags | — | Lists models from app.state.args.model. |
| GET /v1/models | X-Ghost-Key | OpenAI-style model list. |
| POST /api/generate | X-Ghost-Key | Ollama-style single-prompt completion. Internally rewritten to a chat-completion call. Streaming NDJSON or single JSON. |
| POST /chat · POST /v1/chat/completions · POST /api/chat | X-Ghost-Key | Main chat endpoint. Streaming SSE with X-Request-ID correlation header, or JSON when stream=false. Calls agent.handle_chat(body, background_tasks, request_id=...). Errors shaped as {"error": {"message", "type"}}. |
| POST /api/workspace/save | X-Ghost-Key | Packages chat history + scratchpad + sandbox into a streaming zip (64 KB chunks). |
| POST /api/workspace/load | X-Ghost-Key | Restores from uploaded zip (100 MB cap, zip-slip protection). Clears sandbox but preserves acquired_skills/. |
| POST /api/upload | X-Ghost-Key | Upload a file into the sandbox. Path validated (no .., no absolute paths). 100 MB cap. |
| GET /api/download/{filename:path} | X-Ghost-Key | Stream a sandbox file. Path traversal guard. |
| {any other path} | X-Ghost-Key | Catch-all proxy — forwards unknown requests upstream via agent.context.llm_client.http_client. Detects streaming from stream: true JSON body. Sets X-Accel-Buffering: no for SSE so nginx doesn't buffer. |
Sequence diagram
Figure 9 — End-to-end chat request.