prompt-a-thon

Architecture

Wiki home · prompt-a-thon.bitilia.com

browser
  → Caddy or nginx (TLS)
    → Gunicorn, 127.0.0.1:8100, user promptathon
        → Flask app (app.py:create_app)
            → SQLite instance/prompt.db
            → uploads/
            → Unix socket → convert daemon, user promptathon-convert
                              → LibreOffice

app.py builds the Flask app, registers blueprints, CLI commands, security headers, and a few jobs that run on requests (unverified-participant cleanup, at most once a minute per worker).

Path Role
routes/public.py Home, privacy, terms, email tracking image
routes/auth.py Email code and Microsoft
routes/participant.py Dashboard, submit, academy
routes/admin.py Organiser tools
routes/gdpr.py Public data request
models.py Users, submissions, settings, audit, encryption
models_gdpr.py Consent rows and data requests
services/email.py All outbound mail and certificate HTML
services/file_pipeline.py Upload checks and the socket client
conversion/convert_daemon.py The process systemd runs
services/tasks.py, services/academy.py JSON content
middleware/security.py CSP, blocking, connection log
setup.py Installer
deploy/ systemd units and proxy examples

services/convert_daemon.py is not shipped. The daemon that runs is conversion/convert_daemon.py.

Gunicorn is started with 2 workers and 4 threads, timeout 120 seconds. Rate-limit storage is memory inside each worker, so a limit is not one global bucket. SQLite WAL is what allows those workers to read while another writes. It is still one file on one machine.

The admin security page can load a world map (static/world-110m.json, D3, TopoJSON). Those files are third-party. See NOTICE.