chore: dockerize api proxy and add gitignore

Add a Docker-based FastAPI proxy skeleton (charter §4) for parity across
dev / homelab VPS / fly.io, plus root .gitignore for Godot 4.7 and Python.

- api/Dockerfile: python:3.12-slim, non-root, PORT-aware CMD
- api/app/main.py: /health + five role endpoint stubs (§4)
- api/requirements.txt: fastapi/uvicorn/httpx/pydantic, pinned
- docker-compose.yml: local dev with live reload
- api/fly.toml: prod deploy stub with /health check
- api/.env.example: key lives in the proxy, never the client (§4)
- .gitignore: ignores .env, whitelists .env.example

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-09 11:23:27 -05:00
parent d54599e27d
commit d4ada9bedb
9 changed files with 201 additions and 0 deletions

0
api/app/__init__.py Normal file
View File

47
api/app/main.py Normal file
View File

@@ -0,0 +1,47 @@
"""FastAPI proxy entrypoint — the guarding proxy of charter §4.
Skeleton only: a health check plus the five role endpoints as stubs so the
container runs and the client has something to point at. Real prompt routing,
model selection, logging, and auth land later — all server-side (§4, §5).
"""
from fastapi import FastAPI
app = FastAPI(title="coc-rpg proxy", version="0.0.1")
@app.get("/health")
def health() -> dict:
"""Liveness probe for compose / fly.io."""
return {"status": "ok"}
# ── Role endpoints (charter §4) ──────────────────────────────────────────────
# The client knows these paths and nothing about which model or prompt serves
# them. Stubs for now; each will load its prompt from api/prompts/ and route to
# the configured model.
@app.post("/dm/narrate")
def narrate() -> dict:
return {"detail": "not implemented"}
@app.post("/dm/adjudicate")
def adjudicate() -> dict:
return {"detail": "not implemented"}
@app.post("/dm/improvise")
def improvise() -> dict:
return {"detail": "not implemented"}
@app.post("/npc/speak")
def npc_speak() -> dict:
return {"detail": "not implemented"}
@app.post("/party/banter")
def banter() -> dict:
return {"detail": "not implemented"}