LGM-OS versión 1.22.0

Qué ha cambiado en cada versión, y por qué. Una vez instalado el sistema se actualiza solo desde el panel: la ISO no vuelve a hacer falta.

Cómo se actualiza

Una vez instalado, la ISO no vuelve a hacer falta.

Desde el panel
Sistema → LGM-OS → Actualizar. Mientras trabaja enseña la fase y un porcentaje; el panel se reinicia a mitad y la barra sigue contando al volver.
Desde la consola
sudo lgm update
Sola
Se puede dejar en automático: que avise en Notificaciones, o que se instale sin preguntar cada 6, 12 o 24 horas, o cada semana.
Si algo sale mal
La actualización es atómica: compila y comprueba antes de promover, y si el panel no responde después, revierte sola a la versión anterior.
Los dos pasos largos son normales. Instalar los paquetes del sistema y compilar la interfaz tardan varios minutos cada uno sin dar señales. Compilar la interfaz necesita memoria: en un equipo justo, cierra antes las máquinas virtuales.

Novedades

56 versiones publicadas. Las dos últimas van abiertas; el resto se despliegan.

1.22.02026-08-02

El NAS no salía en «Red» de Windows. Nunca

  • Y no era cosa de la red de nadie: era una ruta mal escrita. El servicio que anuncia el NAS a Windows (wsdd) se configuraba con ExecStart=/usr/bin/wsdd, y Debian instala ese programa en /usr/sbin/wsdd. systemd no puede arrancar lo que no existe, así que la unidad moría con 203/EXEC en cada intento. Como el anuncio se aplica sin comprobar nada —a propósito: que falte no puede impedir compartir carpetas—, el resultado era un NAS que servía archivos perfectamente y no aparecía jamás en «Red». La ruta ahora se busca en el disco en vez de darla por sabida, y se corrige sola al arrancar tras esta actualización.
  • El panel además echaba la culpa a quien no era. Decía «Falta wsdd… se instala con la próxima actualización del sistema», y el programa estaba instalado: lo que fallaba era la unidad. Quien leyera eso se quedaba esperando una actualización que ya tenía. Ahora dice lo que pasa —«el servicio wsdd no está funcionando»— sin inventarse la causa.

Cámaras

  • Toda cámara que no arrancaba decía lo mismo: «la cámara no respondió». Daba igual que fuera una contraseña equivocada, una dirección mal puesta o la cámara desenchufada, porque lo que ffmpeg explica por su salida de error se estaba tirando a la basura. Ahora se lee y se traduce: «la cámara rechaza el usuario o la contraseña», «no hay nadie escuchando en ese puerto», «la ruta de la dirección no existe en esa cámara».
  • Y en la rejilla no decía nada en absoluto. Un directo que fallaba devolvía un flujo válido y vacío: el navegador pintaba el icono de imagen rota y ahí se acababa la información. Ahora, si el directo se cae nada más abrirlo, se contesta con el motivo.
  • La grabación podía pararse en silencio y para siempre. Al grabar faltaba el tiempo de espera, así que una cámara que deja de emitir sin cerrar la conexión —wifi flojo, la cámara se cuelga: lo normal en las baratas— dejaba a ffmpeg esperando indefinidamente. Y un proceso vivo pero parado no lo reinicia nadie: el reinicio automático solo actúa cuando el proceso termina. Se descubría el día que hacía falta el vídeo.
  • Un corte de luz se llevaba el último tramo entero. Los tramos se guardaban con el índice al final, que es lo que hace que un MP4 a medias no se pueda abrir: hasta diez minutos ilegibles, y justo los del momento en que pasó algo. Ahora cada tramo se reproduce aunque esté cortado por la mitad.

El diagnóstico de las copias decía justo lo contrario de lo que pasaba

  • «fallo al subir …: Skipped copy as --dry-run is set». Cuando una subida falla sin dejar rastro, el panel repite la misma orden en seco para ver qué dice rclone. Pero si esa prueba funciona, lo que rclone escribe («se salta la copia porque es una prueba en seco», y detrás el recuento con un flamante «100%») es la prueba de que todo va bien —leyó el origen, llegó al destino, supo qué copiar—, no la explicación de nada. Se estaba enseñando como causa del fallo, que es peor que no decir nada: parece una explicación, ocupa el sitio de la de verdad y manda a buscar donde no hay nada.
  • Ahora, cuando la prueba en seco pasa, se dice lo que eso descarta, que no es poco: configuración, conexión, credenciales y lectura del origen. Y si el destino es Nextcloud/ownCloud y el rclone instalado no sabe trocear, se apunta al sospechoso número uno —el tamaño del archivo— con la orden para actualizarlo. Ese caso en seco no se reproduce jamás, porque en seco no se manda un solo byte.
  • Y el consejo ya no llega partido por la mitad. El motivo se recortaba a 400 caracteres y el recorte caía justo en la orden del final: se llegó a ver un «Actualízalo: sudo -v &&» cortado en seco, con la única parte accionable de todo el mensaje a medias.
  • Recordatorio de dónde estaba el fallo de verdad: Debian 12 trae rclone 1.60 y las subidas troceadas a Nextcloud llegaron en la 1.63, así que un archivo de varios GB iba en un solo envío y lo cortaba el propio servidor. La actualización del sistema ya instala un rclone ≥ 1.63 (verificando su suma), así que esto se arregla al actualizar el NAS.

Copias de seguridad: la pantalla ya no miente

  • «Este equipo no tiene ninguna copia de seguridad» cuando lo que pasaba es que no se pudieron leer. Si la carga fallaba (backend reiniciándose, red caída), la lista se rellenaba con un vacío inventado y la app afirmaba que no había trabajos — la peor mentira posible justo en la app que existe para no mentir. Ahora lo que falla se queda como estaba, se enseña el error con su botón de reintentar, y la pantalla se recupera sola cuando el backend vuelve.
  • El cartel verde «Copias al día» junto a «Última copia correcta: nunca». Un trabajo copiando por primera vez no caía en ningún grupo y la app daba todo por bien hecho. Ahora hay un estado propio «copiando ahora» y el cartel verde exige que no haya nada en marcha y que exista al menos una copia correcta de verdad.
  • La prueba verde del destino sobrevivía a editarlo. Cambiabas el servidor, el usuario o la carpeta y la tarjeta seguía enseñando el «conexión correcta» de la configuración anterior. Ahora editar el destino (o cualquier campo del formulario) descarta la prueba.
  • El registro de un fallo que no se podía leer se enseñaba como «no hay registro guardado (el fallo es anterior…)» — una causa inventada. Ahora el error de lectura se enseña como lo que es, con reintento.
  • El fallo al listar versiones se disfrazaba de «todavía no hay versiones», diciéndole a quien iba a restaurar que no había nada que restaurar. Ahora el error se ve y se puede reintentar.
  • Los refrescos ya no se pisan: contador de generación, así la respuesta vieja no machaca a la nueva cuando el intervalo de 30 s coincide con una acción.

Y otros 37 arreglos del mismo barrido, en las demás apps

Botones y desplegables rotos

  • El interruptor de «Espejo exacto del origen» salía deformado. El componente no llevaba shrink-0, y como el texto de ayuda de esa opción tiene 233 caracteres, flexbox le comía el ancho: el carril pasaba de 48 px a 23,7 px y la bolita blanca se salía 22 px por la derecha, flotando fuera de la píldora. Justo el único interruptor peligroso del asistente. Arreglado en el componente, así que se corrigen de paso los cinco del proxy y cualquier otra fila de etiqueta larga + interruptor.
  • 63 botones ignoraban su tamaño de letra y los de borrar salían grises. Las clases .btn-* estaban fuera de toda capa de Tailwind, o sea que se escribían DESPUÉS de las utilidades y les ganaban por orden de aparición: un btn-ghost text-xs salía a 14 px y un btn-ghost text-red-600 salía gris en tema claro —el botón de borrar no parecía peligroso—. Metidas en @layer components, que es donde debían estar.
  • Dos desplegables se salían de la pantalla en el móvil. El centro de notificaciones (384 px fijos) y la bandeja de subidas (320 px) van anclados a la derecha: en una pantalla de 375 px su borde izquierdo caía en −61 px y −69 px, y como el cuerpo no tiene desplazamiento, ese trozo no se podía recuperar de ninguna manera. Ahora tienen tope de viewport.

Actualizar rclone desde el panel, sin entrar por SSH

  • El aviso terminaba en un callejón. El panel detecta perfectamente que el rclone de Debian no sabe trocear las subidas a Nextcloud… y lo único que sabía ofrecer era «abre un terminal y pega este curl». Para quien no entra por SSH —que es la mayoría de quien compra un NAS— eso es el final del camino: sus archivos de más de un giga no se copian y no hay nada que pueda hacer desde el panel. Ahora hay un botón justo debajo del aviso, en Copias → probar destino.
  • Sin abrir ningún agujero. La API no ejecuta nada que venga de la web: solo arranca una unidad de systemd ya instalada (lgm-rclone.service), igual que hace la actualización del sistema. Esa unidad corre el guion del propio repositorio, que reutiliza la misma función que ya se ejecuta en cada actualización y que comprueba el SHA-256 antes de instalar nada. No se le pasa ni un argumento.
  • Si no cuaja, el motivo llega a la pantalla: el guion lo escribe en /var/nas/rclone-update.log y el panel enseña las últimas líneas, en vez de un «no se pudo» a secas.

«Opciones regionales» pasa a llamarse «Región y apariencia»

Fondos del escritorio

  • Tres fondos nuevos, y el de siempre por defecto. Astur Neon Cyber (azul eléctrico con destellos neón en las esquinas), Monolithic Lines (rejilla de pistas de circuito) y Liquid Aurora (manchas pastel fundidas), en Panel de control → Opciones regionales → Apariencia, junto al lado de los controles y los efectos.
  • Pintados con gradientes: ni una imagen, ni un SVG, ni una animación. En un NAS eso no es estética sino ingeniería: el escritorio vive permanentemente bajo varias superficies con backdrop-filter y se recompone con cada ventana que se mueve. El CSS del panel crece 1,2 kB comprimidos y el servidor no se entera de nada: la preferencia vive en el navegador, como el tema y el idioma.
  • «Original» no es un fondo más: es la ausencia de fondo. Al elegirlo se BORRA el atributo del documento en vez de escribir uno, así que no queda ninguna regla sobrescribiendo nada y el CSS nativo claro/oscuro pinta exactamente igual que antes de que esto existiera.
  • La miniatura del selector es el mismo gradiente que el escritorio, declarado una sola vez y reutilizado. Es lo que evita el fallo clásico de estos selectores: que la muestra enseñe un fondo y se aplique otro el día que alguien retoca un color y se olvida del otro.
  • Los cuatro responden al claro/oscuro. Un fondo que ignorase el tema dejaría el cristal claro de las ventanas sobre un fondo negro.

La documentación en el móvil: ilegible por dos motivos

  • Se salía media pantalla, y no era culpa del diseño. En CSS Grid, 1fr significa minmax(auto, 1fr), y ese auto deja que el contenido imponga su ancho mínimo. Con una tabla dentro —y las guías van llenas—, la columna del texto se estiraba hasta los 881 px de la tabla, el índice lateral se estiraba con ella hasta 942, y la guía de Copias se salía 587 px de la pantalla de un móvil. El envoltorio con desplazamiento estaba puesto desde el principio y no servía de nada, porque nunca llegaba a tener un ancho que respetar. La regla de escritorio lo hacía bien; la del móvil se había quedado a medias.
  • Y los bloques de órdenes salían en una sola línea, con la palabra `bash` delante. Las vallas ` sangradas dentro de una lista numerada no se reconocían, así que el bloque caía en el párrafo siguiente y acababa como un código en línea con todos los saltos perdidos. En una guía de pasos casi todos los bloques van dentro de una lista: eran órdenes imposibles de leer y de copiar, y de paso una línea de 1300 px que se llevaba la página por delante.
  • Un `código` en línea largo —una ruta como /etc/letsencrypt/live/…/— tampoco puede empujar la página: en pantalla estrecha se parte antes que desbordar.

rclone no se actualizaba nunca, por un byte

  • La comprobación de versión estaba rota de una forma difícil de ver: donde debía ir \1 —la referencia al número capturado— había un byte de control colado en el archivo. sed devolvía ese byte en vez de la versión, sort -V lo consideraba más moderno que 1.63, y la actualización concluía «rclone suficiente» y se saltaba la descarga. En todas las actualizaciones, desde siempre. De ahí que un NAS actualizado siguiera con el rclone 1.60 de Debian y las copias grandes a Nextcloud siguieran fallando. Reparado, y comprobado el repositorio entero: no queda ningún otro byte así.
  • Y si la descarga falla, ahora se dice lo que cuesta. El aviso era «no se pudo descargar rclone», que no le dice a nadie que sus copias de más de 1 GB van a fallar. Ahora lo explica y da la orden para ponerlo a mano. Además se reintenta por IPv4, que es el mismo motivo por el que se caía npm.

El Centro de Paquetes: ocho AdGuard Home

  • La tienda deduplicaba por NOMBRE. El feed de Community Applications trae la misma imagen publicada por varias personas con nombres distintos —«AdGuard-Home», «AdGuardHome», «AdGuard Home»…—, y cada grafía entraba como una aplicación diferente. Ahora se deduplica por imagen, que es lo que identifica a un programa de verdad, y entre las repetidas se queda la más completa: la que trae puertos, rutas y variables bien puestos es la mantenida; la que llega pelada es justo la que se instala y no arranca.
  • Lo mismo entre fuentes distintas. La misma aplicación llegaba por Community Applications y por CasaOS, y como cada fuente se escribe por su lado, salían las dos.
  • Y ya no se ofrece lo retirado. Se descartaban las entradas marcadas como Plugin y Deprecated, pero no las marcadas Blacklist, Removed u Obsolete: eran imágenes que ya no existen en el registro y que al instalarlas fallan con un «manifest unknown» incomprensible desde el panel.

Almacenamiento moría con un error interno si lsblk fallaba

  • El listado de discos se pedía sin controlar el fallo del programa que los lee. Si lsblk no contestaba —un disco retirado en mitad del recorrido, una controladora que no responde—, la excepción subía tal cual y la pantalla entera moría con un error interno y una traza en el registro, en vez de decir que no se pudieron leer los discos. Se probó llamando a las 92 rutas de la API: era la única que reventaba así.

Pantallas que se quedaban en blanco por una consulta secundaria

  • Estado de los discos: se pedían SMART y el estado del scrub a la vez, y si fallaba el segundo —en un NAS sin pool, por ejemplo— desaparecía también la salud y la marca de los discos, que se habían leído perfectamente.
  • Instantáneas: los datasets son cosa de ZFS, así que un NAS de Btrfs se quedaba sin ver ninguna instantánea.
  • Carpetas compartidas: cuatro consultas, y un fallo en la de grupos dejaba la pantalla sin ninguna carpeta.
  • Copias de seguridad: un destino que diera error escondía todas las tareas, que es justo lo que hay que poder mirar cuando algo va mal.

La actualización se caía por IPv6

  • `npm error errno ENETUNREACH`, y el NAS con internet perfectamente. Al compilar el panel, node prefiere la dirección IPv6 de registry.npmjs.org. Si el router reparte IPv6 pero no hay salida real por ahí —lo normal cuando el IPv6 está a medias—, la conexión muere al instante y la actualización entera se aborta. Ahora se resuelve IPv4 primero, que es lo que funciona en esas redes.
  • Y si aun así falla la red, se dice que es la red. El guion ya distinguía «me quedé sin memoria» de «no compila», pero un corte de internet caía en un genérico «no se pudieron descargar las dependencias» que no decía ni dónde mirar. Además ahora reintenta con esperas crecientes: que se caiga la wifi dos segundos ya no aborta una actualización.

Lo que se hace solo

  • Una revisión rota dejaba sin hacer todas las siguientes. El planificador repasa once cosas cada treinta segundos y las once estaban dentro del mismo bloque de error: la primera que fallara se llevaba por delante al resto de esa vuelta. Como estos fallos son constantes —un smartctl que no entiende una controladora falla igual cada treinta segundos—, no se perdía una vuelta: se perdían para siempre la sincronización de la tienda de aplicaciones, la limpieza de lo grabado por las cámaras, el aviso de actualizaciones y el resumen de notificaciones. Cada revisión va ahora por su cuenta.

Aplicaciones

  • Instalar una aplicación grande fallaba por reloj. Descargar la imagen y arrancarla compartían un único límite de diez minutos, y una aplicación de varios gigas por una conexión de casa se lo come sin despeinarse: el panel daba la instalación por fallida mientras Docker seguía descargando por detrás, y la aplicación acababa instalándose después de que el panel dijera que no. La descarga va ahora aparte, con su propio tiempo y avisando de que puede tardar.

1.21.02026-08-01

Copias a Nextcloud: los archivos grandes

  • Un archivo grande no subía, con la cuenta medio vacía. rclone lo mandaba en un solo PUT, y Nextcloud es PHP sobre Apache: lo corta el upload_max_filesize de PHP (128 M en muchas instalaciones), el LimitRequestBody del Apache de delante o sencillamente el tiempo. Nada de eso tiene que ver con el espacio libre, así que el fallo aparecía con 500 GB disponibles y no había por dónde cogerlo. Ahora la subida va troceada (10 MiB), y la opción viaja en la línea de órdenes: las conexiones que ya tengas quedan arregladas sin rehacerlas.
  • Y la dirección identifica al servidor. El desplegable «Tipo de servidor» era opcional, y dejarlo vacío no era un detalle estético: sin él rclone trata el destino como un WebDAV cualquiera y no trocea. Quien pega la dirección que le da el propio Nextcloud (…/remote.php/dav/files/ana/) no tiene por qué saber que además hay que elegir la marca en una lista: esa ruta solo la sirven Nextcloud y ownCloud, así que ya se deduce.
  • El aviso del fallo se cortaba justo donde decía dónde mirar. Se guardaba acotado a 400 caracteres y la orden completa se comía el final — el «Registro completo: /var/nas/backups/…», que es la única línea accionable. Ahora la orden se omite cuando hay registro guardado (ya va en su cabecera) y el límite da de sobra.

La web, entera en web/ — y las licencias, de un solo uso

  • La web de venta y el portal de licencias eran dos carpetas (web/ con el generador de la documentación y portal/ con la aplicación) para lo que un visitante ve como una sola web. Ahora todo vive en `web/`: landing, compra con Stripe, cuentas, licencias y documentación. El Dockerfile se queda en la raíz a propósito, porque la documentación se genera del README, del CHANGELOG y de docs/.
  • Cada clave vale para UN solo NAS. Al activarla, el equipo la reserva en el servidor con una huella suya (un hash del machine-id; no viaja nada personal). El mismo equipo puede reactivarla siempre —reinstalar no puede castigar a quien pagó—; otro recibe un «ya está activada en otro equipo». Al quitarla del panel se suelta sola, y desde /admin se puede liberar cuando a un cliente se le rompe un disco. Con margen: sin internet, la licencia se activa igual. Un NAS en un armario sin red no puede quedarse sin su producto por no poder llamar a casa, y quien quisiera saltárselo solo tendría que desenchufar el cable.
  • Emitir una licencia a mano desde `/admin`, ligada a un correo. Para lo que no pasa por la tienda: una factura, un regalo, tus propias máquinas o reponerle la clave a quien la perdió. Antes la única forma de emitir una era cobrando, así que todo eso obligaba a entrar en la base de datos.
  • El cliente da de baja su propio NAS, desde su cuenta. Cada clave vale para un equipo, así que sin esto cambiar de máquina —o que se rompa un disco— obligaba a escribirte y esperar respuesta. Es su licencia: puede moverla cuando quiera. El mensaje que ve en el NAS cuando la clave está en otro equipo ya le dice justamente eso.
  • Una clave que no consta emitida no se activa. La firma la comprueba el NAS por su cuenta, pero una licencia que no está en la base de datos no la has vendido tú. Es lo que convierte esto en una lista de clientes de verdad y no en un contador.
  • Licencia gratis al registrarse, con su interruptor en /admin. Para una campaña, una beta o una feria: quien crea una cuenta sale con su clave emitida. No es el modo gratuito — cada uno tiene su licencia, con su correo dentro y para un solo equipo—, y al apagarlo las que ya diste siguen valiendo.
  • PostgreSQL de verdad: psycopg entra en las dependencias. Estaba documentado como «cambia una línea del .env», pero sin el driver ese cambio fallaba al arrancar — el peor momento para descubrirlo.

1.20.02026-08-01

Repaso del Panel de Control con el panel abierto delante

  • El primer arranque no entraba. «Crear y entrar» creaba la cuenta de administrador y devolvía a la pantalla de acceso, sin un mensaje: el asistente mandaba el token en el cuerpo de la respuesta, pero la sesión vive en una cookie desde hace dos versiones y el panel ya no guarda ese token. Lo primero que hace cualquiera con LGM-OS recién instalado parecía roto.
  • Un fallo del servidor llegaba como «Error HTTP 500» a secas. Cualquier excepción que se escapara de un endpoint salía con el cuerpo de texto plano de Starlette, y el panel —que espera {"detail": …}— no encontraba nada que enseñar. Desde el otro lado es indistinguible de que el botón no haga nada. Ahora todo 500 llega con el motivo escrito; el rastreo completo sigue yendo al registro, que es donde se investiga.
  • El diálogo de usuarios se comía el error. Una contraseña que no cumplía la regla, o un nombre repetido: el servidor lo rechazaba, el aviso se pintaba en la pantalla de detrás y el propio diálogo lo tapaba. Pulsabas «Guardar» y no pasaba nada. El aviso va dentro.
  • Falta una carpeta de configuración y el panel no lo decía. Escribir en un directorio de «drop-in» que el paquete aún no ha creado (/etc/ssh/sshd_config.d, por ejemplo) reventaba con un 500 mudo. Se crea si falta, y si aun así falla se explica cuál era.
  • DNS y DHCP repetían el mismo párrafo dos veces. «dnsmasq no está instalado…» viajaba a la vez en hint y en warnings, y el panel pinta los dos, uno debajo del otro.
  • Comprobación del sistema repetía el consejo de cada fallo. El resumen de cada grupo era literalmente el consejo de su primera comprobación fallida, y esa comprobación aparecía tres líneas más abajo con el mismo texto. Ahora la cabecera dice QUÉ falla y el consejo se queda donde sirve.
  • Comprobación del sistema, entera en castellano dentro del panel en inglés. Esa pantalla la escribe toda el servidor —nombres de servicio, resultados y consejos— y no había ni una frase en el catálogo: 70 entradas nuevas, más los «falta el paquete» de certbot, dnsmasq, WireGuard, ffmpeg y rclone, que son lo primero que se lee en un NAS recién instalado.
  • Otros dos restos en castellano en el panel inglés: la zona horaria cuando no se puede leer («desconocida») y la etiqueta «· minimizada» de la barra de tareas.
  • «Programar» salía apagado sin decir por qué. En Tareas programadas el botón depende de un campo cuyo texto solo está en el placeholder. Ahora se dice qué falta.
  • El SAI guardaba en silencio. «Aplicar cambios» guardaba bien y no cambiaba nada en pantalla: no había forma de distinguirlo de un botón que no hace nada.
  • Red, sin ninguna tarjeta, no decía nada. La sección quedaba con el aviso amarillo y el vacío. Y es justo la pantalla a la que se viene cuando el NAS ha perdido la red.
  • El pie de la pestaña rsync era el de NFS. Mandaba a elegir las carpetas en «Carpetas compartidas» cuando se marcan ahí mismo, unas líneas más arriba.
  • Copias: el motivo del fallo podía ser «\<5\>NOTICE:». El prefijo de syslog que rclone escribe en cada línea se colaba donde va la explicación. Parece una explicación, ocupa su sitio y manda a buscar por donde no es.

Copias de seguridad

  • El motivo de un fallo podía ser una línea de «todo va bien». Cuando una copia a la nube falla sin explicarse, el panel relanza la misma orden en seco (--dry-run) para ver qué pasa. Las líneas de ese ensayo —«ISOS/ubuntu.iso: Skipped copy as --dry-run is set»— acababan ocupando el sitio del motivo: son justo lo contrario de un error, la prueba de que rclone leyó el origen y supo qué copiar. Ahora se descartan, con su etiqueta de nivel («NOTICE:») delante o sin ella.
  • Y el código 1 acusaba al panel de un fallo que no era suyo. Se traducía por «error de sintaxis (un fallo del panel, no tuyo: repórtalo)», que es lo que dice la documentación de rclone; en la práctica rclone devuelve 1 para casi cualquier cosa que salga mal durante la transferencia. Una copia que había listado archivos y fallado a mitad llegaba anunciada como un error nuestro, y mandaba a buscar donde no había nada. Ahora dice lo único cierto: que el motivo está en el registro completo.
  • El aviso de «todavía no disponible» empezaba a media frase. El texto de cada capacidad hacía dos trabajos a la vez: describir la función (y se lee detrás de su nombre) y explicar por qué no está. Donde el panel lo enseña solo —el aviso del destino que has elegido— salía «copias incrementales con enlaces duros. Falta rsync…»: una frase sin principio, en minúscula y sin sujeto. Ahora son dos campos: qué hace y por qué no está, cada uno donde sirve.
  • Y lo mismo con el cifrado, en el último paso del asistente: bajo un título que ya decía «Todavía no disponible» venía «el cifrado de la copia todavía no está disponible: …», repitiendo el título y empezando en minúscula.
  • «Crear la copia» salía en gris sin decir qué faltaba. Es el último paso de un asistente de cuatro: se ha recorrido todo y parece que la aplicación se ha atascado. Ahora dice cuál de las tres cosas falta.

Y el resto del escritorio, recorrido igual

  • El terminal se quedaba en negro sin decir nada. Cuando el servidor no puede abrir una sesión manda el motivo y cierra acto seguido; ese texto se escribía dentro de xterm, que en ese caso no ha llegado a pintar un solo fotograma. Lo que quedaba era un rectángulo vacío con un «Desconectado» al lado. El motivo se enseña fuera del terminal.
  • La tienda tenía la misma categoría dos veces, una en inglés. «Networking (24)» junto a «Red (16)», un «Home» que era Domótica, y «Finance» y «Social» sueltas: las tiendas de CasaOS no se llaman igual entre ellas y lo que no estaba en la tabla se colaba con su nombre original. Ahora lo desconocido va a «Otros», como ya hacía la fuente de Unraid.
  • «Analizar» del Informe de espacio, apagado sin decir por qué. En un NAS recién instalado no hay volumen del que proponer carpetas, el campo sale vacío y el botón en gris no contaba nada.
  • Cada carpeta compartida enseña sus TRES protocolos, no dos. WebDAV faltaba en la fila —solo estaban SMB y NFS— aunque el editor de la carpeta sí lo ofrece: la lista decía «va por SMB y no por NFS» y se callaba justo lo que hace falta para llegar a ella desde fuera de casa. Va con su color, el mismo turquesa que WebDAV ya tiene en «Usuario y grupo», y como los otros dos es su propio interruptor.

1.19.02026-08-01

Puesta a punto para producción

  • SMB3 con cifrado obligatorio. Estaba fijo en SMB2 con «cifra si el cliente quiere»: bastaba con que no quisiera para que las contraseñas y los archivos viajaran en claro por la red local. Hay un interruptor —«Admitir equipos antiguos»— para quien tenga Windows 7, algún Android TV o impresoras que solo hablan SMB2, con su aviso de lo que cuesta.
  • El panel se puede acotar por red. Era la única regla del cortafuegos que no se podía restringir: la administración del NAS estaba siempre abierta a quien llegara al equipo. Ahora se indican las redes desde las que se abre (Seguridad → Cortafuegos). Vacío = como hasta ahora, para que nadie se quede fuera al actualizar.
  • La caducidad de contraseñas se aplica de verdad. Se guardaba y se enseñaba en el panel, y al cumplirse el plazo no pasaba nada: un control que aparentaba existir. Ahora, al entrar, una contraseña vencida obliga a cambiarla. Las que nunca tuvieron fecha no caducan de golpe: se les pone la de hoy y el plazo cuenta desde ahí.
  • El asesor de seguridad avisa de los contenedores expuestos. Los puertos que publica un contenedor no pasan por el cortafuegos del NAS (Docker filtra en otra cadena del sistema), así que instalar una aplicación abría un puerto a toda la red sin que nada lo dijera. Ahora se dice, con el arreglo: publicar en «127.0.0.1» y sacarla por el proxy inverso.

1.18.12026-08-01

Auditoría de seguridad: 33 fallos confirmados, 24 corregidos

  • Un usuario de «solo ver» podía cambiar la contraseña de root del NAS. La contraseña nueva viajaba sin filtrar al chpasswd del proceso privilegiado, y ese programa lee una cuenta por línea: bastaba con meter un salto de línea y una segunda línea «root:…». Cambiarse la propia contraseña no exige ser administrador, así que lo alcanzaba cualquiera. Cortado en el modelo, en la política y en el propio sincronizador.
  • El proceso root aceptaba cualquier variable de entorno del panel. Con LD_PRELOAD o PATH se ejecutaba código ajeno dentro de él, lo que anulaba entera la lista blanca de órdenes. Ahora hay una lista blanca cerrada de variables, con el valor validado.
  • Las recetas del catálogo comunitario se revisan antes de instalarse: se rechazan privileged, el socket de Docker, montar carpetas del sistema, los dispositivos y las capacidades con las que se sale del contenedor. Lo que el validador no sabe leer, se rechaza. Por eso salen del catálogo propio Portainer (su función —gestionar Docker— ya está en el panel, y exige el socket que da root) y la capacidad SYS_MODULE de wg-easy (innecesaria: Debian 12 trae WireGuard en el núcleo).
  • Un «solo ver» podía leer el `.env` de cualquier app instalada, con las contraseñas que escribió el administrador. Ahora es solo para administradores.
  • Las URL de webhook y de Discord de las alertas se ocultan como los demás secretos: en esos canales la URL es la credencial.
  • El zip de un enlace público a una carpeta incluía su papelera: quien recibía el enlace se llevaba todo lo borrado.
  • Restaurar de la papelera confiaba en la ruta guardada dentro de ella: quien pudiera escribir ahí elegía dónde aterrizaba el archivo. Ahora nunca sale de su carpeta.
  • El límite de intentos se saltaba inventando una cabecera: se añade un límite por cuenta, que no depende del origen.
  • Cambiar la contraseña cierra las sesiones abiertas (antes una cookie robada seguía valiendo 30 días).
  • Una persona, una contraseña — ahora también en WebDAV. Marcar «Acceso web a los archivos» a alguien que ya existía pedía su contraseña al administrador, que no la sabe. Se apunta y se completa sola en cuanto esa persona entra al panel. De paso: sin contraseña ya no se crea una cuenta de WebDAV vacía.
  • Cambiar la contraseña de otro usuario cierra sus sesiones: antes no había forma de echar a una cuenta comprometida salvo borrarla.
  • Un usuario de «solo ver» ya puede descargar y previsualizar archivos.
  • La papelera respeta que la hayas desactivado en una carpeta compartida.
  • Restaurar dos veces el mismo nombre ya no machaca lo restaurado antes.
  • La restauración desde la nube ya no le cambia el dueño a toda la carpeta destino, solo a lo restaurado.
  • Un fallo pasajero al leer la clave SSH ya no regenera el par, que dejaba fuera de servicio todos los destinos remotos a la vez.
  • Panel: el terminal root vuelve a poder elevarse con 2FA, el análisis de espacio ya no se queda en «Analizando…» al escribir, el formulario del SAI no se pisa solo cada diez segundos, subir dos veces el mismo archivo funciona, y las cámaras dicen cuándo no hay permiso para ver la imagen en vez de girar para siempre.
  • Emitir un segundo certificado borra del vhost del desafío los dominios ya emitidos, y su renovación automática empieza a fallar en silencio (services/certs.py:378).
  • Un certificado comodín no se puede usar en ninguna publicación del proxy inverso: el linaje se busca por el dominio exacto de la regla (api/external.py:638).
  • El DDNS de Cloudflare fuerza `proxied=false` y desactiva el proxy naranja, dejando la IP del domicilio a la vista (api/external.py:231).
  • La regla de WebSockets del proxy casa el prefijo sin exigir separador y puede capturar las conexiones de otra publicación del mismo dominio (services/proxy.py:83).
  • Los puertos que publica un contenedor no pasan por el cortafuegos (Docker filtra en la cadena forward). Sin arreglar a propósito: hacerlo mal tumba la VPN y la salida a internet de todos los contenedores, y no se puede probar sin nftables delante. El asesor de seguridad ya avisa de qué contenedores están expuestos.
  • La descarga de catálogos solo comprueba el esquema de la URL inicial: una redirección puede acabar bajando el catálogo por HTTP en claro (api/apps.py:717).
  • Nada comprueba que el destino local esté montado: la copia se escribe en el disco del sistema y se da por buena (services/backup_engine.py:633).
  • La revocación por devolución nunca se dispara: DEPLOY.md manda registrar en Stripe solo los dos eventos de checkout, sin los de reembolso.
  • El registro del portal delata qué correos tienen cuenta, justo lo que el login evita a propósito con su hash señuelo (portal/app/routes/web.py:78).

1.18.02026-08-01

Identidad de empresa, servicios que faltaban y una tanda de seguridad

  • Un solo botón Editar por persona, con cuatro pestañas: Información (contraseña y acceso al panel), Aplicaciones (qué servicios usa), Grupos y Permisos (la matriz de carpetas). Antes había dos botones que abrían casi lo mismo.
  • La pestaña de permisos refleja los grupos que acabas de marcar, no los que tenía al abrir el diálogo.
  • Unir el NAS a un dominio que ya exista (realmd/sssd): sus usuarios pasan a existir en el equipo y se les dan permisos en las carpetas. La contraseña del alta viaja por stdin, nunca en la línea de órdenes.
  • Opcional y apagado de fábrica: que las carpetas compartidas acepten cuentas del dominio (winbind con los mismos uid que sssd) y que esas cuentas entren al panel, siempre como «solo ver».
  • rsync como servidor: otro NAS deja aquí sus copias, con su usuario y contraseña propios y solo en las carpetas que marques. Puerto 873 atado al interruptor en el cortafuegos.
  • Red: rutas estáticas, DNS manual del propio NAS (lo que pide unirse a un dominio) y proxy de salida para las descargas del NAS y de Docker.
  • El servidor habla inglés: los mensajes que redacta el backend se traducen en la frontera HTTP, incluidos los compuestos («fallo al subir X: rclone terminó con el código 1 (…)»).
  • Un fallo con «código 1» y ni una línea de explicación era el panel diciendo que no sabía nada: con --log-file, rclone manda todo a su registro y si muere antes de abrirlo el motivo no queda escrito en ninguna parte. Ahora se comprueba la conexión antes de subir (que ya no esté en la configuración es justo lo que produce ese código), y si aun así falla mudo, la subida se repite en seco sin registro para que rclone hable. Como última red, el mensaje incluye la orden exacta que lanzó el panel.
  • Una cuenta del dominio llamada «root» podía entrar al panel y, peor, la reposición automática de SMB le reescribía la contraseña de root del NAS. Las cuentas del sistema quedan excluidas del camino del dominio, la contraseña del dominio ya no se copia al equipo, y el ticket de Kerberos se comprueba contra la clave de máquina del keytab (kvno -k): un KDC falso en la red ya no cuela.
  • La contraseña del proxy de salida se escribía en archivos legibles por todos: ahora 0600.
  • El servidor rsync escribe como root, así que lo que entra pierde setuid/setgid: sin eso, quien tuviera la contraseña podía dejar un programa con poderes en una carpeta compartida.
  • Las credenciales del alta al dominio se barren al arrancar si un corte las dejó a medias.
  • Vaciar el DNS manual restaura de verdad el anterior (antes el NAS seguía preguntando a los servidores viejos mientras el panel decía «automático»).
  • Reconfigurar la IP de un bond o una VLAN ya no lo deshace.
  • El grupo de administradores del panel se ve en la pestaña de grupos (antes decía «no hay grupos todavía» en un NAS que sí los tenía).
  • El cortafuegos abre y cierra los puertos de todos los servicios, también al arrancar.

1.17.02026-07-30

Gran revisión: que funcione todo lo que está implementado

  • Probar conexión (Diagnóstico): ping no estaba en la lista de órdenes permitidas y la prueba devolvía un error seco. Ahora funciona (y el paquete se instala en equipos que no lo traigan).
  • La barra de progreso al actualizar LGM-OS: el panel no tenía permiso para leer el registro del actualizador y se quedaba muda justo en la parte larga. Ahora se ve fase, porcentaje y registro en vivo.
  • Crear usuarios con SMB/SSH: el proceso privilegiado tenía /home en solo lectura y el alta fallaba con un error 500. La jaula SFTP ahora se crea de verdad.
  • El candado de los snapshots por fin se ve: zfs holds se invocaba sin argumentos (falla siempre) y ningún snapshot aparecía como protegido aunque lo estuviera. La protección anti-ransomware ahora se muestra tal cual está en ZFS.
  • La elección de widgets ya viaja con tu cuenta: se guardaba contra una ruta que no existía (404 silenciado) y cada navegador volvía a los widgets de fábrica.
  • «Esta sesión» en Usuarios conectados: nunca se marcaba (buscaba la sesión en una cabecera que ya no se usa); ahora tu navegador aparece señalado.
  • La ISO arranca también por BIOS desde USB (faltaba el código de arranque del MBR), y el instalador ya no muestra «paso 16/15».
  • Probar alertas decía «enviada» aunque el SMTP estuviera mal: ahora envía por cada canal configurado, se salta el resumen diario, y dice qué canales salieron y cuáles fallaron.
  • El SAI decía «configurado» con el driver caído: ahora explica por qué no responde (con el último error del sistema) en el propio panel.
  • Fallos de red visibles en todo el panel: NFS, WebDAV, permisos de usuario, redes/ISO de una máquina virtual, la pantalla del servidor y los widgets ya no fingen «no hay nada» cuando lo que pasa es que una petición falló. Y si el backend se está reiniciando, el panel dice «reintentando…» en vez de expulsarte al login con tus credenciales «malas».
  • Las copias en la nube y los pagos, más honestos: un destino Nextcloud mal escrito ahora se autocompleta (la dirección WebDAV de verdad se construye sola) y el error 405 se traduce a qué tocar.
  • Tareas programadas: se pueden pausar y reanudar sin borrarlas, y una tarea pausada se ve claramente distinta (antes eran idénticas y creías tener snapshots que no corrían).
  • 2FA con código QR, como la VPN: se escanea con la cámara en vez de teclear 32 caracteres.
  • Los discos de la visión general ya no fingen ser botones, el asistente de crear pool no pierde lo elegido si algo falla, la tienda no deja instalar sin los campos obligatorios, y el interruptor de registros dice «Pausado» cuando está pausado.
  • Ventanas y diálogos: botones unificados a la IZQUIERDA (cerrar, minimizar, maximizar), con ajuste «Izquierda/Derecha» en Panel de control → Opciones regionales → Apariencia.
  • La pantalla del propio servidor iba al 97 % de CPU: Chromium arrancaba sin aceleración gráfica y el desenfoque del cristal se repintaba por software. Ahora los controladores de la GPU (Intel/AMD/NVIDIA) se detectan e instalan solos —también al actualizar—, Chromium arranca con la GPU activada y el quiosco usa el nuevo modo de efectos ligeros (sin desenfoque, superficies opacas), que cualquiera puede elegir también en Apariencia.
  • ZFS también se reintenta en cada actualización si la instalación se quedó sin él.
  • No se puede cobrar lo que no se puede entregar: la venta solo se ofrece con TODAS las piezas configuradas (Stripe + secreto del webhook + clave de firma), y en producción el portal se niega a arrancar a medias.
  • Ningún pago se entierra: un pago que no se pueda atribuir responde error a Stripe para que lo reintente y lo marque en rojo (antes se «procesaba» y desaparecía). Las devoluciones y disputas revocan la licencia automáticamente (reversible desde admin).
  • La página de «gracias» se actualiza sola hasta que aparece la clave; la sesión caducada vuelve al login en vez de enseñar un JSON; el admin tiene buscador por correo/licencia y el contador dice el total real; la base de datos vive en /data (sobrevive al redeploy) y el límite de intentos de login distingue las IP reales detrás del proxy.

1.16.02026-07-30

El panel, de verdad cómodo en el móvil

  • El panel se siente como una app en iOS y Android, no como una web encogida. Los campos ya no provocan el zoom brusco de Safari al tocarlos (16 px), los botones y campos son lo bastante grandes para el pulgar, desaparece el destello gris al tocar y hay un pequeño hundido al pulsar. El scroll lleva inercia y no arrastra la página por detrás.
  • Los diálogos suben desde abajo (hoja inferior a todo el ancho, con las esquinas redondeadas y respetando el área de la muesca y el indicador de inicio), que es el gesto que todo el mundo reconoce en el móvil. Todo esto es global: mejora TODO el panel de una vez.

Revisión de seguridad del portal de licencias (antes de comercializar)

  • Se cierra un fallo crítico: el portal ya NO arranca en producción con la clave de sesión de fábrica (estaba en el repositorio, y con ella se podía falsificar la cookie de un administrador). Ahora exige una clave propia o se niega a arrancar.
  • Una compra solo la ve su dueño: la página de «gracias» comprobaba únicamente el identificador de la sesión de pago; ahora exige haber iniciado sesión y ser el titular, así que nadie ve la clave de la compra de otro.
  • Login a prueba de fuerza bruta: límite de intentos por IP y comprobación en tiempo constante (no se puede averiguar qué correos tienen cuenta midiendo el tiempo de respuesta).
  • Cabeceras anti-secuestro de clic para que ninguna web pueda enmarcar el panel de administración, pagos diferidos (SEPA) que ahora sí emiten la licencia al confirmarse el cobro, y varios endurecimientos más (límites de tamaño, comprobación de licencias solo por https). El webhook de Stripe ya era sólido y así se confirma.

1.15.02026-07-30

Saber en qué se va el disco y cómo ha ido el equipo

  • Informe de espacio. Analiza una carpeta y enseña, de un vistazo, en qué se está gastando el disco: las carpetas más grandes (con su barra), los archivos más grandes y los archivos duplicados que se podrían borrar, con cuánto se liberaría. Los duplicados se detectan por CONTENIDO, no solo por tamaño —dos archivos del mismo tamaño no bastan—, así que no invita a borrar algo que en realidad es distinto. El análisis corre en segundo plano (no bloquea el panel) y está acotado para no ponerse a leer terabytes. En Panel de control → Sistema.
  • Historial de métricas. El monitor en vivo ya no se olvida de todo al cerrarlo: CPU, memoria, red y disco se guardan en una serie temporal ligera (una muestra por minuto, en su propio SQLite) y se pueden ver por hora, día, semana o mes. Los rangos largos se agregan para que la gráfica pese lo mismo tanto para una hora como para un mes.

Menos toquetear el sistema a mano

  • Editor visual de puertos y carpetas de las apps. Cambiar por qué puerto sale una app o qué carpeta ve ya no obliga a editar el docker-compose a mano: hay un formulario con filas (puerto del NAS → puerto del contenedor, carpeta → carpeta). Lo que no se puede poner en un formulario —una sintaxis avanzada— se respeta tal cual, y un volumen no puede montar el sistema (las rutas tienen que estar dentro de los datos del NAS). Al aplicar, la app se recrea y, si no levanta, se restaura sola la configuración anterior.
  • Los discos aparecen al enchufarlos. Conectar o quitar un disco actualiza la lista del panel al instante, sin recargar, y avisa. Se escucha el flujo de eventos de udev sin privilegios y sin instalar ninguna regla en el sistema.

Licencia vitalicia, sin candados sobre tus datos

  • LGM-OS pasa a ser de pago único y para siempre. Se compra una vez —con las actualizaciones incluidas— y se activa pegando la clave en Panel de control → Sistema → Licencia. Sin suscripción, sin cuotas y sin caducidad.
  • Se comprueba en el propio equipo, sin internet. La clave va firmada (Ed25519) y el NAS la verifica él solo: no depende de estar conectado a ningún servidor. Si hay conexión, de vez en cuando comprueba que la licencia no se haya dado de baja, pero perder la red no cambia nada.
  • El NAS NUNCA se bloquea por la licencia. Sin licencia, con la licencia revocada o sin internet, tus archivos, tus copias y todos los servicios siguen funcionando con normalidad. La licencia habilita el producto; jamás es un candado sobre lo que es tuyo. Un NAS que se apaga solo porque no llega a un servidor sería una forma de perder los datos de la gente, y eso no pasa aquí.
  • Vinculada a ti, no a la máquina. Si el equipo se estropea, montas otro, pegas la misma clave y sigues donde lo dejaste. Sin llamar a nadie y sin límite de activaciones.
  • Interruptor de «modo gratuito». Un ajuste global que, en «no», libera el producto: todos los NAS dejan de pedir licencia. Pensado para el día en que se decida dejarlo gratis. Nunca se libera por un fallo de red: hace falta decirlo expresamente.
  • Portal web de licencias (aplicación aparte, en portal/): registro, compra con Stripe y descarga de la clave. El cobro es a prueba de trampas —la licencia se emite solo con el webhook de Stripe firmado y verificado, nunca desde la página de vuelta, y es idempotente— y el portal no ve ninguna tarjeta (la pantalla de pago la aloja Stripe). Con SQLite por defecto y listo para PostgreSQL cambiando una línea.
  • La web lo cuenta claro y toda la documentación se lee sin registrarse ni iniciar sesión: no hace falta estar conectado a nada para mirar antes de comprar.

1.13.02026-07-30

El corte de luz ya no corrompe datos

  • Apagado ordenado por el SAI. Antes, al agotarse la batería, se hacía un «apagar» a secas y systemd lo paraba todo a la vez: si una máquina virtual o un contenedor seguían escribiendo cuando se desmontaba su disco, el sistema de archivos quedaba a medias. Ahora hay un orden: se cierran las máquinas virtuales (y a las que no obedecen se les corta la corriente), se paran los contenedores, se vuelca todo a disco y solo entonces se apaga. Cada paso con su tiempo de gracia, y el apagado final es incondicional: la batería no espera.
  • El panel enseña qué se apagaría en un corte ahora mismo —tus máquinas y contenedores en marcha—, para que el apagado no sea una caja negra.

SFTP enjaulado: dar acceso a los archivos sin dar una consola

  • Nueva capacidad de usuario «Copia de archivos remota (SFTP)». El usuario entra por SFTP encerrado en su carpeta, sin terminal, sin túneles y sin ver el resto del sistema. Es la forma segura de dar acceso remoto a alguien que solo necesita subir y bajar archivos, sin entregarle una consola de la máquina. La «Consola completa (SSH)» sigue existiendo para quien administra, y las dos son excluyentes.

Agregar tarjetas de red (bond)

  • Unir dos o más tarjetas de red en una sola conexión, desde el panel. El backend ya sabía hacerlo, pero no había forma de pedirlo sin editar archivos de systemd-networkd a mano —así que el «bond» que prometía el README no se podía usar—. Ahora hay un formulario: eliges las tarjetas, el modo (tolerancia a fallos, más velocidad por LACP, o reparto por turnos) y su dirección, cada modo explicado por lo que hace y no por su nombre técnico.

NFS: quién puede montar cada carpeta

  • Clientes NFS autorizados por IP. Antes, exportar una carpeta por NFS la abría a cualquier equipo de la red con permiso de escritura —y NFS no pide contraseña, así que no había barrera ninguna—. Ahora se puede limitar a las IP o redes que tú digas: para conectar Proxmox o ESXi, pones su IP y solo ese equipo la monta. Dejarlo vacío mantiene el comportamiento de antes (abierta a la red). root_squash sigue siempre puesto: root del cliente nunca es root del NAS.

Certificados sin abrir ningún puerto

  • Validación por DNS (DNS-01), además de la de siempre por HTTP. Antes, para un certificado de Let's Encrypt había que abrir el puerto 80 en el router; detrás de CGNAT eso era imposible. Ahora se puede validar creando un registro en el DNS —con el token de la API de Cloudflare— sin abrir nada. Y es el único modo que permite certificados comodín (*.tudominio), que cubren todos los subdominios de una vez. El token se guarda cifrado en el NAS y la renovación automática sigue funcionando igual.

1.12.02026-07-30

La VPN se configura sola

  • La dirección pública se detecta sin preguntar. Antes había que escribir el dominio o la IP a mano, y quien no lo hacía se llevaba una configuración que solo funcionaba dentro de casa. Ahora el orden es: lo que hayas escrito, el dominio del DNS dinámico si lo tienes, la IP pública real preguntada a internet y, en último caso, la IP local (diciéndolo).
  • Redes de casa configurables, que es lo que de verdad decide si un cliente llega a algo. El NAS detecta UNA red —la de su tarjeta—; aquí se añaden las demás, como una VLAN de cámaras o una segunda subred.
  • Se avisa si un cliente no va a llegar a nada. En túnel dividido sin red local detectada se entregaba en silencio una configuración que conecta, pone «conectado» y no alcanza ningún dispositivo: el fallo más difícil de diagnosticar de una VPN. Ahora se dice al crearlo, con la solución dentro.
  • El DNS se propone solo: si el NAS ya sirve DNS o DHCP, se ofrece su propia dirección del túnel en vez de dejarte averiguarla.

Avisos que te enteras (y que no te despiertan sin motivo)

  • Telegram, Discord y ntfy, cada uno con su formulario. No valía apuntarles el webhook genérico: cada uno espera su propio formato de mensaje, y descubrirlo lleva una tarde.
  • Cada nivel elige por dónde sale. Un disco fallando puede sonar en el móvil de madrugada y «hay una actualización de un contenedor» esperar al resumen diario, que agrupa lo no urgente en un solo aviso. Lo que no sale por ningún canal sigue en el panel: no interrumpir no es perderlo.
  • Avisos nuevos: CPU al tope de forma sostenida (con un «durante» configurable, para que no chille cada vez que arranca una copia) y temperatura de disco — SMART no da un disco por averiado porque se caliente, pero el calor es lo que acorta su vida.

El NAS aparece solo en Windows y en el Mac

  • WS-Discovery y Bonjour, encendidos con las carpetas compartidas. Desde que Microsoft retiró SMB1, un NAS que no habla WS-Discovery no sale en «Red» de Windows y hay que escribirse la IP de memoria; macOS mira mDNS, que es otro protocolo distinto.
  • Nombre, descripción y grupo de trabajo configurables, aplicados a la vez en los tres sitios que tienen que decir lo mismo (smb.conf, Avahi y wsdd). Un nombre que Windows no aceptaría se rechaza al guardarlo, no cuando no encuentras tu NAS.

Seguridad

  • El bloqueo automático vale ya para SSH. Antes solo contaba los intentos del formulario del panel: un NAS con SSH abierto recibía miles de intentos al día sin que nada los parara. Ahora se leen del diario del sistema y el bloqueo lo aplica el cortafuegos, que es lo único que sshd respeta. Comparte umbral y lista blanca con el del panel, así que quien ataque por los dos sitios no tiene el doble de oportunidades.
  • Copias en la nube con Object Lock (S3, Wasabi, MinIO): cada archivo subido queda bloqueado el tiempo que digas y ni la propia cuenta puede borrarlo antes. Es la diferencia entre una copia y una defensa contra el ransomware. Requiere activar Object Lock en el cubo desde el proveedor: está avisado en el propio campo.

Herramientas de diagnóstico y automatización

  • Pruebas de red que salen de verdad: ping, comprobar un puerto de cualquier equipo y medir la velocidad de bajada real. Hasta ahora la comprobación del sistema solo miraba hacia dentro.
  • Tres automatizaciones nuevas en las tareas programadas: limpiar temporales, organizar descargas por tipo y ordenar series en Serie/Season NN/. Las tres solo tocan lo que reconocen con certeza: nunca inventan una categoría, nunca sobrescriben y nunca salen de la carpeta indicada.
  • Descargar el registro que se está viendo, ya filtrado, desde el Centro de registros.

El Centro de registros dice la verdad

  • «Máquinas virtuales» no funcionaba, y nunca lo había hecho. Estaba en el desplegable de orígenes pero no en la lista blanca del servidor, así que elegirlo daba siempre un error con los identificadores internos en inglés en pantalla. Ahora hay un test que compara las dos listas para que no vuelvan a desfasarse.
  • El arranque de Samba ya no sale en rojo. Samba traduce su nivel de depuración 0 —que significa «imprimir siempre», no «algo falla»— a prioridad de error, así que el filtro «Solo errores» sobre las carpetas compartidas enseñaba el arranque del servicio en vez de los fallos. Se degrada solo lo que se reconoce como rutina, una a una: un fallo de verdad de Samba sigue llegando en rojo.
  • La mitad de las filas eran rutas del código fuente de Samba. Samba escribe cada mensaje en dos entradas del diario —una cabecera con el archivo y la función, y el mensaje—; ahora se unen, y el sitio del código queda en gris detrás del texto, donde no estorba.

Detalles

  • La consola de una máquina virtual se abre en una pestaña, no en una ventana emergente.
  • Las ventanas y los diálogos recuerdan el tamaño al que los dejaste.
  • Arreglado: el almacén de notificaciones compartía un objeto entre llamadas y el módulo iba acumulando avisos fantasma que ya no estaban en disco.

1.11.02026-07-26

El panel, en el móvil, con forma de aplicación

  • En pantallas estrechas ya no hay escritorio. Por debajo de 820 px el panel cambia de forma: pantalla de inicio con la rejilla de aplicaciones, la app abierta ocupando toda la pantalla y una barra abajo con Inicio, Abiertas y Widgets. No es el escritorio encogido — las ventanas no caben en seis pulgadas y las asas de redimensionar miden dos milímetros.
  • Volver al inicio no cierra nada: las aplicaciones siguen cargadas y se salta entre ellas desde «Abiertas», como en cualquier teléfono.
  • Se respeta la muesca: barras arriba y abajo apartadas de las áreas seguras, y la pantalla completa aprovechada de verdad.
  • El corte va por ANCHO, no por «¿es un móvil?»: una tableta en horizontal se maneja bien como escritorio y en vertical no.
  • Instalable como app también en iPhone. El icono de la pantalla de inicio era un SVG y iOS no los admite ahí: ponía una miniatura de la página en vez del logo. Ahora hay iconos PNG de 192, 512 y 180 px, y el panel se abre sin la barra del navegador.

Una sola puerta: la app Sistema se va al Panel de control

  • Centro de registros, en Panel de control → Administración. Antes era una lista pelada dentro de Sistema: elegías un servicio y salían trescientas líneas en gris. Ahora los registros de los servicios y la auditoría del panel —quién hizo qué y desde dónde— se miran en el mismo sitio, con filtro por gravedad, búsqueda de texto y modo en vivo para dejarlo abierto mientras reproduces el fallo. Y dice cuántas líneas estás viendo de cuántas hay.
  • La app Sistema desaparece del escritorio. Tenía las mismas cosas que el Panel de control y en dos sitios distintos: quien buscaba «alertas» no sabía en cuál mirar. Sus secciones son las MISMAS —no se ha reescrito nada— y ahora viven donde se buscan: Información del equipo, Red, Comprobación, Actualizar LGM-OS, Tareas programadas, Alertas, Copia de la configuración y Apagar y reiniciar.
  • Los textos y las guías que mandaban a «Sistema → algo» apuntan ya al Panel de control: una instrucción que nombra un icono que no existe es peor que no tenerla.

Acceso: decir lo que pasa de verdad

  • La contraseña incorrecta ya no dice «Sesión expirada». Todos los errores 401 se traducían igual, así que el «usuario o contraseña incorrectos» que manda el servidor no llegaba nunca a la pantalla. Peor: el código de doble factor también se pide con un 401, así que con 2FA activo no había forma de entrar.
  • El usuario ya no viene con «admin» escrito. El nombre del administrador lo elige quien instala; sugerir uno hacía que se intentara entrar con una cuenta que no existe. Los dos campos llevan ahora un marcador que dice qué va en cada uno.

Seguridad

  • Los WebSockets comprueban de dónde vienen. No pueden llevar la cabecera del panel, así que lo único que impedía que una web cualquiera abriera un terminal contra el NAS de quien la visitaba era que el navegador respetase la cookie. Ahora se valida el origen en el terminal, la consola de las máquinas, el directo de las cámaras y las métricas.
  • El panel ya no se puede meter en un iframe ajeno (clickjacking), no se adivina el tipo de los archivos servidos y la dirección no se cuenta fuera en la cabecera «Referer», que es por donde se escapaba el vale de las cámaras.
  • El CORS de desarrollo ya no se instala en los NAS: iba escrito en el código y permitía que cualquier cosa servida desde localhost:5173 hablara con la API. Ahora se enciende con una variable de entorno, y solo hace falta al desarrollar.
  • El nombre de una carpeta compartida por enlace ya no se cuela en la cabecera de descarga: una carpeta llamada factura".exe rompía las comillas y el archivo se guardaba con otro nombre.

Cámaras: una sola conexión, y directo de verdad

  • Una conexión por cámara, repartida entre todos los que miran. Antes cada pestaña abierta lanzaba su propio ffmpeg contra la cámara; muchas cámaras domésticas aceptan una sola conexión de vídeo y a la segunda contestan mal. Al irse el último espectador se apaga con unos segundos de gracia, así que recargar la página no cuesta una negociación nueva.
  • El directo va sin recodificar, copiando el vídeo tal cual y troceándolo para el navegador: menos de un segundo de retraso y prácticamente cero procesador. Las cámaras en H.265, que ningún navegador pinta así, se pasan solas a MJPEG y se avisa en pantalla.
  • La miniatura de la rejilla sale gratis del directo si ya hay alguien mirando esa cámara.

Máquinas virtuales

  • El CD se quita solo cuando la máquina ya está instalada, como en VMware. El instalador siempre reinicia al terminar y, con el CD puesto y mandando en el arranque, volvía a salir el instalador: desde fuera parecía que la máquina no se apagaba.
  • Ajustes avanzados: firmware BIOS o UEFI con arranque seguro, chip TPM 2.0, procesador del equipo o genérico, tarjeta gráfica y de dónde arrancar. Con eso ya se puede instalar Windows 11. Lo que este NAS no puede ofrecer se enseña apagado y explicado.
  • Forzar apagado para cuando la máquina no obedece, y tableta USB en las máquinas nuevas: sin ella el ratón de la consola va descolocado.

Almacenamiento: las cifras cuadran

  • Btrfs no se puede medir con la regla de un disco normal. df daba el total contando los discos enteros y lo libre ya descontada la copia del espejo: «7,5 GB usados de 45» con «6,6 GB libres» al lado, dos cifras ciertas de cosas distintas. Ahora las tres salen de la contabilidad propia de btrfs y suman, y los discos se dicen aparte con su perfil.

Documentación y web

  • Web pública generada desde el propio repositorio: presentación con lo que sabe hacer, página de descarga, novedades y las guías en HTML, con capturas de las apps de verdad.

1.10.02026-07-26

La actualización dice por dónde va, y por qué falla

  • Barra de progreso real, del 1 % hasta el reinicio. El actualizador escribe en su registro la fase en la que está, y el panel la convierte en un porcentaje: copia previa, paquetes del sistema, compilar la interfaz, backend, promoción, reinicio y comprobación. Dentro de cada fase avanza contando las líneas que va soltando, así que no se queda clavado cuatro minutos en el mismo número. La barra nunca retrocede y sobrevive al reinicio del propio panel, que es justo cuando antes se quedaba muda.
  • Si falla, el error se ve sin salir del panel. El aviso rojo trae las últimas líneas del registro —que es donde está el error de verdad— y el registro completo se abre por el final, no por el principio, que solo contaba lo que había ido bien.
  • Quedarse sin memoria ya no se confunde con un error de compilación. Cuando el kernel mata a node, npm dice «signal SIGKILL» y no «Killed», así que el mensaje genérico se llevaba también ese caso. Ahora se distinguen quedarse sin memoria, sin espacio y un fallo del código, cada uno con su explicación.

El gestor de archivos, rehecho por dentro y por fuera

  • Una barra de herramientas en vez de dos. Había once botones y nueve estaban apagados casi siempre, esperando a que seleccionaras algo. Arriba queda lo que siempre hace falta —navegar, la ruta, buscar y lista/cuadrícula— y las acciones sobre lo seleccionado aparecen abajo, flotando, solo cuando hay algo marcado y sin mover la lista.
  • Se reconoce el tipo de archivo de un vistazo: cada icono va en una pastilla de su color, las filas respiran y los atajos de cada fila salen al pasar por encima en lugar de una columna de enlaces azules.
  • Las carpetas de Docker y de las máquinas virtuales ya se ven. Son lo que más ocupa en un NAS con aplicaciones y no había forma de mirarlas sin entrar por SSH. Lo que sigue oculto es el almacén interno de Docker: ahí no hay nada tuyo y borrarlo rompe todos los contenedores.
  • La fila seleccionada se ve. El resalte iba en el <tr> y con la cabecera fija —que necesita la tabla en modo «separate»— ese fondo no llega a pintarse.

Contra el secuestro de datos: candado en los snapshots

  • Almacenamiento → Snapshots → Proteger. Un snapshot protegido (zfs hold) no lo borra nadie: ni el botón de eliminar, ni la limpieza automática por antigüedad, ni zfs destroy como root. Es lo primero que hace quien secuestra un NAS —borrar las copias— y lo único que de verdad lo impide. Se quita a mano desde el panel, a propósito.
  • El estado del candado se pregunta a ZFS, no se guarda en el panel: un candado que el panel «cree» tener pero no está sería peor que no tenerlo.
  • Btrfs no sabe hacer esto y la interfaz lo dice en lugar de fingir que sí.
  • Guía nueva: docs/ransomware.md, con qué protege de verdad, qué no y qué hacer en los primeros diez minutos si ya ha pasado.

Cámaras: el sondeo encuentra las que antes no aparecían

  • También se pregunta sin filtro de tipo. Media industria (Reolink, Tapo y casi todo lo genérico) ignora el sondeo por «NetworkVideoTransmitter» y solo contesta al abierto: esas cámaras no salían nunca y parecía que no tenían ONVIF.
  • Manda la dirección desde la que contestó de verdad. Muchas cámaras anuncian su IPv6 de enlace local, un 0.0.0.0 o la IP que tenían la semana pasada; el paquete ha llegado desde una dirección concreta y esa funciona seguro.
  • Un grabador con varios canales sale entero: se leen todas las respuestas del mensaje, no solo la primera. Y lo que no es una cámara —impresoras, televisores— se descarta.
  • Guía nueva: docs/cameras.md.

El anillo del foco

  • Cambiaba la forma del elemento al enfocarlo (forzaba esquinas de 8 px), se sumaba al anillo propio de los campos hasta hacer un borde azul de tres o cuatro píxeles, y lo llevaba *, así que el cuerpo de un diálogo o una fila entera se dibujaban de azul. Ahora es un contorno, por dentro, con la forma que ya tenga el elemento y solo en lo que se puede usar con el teclado.
  • En el tema oscuro, enfocar un campo no hacía nada: la regla clara pedía blanco y la oscura ganaba por ir después.

1.9.12026-07-26

Dos falsos positivos de la comprobación, y el sondeo de cámaras por todas las redes

  • «El panel tiene SSH apagado pero ssh está en marcha». La contradicción era del diagnóstico, no del sistema: SshConfig viene con enabled = True de fábrica y en la comprobación yo había escrito a mano un False. En un NAS donde nadie ha tocado ese interruptor no hay archivo de estado, así que el panel usaba el valor bueno y la comprobación se inventaba otro. Ahora lee el estado con el mismo modelo que usa cada servicio, así que los valores por defecto no pueden volver a separarse.
  • «Sin nas-helper el panel no puede tocar el sistema». Se comprobaba intentando leer /etc/hostname, que no está entre las rutas que la política permite tocar: el rechazo de la política se confundía con un ayudante caído, y salía una avería inexistente mientras el resto de esa misma pantalla usaba el ayudante sin problema. Ahora se le manda una orden de verdad, y el texto explica qué es nas-helper en lugar de nombrarlo a secas.
  • El sondeo de cámaras sale ahora por todas las redes del NAS. Un multicast se va por una única interfaz —la que elija el kernel—, y un NAS con Docker tiene al menos dos (la de casa y la de los contenedores, 172.17.0.1). Si el sondeo se iba por esa, no llegaba a ninguna cámara y el panel decía «ninguna ha contestado», que parece un problema de la cámara y era del NAS.

1.9.02026-07-26

La actualización cuenta por dónde va, y por qué falla

  • Mientras actualiza se ve el progreso. Hasta ahora la pantalla se quedaba muda justo en los dos pasos largos —instalar paquetes y compilar la interfaz, varios minutos cada uno— porque actualizar reinicia el propio panel y con él se muere la barra. Ahora se lee el registro que el actualizador va escribiendo: sale la fase en la que está y las últimas líneas, en directo.
  • El reinicio del panel deja de parecer un cuelgue. Durante los segundos en que el backend no responde, la tarjeta dice que se está reiniciando y que es parte del proceso, en vez de un error rojo. Cuando vuelve, sigue contando por dónde iba.
  • Al fallar se ve el motivo. El actualizador ya guardaba las últimas cuarenta líneas de su registro, pero el panel las descartaba por no declararlas en su modelo: el botón «Ver el detalle» no llegaba a aparecer nunca y solo quedaba la fase. Ahora está el registro entero de la parte que reventó, además de la versión desde la que se venía.

1.8.32026-07-26

El foco ya no se lo come el borde

  • En los diálogos —se veía clarísimo en el campo Memoria de «Nueva máquina virtual»— el anillo azul del campo enfocado salía cortado por el borde de la ventana. El anillo se dibujaba POR FUERA del campo, y el cuerpo del diálogo tiene scroll: todo contenedor con scroll recorta lo que sobresale. Ahora el anillo va por dentro, así que no hay nada que recortar y se ve igual esté donde esté.
  • Los diálogos dan además cuatro píxeles de holgura a cada lado sin cambiar de ancho, para que tampoco se corte lo que se dibuje pegado al borde (el realce de una fila al pasar el ratón, por ejemplo).

1.8.22026-07-26

La comprobación se creía el «apagado» del panel

  • Decía «Consola remota (SSH): apagado» mientras había una sesión SSH abierta en ese mismo momento. El fallo era mío y del tipo que este módulo existe para cazar: para un servicio que el panel cree apagado, me limitaba a repetir lo que decía el archivo de estado, sin mirar el sistema. Dar por bueno el «apagado» guardado es el mismo error que dar por bueno el «encendido».
  • Ahora, cuando el panel cree que un servicio está apagado, se comprueba igual: si la unidad está en marcha o el puerto escucha, sale en ámbar diciendo exactamente eso —«el panel lo tiene apagado, pero ssh está en marcha y escuchando en el puerto 22»— con las dos salidas: encenderlo en el panel para gestionarlo desde aquí, o pararlo de verdad. Vale para SMB, NFS, WebDAV y SSH.
  • La pantalla dice a qué hora se comprobó. No se refresca sola —una comprobación cuesta varios sondeos— y un informe de hace media hora no cuenta nada de lo que pasa ahora.

1.8.12026-07-26

Fuera lo que fingía funcionar

  • «Almacenamiento S3 — no disponible». Era mentira desde que existen las copias a la nube: S3, Wasabi, MinIO y Backblaze B2 funcionan de verdad, por rclone. El tipo de destino «s3» desaparece —con sus cinco campos muertos, que se guardaban sin significar nada— y esas nubes se conectan donde corresponde: en Nube.
  • «No hay actualizaciones pendientes». El asesor de seguridad leía un dato que ya no escribe nadie desde que el panel dejó de actualizar Debian: siempre decía que estaba todo al día sin haberlo mirado. Ahora informa de lo que este equipo sí sabe: si hay una versión nueva de LGM-OS, contada con git, y si no puede saberlo, lo dice.
  • «El cortafuegos está activado». Se afirmaba leyendo la intención guardada en un archivo, no las reglas cargadas en el sistema. Son dos cosas distintas y pueden no coincidir. Ahora se comprueba nftables de verdad, y si el panel lo tiene activado pero las reglas no están cargadas, sale en rojo: no está filtrando nada.
  • Cifrado de la copia. El trabajo tenía una casilla encrypted que solo sabía dar error. Fuera el campo; el aviso honesto de que las copias no van cifradas se queda, que eso sí es información útil.
  • «Volver a conectar la cuenta» de una nube: el texto y el endpoint existían, pero no había ningún botón. Ahora está y funciona — hace falta el día que la nube revoque el permiso y la copia empiece a fallar cada noche.

1.8.02026-07-26

Comprobación de todos los servicios

  • Samba en marcha con un `smb.conf` que no tiene tu carpeta, porque el guardado falló y solo dejó un aviso. El panel la enseñaba y Windows no la veía.
  • AppArmor confinando a Samba sin la lista de rutas compartidas: el servicio arranca pero no puede leer nada de la pool, y el error que llega es un «acceso denegado» que no apunta a ningún sitio. (Si AppArmor no confina a Samba, los avisos de apparmor que salen en el registro son ruido, y ahora lo dice.)
  • Una exportación NFS cuya carpeta ya no existe: tumba exportfs entero y se cae también lo que sí funcionaba.
  • WebDAV activado con Apache parado, o sin nadie con acceso web concedido.
  • Docker con su almacén en el disco del sistema, llenándolo hasta que la actualización deja de caber.
  • Poco espacio en el disco del sistema, un volumen sin montar, o una cámara cuya unidad reintenta cada diez segundos sin llegar a grabar.

1.7.22026-07-26

Las subidas, arriba y a la vista

  • Subir era una sola petición con todos los archivos dentro: el panel se quedaba mudo hasta el final y con doscientas fotos eso son minutos sin saber si va o se ha colgado. Ahora cada archivo es su propia subida, con su barra, y la cuenta va bajando: «Subiendo 37 archivos · 62 %».
  • La cola vive en la barra superior, no dentro de la ventana de Archivos: se puede cerrar esa ventana, irse a otra app y la subida sigue. Se despliega para ver cuál va y cuál ha fallado.
  • Si un archivo falla, los demás siguen. Antes se caía la tanda entera.

Una carpeta compartida, tres formas de servirla

  • WebDAV se marca en la carpeta, como SMB y NFS, y se publica en https://<nas>:<puerto>/<nombre>. Antes tenía su propia ruta escrita a mano en su pantalla, así que WebDAV podía estar sirviendo algo distinto de lo que decían los otros dos servicios sobre la misma máquina.
  • NFS y WebDAV dejan de pedir rutas. Sus pantallas ya solo tienen el interruptor (y el puerto, en WebDAV) y la lista de lo que publican, que sale de Carpetas compartidas. Todo queda dentro del volumen por construcción: no hay dónde escribir una ruta suelta.
  • Las exportaciones NFS sueltas de versiones anteriores se siguen viendo, marcadas como tales, para poder quitarlas. No se pueden añadir más.

Diseño

  • Las etiquetas de permisos («Consola», «Panel · admin», «Carpetas») se leían como pegatinas luminosas en modo oscuro: eran pastel pensado solo para el tema claro. Ahora el color lo pone el texto sobre un velo del mismo tono, y se leen igual en los dos temas.

1.7.12026-07-26

Cámaras: ONVIF y rutas por marca

  • Buscar cámaras (ONVIF). El NAS sondea la red local y lista las cámaras que contesten, con su nombre y su modelo. Se elige una, se escriben el usuario y la contraseña de la cámara, y es ella la que dice su dirección de vídeo: se pide su lista de perfiles y se coge el de más resolución, que es el que se graba.
  • Rutas conocidas por marca, para las que no traen ONVIF o lo traen apagado de fábrica: Hikvision/HiLook, Dahua/Amcrest/Imou, Reolink, TP-Link Tapo/VIGI, Foscam, Axis y EZVIZ. Se elige la marca, se pone la IP y sale la dirección para probarla.
  • La contraseña de la cámara no viaja en claro hacia ella: ONVIF autentica con un resumen SHA1 sobre un número de un solo uso, que es lo que esperan estas cámaras.
  • Sin dependencias nuevas: el sondeo y las consultas van con la biblioteca estándar.

1.7.02026-07-26

Cuotas por usuario

  • En Panel de control → Usuario y grupo → Cuotas: cuánto ocupa cada persona en cada volumen, con su barra, y el tope que se le quiera poner.
  • Lo aplica el sistema de archivos, no el panel. Una cuota que comprobara la web se la saltaría cualquiera copiando por SMB; con userquota de ZFS la hace cumplir el kernel y la escritura falla venga por donde venga.
  • Solo en volúmenes ZFS, y se dice por qué en los demás. Btrfs no sabe limitar por persona —sus qgroups miden subvolúmenes—, así que ahí el panel explica la alternativa en vez de enseñar un número que nadie haría cumplir.
  • Quien tenía cuenta y ya no la tiene sigue apareciendo con su número: sus archivos ocupan y hay que poder verlo.

1.6.12026-07-25

La sesión ya no vive donde cualquier XSS podría cogerla

  • La sesión pasa a una cookie `HttpOnly`, que el JavaScript de la página no puede leer ni por error. Antes estaba en localStorage: si algún día entrara un XSS en el panel, se llevaba la sesión entera y seguía sirviendo al día siguiente.
  • El precio de una cookie es el CSRF —el navegador la manda sola, también cuando la petición la dispara otra web—, y contra eso hay dos barreras: SameSite=Strict y una cabecera propia obligatoria en todo lo que modifica algo, que una web ajena no puede añadir sin un preflight de CORS que este NAS no le aprueba.
  • La cabecera Authorization: Bearer se sigue aceptando para scripts contra la API: ningún navegador la manda solo, así que no reabre la puerta que se acaba de cerrar.
  • La sesión caduca por inactividad (media hora), no a las doce horas. Trabajando se renueva sola en cada respuesta; un panel abierto y olvidado se cierra.

La contraseña de un enlace compartido deja de viajar en la dirección

  • Antes iba en la URL (?password=…), y ahí acaba en el registro del servidor, en el historial del navegador y en cualquier proxy de en medio. Ahora quien abre el enlace ve una pantalla que la pide y la manda por POST.
  • Esa pantalla no enseña ni el nombre del archivo antes de acertar.
  • Cinco intentos por enlace y a esperar diez minutos. El bloqueo automático solo cubría los inicios de sesión, así que la contraseña de un enlace se podía probar sin límite.

1.6.02026-07-25

App nueva: Cámaras

  • Se añade una cámara con su dirección RTSP —la del manual o la de su app— y se prueba antes de guardarla: la prueba dice si responde, con qué códec y a qué resolución, y avisa si el vídeo va en H.265 (se graba igual, pero el navegador no sabe pintarlo).
  • Graba systemd, no el panel. Cada cámara tiene su unidad, con reinicio automático: si la cámara se cae, si se va la red o si el panel se reinicia para actualizarse, la grabación vuelve sola. Una grabadora que deja de grabar en silencio no sirve de nada.
  • Se graba copiando el vídeo, sin recodificar, en trozos de diez minutos. El coste de procesador es prácticamente cero, un corte de luz se lleva como mucho el último trozo y encontrar una hora concreta es abrir un archivo, no rebobinar.
  • Directo sin reproductor ni retardo: la imagen llega en MJPEG dentro de una etiqueta <img>. La rejilla enseña una miniatura de cada cámara que se refresca sola, y al abrir una se ve en vivo; el ffmpeg del servidor solo existe mientras alguien mira.
  • Se limpia sola: cada cámara guarda N días o N GB, lo que antes llegue, y una vez por hora se borra lo que sobra empezando por lo más viejo. Es lo que impide que la vigilancia se coma el volumen entero en unas semanas.
  • Todo en la pool, en <pool>/camaras/<cámara>, como el resto de lo que ocupa.
  • La contraseña de la cámara viaja dentro de su dirección RTSP, así que no sale del NAS: el panel siempre la enseña tapada y la lista de cámaras queda fuera de la copia de la configuración, que es un archivo que se descarga.

Ventanas emergentes arrastrables

  • Los registros, el editor, la papelera y todo lo que abre un diálogo se mueven arrastrando su barra de título y se quedan donde se suelten; doble clic los centra. Un diálogo clavado en el centro tapa justo lo que se está mirando.

1.5.12026-07-25

El panel se apagaba solo un segundo después de arrancar

  • nas-backend.service declaraba Requires=docker.socket, y systemd propaga la parada: cualquier systemctl stop docker —incluido el que hace el propio panel para mudar el almacén de Docker a la pool— se llevaba por delante al backend. El resultado era un bucle: arrancaba, empezaba la mudanza, se mataba a sí mismo, y a la siguiente vuelta otra vez. Ahora es Wants: el panel sigue en pie con Docker parado, que además es la única forma de poder arreglar un Docker caído desde la web.
  • La mudanza del almacén de Docker se rinde al tercer intento seguido fallido y lo dice, en vez de reintentarla en cada arranque para siempre.

El actualizador ya no depende de tu sesión SSH

  • lgm update lanza la actualización por systemd (lgm-update.service) y se queda enseñando el registro. Cerrar la consola, un corte de red o un Ctrl+C ya no pueden matar el proceso justo entre «parar el panel» y «poner la versión nueva», que era la forma de quedarse sin web y sin nadie que la levantara.
  • Si el NAS quedó en una rama que el repositorio no tiene —el caso real: master contra un repositorio que publica main—, tanto lgm update como el botón del panel se pasan solos a la rama principal en vez de morir con un «revisión desconocida» de git.

1.5.02026-07-25

Las copias ya salen a la nube: Drive, OneDrive, Dropbox, S3…

  • Nubes que se pueden conectar: Google Drive, OneDrive, Dropbox, Box, pCloud, Nextcloud/ownCloud (o cualquier WebDAV), Amazon S3, Wasabi, MinIO, Backblaze B2 y MEGA.
  • Conectar la cuenta no obliga a instalar nada en tu ordenador. El permiso que devuelve Google (o quien sea) va a una dirección del NAS, así que al aceptar desde otro equipo el navegador acaba en una página que no carga. En vez de pedirte que instales rclone también ahí —que es lo que hay que hacer normalmente—, el panel te pide que copies esa dirección: con ella, el NAS recoge el permiso él mismo. Dos pasos y ya está.
  • Las credenciales no se guardan en el panel. Viven en /etc/nas/rclone.conf, que es de root, y no vuelven a salir de ahí por ninguna vía: ni la API las devuelve ni aparecen en la configuración exportada.
  • Cómo funcionan las versiones en una nube, dicho claramente porque no es igual que en un disco: allí no hay enlaces duros, así que «actual» es un espejo de tus carpetas —se ve y se restaura tal cual— y lo que cada copia reemplaza o borra se aparta en una carpeta con la fecha de la copia anterior. No se pierde nada, pero una versión antigua solo tiene los archivos que cambiaron después de aquel día.
  • El destino se prueba antes de guardarlo: si la cuenta no responde o no se puede escribir en la carpeta, no se guarda. Un destino que falla la primera noche, cuando ya nadie mira, es peor que no tenerlo.
  • iCloud Drive aparece en la lista, pero desactivado y con el motivo: Apple no publica una API, lo que existe es ingeniería inversa que pide el código del móvil cada pocos días. Para guardar un Mac, lo que sí funciona es al revés: una carpeta del NAS marcada como Time Machine.

1.4.02026-07-25

Almacenamiento y Copias de seguridad volvían a cerrarse al abrirlas

  • Las dos aplicaciones se quedaban en negro nada más cargar sus datos. La causa: la reposición del scroll se pedía después de los «si todavía no hay datos, enseña la ruedecita», así que el primer dibujado hacía una llamada menos que el siguiente y React aborta el componente entero cuando eso pasa. Se pide siempre, antes de nada.

Centro de Paquetes: cada pestaña empieza por el principio

  • Cambiar de pestaña, de categoría o buscar dejaba la lista nueva a la altura a la que se había quedado la anterior. Ahora vuelve arriba, como el resto del sistema.

Docker vive en la pool, y no se pregunta

  • Desaparece el botón «Mover a la pool»: el almacén de Docker se muda solo al crear el volumen y en cada arranque del panel. Donde estaba el botón queda solo el aviso de que la mudanza está en marcha —o de que falló y hay que mirar las notificaciones—.

SMB: por qué Windows no entra

  • La comprobación de SMB termina diciendo la dirección exacta que hay que escribir en el Explorador (\\192.168.1.x\Carpeta). El NAS no se anuncia en «Red», así que no aparece solo por mucho que todo esté bien, y teclear el nombre del equipo no vale.
  • Un usuario mal escrito ya no se convierte en invitado. Windows 10 y 11 rechazan las sesiones de invitado por política y el mensaje que enseñaban —«no puede obtener acceso a la carpeta compartida»— parecía un fallo del NAS; ahora dice lo que pasa de verdad: contraseña o usuario incorrectos. El modo invitado solo se activa si de verdad hay alguna carpeta abierta a invitados.

Barras de desplazamiento a juego con las ventanas

  • La del marco de ventana era la de fábrica del navegador —gris, ancha, con sus flechas— y dentro de una ventana translúcida cantaba muchísimo. Ahora son finas y redondeadas, sin fondo, separadas del contenido, y se marcan un poco al pasar el ratón. En modo oscuro cambian solas.

Archivos: la ruta arriba y los botones debajo

  • La ruta ocupa su propia línea, entera. Antes iba espachurrada entre los botones y el buscador, con su propia barra de scroll horizontal en cuanto entrabas en la segunda carpeta: no se leía nada. Si la ruta es muy larga se pliega por el medio —«Carpetas compartidas › … › carpeta › actual»—, que es la parte que menos importa, y cada trozo sigue llevando a su carpeta.
  • Los botones, en una fila propia debajo, siempre los mismos y en el mismo sitio: los que necesitan algo seleccionado se desactivan en vez de aparecer y desaparecer, que movía toda la fila cada vez que pulsabas en un archivo. Se añade Comprimir ahí mismo.
  • El buscador cierra esa fila, alineado a la derecha, con la papelera al lado.

1.3.42026-07-25

  • Los dos pasos largos del actualizador —instalar paquetes y compilar la interfaz— avisan de que tardan varios minutos y de cómo seguirlos con tail -f. Mandan su salida al registro para no llenar la pantalla, y sin decirlo parecían colgados.

1.3.32026-07-25

La actualización moría en la primera fase de una instalación recién hecha

  • ls -1t /var/nas/pre-update-*.tar.gz no encuentra nada en un equipo recién instalado, sale con código 2 y, con set -euo pipefail, se llevaba por delante toda la actualización antes siquiera de mirar el disco. Lo introduje al añadir la limpieza previa y solo se dispara donde no hay copias anteriores: precisamente en el primer intento tras instalar. Reproducido con el actualizador de verdad sobre un /opt/nas simulado, y arreglado.
  • La misma clase de trampa en el cálculo del espacio libre: si df no contesta, la aritmética recibía una cadena vacía y eso, con set -e, también abortaba.

1.3.22026-07-25

Actualizar desde la consola, en un comando

  • `sudo lgm update`: descarga los cambios y lanza el actualizador. Si el repositorio es privado, reutiliza el token que ya guardaste en el panel y se lo pasa a git por una variable de entorno con un credential helper — nunca dentro de la URL, donde acabaría escrito en .git/config y en cualquier mensaje de error. Sin token guardado lo intenta igual, que es lo que necesita un repositorio público.
  • Dice claramente qué falta cuando no puede: sin checkout de git, sin repositorio de origen o sin permisos.

1.3.12026-07-25

Sin volumen no se instala nada, y se dice

Se retiran las acciones de GitHub

  • El repositorio es privado y sus minutos se cobran, así que .github/workflows/ sale del proyecto. En su lugar queda `verificar.sh`, que hace lo mismo en tu equipo: ruff, pytest, tipos, build y sintaxis de los scripts de despliegue, con un resumen al final. Conviene pasarlo antes de construir la ISO; si tienes WSL, ejecútalo dentro, porque hay comprobaciones que solo fallan en Linux.

1.3.02026-07-25

Todo lo gordo vive en la pool

  • Al crear la pool se prepara sola: aparecen docker/ (aplicaciones), vm/ (discos de las máquinas virtuales) y appstore/ (el catálogo), con los permisos que necesita el panel para desplegar sin privilegios.
  • El almacén de Docker se muda a la pool en ese momento, como tarea con progreso y aviso al terminar. La copia conserva enlaces duros (overlay2 los usa a mansalva: sin eso puede triplicar lo que ocupa) y no empieza si no cabe. Si algo falla, daemon.json vuelve a como estaba y Docker arranca igual: es preferible seguir gastando disco del sistema a quedarse sin contenedores. Cuando sale bien, se libera el disco del sistema.
  • El catálogo de aplicaciones también se va a la pool, dejando un enlace en su sitio.
  • Para un NAS al que le pusieron la pool después, la app Docker avisa de que sus datos siguen en el disco del sistema, dice cuánto ocupan y los mueve con un botón.
  • Permiso de borrado del almacén viejo: esa ruta exacta y ninguna otra; ni /var/lib, ni subcarpetas suyas.

1.2.72026-07-25

El CI vuelve a estar verde (llevaba semanas rojo)

  • Los tests dependían del helper privilegiado. Fuera de root, run() manda todo por el socket de nas-helper, que en una máquina de pruebas no existe: dieciséis pruebas morían con «no se puede conectar con el helper». No se veía porque en Windows os.geteuid no existe (se asume root) y quien lo ejecutaba en Linux solía ser root. El CI corre como usuario normal. Ahora los comandos se lanzan en el propio proceso; el cruce del límite de privilegio tiene sus propias pruebas.
  • Un test de la VPN leía el `/proc/sys/net/ipv4/ip_forward` real de la máquina donde corría. En Windows ese archivo no existe y el test pasaba; en Linux existe y vale 0 mientras nadie active el reenvío, así que fallaba siempre — y con él la publicación de la ISO, que solo sale de código verificado. Ahora el test controla ese valor, como ya hacía con todo lo demás, y hay uno nuevo para el caso contrario: si el reenvío se queda apagado, la VPN no arranca y el panel dice qué ejecutar.

1.2.52026-07-25

La interfaz se compila donde hay sitio

  • npm ci se traía sus ~400 MiB de dependencias dentro de `/opt/nas/src`, es decir, en el disco del sistema: en un equipo con el sistema en un disco pequeño (lo normal cuando los datos van a un pool aparte) la actualización no cabía por mucho que se limpiara. Ahora se compila en el sitio con espacio de verdad —un volumen del NAS, /var/tmp, y como último recurso /opt/nas—, solo vuelve lo compilado (un par de MiB) y la zona de trabajo se borra entera, también si algo falla.
  • Con eso, sobre el disco del sistema hacen falta 400 MiB en vez de 1 GiB.
  • La limpieza previa borra además el node_modules de un intento anterior, que son otros 400 MiB que npm vuelve a crear él solo.
  • El README dice ahora lo que hace falta de verdad: 4 GB de RAM y 32 GB de disco de sistema (50 GB para ir sobrado).

1.2.42026-07-25

El actualizador ya no se atasca por falta de espacio

  • Limpia lo suyo antes de medir el disco. Cada intento dejaba una copia del backend, otra del frontend, otra del entorno de Python y un tar del estado, y nadie los retiraba: bastaban unos cuantos intentos fallidos para que el siguiente muriera con «espacio insuficiente», sin que el motivo real —los restos de los anteriores— apareciera por ningún lado. Ahora se borran primero (son exactamente los que el propio script borra más adelante), se conservan las tres últimas copias del estado y se vacía el caché de apt.
  • Si aun así falta espacio, dice qué lo está ocupando y qué suele sobrar.

La consola del servidor nunca desaparece

  • El modo quiosco pasa al terminal 7: el inicio de sesión de siempre se queda en el 1. Si la pantalla se rompe, Ctrl+Alt+F1 da una consola de verdad y Ctrl+Alt+F7 vuelve al panel. Antes ocupaba el terminal 1 y, si X no arrancaba, el equipo se quedaba sin ninguna forma de entrar desde su propio teclado.
  • `lgm kiosk` desde la consola: on, off, restart y log. Y la pantalla de bienvenida recuerda las dos combinaciones de teclas.

1.2.32026-07-25

La pantalla del servidor se enciende al actualizar

  • El modo quiosco arranca en el momento, no en el siguiente reinicio: al terminar la actualización —y ya con el panel respondiendo— la pantalla del servidor muestra la web. Si el equipo no lo tenía y tiene pantalla (también la consola de una máquina virtual), se instala y se enciende ahí mismo. Si lo apagaste desde el panel, sigue apagado.
  • Los drivers que faltaban. Se instalaba xserver-xorg sin recomendados, y así no viene ningún driver: X arrancaba y moría con «no screens found», o pintaba la pantalla sin responder al teclado. Ahora se declaran los de vídeo (fbdev, vesa y qxl, que es la pantalla emulada de QEMU/Proxmox/VirtualBox) y el de entrada (libinput).
  • Si X no puede arrancar, la unidad se rinde tras cinco intentos en vez de reintentar para siempre llenando el diario, y deja el motivo escrito.

1.2.22026-07-25

«Actualizo y me sigue diciendo 1.1.1»

  • El panel dice ahora cómo fue la última actualización. El actualizador reinicia el propio proceso que lo lanzó, así que deja el desenlace escrito en el estado… y nadie lo leía. Una actualización que fallaba y revertía dejaba la pantalla idéntica —misma versión y el mismo «hay N actualizaciones»—, indistinguible de una que ni se había intentado. Ahora sale en verde de qué versión a cuál se pasó, o en rojo en qué fase falló, con el motivo y el final del registro desplegable.

1.2.12026-07-25

Panel de control: cada cosa en su categoría

  • Los usuarios salen de los servicios de archivos. SMB y WebDAV tenían su propia lista de usuarios, así que la misma persona acababa con cuentas distintas según por dónde se la creara. Ahora cada servicio solo dice quién tiene acceso y enlaza a Usuario y grupo.
  • «Usuario y grupo» es una sola entrada, con pestañas de Usuarios, Grupos y Conectados, y tiene categoría propia junto a la cuenta y el 2FA: las personas y sus permisos no son «una cosa de SMB».
  • SNMP pasa a Conectividad, que es lo que es, y Seguridad se queda con lo que protege el equipo (asesor, cortafuegos y bloqueo de IPs).

Terminal

  • Botón de reconectar cuando se cae la conexión, sin cerrar y volver a abrir la app.
  • 10.000 líneas de historial (antes 1.000): un journalctl se iba muy por encima y lo primero que se busca suele ser el principio.
  • En sesión root, el marco entero se tiñe de rojo: se ve sin leer nada dónde estás.

1.2.02026-07-25

Máquinas virtuales

  • App «VM»: crea, arranca, para y elimina máquinas virtuales sobre KVM/libvirt, con su pantalla dentro del propio panel. Se elige nombre, memoria, procesadores, disco y de qué imagen .iso instalar —las que haya en tus carpetas compartidas—; el resto lo pone LGM-OS. Los discos van al volumen, en <pool>/vm, y crecen según se usan.
  • La pantalla de cada máquina (VNC) escucha solo dentro del equipo: se ve por un puente del panel que exige sesión de administrador, nunca abierta en la red.
  • Si el equipo no tiene libvirt o no ofrece virtualización por hardware, la app lo dice y explica qué falta en vez de fallar sin más.

Se arregla

  • El scroll heredado al cambiar de sección. Cada aplicación vive dentro del marco de ventana, que tiene su propio contenedor con scroll: reponer el del panel no servía de nada porque el que guardaba la posición era el marco. Ahora se reponen todos, y vale igual en el Panel de control, en Sistema, en Almacenamiento y en Copias de seguridad.
  • El modo quiosco no se instalaba en máquinas virtuales. Se miraba solo si había un conector de pantalla en DRM, y una tarjeta emulada (QEMU, Proxmox, VirtualBox, Hyper-V) no siempre publica uno; ahora vale también la tarjeta de vídeo del bus PCI o un framebuffer, que es lo que tienen esos equipos. Y si un equipo no lo tiene instalado, el panel lo dice en Sistema en vez de no enseñar nada.

1.1.42026-07-25

Se arregla lo del catálogo con 177 aplicaciones

  • Una fuente que falla ya no borra lo que trajo antes. Al sincronizar se retiraban las recetas de todas las fuentes salvo las que hubieran contestado ESA vez, así que bastaba un fallo de Community Applications (un 403 de su CDN, un corte de red) para que sus ~3200 aplicaciones desaparecieran de golpe y la tienda se quedara con las de la otra fuente, sin explicación. Ahora solo se retira lo de las fuentes que han respondido.
  • La hoja de fuentes dice qué trajo cada una y cuál falló, con la fecha de la última sincronización y cuántas recetas hay en el equipo ahora mismo.

Panel de control ordenado

  • Portada con las secciones agrupadas en rejilla (Uso compartido de archivos, Conectividad, Seguridad, Sistema), con buscador y vuelta atrás, en vez de una lista lateral de veinte entradas siempre a la vista.

«Windows no puede obtener acceso a \NAS»

  • Botón Comprobar en SMB: recorre en orden servicio encendido, Samba en marcha, puerto 445 escuchando, regla del cortafuegos, carpetas publicadas y usuarios dados de alta, y dice cuál falla y qué hacer. Si todo está bien, explica cómo distinguir si el problema es del PC. Avisa además de las carpetas abiertas a invitados: Windows 10 y 11 no se conectan como invitado y dan exactamente ese mismo error.

1.1.32026-07-25

El servidor deja de ser una caja con una consola negra, el gestor de archivos hace lo que se espera de uno, y hay repaso de seguridad.

El servidor, en su propia pantalla

  • Modo quiosco. Si el equipo tiene un monitor conectado, arranca enseñando el panel a pantalla completa, como cualquier sistema operativo. Se sigue entrando igual desde el resto de la red. Solo se instala cuando hay pantalla —en un servidor sin ella, X y un navegador serían cientos de megas para nada— y se apaga desde Sistema → Información.

Gestor de archivos

  • Comprimir y extraer (ZIP, TAR, tar.gz, tar.bz2, tar.xz) con su progreso como tarea. Extraer crea una carpeta con el nombre del comprimido, en vez de vaciar cien archivos sueltos encima de la carpeta actual.
  • Compartir con enlace: una URL pública para un archivo o una carpeta —que se descarga comprimida al vuelo—, con caducidad y contraseña opcional. La lista de enlaces activos está en el mismo sitio, con su contador de descargas y su botón de revocar.
  • Copiar y mover en bloque en una sola operación: los nombres repetidos pasan a «x (2)» en vez de fallar a medias.
  • Búsqueda de verdad: la lupa (o Intro) recorre todas las subcarpetas en el servidor, sin distinguir mayúsculas ni tildes, y cada resultado enseña en qué carpeta está y lleva hasta ella. Antes solo se filtraba lo que ya estaba en pantalla.
  • Vista previa de fotos, vídeo, audio, PDF y texto sin descargar nada. El archivo se sirve con un sandbox, porque lo ha subido alguien y podría llevar scripts dentro.
  • Copiar a… y Mover a… con selector de carpeta: no hay que escribir ninguna ruta.
  • Menú contextual completo, al estilo File Station.

Seguridad

  • El identificador de una aplicación se valida antes de construir una ruta con él. Llega en la URL y un «..» suelto apuntaba fuera del catálogo.
  • Una cuenta con la contraseña sin estrenar ya no abre el terminal. El resto de la API lo impedía, pero el WebSocket no pasa por esa comprobación, y el terminal es justo lo más peligroso del panel.
  • La descripción de una carpeta compartida no puede llevar saltos de línea: acaba dentro de smb.conf y ahí una línea nueva son directivas nuevas.
  • Al extraer un comprimido se descartan las entradas que apuntan fuera de la carpeta («zip slip») y los enlaces simbólicos, que convierten la siguiente escritura en una escritura en cualquier sitio.

Se retira

  • Las actualizaciones del sistema operativo por apt. Un apt upgrade desatendido puede reiniciar servicios o dejarlos a medias sin nadie delante; quien quiera hacerlo tiene la consola. apt sale también de la lista de programas que el proceso privilegiado puede ejecutar. Lo que sí se sigue actualizando desde el panel es LGM-OS.

1.1.22026-07-25

Los servicios de archivos dejan de ser tres paneles sueltos y los permisos se pueden leer también por persona, que es como se preguntan de verdad.

  • Un solo sitio para SMB, NFS y WebDAV, con pestañas. Antes estaban repartidos por el menú lateral como si fueran cosas distintas, y compartir una carpeta obligaba a recorrer los tres.
  • Los distintivos SMB y NFS de cada carpeta ahora se pulsan. Encender o apagar un protocolo en una carpeta es lo que más se hace y obligaba a abrir el editor entero.
  • Permisos por persona. En el usuario hay una pestaña «Carpetas» con todas las carpetas y tres columnas: sin acceso, lectura y escritura, o solo lectura. Enseña además el permiso que le llega por sus grupos, que es lo que explica los accesos que uno no recuerda haber dado.
  • Una persona, una cuenta. Entrar al panel es ahora una capacidad más de la misma ficha, con la misma contraseña, junto a las carpetas, el acceso web y la consola. Había dos sitios donde crear personas y la misma acababa con dos cuentas y dos contraseñas que se desparejaban; en Seguridad solo queda la lista de quién entra. El panel no se queda sin administrador: ni quitándoselo uno mismo, ni degradando al último que queda.
  • Al cambiar de pestaña dentro de un panel, la vista vuelve arriba en vez de quedarse donde estaba la anterior.

Se arregla

  • Los registros de «Panel» y «Servicio privilegiado» daban error 500 y los de smbd o ssh no. La respuesta del servicio privilegiado viaja en una sola línea y el lector traía 64 KiB de tope, que las unidades más habladoras pasan de sobra. El mismo límite rompía cualquier fichero grande leído o escrito por esa vía.
  • Una línea del diario que no es UTF-8 válido ya no tumba la lista entera de registros.
  • Los registros se eligen por su nombre de siempre («Carpetas compartidas» y no «smbd»), con lo interno del panel aparte y al final.

1.1.12026-07-25

Tanda centrada en que el panel no mienta y en quitarle opciones de encima al usuario.

Se arreglan cosas que decían lo contrario de lo que pasaba

  • «LGM-OS está al día» sin haber mirado nada. El checkout de la ISO se quedaba sin repositorio de origen y, sin remoto, el contador de commits pendientes daba 0. Ahora se distingue «no se puede comprobar» de «al día», y el origen se configura desde el panel.
  • WebDAV seguía «Activo» con el interruptor apagado. Su estado salía de si Apache estaba vivo, y Apache también es el proxy inverso y quien renueva los certificados.
  • «UPS activado pero detenido» en cualquier equipo. Debian deja nut-monitor habilitado al instalar el paquete; sin un SAI configurado no puede arrancar.
  • SMB corriendo con el puerto cerrado. Los servicios de archivos no daban de alta sus reglas de cortafuegos: Windows respondía «no puede obtener acceso a \\NAS» sin más pista.
  • La sesión root del terminal moría al abrirse. Usaba sudo desde un servicio con NoNewPrivileges=yes, que lo impide por diseño; ahora la crea el helper privilegiado.
  • Los diálogos y el menú contextual salían fuera de sitio. Las ventanas se posicionan con transform, que convierte al marco en el origen de los hijos fixed.

Menos opciones, mejor puestas

  • Compartir una carpeta pide el nombre; la ruta la calcula el sistema.
  • Los usuarios se gestionan en un único sitio y valen para todos los servicios.
  • Permisos por persona en cada carpeta: sin acceso, lectura, o lectura y escritura.
  • El panel de actualizaciones enseña la versión y poco más; el resto queda plegado.
  • Cortafuegos encendido de fábrica, con las reglas de los servicios siempre al día.

Novedades

  • Tienda de aplicaciones estilo App Store, con catálogos comunitarios (Unraid, CasaOS), buscador por relevancia, categorías y carga por páginas.
  • Iconos propios de las aplicaciones nativas.
  • Test SMART y scrub con progreso y aviso del resultado.
  • La interfaz se recarga sola cuando el sistema se actualiza.

1.1.02026-07-25

Deja de ser un NAS que sólo se usa desde casa: se publica hacia fuera, guarda copias de verdad y da servicios de red a la propia LAN. Antes, además, se arreglan cuatro cosas que rompían la instalación desde ISO.

Críticos de despliegue

  • La ISO produce equipos actualizables. Empaquetaba una copia del árbol de trabajo sin `.git`, así que install.sh nunca creaba /opt/nas/src y la actualización desde el panel estaba muerta en toda instalación hecha desde ISO. Ahora viaja un clon de git real con su origin reescrito a la URL del repositorio; de paso, node_modules y dist dejan de colarse en la imagen porque sólo viaja lo confirmado.
  • El primer arranque se puede reintentar: el progreso se marca en /var/nas/.install-done. Si el instalador moría a mitad, el equipo se quedaba bloqueado para siempre en «instalando» sin forma de recuperarlo.
  • Actualización atómica con reversión automática: se compila y valida en un área aparte, se promociona con mv conservando la versión anterior y, si el panel no responde tras reiniciar, se vuelve solo a la anterior. El desenlace real queda en lgm_update_result.json, de modo que la UI ya no puede cantar un éxito que no ocurrió.
  • La copia de configuración pasa de lista blanca a lista negra: se llevaba sólo lo que alguien se acordó de apuntar, y por eso perdía las carpetas compartidas (y SNMP, portal de acceso, UPS y bloqueo de IPs). Un test falla si aparece estado nuevo sin clasificar, así que el olvido no se puede repetir.
  • Papelera `#recycle` dentro de cada carpeta compartida, como en Synology y visible también por Samba. La papelera anterior vivía en el disco de sistema: borrar un archivo grande de un pool lo copiaba entre discos y podía llenar el sistema. Se purga por antigüedad desde el planificador.
  • Scripts de despliegue unificados en deploy/common.sh y la netinst de Debian se verifica por SHA256 antes de remasterizarla (no se construye una ISO sobre una descarga corrupta o manipulada).

Acceso externo

  • DDNS para duckdns, no-ip y cloudflare. Resuelve la IP pública y sólo llama al proveedor si ha cambiado, porque repetir la llamada sin cambios cuenta como abuso y acaba en bloqueo. El token vive en un documento de estado aparte: así la configuración viaja en la copia de seguridad y el secreto no, y se redacta de cualquier mensaje de error.
  • Certificados Let's Encrypt con certbot y desafío webroot servido por un vhost propio. El material se copia al TLS del panel y se resincroniza a diario, de modo que una renovación automática llega también al panel en vez de dejarlo con el certificado viejo.
  • Proxy inverso con Apache: un vhost por dominio y reglas ordenadas de ruta más específica a menos, validando con apachectl configtest antes de recargar y restaurando la configuración anterior si falla. Los destinos se restringen a IP privada literal: un nombre lo resolvería Apache en cada petición, y eso convierte el proxy en abierto. El vhost de texto plano conserva el alias de ACME, sin el cual renovar el certificado de un dominio ya publicado sería imposible.
  • Guía docs/external-access.md con qué puertos abrir y por qué el 5000 no es uno de ellos.

Copias de seguridad de datos

  • Aplicación nueva de primer nivel. Copias incrementales con rsync y --link-dest contra la última versión correcta: cada versión se ve entera al abrirla pero sólo ocupa lo que cambió.
  • Cada origen se guarda con su ruta completa, así que dos carpetas que se llaman igual no se pisan y al restaurar se ve de dónde salió cada cosa.
  • Destinos en disco local o servidor remoto por SSH, con par de claves en /etc/nas/ssh que el proceso web no puede leer ni queriendo. La prueba de conexión distingue host inalcanzable, clave no autorizada, huella cambiada, carpeta inexistente y sin permiso de escritura, en vez de un «no se pudo conectar» que no dice qué arreglar.
  • Retención que sólo purga tras una copia correcta y que jamás borra la última que queda: un fallo repetido no puede dejarte sin ninguna copia.
  • Restaurar recrea la ruta original dentro de la carpeta que elijas, de modo que por defecto no machaca los datos vivos.
  • S3 y el cifrado en reposo se declaran no disponibles, con su motivo, en vez de fingir que funcionan: cifrar obligaría a empaquetar cada versión entera y eso rompería los incrementales y la restauración de archivos sueltos.
  • El límite de tiempo del helper sube a 6 h, porque una primera copia legítima dura horas y antes se cortaba sola.

Servicios de red

  • VPN WireGuard integrada. La clave del servidor se genera en /etc/wireguard con permisos 0600 y la API no la devuelve nunca (el parser de wg show dump la descarta explícitamente, con test que lo comprueba). La clave privada de cada cliente se enseña una sola vez, al crearlo, dentro de su configuración y de un QR generado en el propio NAS para que no pase por un servicio de terceros; del cliente sólo se guarda la pública.
  • Las direcciones se asignan a la primera libre y se reutilizan al borrar un cliente. Añadir o quitar clientes usa wg syncconf, que no corta los túneles abiertos.
  • El reenvío y el NAT se integran en el cortafuegos que ya existe en vez de montar un mecanismo paralelo que se pisara con él, y el puerto UDP se abre como regla del propio cortafuegos porque su cadena de entrada descarta por defecto.
  • DNS y DHCP con dnsmasq: registros locales nombre→IP, reenviadores, rango de concesión y reservas por MAC. La configuración se valida con dnsmasq --test antes de instalarla y se restaura el archivo anterior si el servicio no arranca, así que un ajuste mal puesto no deja la red sin DNS.
  • Activar el DHCP exige confirmación explícita y avisa si detecta que la dirección del propio NAS se la dio otro DHCP (dos servidores en la misma LAN es una avería difícil de diagnosticar). El instalador deja dnsmasq instalado pero apagado hasta que se active desde el panel.

Integración continua y publicación

  • El CI comprueba ahora también que todos los scripts de despliegue pasan `bash -n` (incluido deploy/lgm, que no tiene extensión), además de ruff, pytest, tsc y el build del frontend. La comprobación de tipos se hace fuera del modo build para que no pueda darse por buena apoyándose en el tsconfig.tsbuildinfo versionado.
  • Publicación automática de la ISO: al empujar una etiqueta vX.Y.Z se verifica todo el repositorio, se construye la imagen con deploy/build-iso.sh y se adjunta a la release de GitHub junto a su SHA256, con las notas sacadas de este mismo fichero. La imagen se construye desde la rama por defecto, no desde una rama efímera, porque el clon que viaja dentro hereda su seguimiento y una rama inexistente en el remoto dejaría al equipo instalado sin poder buscar actualizaciones.
  • 249 tests, ruff/tsc/build en verde.

1.0.0 «Estreno»2026-07-25

Primera versión pública, renumerada a 1.0 (la historia interna 2.x sigue abajo). Primera instalación real completada en una VM de Unraid desde la ISO propia.

  • Instalador con 4 elecciones: nombre del servidor, red (DHCP o IP estática), usuario de consola y disco del sistema (lista si hay varios). Tema oscuro, menú de arranque LGM-OS y cero preguntas sueltas de Debian.
  • Consola estilo XPenology: pantalla con logo, IP y URL que durante el primer arranque muestra una barra de progreso en vivo (paso N/13, %) y al terminar «✔ LGM-OS listo»; CLI lgm (info, URL con QR, IP estática, lgm progress).
  • Asistente de primer acceso web: sin admin/nas-admin de fábrica; la primera visita crea tu administrador (fuera del rate-limit, 409 si ya hay usuarios).
  • PWA: el panel se instala como aplicación (manifest + iconos + service worker conservador que jamás cachea /api).
  • Sistema muestra «LGM-OS x.y.z · Debian 12» y la versión se resuelve también en instalaciones desde ISO (/opt/nas-src) y en /opt/nas/VERSION.
  • Arreglos de campo de la VM real: docker compose v2 garantizado (binario oficial si apt no lo trae) y validación SSH que creaba /run/sshd antes de sshd -t.

2.9.02026-07-24

Arrastre de ventanas arreglado de raíz

  • Adiós react-rnd: el arrastre y el redimensionado de ventanas ahora son propios, con Pointer Events (ratón, táctil y lápiz). Ocho asas de redimensionado invisibles, límites dentro del escritorio y sin re-render por movimiento (fluido). Verificado de punta a punta. El bundle baja ~33 kB y StrictMode vuelve a estar activo.

Panel de widgets (estilo DSM)

  • Panel lateral derecho anclable con pila de widgets en vivo: Salud del sistema, Monitor de recursos (CPU/RAM + gráfica de red), Almacenamiento, Usuarios conectados, Tareas programadas y Registros recientes.
  • Menú «+» para activar/desactivar widgets, anclar (o cierre al hacer clic fuera), plegar, reordenar y persistencia de la configuración. Botón conmutador en la barra superior. Sustituye a los widgets flotantes.

Marca

  • Logo LGM-OS de verdad (SVG: torre NAS con bahías y LEDs sobre degradado): en el login (sin el texto «LGM-OS», solo la marca), en la barra superior y como favicon.
  • El botón «Cancelar» y demás botones secundarios recuperan su estilo (clase btn-secondary que faltaba en CSS, con variante para modo oscuro).

2.8.02026-07-24

Panel de Control estilo DSM (todo funcional)

  • Carpetas compartidas: una sola definición crea la carpeta en el volumen, aplica permisos por usuario y grupo (lectura/escritura, solo lectura, sin acceso) con ACLs de sistema (setfacl + herencia), la publica por SMB (valid users / write list) y la exporta por NFS — como en Synology.
  • Usuario y grupo: grupos reales del sistema gestionados desde el panel (crear, eliminar, miembros), usables en permisos (@grupo) y ACLs.
  • Usuarios conectados: sesiones web en vivo (con IP y «esta sesión»), conexiones SMB (smbstatus) y sesiones SSH/consola (who); pestaña en el panel + widget en el escritorio.
  • Asesor de seguridad: análisis con puntuación (contraseña de fábrica, 2FA de administradores, endurecimiento SSH, cortafuegos, actualizaciones pendientes, bloqueo automático) y consejos de corrección.
  • Bloqueo automático de IPs: tras N intentos fallidos en una ventana configurable la IP queda bloqueada (persistente, con lista blanca CIDR, desbloqueo desde la UI); se aplica en el login antes que nada.
  • Opciones regionales: zona horaria y NTP vía timedatectl, con estado de sincronización.
  • SNMP: agente v2c de solo lectura (comunidad, ubicación, contacto) con snmpd.conf gestionado.
  • Portal de acceso: título, subtítulo y mensaje personalizables que se muestran en la pantalla de login (endpoint público solo cosmético).
  • Dispositivos externos: discos USB/extraíbles con montar / desmontar / expulsar seguro (montados bajo /mnt/usb-*, visibles en Archivos).

Escritorio

  • Widgets de escritorio arrastrables con posición recordada: Sistema (anillos CPU/RAM + carga), Red (velocidades + gráfica), Almacenamiento (ocupación de pools) y Usuarios conectados.
  • Launchpad solo en el dock (como macOS) con icono degradado distintivo; barra superior más limpia.
  • Arreglo del arrastre de ventanas en desarrollo (React StrictMode dejaba obsoleta la referencia interna de react-draggable; en producción no ocurría).

Correcciones

  • Rutas POSIX de configuración generadas con as_posix() (en Windows str(Path) produce barras invertidas que la allowlist rechaza — /api/users devolvía 500 en desarrollo).
  • Variable de entorno del terminal corregida (LGM_OS, antes LMS_OS).
  • 118 rutas API, 72 tests en verde.

2.7.02026-07-24

Diseño coherente e intuitivo

  • Componentes compartidos mejorados: Section con icono y descripción, EmptyState consistente, y un SidebarNav reutilizable con navegación agrupada.
  • Menús reorganizados por grupos lógicos: el Panel de Control ahora agrupa en «Compartición de archivos», «Acceso remoto», «Seguridad» y «Energía»; Sistema agrupa en «Equipo», «Actualizaciones», «Automatización» y «Mantenimiento» — todo en posiciones con sentido para encontrar cada ajuste de un vistazo.

2.6.02026-07-24

Gestor de archivos (estilo Explorador de Windows)

  • Panel lateral de acceso rápido (Equipo, raíces de almacenamiento, Papelera).
  • Cortar / Copiar / Pegar, mover arrastrando archivos entre carpetas, subida por arrastrar y soltar, nuevo archivo/carpeta.
  • Selección múltiple (clic, Ctrl+clic, Mayús+rango, Ctrl+A) y atajos de teclado (F2 renombrar, Supr a papelera, Ctrl+C/X/V, Enter abrir, Retroceso subir).
  • Ordenar por nombre/tipo/tamaño/fecha, vista lista y cuadrícula, buscar en la carpeta, barra de direcciones editable, breadcrumb relativo a la raíz, barra de estado (nº elementos, selección, tamaño), diálogo de Propiedades y menú contextual completo (clic derecho).

Menús más avanzados

  • Monitor: tabla de procesos en vivo (top por CPU/memoria, con PID y usuario).
  • Menú contextual (clic derecho) en contenedores Docker (logs/abrir/iniciar/detener/ reiniciar/eliminar) y en pools de almacenamiento (scrub/copiar montaje/eliminar).
  • Componente reutilizable de menú contextual accesible.

2.5.02026-07-24

Diseño y accesibilidad

  • Modales accesibles: nuevo componente con role="dialog", aria-modal, foco atrapado y restaurado, y cierre con Escape/backdrop. La papelera y los diálogos lo usan.
  • Diálogos y toasts propios: se sustituyen todos los window.alert/confirm/prompt nativos por diálogos con la estética Liquid Glass y avisos tipo toast.
  • Respeto a `prefers-reduced-motion` (desactiva animaciones) y anillos de foco visibles solo por teclado (focus-visible). Dock con aria-label y desplazable.
  • Gestor de archivos: arrastrar y soltar para subir + menú contextual (clic derecho).

Funciones nuevas

  • Información del equipo (nueva pestaña en Sistema): CPU (modelo, núcleos, frecuencia), RAM/swap, OS, kernel, arquitectura, discos, versión de Docker/ZFS y carga.
  • Actualización de LGM-OS desde git (pestaña en Sistema): comprueba el repo remoto, muestra commits pendientes y aplica la actualización (git pull + recompilación/reinicio vía unidad systemd dedicada). Soporta repositorios privados con token de GitHub (guardado de forma segura, nunca en argv) o clave SSH de despliegue; git nunca se cuelga esperando credenciales.

Marca

  • Rebrand completo a LGM-OS (pestaña del navegador, login, barra superior).
  • 91 rutas API, 65 tests en verde.

2.4.02026-07-24

Instalador

  • Instalador LGM-OS rediseñado: logo en color, pasos numerados, marcadores ✔/⚠, trap de errores y pantalla final. Interfaz web rebrandeada a LGM-OS.

Correcciones funcionales (revisión adversarial de comandos Debian)

  • ROMPÍA: el firewall no se podía activar nunca (/etc/nftables.d faltaba en los prefijos de mkdir de la allowlist → PolicyViolation no capturada → 500).
  • ROMPÍA: crear un pool Btrfs con guion en el nombre generaba una unidad systemd .mount inválida (falta de escape) → los nombres de pool se restringen a [A-Za-z0-9_].
  • ROMPÍA: el instalador no compilaba el módulo de kernel de ZFS → zpool create fallaba. Ahora instala zfs-dkms + cabeceras y hace modprobe zfs.
  • Instalación resiliente del plugin docker compose (nombre de paquete variable en bookworm); WebDAV no duplica Listen en 80/443 (Apache no arrancaba); el driver NUT se reinicia al cambiar la config; userdel limpia la cuenta Unix al borrar usuario SMB; aviso en la UI del firewall de que SMB/NFS/WebDAV quedan bloqueados hasta abrir puertos.

2.3.02026-07-24

Funciones nuevas

  • Launchpad: menú a pantalla completa (estilo macOS) con todas las aplicaciones del sistema y las apps del catálogo Docker instaladas (abre su interfaz web), con buscador en vivo. Accesible desde el dock y la barra superior; se cierra con Escape o al elegir.

2.2.02026-07-24

Funciones nuevas

  • Terminal web (LGM-OS): PTY real por WebSocket con xterm.js, solo para administradores; banner ASCII LGM-OS al abrir. El shell corre como el usuario del servicio (sin privilegios), no como root. Banner LGM-OS también en el instalador.
  • Reemplazo de disco y ampliación de pool desde la UI (ZFS/Btrfs): sustituir un disco fallido y añadir discos a un pool con reequilibrado en segundo plano.

Seguridad (hallazgos de revisión adversarial)

  • CRÍTICO: el backup de configuración ya no incluye la clave JWT y exige rol admin; antes un usuario viewer podía descargarla y falsificar tokens de admin.
  • 2FA: comparación de OTP con longitud fija de 6 dígitos + validación en el login; antes un OTP de 1 dígito permitía adivinar el segundo factor.
  • Contraseña por defecto: se bloquean las acciones del servidor hasta que se rota (antes must_change_password era solo informativo).
  • Rutas de compartición (SMB/NFS/WebDAV) restringidas a /mnt y /srv (evita exportar /).
  • Límites de prefijo de la allowlist en frontera de separador; regex de snapshot sin ...

Correcciones

  • Papelera: restauración por elemento sin abortar el lote; nombrado con uuid (evita sobrescritura); prune de snapshots btrfs anidados; subida sin seguir symlinks.
  • Carrera en el store de tareas programadas (escrituras atómicas); parser de zpool status que no confunde cache/log/spare con datos; throttle del vigilante de pools; reconexión de WebSocket con backoff y sin martillear tras fallo de auth; referencias fuertes a tareas asyncio en segundo plano.

Interfaz / rendimiento

  • Ventana con foco resaltada; xterm cargado de forma diferida (chunk aparte).
  • 87 rutas API, 60 tests en verde.

2.1.02026-07-24

Interfaz

  • Iconografía Lucide en toda la UI (adiós a los emojis).
  • Dock rediseñado con estados diferenciados: app activa (pastilla azul), abierta en segundo plano (punto), minimizada (punto hueco) e hover con realce; la ventana con foco se resalta y las inactivas se atenúan. Clic en el dock trae al frente / minimiza según el foco.

Funciones nuevas

  • App Resumen (dashboard): salud y ocupación de pools, estado de servicios, CPU/RAM, uptime, resumen SMART, contenedores y notificaciones en una vista.
  • Papelera de reciclaje en el gestor de archivos: borrado suave a .recycle por raíz con listar / restaurar / vaciar (y opción de borrado permanente).
  • Self-tests SMART (corto/largo) por disco desde la pestaña Salud.
  • SAI/UPS vía NUT: apagado seguro configurable por umbral de batería; panel con estado en vivo (red/batería, carga, porcentaje) en el Panel de Control.

Backend

  • +7 rutas (dashboard, papelera x3, smart/test, ups x2) — 85 en total.
  • Tests 48 → 53 (papelera end-to-end, dashboard, render NUT, anti-inyección de driver).

2.0.02026-07-24

Seguridad (Fase 0)

  • TLS obligatorio: certificado autofirmado generado en el primer arranque (app/tls_bootstrap.py), reemplazable en /etc/nas/tls/.
  • Separación de privilegios: API como usuario nas sin privilegios + demonio nas-helper (root) con allowlist cerrada de comandos y rutas.
  • Autenticación WebSocket por subprotocolo (el token deja de viajar en la URL).
  • Rate-limiting por IP, bloqueo de cuenta tras 5 fallos y registro de auditoría.
  • Revocación de tokens (logout real, logout global, revocación al cambiar contraseña).
  • 2FA TOTP (RFC 6238) y roles admin/viewer con usuarios del panel.
  • Estado versionado con migraciones automáticas al arrancar.

Núcleo NAS (Fase 2)

  • Gestor de archivos web (listar, subir, descargar con token firmado, renombrar, copiar, eliminar).
  • Usuarios de archivos unificados (SMB + WebDAV con contraseña única).
  • Datasets ZFS / subvolúmenes Btrfs con cuotas y compresión.
  • Snapshots manuales y programados con retención y restauración.
  • SMART (smartctl JSON) + scrubs bajo demanda y programados.
  • Motor de alertas: email SMTP, webhook, umbral de ocupación, vigilancia de pools, servicios caídos y SMART; centro de notificaciones en la UI.
  • Servidor NFS; papelera de red y Time Machine (vfs fruit) en Samba.
  • Copia/restauración de la configuración (descarga y tarea programada).

Sistema (Fase 3)

  • Configuración de red vía systemd-networkd (DHCP/estática, VLAN, bond).
  • Actualizaciones apt desde la UI con progreso.
  • Logs y estadísticas de contenedores; actualización de apps instaladas.
  • Catálogo remoto de aplicaciones con verificación sha256.
  • Programador de tareas integrado (snapshot/scrub/backup).
  • Apagado/reinicio desde la UI y visor de journald.

UX (Fase 4)

  • Tema oscuro Liquid Glass con conmutador y detección del sistema.
  • Infraestructura i18n es/en (shell traducido; apps de dominio en curso).
  • Centro de notificaciones persistente con eventos en vivo.
  • Login con OTP y panel de seguridad (2FA, sesiones, usuarios del panel).

Calidad (Fase 1)

  • 48 tests (parsers con fixtures reales, auth/TOTP/lockout, estado/migraciones, allowlist, API con TestClient).
  • CI de GitHub Actions (ruff + pytest + build del frontend + e2e Playwright).
  • Specs e2e de Playwright (login, métricas en vivo, ventanas).

Distribución (Fase 5)

  • Instalador actualizado (usuario de servicio, TLS, NFS, smartmontools, Node 20).
  • deploy/update.sh con copia previa del estado.
  • Preseed + build-iso.sh para ISO de instalación desatendida.
  • Guía de administración y de recuperación de desastres.

1.0.02026-07-24

Versión inicial: pools ZFS/Btrfs, SMB/SSH/WebDAV, firewall nftables, orquestador Docker + App Store, escritorio web Liquid Glass con métricas en tiempo real.