gioser

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 1483Shell 181Rust 107Markdown 963619 Definitions

licencias: campo `license` en la receta — de 0 a 228 de 1141, sin re-hashear nada

La deuda legal bloqueante del SDD 19 §2.1. Medido hoy: **0 de 1141 recetas** declaraban
licencia, no «5 de 771» como decía el informe anterior. Los dos números estaban mal: los
«5» eran falsos positivos de `grep license` (el paquete `addlicense`, el paquete
`cargo-bundle-licenses`, una línea `install .../share/licenses/` y un comentario), y las
recetas son 1141. Contar con `grep -l <palabra>` sobre TOML cuenta comentarios y nombres,
no campos; `scripts/licencias.sh` cuenta el campo de verdad (clave en la raíz, antes del
primer `[table]`).

LO QUE HACE LA DEUDA PAGABLE: `Recipe::hash_inputs` es una LISTA BLANCA — sólo entran
source, compiler, target, link, patches, flags, phases y deps. `license` no entra, igual
que `evidence` y `slots`. Por eso se puede poblar en las recetas YA SELLADAS sin mover un
solo ArtifactHash. Verificado, no supuesto: en 40 recetas modificadas se comparó el hash
con y sin la línea — 40 idénticos, 0 cambiados. Si el campo entrara al hash, declarar la
licencia costaría reconstruir el corpus entero y no se haría nunca. Clavado con el test
`licencia_round_trip_y_no_afecta_el_hash`.

TRAMPA DE TOML: una clave suelta después de un `[table]` pertenece a esa tabla. Puesta al
final del fichero, `license` acaba dentro de `[deps]` y se pierde EN SILENCIO, porque serde
ignora los campos que no conoce — no hay error, simplemente no está. Va arriba, junto a
`name` y `version`; el sembrador la inserta tras `version`.

NO SE ADIVINA. Declarar mal una licencia es peor que dejarla vacía: convierte un hueco
visible en una afirmación falsa. Sólo se puebla desde una tabla curada entrada por entrada
(`docs/licencias-conocidas.tsv`); lo que no tiene evidencia queda vacío y se CUENTA.
Concretamente se descartó el atajo «k* = KDE ⇒ LGPL»: en este catálogo `kail`, `kind`,
`ko`, `kopia`, `krew`, `kustomize`, `kyverno`, `katana`, `kibi`, `kmon` y toda la familia
`kube*` son herramientas Go sin relación con KDE. El nombre no es evidencia.

Quedan 913, casi todas CLIs Go/Rust importados en masa — y ésas sí son automatizables con
evidencia real: Cargo.toml trae el campo `license` y los módulos Go traen su LICENSE en el
árbol. El cierre estructural es capturarlo en la fase de fetch, que ya descarga y extrae
cada tarball, y inyectar el texto en `hammer pack` (aguas abajo del ArtifactHash) en vez de
en la fase install (que sí re-hashearía).

De paso, respaldo-storagebox.sh reordenado por valor irreemplazable y con zstd: medido en
la oficina, el uplink da 8 Mbps iguales por cable y por wifi ⇒ 128 G no caben en una
sentada, así que sube primero el cerebro (estado + repo) y `--partial-dir` hace que cortar
a mitad de un artefacto no tire lo ya subido.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

346cd597063c3275933d8a2dc03f17ca8a20d05a · by sergio · (2 months ago) · parent b12ea3ac9 · browse the tree at this commit

Semantic delta · 2

crates/hammer-core/src/recipe.rs

232 files · +687 −19

diff --git a/crates/hammer-core/src/recipe.rs b/crates/hammer-core/src/recipe.rs
index b21c8957..78b9772d 100644
--- a/crates/hammer-core/src/recipe.rs
+++ b/crates/hammer-core/src/recipe.rs
@@ -26,6 +26,24 @@ pub struct Recipe {
     /// (`crate::compat`) contra el estado instalado, no el build.
     #[serde(default, skip_serializing_if = "Slots::is_empty")]
     pub slots: Slots,
+    /// **Bajo qué términos se redistribuye este paquete.** Expresión SPDX (`"GPL-2.0-or-later"`,
+    /// `"MIT OR Apache-2.0"`, `"LGPL-2.1-only AND BSD-3-Clause"`).
+    ///
+    /// No es burocracia: al distribuir BINARIOS, la GPL y familia obligan a acompañarlos del texto
+    /// de la licencia y a poder entregar la fuente correspondiente. Una distro que no sabe bajo qué
+    /// términos redistribuye lo que empaqueta no puede cumplir ninguna de las dos cosas, y eso es
+    /// bloqueante para publicar (SDD 19 §2.1). Medido el 2026-08-07: **0 de 1141 recetas** lo
+    /// declaraban.
+    ///
+    /// Igual que `evidence` y `slots`, **NO entra en `hash_inputs`** — y acá esa propiedad es la que
+    /// hace la deuda pagable: describe términos legales, no identidad del build, así que se puede
+    /// poblar en las 1141 recetas YA SELLADAS sin mover un solo `ArtifactHash` ni invalidar el
+    /// baseline de reproducibilidad. Si entrara al hash, poner la licencia costaría reconstruir el
+    /// corpus entero y por eso no se habría hecho nunca.
+    ///
+    /// `None` = deuda declarada, no «sin licencia». `scripts/licencias.sh` mide la cobertura.
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub license: Option<String>,
     /// Directorio base contra el que se resuelven rutas relativas de la receta
     /// (típicamente, `patches`). Lo fija `load_from_path`; al deserializar puro queda vacío.
     #[serde(skip, default)]
@@ -800,6 +818,42 @@ expected_output = "b3:deadbeef"
         );
     }
 
+    #[test]
+    fn licencia_round_trip_y_no_afecta_el_hash() {
+        // ESTE TEST ES LA RAZÓN DE QUE LA DEUDA DE LICENCIAS SEA PAGABLE.
+        //
+        // El 2026-08-07 ninguna de las 1141 recetas declaraba licencia, y sin ese dato la distro no
+        // puede cumplir la GPL al redistribuir binarios (SDD 19 §2.1). Poblarlo sólo es viable si
+        // NO mueve el `ArtifactHash`: si lo moviera, poner la licencia significaría reconstruir el
+        // corpus entero y perder el baseline de reproducibilidad, y nadie lo haría nunca.
+        //
+        // `hash_inputs` es una lista blanca, así que la propiedad se cumple por construcción — pero
+        // se clava acá para que nadie la rompa por descuido metiendo el campo en el hash.
+        // La línea va DELANTE: en TOML una clave suelta después de un `[table]` pertenece a esa
+        // tabla, no a la raíz. Puesta al final, `license` acabaría dentro de `[deps]` y se perdería
+        // en silencio (serde ignora campos desconocidos). Es el mismo cuidado que hay que tener al
+        // poblar las recetas: el campo va arriba, junto a `name` y `version`.
+        let con_toml = format!("license = \"GPL-3.0-or-later\"\n{SAMPLE}");
+        let con = Recipe::from_toml(&con_toml).unwrap();
+        assert_eq!(con.license.as_deref(), Some("GPL-3.0-or-later"));
+
+        // Round-trip por TOML: serializa y vuelve a parsear ⇒ misma licencia.
+        let re = Recipe::from_toml(&con.to_toml().unwrap()).unwrap();
+        assert_eq!(re.license, con.license);
+
+        // Una receta sin el campo parsea igual (las 1141 existentes no se tocan para compilar).
+        let sin = Recipe::from_toml(SAMPLE).unwrap();
+        assert_eq!(sin.license, None);
+
+        // Y el hash es el MISMO con y sin licencia.
+        assert_eq!(
+            sin.hash_inputs(&[]).unwrap(),
+            con.hash_inputs(&[]).unwrap(),
+            "la licencia NO debe entrar en hash_inputs: si entrara, declararla re-hashearía las \
+             1141 recetas ya selladas y tiraría el baseline de reproducibilidad"
+        );
+    }
+
     #[test]
     fn slots_validate_rechaza_hash_sin_prefijo() {
         let mut s = Slots::default();
diff --git a/docs/licencias-conocidas.tsv b/docs/licencias-conocidas.tsv
new file mode 100644
index 00000000..f127b15f
--- /dev/null
+++ b/docs/licencias-conocidas.tsv
@@ -0,0 +1,233 @@
+# Tabla CURADA de licencias, para `scripts/licencias.sh --sembrar`. SDD 19 §2.1.
+#
+# Formato:  <nombre-de-receta><TAB><expresión SPDX>
+#
+# ── LA REGLA ────────────────────────────────────────────────────────────────────────────────────
+# Acá SÓLO entra lo que se puede afirmar con seguridad, entrada por entrada. Declarar mal una
+# licencia es peor que dejarla vacía: convierte un hueco visible —que alguien va a ir a llenar— en
+# una afirmación falsa sobre la que alguien va a construir. Lo que no esté seguro, NO se pone: se
+# queda como deuda contada por `scripts/licencias.sh`.
+#
+# ── POR QUÉ NO SE PUEDE AUTOMATIZAR POR PATRÓN DE NOMBRE ────────────────────────────────────────
+# Tentación evidente: «todo lo que empieza por k* es KDE ⇒ LGPL». **Falso y caro.** En este catálogo
+# `kail`, `kind`, `ko`, `kopia`, `krew`, `kustomize`, `kyverno`, `katana`, `kiterunner`, `kibi`,
+# `kmon`, `kondo`, `kpt`, `kbld`, `kbs2`, `knighttime` y toda la familia `kube*` son herramientas Go
+# sin ninguna relación con KDE — la mayoría Apache-2.0 o MIT. El nombre no es evidencia.
+#
+# ── LO QUE FALTA ES SOBRE TODO Go Y Rust ───────────────────────────────────────────────────────
+# El grueso de las 1141 recetas son CLIs Go/Rust importados en masa. Para esas la licencia SÍ es
+# automatizable con evidencia real y sin red: Cargo.toml trae el campo `license` y los módulos Go
+# traen su LICENSE en el árbol. Es el trabajo estructural descrito en la cabecera de licencias.sh
+# (capturar en la fase de fetch, que ya descarga y extrae el tarball).
+
+# ── toolchain y base C ─────────────────────────────────────────────────────────────────────────
+musl	MIT
+zlib	Zlib
+zlib-shared	Zlib
+bzip2	bzip2-1.0.6
+bzip2-shared	bzip2-1.0.6
+xz	0BSD
+zstd	BSD-3-Clause OR GPL-2.0-only
+lz4	BSD-2-Clause AND GPL-2.0-only
+brotli	MIT
+binutils	GPL-3.0-or-later
+gcc	GPL-3.0-or-later WITH GCC-exception-3.1
+make	GPL-3.0-or-later
+m4	GPL-3.0-or-later
+bison	GPL-3.0-or-later
+flex	BSD-3-Clause
+patch	GPL-3.0-or-later
+diffutils	GPL-3.0-or-later
+findutils	GPL-3.0-or-later
+coreutils	GPL-3.0-or-later
+grep	GPL-3.0-or-later
+sed	GPL-3.0-or-later
+gawk	GPL-3.0-or-later
+tar	GPL-3.0-or-later
+gzip	GPL-3.0-or-later
+bash	GPL-3.0-or-later
+busybox	GPL-2.0-only
+autoconf	GPL-3.0-or-later
+automake	GPL-2.0-or-later
+libtool	GPL-2.0-or-later
+gettext-tiny	GPL-3.0-or-later
+texinfo	GPL-3.0-or-later
+pkgconf	ISC
+cmake	BSD-3-Clause
+meson	Apache-2.0
+samurai	ISC
+ninja	Apache-2.0
+python3	Python-2.0
+perl	Artistic-1.0-Perl OR GPL-1.0-or-later
+sqlite	blessing
+openssl	Apache-2.0
+curl	curl
+ca-certificates	MPL-2.0
+libarchive	BSD-2-Clause
+libcap	BSD-3-Clause OR GPL-2.0-only
+libseccomp	LGPL-2.1-only
+bwrap	LGPL-2.0-or-later
+kbd	GPL-2.0-or-later
+util-linux	GPL-2.0-or-later AND LGPL-2.1-or-later AND BSD-3-Clause
+e2fsprogs	GPL-2.0-only AND LGPL-2.0-only AND BSD-3-Clause
+linux	GPL-2.0-only WITH Linux-syscall-note
+
+# ── bibliotecas de sistema y gráficas ──────────────────────────────────────────────────────────
+expat	MIT
+libffi	MIT
+pcre2	BSD-3-Clause
+libxml2	MIT
+libxml2-shared	MIT
+libyaml	MIT
+libyaml-shared	MIT
+libpng	Libpng
+libpng-shared	Libpng
+libjpeg-turbo	IJG AND BSD-3-Clause AND Zlib
+libjpeg-turbo-shared	IJG AND BSD-3-Clause AND Zlib
+libtiff	libtiff
+libtiff-shared	libtiff
+libwebp	BSD-3-Clause
+freetype	FTL OR GPL-2.0-or-later
+freetype-shared	FTL OR GPL-2.0-or-later
+fontconfig	MIT
+fontconfig-shared	MIT
+harfbuzz	MIT
+fribidi	LGPL-2.1-or-later
+pixman	MIT
+cairo	LGPL-2.1-only OR MPL-1.1
+cairo-shared	LGPL-2.1-only OR MPL-1.1
+pango	LGPL-2.0-or-later
+graphene	MIT
+libepoxy	MIT
+mesa	MIT
+libdrm	MIT
+wayland	MIT
+wayland-protocols	MIT
+plasma-wayland-protocols	MIT
+xkeyboard-config	MIT
+libxkbcommon	MIT
+alsa-lib	LGPL-2.1-or-later
+pipewire	MIT
+wireplumber	MIT
+iso-codes	LGPL-2.1-or-later
+libseccomp-shared	LGPL-2.1-only
+
+# ── GNOME / GTK ────────────────────────────────────────────────────────────────────────────────
+glib	LGPL-2.1-or-later
+gobject-introspection	LGPL-2.0-or-later AND GPL-2.0-or-later
+gdk-pixbuf	LGPL-2.1-or-later
+gtk4	LGPL-2.1-or-later
+libadwaita	LGPL-2.1-or-later
+gsettings-desktop-schemas	LGPL-2.1-or-later
+gnome-desktop	GPL-2.0-or-later AND LGPL-2.0-or-later
+gnome-shell	GPL-2.0-or-later
+gnome-session	GPL-2.0-or-later
+mutter	GPL-2.0-or-later
+json-glib	LGPL-2.1-or-later
+libsoup	LGPL-2.0-or-later
+at-spi2-core	LGPL-2.1-or-later
+atk	LGPL-2.0-or-later
+adwaita-icon-theme	LGPL-3.0-or-later OR CC-BY-SA-3.0
+hicolor	GPL-2.0-or-later
+accountsservice	GPL-3.0-or-later
+colord	GPL-2.0-or-later AND LGPL-2.1-or-later
+geoclue	GPL-2.0-or-later
+appstream	LGPL-2.1-or-later
+libxmlb	LGPL-2.1-or-later
+xdg-desktop-portal	LGPL-2.1-or-later
+xdg-desktop-portal-gnome	LGPL-2.1-or-later
+
+# ── Qt ─────────────────────────────────────────────────────────────────────────────────────────
+# Qt 6 open source: LGPL-3.0 o GPL (la alternativa es la licencia comercial, que no aplica acá).
+qt6-qtbase	LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+qt6-qtdeclarative	LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+qt6-qtwayland	LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+qt6-qtsvg	LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+qt6-qttools	LGPL-3.0-only OR GPL-3.0-only
+qt6-qtshadertools	LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+qt6-qt5compat	LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+qt5compat	LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+# ── KDE Frameworks (bibliotecas: LGPL doble) ───────────────────────────────────────────────────
+# Verificadas una a una: son Frameworks de KDE, NO las herramientas Go homónimas (ver cabecera).
+karchive	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kauth	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kbookmarks	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kcodecs	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kcolorscheme	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kcompletion	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kconfig	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kconfigwidgets	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kcoreaddons	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kcrash	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kdbusaddons	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kdeclarative	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kdecoration	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kded	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kfilemetadata	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kglobalaccel	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kguiaddons	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kholidays	LGPL-2.1-or-later OR LGPL-3.0-or-later
+ki18n	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kiconthemes	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kidletime	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kio	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kirigami	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kirigami-addons	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kitemmodels	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kitemviews	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kjobwidgets	LGPL-2.1-or-later OR LGPL-3.0-or-later
+knewstuff	LGPL-2.1-or-later OR LGPL-3.0-or-later
+knotifications	LGPL-2.1-or-later OR LGPL-3.0-or-later
+knotifyconfig	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kpackage	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kparts	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kplotting	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kpty	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kquickcharts	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kservice	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kstatusnotifieritem	LGPL-2.1-or-later OR LGPL-3.0-or-later
+ksvg	LGPL-2.1-or-later OR LGPL-3.0-or-later
+ktexteditor	LGPL-2.1-or-later OR LGPL-3.0-or-later
+ktextwidgets	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kunitconversion	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kwallet	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kwayland	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kwidgetsaddons	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kwindowsystem	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kxmlgui	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kcmutils	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kdnssd	LGPL-2.1-or-later OR LGPL-3.0-or-later
+attica	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kactivitymanagerd	GPL-2.0-or-later
+plasma-activities	LGPL-2.1-or-later OR LGPL-3.0-or-later
+plasma-activities-stats	LGPL-2.1-or-later OR LGPL-3.0-or-later
+plasma5support	LGPL-2.1-or-later OR LGPL-3.0-or-later
+
+# ── Plasma y aplicaciones KDE (GPL) ────────────────────────────────────────────────────────────
+kwin	GPL-2.0-or-later
+plasma-workspace	GPL-2.0-or-later
+plasma-desktop	GPL-2.0-or-later
+plasma-integration	LGPL-2.1-or-later OR LGPL-3.0-or-later
+plasma-nm	GPL-2.0-or-later
+plasma-pa	GPL-2.0-or-later
+kscreen	GPL-2.0-or-later
+kscreenlocker	GPL-2.0-or-later
+kglobalacceld	GPL-2.0-or-later
+krunner	LGPL-2.1-or-later OR LGPL-3.0-or-later
+kmenuedit	GPL-2.0-or-later
+kinfocenter	GPL-2.0-or-later
+kpipewire	GPL-2.0-or-later OR LGPL-2.1-or-later
+breeze	GPL-2.0-or-later
+breeze-icons	LGPL-3.0-or-later
+ark	GPL-2.0-or-later
+kate	LGPL-2.0-or-later
+konsole	GPL-2.0-or-later
+dolphin	GPL-2.0-or-later
+kcalc	GPL-2.0-or-later
+kcharselect	GPL-2.0-or-later
+kdf	GPL-2.0-or-later
+kfind	GPL-2.0-or-later
+kruler	GPL-2.0-or-later
+kwalletmanager	GPL-2.0-or-later
+kquickimageeditor	LGPL-2.1-or-later OR LGPL-3.0-or-later
diff --git a/recipes/appstream.toml b/recipes/appstream.toml
index 0cfe2394..1517697e 100644
--- a/recipes/appstream.toml
+++ b/recipes/appstream.toml
@@ -3,6 +3,7 @@
 # glib + libxml2 + libyaml.
 name    = "appstream"
 version = "1.0.5"
+license = "LGPL-2.1-or-later"
 
 [source]
 tarball = "https://www.freedesktop.org/software/appstream/releases/AppStream-1.0.5.tar.xz"
diff --git a/recipes/bash.toml b/recipes/bash.toml
index d0a65972..c58cfff6 100644
--- a/recipes/bash.toml
+++ b/recipes/bash.toml
@@ -3,6 +3,7 @@
 # limpio de GNU (5.3 base, sin los parches upstream), build estático musl.
 name    = "bash"
 version = "5.3"
+license = "GPL-3.0-or-later"
 
 [source]
 tarball = "https://ftp.gnu.org/gnu/bash/bash-5.3.tar.gz"
diff --git a/recipes/binutils.toml b/recipes/binutils.toml
index 47caf980..385224ce 100644
--- a/recipes/binutils.toml
+++ b/recipes/binutils.toml
@@ -28,6 +28,7 @@
 
 name    = "binutils"
 version = "2.45.1"
+license = "GPL-3.0-or-later"
 
 [source]
 tarball = "https://ftp.gnu.org/gnu/binutils/binutils-2.45.1.tar.gz"
diff --git a/recipes/bison.toml b/recipes/bison.toml
index dee0f7f7..cc8d0f45 100644
--- a/recipes/bison.toml
+++ b/recipes/bison.toml
@@ -9,6 +9,7 @@
 
 name    = "bison"
 version = "3.8.2"
+license = "GPL-3.0-or-later"
 
 [source]
 tarball = "https://ftp.gnu.org/gnu/bison/bison-3.8.2.tar.gz"
diff --git a/recipes/brotli.toml b/recipes/brotli.toml
index c4dd13e2..12ea9398 100644
--- a/recipes/brotli.toml
+++ b/recipes/brotli.toml
@@ -4,6 +4,7 @@
 # hay que declararlo en [deps].build para que el lab lo materialice (patrón libjpeg-turbo/libtiff).
 name    = "brotli"
 version = "1.2.0"
+license = "MIT"
 
 [source]
 tarball = "https://github.com/google/brotli/archive/refs/tags/v1.2.0.tar.gz"
diff --git a/recipes/busybox.toml b/recipes/busybox.toml
index 85b0fd98..34b6a8d6 100644
--- a/recipes/busybox.toml
+++ b/recipes/busybox.toml
@@ -10,6 +10,7 @@
 
 name    = "busybox"
 version = "1.36.1"
+license = "GPL-2.0-only"
 
 [source]
 tarball = "https://busybox.net/downloads/busybox-1.36.1.tar.bz2"
diff --git a/recipes/bwrap.toml b/recipes/bwrap.toml
index b748cd1e..2f7627c7 100644
--- a/recipes/bwrap.toml
+++ b/recipes/bwrap.toml
@@ -20,6 +20,7 @@
 
 name    = "bwrap"
 version = "0.11.0"
+license = "LGPL-2.0-or-later"
 
 [source]
 tarball = "https://github.com/containers/bubblewrap/releases/download/v0.11.0/bubblewrap-0.11.0.tar.xz"
diff --git a/recipes/bzip2.toml b/recipes/bzip2.toml
index 0745ca0a..8979c914 100644
--- a/recipes/bzip2.toml
+++ b/recipes/bzip2.toml
@@ -2,6 +2,7 @@
 # De-Alpinizada: compiler=zig-cc (migrado de gcc, matar-gcc 2026-07-16), make directo, install con PREFIX (el Makefile no usa DESTDIR).
 name    = "bzip2"
 version = "1.0.8"
+license = "bzip2-1.0.6"
 [source]
 tarball = "https://sourceware.org/pub/bzip2/bzip2-1.0.8.tar.gz"
 sha256  = "ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269"
diff --git a/recipes/ca-certificates.toml b/recipes/ca-certificates.toml
index 8517d91a..be8cdbbc 100644
--- a/recipes/ca-certificates.toml
+++ b/recipes/ca-certificates.toml
@@ -3,6 +3,7 @@
 # sha256 del tarball (el wrapper lo calcula), y adaptar build/install del shell de abuild.
 name    = "ca-certificates"
 version = "20260611"
+license = "MPL-2.0"
 
 [source]
 tarball = "https://gitlab.alpinelinux.org/alpine/ca-certificates/-/archive/20260611/ca-certificates-20260611.tar.bz2"
diff --git a/recipes/cairo.toml b/recipes/cairo.toml
index e769aa68..2fc3a3b4 100644
--- a/recipes/cairo.toml
+++ b/recipes/cairo.toml
@@ -1,6 +1,7 @@
 # cairo 1.18.4 — render 2D vectorial (base de pango/GTK). meson static, backends X/quartz/win off.
 name    = "cairo"
 version = "1.18.4"
+license = "LGPL-2.1-only OR MPL-1.1"
 [source]
 tarball = "https://gitlab.freedesktop.org/cairo/cairo/-/archive/1.18.4/cairo-1.18.4.tar.bz2"
 sha256  = "6d9281e786fd289d382324d4588d59973a36911e1865b40e64f9ec39936ceba8"
diff --git a/recipes/cmake.toml b/recipes/cmake.toml
index 341229de..eaf35a07 100644
--- a/recipes/cmake.toml
+++ b/recipes/cmake.toml
@@ -27,6 +27,7 @@
 
 name    = "cmake"
 version = "3.31.6"
+license = "BSD-3-Clause"
 
 [source]
 tarball = "https://github.com/Kitware/CMake/releases/download/v3.31.6/cmake-3.31.6.tar.gz"
diff --git a/recipes/coreutils.toml b/recipes/coreutils.toml
index 586ed17f..bdbca9bb 100644
--- a/recipes/coreutils.toml
+++ b/recipes/coreutils.toml
@@ -18,6 +18,7 @@
 
 name    = "coreutils"
 version = "9.8"
+license = "GPL-3.0-or-later"
 
 [source]
 tarball = "https://ftp.gnu.org/gnu/coreutils/coreutils-9.8.tar.gz"
diff --git a/recipes/curl.toml b/recipes/curl.toml
index f64fbde1..e1dfe8d9 100644
--- a/recipes/curl.toml
+++ b/recipes/curl.toml
@@ -5,6 +5,7 @@
 # - drop de deps ausentes: --disable-ares, --without-{libidn2,nghttp2,libpsl,libssh2}. HTTP/HTTPS ok.
 name    = "curl"
 version = "8.20.0"
+license = "curl"
 
 [source]
 tarball = "https://curl.se/download/curl-8.20.0.tar.xz"
diff --git a/recipes/diffutils.toml b/recipes/diffutils.toml
index 6e5a6ae6..abef90b4 100644
--- a/recipes/diffutils.toml
+++ b/recipes/diffutils.toml
@@ -10,6 +10,7 @@
 
 name    = "diffutils"
 version = "0.5.0"
+license = "GPL-3.0-or-later"
 
 [source]
 repo   = "https://github.com/uutils/diffutils"
diff --git a/recipes/e2fsprogs.toml b/recipes/e2fsprogs.toml
index b18fd76a..bd4c6137 100644
--- a/recipes/e2fsprogs.toml
+++ b/recipes/e2fsprogs.toml
@@ -3,6 +3,7 @@
 # sha256 del tarball (el wrapper lo calcula), y adaptar build/install del shell de abuild.
 name    = "e2fsprogs"
 version = "1.47.4"
+license = "GPL-2.0-only AND LGPL-2.0-only AND BSD-3-Clause"
 
 [source]
 tarball = "https://github.com/tytso/e2fsprogs/archive/v1.47.4.tar.gz"
diff --git a/recipes/expat.toml b/recipes/expat.toml
index de0577e6..5e896eb3 100644
--- a/recipes/expat.toml
+++ b/recipes/expat.toml
@@ -2,6 +2,7 @@
 # De-Alpinizada (Etapa G): compiler=zig-cc (migrado de gcc, matar-gcc 2026-07-16), configure-split explícito, estático. Sin deps externas.
 name    = "expat"
 version = "2.8.1"
+license = "MIT"
 
 [source]
 tarball = "https://github.com/libexpat/libexpat/releases/download/R_2_8_1/expat-2.8.1.tar.xz"
diff --git a/recipes/findutils.toml b/recipes/findutils.toml
index 5578917a..7fa2bcff 100644
--- a/recipes/findutils.toml
+++ b/recipes/findutils.toml
@@ -16,6 +16,7 @@
 
 name    = "findutils"
 version = "0.9.1"
+license = "GPL-3.0-or-later"
 
 [source]
 repo   = "https://github.com/uutils/findutils"
diff --git a/recipes/flex.toml b/recipes/flex.toml
index 9bf025df..45edc3fa 100644
--- a/recipes/flex.toml
+++ b/recipes/flex.toml
@@ -8,6 +8,7 @@
 
 name    = "flex"
 version = "2.6.4"
+license = "BSD-3-Clause"
 
 [source]
 tarball = "https://github.com/westes/flex/releases/download/v2.6.4/flex-2.6.4.tar.gz"
diff --git a/recipes/fontconfig.toml b/recipes/fontconfig.toml
index 5adbb841..1a77baaa 100644
--- a/recipes/fontconfig.toml
+++ b/recipes/fontconfig.toml
@@ -1,6 +1,7 @@
 # fontconfig 2.18.1 — configuración/cache de fuentes (lo usa cairo/pango). autotools static.
 name    = "fontconfig"
 version = "2.18.1"
+license = "MIT"
 [source]
 tarball = "https://gitlab.freedesktop.org/api/v4/projects/890/packages/generic/fontconfig/2.18.1/fontconfig-2.18.1.tar.xz"
 sha256  = "2300f3dbfa7253b3a44f4feecdbc8dfa45dde5dc2cfb71fceaf31f394cb41031"
diff --git a/recipes/freetype.toml b/recipes/freetype.toml
index 0d82bff0..8017e400 100644
--- a/recipes/freetype.toml
+++ b/recipes/freetype.toml
@@ -1,6 +1,7 @@
 # freetype 2.14.3 — lib C base (de-Alpinizada, Etapa G). compiler=zig-cc (migrado de gcc, matar-gcc 2026-07-16), configure-split, estático.
 name    = "freetype"
 version = "2.14.3"
+license = "FTL OR GPL-2.0-or-later"
 
 [source]
 tarball = "https://download.savannah.gnu.org/releases/freetype/freetype-2.14.3.tar.xz"
diff --git a/recipes/fribidi.toml b/recipes/fribidi.toml
index 36352269..17976863 100644
--- a/recipes/fribidi.toml
+++ b/recipes/fribidi.toml
@@ -2,6 +2,7 @@
 # mínimo, leaf (sin deps).
 name    = "fribidi"
 version = "1.0.16"
+license = "LGPL-2.1-or-later"
 
 [source]
 tarball = "https://github.com/fribidi/fribidi/releases/download/v1.0.16/fribidi-1.0.16.tar.xz"
diff --git a/recipes/gawk.toml b/recipes/gawk.toml
index 5c18555d..1b83a5a9 100644
--- a/recipes/gawk.toml
+++ b/recipes/gawk.toml
@@ -3,6 +3,7 @@
 # sha256 del tarball (el wrapper lo calcula), y adaptar build/install del shell de abuild.
 name    = "gawk"
 version = "5.3.2"
+license = "GPL-3.0-or-later"
 
 [source]
 tarball = "https://ftp.gnu.org/gnu/gawk/gawk-5.3.2.tar.xz"
diff --git a/recipes/gdk-pixbuf.toml b/recipes/gdk-pixbuf.toml
index 38520a0d..384c7cec 100644
--- a/recipes/gdk-pixbuf.toml
+++ b/recipes/gdk-pixbuf.toml
@@ -3,6 +3,7 @@
 # por firma built-in; el sniffing GIO/mime es solo para content-type en runtime, innecesario p/ GTK4.
 name    = "gdk-pixbuf"
 version = "2.44.6"
+license = "LGPL-2.1-or-later"
 [source]
 tarball = "https://download.gnome.org/sources/gdk-pixbuf/2.44/gdk-pixbuf-2.44.6.tar.xz"
 sha256  = "140c2d0b899fcf853ee92b26373c9dc228dbcde0820a4246693f4328a27466fa"
diff --git a/recipes/gettext-tiny.toml b/recipes/gettext-tiny.toml
index 02e5b103..5881174a 100644
--- a/recipes/gettext-tiny.toml
+++ b/recipes/gettext-tiny.toml
@@ -3,6 +3,7 @@
 # i18n.gettext()). LIBINTL=MUSL: usa la libintl que ya trae musl. Reemplaza al gettext "1.0" roto del import.
 name    = "gettext-tiny"
 version = "0.3.2"
+license = "GPL-3.0-or-later"
 
 [source]
 tarball = "https://github.com/sabotage-linux/gettext-tiny/archive/refs/tags/v0.3.2.tar.gz"
diff --git a/recipes/glib.toml b/recipes/glib.toml
index f2a73dd0..ead9f71b 100644
--- a/recipes/glib.toml
+++ b/recipes/glib.toml
@@ -1,6 +1,7 @@
 # glib 2.88.1 — la fundación de GTK (objetos/loops/utilidades). meson static, nls off (sin gettext).
 name    = "glib"
 version = "2.88.1"
+license = "LGPL-2.1-or-later"
 [source]
 tarball = "https://download.gnome.org/sources/glib/2.88/glib-2.88.1.tar.xz"
 sha256  = "51ab804c56f6eab3e5045c774d1290ac5e4c923d4f9a3d8e33123bee45c1840e"
diff --git a/recipes/graphene.toml b/recipes/graphene.toml
index 42f88af2..a83d63b7 100644
--- a/recipes/graphene.toml
+++ b/recipes/graphene.toml
@@ -1,6 +1,7 @@
 # graphene 1.10.8 — tipos/álgebra de vectores y matrices (GTK4/GSK). meson static, usa glib.
 name    = "graphene"
 version = "1.10.8"
+license = "MIT"
 [source]
 tarball = "https://download.gnome.org/sources/graphene/1.10/graphene-1.10.8.tar.xz"
 sha256  = "a37bb0e78a419dcbeaa9c7027bcff52f5ec2367c25ec859da31dfde2928f279a"
diff --git a/recipes/grep.toml b/recipes/grep.toml
index 02baaf9b..d259a494 100644
--- a/recipes/grep.toml
+++ b/recipes/grep.toml
@@ -9,6 +9,7 @@
 
 name    = "grep"
 version = "3.12"
+license = "GPL-3.0-or-later"
 
 [source]
 tarball = "https://ftp.gnu.org/gnu/grep/grep-3.12.tar.gz"
diff --git a/recipes/gtk4.toml b/recipes/gtk4.toml
index e4b78d85..abda9b44 100644
--- a/recipes/gtk4.toml
+++ b/recipes/gtk4.toml
@@ -4,6 +4,7 @@
 # Deps = cierre transitivo .pc completo (pkg-config estático resuelve Requires.private).
 name    = "gtk4"
 version = "4.18.6"
+license = "LGPL-2.1-or-later"
 
 [source]
 tarball = "https://download.gnome.org/sources/gtk/4.18/gtk-4.18.6.tar.xz"
diff --git a/recipes/gzip.toml b/recipes/gzip.toml
index 18ad5dfc..353999e9 100644
--- a/recipes/gzip.toml
+++ b/recipes/gzip.toml
@@ -3,6 +3,7 @@
 # sha256 del tarball (el wrapper lo calcula), y adaptar build/install del shell de abuild.
 name    = "gzip"
 version = "1.14"
+license = "GPL-3.0-or-later"
 
 [source]
 tarball = "https://ftp.gnu.org/gnu/gzip/gzip-1.14.tar.xz"
diff --git a/recipes/harfbuzz.toml b/recipes/harfbuzz.toml
index 8b49aa73..45f2e32d 100644
--- a/recipes/harfbuzz.toml
+++ b/recipes/harfbuzz.toml
@@ -1,6 +1,7 @@
 # harfbuzz 14.2.1 — shaping de texto (lo usa pango). meson static. glib=enabled: GTK4 incluye <hb-glib.h>.
 name    = "harfbuzz"
 version = "14.2.1"
+license = "MIT"
 [source]
 tarball = "https://github.com/harfbuzz/harfbuzz/releases/download/14.2.1/harfbuzz-14.2.1.tar.xz"
 sha256  = "a54a5d8e9380a41fbb762ce367bcbf7704792dfca0d93f1bbca86c5a57902e0e"
diff --git a/recipes/incoming-cosmic/alsa-lib.toml b/recipes/incoming-cosmic/alsa-lib.toml
index 2a07f967..b59bda85 100644
--- a/recipes/incoming-cosmic/alsa-lib.toml
+++ b/recipes/incoming-cosmic/alsa-lib.toml
@@ -5,6 +5,7 @@
 # --disable-python: los bindings pyalsa exigen headers de python + no hacen falta. Sin deps externas.
 name    = "alsa-lib"
 version = "1.2.14"
+license = "LGPL-2.1-or-later"
 
 [source]
 tarball = "https://www.alsa-project.org/files/pub/lib/alsa-lib-1.2.14.tar.bz2"
diff --git a/recipes/incoming-cosmic/json-glib.toml b/recipes/incoming-cosmic/json-glib.toml
index 13218f35..17be3927 100644
--- a/recipes/incoming-cosmic/json-glib.toml
+++ b/recipes/incoming-cosmic/json-glib.toml
@@ -20,6 +20,7 @@
 # el corpus sólo tiene gettext-tiny.
 name    = "json-glib"
 version = "1.10.8"
+license = "LGPL-2.1-or-later"
 
 [source]
 tarball = "https://download.gnome.org/sources/json-glib/1.10/json-glib-1.10.8.tar.xz"
diff --git a/recipes/incoming-cosmic/pipewire.toml b/recipes/incoming-cosmic/pipewire.toml
index a66666ef..a25926be 100644
--- a/recipes/incoming-cosmic/pipewire.toml
+++ b/recipes/incoming-cosmic/pipewire.toml
@@ -54,6 +54,7 @@
 # Quitando la dep, meson saltea pw-top y el resto construye.
 name    = "pipewire"
 version = "1.2.7"
+license = "MIT"
 
 [source]
 tarball = "https://gitlab.freedesktop.org/pipewire/pipewire/-/archive/1.2.7/pipewire-1.2.7.tar.gz"
diff --git a/recipes/incoming-cosmic/wireplumber.toml b/recipes/incoming-cosmic/wireplumber.toml
index ab1cec75..3e9fdfeb 100644
--- a/recipes/incoming-cosmic/wireplumber.toml
+++ b/recipes/incoming-cosmic/wireplumber.toml
@@ -48,6 +48,7 @@
 # porque nadie carga `Wp-0.5` por typelib.
 name    = "wireplumber"
 version = "0.5.15"
+license = "MIT"
 
 [source]
 tarball = "https://gitlab.freedesktop.org/pipewire/wireplumber/-/archive/0.5.15/wireplumber-0.5.15.tar.gz"
diff --git a/recipes/incoming-cosmic/xdg-desktop-portal.toml b/recipes/incoming-cosmic/xdg-desktop-portal.toml
index aa7e37e6..4e950dad 100644
--- a/recipes/incoming-cosmic/xdg-desktop-portal.toml
+++ b/recipes/incoming-cosmic/xdg-desktop-portal.toml
@@ -29,6 +29,7 @@
 # `docbook-docs`/`pytest` — documentación y tests.
 name    = "xdg-desktop-portal"
 version = "1.18.4"
+license = "LGPL-2.1-or-later"
 
 [source]
 tarball = "https://github.com/flatpak/xdg-desktop-portal/releases/download/1.18.4/xdg-desktop-portal-1.18.4.tar.xz"
diff --git a/recipes/incoming-cosmic/xkeyboard-config.toml b/recipes/incoming-cosmic/xkeyboard-config.toml
index efc3c2f0..ea61893c 100644
--- a/recipes/incoming-cosmic/xkeyboard-config.toml
+++ b/recipes/incoming-cosmic/xkeyboard-config.toml
@@ -4,6 +4,7 @@
 # Meson, sin compilación real (instala datos + .pc). nls off (evita gettext); sin libxml2 (validación).
 name    = "xkeyboard-config"
 version = "2.44"
+license = "MIT"
 
 [source]
 tarball = "https://www.x.org/releases/individual/data/xkeyboard-config/xkeyboard-config-2.44.tar.xz"
diff --git a/recipes/incoming-cosmic/zlib-shared.toml b/recipes/incoming-cosmic/zlib-shared.toml
index 35b0de5c..e4d9acdc 100644
--- a/recipes/incoming-cosmic/zlib-shared.toml
+++ b/recipes/incoming-cosmic/zlib-shared.toml
@@ -17,6 +17,7 @@
 
 name    = "zlib-shared"
 version = "1.3.1"
+license = "Zlib"
 
 [source]
 tarball = "https://github.com/madler/zlib/releases/download/v1.3.1/zlib-1.3.1.tar.gz"
diff --git a/recipes/incoming-gnome-onda1/glib.toml b/recipes/incoming-gnome-onda1/glib.toml
index f300b261..dfff09b4 100644
--- a/recipes/incoming-gnome-onda1/glib.toml
+++ b/recipes/incoming-gnome-onda1/glib.toml
@@ -21,6 +21,7 @@
 # el consumidor estático encuentra el `.a` y el scanner encuentra el `.so`.
 name    = "glib"
 version = "2.88.1"
+license = "LGPL-2.1-or-later"
 
 [source]
 tarball = "https://download.gnome.org/sources/glib/2.88/glib-2.88.1.tar.xz"
diff --git a/recipes/incoming-gnome-onda1/gnome-desktop.toml b/recipes/incoming-gnome-onda1/gnome-desktop.toml
index e3b4fa81..ead05f9f 100644
--- a/recipes/incoming-gnome-onda1/gnome-desktop.toml
+++ b/recipes/incoming-gnome-onda1/gnome-desktop.toml
@@ -13,6 +13,7 @@
 # el logind/udev lo cubre arje). backend GL vía gtk4 (ya wayland-only, sin X11).
 name    = "gnome-desktop"
 version = "44.5"
+license = "GPL-2.0-or-later AND LGPL-2.0-or-later"
 
 [source]
 tarball = "https://download.gnome.org/sources/gnome-desktop/44/gnome-desktop-44.5.tar.xz"
diff --git a/recipes/incoming-gnome-onda1/gobject-introspection.toml b/recipes/incoming-gnome-onda1/gobject-introspection.toml
index 1de8dfbe..4f2a091c 100644
--- a/recipes/incoming-gnome-onda1/gobject-introspection.toml
+++ b/recipes/incoming-gnome-onda1/gobject-introspection.toml
@@ -14,6 +14,7 @@
 #      la primera — dejar como frontera medida, no como sellado asumido.
 name    = "gobject-introspection"
 version = "1.84.0"
+license = "LGPL-2.0-or-later AND GPL-2.0-or-later"
 
 [source]
 tarball = "https://download.gnome.org/sources/gobject-introspection/1.84/gobject-introspection-1.84.0.tar.xz"
diff --git a/recipes/incoming-gnome-onda1/gsettings-desktop-schemas.toml b/recipes/incoming-gnome-onda1/gsettings-desktop-schemas.toml
index 14c856f8..76df8499 100644
--- a/recipes/incoming-gnome-onda1/gsettings-desktop-schemas.toml
+++ b/recipes/incoming-gnome-onda1/gsettings-desktop-schemas.toml
@@ -6,6 +6,7 @@
 # lo pidieran vía typelib, se re-activa). Sin docs/tests.
 name    = "gsettings-desktop-schemas"
 version = "48.0"
+license = "LGPL-2.1-or-later"
 
 [source]
 tarball = "https://download.gnome.org/sources/gsettings-desktop-schemas/48/gsettings-desktop-schemas-48.0.tar.xz"
diff --git a/recipes/incoming-gnome-onda2/cairo-shared.toml b/recipes/incoming-gnome-onda2/cairo-shared.toml
index 962a0798..e18a4735 100644
--- a/recipes/incoming-gnome-onda2/cairo-shared.toml
+++ b/recipes/incoming-gnome-onda2/cairo-shared.toml
@@ -11,6 +11,7 @@
 # estado). expat canónico (--with-pic ⇒ se embebe). Ver [[gnome-introspection-dinamica]].
 name    = "cairo-shared"
 version = "1.18.4"
+license = "LGPL-2.1-only OR MPL-1.1"
 
 # Release ESTABLE de cairographics.org (hash fijo). El canónico usa el archive de GitLab, que NO es
 # determinista (recomprime al vuelo: cada fetch da otro sha256 ⇒ imposible de pinnear). El release
diff --git a/recipes/incoming-gnome-onda2/fontconfig-shared.toml b/recipes/incoming-gnome-onda2/fontconfig-shared.toml
index 2d0cbcd9..fb94611f 100644
--- a/recipes/incoming-gnome-onda2/fontconfig-shared.toml
+++ b/recipes/incoming-gnome-onda2/fontconfig-shared.toml
@@ -7,6 +7,7 @@
 # libpng.pc → zlib); sin ellos pkg-config no resuelve freetype2 y ./configure aborta (gotcha .pc transitivo).
 name    = "fontconfig-shared"
 version = "2.18.1"
+license = "MIT"
 
 [source]
 tarball = "https://gitlab.freedesktop.org/api/v4/projects/890/packages/generic/fontconfig/2.18.1/fontconfig-2.18.1.tar.xz"
diff --git a/recipes/incoming-gnome-onda2/freetype-shared.toml b/recipes/incoming-gnome-onda2/freetype-shared.toml
index 8643116d..dbde8863 100644
--- a/recipes/incoming-gnome-onda2/freetype-shared.toml
+++ b/recipes/incoming-gnome-onda2/freetype-shared.toml
@@ -11,6 +11,7 @@
 # no hay brotli sellado (sólo afecta fuentes WOFF2). zlib/png dinámicos vía las variantes -shared.
 name    = "freetype-shared"
 version = "2.14.3"
+license = "FTL OR GPL-2.0-or-later"
 
 [source]
 tarball = "https://download.savannah.gnu.org/releases/freetype/freetype-2.14.3.tar.xz"
diff --git a/recipes/incoming-gnome-onda2/gdk-pixbuf.toml b/recipes/incoming-gnome-onda2/gdk-pixbuf.toml
index 3c9f4480..124671a2 100644
--- a/recipes/incoming-gnome-onda2/gdk-pixbuf.toml
+++ b/recipes/incoming-gnome-onda2/gdk-pixbuf.toml
@@ -6,6 +6,7 @@
 # Mantengo el resto de flags de la corpus (leaf PNG-only, -Dgio_sniffing=false esquiva shared-mime-info).
 name    = "gdk-pixbuf"
 version = "2.44.6"
+license = "LGPL-2.1-or-later"
 
 [source]
 tarball = "https://download.gnome.org/sources/gdk-pixbuf/2.44/gdk-pixbuf-2.44.6.tar.xz"
diff --git a/recipes/incoming-gnome-onda2/glib.toml b/recipes/incoming-gnome-onda2/glib.toml
index f300b261..dfff09b4 100644
--- a/recipes/incoming-gnome-onda2/glib.toml
+++ b/recipes/incoming-gnome-onda2/glib.toml
@@ -21,6 +21,7 @@
 # el consumidor estático encuentra el `.a` y el scanner encuentra el `.so`.
 name    = "glib"
 version = "2.88.1"
+license = "LGPL-2.1-or-later"
 
 [source]
 tarball = "https://download.gnome.org/sources/glib/2.88/glib-2.88.1.tar.xz"
diff --git a/recipes/incoming-gnome-onda2/gobject-introspection.toml b/recipes/incoming-gnome-onda2/gobject-introspection.toml
index 1de8dfbe..4f2a091c 100644
--- a/recipes/incoming-gnome-onda2/gobject-introspection.toml
+++ b/recipes/incoming-gnome-onda2/gobject-introspection.toml
@@ -14,6 +14,7 @@
 #      la primera — dejar como frontera medida, no como sellado asumido.
 name    = "gobject-introspection"
 version = "1.84.0"
+license = "LGPL-2.0-or-later AND GPL-2.0-or-later"
 
 [source]
 tarball = "https://download.gnome.org/sources/gobject-introspection/1.84/gobject-introspection-1.84.0.tar.xz"
diff --git a/recipes/incoming-gnome-onda2/graphene.toml b/recipes/incoming-gnome-onda2/graphene.toml
index 0fd8e350..6dc6cc3d 100644
--- a/recipes/incoming-gnome-onda2/graphene.toml
+++ b/recipes/incoming-gnome-onda2/graphene.toml
@@ -9,6 +9,7 @@
 # con la estática.
 name    = "graphene"
 version = "1.10.8"
+license = "MIT"
 
 [source]
 tarball = "https://download.gnome.org/sources/graphene/1.10/graphene-1.10.8.tar.xz"
diff --git a/recipes/incoming-gnome-onda2/gtk4.toml b/recipes/incoming-gnome-onda2/gtk4.toml
index 3c4e1046..98c95100 100644
--- a/recipes/incoming-gnome-onda2/gtk4.toml
+++ b/recipes/incoming-gnome-onda2/gtk4.toml
@@ -15,6 +15,7 @@
 # símbolo/estado duplicado, escalar la lib culpable a su propia isla. Esperar walls en el worker.
 name    = "gtk4"
 version = "4.18.6"
+license = "LGPL-2.1-or-later"
 
 [source]
 tarball = "https://download.gnome.org/sources/gtk/4.18/gtk-4.18.6.tar.xz"
diff --git a/recipes/incoming-gnome-onda2/harfbuzz.toml b/recipes/incoming-gnome-onda2/harfbuzz.toml
index 2e6552c5..039bb6e6 100644
--- a/recipes/incoming-gnome-onda2/harfbuzz.toml
+++ b/recipes/incoming-gnome-onda2/harfbuzz.toml
@@ -8,6 +8,7 @@
 # coherente). Sombrea el corpus SÓLO para la cola GNOME.
 name    = "harfbuzz"
 version = "14.2.1"
+license = "MIT"
 
 [source]
 tarball = "https://github.com/harfbuzz/harfbuzz/releases/download/14.2.1/harfbuzz-14.2.1.tar.xz"
diff --git a/recipes/incoming-gnome-onda2/libjpeg-turbo-shared.toml b/recipes/incoming-gnome-onda2/libjpeg-turbo-shared.toml
index 44f74cee..e3dc03a6 100644
--- a/recipes/incoming-gnome-onda2/libjpeg-turbo-shared.toml
+++ b/recipes/incoming-gnome-onda2/libjpeg-turbo-shared.toml
@@ -4,6 +4,7 @@
 # zlib-shared/bzip2-shared. Nombre distinto para no colisionar con el canónico. gwenview lo linkea.
 name    = "libjpeg-turbo-shared"
 version = "3.1.3"
+license = "IJG AND BSD-3-Clause AND Zlib"
 
 [source]
 tarball = "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/3.1.3/libjpeg-turbo-3.1.3.tar.gz"
diff --git a/recipes/incoming-gnome-onda2/libpng-shared.toml b/recipes/incoming-gnome-onda2/libpng-shared.toml
index cb9c9ad0..ec37536d 100644
--- a/recipes/incoming-gnome-onda2/libpng-shared.toml
+++ b/recipes/incoming-gnome-onda2/libpng-shared.toml
@@ -3,6 +3,7 @@
 # zlib-shared/xz-shared: autotools --enable-shared, zig-cc, contra zlib-shared (libz.so). gwenview linkea.
 name    = "libpng-shared"
 version = "1.6.58"
+license = "Libpng"
 
 [source]
 tarball = "https://downloads.sourceforge.net/libpng/libpng-1.6.58.tar.gz"
diff --git a/recipes/incoming-gnome-onda2/libtiff-shared.toml b/recipes/incoming-gnome-onda2/libtiff-shared.toml
index 1c723b4f..6e84552e 100644
--- a/recipes/incoming-gnome-onda2/libtiff-shared.toml
+++ b/recipes/incoming-gnome-onda2/libtiff-shared.toml
@@ -4,6 +4,7 @@
 # resuelve ('undefined inflate'). La .so lo resuelve por NEEDED. Ver [[gnome-introspection-dinamica]].
 name    = "libtiff-shared"
 version = "4.7.0"
+license = "libtiff"
 
 [source]
 tarball = "https://download.osgeo.org/libtiff/tiff-4.7.0.tar.gz"
diff --git a/recipes/incoming-gnome-onda2/pango.toml b/recipes/incoming-gnome-onda2/pango.toml
index 9a80bbcb..da518662 100644
--- a/recipes/incoming-gnome-onda2/pango.toml
+++ b/recipes/incoming-gnome-onda2/pango.toml
@@ -11,6 +11,7 @@
 # escala cairo/etc. a su propia isla dinámica. NO persigo el cierre dinámico transitivo completo a ciegas.
 name    = "pango"
 version = "1.57.1"
+license = "LGPL-2.0-or-later"
 
 [source]
 tarball = "https://download.gnome.org/sources/pango/1.57/pango-1.57.1.tar.xz"
diff --git a/recipes/incoming-gnome-onda2/zlib-shared.toml b/recipes/incoming-gnome-onda2/zlib-shared.toml
index 35b0de5c..e4d9acdc 100644
--- a/recipes/incoming-gnome-onda2/zlib-shared.toml
+++ b/recipes/incoming-gnome-onda2/zlib-shared.toml
@@ -17,6 +17,7 @@
 
 name    = "zlib-shared"
 version = "1.3.1"
+license = "Zlib"
 
 [source]
 tarball = "https://github.com/madler/zlib/releases/download/v1.3.1/zlib-1.3.1.tar.gz"
diff --git a/recipes/incoming-gnome/accountsservice.toml b/recipes/incoming-gnome/accountsservice.toml
index 9371c7bf..d067334c 100644
--- a/recipes/incoming-gnome/accountsservice.toml
+++ b/recipes/incoming-gnome/accountsservice.toml
@@ -80,6 +80,7 @@
 # deps. Se encuentra arrancando, no compilando.
 name    = "accountsservice"
 version = "23.13.9"
+license = "GPL-3.0-or-later"
 
 [source]
 tarball = "https://www.freedesktop.org/software/accountsservice/accountsservice-23.13.9.tar.xz"
diff --git a/recipes/incoming-gnome/adwaita-icon-theme.toml b/recipes/incoming-gnome/adwaita-icon-theme.toml
index 8f88d344..55c230b1 100644
--- a/recipes/incoming-gnome/adwaita-icon-theme.toml
+++ b/recipes/incoming-gnome/adwaita-icon-theme.toml
@@ -36,6 +36,7 @@
 # simbólicos y cursores) y la más liviana en tiempo: no hay un solo compilador involucrado.
 name    = "adwaita-icon-theme"
 version = "48.1"
+license = "LGPL-3.0-or-later OR CC-BY-SA-3.0"
 
 [source]
 tarball = "https://download.gnome.org/sources/adwaita-icon-theme/48/adwaita-icon-theme-48.1.tar.xz"
diff --git a/recipes/incoming-gnome/alsa-lib.toml b/recipes/incoming-gnome/alsa-lib.toml
index 2a07f967..b59bda85 100644
--- a/recipes/incoming-gnome/alsa-lib.toml
+++ b/recipes/incoming-gnome/alsa-lib.toml
@@ -5,6 +5,7 @@
 # --disable-python: los bindings pyalsa exigen headers de python + no hacen falta. Sin deps externas.
 name    = "alsa-lib"
 version = "1.2.14"
+license = "LGPL-2.1-or-later"
 
 [source]
 tarball = "https://www.alsa-project.org/files/pub/lib/alsa-lib-1.2.14.tar.bz2"
diff --git a/recipes/incoming-gnome/at-spi2-core.toml b/recipes/incoming-gnome/at-spi2-core.toml
index a58f838d..76bf446d 100644
--- a/recipes/incoming-gnome/at-spi2-core.toml
+++ b/recipes/incoming-gnome/at-spi2-core.toml
@@ -15,6 +15,7 @@
 # ISLA DINÁMICA: produce .so con GObject ⇒ glib dinámica, un solo registro de GType.
 name    = "at-spi2-core"
 version = "2.58.7"
+license = "LGPL-2.1-or-later"
 
 [source]
 tarball = "https://download.gnome.org/sources/at-spi2-core/2.58/at-spi2-core-2.58.7.tar.xz"
diff --git a/recipes/incoming-gnome/cairo-shared.toml b/recipes/incoming-gnome/cairo-shared.toml
index 962a0798..e18a4735 100644
--- a/recipes/incoming-gnome/cairo-shared.toml
+++ b/recipes/incoming-gnome/cairo-shared.toml
@@ -11,6 +11,7 @@
 # estado). expat canónico (--with-pic ⇒ se embebe). Ver [[gnome-introspection-dinamica]].
 name    = "cairo-shared"
 version = "1.18.4"
+license = "LGPL-2.1-only OR MPL-1.1"
 
 # Release ESTABLE de cairographics.org (hash fijo). El canónico usa el archive de GitLab, que NO es
 # determinista (recomprime al vuelo: cada fetch da otro sha256 ⇒ imposible de pinnear). El release
diff --git a/recipes/incoming-gnome/colord.toml b/recipes/incoming-gnome/colord.toml
index 552ead63..4c406b9c 100644
--- a/recipes/incoming-gnome/colord.toml
+++ b/recipes/incoming-gnome/colord.toml
@@ -38,6 +38,7 @@
 # introspection=false: mutter la enlaza en C.
 name    = "colord"
 version = "1.4.7"
+license = "GPL-2.0-or-later AND LGPL-2.1-or-later"
 
 [source]
 tarball = "https://www.freedesktop.org/software/colord/releases/colord-1.4.7.tar.xz"
diff --git a/recipes/incoming-gnome/fontconfig-shared.toml b/recipes/incoming-gnome/fontconfig-shared.toml
index 2d0cbcd9..fb94611f 100644
--- a/recipes/incoming-gnome/fontconfig-shared.toml
+++ b/recipes/incoming-gnome/fontconfig-shared.toml
@@ -7,6 +7,7 @@
 # libpng.pc → zlib); sin ellos pkg-config no resuelve freetype2 y ./configure aborta (gotcha .pc transitivo).
 name    = "fontconfig-shared"
 version = "2.18.1"
+license = "MIT"
 
 [source]
 tarball = "https://gitlab.freedesktop.org/api/v4/projects/890/packages/generic/fontconfig/2.18.1/fontconfig-2.18.1.tar.xz"
diff --git a/recipes/incoming-gnome/freetype-shared.toml b/recipes/incoming-gnome/freetype-shared.toml
index 8643116d..dbde8863 100644
--- a/recipes/incoming-gnome/freetype-shared.toml
+++ b/recipes/incoming-gnome/freetype-shared.toml
@@ -11,6 +11,7 @@
 # no hay brotli sellado (sólo afecta fuentes WOFF2). zlib/png dinámicos vía las variantes -shared.
 name    = "freetype-shared"
 version = "2.14.3"
+license = "FTL OR GPL-2.0-or-later"
 
 [source]
 tarball = "https://download.savannah.gnu.org/releases/freetype/freetype-2.14.3.tar.xz"
diff --git a/recipes/incoming-gnome/gdk-pixbuf.toml b/recipes/incoming-gnome/gdk-pixbuf.toml
index 3c9f4480..124671a2 100644
--- a/recipes/incoming-gnome/gdk-pixbuf.toml
+++ b/recipes/incoming-gnome/gdk-pixbuf.toml
@@ -6,6 +6,7 @@
 # Mantengo el resto de flags de la corpus (leaf PNG-only, -Dgio_sniffing=false esquiva shared-mime-info).
 name    = "gdk-pixbuf"
 version = "2.44.6"
+license = "LGPL-2.1-or-later"
 
 [source]
 tarball = "https://download.gnome.org/sources/gdk-pixbuf/2.44/gdk-pixbuf-2.44.6.tar.xz"
diff --git a/recipes/incoming-gnome/geoclue.toml b/recipes/incoming-gnome/geoclue.toml
index 989ee6cb..09849fb1 100644
--- a/recipes/incoming-gnome/geoclue.toml
+++ b/recipes/incoming-gnome/geoclue.toml
@@ -24,6 +24,7 @@
 # FUENTE: archivo de gitlab.freedesktop.org, como cairo/libdrm/libinput/pixman/modemmanager.
 name    = "geoclue"
 version = "2.8.2"
+license = "GPL-2.0-or-later"
 
 [source]
 tarball = "https://gitlab.freedesktop.org/geoclue/geoclue/-/archive/2.8.2/geoclue-2.8.2.tar.gz"
diff --git a/recipes/incoming-gnome/glib.toml b/recipes/incoming-gnome/glib.toml
index f300b261..dfff09b4 100644
--- a/recipes/incoming-gnome/glib.toml
+++ b/recipes/incoming-gnome/glib.toml
@@ -21,6 +21,7 @@
 # el consumidor estático encuentra el `.a` y el scanner encuentra el `.so`.
 name    = "glib"
 version = "2.88.1"
+license = "LGPL-2.1-or-later"
 
 [source]
 tarball = "https://download.gnome.org/sources/glib/2.88/glib-2.88.1.tar.xz"
diff --git a/recipes/incoming-gnome/gnome-desktop.toml b/recipes/incoming-gnome/gnome-desktop.toml
index 3a077068..91ac77d4 100644
--- a/recipes/incoming-gnome/gnome-desktop.toml
+++ b/recipes/incoming-gnome/gnome-desktop.toml
@@ -36,6 +36,7 @@
 # borrarlos de `sources:` es justo lo que impediría que se declaren.
 name    = "gnome-desktop"
 version = "44.5"
+license = "GPL-2.0-or-later AND LGPL-2.0-or-later"
 
 [source]
 tarball = "https://download.gnome.org/sources/gnome-desktop/44/gnome-desktop-44.5.tar.xz"
diff --git a/recipes/incoming-gnome/gnome-session.toml b/recipes/incoming-gnome/gnome-session.toml
index 5f57e246..6a0c06be 100644
--- a/recipes/incoming-gnome/gnome-session.toml
+++ b/recipes/incoming-gnome/gnome-session.toml
@@ -7,6 +7,7 @@
 # gnome-session usa gtk4 para el diálogo de fallo ("failwhale") y json-glib para el perfil de sesión.
 name    = "gnome-session"
 version = "48.0"
+license = "GPL-2.0-or-later"
 
 [source]
 tarball = "https://download.gnome.org/sources/gnome-session/48/gnome-session-48.0.tar.xz"
diff --git a/recipes/incoming-gnome/gnome-shell.toml b/recipes/incoming-gnome/gnome-shell.toml
index d8f348d6..1d9d69d3 100644
--- a/recipes/incoming-gnome/gnome-shell.toml
+++ b/recipes/incoming-gnome/gnome-shell.toml
@@ -55,6 +55,7 @@
 # con `relocation R_X86_64_32 ... recompile with -fPIC` — es el muro que ya frenó a mutter.
 name    = "gnome-shell"
 version = "48.8"
+license = "GPL-2.0-or-later"
 
 [source]
 tarball = "https://download.gnome.org/sources/gnome-shell/48/gnome-shell-48.8.tar.xz"
diff --git a/recipes/incoming-gnome/gobject-introspection.toml b/recipes/incoming-gnome/gobject-introspection.toml
index 1de8dfbe..4f2a091c 100644
--- a/recipes/incoming-gnome/gobject-introspection.toml
+++ b/recipes/incoming-gnome/gobject-introspection.toml
@@ -14,6 +14,7 @@
 #      la primera — dejar como frontera medida, no como sellado asumido.
 name    = "gobject-introspection"
 version = "1.84.0"
+license = "LGPL-2.0-or-later AND GPL-2.0-or-later"
 
 [source]
 tarball = "https://download.gnome.org/sources/gobject-introspection/1.84/gobject-introspection-1.84.0.tar.xz"
diff --git a/recipes/incoming-gnome/graphene.toml b/recipes/incoming-gnome/graphene.toml
index 0fd8e350..6dc6cc3d 100644
--- a/recipes/incoming-gnome/graphene.toml
+++ b/recipes/incoming-gnome/graphene.toml
@@ -9,6 +9,7 @@
 # con la estática.
 name    = "graphene"
 version = "1.10.8"
+license = "MIT"
 
 [source]
 tarball = "https://download.gnome.org/sources/graphene/1.10/graphene-1.10.8.tar.xz"
diff --git a/recipes/incoming-gnome/gsettings-desktop-schemas.toml b/recipes/incoming-gnome/gsettings-desktop-schemas.toml
index bad0f4c8..fc52c71d 100644
--- a/recipes/incoming-gnome/gsettings-desktop-schemas.toml
+++ b/recipes/incoming-gnome/gsettings-desktop-schemas.toml
@@ -9,6 +9,7 @@
 # reconstruir (gnome-desktop). Sin docs/tests.
 name    = "gsettings-desktop-schemas"
 version = "48.0"
+license = "LGPL-2.1-or-later"
 
 [source]
 tarball = "https://download.gnome.org/sources/gsettings-desktop-schemas/48/gsettings-desktop-schemas-48.0.tar.xz"
diff --git a/recipes/incoming-gnome/gtk4.toml b/recipes/incoming-gnome/gtk4.toml
index 3c4e1046..98c95100 100644
--- a/recipes/incoming-gnome/gtk4.toml
+++ b/recipes/incoming-gnome/gtk4.toml
@@ -15,6 +15,7 @@
 # símbolo/estado duplicado, escalar la lib culpable a su propia isla. Esperar walls en el worker.
 name    = "gtk4"
 version = "4.18.6"
+license = "LGPL-2.1-or-later"
 
 [source]
 tarball = "https://download.gnome.org/sources/gtk/4.18/gtk-4.18.6.tar.xz"
diff --git a/recipes/incoming-gnome/harfbuzz.toml b/recipes/incoming-gnome/harfbuzz.toml
index 2e6552c5..039bb6e6 100644
--- a/recipes/incoming-gnome/harfbuzz.toml
+++ b/recipes/incoming-gnome/harfbuzz.toml
@@ -8,6 +8,7 @@
 # coherente). Sombrea el corpus SÓLO para la cola GNOME.
 name    = "harfbuzz"
 version = "14.2.1"
+license = "MIT"
 
 [source]
 tarball = "https://github.com/harfbuzz/harfbuzz/releases/download/14.2.1/harfbuzz-14.2.1.tar.xz"
diff --git a/recipes/incoming-gnome/iso-codes.toml b/recipes/incoming-gnome/iso-codes.toml
index 9a0d5506..469b6672 100644
--- a/recipes/incoming-gnome/iso-codes.toml
+++ b/recipes/incoming-gnome/iso-codes.toml
@@ -17,6 +17,7 @@
 # corpus, se borra el `for` del configure y vuelven los .mo.
 name    = "iso-codes"
 version = "4.20.1"
+license = "LGPL-2.1-or-later"
 
 [source]
 tarball = "http://deb.debian.org/debian/pool/main/i/iso-codes/iso-codes_4.20.1.orig.tar.xz"
diff --git a/recipes/incoming-gnome/json-glib.toml b/recipes/incoming-gnome/json-glib.toml
index a8d6596d..725a8cd9 100644
--- a/recipes/incoming-gnome/json-glib.toml
+++ b/recipes/incoming-gnome/json-glib.toml
@@ -24,6 +24,7 @@
 # destrabar mutter. Ver [[gnome-introspection-dinamica]].
 name    = "json-glib"
 version = "1.10.8"
+license = "LGPL-2.1-or-later"
 
 [source]
 tarball = "https://download.gnome.org/sources/json-glib/1.10/json-glib-1.10.8.tar.xz"
diff --git a/recipes/incoming-gnome/libadwaita.toml b/recipes/incoming-gnome/libadwaita.toml
index e49e6ff1..39a03ff2 100644
--- a/recipes/incoming-gnome/libadwaita.toml
+++ b/recipes/incoming-gnome/libadwaita.toml
@@ -31,6 +31,7 @@
 # Control: libepoxy=0 (embebe bien) · fontconfig=1122 (rompe el link). Ver [[gnome-introspection-dinamica]].
 name    = "libadwaita"
 version = "1.7.6"
+license = "LGPL-2.1-or-later"
 
 [source]
 tarball = "https://download.gnome.org/sources/libadwaita/1.7/libadwaita-1.7.6.tar.xz"
diff --git a/recipes/incoming-gnome/libjpeg-turbo-shared.toml b/recipes/incoming-gnome/libjpeg-turbo-shared.toml
index 44f74cee..e3dc03a6 100644
--- a/recipes/incoming-gnome/libjpeg-turbo-shared.toml
+++ b/recipes/incoming-gnome/libjpeg-turbo-shared.toml
@@ -4,6 +4,7 @@
 # zlib-shared/bzip2-shared. Nombre distinto para no colisionar con el canónico. gwenview lo linkea.
 name    = "libjpeg-turbo-shared"
 version = "3.1.3"
+license = "IJG AND BSD-3-Clause AND Zlib"
 
 [source]
 tarball = "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/3.1.3/libjpeg-turbo-3.1.3.tar.gz"
diff --git a/recipes/incoming-gnome/libpng-shared.toml b/recipes/incoming-gnome/libpng-shared.toml
index cb9c9ad0..ec37536d 100644
--- a/recipes/incoming-gnome/libpng-shared.toml
+++ b/recipes/incoming-gnome/libpng-shared.toml
@@ -3,6 +3,7 @@
 # zlib-shared/xz-shared: autotools --enable-shared, zig-cc, contra zlib-shared (libz.so). gwenview linkea.
 name    = "libpng-shared"
 version = "1.6.58"
+license = "Libpng"
 
 [source]
 tarball = "https://downloads.sourceforge.net/libpng/libpng-1.6.58.tar.gz"
diff --git a/recipes/incoming-gnome/libseccomp.toml b/recipes/incoming-gnome/libseccomp.toml
index 9c2c8237..d7a7c39a 100644
--- a/recipes/incoming-gnome/libseccomp.toml
+++ b/recipes/incoming-gnome/libseccomp.toml
@@ -7,6 +7,7 @@
 # --disable-python: los bindings piden cython3; el escritorio consume la lib C, no el módulo.
 name    = "libseccomp"
 version = "2.6.1"
+license = "LGPL-2.1-only"
 
 [source]
 tarball = "https://github.com/seccomp/libseccomp/releases/download/v2.6.1/libseccomp-2.6.1.tar.gz"
diff --git a/recipes/incoming-gnome/libsoup.toml b/recipes/incoming-gnome/libsoup.toml
index 812f826f..9e7220cc 100644
--- a/recipes/incoming-gnome/libsoup.toml
+++ b/recipes/incoming-gnome/libsoup.toml
@@ -29,6 +29,7 @@
 # son PIC y no entran en un .so (es el muro que frenó a mutter contra freetype).
 name    = "libsoup"
 version = "3.6.6"
+license = "LGPL-2.0-or-later"
 
 [source]
 tarball = "https://download.gnome.org/sources/libsoup/3.6/libsoup-3.6.6.tar.xz"
diff --git a/recipes/incoming-gnome/libtiff-shared.toml b/recipes/incoming-gnome/libtiff-shared.toml
index 1c723b4f..6e84552e 100644
--- a/recipes/incoming-gnome/libtiff-shared.toml
+++ b/recipes/incoming-gnome/libtiff-shared.toml
@@ -4,6 +4,7 @@
 # resuelve ('undefined inflate'). La .so lo resuelve por NEEDED. Ver [[gnome-introspection-dinamica]].
 name    = "libtiff-shared"
 version = "4.7.0"
+license = "libtiff"
 
 [source]
 tarball = "https://download.osgeo.org/libtiff/tiff-4.7.0.tar.gz"
diff --git a/recipes/incoming-gnome/libxml2-shared.toml b/recipes/incoming-gnome/libxml2-shared.toml
index 1d8d7675..302aa9fa 100644
--- a/recipes/incoming-gnome/libxml2-shared.toml
+++ b/recipes/incoming-gnome/libxml2-shared.toml
@@ -13,6 +13,7 @@
 # Se mantienen --without-{python,lzma,zlib} de la canónica: eds parsea XML, no comprime.
 name    = "libxml2-shared"
 version = "2.13.9"
+license = "MIT"
 
 [source]
 tarball = "https://download.gnome.org/sources/libxml2/2.13/libxml2-2.13.9.tar.xz"
diff --git a/recipes/incoming-gnome/libyaml-shared.toml b/recipes/incoming-gnome/libyaml-shared.toml
index 5dae804d..c6cbd1a7 100644
--- a/recipes/incoming-gnome/libyaml-shared.toml
+++ b/recipes/incoming-gnome/libyaml-shared.toml
@@ -17,6 +17,7 @@
 # Mismo tarball y misma versión que la canónica: no se abre skew entre el mundo estático y el dinámico.
 name    = "libyaml-shared"
 version = "0.2.5"
+license = "MIT"
 
 [source]
 tarball = "https://github.com/yaml/libyaml/releases/download/0.2.5/yaml-0.2.5.tar.gz"
diff --git a/recipes/incoming-gnome/mutter.toml b/recipes/incoming-gnome/mutter.toml
index 3c22ccdd..16ed22e8 100644
--- a/recipes/incoming-gnome/mutter.toml
+++ b/recipes/incoming-gnome/mutter.toml
@@ -6,6 +6,7 @@
 # Feature-minimal: tests/docs/profiler off. El backend nativo (KMS/DRM+libinput) es el de la distro.
 name    = "mutter"
 version = "48.8"
+license = "GPL-2.0-or-later"
 
 [source]
 tarball = "https://download.gnome.org/sources/mutter/48/mutter-48.8.tar.xz"
diff --git a/recipes/incoming-gnome/pango.toml b/recipes/incoming-gnome/pango.toml
index 9a80bbcb..da518662 100644
--- a/recipes/incoming-gnome/pango.toml
+++ b/recipes/incoming-gnome/pango.toml
@@ -11,6 +11,7 @@
 # escala cairo/etc. a su propia isla dinámica. NO persigo el cierre dinámico transitivo completo a ciegas.
 name    = "pango"
 version = "1.57.1"
+license = "LGPL-2.0-or-later"
 
 [source]
 tarball = "https://download.gnome.org/sources/pango/1.57/pango-1.57.1.tar.xz"
diff --git a/recipes/incoming-gnome/pipewire.toml b/recipes/incoming-gnome/pipewire.toml
index a66666ef..a25926be 100644
--- a/recipes/incoming-gnome/pipewire.toml
+++ b/recipes/incoming-gnome/pipewire.toml
@@ -54,6 +54,7 @@
 # Quitando la dep, meson saltea pw-top y el resto construye.
 name    = "pipewire"
 version = "1.2.7"
+license = "MIT"
 
 [source]
 tarball = "https://gitlab.freedesktop.org/pipewire/pipewire/-/archive/1.2.7/pipewire-1.2.7.tar.gz"
diff --git a/recipes/incoming-gnome/wireplumber.toml b/recipes/incoming-gnome/wireplumber.toml
index fdb92faf..77616609 100644
--- a/recipes/incoming-gnome/wireplumber.toml
+++ b/recipes/incoming-gnome/wireplumber.toml
@@ -43,6 +43,7 @@
 # el protocolo de PulseAudio, no con wireplumber por GObject.
 name    = "wireplumber"
 version = "0.5.15"
+license = "MIT"
 
 [source]
 tarball = "https://gitlab.freedesktop.org/pipewire/wireplumber/-/archive/0.5.15/wireplumber-0.5.15.tar.gz"
diff --git a/recipes/incoming-gnome/xdg-desktop-portal-gnome.toml b/recipes/incoming-gnome/xdg-desktop-portal-gnome.toml
index 6c96f2f8..9c89bc2b 100644
--- a/recipes/incoming-gnome/xdg-desktop-portal-gnome.toml
+++ b/recipes/incoming-gnome/xdg-desktop-portal-gnome.toml
@@ -5,6 +5,7 @@
 # Feature-minimal: docs/tests off. GTK4 + libadwaita para la UI de los diálogos. Wayland-only.
 name    = "xdg-desktop-portal-gnome"
 version = "48.0"
+license = "LGPL-2.1-or-later"
 
 [source]
 tarball = "https://download.gnome.org/sources/xdg-desktop-portal-gnome/48/xdg-desktop-portal-gnome-48.0.tar.xz"
diff --git a/recipes/incoming-gnome/xdg-desktop-portal.toml b/recipes/incoming-gnome/xdg-desktop-portal.toml
index a69641a7..afc2a3c9 100644
--- a/recipes/incoming-gnome/xdg-desktop-portal.toml
+++ b/recipes/incoming-gnome/xdg-desktop-portal.toml
@@ -56,6 +56,7 @@
 # `docbook-docs`/`pytest` — documentación y tests.
 name    = "xdg-desktop-portal"
 version = "1.19.4"
+license = "LGPL-2.1-or-later"
 
 [source]
 tarball = "https://github.com/flatpak/xdg-desktop-portal/releases/download/1.19.4/xdg-desktop-portal-1.19.4.tar.xz"
diff --git a/recipes/incoming-gnome/xkeyboard-config.toml b/recipes/incoming-gnome/xkeyboard-config.toml
index efc3c2f0..ea61893c 100644
--- a/recipes/incoming-gnome/xkeyboard-config.toml
+++ b/recipes/incoming-gnome/xkeyboard-config.toml
@@ -4,6 +4,7 @@
 # Meson, sin compilación real (instala datos + .pc). nls off (evita gettext); sin libxml2 (validación).
 name    = "xkeyboard-config"
 version = "2.44"
+license = "MIT"
 
 [source]
 tarball = "https://www.x.org/releases/individual/data/xkeyboard-config/xkeyboard-config-2.44.tar.xz"
diff --git a/recipes/incoming-gnome/zlib-shared.toml b/recipes/incoming-gnome/zlib-shared.toml
index 35b0de5c..e4d9acdc 100644
--- a/recipes/incoming-gnome/zlib-shared.toml
+++ b/recipes/incoming-gnome/zlib-shared.toml
@@ -17,6 +17,7 @@
 
 name    = "zlib-shared"
 version = "1.3.1"
+license = "Zlib"
 
 [source]
 tarball = "https://github.com/madler/zlib/releases/download/v1.3.1/zlib-1.3.1.tar.gz"
diff --git a/recipes/incoming-kde/alsa-lib.toml b/recipes/incoming-kde/alsa-lib.toml
index 2a07f967..b59bda85 100644
--- a/recipes/incoming-kde/alsa-lib.toml
+++ b/recipes/incoming-kde/alsa-lib.toml
@@ -5,6 +5,7 @@
 # --disable-python: los bindings pyalsa exigen headers de python + no hacen falta. Sin deps externas.
 name    = "alsa-lib"
 version = "1.2.14"
+license = "LGPL-2.1-or-later"
 
 [source]
 tarball = "https://www.alsa-project.org/files/pub/lib/alsa-lib-1.2.14.tar.bz2"
diff --git a/recipes/incoming-kde/ark.toml b/recipes/incoming-kde/ark.toml
index bad5575b..e78a9113 100644
--- a/recipes/incoming-kde/ark.toml
+++ b/recipes/incoming-kde/ark.toml
@@ -4,6 +4,7 @@
 # cmake instalado. KF6DocTools se degrada a opcional (evita docbook).
 name    = "ark"
 version = "25.04.3"
+license = "GPL-2.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/release-service/25.04.3/src/ark-25.04.3.tar.xz"
diff --git a/recipes/incoming-kde/attica.toml b/recipes/incoming-kde/attica.toml
index 3b1de193..82bf3a0e 100644
--- a/recipes/incoming-kde/attica.toml
+++ b/recipes/incoming-kde/attica.toml
@@ -4,6 +4,7 @@
 # CMakeLists: solo Qt6 Core+Network. NO tiene option(WITH_X11).
 name    = "attica"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/attica-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/breeze-icons.toml b/recipes/incoming-kde/breeze-icons.toml
index 3420cde8..ecc476f3 100644
--- a/recipes/incoming-kde/breeze-icons.toml
+++ b/recipes/incoming-kde/breeze-icons.toml
@@ -2,6 +2,7 @@
 # not found" y el escritorio se ve sin iconos. CMake/ECM; SKIP_SANDBOX + iconos SVG (no rasteriza).
 name    = "breeze-icons"
 version = "6.27.0"
+license = "LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/breeze-icons-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/breeze.toml b/recipes/incoming-kde/breeze.toml
index 411b62e6..85621f71 100644
--- a/recipes/incoming-kde/breeze.toml
+++ b/recipes/incoming-kde/breeze.toml
@@ -3,6 +3,7 @@
 # (dynamic, wrapper zig). Wayland-puro: sin dep X11-session pesada. -DWITH_WALLPAPERS=OFF (opcional, pesado).
 name    = "breeze"
 version = "6.7.2"
+license = "GPL-2.0-or-later"
 [source]
 tarball = "https://download.kde.org/stable/plasma/6.7.2/breeze-6.7.2.tar.xz"
 sha256  = "80818bf6382a96e1a45c755f983d98357c4f7163f80cb8217897df15f8a02785"
diff --git a/recipes/incoming-kde/bzip2-shared.toml b/recipes/incoming-kde/bzip2-shared.toml
index 78b26aab..0b2bff3f 100644
--- a/recipes/incoming-kde/bzip2-shared.toml
+++ b/recipes/incoming-kde/bzip2-shared.toml
@@ -6,6 +6,7 @@
 # tarball (ya trae -fpic -fPIC, soname libbz2.so.1.0). Nombre distinto para no colisionar con el canónico.
 name    = "bzip2-shared"
 version = "1.0.8"
+license = "bzip2-1.0.6"
 
 [source]
 tarball = "https://sourceware.org/pub/bzip2/bzip2-1.0.8.tar.gz"
diff --git a/recipes/incoming-kde/dolphin.toml b/recipes/incoming-kde/dolphin.toml
index dc8c0334..fce56ab9 100644
--- a/recipes/incoming-kde/dolphin.toml
+++ b/recipes/incoming-kde/dolphin.toml
@@ -2,6 +2,7 @@
 # Patrón KF6 app. baloo (búsqueda de archivos → lmdb) queda opcional/ausente (degrada sin search).
 name    = "dolphin"
 version = "25.04.3"
+license = "GPL-2.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/release-service/25.04.3/src/dolphin-25.04.3.tar.xz"
diff --git a/recipes/incoming-kde/fontconfig-shared.toml b/recipes/incoming-kde/fontconfig-shared.toml
index 2d0cbcd9..fb94611f 100644
--- a/recipes/incoming-kde/fontconfig-shared.toml
+++ b/recipes/incoming-kde/fontconfig-shared.toml
@@ -7,6 +7,7 @@
 # libpng.pc → zlib); sin ellos pkg-config no resuelve freetype2 y ./configure aborta (gotcha .pc transitivo).
 name    = "fontconfig-shared"
 version = "2.18.1"
+license = "MIT"
 
 [source]
 tarball = "https://gitlab.freedesktop.org/api/v4/projects/890/packages/generic/fontconfig/2.18.1/fontconfig-2.18.1.tar.xz"
diff --git a/recipes/incoming-kde/freetype-shared.toml b/recipes/incoming-kde/freetype-shared.toml
index 8643116d..dbde8863 100644
--- a/recipes/incoming-kde/freetype-shared.toml
+++ b/recipes/incoming-kde/freetype-shared.toml
@@ -11,6 +11,7 @@
 # no hay brotli sellado (sólo afecta fuentes WOFF2). zlib/png dinámicos vía las variantes -shared.
 name    = "freetype-shared"
 version = "2.14.3"
+license = "FTL OR GPL-2.0-or-later"
 
 [source]
 tarball = "https://download.savannah.gnu.org/releases/freetype/freetype-2.14.3.tar.xz"
diff --git a/recipes/incoming-kde/kactivitymanagerd.toml b/recipes/incoming-kde/kactivitymanagerd.toml
index 815c7a2e..6119c615 100644
--- a/recipes/incoming-kde/kactivitymanagerd.toml
+++ b/recipes/incoming-kde/kactivitymanagerd.toml
@@ -3,6 +3,7 @@
 # zig, ECM). Usa Qt6Sql (DB de actividades sqlite) + PlasmaActivities + KIO.
 name    = "kactivitymanagerd"
 version = "6.7.2"
+license = "GPL-2.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/plasma/6.7.2/kactivitymanagerd-6.7.2.tar.xz"
diff --git a/recipes/incoming-kde/karchive.toml b/recipes/incoming-kde/karchive.toml
index b06f12d4..5e091567 100644
--- a/recipes/incoming-kde/karchive.toml
+++ b/recipes/incoming-kde/karchive.toml
@@ -2,6 +2,7 @@
 # -Wl,--fatal-warnings (ECM lo agrega, zig lo rechaza), CMAKE_PREFIX_PATH=/usr, ECM + qttools + dbus.
 name    = "karchive"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/karchive-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kate.toml b/recipes/incoming-kde/kate.toml
index 447403ce..59978969 100644
--- a/recipes/incoming-kde/kate.toml
+++ b/recipes/incoming-kde/kate.toml
@@ -2,6 +2,7 @@
 # Patrón KF6 app + ktexteditor. musl-fts por si usa fts. DocTools/doc opcionales.
 name    = "kate"
 version = "25.04.3"
+license = "LGPL-2.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/release-service/25.04.3/src/kate-25.04.3.tar.xz"
diff --git a/recipes/incoming-kde/kauth.toml b/recipes/incoming-kde/kauth.toml
index 41519603..4ccef39c 100644
--- a/recipes/incoming-kde/kauth.toml
+++ b/recipes/incoming-kde/kauth.toml
@@ -5,6 +5,7 @@
 # sin él cae al backend "fake"/dummy (no bloquea). No usa ki18n.
 name    = "kauth"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kauth-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kbookmarks.toml b/recipes/incoming-kde/kbookmarks.toml
index 69647827..381acdfc 100644
--- a/recipes/incoming-kde/kbookmarks.toml
+++ b/recipes/incoming-kde/kbookmarks.toml
@@ -3,6 +3,7 @@
 # Qt6 Widgets+Xml. Deps KF6 (del CMakeLists): kconfig, kcoreaddons, kwidgetsaddons. Sin ki18n, sin QML, sin X11.
 name    = "kbookmarks"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kbookmarks-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kcalc.toml b/recipes/incoming-kde/kcalc.toml
index dd4b3b09..0e696c02 100644
--- a/recipes/incoming-kde/kcalc.toml
+++ b/recipes/incoming-kde/kcalc.toml
@@ -5,6 +5,7 @@
 # (cmake/modules/FindMPC.cmake) localiza mpc.h/libmpc en /usr (deps materializadas).
 name    = "kcalc"
 version = "25.04.3"
+license = "GPL-2.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/release-service/25.04.3/src/kcalc-25.04.3.tar.xz"
diff --git a/recipes/incoming-kde/kcharselect.toml b/recipes/incoming-kde/kcharselect.toml
index b48a8441..e67b4bdf 100644
--- a/recipes/incoming-kde/kcharselect.toml
+++ b/recipes/incoming-kde/kcharselect.toml
@@ -3,6 +3,7 @@
 # KF6DocTools se degrada (sin docbook) + se borra la macro kdoctools_install (queda indefinida sin él).
 name    = "kcharselect"
 version = "25.04.3"
+license = "GPL-2.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/release-service/25.04.3/src/kcharselect-25.04.3.tar.xz"
diff --git a/recipes/incoming-kde/kcmutils.toml b/recipes/incoming-kde/kcmutils.toml
index 71197481..43031c28 100644
--- a/recipes/incoming-kde/kcmutils.toml
+++ b/recipes/incoming-kde/kcmutils.toml
@@ -5,6 +5,7 @@
 #   (verificado en todo el árbol). Sin option(WITH_X11). KIO arrastra el CIERRE tier-1..3 entero.
 name    = "kcmutils"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kcmutils-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kcodecs.toml b/recipes/incoming-kde/kcodecs.toml
index d3ef5ba5..5c3a578e 100644
--- a/recipes/incoming-kde/kcodecs.toml
+++ b/recipes/incoming-kde/kcodecs.toml
@@ -2,6 +2,7 @@
 # -Wl,--fatal-warnings (ECM lo agrega, zig lo rechaza), CMAKE_PREFIX_PATH=/usr, ECM + qttools + dbus.
 name    = "kcodecs"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kcodecs-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kcolorscheme.toml b/recipes/incoming-kde/kcolorscheme.toml
index bedb6df3..4f1a5b78 100644
--- a/recipes/incoming-kde/kcolorscheme.toml
+++ b/recipes/incoming-kde/kcolorscheme.toml
@@ -3,6 +3,7 @@
 # Qt6Gui/Qml ⇒ qtdeclarative. Deps KF6: kconfig, kguiaddons, kcoreaddons, ki18n.
 name    = "kcolorscheme"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kcolorscheme-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kcompletion.toml b/recipes/incoming-kde/kcompletion.toml
index 5856a45a..8c8b1532 100644
--- a/recipes/incoming-kde/kcompletion.toml
+++ b/recipes/incoming-kde/kcompletion.toml
@@ -3,6 +3,7 @@
 # Qt6Widgets. Deps KF6: kconfig, kwidgetsaddons, kcoreaddons, kcodecs. DESIGNERPLUGIN OFF.
 name    = "kcompletion"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kcompletion-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kconfig.toml b/recipes/incoming-kde/kconfig.toml
index 8514b8c3..3495cb13 100644
--- a/recipes/incoming-kde/kconfig.toml
+++ b/recipes/incoming-kde/kconfig.toml
@@ -2,6 +2,7 @@
 # -Wl,--fatal-warnings (ECM lo agrega, zig lo rechaza), CMAKE_PREFIX_PATH=/usr, ECM + qttools + dbus.
 name    = "kconfig"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kconfig-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kconfigwidgets.toml b/recipes/incoming-kde/kconfigwidgets.toml
index ad57d5e6..2984fe27 100644
--- a/recipes/incoming-kde/kconfigwidgets.toml
+++ b/recipes/incoming-kde/kconfigwidgets.toml
@@ -4,6 +4,7 @@
 # DESIGNERPLUGIN OFF.
 name    = "kconfigwidgets"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kconfigwidgets-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kcoreaddons.toml b/recipes/incoming-kde/kcoreaddons.toml
index f54d6595..9141c755 100644
--- a/recipes/incoming-kde/kcoreaddons.toml
+++ b/recipes/incoming-kde/kcoreaddons.toml
@@ -4,6 +4,7 @@
 # docs apagados. Core-only ⇒ sin el set Gui (mesa/xkb/wayland).
 name    = "kcoreaddons"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kcoreaddons-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kcrash.toml b/recipes/incoming-kde/kcrash.toml
index b058f0c0..f029808a 100644
--- a/recipes/incoming-kde/kcrash.toml
+++ b/recipes/incoming-kde/kcrash.toml
@@ -4,6 +4,7 @@
 # en el gate, deuda Capa 0). Plasma es Wayland-first ⇒ kcrash funciona sin la integración X11.
 name    = "kcrash"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kcrash-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kdbusaddons.toml b/recipes/incoming-kde/kdbusaddons.toml
index dead8a92..6ebcd02d 100644
--- a/recipes/incoming-kde/kdbusaddons.toml
+++ b/recipes/incoming-kde/kdbusaddons.toml
@@ -3,6 +3,7 @@
 # Sólo Qt6DBus (viene en qtbase); sin frameworks KF6 extra.
 name    = "kdbusaddons"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kdbusaddons-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kdeclarative.toml b/recipes/incoming-kde/kdeclarative.toml
index 6f4864d1..269b3e6b 100644
--- a/recipes/incoming-kde/kdeclarative.toml
+++ b/recipes/incoming-kde/kdeclarative.toml
@@ -10,6 +10,7 @@
 # Sin option(WITH_X11); sin código X11 → sin -DWITH_X11=OFF.
 name    = "kdeclarative"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kdeclarative-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kdecoration.toml b/recipes/incoming-kde/kdecoration.toml
index 00521f97..3abad69c 100644
--- a/recipes/incoming-kde/kdecoration.toml
+++ b/recipes/incoming-kde/kdecoration.toml
@@ -4,6 +4,7 @@
 # NO expone option(WITH_X11) ni toca X11 → NO lleva -DWITH_X11=OFF.
 name    = "kdecoration"
 version = "6.7.2"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/plasma/6.7.2/kdecoration-6.7.2.tar.xz"
diff --git a/recipes/incoming-kde/kded.toml b/recipes/incoming-kde/kded.toml
index 1de3c303..f031f5b3 100644
--- a/recipes/incoming-kde/kded.toml
+++ b/recipes/incoming-kde/kded.toml
@@ -4,6 +4,7 @@
 # rm -rf po (traducciones; gettext-tiny minimal).
 name    = "kded"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kded-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kdf.toml b/recipes/incoming-kde/kdf.toml
index 969032cb..7ae983cb 100644
--- a/recipes/incoming-kde/kdf.toml
+++ b/recipes/incoming-kde/kdf.toml
@@ -3,6 +3,7 @@
 # KF6DocTools se degrada (sin docbook) + se borra la macro kdoctools_install (queda indefinida sin él).
 name    = "kdf"
 version = "25.04.3"
+license = "GPL-2.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/release-service/25.04.3/src/kdf-25.04.3.tar.xz"
diff --git a/recipes/incoming-kde/kdnssd.toml b/recipes/incoming-kde/kdnssd.toml
index b7b95eb1..7d3a5318 100644
--- a/recipes/incoming-kde/kdnssd.toml
+++ b/recipes/incoming-kde/kdnssd.toml
@@ -7,6 +7,7 @@
 # Sin option(WITH_X11); sin código X11 → sin -DWITH_X11=OFF.
 name    = "kdnssd"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kdnssd-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kfilemetadata.toml b/recipes/incoming-kde/kfilemetadata.toml
index 81e8b721..bab3fff7 100644
--- a/recipes/incoming-kde/kfilemetadata.toml
+++ b/recipes/incoming-kde/kfilemetadata.toml
@@ -13,6 +13,7 @@
 # Sin option(WITH_X11); sin código X11 → sin -DWITH_X11=OFF.
 name    = "kfilemetadata"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kfilemetadata-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kfind.toml b/recipes/incoming-kde/kfind.toml
index da9aebae..60874cfa 100644
--- a/recipes/incoming-kde/kfind.toml
+++ b/recipes/incoming-kde/kfind.toml
@@ -3,6 +3,7 @@
 # KF6DocTools se degrada (sin docbook) + se borra la macro kdoctools_install (queda indefinida sin él).
 name    = "kfind"
 version = "25.04.3"
+license = "GPL-2.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/release-service/25.04.3/src/kfind-25.04.3.tar.xz"
diff --git a/recipes/incoming-kde/kglobalaccel.toml b/recipes/incoming-kde/kglobalaccel.toml
index ab4dc88b..708760b9 100644
--- a/recipes/incoming-kde/kglobalaccel.toml
+++ b/recipes/incoming-kde/kglobalaccel.toml
@@ -4,6 +4,7 @@
 # TIENE código X11 tras option(WITH_X11 ON): Qt está SIN xcb → -DWITH_X11=OFF OBLIGATORIO.
 name    = "kglobalaccel"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kglobalaccel-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kglobalacceld.toml b/recipes/incoming-kde/kglobalacceld.toml
index a870ad69..8df064c4 100644
--- a/recipes/incoming-kde/kglobalacceld.toml
+++ b/recipes/incoming-kde/kglobalacceld.toml
@@ -6,6 +6,7 @@
 # Arrastra el cierre de kio (tier-1/2/3 + kio + libgcrypt/libgpg-error/util-linux/openssl).
 name    = "kglobalacceld"
 version = "6.7.2"
+license = "GPL-2.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/plasma/6.7.2/kglobalacceld-6.7.2.tar.xz"
diff --git a/recipes/incoming-kde/kguiaddons.toml b/recipes/incoming-kde/kguiaddons.toml
index 6215dae5..a67a756a 100644
--- a/recipes/incoming-kde/kguiaddons.toml
+++ b/recipes/incoming-kde/kguiaddons.toml
@@ -2,6 +2,7 @@
 # -Wl,--fatal-warnings (ECM lo agrega, zig lo rechaza), CMAKE_PREFIX_PATH=/usr, ECM + qttools + dbus.
 name    = "kguiaddons"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kguiaddons-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kholidays.toml b/recipes/incoming-kde/kholidays.toml
index 8346c9cd..28074e91 100644
--- a/recipes/incoming-kde/kholidays.toml
+++ b/recipes/incoming-kde/kholidays.toml
@@ -5,6 +5,7 @@
 # festivos). NO usa ki18n (sólo ecm_install_po_files_as_qm vía lrelease de qttools).
 name    = "kholidays"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kholidays-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/ki18n.toml b/recipes/incoming-kde/ki18n.toml
index 41c1f648..6179176e 100644
--- a/recipes/incoming-kde/ki18n.toml
+++ b/recipes/incoming-kde/ki18n.toml
@@ -2,6 +2,7 @@
 # -Wl,--fatal-warnings (ECM lo agrega, zig lo rechaza), CMAKE_PREFIX_PATH=/usr, ECM + qttools + dbus.
 name    = "ki18n"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/ki18n-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kiconthemes.toml b/recipes/incoming-kde/kiconthemes.toml
index 448dc6b3..d6eb2159 100644
--- a/recipes/incoming-kde/kiconthemes.toml
+++ b/recipes/incoming-kde/kiconthemes.toml
@@ -6,6 +6,7 @@
 # lo APAGO con -DUSE_BreezeIcons=OFF (temas de iconos se resuelven en runtime, no en build).
 name    = "kiconthemes"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kiconthemes-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kidletime.toml b/recipes/incoming-kde/kidletime.toml
index 3ce25054..7a17ef80 100644
--- a/recipes/incoming-kde/kidletime.toml
+++ b/recipes/incoming-kde/kidletime.toml
@@ -5,6 +5,7 @@
 # Wayland, PlasmaWaylandProtocols>=1.6, WaylandProtocols>=1.27, Qt6GuiPrivate, Qt6DBus. SET X11 + qtwayland.
 name    = "kidletime"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kidletime-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kinfocenter.toml b/recipes/incoming-kde/kinfocenter.toml
index abf0e83a..cbc1943f 100644
--- a/recipes/incoming-kde/kinfocenter.toml
+++ b/recipes/incoming-kde/kinfocenter.toml
@@ -2,6 +2,7 @@
 # Patrón app/KCM-Plasma (dynamic, wrapper zig, ECM). Todas sus deps ya estaban selladas.
 name    = "kinfocenter"
 version = "6.7.2"
+license = "GPL-2.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/plasma/6.7.2/kinfocenter-6.7.2.tar.xz"
diff --git a/recipes/incoming-kde/kio.toml b/recipes/incoming-kde/kio.toml
index afc32583..09bd1e3c 100644
--- a/recipes/incoming-kde/kio.toml
+++ b/recipes/incoming-kde/kio.toml
@@ -23,6 +23,7 @@
 #   ON (default): la integración wayland de KIO pasa por KF6::WindowSystem (no linkea wayland-client directo). mesa set presente.
 name    = "kio"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kio-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kirigami-addons.toml b/recipes/incoming-kde/kirigami-addons.toml
index 3b890a0b..cce47c0f 100644
--- a/recipes/incoming-kde/kirigami-addons.toml
+++ b/recipes/incoming-kde/kirigami-addons.toml
@@ -2,6 +2,7 @@
 # kickoff (lanzador) lo IMPORTA. Patrón KF6 (dynamic, wrapper zig, ECM). QML-heavy.
 name    = "kirigami-addons"
 version = "1.9.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/kirigami-addons/kirigami-addons-1.9.0.tar.xz"
diff --git a/recipes/incoming-kde/kirigami.toml b/recipes/incoming-kde/kirigami.toml
index cfe1a738..46fb9c88 100644
--- a/recipes/incoming-kde/kirigami.toml
+++ b/recipes/incoming-kde/kirigami.toml
@@ -7,6 +7,7 @@
 # USE_DBUS ON por defecto en Linux → Qt6DBus (qtbase). BUILD_EXAMPLES OFF por defecto. DESKTOP_ENABLED ON.
 name    = "kirigami"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kirigami-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kitemmodels.toml b/recipes/incoming-kde/kitemmodels.toml
index 724930c2..2e3d9d0e 100644
--- a/recipes/incoming-kde/kitemmodels.toml
+++ b/recipes/incoming-kde/kitemmodels.toml
@@ -3,6 +3,7 @@
 # Qt6Core + Qt6Qml (opcional NO_MODULE, provee los tipos QML) ⇒ qtdeclarative. Sin frameworks KF6 extra.
 name    = "kitemmodels"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kitemmodels-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kitemviews.toml b/recipes/incoming-kde/kitemviews.toml
index cd27a915..90258de2 100644
--- a/recipes/incoming-kde/kitemviews.toml
+++ b/recipes/incoming-kde/kitemviews.toml
@@ -3,6 +3,7 @@
 # Sólo Qt6Widgets; sin frameworks KF6 extra. DESIGNERPLUGIN OFF (evita dep Qt Designer).
 name    = "kitemviews"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kitemviews-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kjobwidgets.toml b/recipes/incoming-kde/kjobwidgets.toml
index ee7077ff..59cecfff 100644
--- a/recipes/incoming-kde/kjobwidgets.toml
+++ b/recipes/incoming-kde/kjobwidgets.toml
@@ -3,6 +3,7 @@
 # Qt6Widgets/Gui. Deps KF6: kcoreaddons, kwidgetsaddons, kwindowsystem. DESIGNERPLUGIN OFF.
 name    = "kjobwidgets"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kjobwidgets-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kmenuedit.toml b/recipes/incoming-kde/kmenuedit.toml
index 020dbf27..5b4b22d3 100644
--- a/recipes/incoming-kde/kmenuedit.toml
+++ b/recipes/incoming-kde/kmenuedit.toml
@@ -2,6 +2,7 @@
 # Patrón app/KCM-Plasma (dynamic, wrapper zig, ECM). Todas sus deps ya estaban selladas.
 name    = "kmenuedit"
 version = "6.7.2"
+license = "GPL-2.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/plasma/6.7.2/kmenuedit-6.7.2.tar.xz"
diff --git a/recipes/incoming-kde/knewstuff.toml b/recipes/incoming-kde/knewstuff.toml
index 0f710207..56d61be2 100644
--- a/recipes/incoming-kde/knewstuff.toml
+++ b/recipes/incoming-kde/knewstuff.toml
@@ -7,6 +7,7 @@
 # ki18n → gettext-tiny. kirigami arrastra qtsvg/qtshadertools. karchive → zlib/zstd/openssl.
 name    = "knewstuff"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/knewstuff-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/knotifications.toml b/recipes/incoming-kde/knotifications.toml
index b6ee379e..7fde5679 100644
--- a/recipes/incoming-kde/knotifications.toml
+++ b/recipes/incoming-kde/knotifications.toml
@@ -6,6 +6,7 @@
 # quita el REQUIRED y compila sin audio (libcanberra = stack alsa/pulse/vorbis, deuda audio diferida).
 name    = "knotifications"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/knotifications-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/knotifyconfig.toml b/recipes/incoming-kde/knotifyconfig.toml
index 321b9e3b..c13e61d8 100644
--- a/recipes/incoming-kde/knotifyconfig.toml
+++ b/recipes/incoming-kde/knotifyconfig.toml
@@ -3,6 +3,7 @@
 # kcoreaddons, ki18n, knotifications.
 name    = "knotifyconfig"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/knotifyconfig-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/konsole.toml b/recipes/incoming-kde/konsole.toml
index ce9628b7..9af4ce68 100644
--- a/recipes/incoming-kde/konsole.toml
+++ b/recipes/incoming-kde/konsole.toml
@@ -2,6 +2,7 @@
 # Patrón KF6 app (dynamic, wrapper zig, ECM). Usa kpty (pseudo-terminal).
 name    = "konsole"
 version = "25.04.3"
+license = "GPL-2.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/release-service/25.04.3/src/konsole-25.04.3.tar.xz"
diff --git a/recipes/incoming-kde/kpackage.toml b/recipes/incoming-kde/kpackage.toml
index dfc60766..cb780501 100644
--- a/recipes/incoming-kde/kpackage.toml
+++ b/recipes/incoming-kde/kpackage.toml
@@ -5,6 +5,7 @@
 # ki18n → gettext-tiny. qtdeclarative incluido (consumidores KF6 encadenan QML). Cierre: karchive→zlib/zstd/openssl.
 name    = "kpackage"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kpackage-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kparts.toml b/recipes/incoming-kde/kparts.toml
index 18af81a3..ce8bc12f 100644
--- a/recipes/incoming-kde/kparts.toml
+++ b/recipes/incoming-kde/kparts.toml
@@ -5,6 +5,7 @@
 # Sin option(WITH_X11) en el árbol 6.27. ki18n → gettext-tiny.
 name    = "kparts"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kparts-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kpipewire.toml b/recipes/incoming-kde/kpipewire.toml
index c931231b..381dd03a 100644
--- a/recipes/incoming-kde/kpipewire.toml
+++ b/recipes/incoming-kde/kpipewire.toml
@@ -7,6 +7,7 @@
 # CoreAddons.
 name    = "kpipewire"
 version = "6.7.2"
+license = "GPL-2.0-or-later OR LGPL-2.1-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/plasma/6.7.2/kpipewire-6.7.2.tar.xz"
diff --git a/recipes/incoming-kde/kplotting.toml b/recipes/incoming-kde/kplotting.toml
index b48988da..c4cbf674 100644
--- a/recipes/incoming-kde/kplotting.toml
+++ b/recipes/incoming-kde/kplotting.toml
@@ -3,6 +3,7 @@
 # Sólo Qt6Gui (widget de ploteo QWidget-free); sin frameworks KF6 extra.
 name    = "kplotting"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kplotting-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kpty.toml b/recipes/incoming-kde/kpty.toml
index 72540c6e..ef317b77 100644
--- a/recipes/incoming-kde/kpty.toml
+++ b/recipes/incoming-kde/kpty.toml
@@ -1,6 +1,7 @@
 # kpty 6.27.0 — KDE Frameworks 6: interfaz a pseudo-terminales (pty). Dep de konsole. Patrón KF6.
 name    = "kpty"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kpty-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kquickcharts.toml b/recipes/incoming-kde/kquickcharts.toml
index 1d8eafa6..18550494 100644
--- a/recipes/incoming-kde/kquickcharts.toml
+++ b/recipes/incoming-kde/kquickcharts.toml
@@ -2,6 +2,7 @@
 # IMPORTA. Patrón KF6 (dynamic, wrapper zig, ECM). Usa Qt6Quick/Qml/Gui (shaders).
 name    = "kquickcharts"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kquickcharts-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kquickimageeditor.toml b/recipes/incoming-kde/kquickimageeditor.toml
index 1b0d7887..14e1298b 100644
--- a/recipes/incoming-kde/kquickimageeditor.toml
+++ b/recipes/incoming-kde/kquickimageeditor.toml
@@ -7,6 +7,7 @@
 # (core+imgproc, para el "stack blur" — WITH_OPENCV es TRUE por defecto en UNIX); ambas ya selladas.
 name    = "kquickimageeditor"
 version = "0.6.2.1"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/kquickimageeditor/kquickimageeditor-0.6.2.1.tar.xz"
diff --git a/recipes/incoming-kde/kruler.toml b/recipes/incoming-kde/kruler.toml
index f6580c47..70d236bd 100644
--- a/recipes/incoming-kde/kruler.toml
+++ b/recipes/incoming-kde/kruler.toml
@@ -3,6 +3,7 @@
 # KF6DocTools se degrada (sin docbook) + se borra la macro kdoctools_install (queda indefinida sin él).
 name    = "kruler"
 version = "25.04.3"
+license = "GPL-2.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/release-service/25.04.3/src/kruler-25.04.3.tar.xz"
diff --git a/recipes/incoming-kde/krunner.toml b/recipes/incoming-kde/krunner.toml
index c882b22c..bae65b8c 100644
--- a/recipes/incoming-kde/krunner.toml
+++ b/recipes/incoming-kde/krunner.toml
@@ -4,6 +4,7 @@
 # (rule 1, inerte en prefix) y SET X11 (kwindowsystem transitivo). rm -rf po (muchas traducciones).
 name    = "krunner"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/krunner-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kscreen.toml b/recipes/incoming-kde/kscreen.toml
index b1425f2d..d35ecfbf 100644
--- a/recipes/incoming-kde/kscreen.toml
+++ b/recipes/incoming-kde/kscreen.toml
@@ -3,6 +3,7 @@
 # LayerShellQt (layer-shell-qt), KCMUtils, Svg (ksvg). KF6ImageFormats es CONFIG opcional → se salta.
 name    = "kscreen"
 version = "6.7.2"
+license = "GPL-2.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/plasma/6.7.2/kscreen-6.7.2.tar.xz"
diff --git a/recipes/incoming-kde/kscreenlocker.toml b/recipes/incoming-kde/kscreenlocker.toml
index 3e3791e1..c56fa813 100644
--- a/recipes/incoming-kde/kscreenlocker.toml
+++ b/recipes/incoming-kde/kscreenlocker.toml
@@ -19,6 +19,7 @@
 # ======================================================================
 name    = "kscreenlocker"
 version = "6.7.2"
+license = "GPL-2.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/plasma/6.7.2/kscreenlocker-6.7.2.tar.xz"
diff --git a/recipes/incoming-kde/kservice.toml b/recipes/incoming-kde/kservice.toml
index 5f2ccf55..2447a0ad 100644
--- a/recipes/incoming-kde/kservice.toml
+++ b/recipes/incoming-kde/kservice.toml
@@ -4,6 +4,7 @@
 # NOTA: KF6 6.27 NO trae parser flex/bison (no hay .l/.y ni FLEX_TARGET/BISON_TARGET): sin flex/bison.
 name    = "kservice"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kservice-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kstatusnotifieritem.toml b/recipes/incoming-kde/kstatusnotifieritem.toml
index acc6d0ad..c0db26b2 100644
--- a/recipes/incoming-kde/kstatusnotifieritem.toml
+++ b/recipes/incoming-kde/kstatusnotifieritem.toml
@@ -4,6 +4,7 @@
 # KF6WindowSystem (REQUIRED). Sin qtdeclarative. SET X11 completo (find_package(X11) transitivo). rm -rf po.
 name    = "kstatusnotifieritem"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kstatusnotifieritem-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/ksvg.toml b/recipes/incoming-kde/ksvg.toml
index 1ac092e8..1c22c055 100644
--- a/recipes/incoming-kde/ksvg.toml
+++ b/recipes/incoming-kde/ksvg.toml
@@ -6,6 +6,7 @@
 # GuiAddons(kguiaddons), KirigamiPlatform(kirigami). option(BUILD_TOOLS OFF) default. NO expone WITH_X11.
 name    = "ksvg"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/ksvg-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/ktexteditor.toml b/recipes/incoming-kde/ktexteditor.toml
index 0421ce35..0bbbbfdf 100644
--- a/recipes/incoming-kde/ktexteditor.toml
+++ b/recipes/incoming-kde/ktexteditor.toml
@@ -15,6 +15,7 @@
 #   Qt: Core Widgets Qml PrintSupport. WITH: rm -rf po. NO construye hasta sellar (1) y (2).
 name    = "ktexteditor"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/ktexteditor-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/ktextwidgets.toml b/recipes/incoming-kde/ktextwidgets.toml
index 4a4e2f81..07c62b61 100644
--- a/recipes/incoming-kde/ktextwidgets.toml
+++ b/recipes/incoming-kde/ktextwidgets.toml
@@ -4,6 +4,7 @@
 # WITH_TEXT_TO_SPEECH=ON por defecto → exigiría Qt6TextToSpeech (qtspeech, NO sellado): lo APAGO (-DWITH_TEXT_TO_SPEECH=OFF).
 name    = "ktextwidgets"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/ktextwidgets-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kunitconversion.toml b/recipes/incoming-kde/kunitconversion.toml
index 2709717d..6d7ede8e 100644
--- a/recipes/incoming-kde/kunitconversion.toml
+++ b/recipes/incoming-kde/kunitconversion.toml
@@ -3,6 +3,7 @@
 # Sin X11, sin qtdeclarative. rm -rf po (muchas traducciones de unidades; msgfmt de gettext-tiny minimal).
 name    = "kunitconversion"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kunitconversion-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kwallet.toml b/recipes/incoming-kde/kwallet.toml
index 8b64dfe8..761bcaf6 100644
--- a/recipes/incoming-kde/kwallet.toml
+++ b/recipes/incoming-kde/kwallet.toml
@@ -12,6 +12,7 @@
 # daemon cuando se reactive. Gpgmepp opcional (GPG support off). Reactivar daemon = sellar libsecret+QCA.
 name    = "kwallet"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kwallet-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kwalletmanager.toml b/recipes/incoming-kde/kwalletmanager.toml
index 5ef14089..d09d2c66 100644
--- a/recipes/incoming-kde/kwalletmanager.toml
+++ b/recipes/incoming-kde/kwalletmanager.toml
@@ -3,6 +3,7 @@
 # KF6DocTools se degrada (sin docbook) + se borra la macro kdoctools_install (queda indefinida sin él).
 name    = "kwalletmanager"
 version = "25.04.3"
+license = "GPL-2.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/release-service/25.04.3/src/kwalletmanager-25.04.3.tar.xz"
diff --git a/recipes/incoming-kde/kwayland.toml b/recipes/incoming-kde/kwayland.toml
index 62554d3d..bce60eb1 100644
--- a/recipes/incoming-kde/kwayland.toml
+++ b/recipes/incoming-kde/kwayland.toml
@@ -3,6 +3,7 @@
 # Private ⇒ qtwayland (sellado) + wayland + wayland-protocols. Patrón KF6 (wrapper zig filtra --fatal-warnings).
 name    = "kwayland"
 version = "6.7.2"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/plasma/6.7.2/kwayland-6.7.2.tar.xz"
diff --git a/recipes/incoming-kde/kwidgetsaddons.toml b/recipes/incoming-kde/kwidgetsaddons.toml
index 63a0669e..bde8fc33 100644
--- a/recipes/incoming-kde/kwidgetsaddons.toml
+++ b/recipes/incoming-kde/kwidgetsaddons.toml
@@ -2,6 +2,7 @@
 # -Wl,--fatal-warnings (ECM lo agrega, zig lo rechaza), CMAKE_PREFIX_PATH=/usr, ECM + qttools + dbus.
 name    = "kwidgetsaddons"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kwidgetsaddons-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kwin.toml b/recipes/incoming-kde/kwin.toml
index 0a184a3e..d6351652 100644
--- a/recipes/incoming-kde/kwin.toml
+++ b/recipes/incoming-kde/kwin.toml
@@ -22,6 +22,7 @@
 # ========================================================================================
 name    = "kwin"
 version = "6.7.2"
+license = "GPL-2.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/plasma/6.7.2/kwin-6.7.2.tar.xz"
diff --git a/recipes/incoming-kde/kwindowsystem.toml b/recipes/incoming-kde/kwindowsystem.toml
index e668d9a4..4c243e0d 100644
--- a/recipes/incoming-kde/kwindowsystem.toml
+++ b/recipes/incoming-kde/kwindowsystem.toml
@@ -9,6 +9,7 @@
 # ya está en [deps].build. Rebuild en la granja (zig-skew: NO en el laptop).
 name    = "kwindowsystem"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kwindowsystem-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/kxmlgui.toml b/recipes/incoming-kde/kxmlgui.toml
index 8b905e4d..25c286a4 100644
--- a/recipes/incoming-kde/kxmlgui.toml
+++ b/recipes/incoming-kde/kxmlgui.toml
@@ -6,6 +6,7 @@
 # NOTA: 6.27 NO expone option(WITH_X11) ni toca qtx11extras → NO lleva -DWITH_X11=OFF (verificado en el árbol).
 name    = "kxmlgui"
 version = "6.27.0"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/frameworks/6.27/kxmlgui-6.27.0.tar.xz"
diff --git a/recipes/incoming-kde/libarchive.toml b/recipes/incoming-kde/libarchive.toml
index 7efce0a8..46764eff 100644
--- a/recipes/incoming-kde/libarchive.toml
+++ b/recipes/incoming-kde/libarchive.toml
@@ -7,6 +7,7 @@
 # el cierre sea explícito.
 name    = "libarchive"
 version = "3.7.7"
+license = "BSD-2-Clause"
 
 [source]
 tarball = "https://github.com/libarchive/libarchive/releases/download/v3.7.7/libarchive-3.7.7.tar.xz"
diff --git a/recipes/incoming-kde/libjpeg-turbo-shared.toml b/recipes/incoming-kde/libjpeg-turbo-shared.toml
index 44f74cee..e3dc03a6 100644
--- a/recipes/incoming-kde/libjpeg-turbo-shared.toml
+++ b/recipes/incoming-kde/libjpeg-turbo-shared.toml
@@ -4,6 +4,7 @@
 # zlib-shared/bzip2-shared. Nombre distinto para no colisionar con el canónico. gwenview lo linkea.
 name    = "libjpeg-turbo-shared"
 version = "3.1.3"
+license = "IJG AND BSD-3-Clause AND Zlib"
 
 [source]
 tarball = "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/3.1.3/libjpeg-turbo-3.1.3.tar.gz"
diff --git a/recipes/incoming-kde/libpng-shared.toml b/recipes/incoming-kde/libpng-shared.toml
index cb9c9ad0..ec37536d 100644
--- a/recipes/incoming-kde/libpng-shared.toml
+++ b/recipes/incoming-kde/libpng-shared.toml
@@ -3,6 +3,7 @@
 # zlib-shared/xz-shared: autotools --enable-shared, zig-cc, contra zlib-shared (libz.so). gwenview linkea.
 name    = "libpng-shared"
 version = "1.6.58"
+license = "Libpng"
 
 [source]
 tarball = "https://downloads.sourceforge.net/libpng/libpng-1.6.58.tar.gz"
diff --git a/recipes/incoming-kde/libtool.toml b/recipes/incoming-kde/libtool.toml
index 30c91598..41b53ab6 100644
--- a/recipes/incoming-kde/libtool.toml
+++ b/recipes/incoming-kde/libtool.toml
@@ -2,6 +2,7 @@
 # Sólo nos interesa libltdl (ltdl.h + libltdl.so + libltdl.pc); el script libtool en sí no lo usa hammer.
 name    = "libtool"
 version = "2.5.4"
+license = "GPL-2.0-or-later"
 [source]
 tarball = "https://ftpmirror.gnu.org/libtool/libtool-2.5.4.tar.xz"
 sha256  = "f81f5860666b0bc7d84baddefa60d1cb9fa6fceb2398cc3baca6afaa60266675"
diff --git a/recipes/incoming-kde/libxkbcommon.toml b/recipes/incoming-kde/libxkbcommon.toml
index 844d1dd0..672a92b5 100644
--- a/recipes/incoming-kde/libxkbcommon.toml
+++ b/recipes/incoming-kde/libxkbcommon.toml
@@ -4,6 +4,7 @@
 # wayland/xkbregistry/tools). En runtime usa los datos de xkeyboard-config (no dep de build).
 name    = "libxkbcommon"
 version = "1.7.0"
+license = "MIT"
 
 [source]
 tarball = "https://github.com/xkbcommon/libxkbcommon/archive/refs/tags/xkbcommon-1.7.0.tar.gz"
diff --git a/recipes/incoming-kde/libxml2.toml b/recipes/incoming-kde/libxml2.toml
index 35715567..48078509 100644
--- a/recipes/incoming-kde/libxml2.toml
+++ b/recipes/incoming-kde/libxml2.toml
@@ -4,6 +4,7 @@
 # incoming-kde (mismo name). compiler=gcc (zig miscompila). Sin python/lzma; CON zlib no (mantener mínimo).
 name    = "libxml2"
 version = "2.13.9"
+license = "MIT"
 
 [source]
 tarball = "https://download.gnome.org/sources/libxml2/2.13/libxml2-2.13.9.tar.xz"
diff --git a/recipes/incoming-kde/openssl.toml b/recipes/incoming-kde/openssl.toml
index 4e7b74d5..f5d4507b 100644
--- a/recipes/incoming-kde/openssl.toml
+++ b/recipes/incoming-kde/openssl.toml
@@ -7,6 +7,7 @@
 # Patrón static↔dinámico Capa 0 (mismo que zstd/libgcrypt/util-linux-shared).
 name    = "openssl"
 version = "3.5.4"
+license = "Apache-2.0"
 
 [source]
 tarball = "https://github.com/openssl/openssl/releases/download/openssl-3.5.4/openssl-3.5.4.tar.gz"
diff --git a/recipes/incoming-kde/pipewire.toml b/recipes/incoming-kde/pipewire.toml
index 36b2d093..4d0c1a46 100644
--- a/recipes/incoming-kde/pipewire.toml
+++ b/recipes/incoming-kde/pipewire.toml
@@ -16,6 +16,7 @@
 # Quitando la dep, meson saltea pw-top y el resto construye.
 name    = "pipewire"
 version = "1.2.7"
+license = "MIT"
 
 [source]
 tarball = "https://gitlab.freedesktop.org/pipewire/pipewire/-/archive/1.2.7/pipewire-1.2.7.tar.gz"
diff --git a/recipes/incoming-kde/plasma-activities-stats.toml b/recipes/incoming-kde/plasma-activities-stats.toml
index 55e744d3..5d21aea4 100644
--- a/recipes/incoming-kde/plasma-activities-stats.toml
+++ b/recipes/incoming-kde/plasma-activities-stats.toml
@@ -5,6 +5,7 @@
 # Sin X11 obligatorio. Construye limpio.
 name    = "plasma-activities-stats"
 version = "6.7.2"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/plasma/6.7.2/plasma-activities-stats-6.7.2.tar.xz"
diff --git a/recipes/incoming-kde/plasma-activities.toml b/recipes/incoming-kde/plasma-activities.toml
index b582f2b8..43b818ce 100644
--- a/recipes/incoming-kde/plasma-activities.toml
+++ b/recipes/incoming-kde/plasma-activities.toml
@@ -6,6 +6,7 @@
 # NO expone WITH_X11 ni Boost. Qml/Quick → qtdeclarative. Sql → qtbase compilado con SQL ON.
 name    = "plasma-activities"
 version = "6.7.2"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/plasma/6.7.2/plasma-activities-6.7.2.tar.xz"
diff --git a/recipes/incoming-kde/plasma-desktop.toml b/recipes/incoming-kde/plasma-desktop.toml
index 60359926..01b6b262 100644
--- a/recipes/incoming-kde/plasma-desktop.toml
+++ b/recipes/incoming-kde/plasma-desktop.toml
@@ -4,6 +4,7 @@
 # wrapper zig filtra -Wl,--fatal-warnings, Ninja, rm -rf po, WITH_X11=OFF Wayland-puro).
 name    = "plasma-desktop"
 version = "6.7.2"
+license = "GPL-2.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/plasma/6.7.2/plasma-desktop-6.7.2.tar.xz"
diff --git a/recipes/incoming-kde/plasma-integration.toml b/recipes/incoming-kde/plasma-integration.toml
index 51ac51be..7ef1f847 100644
--- a/recipes/incoming-kde/plasma-integration.toml
+++ b/recipes/incoming-kde/plasma-integration.toml
@@ -2,6 +2,7 @@
 # de Plasma (usa breeze + KF6 + Qt6 private). Patrón KF6 (dynamic, wrapper zig). Wayland-puro. Runtime del escritorio.
 name    = "plasma-integration"
 version = "6.7.2"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 [source]
 tarball = "https://download.kde.org/stable/plasma/6.7.2/plasma-integration-6.7.2.tar.xz"
 sha256  = "f1ce2c3fb0e07a4e0cd5e3d5566fb67e6063aaf435e8efb3402f7d65e1da95a4"
diff --git a/recipes/incoming-kde/plasma-nm.toml b/recipes/incoming-kde/plasma-nm.toml
index 88ea8a0e..61512dee 100644
--- a/recipes/incoming-kde/plasma-nm.toml
+++ b/recipes/incoming-kde/plasma-nm.toml
@@ -12,6 +12,7 @@
 # sellados. ecm_find_qmlmodule REQUIRED: org.kde.prison + org.kde.kirigamiaddons.formcard (kirigami-addons).
 name    = "plasma-nm"
 version = "6.7.2"
+license = "GPL-2.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/plasma/6.7.2/plasma-nm-6.7.2.tar.xz"
diff --git a/recipes/incoming-kde/plasma-pa.toml b/recipes/incoming-kde/plasma-pa.toml
index 00a639f3..bba0a3a1 100644
--- a/recipes/incoming-kde/plasma-pa.toml
+++ b/recipes/incoming-kde/plasma-pa.toml
@@ -7,6 +7,7 @@
 # Kirigami/KirigamiAddons son RUNTIME. gio-2.0 REQUIRED → glib-shared.
 name    = "plasma-pa"
 version = "6.7.2"
+license = "GPL-2.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/plasma/6.7.2/plasma-pa-6.7.2.tar.xz"
diff --git a/recipes/incoming-kde/plasma-wayland-protocols.toml b/recipes/incoming-kde/plasma-wayland-protocols.toml
index 5bb2e16d..91ff3063 100644
--- a/recipes/incoming-kde/plasma-wayland-protocols.toml
+++ b/recipes/incoming-kde/plasma-wayland-protocols.toml
@@ -4,6 +4,7 @@
 # CMake+ECM puro (sin Qt) ⇒ no necesita el set Qt-consumer, sólo ECM.
 name    = "plasma-wayland-protocols"
 version = "1.21.0"
+license = "MIT"
 
 [source]
 tarball = "https://download.kde.org/stable/plasma-wayland-protocols/plasma-wayland-protocols-1.21.0.tar.xz"
diff --git a/recipes/incoming-kde/plasma-workspace.toml b/recipes/incoming-kde/plasma-workspace.toml
index 6dfdaf2d..252e7d66 100644
--- a/recipes/incoming-kde/plasma-workspace.toml
+++ b/recipes/incoming-kde/plasma-workspace.toml
@@ -60,6 +60,7 @@
 # ==================================================================================================
 name    = "plasma-workspace"
 version = "6.7.2"
+license = "GPL-2.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/plasma/6.7.2/plasma-workspace-6.7.2.tar.xz"
diff --git a/recipes/incoming-kde/plasma5support.toml b/recipes/incoming-kde/plasma5support.toml
index 05ec16aa..bb9e9288 100644
--- a/recipes/incoming-kde/plasma5support.toml
+++ b/recipes/incoming-kde/plasma5support.toml
@@ -16,6 +16,7 @@
 # ===================================================================
 name    = "plasma5support"
 version = "6.7.2"
+license = "LGPL-2.1-or-later OR LGPL-3.0-or-later"
 
 [source]
 tarball = "https://download.kde.org/stable/plasma/6.7.2/plasma5support-6.7.2.tar.xz"
diff --git a/recipes/incoming-kde/qt5compat.toml b/recipes/incoming-kde/qt5compat.toml
index 15c033e9..7dba4739 100644
--- a/recipes/incoming-kde/qt5compat.toml
+++ b/recipes/incoming-kde/qt5compat.toml
@@ -4,6 +4,7 @@
 # Necesita qtdeclarative (QML) además de qtbase.
 name    = "qt5compat"
 version = "6.11.1"
+license = "LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only"
 
 [source]
 tarball = "https://download.qt.io/official_releases/qt/6.11/6.11.1/submodules/qt5compat-everywhere-src-6.11.1.tar.xz"
diff --git a/recipes/incoming-kde/util-linux.toml b/recipes/incoming-kde/util-linux.toml
index 4a4b2291..d26450f3 100644
--- a/recipes/incoming-kde/util-linux.toml
+++ b/recipes/incoming-kde/util-linux.toml
@@ -7,6 +7,7 @@
 # sigue con la canónica static. Conserva el hack del filtro -mcpu=baseline de los generadores.
 name    = "util-linux"
 version = "2.42.2"
+license = "GPL-2.0-or-later AND LGPL-2.1-or-later AND BSD-3-Clause"
 
 [source]
 tarball = "https://www.kernel.org/pub/linux/utils/util-linux/v2.42/util-linux-2.42.2.tar.xz"
diff --git a/recipes/incoming-kde/xkeyboard-config.toml b/recipes/incoming-kde/xkeyboard-config.toml
index efc3c2f0..ea61893c 100644
--- a/recipes/incoming-kde/xkeyboard-config.toml
+++ b/recipes/incoming-kde/xkeyboard-config.toml
@@ -4,6 +4,7 @@
 # Meson, sin compilación real (instala datos + .pc). nls off (evita gettext); sin libxml2 (validación).
 name    = "xkeyboard-config"
 version = "2.44"
+license = "MIT"
 
 [source]
 tarball = "https://www.x.org/releases/individual/data/xkeyboard-config/xkeyboard-config-2.44.tar.xz"
diff --git a/recipes/incoming-kde/zlib-shared.toml b/recipes/incoming-kde/zlib-shared.toml
index 35b0de5c..e4d9acdc 100644
--- a/recipes/incoming-kde/zlib-shared.toml
+++ b/recipes/incoming-kde/zlib-shared.toml
@@ -17,6 +17,7 @@
 
 name    = "zlib-shared"
 version = "1.3.1"
+license = "Zlib"
 
 [source]
 tarball = "https://github.com/madler/zlib/releases/download/v1.3.1/zlib-1.3.1.tar.gz"
diff --git a/recipes/kbd.toml b/recipes/kbd.toml
index 6f4694e3..56b2b78a 100644
--- a/recipes/kbd.toml
+++ b/recipes/kbd.toml
@@ -3,6 +3,7 @@
 # sha256 del tarball (el wrapper lo calcula), y adaptar build/install del shell de abuild.
 name    = "kbd"
 version = "2.8.0"
+license = "GPL-2.0-or-later"
 
 [source]
 tarball = "https://www.kernel.org/pub/linux/utils/kbd/kbd-2.8.0.tar.gz"
diff --git a/recipes/libadwaita.toml b/recipes/libadwaita.toml
index e00a4a95..f92982af 100644
--- a/recipes/libadwaita.toml
+++ b/recipes/libadwaita.toml
@@ -3,6 +3,7 @@
 # deps = gtk4 + su cierre .pc completo + sassc.
 name    = "libadwaita"
 version = "1.7.6"
+license = "LGPL-2.1-or-later"
 
 [source]
 tarball = "https://download.gnome.org/sources/libadwaita/1.7/libadwaita-1.7.6.tar.xz"
diff --git a/recipes/libarchive.toml b/recipes/libarchive.toml
index d0b4b4f3..9b4758f3 100644
--- a/recipes/libarchive.toml
+++ b/recipes/libarchive.toml
@@ -14,6 +14,7 @@
 
 name    = "libarchive"
 version = "3.8.1"
+license = "BSD-2-Clause"
 
 [source]
 tarball = "https://github.com/libarchive/libarchive/releases/download/v3.8.1/libarchive-3.8.1.tar.xz"
diff --git a/recipes/libcap.toml b/recipes/libcap.toml
index 672ce708..866465f6 100644
--- a/recipes/libcap.toml
+++ b/recipes/libcap.toml
@@ -18,6 +18,7 @@
 
 name    = "libcap"
 version = "2.78"
+license = "BSD-3-Clause OR GPL-2.0-only"
 
 [source]
 tarball = "https://mirrors.edge.kernel.org/pub/linux/libs/security/linux-privs/libcap2/libcap-2.78.tar.gz"
diff --git a/recipes/libdrm.toml b/recipes/libdrm.toml
index 43d59c82..cb84342c 100644
--- a/recipes/libdrm.toml
+++ b/recipes/libdrm.toml
@@ -5,6 +5,7 @@
 # los helpers intel/radeon/nouveau viejos). link=dynamic: shared lib que Mesa/mirada enlazan.
 name    = "libdrm"
 version = "2.4.134"
+license = "MIT"
 
 [source]
 tarball = "https://gitlab.freedesktop.org/mesa/libdrm/-/archive/libdrm-2.4.134/libdrm-libdrm-2.4.134.tar.gz"
diff --git a/recipes/libepoxy.toml b/recipes/libepoxy.toml
index 7ac82f5a..5dbe46da 100644
--- a/recipes/libepoxy.toml
+++ b/recipes/libepoxy.toml
@@ -1,6 +1,7 @@
 # libepoxy 1.5.10 — dispatch de OpenGL/EGL (GTK4/GSK). meson static, sin X11 (wayland/egl).
 name    = "libepoxy"
 version = "1.5.10"
+license = "MIT"
 [source]
 tarball = "https://github.com/anholt/libepoxy/archive/refs/tags/1.5.10.tar.gz"
 sha256  = "a7ced37f4102b745ac86d6a70a9da399cc139ff168ba6b8002b4d8d43c900c15"
diff --git a/recipes/libffi.toml b/recipes/libffi.toml
index c53f2603..97c59755 100644
--- a/recipes/libffi.toml
+++ b/recipes/libffi.toml
@@ -5,6 +5,7 @@
 # es superset: sigue sirviendo para el enlace estático de exes. (2026-06-27, stack gráfico tawasuyu.)
 name    = "libffi"
 version = "3.5.2"
+license = "MIT"
 [source]
 tarball = "https://github.com/libffi/libffi/releases/download/v3.5.2/libffi-3.5.2.tar.gz"
 sha256  = "f3a3082a23b37c293a4fcd1053147b371f2ff91fa7ea1b2a52e335676bac82dc"
diff --git a/recipes/libjpeg-turbo.toml b/recipes/libjpeg-turbo.toml
index 40999126..d4b22284 100644
--- a/recipes/libjpeg-turbo.toml
+++ b/recipes/libjpeg-turbo.toml
@@ -1,6 +1,7 @@
 # libjpeg-turbo 3.1.3 — JPEG (loader de imágenes). cmake static, SIMD off (sin nasm).
 name    = "libjpeg-turbo"
 version = "3.1.3"
+license = "IJG AND BSD-3-Clause AND Zlib"
 [source]
 tarball = "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/3.1.3/libjpeg-turbo-3.1.3.tar.gz"
 sha256  = "075920b826834ac4ddf97661cc73491047855859affd671d52079c6867c1c6c0"
diff --git a/recipes/libpng.toml b/recipes/libpng.toml
index e5143eff..a9b695bd 100644
--- a/recipes/libpng.toml
+++ b/recipes/libpng.toml
@@ -2,6 +2,7 @@
 # De-Alpinizada: compiler=zig-cc (migrado de gcc, matar-gcc 2026-07-16), configure-split, contra zlib del corpus, estático.
 name    = "libpng"
 version = "1.6.58"
+license = "Libpng"
 [source]
 tarball = "https://downloads.sourceforge.net/libpng/libpng-1.6.58.tar.gz"
 sha256  = "8c9b05b675ca7301a458df2c2e46f26e1d41ff36b8863f8c33530bc58c2e6225"
diff --git a/recipes/libtiff.toml b/recipes/libtiff.toml
index a924bf7b..2672cff7 100644
--- a/recipes/libtiff.toml
+++ b/recipes/libtiff.toml
@@ -2,6 +2,7 @@
 # Solo zlib+jpeg; el resto de codecs opcionales OFF (evita autodetectar .so del rootfs Alpine).
 name    = "libtiff"
 version = "4.7.0"
+license = "libtiff"
 
 [source]
 tarball = "https://download.osgeo.org/libtiff/tiff-4.7.0.tar.gz"
diff --git a/recipes/libwebp.toml b/recipes/libwebp.toml
index d943480f..acba85b8 100644
--- a/recipes/libwebp.toml
+++ b/recipes/libwebp.toml
@@ -3,6 +3,7 @@
 # sha256 del tarball (el wrapper lo calcula), y adaptar build/install del shell de abuild.
 name    = "libwebp"
 version = "1.6.0"
+license = "BSD-3-Clause"
 
 [source]
 tarball = "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.6.0.tar.gz"
diff --git a/recipes/libxkbcommon.toml b/recipes/libxkbcommon.toml
index b11906c6..f3aa8375 100644
--- a/recipes/libxkbcommon.toml
+++ b/recipes/libxkbcommon.toml
@@ -4,6 +4,7 @@
 # (En runtime usa los datos de xkeyboard-config; no es dep de build.)
 name    = "libxkbcommon"
 version = "1.7.0"
+license = "MIT"
 
 [source]
 tarball = "https://github.com/xkbcommon/libxkbcommon/archive/refs/tags/xkbcommon-1.7.0.tar.gz"
diff --git a/recipes/libxml2.toml b/recipes/libxml2.toml
index 146a6c1d..1214dff9 100644
--- a/recipes/libxml2.toml
+++ b/recipes/libxml2.toml
@@ -7,6 +7,7 @@
 # - patches CVE de Alpine conservados (seguridad).
 name    = "libxml2"
 version = "2.13.9"
+license = "MIT"
 
 [source]
 tarball = "https://download.gnome.org/sources/libxml2/2.13/libxml2-2.13.9.tar.xz"
diff --git a/recipes/libxmlb.toml b/recipes/libxmlb.toml
index a4bc3be9..aee48d70 100644
--- a/recipes/libxmlb.toml
+++ b/recipes/libxmlb.toml
@@ -1,6 +1,7 @@
 # libxmlb 0.3.22 — cache binaria de XML (appstream la exige). meson static. glib + zstd; lo opcional off.
 name    = "libxmlb"
 version = "0.3.22"
+license = "LGPL-2.1-or-later"
 
 [source]
 tarball = "https://github.com/hughsie/libxmlb/releases/download/0.3.22/libxmlb-0.3.22.tar.xz"
diff --git a/recipes/libyaml.toml b/recipes/libyaml.toml
index d18266cd..aa45a2ad 100644
--- a/recipes/libyaml.toml
+++ b/recipes/libyaml.toml
@@ -2,6 +2,7 @@
 # (el repo git no trae configure). De-Alpinizada: compiler=zig-cc (migrado de gcc, matar-gcc 2026-07-16), configure-split, estático.
 name    = "libyaml"
 version = "0.2.5"
+license = "MIT"
 [source]
 tarball = "https://github.com/yaml/libyaml/releases/download/0.2.5/yaml-0.2.5.tar.gz"
 sha256  = "c642ae9b75fee120b2d96c712538bd2cf283228d2337df2cf2988e3c02678ef4"
diff --git a/recipes/linux.toml b/recipes/linux.toml
index 0216ce29..dde2ec92 100644
--- a/recipes/linux.toml
+++ b/recipes/linux.toml
@@ -56,6 +56,7 @@
 
 name    = "linux"
 version = "6.16.12"
+license = "GPL-2.0-only WITH Linux-syscall-note"
 
 [source]
 tarball = "https://mirrors.edge.kernel.org/pub/linux/kernel/v6.x/linux-6.16.12.tar.gz"
diff --git a/recipes/lz4.toml b/recipes/lz4.toml
index 0e334aa1..a56dbe98 100644
--- a/recipes/lz4.toml
+++ b/recipes/lz4.toml
@@ -1,6 +1,7 @@
 # lz4 1.10.0 — compresión rápida. Makefile, static only.
 name    = "lz4"
 version = "1.10.0"
+license = "BSD-2-Clause AND GPL-2.0-only"
 [source]
 tarball = "https://github.com/lz4/lz4/releases/download/v1.10.0/lz4-1.10.0.tar.gz"
 sha256  = "537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b"
diff --git a/recipes/m4.toml b/recipes/m4.toml
index 645be825..ad60ad7e 100644
--- a/recipes/m4.toml
+++ b/recipes/m4.toml
@@ -9,6 +9,7 @@
 
 name    = "m4"
 version = "1.4.20"
+license = "GPL-3.0-or-later"
 
 [source]
 tarball = "https://ftp.gnu.org/gnu/m4/m4-1.4.20.tar.gz"
diff --git a/recipes/make.toml b/recipes/make.toml
index 91dfc891..9bbef4ce 100644
--- a/recipes/make.toml
+++ b/recipes/make.toml
@@ -11,6 +11,7 @@
 
 name    = "make"
 version = "4.4.1"
+license = "GPL-3.0-or-later"
 
 [source]
 tarball = "https://ftp.gnu.org/gnu/make/make-4.4.1.tar.gz"
diff --git a/recipes/mesa.toml b/recipes/mesa.toml
index 20fb5bdd..92223270 100644
--- a/recipes/mesa.toml
+++ b/recipes/mesa.toml
@@ -56,6 +56,7 @@
 # más lento). Es estrictamente mejor que no arrancar.
 name    = "mesa"
 version = "24.0.9"
+license = "MIT"
 
 [source]
 tarball = "https://archive.mesa3d.org/mesa-24.0.9.tar.xz"
diff --git a/recipes/meson.toml b/recipes/meson.toml
index f9b47374..b7f2fa76 100644
--- a/recipes/meson.toml
+++ b/recipes/meson.toml
@@ -6,6 +6,7 @@
 # árbol Python sellado. python3 es dep de RUNTIME (meson lo invoca al usarse como build-dep).
 name    = "meson"
 version = "1.11.1"
+license = "Apache-2.0"
 
 [source]
 tarball = "https://github.com/mesonbuild/meson/releases/download/1.11.1/meson-1.11.1.tar.gz"
diff --git a/recipes/musl.toml b/recipes/musl.toml
index 36a2e6af..6eb2807e 100644
--- a/recipes/musl.toml
+++ b/recipes/musl.toml
@@ -6,6 +6,7 @@
 
 name    = "musl"
 version = "1.2.5"
+license = "MIT"
 
 [source]
 tarball = "https://musl.libc.org/releases/musl-1.2.5.tar.gz"
diff --git a/recipes/openssl.toml b/recipes/openssl.toml
index edada6ed..4a614ed5 100644
--- a/recipes/openssl.toml
+++ b/recipes/openssl.toml
@@ -16,6 +16,7 @@
 
 name    = "openssl"
 version = "3.5.4"
+license = "Apache-2.0"
 
 [source]
 tarball = "https://github.com/openssl/openssl/releases/download/openssl-3.5.4/openssl-3.5.4.tar.gz"
diff --git a/recipes/pango.toml b/recipes/pango.toml
index 2fab4f94..e2c4e462 100644
--- a/recipes/pango.toml
+++ b/recipes/pango.toml
@@ -1,6 +1,7 @@
 # pango 1.57.1 — layout/render de texto i18n (base de GTK). meson static.
 name    = "pango"
 version = "1.57.1"
+license = "LGPL-2.0-or-later"
 [source]
 tarball = "https://download.gnome.org/sources/pango/1.57/pango-1.57.1.tar.xz"
 sha256  = "e65d6d117080dc3aeeb7d8b4b3b518f7383aa2e6cfce23117c623cd624764c2f"
diff --git a/recipes/patch.toml b/recipes/patch.toml
index 7730bce7..bd858045 100644
--- a/recipes/patch.toml
+++ b/recipes/patch.toml
@@ -8,6 +8,7 @@
 
 name    = "patch"
 version = "2.8"
+license = "GPL-3.0-or-later"
 
 [source]
 tarball = "https://ftp.gnu.org/gnu/patch/patch-2.8.tar.gz"
diff --git a/recipes/pcre2.toml b/recipes/pcre2.toml
index 09dc36b8..01225db9 100644
--- a/recipes/pcre2.toml
+++ b/recipes/pcre2.toml
@@ -5,6 +5,7 @@
 # - sin --enable-pcre2test-libedit (libedit no está en corpus, es solo para el test interactivo).
 name    = "pcre2"
 version = "10.47"
+license = "BSD-3-Clause"
 
 [source]
 tarball = "https://github.com/PhilipHazel/pcre2/releases/download/pcre2-10.47/pcre2-10.47.tar.bz2"
diff --git a/recipes/perl.toml b/recipes/perl.toml
index d4587b03..8fc13a1e 100644
--- a/recipes/perl.toml
+++ b/recipes/perl.toml
@@ -10,6 +10,7 @@
 # escritorio, es herramienta de build. Configure propio de perl (no autotools). zig cc vía wrapper.
 name    = "perl"
 version = "5.40.2"
+license = "Artistic-1.0-Perl OR GPL-1.0-or-later"
 
 [source]
 tarball = "https://www.cpan.org/src/5.0/perl-5.40.2.tar.gz"
diff --git a/recipes/pixman.toml b/recipes/pixman.toml
index 18528842..e846c73d 100644
--- a/recipes/pixman.toml
+++ b/recipes/pixman.toml
@@ -2,6 +2,7 @@
 # para composición/recorte en CPU). Proyecto meson, sin deps externas. Sin tests/demos/gtk.
 name    = "pixman"
 version = "0.44.2"
+license = "MIT"
 
 [source]
 tarball = "https://gitlab.freedesktop.org/pixman/pixman/-/archive/pixman-0.44.2/pixman-pixman-0.44.2.tar.gz"
diff --git a/recipes/pkgconf.toml b/recipes/pkgconf.toml
index c9135c3a..65729b92 100644
--- a/recipes/pkgconf.toml
+++ b/recipes/pkgconf.toml
@@ -10,6 +10,7 @@
 
 name    = "pkgconf"
 version = "2.5.1"
+license = "ISC"
 
 [source]
 tarball = "https://distfiles.ariadne.space/pkgconf/pkgconf-2.5.1.tar.gz"
diff --git a/recipes/python3.toml b/recipes/python3.toml
index 5b53daf7..9768d703 100644
--- a/recipes/python3.toml
+++ b/recipes/python3.toml
@@ -21,6 +21,7 @@
 
 name    = "python3"
 version = "3.12.10"
+license = "Python-2.0"
 
 [source]
 tarball = "https://www.python.org/ftp/python/3.12.10/Python-3.12.10.tgz"
diff --git a/recipes/samurai.toml b/recipes/samurai.toml
index 085a70f8..7f719b69 100644
--- a/recipes/samurai.toml
+++ b/recipes/samurai.toml
@@ -4,6 +4,7 @@
 # con "reallocarray: No error information" — el patrón conocido (binutils/elfutils/flex).
 name    = "samurai"
 version = "1.3"
+license = "ISC"
 
 [source]
 tarball = "https://github.com/michaelforney/samurai/releases/download/1.3/samurai-1.3.tar.gz"
diff --git a/recipes/sed.toml b/recipes/sed.toml
index 72721e89..d336470f 100644
--- a/recipes/sed.toml
+++ b/recipes/sed.toml
@@ -3,6 +3,7 @@
 # sha256 del tarball (el wrapper lo calcula), y adaptar build/install del shell de abuild.
 name    = "sed"
 version = "4.10"
+license = "GPL-3.0-or-later"
 
 [source]
 tarball = "https://ftp.gnu.org/pub/gnu/sed/sed-4.10.tar.xz"
diff --git a/recipes/sqlite.toml b/recipes/sqlite.toml
index 4dfbdf2a..057839f7 100644
--- a/recipes/sqlite.toml
+++ b/recipes/sqlite.toml
@@ -1,6 +1,7 @@
 # sqlite 3.46.1 — base de datos embebida (la usan incontables apps). autotools (amalgamation), static.
 name    = "sqlite"
 version = "3.46.1"
+license = "blessing"
 [source]
 tarball = "https://sqlite.org/2024/sqlite-autoconf-3460100.tar.gz"
 sha256  = "67d3fe6d268e6eaddcae3727fce58fcc8e9c53869bdd07a0c61e38ddf2965071"
diff --git a/recipes/tar.toml b/recipes/tar.toml
index b64c788e..838c492f 100644
--- a/recipes/tar.toml
+++ b/recipes/tar.toml
@@ -3,6 +3,7 @@
 # sha256 del tarball (el wrapper lo calcula), y adaptar build/install del shell de abuild.
 name    = "tar"
 version = "1.35"
+license = "GPL-3.0-or-later"
 
 [source]
 tarball = "https://ftp.gnu.org/gnu/tar/tar-1.35.tar.xz"
diff --git a/recipes/util-linux.toml b/recipes/util-linux.toml
index 0363bd28..df64eff9 100644
--- a/recipes/util-linux.toml
+++ b/recipes/util-linux.toml
@@ -3,6 +3,7 @@
 # mount/umount/lsblk/blkid/fdisk/lscpu (utilería base del sistema).
 name    = "util-linux"
 version = "2.42.2"
+license = "GPL-2.0-or-later AND LGPL-2.1-or-later AND BSD-3-Clause"
 
 [source]
 tarball = "https://www.kernel.org/pub/linux/utils/util-linux/v2.42/util-linux-2.42.2.tar.xz"
diff --git a/recipes/wayland-protocols.toml b/recipes/wayland-protocols.toml
index e973f8ee..e5fd42dd 100644
--- a/recipes/wayland-protocols.toml
+++ b/recipes/wayland-protocols.toml
@@ -3,6 +3,7 @@
 # de meson chequea wayland-scanner). Sin tests.
 name    = "wayland-protocols"
 version = "1.48"
+license = "MIT"
 
 [source]
 tarball = "https://gitlab.freedesktop.org/wayland/wayland-protocols/-/releases/1.48/downloads/wayland-protocols-1.48.tar.xz"
diff --git a/recipes/wayland.toml b/recipes/wayland.toml
index b0168cb0..a0878a5c 100644
--- a/recipes/wayland.toml
+++ b/recipes/wayland.toml
@@ -14,6 +14,7 @@
 # docs/14 §Estado de build y el encabezado del .patch.
 name    = "wayland"
 version = "1.25.0"
+license = "MIT"
 
 [source]
 tarball = "https://gitlab.freedesktop.org/wayland/wayland/-/releases/1.25.0/downloads/wayland-1.25.0.tar.xz"
diff --git a/recipes/xz.toml b/recipes/xz.toml
index ed8cf871..0473aed3 100644
--- a/recipes/xz.toml
+++ b/recipes/xz.toml
@@ -2,6 +2,7 @@
 # De-Alpinizada: compiler=zig-cc (migrado de gcc, matar-gcc 2026-07-16), configure-split, solo la lib (--disable CLI tools), estático.
 name    = "xz"
 version = "5.8.3"
+license = "0BSD"
 [source]
 tarball = "https://github.com/tukaani-project/xz/releases/download/v5.8.3/xz-5.8.3.tar.xz"
 sha256  = "fff1ffcf2b0da84d308a14de513a1aa23d4e9aa3464d17e64b9714bfdd0bbfb6"
diff --git a/recipes/zlib.toml b/recipes/zlib.toml
index cc9cfd5a..22d65437 100644
--- a/recipes/zlib.toml
+++ b/recipes/zlib.toml
@@ -14,6 +14,7 @@
 
 name    = "zlib"
 version = "1.3.1"
+license = "Zlib"
 
 [source]
 tarball = "https://github.com/madler/zlib/releases/download/v1.3.1/zlib-1.3.1.tar.gz"
diff --git a/recipes/zstd.toml b/recipes/zstd.toml
index 3ea80541..c7f1f388 100644
--- a/recipes/zstd.toml
+++ b/recipes/zstd.toml
@@ -2,6 +2,7 @@
 # (compresión moderna) + cola C. De-Alpinizada: compiler=zig-cc (migrado de gcc, matar-gcc 2026-07-16), build de lib/ (no CLI), estático.
 name    = "zstd"
 version = "1.5.7"
+license = "BSD-3-Clause OR GPL-2.0-only"
 [source]
 tarball = "https://github.com/facebook/zstd/releases/download/v1.5.7/zstd-1.5.7.tar.gz"
 sha256  = "eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3"
diff --git a/scripts/licencias.sh b/scripts/licencias.sh
new file mode 100755
index 00000000..62828ba7
--- /dev/null
+++ b/scripts/licencias.sh
@@ -0,0 +1,117 @@
+#!/usr/bin/env bash
+# licencias.sh — mide y puebla el campo `license` de las recetas. SDD 19 §2.1.
+#
+# ── EL PROBLEMA ─────────────────────────────────────────────────────────────────────────────────
+# Medido el 2026-08-07: **0 de 1141 recetas** declaraban licencia. (Un informe anterior dijo «5 de
+# 771»; los dos números estaban mal. Los «5» eran falsos positivos de `grep license`: el paquete
+# `addlicense`, el paquete `cargo-bundle-licenses`, una línea `install ... /usr/share/licenses/` y un
+# comentario. Y las recetas son 1141, no 771. Lección: contar con `grep -l <palabra>` sobre TOML
+# cuenta comentarios y nombres, no campos.)
+#
+# Sin ese dato la distro no puede cumplir la GPL cuando redistribuye binarios: la obligación es
+# acompañar el binario del texto de la licencia y poder entregar la fuente correspondiente. Es
+# BLOQUEANTE para publicar, y es la deuda que más barato sale pagar temprano: a 1141 recetas es un
+# rato de trabajo, a 2600 es un proyecto.
+#
+# ── POR QUÉ ESTO NO RE-HASHEA NADA (lo que hace la deuda pagable) ───────────────────────────────
+# `Recipe::hash_inputs` es una LISTA BLANCA: sólo entran source, compiler, target, link, patches,
+# flags, phases y deps. `license` no entra, igual que `evidence` y `slots`. O sea que se puede poblar
+# en las 1141 recetas YA SELLADAS sin mover un solo ArtifactHash ni invalidar el baseline de
+# reproducibilidad. Está clavado con un test: `licencia_round_trip_y_no_afecta_el_hash`.
+#
+# ── DÓNDE VA LA LÍNEA (trampa de TOML, cuesta una tarde si se ignora) ───────────────────────────
+# En TOML una clave suelta después de un `[table]` pertenece a ESA tabla. `license = "..."` puesto al
+# final del fichero acaba dentro de `[deps]` y **se pierde en silencio**, porque serde ignora los
+# campos que no conoce: no hay error, simplemente no está. Va ARRIBA, junto a `name` y `version`.
+#
+# ── LA REGLA DE HONESTIDAD: NO SE ADIVINA ───────────────────────────────────────────────────────
+# Declarar mal una licencia es PEOR que no declararla — convierte un hueco visible en una afirmación
+# falsa sobre la que alguien va a construir. Por eso este script no infiere de la URL ni del nombre.
+# Sólo puebla desde `docs/licencias-conocidas.tsv`, una tabla curada y auditable a mano, y desde los
+# ficheros COPYING/LICENSE de árboles de fuente realmente extraídos. Lo que no tiene evidencia se
+# queda vacío y SE CUENTA. Un hueco contado es deuda; un hueco rellenado a ojo es una mentira.
+#
+# ── EL ARREGLO ESTRUCTURAL, QUE ES OTRO TICKET ──────────────────────────────────────────────────
+# Poblar a mano tapa el agujero de hoy pero no impide que vuelva a abrirse: cada receta nueva nace
+# sin licencia. El cierre de verdad es capturarla donde ya pasa el dato gratis — hammer YA descarga y
+# extrae cada tarball al construir, así que la fase de fetch puede guardar el COPYING/LICENSE del
+# árbol junto al artefacto, sin descargas extra. Y el texto de la licencia dentro del paquete conviene
+# inyectarlo en `hammer pack`, NO en la fase install de la receta: pack es aguas abajo del
+# ArtifactHash, así que cumplir la GPL no cuesta reconstruir el corpus. Ver SDD 19.
+#
+# Uso:  scripts/licencias.sh                 informe de cobertura (default, no toca nada)
+#       scripts/licencias.sh --sembrar       escribe las licencias de la tabla curada
+#       scripts/licencias.sh --faltan        lista las recetas sin licencia, para trabajarlas
+set -uo pipefail
+ROOT="$(cd "$(dirname "$0")/.." && pwd)"; cd "$ROOT"
+TABLA="${TABLA:-docs/licencias-conocidas.tsv}"
+MODO="${1:-informe}"
+
+recetas() { ls recipes/*.toml recipes/incoming-*/*.toml 2>/dev/null; }
+
+# Un campo `license` de verdad: clave a principio de línea, ANTES del primer `[table]`.
+# (Ver arriba: después de un `[table]` la clave pertenece a la tabla y no la lee el parser.)
+tiene_licencia() {
+  awk '/^\[/{exit} /^[[:space:]]*license[[:space:]]*=/{print "si"; exit}' "$1" | grep -q si
+}
+
+licencia_de() {
+  awk -F'"' '/^\[/{exit} /^[[:space:]]*license[[:space:]]*=/{print $2; exit}' "$1"
+}
+
+case "$MODO" in
+# ── sembrar: escribir las licencias de la tabla curada ──────────────────────────────────────────
+--sembrar)
+  [ -s "$TABLA" ] || { echo "!!  falta la tabla curada: $TABLA"; exit 1; }
+  puestas=0; ya=0; sin_receta=0
+  while IFS=$'\t' read -r pkg lic; do
+    case "$pkg" in ''|'#'*) continue ;; esac
+    [ -n "${lic:-}" ] || continue
+    hallada=0
+    for f in recipes/"$pkg".toml recipes/incoming-*/"$pkg".toml; do
+      [ -f "$f" ] || continue
+      hallada=1
+      if tiene_licencia "$f"; then ya=$((ya+1)); continue; fi
+      # Insertar DESPUÉS de la línea `version = ...`, que está en la raíz del TOML y antes de
+      # cualquier `[table]`. Es el único sitio seguro (ver la trampa de TOML en la cabecera).
+      if grep -qE '^version[[:space:]]*=' "$f"; then
+        awk -v L="license = \"$lic\"" '
+          {print}
+          !done && /^version[[:space:]]*=/ {print L; done=1}
+        ' "$f" > "$f.tmp" && mv "$f.tmp" "$f"
+        puestas=$((puestas+1))
+      else
+        echo "  ??  $pkg: sin línea `version` en la raíz, no toco el fichero"
+      fi
+    done
+    [ "$hallada" = 0 ] && sin_receta=$((sin_receta+1))
+  done < "$TABLA"
+  echo "==> sembradas $puestas · ya tenían $ya · en la tabla pero sin receta $sin_receta"
+  echo "==> verificá que el hash NO se movió:  ./scripts/store-gc.sh   (debe dar los mismos vigentes)"
+  ;;
+
+# ── faltan: la lista de trabajo ─────────────────────────────────────────────────────────────────
+--faltan)
+  recetas() { ls recipes/*.toml recipes/incoming-*/*.toml 2>/dev/null; }
+  for f in $(recetas); do tiene_licencia "$f" || basename "$f" .toml; done
+  ;;
+
+# ── informe (default) ───────────────────────────────────────────────────────────────────────────
+*)
+  tot=0; con=0
+  : > /tmp/.lic-cuenta.$$
+  for f in $(recetas); do
+    tot=$((tot+1))
+    if tiene_licencia "$f"; then con=$((con+1)); licencia_de "$f" >> /tmp/.lic-cuenta.$$; fi
+  done
+  pct=0; [ "$tot" -gt 0 ] && pct=$((con*100/tot))
+  echo "==> licencias declaradas: $con de $tot recetas (${pct}%)"
+  if [ "$con" -gt 0 ]; then
+    echo "==> reparto:"
+    sort /tmp/.lic-cuenta.$$ | uniq -c | sort -rn | head -25 | sed 's/^/    /'
+  fi
+  rm -f /tmp/.lic-cuenta.$$
+  [ "$con" -lt "$tot" ] && echo "==> faltan $((tot-con)). Lista:  scripts/licencias.sh --faltan"
+  echo "==> recordá: NO se rellena a ojo. Sin evidencia, se deja vacío y se cuenta (ver cabecera)."
+  ;;
+esac
diff --git a/scripts/respaldo-storagebox.sh b/scripts/respaldo-storagebox.sh
index 02c06d6e..b1e2bcd9 100755
--- a/scripts/respaldo-storagebox.sh
+++ b/scripts/respaldo-storagebox.sh
@@ -9,11 +9,36 @@
 # El Storage Box SÍ es el producto para esto: se monta desde acá (SSHFS/CIFS) y habla rsync/borg/
 # restic por SSH. BX11 = 1 TiB por €3,20/mes, sin coste de alta. Ver [[capacidad-disco-proyeccion]].
 #
+# ── ES INTERRUMPIBLE Y CONTINUABLE. ESE ES EL DISEÑO, NO UN EFECTO SECUNDARIO ───────────────────
+# Medido en la oficina el 2026-08-07: el enlace da **8 Mbps de subida**, idénticos por cable (eth0)
+# y por wifi (wlan0) ⇒ el cuello NO está en el laptop, está en el uplink. Con 128 G de store eso son
+# ~35 h sin comprimir. Por lo tanto el respaldo NO se completa en una sentada, y todo el script está
+# construido alrededor de esa realidad:
+#   · rsync incremental: lo ya subido no se vuelve a subir (el store es CAS, los ficheros nunca
+#     cambian, así que la comparación por tamaño+fecha es exacta y baratísima).
+#   · `--partial-dir`: un fichero cortado a la mitad conserva el trozo y la próxima corrida lo
+#     TERMINA en vez de empezarlo de cero. Sin esto, cortar a mitad de un artefacto de 500 MB
+#     tiraría los 400 MB ya subidos.
+#   · Se puede matar con Ctrl-C o apagar el laptop en cualquier momento. Se relanza y sigue.
+#
+# ── EL ORDEN ES POR VALOR IRREEMPLAZABLE, NO POR TAMAÑO ─────────────────────────────────────────
+# Como no cabe todo en una ventana, lo que sube primero importa. De más a menos crítico:
+#   1. ESTADO Y REPO (~cientos de MB, minutos). Recetas, scripts, SDDs, el grafo. Es el CEREBRO: con
+#      esto solo, el store entero se reconstruye — caro en CPU, pero se reconstruye. Sin esto no hay
+#      proyecto. Va primero aunque ya esté espejado a GitHub, porque un respaldo que depende de otro
+#      respaldo no es un respaldo.
+#   2. HUÉRFANOS del store (~4,5 G). Artefactos que son el ÚNICO ejemplar de su receta: su hash
+#      vigente no está sellado, así que perderlos es perder trabajo real, no reconstruible en dos
+#      minutos. Es el material genuinamente irreemplazable y es sorprendentemente barato.
+#   3. EL RESTO DEL STORE (~128 G). Meses de CPU, pero todo reconstruible desde (1).
+#
 # ── POR QUÉ rsync PLANO Y NO borg/restic ────────────────────────────────────────────────────────
 # El store es CAS: los ficheros son INMUTABLES y se nombran por hash. Nunca cambian, sólo aparecen o
 # desaparecen. Para eso rsync incremental es exactamente lo correcto y no hay que mantener repos ni
-# claves de cifrado. borg comprimiría 4x (el 79% del store es `.debug_`), pero 126 G en 1 TiB no
-# aprieta a nadie: no vale la complejidad todavía. Si algún día aprieta, ahí sí borg.
+# claves de cifrado. Sí se usa COMPRESIÓN zstd, que aquí no es un lujo: medido, 53 MB tardan 53 s sin
+# comprimir y 27 s con `--compress-choice=zstd` — **el doble de rendimiento efectivo**, porque el 79%
+# del contenido del store son secciones `.debug_*` y comprimen como texto. Con un enlace de 8 Mbps la
+# CPU sobra y el ancho de banda es el recurso escaso: comprimir siempre gana.
 #
 # ── EL STORE NO SE BORRA EN EL DESTINO, A PROPÓSITO ─────────────────────────────────────────────
 # `--delete` NO va sobre el store. Un respaldo que replica los borrados no protege del borrado por
@@ -22,6 +47,7 @@
 # deliberada y aparte. El repo sí va con `--delete`, que para eso está git.
 #
 # Uso:  scripts/respaldo-storagebox.sh [--seco]
+#       Relanzalo cuantas veces quieras: continúa donde quedó.
 set -eu
 
 SB_USER="${SB_USER:-u647150}"
@@ -33,33 +59,43 @@ RAIZ="${RAIZ:-/home/sergio/hammer}"
 SECO=""
 [ "${1:-}" = "--seco" ] && SECO="--dry-run"
 
-SSH_CMD="ssh -p $SB_PORT -i $KEY -o StrictHostKeyChecking=accept-new"
+# `-4` a propósito: el box tiene AAAA y el laptop tiene IPv6 sólo en wlan0, así que sin esto la ruta
+# por cable nunca se usaría aunque el cable estuviera enchufado.
+SSH_CMD="ssh -4 -p $SB_PORT -i $KEY -o StrictHostKeyChecking=accept-new -o ServerAliveInterval=30"
 
-if ! $SSH_CMD "$SB_USER@$SB_HOST" true 2>/dev/null; then
+# Opciones comunes. `--partial-dir` es lo que hace el respaldo continuable a mitad de fichero.
+RS="rsync -a --partial-dir=.rsync-partial --info=progress2 -z --compress-choice=zstd --compress-level=3"
+
+if ! $SSH_CMD "$SB_USER@$SB_HOST" 'df -h .' >/dev/null 2>&1; then
   echo "!!  No hay SSH al Storage Box ($SB_HOST:$SB_PORT)."
   echo "!!  Si acabás de crearlo, el DNS tarda unos minutos en propagar. Reintentá."
   exit 1
 fi
 
 echo "==> destino: $SB_USER@$SB_HOST:$SB_PORT  ${SECO:+(SECO)}"
-$SSH_CMD "$SB_USER@$SB_HOST" 'mkdir -p hammer/store hammer/repo hammer/estado' 2>/dev/null || true
+$SSH_CMD "$SB_USER@$SB_HOST" 'mkdir hammer hammer/store hammer/repo hammer/estado' >/dev/null 2>&1 || true
 
-# 1. EL STORE — lo caro e irreemplazable (meses de CPU). Sin --delete, ver cabecera.
-echo "==> store"
-rsync -a --info=progress2 --partial $SECO -e "$SSH_CMD" \
-  "$RAIZ/store/" "$SB_USER@$SB_HOST:hammer/store/"
+# ── 1. EL CEREBRO: estado + repo. Minutos, y es lo que no se puede perder. ──────────────────────
+echo "==> [1/3] estado (el grafo: qué había construido y con qué hash)"
+$RS $SECO -e "$SSH_CMD" "$RAIZ/docs/state/" "$SB_USER@$SB_HOST:hammer/estado/" || true
 
-# 2. EL REPO — barato, y ya está espejado a GitHub privado; va igual para tener todo en un sitio.
-#    Se excluye lo regenerable y lo gigante: store (arriba), work, target, .dev-fs.
-echo "==> repo"
-rsync -az --delete $SECO -e "$SSH_CMD" \
-  --exclude /store --exclude /work --exclude /target --exclude /.dev-fs \
-  --exclude /dist --exclude /.scratch \
+echo "==> [2/3] repo (recetas, scripts, SDDs — el cerebro; con esto solo se reconstruye el store)"
+$RS --delete $SECO -e "$SSH_CMD" \
+  --exclude /store --exclude /store-rust --exclude /store-kern \
+  --exclude /work --exclude /target --exclude /.dev-fs --exclude /dist --exclude /.scratch \
   "$RAIZ/" "$SB_USER@$SB_HOST:hammer/repo/"
 
-# 3. El grafo de estado, aparte, para poder mirar qué había sin desempacar nada.
-echo "==> estado"
-rsync -az $SECO -e "$SSH_CMD" "$RAIZ/docs/state/" "$SB_USER@$SB_HOST:hammer/estado/" 2>/dev/null || true
+# ── 2 y 3. EL STORE, huérfanos primero. ────────────────────────────────────────────────────────
+# La lista de huérfanos la produce `store-gc.sh` como subproducto; si no hay una reciente, se sube
+# el store entero en un solo paso y santas pascuas (rsync es incremental: no se pierde nada).
+echo "==> [3/3] store (128 G a 8 Mbps ⇒ NO cabe en una sentada; esto se corta y se continúa)"
+HUER="$(ls -t "$RAIZ"/work/store-gc-huerfanos-*.txt 2>/dev/null | head -1)"
+if [ -n "$HUER" ] && [ -s "$HUER" ]; then
+  echo "    · huérfanos primero ($(wc -l < "$HUER") artefactos, únicos ejemplares) — desde $HUER"
+  $RS $SECO -e "$SSH_CMD" --files-from="$HUER" "$RAIZ/store/" "$SB_USER@$SB_HOST:hammer/store/" || true
+fi
+echo "    · resto del store"
+$RS $SECO -e "$SSH_CMD" "$RAIZ/store/" "$SB_USER@$SB_HOST:hammer/store/"
 
 echo "==> ocupación en el Storage Box:"
-$SSH_CMD "$SB_USER@$SB_HOST" 'df -h . 2>/dev/null | tail -1; du -sh hammer/* 2>/dev/null'
+$SSH_CMD "$SB_USER@$SB_HOST" 'df -h .' 2>/dev/null | tail -2