- Secrets only in the environment /
.env(git-ignored). Never in code, docs, reports, logs or containers baked at build time. - The Azure PAT must never appear in logs. The adapter redacts it from any error message and never includes it in URLs it logs.
- Defense in depth: network isolation (Tailscale) + API token + input validation
- scanning in CI.
- Least privilege: a dedicated PAT with read-only scopes for sync; a write scope (work items) only if Azure comments/PRs are enabled.
| Secret | Env var | Usage |
|---|---|---|
| Azure PAT | AZURE_DEVOPS_PAT |
Azure REST API |
| API token | API_TOKEN |
Bearer auth on the API |
| LLM key | LLM_API_KEY |
LLM provider |
| Telegram token | TELEGRAM_BOT_TOKEN |
Notifications |
| SMTP creds | EMAIL_USERNAME/PASSWORD |
Notifications |
.env.example contains only placeholders. .gitignore excludes .env,
*.pem, backups/, reports/generated/.
- API:
Authorization: Bearer <API_TOKEN>. IfAPI_TOKENis unset the API logs a warning and runs unauthenticated (explicit small-team/Tailscale default), except demo endpoints which are always gated byAPP_ENV. - Frontend: SPA is unauthenticated by default behind Tailscale. An optional
PROXY_BASIC_AUTH_USER/PASSWORDenables basic auth at nginx for the dashboard. - Authorization is coarse (admin = API token holder). Role model can be added later; interfaces are prepared.
- Pydantic validation at every router boundary (request + response).
- Canonical test-result ingestion validates enums, sizes, and artifact paths.
ArtifactStoragenormalizes paths to prevent directory traversal; generated HTML is sanitized (escape all interpolated values; no raw user HTML).
- All structured output validated against Pydantic schemas (twice: provider + service).
- Untrusted content (work items, comments) is passed to models with a prompt-injection warning; LLM output is treated as data, never executed.
- No LLM output is ever rendered without escaping.
- Secret scanning:
gitleakson the repo. - Dependency scanning:
pip-audit(Python),npm audit(frontend),trivyfor images. - Container scanning:
trivyon built images (alsodocklelints). - Pipeline is advisory: scans do not block PR merges by humans, but images are
tagged
-unscannedwhen scans are skipped.
- Structured JSON logs; fields
request_id,job_id,user,action. - Redactors: Azure PAT, API tokens, bot tokens, passwords.
- No secrets in
AgentJob.resultor error payloads (sanitize before persist).
- DB only on the internal network; backups encrypted at rest if the backup disk is encrypted (documented).
- Container run as non-root (
USER app), read-only root filesystem where the framework allows,no-new-privileges. - Image pulls pinned to digests in production compose override.