# SimpleAudit Studio — Deployment Status: current (matches the code) Date: 2026-10-03 This is the practical reference for running Studio. Every setup opens the app at , prepares the database, and creates the admin user and default workspace when needed. | # | Setup | Start with | Data | |---|------|---------------------|----------| | 1 | **Quick run** | `uvx simpleaudit-studio@latest` | `~/.simpleaudit-studio/` | | 2 | **Docker** | `docker build ...` then `docker run ...` | Docker volume | | 3 | **Compose** | `cp .env.example .env` then `docker compose up -d` | Docker volumes | | 4 | **Development** | `uv run manage.py dev` | Local files | Setups 1–3 need no separate database server: the app bundles a local SQLite file and an embedded job-queue engine. Only Compose runs the full multi-container stack with PostgreSQL. Chat is on by default. To turn it off, use `--disable-chat` where available or set `SIMPLEAUDIT_CHAT=off`. The flag takes priority over the environment value. `uv run manage.py mode` prints the active setup and settings. ## 1. One-liner (`uvx simpleaudit-studio`) ```bash uvx simpleaudit-studio # models point at api.openai.com; add a key in the UI uvx simpleaudit-studio --mock # built-in mock model server, simulated results uvx simpleaudit-studio --disable-chat # start without the chat assistant ``` One process: migrates the database, creates the admin user and default workspace, seeds scenario packs and model connections, starts the embedded job queue, and runs the web app and worker. Data lives in `~/.simpleaudit-studio/` (`SIMPLEAUDIT_DATA_DIR` moves it): `studio.sqlite3` is the database, `embedded-pg/` the job queue. Back up that folder to back up your work. The same root `Dockerfile` also powers the hosted [Hugging Face Space demo](https://sushantgautam-simpleaudit-studio.hf.space) (demo login `studio` / `localdevpass123`; override the secrets in the Space's settings). The Space is disposable — its data is lost on restart. ## 2. Docker — one container The root `Dockerfile` builds everything into a single container: web app, worker, embedded job queue and chat, with all data in `/data`. ```bash git clone https://github.com/SimulaMet/SimpleAuditStudio cd SimpleAuditStudio docker build -t simpleaudit-studio . docker run -d -p 8000:7860 -v sa-data:/data --name simpleaudit-studio simpleaudit-studio ``` - The container serves on port **7860** (the Hugging Face Space convention); `-p 8000:7860` maps it to on your machine. Use `-p 7860:7860` if you'd rather keep the same number. - All data (database, job queue, chat) lives in the `sa-data` volume, so the container can be deleted and recreated without losing anything. Without the `-v`, data is lost when the container is removed. - Login is `studio` / `localdevpass123`. For anything shared, add `-e BOOTSTRAP_PASSWORD=` to the `docker run` line (it only takes effect on first start, while the admin is created). - Chat is on by default; add `-e SIMPLEAUDIT_CHAT=off` to run without it. - Updating: rebuild the image, stop and remove the old container, run the same `docker run` line again — the `sa-data` volume carries over. ## 3. Compose (teams and production) ```bash cp .env.example .env # set POSTGRES_PASSWORD, BOOTSTRAP_PASSWORD, DJANGO_SECRET_KEY docker compose up -d docker compose --profile mock up -d # also start the mock model server ``` The full stack, each piece in its own container (`docker-compose.yml`): - `postgres` — the database; `deploy/postgres-init.sql` adds the queue's database on first start. - `hatchet-server` — the job queue. Its dashboard (8888) and gRPC (7077) ports are published on 127.0.0.1 only: the default `hatchet-lite-dev` image has authentication disabled, so never expose them on a public interface. - `worker` — background jobs: audit runs, the stuck-run sweeper, monitor ticks. - `web` — the app at (gunicorn). On start it applies the database updates, creates the admin and workspace, and seeds scenario packs (`SEED_ON_BOOT=false` skips seeding; `SEED_DEMO_AUDITS=false` keeps only the demo runs). Gunicorn is configured in `gunicorn.conf.py` with threaded workers; each open live audit-run event stream holds one thread, so `GUNICORN_WORKERS` × `GUNICORN_THREADS` (default 2 × 16) caps concurrent viewers plus regular requests. Tune via those env vars. - `open-webui`, `chat-proxy` — the chat assistant at `/chat/` (on by default, below). - `mock-model` — optional profile `mock`; a fake model API for testing. Startup checks refuse to boot until `DJANGO_SECRET_KEY`, `POSTGRES_PASSWORD` and `BOOTSTRAP_PASSWORD` in `.env` are set to real values (empty or `change-me` is rejected). Your data lives in the named volumes `postgres_data` (database) and `open_webui_data` (chat). `web` and `worker` build from `deploy/compose/Dockerfile`. Chat is on by default: `.env` ships with `SIMPLEAUDIT_CHAT=docker` and `COMPOSE_PROFILES=chat`. To run Compose without chat, remove or comment out both lines and run `docker compose up -d` again. (If your `.env` predates chat being on by default, add those two lines to get it.) ## 4. Development (`uv run manage.py dev`) The one standard way to run the app while working on this repository: ```bash cp .env.dev.example .env # local settings (SQLite, local admin) uv sync --extra dev # install the app + dev tools, once uv run manage.py setup_local # first time only: create database + admin + sample data uv run manage.py dev # start: web + worker + queue + chat → http://localhost:8000 ``` On every start it applies database updates, makes sure the admin user and default workspace exist, and prints a one-time sign-in link, opening the browser signed in (`--no-browser` skips the pop-up). Day-to-day notes: - Only one Studio can run at a time on a machine — the one-liner, the dev stack and the Docker container all use the same embedded job-queue folder. - The web app hot-reloads on code changes; the background worker does not. After changing `infra/worker.py` or `infra/engine.py`, restart. - Flags: `--disable-chat` (start without the chat assistant), `--no-worker` (web + API only), `--no-reload` (no auto-reload), `--port` (default 8000). - Tests: `uv run pytest -n auto -m "not slow and not embedded_hatchet"` (fast set) or drop the marker filter for everything. ## 5. Models The starter connections use a built-in mock model. For real audits, open **Models** in Studio and add any OpenAI-compatible service. Use its base URL and API key; local services such as Ollama usually do not need a key. Examples: | Service | Base URL | |---|---| | Ollama | `http://localhost:11434/v1` | | vLLM | `http://your-server:8000/v1` | | OpenAI | `https://api.openai.com/v1` | Then start an experiment from **New Experiment**. The `--mock` option starts a fake model for local testing without an API key. ## 6. Environment variables Only these are read by the code. `SIMPLEAUDIT_MODE` is the single knob you set to pin the mode (`single-docker`, `compose`, `embedded`, `dev`); the entry points set it for you, and `manage.py mode` shows the resolved result. The flags marked **internal** are set by the entry points / `SIMPLEAUDIT_MODE` — you only override them when you deliberately need to. | Variable | Purpose | |---|---| | `SIMPLEAUDIT_MODE` | Pin the run mode: `single-docker`, `compose`, `embedded`, `dev`. See `manage.py mode`. | | `DJANGO_SECRET_KEY` | Required outside local SQLite mode. | | `DJANGO_DEBUG` | `true` for debugging only. `dev` sets it; the others leave it off. | | `DJANGO_ALLOWED_HOSTS` | Comma-separated; default `*` (see security notes). | | `SIMPLEAUDIT_SECURE_COOKIES` | Session and CSRF cookies are `Secure` by default (production). Set `false` only for a plain-HTTP local replica, e.g. `http://127.0.0.1`, where the browser would otherwise drop the login cookie. | | `DJANGO_CSRF_TRUSTED_ORIGINS` | Extra trusted origins (full URLs). | | `GUNICORN_BIND`, `GUNICORN_WORKERS`, `GUNICORN_THREADS`, `GUNICORN_TIMEOUT`, `GUNICORN_GRACEFUL_TIMEOUT`, `GUNICORN_KEEPALIVE` | Gunicorn tuning (`gunicorn.conf.py`). Defaults: `0.0.0.0:8000`, 2, 16, 60, 30, 5. | | `POSTGRES_DB`, `POSTGRES_USER`, `POSTGRES_PASSWORD`, `POSTGRES_HOST`, `POSTGRES_PORT`, `POSTGRES_CONN_MAX_AGE` | PostgreSQL connection (`compose` mode). | | `SIMPLEAUDIT_LOCAL_SQLITE` **internal** | `1` = local SQLite database at the repository's `dev.sqlite3`. Set automatically by the `dev` entry point and by the test suite. | | `SIMPLEAUDIT_SQLITE_PATH` | Override where the local dev SQLite file lives. Default: `/dev.sqlite3`. Relative paths resolve from the repo root; absolute paths are used as-is. Ignored unless `SIMPLEAUDIT_LOCAL_SQLITE=1`. | | `VISUALIZER_RESULTS_DIR` | Folder of JSON audit-result files shown by `/visualizer/`. Dev defaults to `./results`; the folder is created on demand. | | `VISUALIZER_MAX_INSPECTED_FILES` | Max JSON files to inspect per scan (default `5000`). When hit, the tree is returned with `truncated: true, reason: "file_limit"`. | | `VISUALIZER_SCAN_TIME_BUDGET_S` | Wall-clock time budget for the scan in seconds (default `5`). When hit, the tree is returned with `truncated: true, reason: "time_budget"`. | | `VISUALIZER_MAX_TREE_DEPTH` | Max directory depth to walk (default `8`). When hit, the tree is returned with `truncated: true, reason: "depth_limit"`. | | `VISUALIZER_MAX_FILE_SIZE_MB` | Max JSON file size in MB to parse (default `100`). Larger files are skipped. | | `SIMPLEAUDIT_MINIMAL` **internal** | `1` = single-process demo mode. Set by the CLI. | | `SIMPLEAUDIT_CHAT` | Chat mode: `embedded` / `docker`, or `off` (and `disabled`, `false`, `no`, `0`, unset). On by default in every mode (`embedded` in the single-process modes, `docker` in compose — set in `.env`). Turn off with this var or the `--disable-chat` flag; the flag beats the env value (flag > env > mode default). | | `SIMPLEAUDIT_OTLP_ALLOW_UNAUTHENTICATED` | Accept OTLP pushes with no `Authorization` header via an enabled `none` credential. Defaults to `true` (Open WebUI's boot exporter cannot send credentials). Set `false` to answer 401 to every unauthenticated export. | | `SIMPLEAUDIT_OTLP_HOLD_SECONDS` | How long spans of a trace no run has claimed are held before pruning. Defaults to 600. `0` stores nothing for unclaimed traces; a run claims each turn's trace asynchronously, so very small values can drop early spans of an audit trace (30+ is safe). | | `SIMPLEAUDIT_OTLP_AUTH_CACHE_SECONDS` | How long a verified OTLP `Authorization` header is cached in memory (bad ones: 5 s), so steady exporters cost no DB query per batch. Defaults to 30. A change in the same process applies at once; another web process sees a revoked or rotated secret within this time. `0` disables the cache. | | `SIMPLEAUDIT_CHAT_SYNC_RETRY_SECONDS` / `SIMPLEAUDIT_CHAT_SYNC_RETRY_ATTEMPTS` | A startup chat sync that fails because Open WebUI is still booting is retried in the background (the worker process) every N seconds, up to M times. Defaults: 15 s, 40 attempts. | | `SIMPLEAUDIT_CHAT_OTLP_PASSWORD` | Basic-auth password Open WebUI exports its spans with. Optional: embedded and compose derive one from `DJANGO_SECRET_KEY` when unset. Set it to use your own (compose: in `.env`). | | `SIMPLEAUDIT_OTLP_MAX_BODY_BYTES` | Largest OTLP export body, raw or gunzipped, before the receiver answers 413. Defaults to 8 MiB. | | `SIMPLEAUDIT_CHAT_OTLP` | Export Open WebUI structural spans to Studio. Defaults to `true` in all modes (agent audits use the spans as trace evidence). Set `false` to opt out. Content capture remains independently opt-in. | | `SIMPLEAUDIT_DATA_DIR` | Where single-process mode keeps its data (default `~/.simpleaudit-studio`): the SQLite database and embedded Hatchet's PostgreSQL. | | `SIMPLEAUDIT_EMBEDDED_PG_DIR` | Override just embedded Hatchet's PostgreSQL directory (default `/embedded-pg`). | | `HATCHET_SERVER_URL`, `HATCHET_GRPC_URL`, `HATCHET_API_KEY`, `HATCHET_TOKEN_FILE`, `HATCHET_TLS_STRATEGY` | External Hatchet connection (`compose` mode). | | `HATCHET_EMBEDDED_HANDSHAKE` **internal** | Set by the entry points so the web app and worker find the embedded engine; don't set it yourself. | | `WORKER_POOL` | Worker label (`cpu` by default). | | `BOOTSTRAP_USERNAME`, `BOOTSTRAP_EMAIL`, `BOOTSTRAP_PASSWORD`, `BOOTSTRAP_PROJECT_NAME` | First admin user and workspace. | | `DEMO_MODE`, `DEMO_USERNAME`, `DEMO_PASSWORD` | Prefilled demo login and the HF iframe cookie/CSRF policy. | | `DEMO_BANNER_HTML` | Optional custom HTML for the demo-mode login banner. Empty = built-in banner. Trusted operator content only. | | `WORKOS_CLIENT_ID`, `WORKOS_API_KEY` | Optional passwordless email sign-in. | | `SIMPLEAUDIT_GRAVATAR` | `true` = show Gravatar avatars (the browser sends the MD5 hash of the user's email to gravatar.com). Default off: the user's initial is shown. | | `SENTRY_DSN`, `SENTRY_ENVIRONMENT` | Optional error tracking. | | `LOG_LEVEL`, `PORT` | Logging level; web port for the CLI. | | any name in a connection's `secret_reference` | Model API key read at execution time. | ## 7. Management commands | Command | Does | |---|---| | `setup_local` | migrate + bootstrap + seed (local dev, one-shot). | | `bootstrap_platform` | Create the admin user and default workspace (idempotent). | | `seed_platform` | Import scenario packs and model connections, plus demo runs (`seed_demo_audits`) and the demo "Support Refund Assistant" agent (skip with `--skip-demo-agent`). | | `seed_agentic_scenarios` | Seed the default agentic audit scenario set (D01-D10). | | `seed_agentic_demo` | Preload completed synthetic results, traces, and a separate Agentic verdict rollup after the demo Agent sync; never runs an audit or calls a model. | | `dev` | Local dev stack (embedded, hot-reload, chat, one-time sign-in). `--disable-chat`, `--no-worker`, `--no-reload`, `--no-browser`. | | `dev_server` | Lower-level version of the same stack. Use `dev` instead. | | `mode` | Print the resolved run mode and its settings. | | `run_worker` | Hatchet worker (Compose `worker`). | | `run_monitors` | One monitor pass, for an external cron if you don't run the worker sweeper. | | `purge_test_data` | Delete runs, scenario sets and models left by smoke tests (`--dry-run` first). | ## 8. Operations - **Health:** `/healthz` (liveness), `/readyz` (readiness), `/health/` (admin panel). - **Backups:** `docker compose exec postgres pg_dump -U simpleaudit simpleaudit > backup.sql`. Restore with `psql` into an empty database before starting `web`. - **Upgrades:** pull, `docker compose build`, `docker compose up -d`. Migrations run when `web` starts; take a backup first. ## 9. Security notes - Set a strong `DJANGO_SECRET_KEY` and change `BOOTSTRAP_PASSWORD` (start-up checks reject empty values and `change-me`). - `DJANGO_ALLOWED_HOSTS=*` is allowed because HF Spaces and similar proxies use unpredictable host names. Behind your own domain, list it explicitly. - Direct API keys are stored on the connection in the database (never in run records or API responses); prefer `secret_reference` environment variables in production.