انتقل إلى المحتوى

Architecture overview

Vistilla is deliberately small. Everything the server does lives in one Rust binary, vistilla-api. It serves:

  • the web pages and their static assets;
  • the JSON API;
  • video streaming and downloads;
  • this documentation;
  • and, in the same process, the background workers.

State lives in two places:

  • a SQLite database for every record (users, videos, comments…);
  • an object store for the big files (recordings, poster images). This is either a folder on disk or any S3-compatible bucket.
+----------------------------------------------------------------+
| Browser                                                        |
| HTML pages + ES modules (no framework, no build step)          |
| getDisplayMedia / getUserMedia -> canvas -> MediaRecorder      |
+----------------------------------------------------------------+
        |  HTTPS, session cookie vistilla_session
        v
+----------------------------------------------------------------+
| Cloudflare Worker: reverse proxy, 'starting' page,             |
| cron ping GET /api/healthz every 10 minutes                    |
+----------------------------------------------------------------+
        |
        v
+----------------------------------------------------------------+
| vistilla-api (axum on tokio)                                       |
|   pages    /  /app  /record  /v/:id  /s/:token  /settings ...  |
|   static   /assets/*   /docs/*                                 |
|   API      /api/*    JSON; errors as {"error": ...}            |
|   media    /media/*  proxied with Range, or 307 to presigned   |
|   jobs     seek index > silences > transcript > summary >      |
|            sweep > backfill                                    |
+----------------------------------------------------------------+
        |  sqlx               |  SigV4 over HTTPS     |  HTTPS
        v                     v                       v
+------------------+  +--------------------+  +--------------------+
| SQLite (WAL)     |  | Object store       |  | External services  |
| /data/vistilla.db    |  | fs, or S3: B2, R2, |  | AI providers       |
| replicated by    |  | MinIO, AWS         |  | mail relay         |
| Litestream to    |  | videos/ thumbs/    |  |                    |
| the bucket       |  | litestream/        |  |                    |
+------------------+  +--------------------+  +--------------------+

The AI providers are Google Gemini, OpenAI and Alibaba Qwen (chosen per workspace), plus Anthropic for summaries when the operator sets it as the server-wide fallback. Mail goes out through the first configured of SMTP, Brevo's API or the Apps Script relay.

Request flow

  1. A page such as /app is a static HTML file. The server only injects link-preview tags into /v/:id and /s/:token.
  2. The page's JavaScript calls the JSON API under /api with the session cookie.
  3. The API checks the session, works out the caller's role, and reads or writes SQLite.
  4. The video element streams from /media/.... The server checks access, then either streams the bytes itself (with HTTP Range support) or redirects to a short-lived signed URL for the bucket. On the hosted instance that URL points at the Cloudflare Worker's /_b2/, which passes it on to Backblaze (VISTILLA_S3_DOWNLOAD_PROXY), because what leaves Backblaze for Cloudflare is free.

Why it is shaped this way

  • One process, no queue service. Background work (transcripts, summaries) is stored as rows in SQLite and picked up by workers in the same process. A replica can turn its workers off with VISTILLA_RUN_WORKERS=0.
  • No front-end build. Pages and modules are served as written. Asset URLs never change, so every asset is sent with Cache-Control: no-cache and revalidated (a cheap 304) rather than going stale.
  • No AWS SDK. S3 access is a small hand-written SigV4 signer, which keeps the binary and the build small.
  • Free hosting. The hosted instance runs on a free Render web service with no disk. A new server takes the database over from the running one (or, with none running, Litestream restores it from the bucket) and Litestream streams every change back. See Deployment.

The parts

Part Page
Rust server: modules, startup, workers, middleware Backend
Pages, modules, recording in the browser, theming Frontend
Tables and migrations Database
Uploads, seek index, trimming, silences, streaming Media pipeline
Transcription, summaries, chapters, captions AI pipeline
In-app notifications, email Notifications and email
Sessions, roles, encryption, headers Security