* System audit: fix security, data-integrity and correctness defects
Iterative audit (32 passes) of the whole system; every confirmed defect is
fixed with a regression test. Findings, evidence, open owner decisions and
continuation steps are in Audit.md.
Highlights:
- Secrets: connection keys can no longer name platform secrets or be sent
to a host other than the one they were resolved for; the Open WebUI admin
token only goes to Studio's own Open WebUI; embedded Open WebUI no longer
inherits Studio secrets; auth tokens are revoked on password change.
- Authorization: workspace-scoped role checks on experiment visibility,
agents/KB/tools API, connections, OTLP credentials, run cancel and
monitor run-now; stale sessions lose access to removed workspaces;
visualizer API requires login; public run pages hide endpoint URLs.
- Run integrity: conditional cancel/fail/finalize, no downgrade of
completed results, per-run auditor dispatch for concurrent runs, frozen
agent endpoβ¦
7 days ago
feat(agentic): route traced agent runs through studio agent modelsβ
- Traced agent runs use the studio.agent-* wrapper model with agent tool ids.
- Retrieval checks read gen_ai.retrieval.documents instead of data_source.
- Duplicate model-call flags and redundant badge in the timeline.
- Default agentic pack seeded from infra/fixtures; acme demo pack removed.
- Pipelines/Langfuse sidecar removed; chat sync installs RAG overrides and OTLP credentials.
- Service account (trusted-header admin) for chat API calls.
Co-Authored-By: Claude <noreply@anthropic.com>
8 days ago
feat(chat): startup sync of connections, functions, OTLP credentials and agentsβ
- chat/sync.py: startup_sync() runs push_now (connections, bundled functions,
OTLP credentials) and pushes every agent. Best-effort; never fails startup.
ensure_agent_otlp_credentials() gives each Open WebUI Agents connection a
'none' OTLP credential when chat tracing is on, so agent runs show the trace
checkbox.
- sync_chat_startup management command; called by the worker, the compose web
boot, manage.py dev and the spin/uvx launcher.
- docs/chat.md describes the startup sync.
Co-Authored-By: Claude <noreply@anthropic.com>
feat(chat): per-agent retrieval via bundled RAG overrides filter; drop Langfuse pipelinesβ
Retrieval:
- Agent search modes are Semantic and Hybrid; top K, final K (replaces the
reranker-based rerank top K), relevance threshold, BM25 weight and full
context are saved on the agent. Presets and reranker fields are removed.
- chat/openwebui_functions/studio_rag_overrides.py (global Filter) reads
meta.simpleaudit.retrieval and runs retrieval for that model. Models
without those settings use the global RAG settings.
- chat/sync.py installs the filter, keeps it active and global, and
refreshes it at worker start (ensure_chat_functions), on connection sync
and via sync_chat_models. seed_platform does not need a re-seed.
Seeding:
- seed_platform also loads the Acme Agentic Safety scenario set.
Remove the Langfuse pipelines service (OTel export to Langfuse replaces it):
- Delete chat/pipelines_service.py, chat/pipelines_sidecar.py, the
studio_langfuse_capture filter and their tests.
- Drop the pipelines sidecar from dev_server and the uvx CLI, the pipelines
port fromβ¦
17 days ago
fix: OTLP tracing, timeline merge, port drift, and agent pickerβ
- Fix OTLP_ENABLED frozen before setdefault: move SIMPLEAUDIT_CHAT_OTLP
into manage.py before django.setup() so OWUI exports real spans
- Add _fix_owui_connection_url() to dev_server startup: auto-corrects
the OWUI ModelConnection base_url after port allocation shifts
- Merge synthetic tool/response spans into trajectory timeline when
OWUI only exports CHAIN+LLM (no TOOL/RETRIEVER child spans)
- Add retry-on-empty guard in _streaming_call for SimulaChat's
intermittent empty completions on multi-turn conversations
- Remove leaking {# ... #} template comment from model_pickers.html
- Unify bootstrap password on localdevpass123 (cli, settings, Dockerfile,
docs) + drift-guard test
- Agents target-only in model pickers; server-side guard in parse_design
- OTLP on by default in all modes (.env.example, dev_server, docs)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds simpleaudit_studio/lifecycle.py so agents can safely start, reuse,
stop, restart, reset and uninstall a local stack without killing unrelated
processes or resetting real data.
- Instance identity: sha256 of canonical checkout path (dev) or data dir
(spin); file locks (owner.lock non-blocking, ports.lock blocking).
- Port allocation before Django starts; worktree baseline 8100.
- _live() validates PID running + create_time + UID to reject PID reuse.
- Destructive boundaries: refuses custom SIMPLEAUDIT_DATA_DIR /
SIMPLEAUDIT_SQLITE_PATH on reset/uninstall; uv cache cleanup is
package-scoped only (never global `uv cache clean`).
- manage.py `dev` and spin CLI dispatch subcommands before django.setup().
- devctl.sh rewritten as a thin compatibility wrapper; all legacy
kill -9 / pgrep / lsof port-based cleanup removed.
Fix orphaned runserver on shutdown: _stop_web now SIGTERMs the reloader
subtree by PID (psutil), never by process group, because the reloader tree
shares the oβ¦
24 days ago
feat(chat): self-contained Pipelines filter service for Langfuse tracing (issue #21)β
Add a tiny FastAPI service that speaks the Open WebUI Pipelines filter
protocol so Studio chats can be traced to Langfuse without Docker or the
pipelines torch/opencv dependency pile. Runs as an opt-in background
sidecar (SIMPLEAUDIT_PIPELINES=on), wired into both manage.py dev and spin.
Captures per chat turn, all at no cost (already in the request/response):
- full messages array (system prompt + history) as trace input/output
- model_parameters (temperature, max_tokens, tools, stop, etc.)
- token usage, finish_reason, requested_model
- first-class session_id (chat id) + user_id for Langfuse Sessions/Users views
- tags: simpleauditstudio
- raw_params catch-all so nothing is silently dropped
Endpoints: /v1/models, /v1/pipelines, /v1/chat/completions (forward or canned),
/v1/{id}/filter/inlet, /v1/{id}/filter/outlet. Uses Langfuse SDK v4 API
(start_as_current_observation + propagate_attributes).
Adds fastapi, uvicorn[standard], langfuse to deps; documents settings in
.env.dev.examplβ¦
29 days ago
Merge PR #19: fix(visualizer) bounded scan + test gRPC hangβ
The visualizer file-tree scan (GET /api/visualizer/api/files/) performed an
unbounded os.walk over the results directory, parsing every JSON file with no
limits. When --results_dir resolved to a large tree (e.g. $HOME), the scan
took minutes and the browser fetch stayed pending forever.
Bounded scan (infra/visualizer.py):
- Limits: 5,000 inspected JSON files, 5s budget, depth 8, 100 MB/file β
enforced during traversal, not afterward.
- Partial results with truncated/reason/inspected/elapsed_seconds metadata;
never reports a truncated tree as complete.
- Persistent metadata index (~/.simpleaudit-studio/visualizer-index.json)
keyed by (path, mtime, size) β unchanged files are not re-parsed.
- watchfiles for incremental updates via /api/visualizer/api/files/changed/.
- Frontend shows a truncation warning banner and polls the changed endpoint.
- CLI warns when results dir is home/cwd and displays scan limits.
Test fix (infra/tests/test_api_e2e.py):
- Mock audits.views.submit_audit_β¦
fix(visualize-only): land on /visualizer/ after one-time sign-inβ
The auto-login view always redirected to the dashboard, so
`uvx simpleaudit-studio --visualize-only` dropped the user on the
unpopulated dashboard instead of the visualizer.
- auto_login_view now honors SIMPLEAUDIT_AUTO_LOGIN_NEXT (server-side
env var, relative paths only β no open redirect) as the post-login
target, falling back to the dashboard
- visualize-only mode sets it to /visualizer/, so both the printed
one-time sign-in link and the auto-opened browser land on the
visualizer
- tests for the new redirect + relative-path guard
Seed descriptive Acme scenarios and a completed synthetic run with trace and Agentic verdict rollups, without running a model. Preserve the text-judge result separately from deterministic trace checks.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
setup_local seeds the demo agent, knowledge base, and tool while Open
WebUI is not yet up (chat disabled), so they end up as local-only rows with
empty external_ids. The OWUI-backed /agents/knowledge/ and /agents/tools/
pages showed nothing because nothing was ever pushed to OWUI.
Add backfill_demo_chat_resources (infra/seed.py) β called from the existing
chat-ready hook in both dev_server.py and cli.py β which idempotently pushes
the demo agent, KB, and tool to Open WebUI and backfills their external_ids.
Agent is pushed last so push_agent can wire the KB/tool links into the OWUI
model. Never raises; safe to retry on every startup.
Also fix a UNIQUE(project,name) IntegrityError on POST /agents/sync/:
_sync_openwebui_resources was keying KnowledgeBase.update_or_create on
external_id, which could CREATE a duplicate row whose name already existed
from the seed/backfill. Now keys on name (the UNIQUE column), matching the
Tool path.
Tests: 6 new (backfill: full, rewire, idempotent, disaβ¦
refactor: drop the pure-Python chat front-door fallbackβ
Caddy is the only front-door implementation. caddyserver is a hard
dependency with wheels for every platform, so the ~520-line Python handler
could only run on a broken install, where it silently diverged from the
Caddyfile it was meant to mirror (second implementation of identity
strip, forward-auth, CSS mask, favicon, /workspace repair and WS piping).
_caddy_binary() now raises instead of returning None and serve() has no
fallback; a missing binary (or Caddy that never binds) surfaces at start,
and the dev/CLI callers stop Open WebUI and continue the rest of the stack.
Removes the fallback-only stubs and their 19 tests.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
feat(chat): unify Studio + Open WebUI behind one Caddy front door at /chat/β
Replaces the two-port embedded model (Studio :8000 + OWUI :8801 in an
iframe) with a single-origin, single-port wiring, identical for local dev
and Docker:
- Caddy is the ONLY published listener (:8000). Studio and Open WebUI
publish no ports; /chat/* reverse-proxies to the subpath build with the
prefix preserved (handle, not handle_path); /ai/ and everything else
go to Django.
- Uses the subpath-capable Open WebUI fork wheel (v0.11.4-subpath,
WEBUI_SUBPATH=/chat), so OWUI emits every URL prefixed with /chat and
no Caddy-side URL rewriting is needed.
- Forward-auth to /chat/authz injects trusted identity headers
(X-Studio-Email/Name/Role); client-forged values are stripped at site
level; 401 redirects to the Studio login.
- Studio's /chat/css-mask serves the embed stylesheet (admin vs plain
mask) in place of a mounted custom.css.
- Local dev: manage.py dev manages a bundled Caddy (uvx caddy /
caddyserver) with a generated Caddyfile; Docker: caddy:2-alpine
chat-proxβ¦
Add four run modes; make chat always disableableβ
Introduce a single source of truth for how the stack runs
(config/runtime.py: ModeProfile, resolve_mode, effective_chat_mode) and
expose four modes: single-docker, compose, embedded, dev.
- `manage.py dev` (new): zero-Docker dev stack (SQLite + embedded Hatchet),
chat on by default, with --disable-chat/--no-worker/--no-reload.
- `manage.py mode` (new): diagnostic of the active mode.
- dev_server: the removed legacy external-Postgres path is gone; bare
`dev_server` now errors listing the four modes.
- Chat precedence is flag > env > default, resolved in manage.py before
django.setup() so --disable-chat wins even when a .env pins chat on.
The uvx CLI already applied the same rule.
- Docs: README four-modes section, deployment.md restructure, spec update,
.env.local.example chat section.
Verified: 17 runtime unit tests + 691-test fast suite pass; dev with and
without --disable-chat smoke-tested (8801 up/down accordingly); ruff clean.
Co-authored-by: Copilot <223556219+Copilotβ¦
feat(visualizer): absorb SimpleAudit result visualizer into Studioβ
Port the single-file HTML viewer (formerly `simpleaudit serve` /
`export-html` in the core) into Studio as Django views plus two
self-contained static assets.
- infra/visualizer.py: file-tree, JSON/image APIs, standalone HTML export,
and the /visualizer/ + /visualizer/upload/ SPA pages
- config/urls.py: routes for the SPA and its APIs. The SPA hardcodes
"/api/*" fetch paths prefixed by __VISUALIZER_API_BASE, so the API routes
live under /api/visualizer/api/* to match
- simpleaudit_studio/cli.py: --results_dir + --visualize-only to browse a
folder of dumped results server-side
- templates: export-html entry points on run detail/result and compare
- tests: cover shape detection, file tree, JSON/image endpoints, path
traversal, login gating, and standalone export
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
chore(prod): run local dev with DEBUG off and enable prod security headersβ
- CLI now defaults DJANGO_DEBUG=false so local dev exercises real
production behavior (host checks, static serving, no debug pages).
Override with DJANGO_DEBUG=true to get the debug tooling back.
- Add SECURE_SSL_REDIRECT, HSTS, secure cookies, and nosniff for real
HTTPS deployments; gated off for local/minimal/demo mode.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add SIMPLEAUDIT_CHAT_OTLP to export Open WebUI spans to Studioβ
When enabled, Studio starts Open WebUI with OpenTelemetry tracing on and pointed at its own OTLP listener (POST /otlp/v1/traces), so the chat module's spans land in the same place as any other target's. Off by default. Wired for both embedded (proxy) and docker modes, with docs and tests.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Migrate test runner to pytest; fix ruff lint errorsβ
- Switch CI from manage.py test to pytest --testmon -n auto with a
.testmondata cache for affected-test selection
- Update README test section to document pytest commands (fast, full,
affected-only, targeting)
- Fix 21 pre-existing ruff errors (unused imports, unsorted imports,
Optional[X] -> X | None, f-strings, startswith tuple, nested with)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
fix: make /auto-login/ one-time via single-use tokenβ
The demo server binds 0.0.0.0, so the credential-less /auto-login/
endpoint let any machine on the LAN sign in as the bootstrap user.
- CLI generates a secrets.token_urlsafe(32) token at startup and
prints the full one-time sign-in link in the banner
- auto_login_view checks the ?token=... param in constant time
(hmac.compare_digest) and consumes it on first use, so the URL
cannot be replayed
- browser readiness polling now hits /healthz instead of the token
URL so the probe does not consume the one-time token
- if no browser opens, the printed link is the manual fallback
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
spin: detect port occupants and offer to stop our own instancesβ
When a needed port is held, identify the occupant via psutil (per-PID
connections, which works where lsof and the global psutil call are
blocked by macOS proc_pidinfo restrictions). If it is another Studio
instance or one of its derivatives (Open WebUI, the Hatchet sidecar),
offer to stop it and spin cleanly β automatic with --yes, declined with
--no-force-kill. Anything else, or a declined offer, exits with a free
port and the exact flag or env var that moves the conflicting one.
Also fixes two readiness/cleanup bugs found while debugging occupied
ports: wait_until_ready no longer trusts a 200 from a dead process
(another Open WebUI on the port answered for ours), and the pid file is
never overwritten or deleted when it records a live process that is not
ours.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
feat: push model connections to chat automatically, and quieten the browser consoleβ
Connections now reach Open WebUI on their own: chat/signals.py follows
ModelConnection and RegisteredModel, and the CLI syncs once as soon as chat
answers, which covers whatever changed while it was off. The management command
stays for a manual run and for --dry-run, and now shares the same code path.
The push deliberately stays off the request's path β on_commit so a rolled-back
row is never pushed, in a background thread so a save does not wait on a second
service, debounced so an edit that writes a connection and its models is one
push, and best-effort so a chat that is down is logged and forgotten. Studio's
data is the source of truth; the next push catches up.
A connection's registered models are pushed as that provider's model_ids, so chat
offers what Studio registered rather than everything the provider lists. No
registered models means no restriction.
Two things the browser console complained about:
- WebSocket upgrades were answered with 501, so Socket.IO retried and fellβ¦
refactor: move chat into its own Django app, and scaffold the API in both directionsβ
The module was spread across infra/ (chat.py, chat_proxy.py, chat_api.py), two
management command folders, infra/tests and the shared templates folder, which is
the wrong shape for something that is meant to be optional and removable. It is
now one app:
chat/
config.py what the module is configured to do, and who you are
views.py urls.py the iframe page and /chat/authz
proxy.py the forward-auth proxy + Open WebUI's lifecycle
api.py talking to Open WebUI's API, both directions
management/ sync_chat_models, chat_knowledge
templates/ tests/
The app is installed either way so its templates, commands and tests resolve;
SIMPLEAUDIT_CHAT still decides whether anything runs. Removing the feature is now
deleting a folder and four one-line references.
chat/api.py is new, and is the scaffolding for tighter integration. It
authenticates the way the proxy makes the browser authenticate β the trusted
headers to /api/vβ¦
It is printed from a background thread minutes after startup, and stdout is
block-buffered when the CLI's output is a file or a pipe rather than a terminal,
so the line could sit unwritten for the rest of the run.
fix: manage Open WebUI's lifecycle like the embedded Hatchet engineβ
Chat was started with a bare Popen and stopped with terminate(), which left two
ways to orphan it. `uvx open-webui` is a launcher with the real server as its
child, so terminate() signalled the launcher and left the server holding the
port; and a hard-killed run (SIGKILL, crash, closed terminal) never got to stop
anything at all, so the next start failed to bind.
Open WebUI now follows the same pattern as the embedded engine:
- one instance per process, guarded by a lock; a second start returns the first
- started in its own process group, so stopping it reaches the real server
- stopped by the CLI's shutdown path and by atexit, SIGTERM then SIGKILL
- its PID recorded in openwebui/open-webui.pid, so the next start can stop a
leftover from a killed run β only when its parent is gone, never one that
belongs to another running Studio
Verified against the real Open WebUI: explicit stop, exit without stopping, and
SIGKILL of the Studio process followed by a restart all end with no suβ¦
feat: say what the bundled chat is doing while it startsβ
Open WebUI takes minutes to come up on a first run β it is downloaded, then it
migrates its database β and until now the console said only "first start
downloads it" before the chat's own log poured into the terminal.
The CLI now prints that chat is starting, warns on a first run that this means a
~1 GB download, and names the folders its data and log live in. A background
thread polls until Open WebUI answers and prints either the ready line with the
/chat/ URL, or why it stopped, pointing at the log. Studio and the worker come up
meanwhile, as before.
Open WebUI's own output now goes to openwebui/server.log instead of the console,
which also makes a failure readable after the fact. The readiness poll uses
urllib rather than httpx so it does not log a request line every two seconds.
feat: chat is part of the bundle, opted out rather than opted inβ
The chat module now runs by default where it can: `uvx simpleaudit-studio`
starts Open WebUI alongside Studio, and `.env.example` sets both switches the
Compose deployment needs, so a plain `docker compose up -d` brings the chat
containers up without an extra --profile flag.
Opting out:
uvx simpleaudit-studio --disable-chat (--no-chat also accepted)
SIMPLEAUDIT_CHAT=disabled (off/false/no/0/none too)
comment out SIMPLEAUDIT_CHAT + COMPOSE_PROFILES in .env for Compose
SIMPLEAUDIT_CHAT previously recognised only "embedded" and "docker", so any
other spelling β including "disabled" β silently meant off. It now reads a
documented set of disabling values and treats anything else as on, which is
also what makes the CLI default work.
Deployments that set neither the variable nor the Compose profile are
unaffected: the routes still 404 and nothing extra runs.
feat: optional Open WebUI chat module with Studio single sign-onβ
Adds an off-by-default module that embeds Open WebUI in Studio at /chat/,
signed in as the Studio user. With SIMPLEAUDIT_CHAT unset nothing changes:
the routes 404, the sidebar has no Chat entry and no extra process runs.
Open WebUI serves from the root of an origin only β it has no base-path
setting and its HTML references /static, /api and /ws absolutely β so it
cannot be proxied under Studio's own /chat/ path. It gets its own origin,
which Studio embeds in an iframe.
Sign-on uses Open WebUI's trusted-header mode. A proxy in front of it asks
Studio who the browser is (GET /chat/authz, the standard forward-auth
contract) and injects the answer as X-Studio-Email/-Name/-Role. Django stays
the only authority on identity; nothing outside it reads sessions or user
tables. Superusers and workspace admins map to Open WebUI's admin role,
everyone else to user.
Two deployments, same contract:
embedded infra/chat_proxy.py, a stdlib HTTP proxy started by
`uvx simpleaudit-studiβ¦
fix: uvx installs keep their data across restarts and upgradesβ
The single-process database lived inside the installed package
(site-packages/demo.sqlite3): every new version started with an empty
database, and `uv cache clean` deleted it.
Data now lives in ~/.simpleaudit-studio/ (SIMPLEAUDIT_DATA_DIR overrides):
studio.sqlite3 plus the embedded Hatchet Postgres, one definition in
simpleaudit_studio/paths.py. The first start copies in the most recently
used old database (this install's own or any uvx install in the uv cache,
via SQLite's backup API so WAL contents come along) and says so. The CLI
prints the data folder on start; nothing is written to the working
directory.
fix(cli): point model connections at OpenAI by default, not the mockβ
After seeding, model connections now keep OpenAI's real base URL
(https://api.openai.com/v1) instead of being repointed at the local
mock server. The mock is now opt-in via --mock (was the default).
- Add _restore_real_model_endpoints() to repair any connection an
earlier version left pointing at the local mock (127.0.0.1/localhost).
- Dockerfile passes --mock so the public HF Space demo keeps working.
- Update tests: verify seeded connections point at OpenAI and that
the repair function fixes mock-pointing connections.
fix(cli): keep mock model connection keyless in minimal-config modeβ
_update_model_endpoints() hardcoded api_key_direct="mock-key" when
repointing seeded connections at the local mock OpenAI server. The mock
server ignores auth entirely, so the key was cosmetic but made the UI
show "π key set". Leave it empty so the connection shows "β no key".
feat: connection-grouped model picker with key warnings; remove legacy ModelEndpointβ
The new-audit model pickers now group models by connection (optgroup) and
show a 'no key' warning for connections without an API key, matching the
/models/ page. AuditRun FKs migrate from the flat ModelEndpoint to
RegisteredModel (with a one-time data migration backfilling existing runs),
and the deprecated ModelEndpoint model, its API endpoints, admin entry, and
all UI references are removed.
fix: clear port-conflict error + clean SIGTERM shutdown for spinβ
- Pre-check port availability before starting embedded Hatchet so a
busy port gives an actionable message instead of a sidecar traceback.
- Handle SIGTERM (kill <pid>) like Ctrl+C so the embedded Postgres and
Hatchet sidecar are stopped cleanly rather than orphaned.
feat: minimal SEO landing page + auto-login for local demoβ
Landing page (templates/public/landing.html):
- Rewrote as minimal, SEO-focused page (76 lines, ~7 KB rendered)
- Hero: H1 'Reproducible AI model auditing, self-hosted' + one-sentence subtext
- CTA: 'Get started' β /login/ (natural for already-running instance)
- 3 feature cards: Frozen inputs, Any model endpoint, Compare & export
- Removed: trust bar, 3 of 6 feature cards, how-it-works section, standalone CTA
- Kept full SEO metadata (title, description, OG, JSON-LD schemas)
Auto-login for local one-liner demo:
- New /auto-login/ view (MINIMAL_CONFIG only, 404 elsewhere) that signs in
the bootstrap user and redirects to dashboard
- CLI opens this URL in default browser after server is ready
- --no-browser flag to skip auto-open
chore: consolidate deps to pyproject.toml + fix all ruff lint errorsβ
Single source of truth: delete requirements.txt, standardize on uv.
- pyproject.toml is now the only dependency manifest (uv.lock pins versions)
- Makefile, Dockerfiles, CI all use 'uv sync' instead of 'pip install -r'
- Add 'server' extra (gunicorn) for the compose image
- Bump simpleaudit 0.2.0 -> 0.2.1 in uv.lock (was already in pyproject)
Fix all 169 pre-existing ruff errors so CI lint step passes:
- Auto-fix: I001, RUF100, UP017, PLR1730, F401, etc. (94)
- Ignore RUF012 (Django Meta/admin/factory false positives) via config
- BLE001: add justified noqa on intentional broad catches (16)
- SIM117: merge nested with statements (5)
- S110/DTZ005/SIM115/EXE001/TRY004/TRY203: targeted fixes (8)
chore: add ruff linting, fix lint violations, fix test indentationβ
- BOOTSTRAP_ADMIN_USERNAME β BOOTSTRAP_USERNAME (default: studio)
- BOOTSTRAP_ADMIN_EMAIL β BOOTSTRAP_EMAIL
- BOOTSTRAP_ADMIN_PASSWORD β BOOTSTRAP_PASSWORD
- DEMO_USERNAME default: admin β studio
- Dockerfile: use studio credentials, change-me password placeholder
The account is a normal platform owner, not a Django admin β the old
naming was misleading.
fix: persist embedded Hatchet Postgres in a fixed dir instead of leaking temp dirsβ
Each uvx run created a fresh tempfile.mkdtemp() for the embedded Postgres
cluster and never removed it, leaking ~218M per run (37 runs β 8GB found
in $TMPDIR). Use a persistent cluster at ~/.simpleaudit-studio/embedded-pg/
(override with SIMPLEAUDIT_EMBEDDED_PG_DIR) so initdb runs once, and
register an atexit hook so the engine stops cleanly even if the caller
forgets. Also align demo bootstrap username assertions with the studio
default.
refactor: merge seed_demo_audits into seed_platformβ
One command now does the full first-run seed:
1. Scenario packs
2. Default model connections
3. Demo audit runs (from pre-recorded fixture)
- CLI calls only seed_platform (no separate seed_demo_audits call)
- docker-compose simplified: single seed_platform invocation
- SEED_DEMO_AUDITS=false passes --skip-demo-audits to opt out
- seed_demo_audits still exists as standalone command for re-seeding
feat: seed demo audit runs in minimal config bootβ
The CLI now calls seed_demo_audits after seed_platform, so the
dashboard is populated with 4 completed demo audit runs (safety,
rag, health baselines + safety elevated-temp) out of the box.
Replace the supervisord multi-process Dockerfile with a clean single-process
image that runs the full stack (Django + embedded Hatchet + worker) in one
Python process via 'python -m simpleaudit_studio.cli'.
Two deployment configs now:
- Minimal: root Dockerfile / uvx simpleaudit-studio (HF Space, laptop, VPS)
- Full: docker-compose.yml (teams, GPU workers, production)
Changes:
- New pyproject.toml with 'simpleaudit-studio' console script entry point
- New simpleaudit_studio/cli.py: boots SQLite + embedded Hatchet + mock
server + web thread + worker in main thread
- New infra/minimal_config.py: embedded Hatchet lifecycle management
- Simplified Dockerfile: no Postgres, no supervisord, no Hatchet binaries
- Removed deploy/hf-space/ (supervisord.conf, start.sh)
- Settings: MINIMAL_CONFIG flag selects SQLite + local Hatchet URLs
- Worker: get_client() returns embedded client in minimal config
- Mock server: importable start_mock_server()/stop_mock_server() API
- CLI flags: --port, --noβ¦