Add public-release plan (phases 7-10)
Phases: public repo & Docker (7), guest-first optional accounts (8), production hardening (9), Render deploy (10). Confirmed decisions and per-phase open questions recorded in each file. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KFsGHju9szibJJa2YJcdbg
This commit is contained in:
co-authored by
Claude Fable 5
parent
db9f904222
commit
466d7bc0e4
@@ -72,3 +72,31 @@ player input
|
||||
15, embedding-based retrieval of relevant memories into context.
|
||||
|
||||
Each phase ends with the app runnable and testable end-to-end.
|
||||
|
||||
## Public release (phases 7–10)
|
||||
|
||||
Goal: public GitHub repo + hosted multi-user deployment, linkable from resume/website.
|
||||
|
||||
| Area | Decision |
|
||||
|---|---|
|
||||
| License | MIT |
|
||||
| Auth | Email + password, **optional** — guest sessions play instantly, register to keep data |
|
||||
| Signup | Open (rate-limited) |
|
||||
| LLM keys | BYOK per user + shared server-funded demo key (capped, details TBD in Phase 8) |
|
||||
| Database (hosted) | **TBD — ask at start of Phase 9** (SQLite-on-disk vs Postgres) |
|
||||
| Hosting | Render (tier TBD in Phase 10) |
|
||||
| Domain | Platform URL (custom domain later, optional) |
|
||||
| README media | Deferred — text-only first, screenshots/GIF in a later pass |
|
||||
|
||||
Open questions are recorded at the top of each phase file under "Ask before implementing".
|
||||
|
||||
7. **[Phase 7 — Public repo & portability](07-phase-public-repo.md)**: MIT license, portfolio
|
||||
README, Dockerfile + compose, cross-platform run instructions, publish to GitHub.
|
||||
8. **[Phase 8 — Optional accounts & multi-user](08-phase-accounts.md)** *(the big one)*:
|
||||
guest-first sessions, optional email+password upgrade, per-user data scoping across all
|
||||
routers/tables, per-user encrypted BYOK settings, shared demo key with caps.
|
||||
9. **[Phase 9 — Production hardening](09-phase-hardening.md)**: env-var config, quickjs
|
||||
time/memory limits, rate limiting, size/row caps, locked-down debug surface, production
|
||||
serving, database decision.
|
||||
10. **[Phase 10 — Deploy & publish](10-phase-deploy.md)**: Render blueprint + deploy, seeded
|
||||
demo scenarios, live smoke test, resume/website links and blurb.
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# Phase 7 — Public repo & portability
|
||||
|
||||
**Goal:** make the repo public-worthy and runnable by anyone on any OS, so the GitHub link is
|
||||
immediately usable on a resume — before any hosted-deployment work.
|
||||
|
||||
## Decisions (confirmed)
|
||||
|
||||
| Question | Answer |
|
||||
|---|---|
|
||||
| License | **MIT** |
|
||||
| README media (screenshots/GIF) | **Skip for now** — text-only README; visuals in a later pass |
|
||||
|
||||
**Ask before implementing:** GitHub repo name (default suggestion: `ai-dnd`) and whether the
|
||||
existing local commit history/message is fine to publish as-is.
|
||||
|
||||
## Repo hygiene
|
||||
|
||||
- [ ] Add `LICENSE` (MIT, current year, Parth Thakkar).
|
||||
- [ ] Verify no secrets or user data are tracked (`openrouter_key.env`, `data.db` — already
|
||||
gitignored and never committed; re-verify before push).
|
||||
- [ ] Add `backend/.env.example` documenting every env var the app reads (grows in Phase 9).
|
||||
- [ ] Decide what to do with `CODE_REVIEW_FINDINGS.md` and `plan/` — keep (shows process, good
|
||||
for a portfolio) — just give them a one-line mention in the README.
|
||||
|
||||
## README rewrite (portfolio-grade, text-only)
|
||||
|
||||
- [ ] Pitch paragraph: what it is, what makes it interesting (AI Dungeon-compatible scripting,
|
||||
memory bank with embeddings, full prompt transparency/Insights, provider-agnostic).
|
||||
- [ ] Feature list with pointers into the code (scripting engine, context builder, memory bank).
|
||||
- [ ] Architecture diagram (reuse/refresh the one in `plan/00-OVERVIEW.md`).
|
||||
- [ ] Setup instructions for **Windows (start.ps1), macOS/Linux (manual), and Docker**.
|
||||
- [ ] "Bring your own model" section: Ollama / LM Studio / OpenRouter free models — emphasize it
|
||||
runs fully free.
|
||||
- [ ] Placeholder section for screenshots/GIF (added in a later pass).
|
||||
|
||||
## Docker (one-command run for non-Windows users)
|
||||
|
||||
- [ ] `Dockerfile`: multi-stage — build frontend (`npm run build`), then Python image serving
|
||||
FastAPI with the built SPA mounted (SPA fallback already exists in `app/main.py`).
|
||||
- [ ] `docker-compose.yml`: single service, volume for `data.db`, port mapping.
|
||||
- [ ] `start.sh` for macOS/Linux dev parity with `start.ps1` (optional, nice-to-have).
|
||||
- [ ] Test: `docker compose up` from a clean clone → app works at `http://localhost:8000`.
|
||||
|
||||
## Publish
|
||||
|
||||
- [ ] Create public GitHub repo (`gh repo create`), push `main`.
|
||||
- [ ] Add repo description, topics (`ai-dungeon`, `fastapi`, `react`, `llm`, `interactive-fiction`).
|
||||
- [ ] Confirm the GitHub rendering of README looks right.
|
||||
|
||||
## Exit criteria
|
||||
|
||||
A stranger on macOS with Docker installed can clone the repo, run one command, open the app,
|
||||
paste an OpenRouter free-tier key, and play an adventure — without asking you anything.
|
||||
@@ -0,0 +1,71 @@
|
||||
# Phase 8 — Optional accounts & multi-user
|
||||
|
||||
**Goal:** turn the single-user app into a multi-user one where **accounts are optional**:
|
||||
a visitor can start playing instantly as a guest, and can register (email + password) at any
|
||||
point to keep their adventures across devices/browsers. This is the largest phase — it touches
|
||||
every router and most tables.
|
||||
|
||||
## Decisions (confirmed)
|
||||
|
||||
| Question | Answer |
|
||||
|---|---|
|
||||
| Auth method | **Email + password, optional** — guest sessions work without an account |
|
||||
| Signup policy | **Open signup** (rate-limited) |
|
||||
| LLM API keys | **BYOK + shared demo key** — users can paste their own key; users without one get limited turns on a server-funded key |
|
||||
|
||||
**Ask before implementing:**
|
||||
- Demo-key specifics: which provider/key funds it, model whitelist (free models only?),
|
||||
per-user turn/day cap, what the "out of demo turns" message says.
|
||||
- Guest data retention: how long before unclaimed guest data is deleted (suggestion: 30 days
|
||||
of inactivity).
|
||||
- Password reset: skip for v1, or implement email-based reset (requires an email provider)?
|
||||
|
||||
## Data model
|
||||
|
||||
- [ ] `User` table: id, email (nullable — null means guest), password_hash (nullable),
|
||||
created_at, last_seen_at, is_guest flag (derivable from email; keep explicit for clarity).
|
||||
- [ ] Add `user_id` FK to: `Adventure`, `Scenario`, `Script`, `Settings` (and anything else
|
||||
global today — audit `models.py`). Story cards/actions inherit scope via their parent.
|
||||
- [ ] Settings becomes **per-user** (endpoint URL, API key, models, memory-bank config).
|
||||
API key **encrypted at rest** (Fernet with a server-side `SECRET_KEY` env var).
|
||||
- [ ] Migration (in `migrations.py` style): create a "local user", assign all existing rows to
|
||||
it — a fresh clone/local install keeps working exactly as before.
|
||||
- [ ] Demo/starter scenarios: mark as `user_id = NULL` + `is_public` so everyone sees them
|
||||
(decide exact mechanism when implementing; seed via `seed_demo.py`).
|
||||
|
||||
## Auth & sessions
|
||||
|
||||
- [ ] Guest flow: first API call with no session → create guest User, set a signed, long-lived
|
||||
httpOnly session cookie. No signup wall anywhere.
|
||||
- [ ] Register: email + password (hashed with bcrypt/argon2) **upgrades the current guest user
|
||||
in place** — same user_id, data automatically kept.
|
||||
- [ ] Login: standard session issue; logging in from a fresh guest session with existing account
|
||||
discards the empty guest (or merges — ask if guest has data).
|
||||
- [ ] Session middleware/dependency: every router handler resolves `current_user`; **every query
|
||||
filtered by `user_id`** (this is the bulk of the diff — go router by router).
|
||||
- [ ] Rate limits on register/login endpoints (brute-force protection).
|
||||
- [ ] Local/self-hosted mode stays frictionless: single auto-created local user, no login UI
|
||||
unless `MULTI_USER=true` (env var) — resume demo runs multi-user, local clones don't care.
|
||||
|
||||
## Shared demo key (BYOK fallback)
|
||||
|
||||
- [ ] Server env vars: `DEMO_API_KEY`, `DEMO_ENDPOINT_URL`, `DEMO_MODEL_WHITELIST`,
|
||||
`DEMO_TURNS_PER_DAY`.
|
||||
- [ ] If a user has no API key configured: use demo key, restrict model picker to the whitelist,
|
||||
count turns per user per day, friendly error + "add your own key in Settings" when capped.
|
||||
- [ ] Turn counting includes memory-bank background calls (or disable memory bank on demo key —
|
||||
decide when implementing).
|
||||
|
||||
## Frontend
|
||||
|
||||
- [ ] Auth UI: register/login modal or page, "Save your progress" nudge for guests (subtle,
|
||||
e.g. in the header), logout, account menu.
|
||||
- [ ] `api.js`: send cookies (`credentials: include`), handle 401 → re-establish guest session.
|
||||
- [ ] Settings page: per-user; show demo-key status ("Using shared demo key — N turns left today").
|
||||
|
||||
## Exit criteria
|
||||
|
||||
Two different browsers hit the deployed app: each gets its own guest world (adventures invisible
|
||||
to the other), both can play immediately on the demo key. One registers mid-adventure and its
|
||||
data survives; logging in from the other browser shows the same account data. Local
|
||||
`start.ps1` / `docker compose up` still works with zero auth friction.
|
||||
@@ -0,0 +1,55 @@
|
||||
# Phase 9 — Production hardening
|
||||
|
||||
**Goal:** make the app safe and stable to expose to strangers on the internet: config via
|
||||
environment, resource limits on everything user-controlled, and a single-service production
|
||||
build.
|
||||
|
||||
## Decisions
|
||||
|
||||
| Question | Answer |
|
||||
|---|---|
|
||||
| Database | **Decide at start of this phase.** SQLite on a persistent disk (zero code change, but Render disks require the ~$7/mo starter tier) vs Postgres (free/cheap managed options, better resume talking point, needs SQLAlchemy URL + migration tweaks). Revisit with current Render pricing. |
|
||||
|
||||
**Ask before implementing:** the database choice above, and target monthly budget (drives
|
||||
Render tier: free tier sleeps after idle + has no persistent disk).
|
||||
|
||||
## Configuration
|
||||
|
||||
- [ ] All config via env vars with sane local defaults: `DATABASE_URL`, `SECRET_KEY`
|
||||
(sessions + API-key encryption), `MULTI_USER`, `CORS_ORIGINS`, demo-key vars (Phase 8),
|
||||
port/host. Document each in `backend/.env.example`.
|
||||
- [ ] Fail fast on missing `SECRET_KEY` when `MULTI_USER=true`.
|
||||
|
||||
## Abuse & resource limits
|
||||
|
||||
- [ ] **quickjs limits**: per-execution time limit and memory limit on the scripting engine
|
||||
(`scripting/engine.py`) — user-submitted JS must not be able to hang or OOM the server.
|
||||
- [ ] Rate limiting on expensive endpoints (turn generation, script run, auth) — per-user and
|
||||
per-IP (e.g. `slowapi`).
|
||||
- [ ] Request size limits (script source length, memory/story-card text lengths, action text).
|
||||
- [ ] Cap per-user row counts (adventures, scenarios, scripts, story cards) with friendly errors.
|
||||
- [ ] Audit debug router (`routers/debug.py`) and `/docs`: admin-only or disabled when
|
||||
`MULTI_USER=true` — debug log may contain other users' prompts.
|
||||
|
||||
## Production serving
|
||||
|
||||
- [ ] Single service: FastAPI serves the built SPA (fallback already exists) — verify the Docker
|
||||
image from Phase 7 is production-ready (no `--reload`, multiple workers or async-safe
|
||||
single worker; check SQLite + multiple workers interaction before choosing).
|
||||
- [ ] CORS locked to the deployed origin (moot if same-origin single service — verify).
|
||||
- [ ] Security headers middleware; cookies `Secure` + `SameSite`.
|
||||
- [ ] Streaming (SSE) works behind Render's proxy — verify no buffering issues.
|
||||
- [ ] Structured logging; scrub API keys from all logs and the debug page.
|
||||
|
||||
## Database (after decision)
|
||||
|
||||
- [ ] If Postgres: swap `DATABASE_URL`, verify JSON-blob columns (embeddings) and
|
||||
`migrations.py` work; test full play loop.
|
||||
- [ ] If SQLite-on-disk: confirm WAL mode + single-worker (or serialized writes) is acceptable.
|
||||
- [ ] Backup story: platform DB backups (Postgres) or a scheduled dump of the disk (SQLite).
|
||||
|
||||
## Exit criteria
|
||||
|
||||
Running the production Docker image locally with `MULTI_USER=true`: a hostile user cannot hang
|
||||
the server with a `while(true)` script, cannot see another user's data or the debug log, gets
|
||||
rate-limited instead of burning the demo key, and the app streams turns normally the whole time.
|
||||
@@ -0,0 +1,46 @@
|
||||
# Phase 10 — Deploy & publish
|
||||
|
||||
**Goal:** the app live on Render at a public URL, linked from resume/website alongside the
|
||||
GitHub repo.
|
||||
|
||||
## Decisions (confirmed)
|
||||
|
||||
| Question | Answer |
|
||||
|---|---|
|
||||
| Platform | **Render** |
|
||||
| Domain | **Platform URL is fine** (e.g. `ai-dnd.onrender.com`); custom domain can be added later anytime |
|
||||
|
||||
**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).
|
||||
|
||||
## 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.
|
||||
- [ ] 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.
|
||||
- [ ] Smoke test the live URL: guest play on demo key, register, BYOK flow, scripting, memory
|
||||
bank, Insights — from a device/network that isn't yours.
|
||||
- [ ] Free-tier note: if on free tier, first request after idle takes ~30–60s to wake — add a
|
||||
friendly loading state or accept it (revisit tier if it feels bad).
|
||||
|
||||
## Post-launch guardrails
|
||||
|
||||
- [ ] Watch demo-key spend/usage for the first days (OpenRouter dashboard); confirm caps hold.
|
||||
- [ ] Set up uptime monitoring (free: UptimeRobot or similar) — optional.
|
||||
- [ ] Error visibility: Render logs are enough for v1; note how to pull them.
|
||||
|
||||
## Resume / website
|
||||
|
||||
- [ ] README: add the live-demo link + "Try it" section at the top.
|
||||
- [ ] 2–3 sentence project blurb for resume/website (stack, the interesting hard parts:
|
||||
AI Dungeon-compatible JS scripting sandbox, embedding-based memory bank, prompt
|
||||
transparency, guest-first optional auth).
|
||||
- [ ] Later pass (deferred from Phase 7): screenshots/demo GIF for README and website card.
|
||||
|
||||
## Exit criteria
|
||||
|
||||
A recruiter clicks one link on your resume, lands on the live app, plays three turns of a demo
|
||||
scenario as a guest without configuring anything, and can find the GitHub repo from the page.
|
||||
Reference in New Issue
Block a user