1# Runbook — COSMIC, el cuarto escritorio
2
3Estado al 2026-08-03: **COSMIC COMPONE EN QEMU.** `cosmic-comp` arranca sobre el kernel de hammer con
4arje-zero como PID1, toma el DRM, expone `wayland-1` y **presenta frames**: pantalla completa en el
5gris de COSMIC `(39,41,42)` con el cursor dibujado, 134 colores distintos. Falta que los clientes se
6conecten. Este documento se escribe mientras se hace, no después.
7
8
9
10COSMIC es el escritorio de System76, en Rust sobre **smithay** (compositor) e **iced/libcosmic**
11(clientes). Es el cuarto de hammer, tras [mirada](mirada-usb-nvidia.md),
12[KDE Plasma 6](kde-qemu-desktop.md) y [GNOME](gnome-qemu-desktop.md).
13
14## Por qué NO se parece a los dos anteriores
15
16KDE y GNOME costaron lo que costaron por la misma razón: una torre de C con Qt o
17glib/GObject/introspección debajo, donde cada capa se paga entera antes de ver un píxel. COSMIC no
18tiene esa torre. La primera receta —`cosmic-session`— salió **a la primera y sin una sola dep en C**:
19ELF estático, cero `NEEDED`.
20
21Lo que sí hereda es lo que ya está pago:
22
23| lo que COSMIC necesita | de dónde sale | costo |
24|---|---|---|
25| las deps en C de smithay (drm, gbm, seat, input, udev, xkb, pixman) | `mirada-compositor`, que ya es un compositor smithay de este lab | **cero** — misma lista |
26| `libdisplay-info` + `hwdata` | copiadas byte a byte de la cola GNOME | **cero** — mismo hash, artefacto ya sellado |
27| `bash`, `dbus-run-session`, `hicolor-icon-theme`, mesa-llvmpipe | corpus | cero |
28| el andamiaje de arranque en QEMU (`run-qemu-desktop.sh`, base rootfs, console-getty, screendump) | campañas KDE/GNOME | cero |
29
30La deuda propia que sí trae, y que ninguna de las otras campañas tocó, es la del **unwinder de la std
31de rustc** cuando el enlace no es estático — la misma que ya está documentada para las recetas Cargo
32del corpus.
33
34## El pin es de la SUITE, no de cada paquete
35
36COSMIC versiona **todos** sus repos en lockstep con el tag `epoch-N`. Comprobado repo por repo: los
37doce componentes están en `epoch-1.5.0`.
38
39**No mezclar epochs entre piezas.** Comparten protocolos propios (`cosmic-protocols`) y el formato de
40configuración (`cosmic-config`); un desajuste ahí no da error de compilación, da un escritorio que
41arranca y no se entiende consigo mismo — que es mucho más caro de diagnosticar.
42
43## La cola
44
45`recipes/incoming-cosmic/`, aislada (otro agente comparte el repo; nunca `git add -A`).
46
47Las deps se resuelven **hermano → padre**: desde `incoming-cosmic/` se ven las recetas de
48`recipes/`, pero **no** las de `incoming-gnome/` ni `incoming-kde/`. Por eso `libdisplay-info` y
49`hwdata` entran copiadas.
50
51**Copiar es gratis sólo si TODA la clausura transitiva resuelve a lo mismo.** El ArtifactHash no
52depende de la ruta de la receta, así que un fichero idéntico *cuyas deps resuelven igual* da el mismo
53hash ⇒ cache hit sobre el artefacto que ya existe. Se cumplió con `libdisplay-info`, `hwdata` y
54`nasm`, y se verificó con `hammer hash` **antes** de copiar (`b3:260f0519`, `b3:bdc36cf9`,
55`b3:33383070`), no después. Dónde deja de cumplirse —y por qué importa— está en la sección de
56`cosmic-settings-daemon`, más abajo.
57
58## Qué levanta la sesión, y por lo tanto el orden de ataque
59
60`cosmic-session/src/main.rs` es la lista autoritativa —el mismo método que cerró la capa JS de GNOME:
61*leé el fichero donde el programa declara lo que arranca, no lo descubras de a uno*:
62
63```
64cosmic-comp → cosmic-notifications · cosmic-panel · cosmic-app-library
65 → cosmic-launcher · cosmic-workspaces · cosmic-osd · cosmic-bg · cosmic-idle
66```
67
68Más `cosmic-settings-daemon` y `xdg-desktop-portal-cosmic` fuera de esa lista.
69
70| pieza | estado | hash |
71|---|---|---|
72| `cosmic-session` | ✅ sellado | `b3:81f02351` |
73| `cosmic-icons` | ✅ sellado (671 SVGs) | `b3:94d06f77` |
74| `cosmic-bg` | ✅ sellado (estático) | `b3:7b4962f0` |
75| `dav1d` 1.5.4 | ✅ sellado (dep arrastrada, ver gotcha 8) | `b3:aab7a874` |
76| **`cosmic-comp`** | ✅ **SELLADO — el gate** | `b3:b8abc52f` |
77| `cosmic-idle` | ✅ sellado (estático) | `b3:175632b7` |
78| `cosmic-osd` | 🔨 en construcción | — |
79| `cosmic-panel` · `cosmic-launcher` · `cosmic-app-library` · `cosmic-workspaces-epoch` · `cosmic-notifications` | receta escrita, sin construir | — |
80| `cosmic-settings-daemon` | ni receta | — |
81
82**El orden no es el de la lista de arranque.** `cosmic-bg` va antes que el panel a propósito: habla
83Wayland con `smithay-client-toolkit` y **no toca libcosmic ni iced**, mientras que panel, launcher,
84osd y workspaces son todos clientes de iced. Con el fondo sellado, «pinta el fondo pero no el panel»
85separa la capa de UI del transporte; sin él, las dos preguntas llegan juntas.
86
87### `cosmic-settings-daemon`: por qué queda al final y NO se resuelve copiando
88
89Es el único con cadena propia. Su workspace incluye `audio-server` como dep de path **no opcional**,
90que arrastra `cosmic-pipewire` → `pipewire-sys` → `libpipewire-0.3` por pkg-config. Y `pipewire` hoy
91vive sólo en las colas GNOME y KDE.
92
93**Acá el truco de copiar deja de ser gratis, y eso corrige lo que dice más arriba.** La regla completa
94es: *copiar una receta entre colas es gratis sólo si **toda su clausura transitiva** resuelve a lo
95mismo.* Se cumplió con `libdisplay-info`, `hwdata` y `nasm` —sus deps están todas en el corpus—, y
96**no** se cumple con pipewire: de sus catorce deps, cinco viven sólo en colas ajenas
97(`dbus-shared`, `alsa-lib`, `libsndfile`, `pulseaudio`, `zlib-shared`) y —lo decisivo— **`glib`
98resolvería distinto**. Desde `incoming-gnome`, `glib` es la sombra de esa cola (`b3:f6ccdf98`); desde
99`incoming-cosmic` sería la del corpus (`b3:93d2cad0`). Son recetas distintas, no la misma en dos
100lugares.
101
102O sea que copiar produciría un **segundo pipewire** construido contra otra glib, arrastrando su cadena
103entera. Y eso no es sólo caro: es exactamente el cuadro que la campaña GNOME ya midió y evitó —dos
104glib distintas con 370 rutas solapadas y dos registros de GType en un proceso—.
105
106Antes de tocar nada se midió con `yupana radio` (la puerta única, nunca `grep`): `pipewire` tiene
107radio 1 en GNOME y 2 en KDE, pero `glib` del corpus tiene **21 dependientes transitivos y afecta la
108imagen escritorio-gnome**. La decisión correcta no es de esta campaña: es *dónde vive pipewire en el
109corpus*, y es una decisión de catálogo, no de COSMIC. Hasta entonces, el escritorio arranca sin
110settings-daemon —se pierden brillo, tema automático y perfiles de audio, no el escritorio—.
111
112## 🚪 EL GATE PASADO: cosmic-comp compila (2026-08-03)
113
114`b3:b8abc52f`, 51 min 39 s de cargo con `CARGO_BUILD_JOBS=1` y `lto = "fat"`. **A la primera**, salvo
115el `--bin` del gotcha 1. Sin un solo parche a la fuente.
116
117Lo que dice el enlace es la parte que vale, porque es la medición y no la impresión:
118
119```
120NEEDED: libdisplay-info.so.2 · libgbm.so.1 · libseat.so.1 · libudev.so.1
121 libinput.so.10 · libpixman-1.so.0 · libxkbcommon.so.0 · libc.so
122```
123
124Ocho, y las siete primeras son **exactamente** los backends de smithay que la receta declara. La
125octava es musl. **No hay `libgcc_s`** — o sea que el compositor no arrastra la deuda del unwinder que
126sí tienen varias recetas Cargo del corpus, porque acá el enlace es dinámico contra musl y no hay
127`+crt-static` que forzar. Y **no aparece nada que no se haya declarado**: la clausura de build y la de
128runtime coinciden, que es la propiedad que uno quiere y casi nunca se cumple gratis.
129
130Que un compositor Wayland completo —DRM, GBM, EGL, libinput, seat, Vulkan, XWayland, un renderer
131multi-GPU y una capa de UI en iced— cierre con ocho librerías compartidas y ningún parche es el
132resultado más limpio de las cuatro campañas de escritorio.
133
134## 🖥 EL PRIMER ARRANQUE: qué falló y qué no (2026-08-03)
135
136Tres imágenes y tres causas distintas, todas medidas. Vale dejarlas porque **ninguna estaba en el
137camino que uno anticipa**, y las tres se manifestaron a varias capas de distancia de su causa.
138
139**1. `cosmic-session` no es opcional en el sentido que yo asumí — y `cosmic-settings-daemon` tampoco.**
140El compositor arrancó perfecto y le devolvió a la sesión su `WAYLAND_DISPLAY=wayland-1`; el gestor
141entonces intentó lanzar `cosmic-settings-daemon`, no lo encontró, y **panickeó**:
142
143```
144INFO cosmic_session: got environmental variables from cosmic-comp: [("WAYLAND_DISPLAY", "wayland-1")]
145ERROR panic: 'failed to start settings daemon: NotFound' src/main.rs:255
146```
147
148Es un `.expect()`, sin feature que lo apague. **Esto corrige lo que decía la sección de
149cosmic-settings-daemon**: no es que sin él se pierdan el brillo y el tema, es que **no hay sesión**.
150Queda por eso el modo `bare` en `cosmic-start` —el camino que upstream ya soporta como
151`install-bare-session`—: se lanza el compositor solo y los clientes a mano contra su WAYLAND_DISPLAY.
152Separa «¿compone?» de «¿arranca la sesión?», que son dos preguntas y estaban viniendo juntas.
153
154**2. La pantalla negra era `libz.so.1`.** El corpus trae `zlib` estática, que le alcanza a todo el
155cierre de COSMIC. Pero `kms_swrast_dri.so` es un `.so` que mesa abre con `dlopen` y NEEDea
156`libz.so.1`:
157
158```
159MESA-LOADER: failed to open kms_swrast: Error loading shared library libz.so.1
160WARN cosmic_comp::backend::kms: Failed to add device /dev/dri/card0: Failed to initialize GBM device
161```
162
163Sin driver de software no hay GBM, y **el compositor arranca igual y presenta una pantalla negra**. La
164causa está a tres capas del síntoma. Se arregla agregando `zlib-shared` como raíz del hidratador.
165
166**3. Los clientes NO pueden ser estáticos.** `cosmic-bg` moría con «The wayland library could not be
167loaded» **teniendo `libwayland-client.so.0` en el rootfs**. `wayland-client` entra con la feature
168`dlopen`: carga libwayland en runtime en vez de enlazarla, y **un binario musl estático no tiene
169dlopen funcional**. No faltaba la librería: faltaba poder abrirla. Todos los clientes pasan a
170`link = "dynamic"`.
171
172**Lo que NO falló, y conviene decirlo:** el compositor no necesitó un solo parche, ni una perilla de
173KMS, ni forzar modeset legacy. Las cuatro perillas de render por software heredadas de KDE y GNOME
174(`GBM_ALWAYS_SOFTWARE`, `kms_swrast`, `LP_NUM_THREADS=1`) alcanzaron. GNOME costó cinco muros para
175llegar a este punto; COSMIC costó tres, y ninguno era del compositor.
176
177### Dos carreras con /run, la misma causa
178
179`dbus-daemon --system` falló con «Failed to bind socket /run/dbus/system_bus_socket: No such file or
180directory» **aunque el directorio se creaba al principio del script**, y dbus-run-session se quejó
181por su lado de que `XDG_RUNTIME_DIR` no existía. Entre el arranque del getty y el uso pasan varios
182segundos en los que arje-zero sigue montando, y un tmpfs sobre `/run` se lleva puesto lo que hubiera.
183**Crear justo antes de usar** es la única forma de no depender de ese orden; comprobar al principio y
184usar al final es exactamente cómo se cuela una carrera — la misma forma del bug de PolicyKit1 que se
185cerró hoy en GNOME.
186
187## Gotchas medidos (no previstos)
188
1891. **`cargo rustc --` exige UN target.** La fase compile del lab pasa flags tras el `--`, y cualquier
190 paquete con binario + lib + ejemplos corta con «extra arguments to `rustc` can only be passed to
191 one target». Se arregla con `flags = ["--bin", "<nombre>"]`. Le pasa a cosmic-comp y a cosmic-bg
192 (workspace con `config/`), y le va a pasar a casi todos: **ponelo desde el principio.**
1932. **`start-cosmic` es bash de verdad, no sh.** Usa `mapfile`, `[[ ]]` y expansión indirecta
194 `${!var}`; con el busybox del rootfs no arranca. `bash` ya está en el corpus, pero **tiene que
195 entrar en la imagen** — y no lo pide ningún `[deps]`, así que no lo va a traer nadie por accidente.
196 Lo mismo con `dbus-run-session` (de `dbus`), que es lo que el script exec-uta al final.
1973. **`rust-toolchain.toml` con `channel = "1.93"` es inerte acá** — es una perilla de rustup, y el
198 sandbox tiene el rustc de Alpine (1.96) pelado. Conviene saberlo antes de «arreglarlo».
1994. **`cosmic.desktop` existe DOS veces** (en cosmic-session y en cosmic-comp) con contenido distinto
200 y en la misma ruta. El de cosmic-comp es el de la «bare session» y apunta a `/usr/bin/cosmic-service`,
201 que vive en la rama de systemd que acá no va. Se instala **sólo** el de cosmic-session; si entraran
202 los dos, quién gana depende del orden de hidratación y el que pierde es el bueno.
2035. **`libsystemd` no es systemd.** La feature `default = ["systemd"]` de cosmic-comp trae el crate
204 `libsystemd`, que es una **reimplementación en Rust** del protocolo, no un binding a la C. Y
205 `logind` habla `org.freedesktop.login1`, que acá lo sirve `arje-logind-compat`. No hay que apagar
206 nada: el nombre de la feature es el del protocolo, no el del programa que lo contesta.
2076. **Las features de smithay que asustan no cuestan deps de build**: `backend_vulkan` (crate `ash`),
208 `backend_x11` (`x11rb` con `dl-libxcb`) y `xwayland` cargan todo por **dlopen**. Son deuda de
209 RUNTIME —libxcb y vulkan-loader viven hoy sólo en la cola KDE—, no un muro de compilación.
2107. **`libxkbcommon` + `pkgconf` van en TODA la cola, sin excepción — incluso en las piezas sin
211 interfaz.** Se aprendió dos veces, con dos razonamientos distintos y los dos equivocados:
212 - `cosmic-bg` fue sin `[deps]` porque «habla el protocolo en Rust y no toca C». Reventó en el
213 `build.rs` de `smithay-client-toolkit` pidiendo `xkbcommon.pc`: **sctk ENLAZA libxkbcommon**; el
214 que la dlopea es `winit`, que es otra capa. Hablar Wayland en Rust dice cómo viaja el byte, no
215 con qué se interpreta un teclado.
216 - `cosmic-idle` fue sin `[deps]` porque *ese* argumento ya no aplicaba —no usa sctk— y un demonio
217 de inactividad no interpreta teclas. Reventó en el ENLACE con `-lxkbcommon`, arrastrado por
218 **`cosmic-settings-config`**: de ahí sale la tabla de atajos y la usa **todo** componente de la
219 suite. La dep no viene de lo que el paquete hace, sino de la librería de configuración común.
220
221 La lección de método vale más que la dep: **la dep no se deduce de la función del paquete.** Está
222 escrita en el `build.rs` que panickea o en la línea de enlace, y las dos veces el error dijo
223 exactamente quién y por qué. El `link = "static"` sobrevive porque el artefacto de libxkbcommon
224 trae `.a` además de `.so`.
2258. **Una feature apagada en el paquete puede estar prendida por otro.** `cosmic-bg` declara `image`
226 con `default-features = false` y sin AVIF, y aun así compila `dav1d-sys`: **las features de cargo
227 se UNIFICAN en el grafo**. Se resolvió trayendo `dav1d` (b3:aab7a874) en vez de parchear el
228 Cargo.toml de upstream — si el grafo dice que sabe decodificar AVIF, que lo sepa de verdad.
229 Mismo modo de falla que un `.pc Requires` arrastrando una dep que nadie declaró.
230
231## Cómo se construye
232
233```sh
234cd ~/hammer
235./target/release/hammer --store store hash recipes/incoming-cosmic/<pieza>.toml # dry-run, ~2ms
236./target/release/hammer build recipes/incoming-cosmic/<pieza>.toml --store store
237```
238
239## Lo que falta, en orden
240
2411. Cerrar `cosmic-comp`. Es el gate: sin compositor no hay escritorio que probar.
2422. El primer cliente de iced (`cosmic-panel`), que es la pregunta abierta de verdad — libcosmic con
243 `winit`/`wgpu` puede pedir más de lo que pide cosmic-bg.
2443. `cosmic-settings-daemon`, `cosmic-launcher`, `cosmic-app-library`, `cosmic-notifications`,
245 `cosmic-osd`, `cosmic-workspaces-epoch`, `cosmic-idle`.
2464. Imagen QEMU con `bash` + `dbus-run-session` + `hicolor-icon-theme`, y **validar CON PANTALLA**
247 (`DISP=gtk` + `screendump`, contar colores distintos) — regla heredada de KDE y GNOME.
2485. `xdg-desktop-portal-cosmic` y `cosmic-greeter`, que son de la etapa siguiente.
cosmic-desktop.md
Runbook — COSMIC, el cuarto escritorio
Estado al 2026-08-03: COSMIC COMPONE EN QEMU. cosmic-comp arranca sobre el kernel de hammer con
arje-zero como PID1, toma el DRM, expone wayland-1 y presenta frames: pantalla completa en el
gris de COSMIC (39,41,42) con el cursor dibujado, 134 colores distintos. Falta que los clientes se
conecten. Este documento se escribe mientras se hace, no después.

COSMIC es el escritorio de System76, en Rust sobre smithay (compositor) e iced/libcosmic
(clientes). Es el cuarto de hammer, tras mirada,
KDE Plasma 6 y GNOME.
Por qué NO se parece a los dos anteriores
KDE y GNOME costaron lo que costaron por la misma razón: una torre de C con Qt o
glib/GObject/introspección debajo, donde cada capa se paga entera antes de ver un píxel. COSMIC no
tiene esa torre. La primera receta —cosmic-session— salió a la primera y sin una sola dep en C:
ELF estático, cero NEEDED.
Lo que sí hereda es lo que ya está pago:
| lo que COSMIC necesita | de dónde sale | costo |
| las deps en C de smithay (drm, gbm, seat, input, udev, xkb, pixman) | mirada-compositor, que ya es un compositor smithay de este lab | cero — misma lista |
libdisplay-info + hwdata | copiadas byte a byte de la cola GNOME | cero — mismo hash, artefacto ya sellado |
bash, dbus-run-session, hicolor-icon-theme, mesa-llvmpipe | corpus | cero |
el andamiaje de arranque en QEMU (run-qemu-desktop.sh, base rootfs, console-getty, screendump) | campañas KDE/GNOME | cero |
La deuda propia que sí trae, y que ninguna de las otras campañas tocó, es la del unwinder de la std
de rustc cuando el enlace no es estático — la misma que ya está documentada para las recetas Cargo
del corpus.
El pin es de la SUITE, no de cada paquete
COSMIC versiona todos sus repos en lockstep con el tag epoch-N. Comprobado repo por repo: los
doce componentes están en epoch-1.5.0.
No mezclar epochs entre piezas. Comparten protocolos propios (cosmic-protocols) y el formato de
configuración (cosmic-config); un desajuste ahí no da error de compilación, da un escritorio que
arranca y no se entiende consigo mismo — que es mucho más caro de diagnosticar.
La cola
recipes/incoming-cosmic/, aislada (otro agente comparte el repo; nunca git add -A).
Las deps se resuelven hermano → padre: desde incoming-cosmic/ se ven las recetas de
recipes/, pero no las de incoming-gnome/ ni incoming-kde/. Por eso libdisplay-info y
hwdata entran copiadas.
Copiar es gratis sólo si TODA la clausura transitiva resuelve a lo mismo. El ArtifactHash no
depende de la ruta de la receta, así que un fichero idéntico cuyas deps resuelven igual da el mismo
hash ⇒ cache hit sobre el artefacto que ya existe. Se cumplió con libdisplay-info, hwdata y
nasm, y se verificó con hammer hash antes de copiar (b3:260f0519, b3:bdc36cf9,
b3:33383070), no después. Dónde deja de cumplirse —y por qué importa— está en la sección de
cosmic-settings-daemon, más abajo.
Qué levanta la sesión, y por lo tanto el orden de ataque
cosmic-session/src/main.rs es la lista autoritativa —el mismo método que cerró la capa JS de GNOME:
leé el fichero donde el programa declara lo que arranca, no lo descubras de a uno:
cosmic-comp → cosmic-notifications · cosmic-panel · cosmic-app-library
→ cosmic-launcher · cosmic-workspaces · cosmic-osd · cosmic-bg · cosmic-idle
Más cosmic-settings-daemon y xdg-desktop-portal-cosmic fuera de esa lista.
| pieza | estado | hash |
cosmic-session | ✅ sellado | b3:81f02351 |
cosmic-icons | ✅ sellado (671 SVGs) | b3:94d06f77 |
cosmic-bg | ✅ sellado (estático) | b3:7b4962f0 |
dav1d 1.5.4 | ✅ sellado (dep arrastrada, ver gotcha 8) | b3:aab7a874 |
cosmic-comp | ✅ SELLADO — el gate | b3:b8abc52f |
cosmic-idle | ✅ sellado (estático) | b3:175632b7 |
cosmic-osd | 🔨 en construcción | — |
cosmic-panel · cosmic-launcher · cosmic-app-library · cosmic-workspaces-epoch · cosmic-notifications | receta escrita, sin construir | — |
cosmic-settings-daemon | ni receta | — |
El orden no es el de la lista de arranque. cosmic-bg va antes que el panel a propósito: habla
Wayland con smithay-client-toolkit y no toca libcosmic ni iced, mientras que panel, launcher,
osd y workspaces son todos clientes de iced. Con el fondo sellado, «pinta el fondo pero no el panel»
separa la capa de UI del transporte; sin él, las dos preguntas llegan juntas.
cosmic-settings-daemon: por qué queda al final y NO se resuelve copiando
Es el único con cadena propia. Su workspace incluye audio-server como dep de path no opcional,
que arrastra cosmic-pipewire → pipewire-sys → libpipewire-0.3 por pkg-config. Y pipewire hoy
vive sólo en las colas GNOME y KDE.
Acá el truco de copiar deja de ser gratis, y eso corrige lo que dice más arriba. La regla completa
es: copiar una receta entre colas es gratis sólo si toda su clausura transitiva resuelve a lo
mismo. Se cumplió con libdisplay-info, hwdata y nasm —sus deps están todas en el corpus—, y
no se cumple con pipewire: de sus catorce deps, cinco viven sólo en colas ajenas
(dbus-shared, alsa-lib, libsndfile, pulseaudio, zlib-shared) y —lo decisivo— glib
resolvería distinto. Desde incoming-gnome, glib es la sombra de esa cola (b3:f6ccdf98); desde
incoming-cosmic sería la del corpus (b3:93d2cad0). Son recetas distintas, no la misma en dos
lugares.
O sea que copiar produciría un segundo pipewire construido contra otra glib, arrastrando su cadena
entera. Y eso no es sólo caro: es exactamente el cuadro que la campaña GNOME ya midió y evitó —dos
glib distintas con 370 rutas solapadas y dos registros de GType en un proceso—.
Antes de tocar nada se midió con yupana radio (la puerta única, nunca grep): pipewire tiene
radio 1 en GNOME y 2 en KDE, pero glib del corpus tiene 21 dependientes transitivos y afecta la
imagen escritorio-gnome. La decisión correcta no es de esta campaña: es dónde vive pipewire en el
corpus, y es una decisión de catálogo, no de COSMIC. Hasta entonces, el escritorio arranca sin
settings-daemon —se pierden brillo, tema automático y perfiles de audio, no el escritorio—.
🚪 EL GATE PASADO: cosmic-comp compila (2026-08-03)
b3:b8abc52f, 51 min 39 s de cargo con CARGO_BUILD_JOBS=1 y lto = "fat". A la primera, salvo
el --bin del gotcha 1. Sin un solo parche a la fuente.
Lo que dice el enlace es la parte que vale, porque es la medición y no la impresión:
NEEDED: libdisplay-info.so.2 · libgbm.so.1 · libseat.so.1 · libudev.so.1
libinput.so.10 · libpixman-1.so.0 · libxkbcommon.so.0 · libc.so
Ocho, y las siete primeras son exactamente los backends de smithay que la receta declara. La
octava es musl. No hay libgcc_s — o sea que el compositor no arrastra la deuda del unwinder que
sí tienen varias recetas Cargo del corpus, porque acá el enlace es dinámico contra musl y no hay
+crt-static que forzar. Y no aparece nada que no se haya declarado: la clausura de build y la de
runtime coinciden, que es la propiedad que uno quiere y casi nunca se cumple gratis.
Que un compositor Wayland completo —DRM, GBM, EGL, libinput, seat, Vulkan, XWayland, un renderer
multi-GPU y una capa de UI en iced— cierre con ocho librerías compartidas y ningún parche es el
resultado más limpio de las cuatro campañas de escritorio.
🖥 EL PRIMER ARRANQUE: qué falló y qué no (2026-08-03)
Tres imágenes y tres causas distintas, todas medidas. Vale dejarlas porque ninguna estaba en el
camino que uno anticipa, y las tres se manifestaron a varias capas de distancia de su causa.
1. cosmic-session no es opcional en el sentido que yo asumí — y cosmic-settings-daemon tampoco.
El compositor arrancó perfecto y le devolvió a la sesión su WAYLAND_DISPLAY=wayland-1; el gestor
entonces intentó lanzar cosmic-settings-daemon, no lo encontró, y panickeó:
INFO cosmic_session: got environmental variables from cosmic-comp: [("WAYLAND_DISPLAY", "wayland-1")]
ERROR panic: 'failed to start settings daemon: NotFound' src/main.rs:255
Es un .expect(), sin feature que lo apague. Esto corrige lo que decía la sección de
cosmic-settings-daemon: no es que sin él se pierdan el brillo y el tema, es que no hay sesión.
Queda por eso el modo bare en cosmic-start —el camino que upstream ya soporta como
install-bare-session—: se lanza el compositor solo y los clientes a mano contra su WAYLAND_DISPLAY.
Separa «¿compone?» de «¿arranca la sesión?», que son dos preguntas y estaban viniendo juntas.
2. La pantalla negra era libz.so.1. El corpus trae zlib estática, que le alcanza a todo el
cierre de COSMIC. Pero kms_swrast_dri.so es un .so que mesa abre con dlopen y NEEDea
libz.so.1:
MESA-LOADER: failed to open kms_swrast: Error loading shared library libz.so.1
WARN cosmic_comp::backend::kms: Failed to add device /dev/dri/card0: Failed to initialize GBM device
Sin driver de software no hay GBM, y el compositor arranca igual y presenta una pantalla negra. La
causa está a tres capas del síntoma. Se arregla agregando zlib-shared como raíz del hidratador.
3. Los clientes NO pueden ser estáticos. cosmic-bg moría con «The wayland library could not be
loaded» teniendo libwayland-client.so.0 en el rootfs. wayland-client entra con la feature
dlopen: carga libwayland en runtime en vez de enlazarla, y un binario musl estático no tiene
dlopen funcional. No faltaba la librería: faltaba poder abrirla. Todos los clientes pasan a
link = "dynamic".
Lo que NO falló, y conviene decirlo: el compositor no necesitó un solo parche, ni una perilla de
KMS, ni forzar modeset legacy. Las cuatro perillas de render por software heredadas de KDE y GNOME
(GBM_ALWAYS_SOFTWARE, kms_swrast, LP_NUM_THREADS=1) alcanzaron. GNOME costó cinco muros para
llegar a este punto; COSMIC costó tres, y ninguno era del compositor.
Dos carreras con /run, la misma causa
dbus-daemon --system falló con «Failed to bind socket /run/dbus/system_bus_socket: No such file or
directory» aunque el directorio se creaba al principio del script, y dbus-run-session se quejó
por su lado de que XDG_RUNTIME_DIR no existía. Entre el arranque del getty y el uso pasan varios
segundos en los que arje-zero sigue montando, y un tmpfs sobre /run se lleva puesto lo que hubiera.
Crear justo antes de usar es la única forma de no depender de ese orden; comprobar al principio y
usar al final es exactamente cómo se cuela una carrera — la misma forma del bug de PolicyKit1 que se
cerró hoy en GNOME.
Gotchas medidos (no previstos)
-
cargo rustc -- exige UN target. La fase compile del lab pasa flags tras el --, y cualquier
paquete con binario + lib + ejemplos corta con «extra arguments to rustc can only be passed to
one target». Se arregla con flags = ["--bin", "<nombre>"]. Le pasa a cosmic-comp y a cosmic-bg
(workspace con config/), y le va a pasar a casi todos: ponelo desde el principio.
-
start-cosmic es bash de verdad, no sh. Usa mapfile, [[ ]] y expansión indirecta
${!var}; con el busybox del rootfs no arranca. bash ya está en el corpus, pero tiene que
entrar en la imagen — y no lo pide ningún [deps], así que no lo va a traer nadie por accidente.
Lo mismo con dbus-run-session (de dbus), que es lo que el script exec-uta al final.
-
rust-toolchain.toml con channel = "1.93" es inerte acá — es una perilla de rustup, y el
sandbox tiene el rustc de Alpine (1.96) pelado. Conviene saberlo antes de «arreglarlo».
-
cosmic.desktop existe DOS veces (en cosmic-session y en cosmic-comp) con contenido distinto
y en la misma ruta. El de cosmic-comp es el de la «bare session» y apunta a /usr/bin/cosmic-service,
que vive en la rama de systemd que acá no va. Se instala sólo el de cosmic-session; si entraran
los dos, quién gana depende del orden de hidratación y el que pierde es el bueno.
-
libsystemd no es systemd. La feature default = ["systemd"] de cosmic-comp trae el crate
libsystemd, que es una reimplementación en Rust del protocolo, no un binding a la C. Y
logind habla org.freedesktop.login1, que acá lo sirve arje-logind-compat. No hay que apagar
nada: el nombre de la feature es el del protocolo, no el del programa que lo contesta.
-
Las features de smithay que asustan no cuestan deps de build: backend_vulkan (crate ash),
backend_x11 (x11rb con dl-libxcb) y xwayland cargan todo por dlopen. Son deuda de
RUNTIME —libxcb y vulkan-loader viven hoy sólo en la cola KDE—, no un muro de compilación.
-
libxkbcommon + pkgconf van en TODA la cola, sin excepción — incluso en las piezas sin
interfaz. Se aprendió dos veces, con dos razonamientos distintos y los dos equivocados:
cosmic-bg fue sin [deps] porque «habla el protocolo en Rust y no toca C». Reventó en el
build.rs de smithay-client-toolkit pidiendo xkbcommon.pc: sctk ENLAZA libxkbcommon; el
que la dlopea es winit, que es otra capa. Hablar Wayland en Rust dice cómo viaja el byte, no
con qué se interpreta un teclado.
cosmic-idle fue sin [deps] porque ese argumento ya no aplicaba —no usa sctk— y un demonio
de inactividad no interpreta teclas. Reventó en el ENLACE con -lxkbcommon, arrastrado por
cosmic-settings-config: de ahí sale la tabla de atajos y la usa todo componente de la
suite. La dep no viene de lo que el paquete hace, sino de la librería de configuración común.
La lección de método vale más que la dep: la dep no se deduce de la función del paquete. Está
escrita en el build.rs que panickea o en la línea de enlace, y las dos veces el error dijo
exactamente quién y por qué. El link = "static" sobrevive porque el artefacto de libxkbcommon
trae .a además de .so.
-
Una feature apagada en el paquete puede estar prendida por otro. cosmic-bg declara image
con default-features = false y sin AVIF, y aun así compila dav1d-sys: las features de cargo
se UNIFICAN en el grafo. Se resolvió trayendo dav1d (b3:aab7a874) en vez de parchear el
Cargo.toml de upstream — si el grafo dice que sabe decodificar AVIF, que lo sepa de verdad.
Mismo modo de falla que un .pc Requires arrastrando una dep que nadie declaró.
Cómo se construye
cd ~/hammer
./target/release/hammer --store store hash recipes/incoming-cosmic/<pieza>.toml # dry-run, ~2ms
./target/release/hammer build recipes/incoming-cosmic/<pieza>.toml --store store
Lo que falta, en orden
- Cerrar
cosmic-comp. Es el gate: sin compositor no hay escritorio que probar.
- El primer cliente de iced (
cosmic-panel), que es la pregunta abierta de verdad — libcosmic con
winit/wgpu puede pedir más de lo que pide cosmic-bg.
cosmic-settings-daemon, cosmic-launcher, cosmic-app-library, cosmic-notifications,
cosmic-osd, cosmic-workspaces-epoch, cosmic-idle.
- Imagen QEMU con
bash + dbus-run-session + hicolor-icon-theme, y validar CON PANTALLA
(DISP=gtk + screendump, contar colores distintos) — regla heredada de KDE y GNOME.
xdg-desktop-portal-cosmic y cosmic-greeter, que son de la etapa siguiente.