# Copias de seguridad: instalación controlada y contrato Estado: implementación en rama de trabajo, integración cerrada por defecto. No desplegada. Base: `1174c120dec0982b4c628a353c66cdaeab894b1e`, rama original `main`, árbol original limpio. El HEAD remoto coincidía al consultar `git ls-remote origin HEAD` el 19/09/2026. Rama de trabajo: `feature/client-backups-summary`. No se ha hecho push ni merge. ## Contratación y autorización El propietario ha unificado las opciones en el nombre exacto `Backup diario`. En WHMCS 8.13.3-release.1 se observaron desde `AdminServicesTabFields` dos casos: marcado → clave presente, tipo PHP `integer`, valor original `1`; desmarcado → clave presente, tipo PHP `integer`, valor original `0`. Los IDs y evidencias particulares se mantienen fuera del repositorio público. La regla fija exige integración habilitada con `enabled === true`, resolución aplicable exactamente `['Backup diario']`, presencia mediante `array_key_exists` y valor `=== 1`. No hay lista configurable de valores activos ni conversiones. Se deniegan `0`, `"1"`, `true`, `1.0`, null, arrays, claves ausentes y cualquier valor desconocido. El ejemplo conserva `enabled=false`: la confirmación de valores no autoriza un despliegue ni valida el origen de copias. `BackupOption::applicable()` resuelve únicamente los grupos vinculados al producto del servicio autorizado mediante `tblhosting.packageid` → `tblproductconfiglinks.pid/gid` → `tblproductconfigoptions.gid`. No usa LIKE, ni el primer resultado global, ni IDs de opciones para conceder acceso. Las definiciones duplicadas o no Sí/No fallan cerradas. El nombre antiguo `Backup diario VPS` se conserva solo en la detección de definiciones/conflictos: no tiene una regla de activación validada y no concede acceso. Si ambas claves están presentes con valores no idénticos (`!==`), se registra `OPTION_CONFLICT` con el ID y se deniega, sin elegir arbitrariamente. La entrada es una función de módulo invocada por WHMCS. El registro `ClientAreaCustomButtonArray` conserva los cinco botones existentes y añade Backups solo si es elegible. La función `vpsmanager_Backups` vuelve a comprobarlo. Antes de caché/HTTP, `BackupAccess` comprueba CurrentUser, cuenta de cliente, permiso `products`, y pertenencia del servicio en la base de datos. No confunde el usuario delegado con el ID de la cuenta cliente. Versiones sin estas interfaces fallan de forma cerrada. La autorización efectiva del router WHMCS y sus accesos delegados debe comprobarse en staging de la versión instalada. ## Instalación, sin ejecución automática 1. Respaldar el directorio actual del módulo y la configuración privada, conservando permisos. Registrar el commit instalado y los checksums. No usar operaciones reales sobre VPS/copias como pruebas de regresión. 2. Revisar el diff de la rama frente al commit base. Requisitos: PHP compatible con el módulo (probado 8.1.2), extensiones DOM/libxml y cURL. No instalar tests, fixtures, documentación, herramientas ni `.git` bajo el directorio web. Copiar únicamente el módulo y sus dependencias runtime en una ventana controlada, con la integración desactivada. 3. Ampliar el archivo privado que ya carga `Config::load()` (variable `O6H_VPSMANAGER_CONFIG` o `/etc/open6hosting/whmcs-vpsmanager.php`) con la sección `backups` de `config.example.php`, manteniendo intactas las opciones de Graphite/API. Permisos ajustados al usuario PHP; nunca guardar credenciales en el repo o en URLs/comandos. 4. Configurar el endpoint HTTPS fijo del CGI, sin query/fragmento/credenciales embebidas, y autenticación HTTP Basic sobre TLS verificado. Si la instalación usa otro mecanismo, no activar hasta adaptar/probarlo. Confirmar permisos efectivos de la cuenta: no ser administrador no demuestra solo lectura. Si la interfaz concede acciones, restringir adicionalmente el acceso de integración a la ruta y consulta de host previstas mediante un proxy/política revisados; no se ha instalado tal restricción. 5. Confirmar la zona horaria IANA del origen. Crear un directorio de caché privado, modo 0700, accesible al usuario PHP y fuera de todas las raíces/alias web; configurar `cache_directory`. Los archivos usan 0600. Directorios públicos o permisivos se rechazan; un error de caché no impide la consulta, pero registra `CACHE` y elimina la ventaja de caché entre peticiones. Programar limpieza administrativa de JSON antiguos en ese directorio si cambian muchos servicios/mapeos: no se incluye un cron automático. 6. Por producto, comprobar si ya existe `backuppc_host`. Crear solo si falta, sin duplicados ni migraciones de dominio: texto, exclusivamente administrativo, no pedido, no factura, no campo de comunicaciones. Guardar un mapeo explícito. El código exige `adminonly='on'`, `showorder=''`, `showinvoice=''`, tipo `text`; confirmar estos valores de esquema en la versión instalada. No modificar campos automáticamente. La plantilla completa y los emails pueden tener personalizaciones: verificar que nunca exponen el nombre ni el valor. 7. La pareja activo/inactivo ya se observó en esta instalación y el diagnóstico fue deshabilitado y retirado, restaurando el módulo original con checksum verificado. Antes de activar, comprobar que no ha cambiado el nombre, tipo de opción ni versión/comportamiento de WHMCS. Si cambia el contrato, mantener desactivado y repetir una observación administrativa acotada; no ampliar la regla por aproximación. 8. En staging, comprobar contratado/no contratado, servicio ajeno cambiando ID, sin sesión, delegado permitido y denegado, revocación, cambio de mapeo y conflicto de nombres. Comprobar la página completa, DOM, HTML, emails y solicitudes del navegador: ninguna referencia al proveedor, host o credenciales; solo rutas WHMCS para esta función. 9. Probar fallo del backend y aislamiento respecto a los controles existentes. Desactivar cualquier caché HTTP/CDN de páginas privadas y confirmar que nunca comparte respuestas entre cuentas. El callback añade `Cache-Control: private, no-store`. 10. Activar únicamente tras superar esas comprobaciones y la revisión operativa. No iniciar, borrar, parar, descargar ni restaurar copias para validar esta lectura. ## Datos y comportamiento Una sola petición a `endpoint?host=...`, exclusivamente el host administrativo validado con `\A[A-Za-z0-9][A-Za-z0-9._-]{0,63}\z`. No se siguen redirecciones, enlaces ni formularios. TLS verificado, límite por fragmento recibido de 2 MiB, conexión 2 s y total 5 s por defecto. Códigos HTTP distintos de 200 fallan. No hay endpoint público nuevo ni consultas del navegador al origen. DOM/libxml analiza sin resolución externa y con `LIBXML_NONET`. La identidad se exige en el único h1 exacto del detalle. Las tablas se reconocen por cabeceras; tamaños exigen el grupo Totals; la unión es por número interno. Se rechazan duplicados. Las filas y tablas pueden reordenarse. El número de copia, host, conteo de ficheros, HTML, enlaces y configuración no llegan a la plantilla. El contrato público contiene disponibilidad, actualización, indicación de datos antiguos, zona, actividad reconocida, total/desglose, inicio de última completa, antigüedad en horas y hasta cinco filas con inicio/tipo/duración/tamaño/incidencias. Todas las variables se escapan en HTML. La plantilla Smarty recibe únicamente HTML propio ya escapado y reconstruido; no HTML del origen. Las fechas son inicios, nunca finales calculados. Se normalizan a timestamps con zona explícita; minutos inexistentes o ambiguos por cambio horario fallan de forma cerrada porque el HTML no incluye offset. El formato presentado es `d/m/Y H:i T`, con zona IANA visible; validar coherencia con el tema/locale instalado. MiB/1024 son GiB lógicos aproximados, no ocupación ni transferencia. Filled no modifica incidencias. Cuatro contadores presentes a cero significan «Sin incidencias registradas»; alguno positivo, «Con incidencias»; faltantes, «Información no disponible». No se suma un total de errores. Idle significa solo ausencia de actividad; estados desconocidos se omiten. No hay alarmas SLA ni promesas de restauración. Una tabla reconocida con cabecera completa y sin filas se trata como cero retenido (caso sintético probado); la ausencia de tabla es error, nunca cero. No se han aportado ejemplos reales de detalle sin copias: cualquier otra estructura fallará cerrada hasta disponer de evidencia. La caché contiene datos normalizados sin credenciales ni host. Su clave es SHA-256 de versión/configuración/servicio/host. Autorización y contratación se repiten antes de leerla. TTL 120 s; ante fallo, datos antiguos hasta 900 s desde su obtención, con aviso y fecha originales. Caducados: indisponibilidad. Cambios de mapeo/destino/configuración no reutilizan entradas anteriores. Logs administrativos: solo códigos fijos y servicio; no excepciones ni respuestas del origen. ## Rollback Desactivar `backups.enabled` inmediatamente en la configuración privada. Restaurar el módulo respaldado o distribuir el archivo runtime del commit base `1174c120dec0982b4c628a353c66cdaeab894b1e` desde un checkout de preparación. No hacer reset destructivo en un árbol compartido. Limpiar la caché privada de esta integración y la caché de plantillas WHMCS según el procedimiento de la instalación. Retirar únicamente la sección de configuración nueva si se desea; conservar Graphite/API. No hay migraciones ni cambios en contratación que revertir. Puede conservarse el campo administrativo desactivado/documentado, o retirarlo solo tras respaldar sus valores y aprobarlo. Retirar el diagnóstico temporal y restaurar la función administrativa si sigue instalado. Comprobar que desaparece Backups y que panel/gráficas y botones existentes conservan comportamiento. No confundir un rollback de código con revertir contratos. ## Referencias de integración consultadas - https://developers.whmcs.com/provisioning-modules/custom-functions - https://developers.whmcs.com/advanced/authentication - https://developers.whmcs.com/classes/whmcs/user/client Estas referencias no sustituyen la validación de la versión y tema instalados.