SkateSpots/ai-proxy/DEPLOY.md
SkateSpots ec06a63b01 feat(ai-proxy): run the AI proxy on the CVM host, ready to switch over
Same proxy as netlify/functions/ai.mjs, rebuilt as a standalone Bun service on
the CVM host so AI calls stop spending Netlify compute/bandwidth credits and
the sats-paid Routstr key lives on our box.

- ai-proxy/server.ts: Bun.serve on 127.0.0.1:8787 — NIP-98 + Pro gate, model
  allowlist, per-IP throttle, /api/ai + /api/ai/status. Own tsconfig
  (typecheck:ai in npm test); eslint-ignored like cvm-server.
- ai-proxy/{package.json,.env.example,skatespots-ai.service,Caddyfile,DEPLOY.md}.
- scripts/deploy-ai-proxy.sh (npm run deploy:ai): scp + bun install + unit +
  restart, mirroring deploy-cvm.sh.
- Client: one place decides the AI base, src/lib/sasquatchAi.ts (also the NIP-98
  u tag). Defaults unchanged (/api/ai on Netlify); set VITE_SASQUATCH_AI_BASE to
  move it. Fully reversible.

Deployed and verified on the host: /api/ai/status returns the live Routstr
balance. Public TLS (DNS + Caddy) is the remaining step before switching the app
over, so no client behaviour changes yet.
2026-09-28 13:32:05 +07:00

73 lines
2.7 KiB
Markdown

# SkateSpots AI proxy — placement on the CVM host
Why: the Netlify Function (`netlify/functions/ai.mjs`) works, but every AI call
spends Netlify **compute + response bandwidth** credits. This service is the same
proxy running on the CVM host we already own, so AI traffic never touches Netlify
and the Routstr key lives on our box.
```
browser / APK ──HTTPS──▶ Caddy (TLS) ──http──▶ 127.0.0.1:8787 (Bun)
│
└──HTTPS──▶ Routstr node (sats)
```
## What's needed (once)
1. **A hostname** pointing at the CVM host, e.g. `ai.skate-spots.org` → the
host's public IPv4. Add it wherever `skate-spots.org` DNS is managed.
2. **Ports 80 + 443 open** to the internet (firewall at the provider; `ufw` is
inactive on the host).
3. **Caddy** for automatic TLS (see `Caddyfile` for the install snippet).
## Deploy
```bash
npm run deploy:ai # uploads server.ts + package.json + unit, installs, restarts
bash scripts/deploy-ai-proxy.sh --logs
```
The **first** deploy needs `/home/ubuntu/skatespots-ai/.env` to exist (the unit
reads it). Create it from `ai-proxy/.env.example` with the real Routstr key — that
key is the one currently in Netlify env `SASQUATCH_AI_KEY`:
```bash
ssh <host> 'cat > ~/skatespots-ai/.env' <<'EOF'
SASQUATCH_AI_BASE=https://routstr.githappens.space/v1
SASQUATCH_AI_KEY=<the sk-… Routstr session key>
SASQUATCH_MODELS=
SASQUATCH_REQUIRE_PRO=1
AI_PROXY_HOST=127.0.0.1
AI_PROXY_PORT=8787
EOF
ssh <host> 'chmod 600 ~/skatespots-ai/.env'
```
## Point the app at it
The client resolves its AI base in one place (`src/lib/sasquatchAi.ts`). Set the
build-time override and redeploy the web app:
```
VITE_SASQUATCH_AI_BASE=https://ai.skate-spots.org/api/ai
```
Leave it unset and the app keeps using `/api/ai` (the Netlify Function), so the
move is reversible and can be rolled out gradually. The proxy's CORS allowlist
already includes `https://skate-spots.org` and the local/Capacitor origins.
## Verify
```bash
# from anywhere, once TLS is up:
curl -s -X POST https://ai.skate-spots.org/api/ai/status
# → {"configured":true,...} (owner balance; public on purpose)
```
## Notes
- `SASQUATCH_REQUIRE_PRO=1` keeps the Pro gate (owner pubkeys bypass). A client
without a valid NIP-98 header gets 401; without an active Pro pass, 402.
- The proxy is per-IP throttled at 20 req/min. It's a single long-lived process,
so the throttle actually holds (unlike the serverless version).
- `netlify/functions/ai.mjs` stays in the repo as the fallback path; nothing
breaks if this service is down and the client is still pointed at `/api/ai`.