docs: bootstrap project instructions and roadmap
Add CLAUDE.md with authoritative stack (FastAPI + Jinja2 + SQLite), deployment topology (CF-proxied -> OPNsense -> Caddy -> VM), security must-haves, magic-link auth, and project git flow. Add docs/ROADMAP.md with phased build plan, dataclasses, SQL schema, caching strategy, visual design tokens (light-blue farm palette), and .env contract. Commit generic code_guidelines.md and security.md, note FastAPI per-project override. Add docs/README.md. Commit logo assets and .gitignore (venv, .env, data/, SQLite, caches). No app code in this pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
116
CLAUDE.md
Normal file
116
CLAUDE.md
Normal file
@@ -0,0 +1,116 @@
|
||||
# Chicken Babies R Us — Project Instructions
|
||||
|
||||
Small farm website for **Chicken Babies R Us** (Morrison TN — address is intentionally **not** displayed publicly). Public brochure site with a blog-style home, About, Contact, and a disabled "Shop (coming soon)" placeholder. The owner's wife, **Head Hen**, edits all content through an admin area protected by email magic-link authentication.
|
||||
|
||||
> This file is authoritative for *this project*. Where it conflicts with `code_guidelines.md`, this file wins.
|
||||
|
||||
---
|
||||
|
||||
## Stack (authoritative for this project)
|
||||
|
||||
| Layer | Choice |
|
||||
|---|---|
|
||||
| Language | Python 3.12 |
|
||||
| Web framework | **FastAPI** (overrides the Flask default in `code_guidelines.md`) |
|
||||
| Templates | Jinja2 |
|
||||
| ASGI server | Uvicorn, behind Caddy reverse proxy |
|
||||
| Database | SQLite (WAL mode) — single-writer is fine at this scale |
|
||||
| Cache | In-process TTL cache + row-level rendered-HTML cache (no Redis) |
|
||||
| Email | Resend (contact form + magic-link auth) |
|
||||
| Anti-spam | hCaptcha + honeypot + SlowAPI rate limits |
|
||||
| Logging | `structlog` |
|
||||
| Container | Docker (multi-stage), target = Debian 12 VM on home server |
|
||||
| Repo host | Gitea (Actions for image build/publish wired later) |
|
||||
|
||||
## Deployment Topology
|
||||
|
||||
```
|
||||
Internet
|
||||
→ Cloudflare (proxied DNS)
|
||||
→ OPNsense firewall (inbound 443 allowed only from Cloudflare IP ranges)
|
||||
→ Virtual IP
|
||||
→ Debian 12 VM
|
||||
→ Caddy (TLS termination)
|
||||
→ Uvicorn + FastAPI container
|
||||
```
|
||||
|
||||
The app MUST trust `X-Forwarded-For` / `X-Forwarded-Proto` **only from Caddy's IP**. Run Uvicorn with `--proxy-headers --forwarded-allow-ips=<caddy-ip>`. Never read `X-Forwarded-*` in application code directly — let Starlette's `ProxyHeadersMiddleware` do it.
|
||||
|
||||
## Target Repository Layout (after Phase 0)
|
||||
|
||||
```
|
||||
/ repo root
|
||||
├── app/ FastAPI application package
|
||||
│ ├── main.py app factory + startup
|
||||
│ ├── config.py typed config loader (pydantic-settings)
|
||||
│ ├── models/ dataclasses + SQL schema + migrations
|
||||
│ ├── routes/ public_router.py, admin_router.py, auth_router.py
|
||||
│ ├── services/ auth, email, cache, markdown, media, hcaptcha
|
||||
│ ├── templates/ Jinja2 (public/ + admin/ + emails/)
|
||||
│ └── static/ CSS, JS, site images, logo
|
||||
├── data/ runtime (SQLite DB + uploads) — mounted volume in prod
|
||||
├── docs/ business + architecture docs (NO application code)
|
||||
│ ├── README.md
|
||||
│ ├── ROADMAP.md
|
||||
│ └── MANUAL_TESTING.md (added in Phase 1)
|
||||
├── tests/ pytest
|
||||
├── Logo/ brand assets (source)
|
||||
├── Dockerfile
|
||||
├── docker-compose.yml
|
||||
├── requirements.txt
|
||||
├── .env.example
|
||||
├── .gitignore
|
||||
├── CLAUDE.md
|
||||
├── code_guidelines.md
|
||||
└── security.md
|
||||
```
|
||||
|
||||
## Security Must-Haves (in addition to `security.md`)
|
||||
|
||||
- **SQL**: parameterized statements only (sqlite3 `?` placeholders or SQLAlchemy Core bind params). Never f-string a query.
|
||||
- **Markdown**: hardened pipeline `markdown-it-py` → `bleach` allowlist. No raw HTML pass-through.
|
||||
- **Image uploads**: validate magic bytes with `python-magic`, cap at 8 MB, re-encode through Pillow, store under a random filename, discard the client-supplied extension.
|
||||
- **CSRF**: double-submit cookie on every admin `POST` / `PUT` / `DELETE`.
|
||||
- **Cookies**: `Secure`, `HttpOnly`, `SameSite=Lax`; session IDs signed with `itsdangerous`.
|
||||
- **Security headers** (middleware): strict nonce-based CSP, HSTS, `X-Content-Type-Options: nosniff`, `Referrer-Policy: strict-origin-when-cross-origin`, `Permissions-Policy`.
|
||||
- **Magic-link tokens**: 256-bit random (`secrets.token_urlsafe(32)`), stored hashed (SHA-256), single-use, 15-minute expiry. IP is logged but not enforced (mobile roaming).
|
||||
- **Rate limits** on auth endpoints: 5 requests / 15 min / IP *and* / email.
|
||||
- **Admin allowlist**: only addresses in `ADMIN_EMAILS` env var may request a magic link. Any other address silently succeeds (no user enumeration) but sends no email.
|
||||
- **Secrets**: env / `.env` only, never committed. `.env.example` is the public contract.
|
||||
- **Audit logging**: every auth event (link requested, link consumed, session created/revoked, rate-limit hit) at INFO. Never log raw tokens or email bodies.
|
||||
|
||||
## How to Run (dev)
|
||||
|
||||
```bash
|
||||
python3.12 -m venv venv
|
||||
source venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
cp .env.example .env # fill RESEND_API_KEY, HCAPTCHA_*, ADMIN_EMAILS, SECRET_KEY
|
||||
uvicorn app.main:app --reload
|
||||
```
|
||||
|
||||
Docker (parity with prod):
|
||||
|
||||
```bash
|
||||
docker compose up --build
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
- `pytest` for auth, magic-link lifecycle, markdown sanitization, rate limits, contact form.
|
||||
- **Never mock the DB** in auth/magic-link tests — use a temp SQLite file so behavior matches prod.
|
||||
- Manual test checklist lives in `docs/MANUAL_TESTING.md` (added in roadmap Phase 1).
|
||||
|
||||
## Git Strategy (this project)
|
||||
|
||||
- Branches: `master` (prod) · `dev` (integration) · `feat/*`, `chore/*`, `docs/*`, `fix/*` (work branches).
|
||||
- **Work branches off `dev`.** Local `--no-ff` merge into `dev`, then push `dev`. Promote `dev` → `master` with `--no-ff` for releases. Tag `master` with `vX.Y.Z`.
|
||||
- Conventional commits: `feat:`, `fix:`, `chore:`, `docs:`, `refactor:`, `test:`.
|
||||
- Do **not** push directly to `master`. Do **not** force-push shared branches.
|
||||
|
||||
## Pointers
|
||||
|
||||
- Generic Python standards: `code_guidelines.md` (FastAPI overrides its Flask default)
|
||||
- Security baseline: `security.md`
|
||||
- Phased roadmap, dataclasses, SQL schema, visual design: `docs/ROADMAP.md`
|
||||
- Docs folder guide: `docs/README.md`
|
||||
Reference in New Issue
Block a user