Full-stack application template — FastAPI backend + React frontend, all Dockerized.
Backend: FastAPI, SQLModel (PostgreSQL), Celery (Redis), Keycloak SSO, structlog, pydantic-ai
Frontend: React 19, TypeScript, Vite, Tailwind CSS 4, Keycloak SSO
Infrastructure: Docker Compose, PgBouncer (connection pooling), Nginx (frontend proxy + production SSL), systemd
For contributors: see the agent/ system (agent/README.md, agent/project-map.md,
agent/rules/, and agent/workflows/) for governance, folder conventions, and
workflows. Theme system docs live in docs/THEMES.md.
Design specs for shared core primitives live in docs/specs/ — e.g.
realtime-jobs.md. Legacy per-area guides have
been consolidated into pointers (see git history).
# 1. Copy and edit environment
cp example.env .env
# 2. Start everything
./start.sh
# 3. Stop
./stop.sh
./stop.sh --volumes # also delete data- Frontend: https://localhost (TLS on :443, terminated in the frontend container)
- API Docs: https://localhost/docs (proxied through the edge; backend is internal-only)
genesis/
├── docker-compose.yml # All services
├── genesis.service # systemd unit
├── start.sh # Build + start (calls stop first)
├── stop.sh # Graceful shutdown (--volumes, --force)
├── example.env # Configuration template
├── ssl/ # TLS cert/key for the frontend edge (gitignored)
├── pgbouncer/
│ ├── pgbouncer.ini # Connection pooler config
│ └── entrypoint.sh # Generates auth from env vars
├── backend/
│ ├── Dockerfile
│ ├── pyproject.toml # deps (uv project) + tool config
│ ├── uv.lock # fully pinned dependency lock
│ └── app/
│ ├── main.py # FastAPI app + lifespan
│ ├── celery_app.py # Celery config
│ ├── core/ # platform plumbing (config, db, security, logging, middleware, redis, encryption, realtime WS + jobs)
│ ├── llm/ # LLM subsystem (service, factory, runner, profile/prompt models & admin routers)
│ └── features/ # app features — one folder per feature
├── docs/ # contributor docs (DEVELOPING, THEMES) + specs/ (design specs)
└── frontend/ # React + Vite
├── Dockerfile # Multi-stage: npm build → nginx
├── templates/ # nginx config (envsubst at boot): TLS, /v1 proxy, SPA, X-Accel
├── package.json
└── src/
├── main.tsx # entry
├── App.tsx # route tree
├── core/ # auth, api client, layout, hooks, LLM admin UI, settings shells, theme engine, realtime WS
├── pages/ # top-level pages (Home, Dashboard)
└── features/ # app features — one folder per feature
Browser → Stream proxy (TLS/PROXY protocol on :443)
└── Frontend container — nginx (8443, terminates TLS)
├── Static files (SPA)
├── /v1/ → Backend (8000) # same-origin; WS at /v1/ws
└── /_xaccel_data/ → DATA_ROOT # internal, auth-gated file serving
├── PgBouncer (6432) → PostgreSQL (5432)
├── Redis # also WS pub/sub fan-out
├── Celery Worker
└── Celery Beat
Frontend nginx terminates TLS and proxies /v1/ — including the /v1/ws WebSocket — to the backend on the Docker network, same-origin (no CORS). The WS handshake is authenticated via ?token=<JWT> and fans out per-user through Redis, so every tab a user has open receives that user's events.
| Service | Container | Port |
|---|---|---|
| Frontend | {slug}-frontend |
443 (8443) |
| Backend | {slug}-backend |
— (internal) |
| Worker | {slug}-worker |
— |
| Scheduler | {slug}-scheduler |
— |
| PgBouncer | {slug}-pgbouncer |
6432 |
| PostgreSQL | {slug}-postgres |
5432 |
| Redis | {slug}-redis |
6379 |
/health,/ready,/v1/meendpoints- LLM profiles with Fernet-encrypted API keys (OpenAI, Anthropic, Google, Groq, xAI, DeepSeek, Z.AI)
- Default / fallback profiles seeded from env vars on first
alembic upgrade head— DB is source of truth after that - DB-backed prompt templates with variable substitution
- Resilient pydantic-ai agent runner with timeout + default/fallback profile
- Token-based theme engine: 4 seeded system themes, admin CRUD, optional per-user override
- Authenticated WebSocket endpoint (
/v1/ws) with bearer-token handshake for realtime push - Async JWKS fetch with TTL cache, stampede lock, and stale-cache fallback on transient errors
- Fail-closed JWT verification (requires
audience,issuer, andhttps://JWKS URL) - Security response headers (HSTS, X-Content-Type-Options, X-Frame-Options, Referrer-Policy) + opt-in CORS allowlist
request_idbound to every log line via middleware; surfaced in error response bodies- structlog: JSON in prod, pretty console in dev
- Video/image background home page (env-configurable)
- Keycloak SSO with auto token refresh
- Role-based access control
- Demo mode without Keycloak
- Protected routes
- Live-switchable themes via CSS custom properties (Tailwind v4
@theme); admin editor + per-user picker - Single shared WebSocket client with auto-reconnect, hardened against token refresh
See example.env for all options. Key vars:
| Variable | Description |
|---|---|
COMPOSE_PROJECT_NAME |
Prefix for docker containers/networks/volumes; also the FastAPI app title |
DOMAIN_NAME |
nginx server_name for the TLS vhost |
REAL_IP_FROM |
Trusted PROXY-protocol source for real client IP |
CLIENT_MAX_BODY_SIZE |
nginx upload cap (default: 50m) |
PROXY_TIMEOUT |
Backend proxy connect/read/send timeout (default: 300s) |
DATA_ROOT |
Host path for feature binary files; served via /_xaccel_data/ |
JOBS_RETENTION_DAYS |
Days to keep terminal job rows before sweep (default: 7) |
WS_ENABLED |
Mount the realtime WS + job-tracker endpoints (default: true) |
EXPOSE_DOCS |
Serve FastAPI /docs + /openapi.json (default: false — schema is public when on) |
VITE_WS_ENABLED |
SPA opens the WebSocket (default: true; mirror of WS_ENABLED) |
VITE_SITE_TITLE |
Displayed in browser/UI |
VITE_SITE_SUBTITLE |
Tagline on home page |
VITE_HOME_VIDEO |
Home background video (webm) |
VITE_HOME_IMAGE |
Home background image fallback |
KEYCLOAK_* |
Backend SSO config |
VITE_KEYCLOAK_* |
Frontend SSO config |
PGBOUNCER_PORT |
PgBouncer port (default: 6432) |
ENCRYPTION_KEY |
Fernet key for API key encryption |
CORS_ALLOWED_ORIGINS |
Comma-separated origin allowlist (empty = deny all cross-origin) |
# Copy to server
scp -r . your-server:/opt/genesis
# Install systemd service
sudo cp genesis.service /etc/systemd/system/
sudo systemctl enable genesis
sudo systemctl start genesis
# TLS is terminated inside the frontend container. Drop your cert + key
# into ./ssl/ (genesis.crt / genesis.key). nginx listens 8443 ssl
# proxy_protocol (published on :443) and expects PROXY protocol, so front
# it with a TCP/stream proxy (host nginx stream{} block, Cloudflare, etc.).The frontend is managed via git subtree from the Vitamin repo:
git subtree pull --prefix=frontend https://git.idanow.org/idanow/vitamin.git v1.1.0 --squash