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 · PathAuthBehaviour
GET / · HEAD /Returns plain text "Ollama is running". Compatibility shim.
GET /api/version{"version": "0.1.24"}.
POST /api/showOllama-compatible model metadata (family: qwen3, format: gguf).
GET /api/tagsLists models from app.state.args.model.
GET /v1/modelsX-Ghost-KeyOpenAI-style model list.
POST /api/generateX-Ghost-KeyOllama-style single-prompt completion. Internally rewritten to a chat-completion call. Streaming NDJSON or single JSON.
POST /chat · POST /v1/chat/completions · POST /api/chatX-Ghost-KeyMain 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/saveX-Ghost-KeyPackages chat history + scratchpad + sandbox into a streaming zip (64 KB chunks).
POST /api/workspace/loadX-Ghost-KeyRestores from uploaded zip (100 MB cap, zip-slip protection). Clears sandbox but preserves acquired_skills/.
POST /api/uploadX-Ghost-KeyUpload a file into the sandbox. Path validated (no .., no absolute paths). 100 MB cap.
GET /api/download/{filename:path}X-Ghost-KeyStream a sandbox file. Path traversal guard.
{any other path}X-Ghost-KeyCatch-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

client routes GhostAgent upstream LLM POST /api/chat handle_chat() stream completions tokens (SSE) cleaned chunks SSE response

Figure 9 — End-to-end chat request.