En tránsito: HTTPS/TLS 1.2+ en todas las conexiones (Shopify ↔ app, navegador ↔ app, app ↔ Resend). Los webhooks se verifican por HMAC; las peticiones del admin por token de sesión JWT firmado.
En reposo: producción usa SQLite en un volumen Docker del VPS. Los datos que identifican a una persona van cifrados a nivel de aplicación con AES-256-GCM (app/lib/crypto.server.ts, clave DATA_KEY de 32 bytes): el snapshot del pedido y el token de acceso de Shopify. El email del cliente no se guarda: solo un HMAC que permite localizar sus disputas para borrarlas. Una copia de la base de datos sin la clave no revela nada de eso.
Límite conocido: la clave vive en el entorno del mismo servidor, así que protege copias (backups, snapshots de disco, volumen robado), no a quien obtenga root en la máquina viva. El disco del VPS no está cifrado por el proveedor; el cifrado completo de disco exige reinstalar el servidor y está pendiente de evaluación.
En desarrollo local, SQLite queda bajo FileVault y sin DATA_KEY los valores se guardan en claro.
Secretos: variables de entorno del hosting, nunca en el repositorio (.env ignorado).
Copias de seguridad
deploy/backup.sh crea una copia diaria consistente (sqlite3 .backup) en /root/backups y conserva 14. El archivo incluye la base de datos y .env.production, porque sin DATA_KEY los datos cifrados no se pueden leer.
Cada copia se cifra con openssl smime -aes256 contra un certificado público instalado en el servidor. La clave privada solo existe en el portátil del operador (~/.refutely/backup-key.pem), nunca en el VPS: quien acceda al servidor no puede leer las copias.
Al borrar datos (retención, desinstalación, redact), las copias previas expiran en ese plazo.
Restauración probada el 2026-09-22: descifrado con la clave privada, base de datos abierta y DATA_KEY recuperada del mismo archivo.
Separación de entornos
Producción: tienda del comerciante, base de datos de producción.
Desarrollo: tienda de desarrollo de Shopify con datos ficticios, base de datos aparte.
Tests: SQLite desechable por proceso, sin datos reales.
Minimización y prevención de pérdida de datos
Se guardan solo los campos del apartado 3 de docs/privacidad-y-dpa.md; no hay copia de archivos de evidencia (van directos a Shopify).
No se exportan datos a terceros salvo los subencargados listados.
Sin acceso público a la base de datos; solo la app, por red privada del proveedor.
Registro de auditoría de cada acción sobre datos (AuditEvent).
Control de acceso
Los usuarios entran por el admin de Shopify; Refutely no gestiona contraseñas de usuario. Shopify exige verificación en dos pasos para el personal con acceso a apps.
Acceso a servidores y base de datos: solo el operador, con clave SSH / cuenta del proveedor con 2FA y contraseña larga generada por gestor.
Acceso al repositorio: cuentas con 2FA.
Registro
Guardado, envío, decisión, retención y borrado quedan registrados en AuditEvent (quién, qué, cuándo). Abrir un expediente no genera evento de auditoría.
Logs de aplicación sin datos personales más allá del número de pedido.
Dependencias
Versiones fijadas en package-lock.json; el despliegue actual no ejecuta npm audit.
Acceso al servidor
SSH solo con clave (PasswordAuthentication no, PermitRootLogin prohibit-password), fail2ban activo y cortafuegos ufw con 22/80/443 abiertos.