Operations and security
Update, back up, restore, scale, rotate secrets; observability; the security model.
Operations#
- Update:
docker compose build && docker compose up -dwith the same-ffiles you started with. The health check settles within seconds; schema changes are applied at startup; missing embeddings are filled in the background. - Back up: three places hold everything.
- The database (
db_datavolume, orpg_dumpof thestudiodatabase): the model and its versions, the Library's collections, sources, passages, topics and statements, users, tokens, connectors, settings, activity, and the demo database. - The original files of the Library:
./data/librarywith the local driver, or the bucket (s3_datavolume of the storage overlay, or your S3 service's own backups) with the s3 driver. ./dataotherwise: Excel backups and connector files. Excel exports of the model and zip exports of collections are portable second copies.
- The database (
- Restore: restore the database dump into a new volume, put the files back (data folder or bucket) and start the stack.
- Scale the work: add the worker overlay and
--scale worker=N; the web app stays responsive while documents are processed. - Rotate a token: create a new one, update the agent, revoke the old one.
- Rotate
SECRET_KEY: re-enter connector passwords, the AI key, the S3 secret and the fetcher token if they were set on the page. - Pinned images: the compose files pin exact image versions (PostgreSQL with pgvector, SeaweedFS, the AWS CLI used once to create the bucket, Playwright); bump them deliberately.
- Vulnerability scan:
scripts/audit.shchecks the locked Python packages and the frontend's runtime packages.
Observability#
Activity (admins, Admin → Activity):
- Sessions: every Alfred conversation and every agent session (an MCP token's calls with no gap over 30 minutes), newest first, with source, who, number of questions, the latest question and when it was last active.
- Steps of a session: the question, each model call (time, first token, tokens in and out, cost, tools requested), each tool call (arguments, time, outcome, answer size, rows, and the response itself, cut to
ACTIVITY_RESULT_CHARS), the answer, and whether Alfred ran out of lookups. A long-running chat shows a break wherever it paused for more than 30 minutes, with times restarting after it. Responses load when opened and can be copied. - Summaries per tool and per model: calls, errors, time, tokens and cost over the last days.
- A response is exactly what that caller received, so it holds data only as their entitlements allowed it (masked columns stay hashed); set
ACTIVITY_RESULT_CHARS=0to keep only the summary. - Steps older than
ACTIVITY_DAYSare purged every hour, and sessions with nothing left go with them. Writing to the log never slows or breaks a request.
Audit log (Changes & versions): every model edit, import, publish, discard, grant, connector change and activation, setting change and reset, with who and when.
Questions nobody could answer (Settings > Maintenance): agent and Alfred searches that found nothing, newest first.
Search index (Settings > Maintenance): embedding model and Alfred status, chunks without an embedding, and Rebuild search index (fills gaps, for example after changing the embedding model; also done automatically at startup).
Health: GET /api/health returns {"ok": true} without signing in, for load balancers and monitoring.
Security#
- The app runs as a non-root user and connects to PostgreSQL with its own non-superuser account; the superuser is only used to create it.
- Connections to your data are read-only at the session level, with statement timeouts and row limits. Use read-only database accounts: the connector test warns when an account can write.
- Every data query is checked and regenerated by the server; restricted columns never leave the database without an explicit grant; masked columns leave only as hashes.
- Secrets at rest: connector passwords and an OpenRouter key set on the Settings page are encrypted; session and API tokens are stored hashed; user passwords use scrypt.
- Sessions are HttpOnly, SameSite=Lax cookies, Secure over HTTPS. Responses carry a Content-Security-Policy and the usual hardening headers.
- Rate limits per client address (API and MCP), per address for sign-in, and per user for Alfred; sign-in lockout after repeated failures.
- Connectors cannot reach the app's own database or link-local and cloud-metadata addresses; file databases must live under
CONNECTOR_FILES_DIR. - Library: every document-derived row belongs to one collection and is reached only through a scope object that checks grants (no grant is a 404); uploaded files are stored under generated keys, path-checked; web capture refuses private, loopback and metadata addresses, pins the resolved address and re-checks redirects; the optional browser runs in its own container with no database or files; the UI renders model-written Markdown without HTML or images; heavy and AI-calling actions are rate limited per user; document text is treated as data in prompts, and an agent only receives what its grants allow.
- Python dependencies are installed from a lock file with hashes; base images are pinned to exact versions.