Deployment
Simmetric Chat es air-gap capable y local-first. Cinco targets de deployment: Docker Compose multi-container (recomendada para producción), container único all-in-one (entornos air-gapped), stack gestionado Coolify, override dev (hot-reload sobre Docker)…
Simmetric Chat es air-gap capable y local-first. Cinco targets de deployment: Docker Compose multi-container (recomendada para producción), container único all-in-one (entornos air-gapped), stack gestionado Coolify, override dev (hot-reload sobre Docker) y desktop Tauri. Todos los artefactos viven en docker/; el shell desktop en src-tauri/. Las imágenes publicadas en GHCR son solo amd64 (linux/amd64 - los builds arm64 emulados vía QEMU se bloqueaban; en arm64 se hace self-build local desde el host nativo). Todos los containers usan healthcheck wget (--spider): server GET /api/health, collector GET /api/health en 3210, widget GET /health en 3211, Postgres pg_isready, Redis redis-cli ping.
Prerrequisitos
- Node.js ≥ 24, pnpm 11.24.0, PostgreSQL 16.
- Para embedding local: ~2 GB RAM por
all-MiniLM-L6-v2(Xenova, CPU). - Para LLM local: Ollama con modelo(s) pre-pulleados (
gemma4:latestdefault). - Para OCR vision: Ollama con modelo vision (
glm-ocr:latestdefault).
Docker Compose (producción)
El compose de producción (docker/docker-compose.yml) define 6 servicios activos: frontend (nginx, :80/:443), server (:3000), collector (:3210), widget (:3211), postgres (16, imagen pgvector/pgvector:pg16), redis (7-alpine, cache de scaling opcional). ollama y qdrant están deshabilitados por defecto (bloques comentados: descomenta + establece OLLAMA_BASE_URL / VECTOR_DB_PROVIDER=qdrant para usarlos).
cp .env.example .env
# Modifica .env: JWT_SECRET, COLLECTOR_SECRET, WIDGET_API_KEY, DATABASE_URL, LICENSE_KEY (si Enterprise)
docker compose -f docker/docker-compose.yml up -dEnv críticos de producción (ver 03 - Configuration):
JWT_SECRET- obligatorio, material criptográfico (derivaENCRYPTION_KEYsi unset).COLLECTOR_SECRET- obligatorio, debe matchar entre server y collector.WIDGET_API_KEY- obligatorio para widget, debe matchar entre widget y server.DATABASE_URL- PostgreSQL 16.LICENSE_KEY+LICENSE_SECRET- solo Enterprise.ENCRYPTION_KEY(base64 32 byte) - recomendado para persistencia data-at-rest más allá de rotaciones deJWT_SECRET.
Rotación de claves: establece LEGACY_PREVIOUS_ENCRYPTION_KEYS (CSV base64 32-byte) para mantener la decryptabilidad de backups/datos antiguos durante la rotación. No existe job automático de re-encryption: procedimiento manual decrypt-old → re-encrypt-new.Auto-provisioning de claves (entrypoint server)
El entrypoint de producción (docker/entrypoint-server.sh) ejecuta en cada boot: provisioning de ENCRYPTION_KEY y API_KEY_HMAC_SECRET con precedencia valor operator-supplied > valor persistido en el volumen server-storage > generado una vez y persistido (nunca regenerado sobre material existente); luego prisma migrate deploy + seed y el arranque del servidor. Las configuraciones dev (docker-compose.dev.yml) saltan el provisioning y mantienen el fallback scrypt.
Volúmenes & persistencia
- PostgreSQL: volumen dedicado (
pgdata). - LanceDB:
${STORAGE_PATH}/vectors/lancedb(collector). - File storage:
${STORAGE_PATH}(collector, documentos/avatar/uploads). - Backup: destinos configurados por-job (disk/S3/Dropbox/...).
Health check post-deploy
Los containers se auto-verifican vía wget (ver intro). Checks manuales equivalentes:
curl http://localhost:3000/api/health
# { "status": "ok", "checks": { "database": "ok", "collector": "ok", "disk": "ok" } }
curl http://localhost:3210/api/health # collector
curl http://localhost:3211/health # widgetContainer único all-in-one
docker/Dockerfile construye una única imagen supervisada por supervisord que ejecuta PostgreSQL 16 (pgvector compilado offline para air-gap), server, collector y widget. Expone 3000/3210/3211, usa DATABASE_URL en localhost, healthcheck en /api/health. Pensada para entornos air-gapped/offline/demo: los env por defecto son placeholder - sobrescríbelos vía .env o -e.
Coolify
docker/docker-compose.coolify.yml + docker/nginx.coolify.conf automatizan build de imágenes, networking interno, volúmenes, healthchecks y el mount del plugin enterprise (path host absoluto ${ENTERPRISE_PLUGIN_PATH:-/opt/simmetric-enterprise}). Los secretos requeridos usan la sintaxis :? (Coolify rechaza el deploy si faltan): JWT_SECRET, COLLECTOR_SECRET, WIDGET_API_KEY, POSTGRES_PASSWORD, APP_URL, ALLOWED_ORIGINS. Diferencias con el compose base: sin container_name, sin puerto host en las apps (ingress confiado a Traefik, HTTP-only - TLS terminado por Traefik), ENCRYPTION_KEY/API_KEY_HMAC_SECRET no interpoladas (auto-provisionadas en el volumen).
Desarrollo local
pnpm install
pnpm db:generate # Prisma client (+ fix symlink Prisma/pnpm)
pnpm --filter server db:migrate # aplica las migraciones
pnpm dev # turborepo: server + collector + widget + frontend en paraleloDetalles en 10 - Development.
Override dev (Docker)
docker/docker-compose.dev.yml monta las fuentes sobre el compose de producción para hot-reload (NODE_ENV=development). docker-compose.infra.yml arranca solo Postgres/Qdrant con la app ejecutándose en el host.
Desktop (Tauri)
src-tauri/ - app desktop que wrappea el frontend. Build con pnpm tauri build. Configura src-tauri/tauri.conf.json para URL server remota o bundle local.
Reverse proxy (Nginx)
Puntos clave:
- SSE: deshabilitar buffering (
proxy_buffering off;,X-Accel-Buffering: noya enviado por el server/widget). - Timeout: LLM locales lentos →
proxy_read_timeoutalto (≥ 600s, el agent watchdog default es 10 min). - Upload:
client_max_body_size≥ 100 MB (límite multer documentos). - CORS: la allowlist se gestiona del lado app (
ALLOWED_ORIGINS); el widget tiene CORS dinámico per-origin (allowedOrigins).
Backup en producción
- Crea destino (
POST /api/backup-destinations). - Crea job (
POST /api/backup-jobs) con cronfrequency/time+retentionDays. - Verifica con
POST /backup-destinations/:id/testy un run manualPOST /backup-jobs/:id/run. - Restore:
POST /api/backups/restore/:logId(dry-run recomendado antes:/restore/:logId/dry-run). Atomicidad vía psql--single-transaction.
Ver 05 - Feature Guide § Backup.
Seguridad de producción
- Cambia la password admin bootstrap; establece
ALLOW_REGISTRATION=falseen prod (self-registration cerrada). DLP_ENABLEDdefault"true"en v0.22 - redaction input/output chat sobre 10 patrones built-in (ver 05 - Feature Guide); usaDLP_BYPASS_ROLESpara eximir roles seleccionados.- Habilita
audit_log_immutable(Enterprise) para audit append-only. - HTTPS terminado en el reverse proxy; headers
helmetactivos en el server (relajados en el widget para embedding). - Rate limit ya configurados (ver 04 - API Reference § Rate limit): el límite chat ahora lo gestiona el budget tracker del agente (concurrencia per-usuario, wallclock, token) más la red de protección global 200 req/min.
Cross-link
- Env completos: 03 - Configuration
- Desarrollo: 10 - Development
- Troubleshooting deploy: 12 - Troubleshooting