From 68c165585c7a408c4b61c31c4b9c58dcb0ba403b Mon Sep 17 00:00:00 2001 From: parththakkar106 Date: Tue, 7 Jul 2026 14:07:08 +0530 Subject: [PATCH] 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 Claude-Session: https://claude.ai/code/session_017e6tQuojBLYPetUfmhit4X --- README.md | 16 +++++++++++++ plan/09-phase-hardening.md | 19 +++++++++++++-- plan/10-phase-deploy.md | 16 ++++++++++--- render.yaml | 48 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 5 deletions(-) create mode 100644 render.yaml diff --git a/README.md b/README.md index a3edcee..e93b9f8 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/plan/09-phase-hardening.md b/plan/09-phase-hardening.md index c613ec2..5f04b43 100644 --- a/plan/09-phase-hardening.md +++ b/plan/09-phase-hardening.md @@ -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). diff --git a/plan/10-phase-deploy.md b/plan/10-phase-deploy.md index 0e27375..b85f3a5 100644 --- a/plan/10-phase-deploy.md +++ b/plan/10-phase-deploy.md @@ -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. diff --git a/render.yaml b/render.yaml new file mode 100644 index 0000000..7404547 --- /dev/null +++ b/render.yaml @@ -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.