Seguridad Guía

Operación y arquitectura de la eliminación permanente de empresas

Guía funcional y técnica del flujo de suspensión, eliminación lógica y eliminación permanente: modos, worker, protecciones, jobs durables, variables de entorno y límites de la versión actual.

38 min de lectura

Este documento explica cómo administrar el ciclo de vida de una empresa en Neges y cómo funciona el control plane de eliminación permanente. Sirve para operadores de Platform, desarrolladores y agentes de código que necesiten mantener el feature. Describe el comportamiento implementado al 2 de agosto de 2026; el código y las pruebas siguen siendo la fuente de verdad definitiva.

Resumen ejecutivo y límite más importante

Neges distingue tres acciones diferentes: suspender, eliminar de forma lógica y solicitar una eliminación permanente. Suspender y eliminar lógicamente son reversibles. La eliminación permanente usa un flujo separado, reforzado y durable que sólo aparece cuando la empresa ya está archivada.

Este cierre por defecto es intencional: evita que una configuración incompleta, un error de ownership o una expectativa equivocada sobre backups y proveedores externos termine en una pérdida parcial e imposible de auditar.

Suspender, eliminar y eliminar permanentemente

Las acciones no son sinónimos y responden a necesidades distintas.
AcciónEstado resultanteDatosReversibleUso recomendado
SuspendersuspendedSe conservan íntegramente.Sí, con Habilitar.Pausa temporal por seguridad, cobro, soporte o revisión.
EliminararchivedSe conservan íntegramente.Sí, con Restaurar.Retiro lógico del circuito operativo y paso previo obligatorio al purge.
Eliminación permanentepurging y job durableSe inventarían y, en una versión futura certificada, se eliminarán físicamente según política.No una vez ejecutada físicamente.Cierre definitivo, sujeto a controles técnicos, legales y de ownership.
ciclo-de-vida-tenant.mmdmermaid
El diagrama se renderiza al cargar la pagina.

Cómo opera un administrador de Platform

  1. Revisa el interruptor de infraestructura

    TENANT_PURGE_WORKER_ENABLED debe estar en true si se espera que Strapi procese la cola. Si está en false o no existe, Configuración muestra un aviso explícito y el worker no toma jobs.

    Tip: Cambiar una variable de entorno requiere reiniciar o redesplegar Strapi.
  2. Selecciona el modo en Platform / Configuración

    Abre /admin/settings/tenant-purge, elige Vista previa, Staging o Producción, indica la contraseña actual y, opcionalmente, el motivo del cambio.

    Tip: Si presionas Guardar sin cambiar el modo, la UI responde “No hay cambios para aplicar”.
  3. Revisa las protecciones técnicas

    El acordeón resume ocho señales administradas por infraestructura. Producción no se puede guardar mientras cualquiera permanezca bloqueada.

  4. Prepara la empresa

    En /admin/tenants/:documentId usa Eliminar para dejar la empresa archived. Una empresa active o suspended no puede entrar directamente a la cola permanente.

  5. Abre Eliminación permanente

    La pantalla calcula el inventario, las filas contadas, los recursos sin ownership concluyente, los blockers y un digest del preview.

  6. Confirma la solicitud

    Escribe exactamente el slug de la empresa, la frase ELIMINAR.PERMANENTEMENTE y tu contraseña actual. Strapi genera un challenge de un solo uso válido por cinco minutos.

  7. Sigue el job

    Actualiza el estado y usa Verificar datos restantes, Reintentar o Cancelar cuando el estado lo permita. Conserva el documentId del job para soporte e investigación.

Modos de eliminación: Vista previa, Staging y Producción

ModoPreviewCrear jobsBlockers al encolarObjetivo
Vista previaSí.No.La cola permanece cerrada.Inspeccionar recursos y configuración sin procesar solicitudes.
StagingSí.Sí, para empresas archivadas no protegidas.Puede encolar aunque existan blockers; el worker se detendrá y dejará evidencia.Probar challenge, cola, freeze, reintento, cancelación y verificación de manera controlada.
ProducciónSí.Sólo para empresas archivadas, no protegidas y sin blockers.No admite blockers.Reservado para operación certificada con todas las protecciones listas.

El modo ya no es una variable de entorno. Se guarda como purgeMode en el single type platform_settings de PostgreSQL y se cambia desde /admin/settings/tenant-purge. Cada modificación aumenta revision y crea un registro en platform_setting_audits con actor, ambiente, valor anterior, valor nuevo, campos modificados y motivo.

Dos controles complementarios: infraestructura y operación

modo-efectivo.mmdmermaid
El diagrama se renderiza al cargar la pagina.

TENANT_PURGE_WORKER_ENABLED es un kill switch de infraestructura. Vive en el proceso de Strapi tanto en local como en Railway y no puede ser activado por la UI. purgeMode es una decisión operativa guardada en base de datos. Para que el worker procese un job deben cumplirse ambos controles: variable true y modo staging o production.

Worker envModo DBResultado efectivo
false o ausenteCualquieraWorker detenido; la cola no se procesa.
truepreviewWorker iniciado a nivel de Strapi, pero tenant purge no toma jobs.
truestagingProcesa como máximo un job por ciclo y permite probar blockers.
trueproductionProcesa sólo el flujo elegible sin blockers.

Qué son las ocho protecciones técnicas

Las protecciones son precondiciones de seguridad visibles en Configuración. No son simples adornos de UI: Strapi vuelve a evaluarlas en el backend. Para guardar Producción las ocho deben estar listas; además, el preview de una empresa puede añadir blockers específicos según sus datos.

ProtecciónQué compruebaFuente actualCómo se habilita
Challenge reforzadoExiste una clave HMAC de al menos 32 caracteres para tokens temporales.TENANT_PURGE_CHALLENGE_SECRETSecreto de infraestructura.
Barrera de InboxStrapi puede congelar escrituras y consultar residuos en neges-inbox.TENANT_PURGE_INBOX_API_BASE_URL + TENANT_PURGE_INBOX_INTERNAL_TOKENURL y token interno válidos.
Retención PostgreSQLLa permanencia eventual en backups compartidos y WAL fue aceptada.TENANT_PURGE_SHARED_BACKUP_RETENTION_ACCEPTEDDecisión legal y operacional documentada.
Retención de archivosSoft delete y ownership histórico de archivos GCS fueron aceptados.TENANT_PURGE_GCS_RETENTION_ACCEPTEDPolítica de retención y registro de ownership aprobados.
Retención tributariaEl tratamiento de documentos tributarios obligatorios está aprobado.TENANT_PURGE_DTE_RETENTION_APPROVEDDecisión legal; sólo produce blocker si el tenant tiene DTE.
Propiedad de recursosSe decidió cómo tratar recursos de ownership incierto, JSON o usuario.TENANT_PURGE_UNRESOLVED_OWNERSHIP_ACCEPTEDClasificación y aceptación explícita.
Adapters certificadosLos adaptadores destructivos pasaron fixtures, contratos y failure injection.TENANT_PURGE_DESTRUCTIVE_ADAPTERS_READYSólo después de evidencia automatizada y revisión.
Ejecutor destructivoExiste código certificado que realiza el borrado físico y genera recibo.Hardcoded en false en esta versión.No es configurable todavía; requiere una entrega de ingeniería separada.

Blockers específicos de una empresa

CódigoCuándo apareceResolución esperada
DTE_RETENTION_UNRESOLVEDHay DTE y su disposición legal no está aprobada.Separar las copias obligatorias y registrar la decisión legal.
SHARED_BACKUP_RETENTION_UNRESOLVEDNo se aceptó la expiración eventual de PostgreSQL y WAL compartidos.Aprobar retención o migrar a almacenamiento segregado/crypto-erased.
GCS_SOFT_DELETE_RETENTION_UNRESOLVEDNo se aceptó soft delete u ownership de medios históricos.Definir ventana de retención y completar ownership de archivos.
RESOURCE_OWNERSHIP_UNRESOLVEDAlgún recurso no puede atribuirse con certeza o falló su conteo.Clasificar recursos user-owned, JSON y legacy.
DESTRUCTIVE_ADAPTERS_NOT_READYLos adaptadores destructivos no están certificados.Completar pruebas de contrato, fixtures y fallas parciales.
DESTRUCTIVE_EXECUTION_FAIL_CLOSEDTodos los gates previos pasaron, pero no existe ejecutor físico en esta entrega.Implementar y revisar el ejecutor en una versión futura.

El catálogo resource-catalog.ts descubre content types tenant-aware, define la estrategia de ownership y cuenta hasta ocho recursos en paralelo. Los tipos globales no se borran; los recursos de ownership incierto se informan sin inventar un conteo. El digest SHA-256 del manifest evita aprobar un inventario y luego encolar otro distinto.

Arquitectura del control plane

arquitectura-tenant-purge.mmdmermaid
El diagrama se renderiza al cargar la pagina.

El backoffice es la consola. Strapi conserva la autoridad: valida autenticación y rol Platform, aplica la allowlist de operadores destructivos, calcula el preview, crea el challenge, persiste los jobs y ejecuta el cron. PostgreSQL vuelve durable el proceso; cerrar el navegador o reiniciar el frontend no elimina el job.

neges-inbox participa como barrera externa: freeze impide nuevas escrituras asociadas al tenant y verify devuelve frozen, residualRows y conteos por tabla. GCS, backups y DTE se modelan actualmente como decisiones de retención y blockers; todavía no son ejecutados por un adaptador destructivo.

Flujo completo de una solicitud

secuencia-solicitud-purge.mmdmermaid
El diagrama se renderiza al cargar la pagina.
  • El usuario está autenticado y es administrador Platform.
  • Su ID numérico está incluido en TENANT_PURGE_ADMIN_USER_IDS para acciones destructivas.
  • La empresa está archived y no pertenece a TENANT_PURGE_PROTECTED_DOCUMENT_IDS.
  • El origin del navegador coincide exactamente con TENANT_PURGE_ALLOWED_ORIGINS.
  • El preview usado para confirmar sigue vigente.
  • El slug y ELIMINAR.PERMANENTEMENTE coinciden exactamente.
  • La contraseña actual es válida y el challenge no expiró ni fue consumido.
  • No existe otro job activo para la misma empresa.

Jobs durables, cancelar, reintentar y verificar residuos

Durable significa que la solicitud y cada paso se almacenan en PostgreSQL, no sólo en memoria. Un reinicio de Strapi no borra la intención ni su estado. El job guarda tenant, usuario solicitante, digest, preview, opciones, intentos, lease, errores y timestamps. Los steps guardan por separado freeze, destructive-gate y verify.

estados-job.mmdmermaid
El diagrama se renderiza al cargar la pagina.
AcciónEstados admitidosEfecto
Cancelarqueued, blocked o failedMarca cancelled. Si ya estaba congelada, elimina la barrera de Inbox y devuelve la empresa a archived.
Reintentarblocked o failedLimpia el error, vuelve a queued y obliga a recalcular preview y gates.
Verificar residuosblocked, failed o verification_pending desde la UIRecalcula el inventario y consulta a Inbox; guarda conteos y blockers como evidencia. No borra datos.

El worker usa un lease de 60 segundos con RAILWAY_REPLICA_ID o el PID local como owner. Si un proceso muere durante freezing y el lease vence, el siguiente ciclo marca el job failed con TENANT_PURGE_WORKER_LEASE_EXPIRED. La clave de idempotencia evita crear duplicados por doble clic o reintento de red.

Dónde vive el worker y cada cuánto corre

El worker está implementado en neges-strapi/config/cron-tasks.ts usando el cron integrado de Strapi. La expresión */1 * * * * ejecuta el ciclo cada minuto. processPending consulta Platform Settings y retorna sin trabajo si el resultado efectivo está apagado.

EntornoProcesoConfiguración
LocalEl mismo proceso iniciado con yarn develop o yarn start en neges-strapi.Variables del .env local y purgeMode de la PostgreSQL local.
ProducciónEl servicio neges-strapi desplegado en Railway.Variables del servicio Railway y purgeMode de la PostgreSQL conectada.

config/server.ts habilita el motor de cron si está activo tenant purge, notification outbox o domain verification. Por eso el cron global puede estar encendido aunque TENANT_PURGE_WORKER_ENABLED sea false; processPending aplica de nuevo el control específico y no procesa la cola de purge.

Variables de entorno

.envdotenv
# Interruptor maestro de infraestructura. False o ausente = worker apagado.
TENANT_PURGE_WORKER_ENABLED=false

# IDs numéricos de usuarios Strapi autorizados a crear challenges y operar jobs.
TENANT_PURGE_ADMIN_USER_IDS=123,456

# Denylist de tenants que la UI nunca puede sobrepasar.
TENANT_PURGE_PROTECTED_DOCUMENT_IDS=documentId-critico

# Orígenes exactos permitidos para crear el challenge.
TENANT_PURGE_ALLOWED_ORIGINS=http://localhost:4300,https://backoffice.neges.cl

# Secreto aleatorio de 32 caracteres o más. No usar este ejemplo en producción.
TENANT_PURGE_CHALLENGE_SECRET=<secret-random-de-32-o-mas-caracteres>

# Barrera interna de neges-inbox.
TENANT_PURGE_INBOX_API_BASE_URL=http://localhost:3100/api
TENANT_PURGE_INBOX_INTERNAL_TOKEN=<token-interno>

# Aprobaciones explícitas. Mantener false hasta contar con evidencia real.
TENANT_PURGE_DTE_RETENTION_APPROVED=false
TENANT_PURGE_SHARED_BACKUP_RETENTION_ACCEPTED=false
TENANT_PURGE_GCS_RETENTION_ACCEPTED=false
TENANT_PURGE_UNRESOLVED_OWNERSHIP_ACCEPTED=false
TENANT_PURGE_DESTRUCTIVE_ADAPTERS_READY=false
VariableObligatoriaPropósito y comportamiento
TENANT_PURGE_WORKER_ENABLEDSí para procesarKill switch. true, 1, yes u on habilitan; false, ausente u otro valor mantienen apagado.
TENANT_PURGE_ADMIN_USER_IDSSí para operar jobsCSV de IDs numéricos Strapi. Vacía significa que nadie puede crear challenge, encolar, cancelar, reintentar ni verificar.
TENANT_PURGE_PROTECTED_DOCUMENT_IDSRecomendadaCSV de documentId críticos. Es una denylist de infraestructura que la UI no puede sobrescribir.
TENANT_PURGE_ALLOWED_ORIGINSObligatoria en producciónCSV de origins exactos autorizados para pedir challenge. Incluye esquema, host y puerto, sin path.
TENANT_PURGE_CHALLENGE_SECRETSí para confirmarSecreto HMAC aleatorio de al menos 32 caracteres. No se expone al navegador.
TENANT_PURGE_INBOX_API_BASE_URLSí para freeze/verifyBase interna de neges-inbox incluyendo su prefijo /api.
TENANT_PURGE_INBOX_INTERNAL_TOKENSí para freeze/verifyBearer privado compartido con Inbox. Nunca documentar el valor real.
TENANT_PURGE_DTE_RETENTION_APPROVEDPara DTE/ProducciónConfirma que existe una decisión legal para documentos tributarios.
TENANT_PURGE_SHARED_BACKUP_RETENTION_ACCEPTEDPara ProducciónAcepta la retención eventual en backup y WAL compartidos.
TENANT_PURGE_GCS_RETENTION_ACCEPTEDPara ProducciónAcepta soft delete y la política de ownership/retención de archivos.
TENANT_PURGE_UNRESOLVED_OWNERSHIP_ACCEPTEDPara ownership inciertoRegistra que existe una resolución explícita para recursos no atribuibles automáticamente.
TENANT_PURGE_DESTRUCTIVE_ADAPTERS_READYPara Producción futuraDeclara adapters certificados. En esta versión no basta para abrir el ejecutor físico.

Seguridad, reautenticación y auditoría

  • Autenticación normal y policy require-platform-admin en todos los endpoints.
  • Allowlist adicional TENANT_PURGE_ADMIN_USER_IDS para acciones destructivas.
  • Reautenticación con contraseña tanto al cambiar el modo como al crear el challenge.
  • Challenge aleatorio almacenado sólo como HMAC, de un uso y con TTL de cinco minutos.
  • Rate limit de un challenge por usuario y tenant dentro de cinco minutos.
  • Origin allowlist para impedir confirmaciones desde una UI no autorizada.
  • Confirmación exacta con slug y frase ELIMINAR.PERMANENTEMENTE.
  • Digest del preview para detectar cambios entre inspección y solicitud.
  • Idempotency key hasheada y un único job activo por tenant.
  • Auditoría de modo con actor, email, ambiente, revisión y motivo opcional.
  • Errores sanitizados antes de persistir y exponer mensajes del worker.

Operación segura e incidentes

  1. Detención de emergencia

    Configura TENANT_PURGE_WORKER_ENABLED=false en el entorno afectado y reinicia Strapi. Esto detiene nuevos ciclos, pero no borra jobs ni revierte automáticamente una empresa ya congelada.

  2. Cierre operativo sin cambiar infraestructura

    Cambia el modo a Vista previa desde Platform / Configuración. El cron sigue vivo para otros features, pero tenant purge deja de tomar jobs.

  3. Inspección

    Conserva documentId del tenant y del job. Revisa purgeStatus, currentStep, blockerCodes, errorCode, attempts, leaseExpiresAt y los tres tenant_purge_steps.

  4. Recuperación

    Si el job está blocked o failed, corrige la causa y usa Reintentar. Si la solicitud debe abandonarse, usa Cancelar para retirar la barrera de Inbox y volver a archived.

  5. Evidencia

    Ejecuta Verificar datos restantes y conserva su result. No cambies estados directamente en PostgreSQL salvo un runbook de incidente revisado.

Contratos API y modelo persistido

Método y rutaResponsabilidad
GET /api/admin/platform-settings/tenant-purgeLee modo, resultado efectivo, infraestructura y frecuencia.
PUT /api/admin/platform-settings/tenant-purgeReautentica, valida Producción y guarda modo auditado.
GET /api/admin/platform-settings/auditsLista cambios de configuración paginados.
GET /api/admin/tenants/:documentId/purge-previewGenera inventario, blockers, digest y elegibilidad.
POST /api/admin/tenants/:documentId/purge-challengesReautentica y emite challenge temporal.
POST /api/admin/tenants/:documentId/purgeConsume challenge y crea job durable.
GET /api/admin/tenant-purge-jobs/:jobDocumentIdRecupera job y steps.
POST .../:jobDocumentId/cancelCancela y descongela cuando corresponde.
POST .../:jobDocumentId/retryVuelve a encolar blocked o failed.
POST .../:jobDocumentId/verifyRecalcula recursos y consulta residuos de Inbox.
EntidadDatos principales
platform_settingspurgeMode y revision.
platform_setting_auditsactor, ambiente, previousValue, nextValue, changedFields, reason y revision.
tenant_purge_challengestenant, usuario, digest, tokenHash, expiración, consumo y origin privado.
tenant_purge_jobstenant, requester, status, preview, digest, opciones, lease, errores, intentos y receipt futuro.
tenant_purge_stepsfreeze, destructive-gate y verify con estado, intentos, resultado y errores.

Mapa de implementación para desarrolladores y agentes de código

repositorios.txttext
neges-backoffice/
  src/app/features/platform-settings/       # modo, protecciones y auditoría
  src/app/features/platform-tenants/
    pages/platform-tenant-detail-page/       # suspender, eliminar, restaurar
    pages/platform-tenant-purge-page/        # preview, confirmación y job
    data-access/platform-tenant-purge.service.ts

neges-strapi/
  config/cron-tasks.ts                       # ciclo */1 * * * *
  config/server.ts                           # habilitación del cron de Strapi
  src/policies/require-tenant-purge-admin.ts
  src/api/platform-setting/                  # purgeMode y auditoría
  src/api/tenant-purge/                      # preview, challenge, worker y acciones
  src/api/tenant-purge-job/                  # job durable
  src/api/tenant-purge-step/                 # evidencia por paso
  src/api/tenant-purge-challenge/            # challenge temporal
  src/api/tenant-purge/services/resource-catalog.ts

neges-inbox/
  endpoint interno tenant-purge              # freeze, unfreeze y verify

Las reglas decisivas deben permanecer en Strapi. El backoffice puede anticipar validaciones para UX, pero no debe decidir elegibilidad, gates ni estados finales. Al ampliar el inventario, resource-catalog.ts debe cubrir cada nuevo content type tenant-aware; su guard de cobertura existe para impedir que un recurso nuevo quede fuera silenciosamente.

El feature usa documentId como identificador público de Strapi 5. Los jobs, steps y challenges son content types de control, no datos de negocio del tenant, y se excluyen del catálogo destructivo. Los cambios en estos endpoints deben mantener DTOs estrictos y actualizar las pruebas del backoffice y de Strapi.

Pruebas y camino para habilitar borrado físico

  • Fixtures que prueben todas las relaciones tenant, tenant_scalar, tenant_root, global, user_owned y unresolved.
  • Contrato de cada adapter externo con timeout, idempotencia, 404 tolerable y respuestas inválidas.
  • Failure injection entre cada paso para probar reanudación, lease vencido y fallas parciales.
  • Borrado ordenado por dependencias y transacción cuando el motor lo permita.
  • Recibo final firmado con conteos previstos, eliminados y residuos por proveedor.
  • Verificación posterior que sólo marque completed cuando los residuos permitidos estén documentados.
  • Runbook de backups, WAL, DTE, GCS soft delete y restauración accidental.
  • Prueba multi-réplica para demostrar que dos workers no ejecutan el mismo job.
  • Revisión de seguridad y legal antes de cambiar destructiveExecutorAvailable a true.
  • Pruebas E2E desde una empresa fixture, incluyendo cancelar y reintentar.

Diagnóstico rápido

SíntomaCausa probableQué revisar
“El worker está deshabilitado” en local.TENANT_PURGE_WORKER_ENABLED es false o no está definida..env de neges-strapi y reinicio del proceso local.
No se puede crear challenge.Usuario fuera de allowlist, secret corto o origin distinto.ADMIN_USER_IDS, CHALLENGE_SECRET y ALLOWED_ORIGINS exacto.
La empresa no permite encolar.No está archived, está protegida, el modo es preview o Producción tiene blockers.state, denylist, purgeMode y preview.blockers.
Producción no se guarda.Alguna de las ocho protecciones está false.Detalle de Protecciones técnicas. En esta versión el ejecutor siempre está bloqueado.
El job queda blocked después de freeze.Comportamiento esperado de esta release o blocker de retención/ownership.errorCode, blockerCodes y step destructive-gate.
El cron está activo pero no procesa purge.Puede estar activo por outbox o dominios; el control efectivo de purge sigue apagado.TENANT_PURGE_WORKER_ENABLED y purgeMode.
Puerto 1337 ocupado al iniciar Strapi.Ya existe otro proceso Strapi local.Cerrar el proceso que escucha el puerto; no es un problema de Railway ni del worker.

¿Te fue útil este contenido?

Tu feedback nos ayuda a mejorar la documentación de Neges.

Soporte humano

¿No encuentras lo que buscas?

Conversa con nuestro equipo de soporte en español. Respondemos por WhatsApp, correo o ticket en menos de 24 horas hábiles.

contacto@neges.clLun a Vie · 9 a 18 hrs
+56 9 5379 1163WhatsApp Business