All documentation

Reviews & Audits

Module 20 — Phases 10-14: Deployment, Installer, Health, Backup, Observability, SSE

Deployment/installer/backup/observability were originally built in Module 14; health endpoints, SSE hardening, and backup-recovery documentation were hardened further in Module 18. Since then, Modules 16, 17, 19, and 20 added substantial new surface area (SOC/threat-intel/ incidents/remediation modules, a new real-time WebSocket gateway, billing tables, new migrations). This phase re-audits for drift rather than rebuilding, per an audit sub-agent's findings.

Deployment artifacts — real gap, fixed

Module 15/19/20 added Stripe billing (BILLING_PROVIDER, STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, STRIPE_PUBLISHABLE_KEY) and a separate enterprise generic-billing-webhook shared secret (BILLING_WEBHOOK_SECRET) to apps/api/src/config/env.validation.ts, but none of them ever made it into .env.example, deploy/k8s/01-configmap.yaml/02-secret.yaml.example, or docker-compose.yml. All five are optional() so nothing breaks at boot, but an operator following the shipped deployment artifacts to stand up a paid deployment had no documented place to put Stripe credentials.

Fixed: added the five vars to docker-compose.yml (both api and worker services — BillingCoreModule's PAYMENT_PROVIDER factory, keyed off BILLING_PROVIDER, is a transitive dependency of AiBudgetEnforcerService, which runs in the worker process too, so both processes need matching config), a new "Billing" section in .env.example with accurate documentation of what each var does and how STRIPE_WEBHOOK_SECRET differs from BILLING_WEBHOOK_SECRET, BILLING_PROVIDER added to deploy/k8s/01-configmap.yaml (non-secret), and the four secret values added to deploy/k8s/02-secret.yaml.example.

Installer — real (minor) bug, fixed

deploy/installer/install.sh prompted for --queue/--storage/--cache provider choices when EDITION=enterprise, but the captured answers were never written into .env/.env.enterprise or passed to docker compose — docker-compose.enterprise.yml hardcodes QUEUE_PROVIDER: redis, CACHE_PROVIDER: redis, STORAGE_PROVIDER: s3 directly as literal YAML values (not ${VAR:-default} substitutions), so every enterprise install has always gotten Redis+MinIO regardless of what was typed. Harmless in practice (the hardcoded values are the sane defaults) but misleading UX — an operator who typed a different answer would have no idea it was ignored.

Fixed: removed the three dead prompts, replaced with a one-line informational message. Making the choice actually configurable would require docker-compose.enterprise.yml itself to read those as substitutable env vars — a real feature addition, not a Module 20 final- pass fix, so it's left as-is (redis+redis+s3, unconditionally, for Enterprise Edition) rather than half-wired.

No other installer issues found — configure.sh, backup-wizard.sh, health-check.sh, recovery-test.sh, rollback.sh, upgrade.sh all still exist and are not flagged as broken in any prior review.

Health endpoints — no gap

apps/api/src/modules/observability/controllers/health.controller.ts checks actual infrastructure abstractions (DB via SELECT 1, Redis via queue.stats(), S3 via storage.exists() — the latter two only when those providers are actually configured, correctly skipped for Community Edition defaults) rather than enumerating per-module dependencies, so nothing added by Modules 16/17/19/20 requires a change here. Wired into deploy/k8s/10-api-deployment.yaml's readiness/liveness probes and apps/api/Dockerfile's HEALTHCHECK. No changes needed.

Backup system — no gap

apps/api/src/modules/backups/backup.service.ts is schema-agnostic by design: the DATABASE scope shells a real pg_dump --format=custom against the whole DATABASE_URL, and the STORAGE scope walks actual object-storage keys via StorageService.list() — neither enumerates a per-table/per-model list. Every Prisma model added since Module 14 (SOC, threat-intel, incidents, remediation, billing tables) is automatically captured without any code change. No changes needed.

SSE endpoints — no gap

Five @Sse(...) endpoints exist repo-wide (agent runs, AI conversation streaming, recon job logs, vuln scan job logs, the Module 16 real-time gateway), all behind the global JwtAuthGuard and all performing an explicit ownership/membership check before touching stream data. No newer SSE endpoint was found in the Module 16/17 SOC/threat-intel/ incidents/remediation/collaboration surfaces — those use the WebSocket gateway instead, which Module 18 Phase 15's hardening pass already covers. No changes needed.

Verification

docker-compose.yml, .env.example, deploy/k8s/01-configmap.yaml, deploy/k8s/02-secret.yaml.example, and deploy/installer/install.sh are configuration/shell files, not TypeScript — verified by manual review (env var names cross-checked character-for-character against env.validation.ts, and against the same var's usage in every other deployment artifact for consistency) rather than a compiler pass. No apps/api/apps/web TypeScript files were touched in this phase.