Table of Contents
Agent Context
| Meta | Value |
|---|---|
| Repo | xpltdco/fractafrag |
| Language | Python 3.12 (API), TypeScript (Frontend), Node.js (Renderer/MCP) |
| Framework | FastAPI + SQLAlchemy 2 (async) + React 18 + Vite |
| Entry Point | services/api/app/main.py |
| Test Command | make test or docker compose exec api pytest tests/ -v |
| Build Command | docker compose build |
| Compose Services | 8 (nginx, frontend, api, mcp, renderer, worker, postgres, redis) |
| Database | PostgreSQL 16 + pgvector |
| Cache/Queue | Redis 7 (Celery broker, token blocklist) |
| Last Updated | 2026-04-04 |
File Index (Read These First)
| Priority | File | Purpose |
|---|---|---|
| 1 | services/api/app/main.py |
FastAPI app setup, lifespan, CORS, router registration |
| 2 | services/api/app/config.py |
Pydantic Settings — all env vars |
| 3 | services/api/app/models/models.py |
All SQLAlchemy ORM models (14 tables) |
| 4 | services/api/app/schemas/schemas.py |
Pydantic request/response schemas |
| 5 | services/api/app/middleware/auth.py |
JWT auth, password hashing, dependencies |
| 6 | services/api/app/routers/shaders.py |
Shader CRUD, versioning, fork, search |
| 7 | services/api/app/routers/feed.py |
Personalized feed, trending, tag affinity |
| 8 | services/api/app/routers/votes.py |
Voting + Wilson hot score calculation |
| 9 | services/api/app/routers/desires.py |
Bounty board (create, list, fulfill) |
| 10 | services/api/app/routers/auth.py |
Register, login, refresh, logout |
| 11 | services/api/app/routers/users.py |
Profile, BYOK keys |
| 12 | services/api/app/worker/__init__.py |
Celery tasks (render, embed, cluster, generate) |
| 13 | services/api/app/services/embedding.py |
TF-IDF + SVD vectorizer (512-dim) |
| 14 | services/api/app/services/clustering.py |
pgvector cosine nearest-neighbor |
| 15 | services/api/app/services/glsl_validator.py |
Static GLSL syntax analysis |
| 16 | services/api/app/database.py |
Async SQLAlchemy engine + sessions |
| 17 | services/api/app/redis.py |
Async Redis client singleton |
| 18 | services/mcp/server.py |
MCP tools for AI agents |
| 19 | services/renderer/server.js |
Express + Puppeteer rendering pipeline |
| 20 | docker-compose.yml |
Service orchestration |
| 21 | db/init.sql |
Full PostgreSQL schema + indexes |
Dependency Map
docker-compose.yml
├── nginx (routes to frontend, api, mcp, renders)
├── frontend (React SPA, Vite)
├── api (FastAPI)
│ ├── app/main.py
│ │ ├── app/routers/*.py (10 router modules)
│ │ ├── app/middleware/auth.py (JWT)
│ │ └── app/middleware/rate_limit.py
│ ├── app/database.py → PostgreSQL (asyncpg)
│ ├── app/redis.py → Redis
│ ├── app/models/models.py (ORM)
│ ├── app/schemas/schemas.py (Pydantic)
│ └── app/services/
│ ├── embedding.py (TF-IDF + SVD)
│ ├── clustering.py (pgvector cosine)
│ ├── glsl_validator.py
│ ├── renderer_client.py → renderer:3100
│ └── byok.py (encryption)
├── worker (Celery, reuses api image)
│ └── app/worker/__init__.py
│ ├── render_shader → renderer:3100
│ ├── process_desire → embedding + clustering
│ └── ai_generate → external LLM APIs
├── mcp (FastMCP)
│ └── server.py → api:8000 (internal)
├── renderer (Express + Puppeteer)
│ └── server.js → /renders volume
├── postgres (pgvector/pgvector:pg16)
│ └── db/init.sql (bootstrap schema)
└── redis (redis:7-alpine)
Common Modification Patterns
To add a new API endpoint:
- Create/edit router in
services/api/app/routers/ - Add Pydantic schemas in
services/api/app/schemas/schemas.py - Register router in
services/api/app/main.pyif new file - Add
Depends(get_current_user)for auth,Depends(get_db)for database
To modify the database schema:
- Edit
db/init.sql(for new installs) - Edit ORM model in
services/api/app/models/models.py - Create Alembic migration:
docker compose exec api alembic revision --autogenerate -m "change" - Apply:
docker compose exec api alembic upgrade head
To add a Celery task:
- Define in
services/api/app/worker/__init__.py - Use sync DB (psycopg2, not asyncpg) — Celery is sync
- Enqueue from router:
my_task.delay(args)
To add an MCP tool:
- Add function in
services/mcp/server.pywith@mcp.tool() - Call API internally via httpx to
http://api:8000/api/v1/...
Gotchas
-
Async vs Sync DB — The API uses
asyncpg(async SQLAlchemy). The Celery worker usespsycopg2(sync). Don't mix them — worker tasks must create their own sync engine/session. -
Celery task imports — The worker image is built from the same Dockerfile as the API. Tasks are defined in
app/worker/__init__.py. The Celery app must be importable asapp.worker. -
System user — UUID
00000000-0000-0000-0000-000000000001is the platform system account. It's created bydb/init.sql. System-generated shaders use this author. Don't delete it. -
GLSL validation — The validator in
glsl_validator.pydoes static analysis only (no GPU compilation). It checks formainImageentry point, balanced braces, banned extensions, and infinite loop patterns. A shader can pass validation but still fail to render. -
Render status — Shaders go through:
pending→rendering→readyorfailed. The feed only shows shaders withrender_status='ready'. A newly published shader won't appear in the feed until the worker renders it. -
JWT refresh rotation — On every
/refreshcall, the old refresh token is blocklisted in Redis. If a client uses the same refresh token twice, the second attempt fails (preventing replay attacks). The blocklist entries have TTL matching the refresh token lifetime (30 days). -
pgvector cosine — The
<=>operator returns cosine distance (0 = identical, 2 = opposite). Similarity = 1 - distance. The clustering threshold (0.82 similarity) means distance < 0.18. -
Embedding dimensions — All vectors are 512-dimensional. This is hardcoded in the TF-IDF + SVD pipeline and in the PostgreSQL
vector(512)column types. Changing it requires rebuilding all embeddings and altering the column type. -
CORS — Currently set to
allow_origins=["*"]. This must be restricted before production deployment. -
Init SQL vs Alembic —
db/init.sqlbootstraps the schema on first PostgreSQL startup. For schema changes after initial deployment, use Alembic migrations. The init.sql and ORM models must stay in sync manually.
MCP Tools (for AI Agents)
The MCP server at /mcp/* exposes these tools:
| Tool | Purpose |
|---|---|
browse_shaders(query, tags, shader_type, sort, limit) |
Search/browse shaders |
get_shader(shader_id) |
Get full shader with GLSL source |
get_shader_versions(shader_id) |
Version history |
get_shader_version_code(shader_id, version_number) |
Specific version code |
submit_shader(title, glsl_code, ...) |
Create new shader |
update_shader(shader_id, glsl_code, ...) |
Update shader (new version) |
get_trending(limit) |
Trending shaders |
get_similar_shaders(shader_id, limit) |
Similar by tag overlap |
get_desire_queue(min_heat, limit) |
Browse bounties |
fulfill_desire(desire_id, shader_id) |
Mark bounty fulfilled |
GLSL Shader Format (Shadertoy-compatible WebGL2)
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;
fragColor = vec4(uv, 0.5 + 0.5 * sin(iTime), 1.0);
}
Available uniforms: iTime (float), iResolution (vec3), iMouse (vec4)
Verification Commands
# Start all services
make up
# Check service health
docker compose ps
# Run API tests
make test
# Lint Python code
docker compose exec api ruff check app/ tests/
# Database shell
make db-shell
# View logs
make logs
# Rebuild after changes
make build && make up
Architecture Summary
8-service Docker Compose stack:
nginx:80 (reverse proxy)
├── / → frontend:5173 (React SPA)
├── /api/* → api:8000 (FastAPI)
├── /mcp/* → mcp:3200 (FastMCP for AI agents)
└── /renders/* → static volume (shader thumbnails)
api:8000 (FastAPI, async)
├── JWT auth + bcrypt passwords
├── SQLAlchemy 2 async → PostgreSQL
├── Redis (token blocklist, rate limits)
└── Enqueues Celery tasks
worker (Celery, sync)
├── render_shader → renderer:3100
├── process_desire → embed + cluster
└── Periodic: feed cache, bounty expiry
renderer:3100 (Puppeteer + Chromium)
└── GLSL → WebGL → screenshot → /renders
postgres:5432 (pgvector)
└── 14 tables, HNSW indexes, trigram search
redis:6379
└── Celery broker/backend, JWT blocklist