Architecture overview
The services, the database and the storage, and how they talk.
potocolom has three deployable parts: a web frontend, an API server and an inference worker. Two more parts hold data: PostgreSQL for records, and a storage adapter for image files. The same three parts run in every deployment. A self-hosted install differs from other deployments only in its settings, not in its code.
The parts
- Frontend: a single page app built with SvelteKit and the static adapter. It runs in the browser. On start it reads
GET /api/v1/configto learn which sign-in methods exist and which features are on. In a self-hosted install, the API server serves the built files. - API server: a FastAPI app. It serves REST endpoints for sign-in, the model registry, generation jobs, generation history and saved images. It also accepts the realtime drawing socket from the browser. Admin endpoints sit behind an admin role flag.
- Worker: a Python process that runs the image models with Hugging Face diffusers and PyTorch. It accepts no inbound connections. It dials out to the API at
/api/v1/fleetand keeps one connection open for registration, job dispatch, frames and heartbeats. ItsDEVICEsetting selectscuda(NVIDIA),rocm(AMD) orcpu. The default iscpu. It downloads and removes model files when the API asks, and it lists only the models it has installed. - Storage: holds the image bytes. A self-hosted install stores them on local disk by default. The storage adapter can also use an S3 compatible service.
- Database: PostgreSQL holds users, sessions, the model registry, jobs and generation history. It also records the install's sign-in choice, which is
unclaimeduntil the setup link is spent, thennoneoraccounts. Image bytes do not go into the database.
How a request moves
Generating an image
- The browser sends
POST /api/v1/generations. - The API checks the request and writes a job row to PostgreSQL.
- The API places the job in its in-process queue.
- The API hands the job to a worker over that worker's fleet connection.
- The worker reports progress and then sends
job_done. The browser follows the job's state and progress onGET /api/v1/generations/{job_id}/events, a server-sent event stream. - The worker uploads the image to the storage target named in the dispatch message. The job then completes in PostgreSQL.
Browsing saved work
- The browser calls
GET /api/v1/generationsfor its history. - The API reads the rows the user owns from PostgreSQL.
- The browser loads each image from
GET /api/v1/assets/{id}. The API checks ownership before it returns the bytes.
Drawing in realtime
A drawing session uses its own socket and does not go through the job queue. The frames travel browser, API, worker and back. See the realtime loop page.
The self-hosted shape
A self-hosted install is one Docker Compose stack on one machine. The file is deploy/compose/compose.yml.
Browser --HTTP and WebSocket--> api (FastAPI, also serves the frontend)
api --SQL--> postgres
api --files--> assets volume (local disk)
worker --dials out, one connection--> api at /api/v1/fleet
The compose file defines these services:
api: the API server. It also serves the frontend.postgres: PostgreSQL 16.worker: the NVIDIA worker. It starts with thegpuprofile.worker-rocm: the AMD worker. It starts with therocmprofile.
From a source checkout, start the NVIDIA stack with the command from the README:
docker compose -f deploy/compose/compose.yml --profile gpu up -d --build
For install steps, read the self-hosting guide in the repository.
Where to read next
- Realtime loop: how a stroke becomes a frame, and how the frame comes back.
- GPU scheduling: how jobs and live sessions share the workers today.
- Deployment profiles: the self-hosted profile and version upgrades.
The full design record is in docs/architecture.md.