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:
58
.gitignore
vendored
Normal file
58
.gitignore
vendored
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
# ─────────────────────────────────────────────
|
||||||
|
# Godot 4.7 (client/)
|
||||||
|
# ─────────────────────────────────────────────
|
||||||
|
# Editor + import cache (4.x replaced .import/ with .godot/)
|
||||||
|
.godot/
|
||||||
|
# Exported builds
|
||||||
|
/client/build/
|
||||||
|
export.cfg
|
||||||
|
# export_presets.cfg holds keystore paths / signing config — keep secrets out of history
|
||||||
|
export_presets.cfg
|
||||||
|
# Mono/C# (only if we drop to C# per charter §16)
|
||||||
|
.mono/
|
||||||
|
data_*/
|
||||||
|
*.mono/
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────
|
||||||
|
# Python / FastAPI (api/)
|
||||||
|
# ─────────────────────────────────────────────
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
*.egg-info/
|
||||||
|
.eggs/
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
# Virtual envs
|
||||||
|
.venv/
|
||||||
|
venv/
|
||||||
|
env/
|
||||||
|
ENV/
|
||||||
|
# Tooling caches
|
||||||
|
.pytest_cache/
|
||||||
|
.mypy_cache/
|
||||||
|
.ruff_cache/
|
||||||
|
.coverage
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────
|
||||||
|
# Secrets — the API key lives here in dev, NEVER in the client (charter §4)
|
||||||
|
# ─────────────────────────────────────────────
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────
|
||||||
|
# Editors / OS
|
||||||
|
# ─────────────────────────────────────────────
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────
|
||||||
|
# Logs (charter §4/§10 logs go to a store, not the repo)
|
||||||
|
# ─────────────────────────────────────────────
|
||||||
|
*.log
|
||||||
16
api/.dockerignore
Normal file
16
api/.dockerignore
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# Keep the build context small and secrets out of the image.
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
.venv/
|
||||||
|
venv/
|
||||||
|
.pytest_cache/
|
||||||
|
.mypy_cache/
|
||||||
|
.ruff_cache/
|
||||||
|
.coverage
|
||||||
|
htmlcov/
|
||||||
|
docs/
|
||||||
|
README.md
|
||||||
|
Dockerfile
|
||||||
|
.dockerignore
|
||||||
11
api/.env.example
Normal file
11
api/.env.example
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Copy to .env for local dev. NEVER commit the real .env. The client never sees
|
||||||
|
# any of these — keys live only in the proxy (charter §4).
|
||||||
|
|
||||||
|
# Where the proxy sends model calls in dev (Ollama on the homelab).
|
||||||
|
OLLAMA_BASE_URL=http://localhost:11434
|
||||||
|
|
||||||
|
# Prod model provider (charter §4). Leave blank in dev.
|
||||||
|
REPLICATE_API_TOKEN=
|
||||||
|
|
||||||
|
# Port the proxy binds. compose / fly.io override this.
|
||||||
|
PORT=8000
|
||||||
26
api/Dockerfile
Normal file
26
api/Dockerfile
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# /api — FastAPI proxy (charter §4). Docker-based for parity across
|
||||||
|
# dev / homelab VPS / fly.io.
|
||||||
|
FROM python:3.12-slim
|
||||||
|
|
||||||
|
# Faster, quieter Python in containers.
|
||||||
|
ENV PYTHONUNBUFFERED=1 \
|
||||||
|
PYTHONDONTWRITEBYTECODE=1 \
|
||||||
|
PIP_NO_CACHE_DIR=1
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install deps first so the layer caches when only app code changes.
|
||||||
|
COPY requirements.txt .
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
# App code.
|
||||||
|
COPY app ./app
|
||||||
|
|
||||||
|
# Run as non-root.
|
||||||
|
RUN useradd --create-home --uid 1000 appuser
|
||||||
|
USER appuser
|
||||||
|
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
# fly.io / compose set PORT; default to 8000 (charter localhost:8000).
|
||||||
|
CMD ["sh", "-c", "uvicorn app.main:app --host 0.0.0.0 --port ${PORT:-8000}"]
|
||||||
0
api/app/__init__.py
Normal file
0
api/app/__init__.py
Normal file
47
api/app/main.py
Normal file
47
api/app/main.py
Normal 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"}
|
||||||
21
api/fly.toml
Normal file
21
api/fly.toml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# fly.io deploy config for the proxy (charter §4, prod). Stub — set app name and
|
||||||
|
# region, then `fly deploy` from api/. Secrets (REPLICATE_API_TOKEN) go via
|
||||||
|
# `fly secrets set`, never in this file.
|
||||||
|
app = "coc-rpg-proxy" # TODO: claim a real app name
|
||||||
|
primary_region = "ord" # TODO: pick a region
|
||||||
|
|
||||||
|
[build]
|
||||||
|
dockerfile = "Dockerfile"
|
||||||
|
|
||||||
|
[http_service]
|
||||||
|
internal_port = 8000
|
||||||
|
force_https = true
|
||||||
|
auto_stop_machines = true
|
||||||
|
auto_start_machines = true
|
||||||
|
min_machines_running = 0
|
||||||
|
|
||||||
|
[[http_service.checks]]
|
||||||
|
method = "GET"
|
||||||
|
path = "/health"
|
||||||
|
interval = "15s"
|
||||||
|
timeout = "2s"
|
||||||
5
api/requirements.txt
Normal file
5
api/requirements.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# FastAPI proxy runtime deps. Pinned from first resolve; bump deliberately.
|
||||||
|
fastapi==0.139.0
|
||||||
|
uvicorn[standard]==0.51.0
|
||||||
|
httpx==0.28.1 # calling Ollama / Replicate
|
||||||
|
pydantic==2.13.4 # request/response contracts
|
||||||
17
docker-compose.yml
Normal file
17
docker-compose.yml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# Local dev — run the proxy in Docker with live reload.
|
||||||
|
# docker compose up --build
|
||||||
|
# Ollama runs on the homelab (charter §4), not here — point OLLAMA_BASE_URL at it.
|
||||||
|
services:
|
||||||
|
api:
|
||||||
|
build: ./api
|
||||||
|
ports:
|
||||||
|
- "8000:8000"
|
||||||
|
env_file:
|
||||||
|
- ./api/.env
|
||||||
|
environment:
|
||||||
|
PORT: 8000
|
||||||
|
# Mount source + reload so edits are live without a rebuild.
|
||||||
|
volumes:
|
||||||
|
- ./api/app:/app/app:ro
|
||||||
|
command: >
|
||||||
|
uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
|
||||||
Reference in New Issue
Block a user