|
| 1 | +# Propuesta: Respaldo (Backup) y Restauración de Apps Flatpak |
| 2 | + |
| 3 | +Estado: propuesta aprobada en discusión, pendiente de implementación. |
| 4 | + |
| 5 | +## Objetivo |
| 6 | + |
| 7 | +Permitir al usuario respaldar selectivamente las apps Flatpak instaladas, eligiendo |
| 8 | +por app si se incluyen sus datos (`~/.var/app/<app-id>`) o no, y poder restaurarlas |
| 9 | +después — incluso si la app ya no está disponible en Flathub o no hay red. |
| 10 | + |
| 11 | +## Ubicación en la app |
| 12 | + |
| 13 | +Sección dedicada de navegación **"Respaldos"** (nueva ruta, ítem de sidebar propio), |
| 14 | +en lugar de un botón dentro de "Mis Apps" o una acción por card. Motivo: el respaldo |
| 15 | +tiene estado propio (historial de backups hechos, ubicación en disco, restauración) |
| 16 | +que crecería mal colgado de un modal en `MyApps.tsx`. Una página propia da desde el |
| 17 | +inicio el lugar natural para listar backups existentes y lanzar restauraciones, sin |
| 18 | +tener que migrar la UX más adelante. |
| 19 | + |
| 20 | +## Por qué no basta con copiar `~/.var/app/<id>` de vuelta |
| 21 | + |
| 22 | +`~/.var/app/<app-id>` solo contiene datos/config/cache de la app — **no** el binario |
| 23 | +ni sus dependencias. El ejecutable real vive gestionado por OSTree en |
| 24 | +`/var/lib/flatpak/app/<id>/...` y requiere que Flatpak tenga la app *registrada* |
| 25 | +(refs, deployment, permisos de sandboxing) para poder ejecutarla. No existe una |
| 26 | +"carpeta portable de la app" que se pueda simplemente copiar; siempre hace falta |
| 27 | +un paso de instalación gestionado por Flatpak para tener algo ejecutable. |
| 28 | + |
| 29 | +## Por qué no confiar en que Flathub retenga versiones viejas |
| 30 | + |
| 31 | +Flathub es un repo OSTree: cada actualización crea un commit nuevo en la rama |
| 32 | +`stable`, pero los commits viejos son podados sin garantía de retención documentada. |
| 33 | +Es técnicamente posible pedir `flatpak install --commit=<hash>` para fijar una |
| 34 | +versión exacta, pero si Flathub ya podó ese commit, la instalación falla. Esta vía |
| 35 | +queda como **fallback opcional** (ver más abajo), no como mecanismo principal. |
| 36 | + |
| 37 | +## Diseño principal: bundle local vía `flatpak build-bundle` |
| 38 | + |
| 39 | +En vez de depender de un tercero (Flathub) para recuperar una versión exacta, |
| 40 | +cada backup **congela la app tal como está instalada localmente** en el momento |
| 41 | +del respaldo, usando `flatpak build-bundle` sobre el repo OSTree local del usuario. |
| 42 | +Esto la hace autocontenida y no depende de red ni de qué tan generoso sea Flathub |
| 43 | +reteniendo commits viejos. |
| 44 | + |
| 45 | +### Contenido de cada backup de app |
| 46 | + |
| 47 | +1. **`<app-id>.flatpak`** — bundle exportado vía `build-bundle` (la app exacta, |
| 48 | + versión congelada, generado desde el repo OSTree local). |
| 49 | +2. **`manifest.json`** — metadata: `app_id`, nombre, versión, ref del runtime exacto |
| 50 | + (id + arquitectura + rama + commit), permisos (`flatpak override`, ver abajo), |
| 51 | + fecha del respaldo. |
| 52 | +3. **`data.tar.zst`** *(opcional, según selección del usuario)* — contenido de |
| 53 | + `~/.var/app/<app-id>` comprimido con zstd. |
| 54 | +4. **Runtime del bundle** *(opcional, seleccionable por el usuario, ver sección de |
| 55 | + deduplicación)* — bundle del runtime exportado con `build-bundle --runtime`, |
| 56 | + para permitir restauración 100% offline. |
| 57 | + |
| 58 | +### Permisos (`flatpak override`) |
| 59 | + |
| 60 | +Los permisos otorgados a una app (`flatpak override`) **no viven** dentro de |
| 61 | +`~/.var/app/<app-id>` — viven en `~/.local/share/flatpak/overrides/<id>` (o el |
| 62 | +equivalente de sistema). Un backup ingenuo que solo copie `~/.var/app` los pierde. |
| 63 | +El `manifest.json` debe capturarlos explícitamente para poder reaplicarlos con |
| 64 | +`flatpak override` al restaurar. |
| 65 | + |
| 66 | +## Selección de formato de compresión |
| 67 | + |
| 68 | +`tar` + `zstd` (`.tar.zst`) para los datos de usuario. Comprime casi tan bien como |
| 69 | +xz pero mucho más rápido — relevante para carpetas de datos grandes (juegos, IDEs). |
| 70 | +Es además el códec que ya usa Flatpak/OSTree internamente, y el que usa Warehouse |
| 71 | +(la referencia principal del ecosistema) en producción para su feature de |
| 72 | +snapshots. Se implementa con las crates nativas `tar` y `zstd` en Rust (sin |
| 73 | +depender de binarios externos del sistema). |
| 74 | + |
| 75 | +## Runtime: opción seleccionable + deduplicación entre backups |
| 76 | + |
| 77 | +Incluir el runtime en el bundle es **opcional y seleccionable por el usuario**, |
| 78 | +igual que la opción "con datos" — no todos los backups necesitan ser 100% offline, |
| 79 | +y el runtime puede pesar mucho más que la app misma. |
| 80 | + |
| 81 | +Problema a evitar: si el usuario respalda 5 apps que comparten `org.gnome.Platform//43`, |
| 82 | +no tiene sentido exportar el mismo runtime 5 veces. |
| 83 | + |
| 84 | +### Solución: biblioteca compartida de runtimes por carpeta de destino |
| 85 | + |
| 86 | +- La carpeta de destino de backups tiene una subcarpeta compartida `runtimes/` |
| 87 | + (no por-app). |
| 88 | +- Antes de exportar un runtime para una app, se comprueba si ya existe |
| 89 | + `runtimes/<runtime-id>-<version>-<arch>.flatpak` en esa carpeta. Si existe, no |
| 90 | + se vuelve a exportar: el `manifest.json` de la app simplemente referencia ese |
| 91 | + archivo compartido por nombre. |
| 92 | +- Si no existe, se exporta una vez (`build-bundle --runtime`) y queda disponible |
| 93 | + para que cualquier otro backup futuro en la misma carpeta lo reutilice. |
| 94 | +- El `manifest.json` de cada app guarda el ref del runtime y un puntero relativo |
| 95 | + a `../runtimes/<archivo>.flatpak` (no una copia). |
| 96 | + |
| 97 | +### Resolución al restaurar |
| 98 | + |
| 99 | +1. Si el runtime exacto ya está instalado en el sistema destino → se usa ese, se |
| 100 | + ignora cualquier bundle. |
| 101 | +2. Si no está instalado pero el bundle existe en `runtimes/` (ya sea de esta carpeta |
| 102 | + de backups o el usuario lo tiene junto al backup) → se instala desde ahí, sin red. |
| 103 | +3. Si no está instalado y no hay bundle disponible → se intenta traer desde Flathub |
| 104 | + por red como último recurso, y se avisa claramente al usuario si eso falla. |
| 105 | + |
| 106 | +## Plan B (descartado como mecanismo principal, mantenido como fallback informativo) |
| 107 | + |
| 108 | +Guardar el commit hash de Flathub en el manifiesto e intentar |
| 109 | +`flatpak install --commit=<hash> flathub <app-id>` como alternativa si por algún |
| 110 | +motivo no se dispone del bundle local (p. ej. el usuario perdió el archivo del |
| 111 | +bundle pero conserva el manifiesto). Si el commit fue podado, se debe avisar |
| 112 | +explícitamente al usuario y ofrecer instalar la última versión disponible — |
| 113 | +nunca fallar en silencio ni sustituir la versión sin decirlo. |
| 114 | + |
| 115 | +## Validación contra el estado del arte (Warehouse) |
| 116 | + |
| 117 | +Investigación de la app GNOME **Warehouse** (`io.github.flattool.Warehouse`, |
| 118 | +referencia principal del ecosistema para este problema): |
| 119 | + |
| 120 | +- Su feature "Snapshots" **solo** respalda `~/.var/app/<app-id>` (datos), asumiendo |
| 121 | + que la app siempre se puede reinstalar después desde Flathub. Nunca empaqueta el |
| 122 | + binario ni el runtime. |
| 123 | +- Usa `tar` (shell-out al binario del sistema) con zstd para el archivo de datos, |
| 124 | + más un JSON sidecar con metadata — confirma la elección de zstd como estándar de |
| 125 | + facto. |
| 126 | +- No resuelve el caso "la app ya no está en Flathub" ni "quiero restaurar sin red" — |
| 127 | + hueco real que nuestro diseño con `build-bundle` sí cubre. |
| 128 | +- Conclusión: nuestro diseño es más completo que el estado del arte existente para |
| 129 | + el caso de backup verdaderamente offline/a-prueba-de-desaparición-en-Flathub, a |
| 130 | + costa de bundles más pesados que un simple tarball de datos. |
| 131 | + |
| 132 | +## Gotchas técnicos a respetar en la implementación |
| 133 | + |
| 134 | +- `build-bundle` exporta un solo ref por invocación (la app **o** el runtime, no |
| 135 | + ambos) — hace falta una invocación separada por cada uno. |
| 136 | +- GPG signing es opcional en `build-bundle`/`install`; no hace falta montar |
| 137 | + infraestructura de firmas para que los bundles se instalen localmente. |
| 138 | +- Sin bundle de runtime y sin runtime ya instalado en destino, la instalación |
| 139 | + offline de un bundle de app falla con "runtime not found" — de ahí la |
| 140 | + importancia de registrar el ref exacto del runtime en el manifiesto incluso |
| 141 | + cuando no se incluya el bundle del runtime. |
0 commit comments