Architecture overview
ContentsVision is a two-tier web application with an asynchronous AI processing pipeline. This page is the map: the stack, the repo layout, the runtime topology, and how requests and jobs flow. The following pages drill into each layer.
Tech stack
| Layer | Technology |
|---|---|
| Frontend | Next.js 14 (App Router), React 18, TypeScript, Tailwind CSS, Framer Motion, TanStack Query and Virtual |
| Backend | Python 3.11, FastAPI, SQLAlchemy 2 (async), Pydantic v2 |
| Database | SQLite via aiosqlite (async) |
| Media | FFmpeg (via ffmpeg-python), Pillow, OpenCV (headless) |
| AI | OpenRouter (Claude vision and analysis), Deepgram Nova-2 (transcription) |
| Auth | AdjustSquare SSO, JSON Web Tokens (python-jose, HS256) |
| Analytics | PostHog (product analytics + session replay), optional, off by default |
| Exports | pandas + openpyxl (Excel), hand-rolled CSV |
| Deployment | Docker Compose, nginx, Cloudflare Tunnel |
Repository layout
contentsvision/
├── backend/
│ ├── app/
│ │ ├── main.py # FastAPI app, router registration, CORS, /health
│ │ ├── api/routes/ # HTTP endpoints (auth, claims, rooms, upload, jobs, inventory, settings, activity, billing)
│ │ ├── core/ # config, database, security (JWT), auth_deps, actor, observability
│ │ ├── models/ # SQLAlchemy ORM models (claim, room, job, inventory, team, user, activity)
│ │ ├── schemas/ # Pydantic request/response shapes
│ │ └── services/ # pipeline, video_processor, audio_processor, claude_analyzer,
│ │ # numbering, auto_approve, prompt_settings, billing, activity, adjust_square
│ ├── tests/ # pytest suite (offline, in-memory DB)
│ └── requirements.txt
├── frontend/
│ └── src/
│ ├── app/ # Next.js App Router pages
│ ├── components/ # React components
│ ├── lib/ # api client, auth, settings contexts, helpers
│ └── types/ # TypeScript interfaces
├── docs/ # this handbook (Docusaurus)
├── nginx/nginx.conf
├── docker-compose.yml # local
└── docker-compose.prod.yml # production (nginx + Cloudflare Tunnel)
Runtime topology
In production, four containers run behind a Cloudflare Tunnel. No ports are exposed to the public internet; all ingress arrives through the tunnel into nginx.
The same logical split exists in local development, just without nginx or the tunnel: the Next dev server proxies /api to the uvicorn backend. See local development.
Backend layering
The backend follows a conventional layered shape. Requests flow down; the pipeline runs to the side as a background task.
| Layer | Responsibility | Key files |
|---|---|---|
| Routes | HTTP surface, validation, auth, response shaping | app/api/routes/* |
| Auth | Validate JWT, resolve current user and team, build the Actor | core/auth_deps.py, core/actor.py, core/security.py |
| Services | Business logic: the pipeline, billing, numbering, auto-approve, Adjust Square client | app/services/* |
| Models | SQLAlchemy ORM tables | app/models/* |
| Schemas | Pydantic request/response contracts | app/schemas/* |
| Core | Config, database engine, observability | core/config.py, core/database.py, core/observability.py |
Two request paths: synchronous API vs background pipeline
ContentsVision has two distinct execution paths, and understanding the split explains most of the design.
Synchronous API calls
Standard REST. The route reads or writes the database and returns immediately. Most endpoints (claims, rooms, inventory edits, settings) work this way.
Background pipeline jobs
Uploading media returns right away, but the heavy AI work runs as a FastAPI background task against its own database session. The frontend then watches progress over Server-Sent Events.
This is why a long video does not block the HTTP request, and why the Adjust Square submission also hands its slow photo uploads to a background task (see Adjust Square).
Where to go next
- The AI pipeline: the most important subsystem, in full.
- Data model: the tables and their relationships.
- Backend API: the endpoint reference.
- Frontend: the Next.js app.