Phase 10: Render deploy blueprint
- render.yaml: single Docker web service (SPA + API same-origin), free tier, Neon Postgres via AIDND_DATABASE_URL, generated AIDND_SECRET_KEY, /api/health check, us-east region, auto-deploy on main. - README: "Deploy (Render)" section. - Record verified Postgres path (real Neon, PG 18.4) and Phase 10 decisions (Neon, free tier, cloud Docker build) in plan/. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017e6tQuojBLYPetUfmhit4X
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
4772171b6c
commit
68c165585c
@@ -115,6 +115,22 @@ frontend/ React + Vite SPA ──HTTP/SSE──► backend/ FastAPI
|
||||
In production the backend serves the built SPA from one port (see `Dockerfile`); in
|
||||
development Vite proxies `/api` to FastAPI.
|
||||
|
||||
## Deploy (Render)
|
||||
|
||||
The repo ships a [`render.yaml`](render.yaml) blueprint: one Docker web service that
|
||||
serves the SPA and API same-origin, backed by external [Neon](https://neon.tech) Postgres
|
||||
(the free tier has no persistent disk, so the database lives off-box).
|
||||
|
||||
1. Create a **Neon** project and copy its pooled connection string.
|
||||
2. In Render: **New → Blueprint**, point it at this repo. Render reads `render.yaml`.
|
||||
3. Fill the secrets it prompts for (`sync: false` vars): `AIDND_DATABASE_URL` (the Neon
|
||||
string) and, to offer a no-signup demo, `AIDND_DEMO_API_KEY` / `AIDND_DEMO_MODELS`.
|
||||
`AIDND_SECRET_KEY` is generated automatically and kept stable across deploys.
|
||||
4. Deploy. Pushes to `main` auto-deploy thereafter. Health check: `/api/health`.
|
||||
|
||||
On the free tier the service sleeps after ~15 min idle; the first request then takes
|
||||
~30–60s to wake.
|
||||
|
||||
## Repo notes
|
||||
|
||||
- `plan/` — the phased implementation plan this was built from, kept as a build log
|
||||
|
||||
@@ -74,5 +74,20 @@ rate-limited instead of burning the demo key, and the app streams turns normally
|
||||
(No LLM key configured here, so it streams the "No model configured" error event; a *live*
|
||||
provider turn through this path was verified end-to-end in Phase 8.)
|
||||
|
||||
All Phase 9 exit criteria met. Not yet exercised: Postgres (`DATABASE_URL`) path and the
|
||||
Docker production image specifically — both are Phase 10 deploy steps.
|
||||
All Phase 9 exit criteria met.
|
||||
|
||||
### Postgres path verified 2026-07-07 (real Neon, PostgreSQL 18.4)
|
||||
|
||||
Pointed the backend at a live Neon `DATABASE_URL` (pooled, `sslmode=require&channel_binding=require`):
|
||||
|
||||
- **Driver/URL:** `postgres://`/`postgresql://` normalized to `postgresql+psycopg://`; raw
|
||||
psycopg3 connect succeeds.
|
||||
- **Bootstrap:** fresh DB → `create_all` builds all 11 tables and stamps `schema_version = 23`
|
||||
(= `LATEST_VERSION`); the SQLite-only migrations 2–23 are correctly skipped on a fresh DB.
|
||||
- **Embedding column:** `memories.embedding` is Postgres `json`; a float list round-trips
|
||||
intact (`[0.1, 0.2, 0.3, -0.4]` back as a `list`).
|
||||
- **ORM CRUD:** user → scenario → adventure → memory create/read/delete with FK cascades works.
|
||||
- **HTTP round-trip (multi-user, TestClient):** guest bootstrap → register → me → create
|
||||
scenario → list → settings → delete, all 200/201/204 against Neon.
|
||||
|
||||
Remaining Phase 10 step: the Docker production image specifically (build + run the container).
|
||||
|
||||
+13
-3
@@ -13,11 +13,21 @@ GitHub repo.
|
||||
**Ask before implementing:** Render tier (free-with-sleep vs ~$7/mo always-on — depends on the
|
||||
Phase 9 database decision), and the exact service name (it becomes the public URL).
|
||||
|
||||
### Decisions (confirmed 2026-07-07)
|
||||
|
||||
| Question | Answer |
|
||||
|---|---|
|
||||
| Database | **Neon Postgres** (external managed; free tier has no persistent disk). Path fully verified against real Neon — see `plan/09-phase-hardening.md`. |
|
||||
| Tier | **Free** (sleeps after ~15 min idle; ~30–60s first-wake). |
|
||||
| Service name | `ai-dnd` (→ `ai-dnd.onrender.com`, adjustable in dashboard). |
|
||||
| Region | `virginia` (us-east, matches Neon us-east-1). |
|
||||
| Local Docker preflight | Skipped by choice — Render builds the same Dockerfile in the cloud; its build logs are the image test. |
|
||||
|
||||
## Deploy
|
||||
|
||||
- [ ] `render.yaml` blueprint: web service from the Dockerfile, env vars (SECRET_KEY generated,
|
||||
demo-key vars, `MULTI_USER=true`), health check endpoint, plus disk or managed Postgres
|
||||
per the Phase 9 decision.
|
||||
- [x] `render.yaml` blueprint: web service from the Dockerfile, env vars (SECRET_KEY generated,
|
||||
demo-key vars, `MULTI_USER=true`), `/api/health` health check, external Neon Postgres.
|
||||
README gained a "Deploy (Render)" section.
|
||||
- [ ] Set up the Render service, connect the GitHub repo, auto-deploy on push to `main`.
|
||||
- [ ] Seed production with 2–3 good demo scenarios (public/starter scenarios from Phase 8) so
|
||||
first-time visitors have something great to click immediately.
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
# Render Blueprint — https://render.com/docs/blueprint-spec
|
||||
#
|
||||
# One web service: the multi-stage Dockerfile builds the React SPA and the
|
||||
# FastAPI backend into a single image that serves both same-origin. Database
|
||||
# is external Neon Postgres (set AIDND_DATABASE_URL in the dashboard); the
|
||||
# free tier has no persistent disk, which is why the DB lives off-box.
|
||||
#
|
||||
# First deploy: create a Blueprint from this repo in the Render dashboard,
|
||||
# then fill the `sync: false` secrets (Neon URL + demo key). Pushes to `main`
|
||||
# auto-deploy thereafter.
|
||||
|
||||
services:
|
||||
- type: web
|
||||
name: ai-dnd
|
||||
runtime: docker
|
||||
dockerfilePath: ./Dockerfile
|
||||
dockerContext: .
|
||||
plan: free # sleeps after ~15 min idle; first wake takes ~30-60s
|
||||
region: virginia # us-east, closest to the Neon us-east-1 database
|
||||
healthCheckPath: /api/health
|
||||
autoDeploy: true
|
||||
envVars:
|
||||
# Multi-user hardening on (guest sessions, per-user data, rate limits).
|
||||
- key: AIDND_MULTI_USER
|
||||
value: "1"
|
||||
|
||||
# Signs session cookies + encrypts stored API keys. Render generates a
|
||||
# strong value once and keeps it stable across deploys (a regenerated
|
||||
# secret would log out every user on each deploy).
|
||||
- key: AIDND_SECRET_KEY
|
||||
generateValue: true
|
||||
|
||||
# Neon Postgres connection string (pooled, sslmode=require). Secret —
|
||||
# set it in the dashboard; never commit it.
|
||||
- key: AIDND_DATABASE_URL
|
||||
sync: false
|
||||
|
||||
# --- Shared demo key (optional): lets first-time visitors play without
|
||||
# bringing their own API key. Set these in the dashboard to enable;
|
||||
# leave unset to require BYOK. ---
|
||||
- key: AIDND_DEMO_API_KEY
|
||||
sync: false
|
||||
- key: AIDND_DEMO_MODELS
|
||||
sync: false
|
||||
- key: AIDND_DEMO_TURNS_PER_DAY
|
||||
value: "20"
|
||||
|
||||
# CORS is unset on purpose: the SPA is served same-origin by FastAPI.
|
||||
Reference in New Issue
Block a user