GC-07

Como usuario de Linux, no suelo ser el mejor ejemplo para mis amigos !IT. Si no se te ocurre qué puede ser !IT, es porque probablemente seas uno, un TI (Tech Illiterate). Y aunque pueda sonar agresivo, va con cariño. No soy el mejor ejemplo porque no soy usuario de un producto, sino de una tecnología, y lo que el usuario promedio necesita es un producto, con sus límites, colores y estrellitas. Por otra parte, el concepto de Linux como OS, para cualquier TI, ya es difuso, y para quien busca instalar Linux por primera vez, no saber dónde ir a comprarlo o de qué marca es, ya alcanza para ni siquiera intentarlo.
NixOS
Dentro del mercado de posibilidades que ofrece Linux el market share en el cual tradeo es, más bien, chico. Esto tiene lo bueno y lo malo de todo mercado que no se ha masificado y probablemente nunca lo haga. Hoy te voy a hablar un poco de ambas, pero primero dejame venderte NixOS.
NixOS, es la distribución de Linux que uso hace unos 3 años, después de haber estado, orgullosamente, 9 años con Arch. Cuando arranqué con NixOS, no existía la IA, o al menos no era tan potente como lo es ahora. Configurarlo fue tedioso, pero mi objetivo era claro, manejar la cantidad innecesaria de computadoras que tengo bajo mi poder desde un solo lugar. Hoy, por suerte, son solo tres: desktop, notebook y server. Empecé con mi desktop, siguió la notebook pero la idea de NixOS en un servidor me resultaba rara, nunca la había escuchado. Tras descubrir que Michael Stapelberg siquiera lo consideró para su server terminé por instalarlo en mi server también.

A diferencia de otras distribuciones de Linux, NixOS no se configura con archivos dispersos por todo el sistema (/etc, /usr), sino que se configura en archivos .nix. Por ejemplo: Para alterar el estado de tu Debian o Windows, correrías algo como sudo apt-get install python o harías varios clicks y Siguiente;Siguiente;Siguiente. Eso crearía por detrás una serie de archivos y configuraciones en lugares que nunca te interesaron, alteraría variables de entorno que no sabés cuáles son, entre otras cosas que desconocés ni te importan hasta que se te rompe algo. Cuando borrás ese programa, rezá que deje tu sistema más o menos como lo encontró. En NixOS es distinto, si querés hacer lo mismo en Nix, en vez de hacerlo de manera imperativa con un comando, editás un archivo .nix. En este archivo, declarás el paquete que querés instalar y por qué no sus configuraciones, seguido de un sudo nixos-rebuild switch, ya tenés tu programa instalado. Nix lee esa declaración, construye el nuevo sistema de forma determinista y lo activa de forma atómica. ¿Atómica? Sí, “A-tó-mi-ca”, es una forma elegante de decir que el cambio o se aplica completamente, o no se aplica en absoluto, no hay grises. Ese archivo .nix es, literalmente, la fuente de verdad de tu sistema operativo, el estado de tu sistema operativo. Todo tu sistema en dos carpetas /boot y /nix, el resto, se pueden recrear. La gran particularidad (y desventaja para muchos) es que para poder cumplir con estas garantías, NixOS usa una distribución de archivos peculiar, el Nix Store (/nix/store). En lugar de instalar los programas en las rutas tradicionales (/usr/bin/, /bin/ o C:\Archivos de programa), Nix guarda cada programa (o más precisamente, para cada output de una derivation) en una carpeta con un hash único que representa su contenido exacto (e.g realpath $(which nvim) -> /nix/store/n1bm101nhgw33fv114ibkv10xvh8fgp1-neovim-0.11.7/bin/nvim). Este hash, no es siempre el mismo, sino que, como la mayoría de los hashes, se usa para denotar unicidad, individualidad, si algo cambia aunque sea mínimamente, el hash cambia y se genera una carpeta nueva. Estos hashes no están aislados, sino que entre ellos forman una suerte de Árbol de Merkle Acíclico o DAG. Hay diferencias técnicas muy interesantes pero da para otro video.

Merkle Tree Acíclico, nada muy loco de entender:
- Las hojas son hashes de datos -> Siendo un hash un identificador único generado a partir de una serie de datos.
- Cada nodo interno es el hash de sus hijos -> El hash de un nodo, está determinado por los nodos que se le relacionan.
- Cambiar cualquier hoja cambia todos los hashes hacia arriba, hasta la raíz -> Consecuencia de 1 y 2.
Ya te podés imaginar la relación con el Nix Store:
- Cada “programa” en el store es un nodo con su hash.
- Las dependencias de ese programa son referencias o edges (en criollo: flechitas) hacia otros nodos (programas).
- El hash de un programa incluye los hashes de sus dependencias, por lo que cambiar cualquier dependencia, cambia todo el programa, como la vida misma.

Al instante de aprender esto, obviamente quise graficarlo: nix-store -q --graph $(which openssl) | dot -Tpng > graph.png y te da algo así (si no tenés dot: probá con nix-shell -p graphviz) y guarda con hacer esto con programas grandes porque podés estar una vida esperando. Lo más noble de este árbol, es que si dos paquetes comparten una dependencia, solo existe una copia en el store, pero si dos paquetes dependen de versiones distintas de un programa/librería, no pasa nada, porque estos viven en carpetas distintas, con hashes distintos, etc. En cualquier otro sistema, tendrías una sola versión de ese programa (e.g /usr/lib/libfoo.so) y de ahí los virtual envs como nvm, virtualenvs, Flatpak, AppImage, LD_LIBRARY_PATH y soluciones con las que tenés que lidiar vos o el manejador de paquetes (package manager) de tu OS (apt, apk, pacman, yum, etc.)
No todo es color de rosa en el mundo Nix, y si algo es seguro, es que Nix no tiene un solo color. Esta complejidad, tiene un costo y es, para mí, uno de los trade-offs más fuertes que tiene NixOS, no ser un FHS (Filesystem Hierarchy Standard). Este estándar define cómo debe organizarse el sistema de archivos en sistemas tipo Unix/Linux y el gran problema es que la mayoría de los programas que corren en Linux, siguen, lógicamente, este estándar.
Velocidrone
Hace rato que estoy con ganas de comprarme un drone chiquito (mejor conocido como Tiny Whoop) para boludear en casa. Manejar estos bichos no es tarea fácil, por lo que, si no querés estrolarte contra la pared al día 1 de comprar tu drone, primero hay que practicar con un simulador. Spoiler: te vas a estrolar igual. Uno de los que me recomendaron fue VelociDrone. El simulador está hecho en Unity y soporta Güindows, OSX (Intel, M1 y M2) y Linux (Redhat y Debian) pero, muy a mi pesar, no anda en NixOS. Eso no me iba a impedir practicar, entonces un día, muy inocentemente, les levanté un ticket a la gente de “Bat Cave Games” preguntando si podían dar soporte. A lo que ellos contestaron:

Claramente, no iban a dar soporte. No pasa nada, loco, está todo bien. Pero ¿por qué no anda? ¿Realmente alcanza con instalar las dependencias de Unity en NixOS? ¿Cómo se instala un programa en NixOS?
Primero que nada, tenés que ver si tu paquete existe. Esto lo hacés en search.nixos.org/packages, si no tenés suerte, toca codear un poquito en Nix. Pero antes, te recomiendo buscar si alguien ya lo hizo por vos, para eso te recomiendo, lamentablemente, Github. En Github podés buscar repositorios públicos, y como en Nix es todo declarativo hay mucha info que podés simplemente copiar y pegar. Para buscar, el keyword y tip que tenés que llevarte a casa hoy si sos usuario de Nix es : lang:nix. En este caso, mi query (?q=) fue lang:nix velocidrone.

Al tirar la query encontré al menos 7 gordos con su repo público. La foto de arriba es lo que yo denomino un GC-07 y determina que existen, al menos, siete Gordos Compu que están en la misma que vos. Un GC-XX puede ocasionar un sinfín de emociones: desde una tristeza y desolación inconmensurables, si es un GC-00, hasta una mera indicación de que estás en el camino correcto, si es un GC-N con N > 100. O puede, como en este caso, dar un pequeño ápice de esperanza de que lo que querés hacer, y como lo querés hacer, se puede; y alguien en este ciber-mundo ya lo intentó.
Qué el gordo sepa que el gordo puede
Lo que queda de esta entrada, son simplemente detalles técnicos de lo que se tuvo que hacer para instalar Velocidrone. El post se pone un poco más técnico
No totalmente desesperanzado terminé encontrando al gordo ucraniano ivankovnatsky al cual le robé su velocidrone/default.nix y lo hice propio. Analicemos un poco qué tuvo que hacer Ivan para instalar Velocidrone en NixOS.

Para instalar un programa en Nix, tenés que declarar un archivo cuyo contenido indica cómo es que este programa tiene que ser construido, qué dependencias va a usar, de dónde se obtiene el código fuente, qué pasos de compilación/instalación deben ejecutarse, entre otras cosas. Un paquete declarado puede verse algo así:
{ stdenv, fetchFromGitHub }: # Estas son tus dependencias
stdenv.mkDerivation rec { # Esto es el nodo dentro de árbol de dependencias
pname = "mi-programa";
version = "1.0.0";
src = fetchFromGitHub { # Uso una dependencia para descargar el código fuente
owner = "usuario";
repo = "mi-programa";
rev = "v${version}";
sha256 = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
};
buildInputs = [ ]; # buildInputs declares the dependencies that should be available while building a package. Nix isolates the build in a separate environment
installPhase = ''
mkdir -p $out/bin
cp mi-programa $out/bin/
'';
}
Veamos qué hizo nuestro amigo Iván. Primero descarga el .zip de la página velocidrone:
src = requireFile {
name = "production-launcher-debian.zip";
url = "https://www.velocidrone.com/download/launcher?id=debian&export=download";
hash = "sha256-pVgQxuPkte5Apx05MuVGdh0MYaJ4Wxx+EhsUe79aiJU=";
};
Cabe aclarar que Nix, es un lenguaje de programación funcional, que si te comiste la PsyOps del OOP, puede que cueste un poco al principio.
Este hash es el que vamos a tener que hacer coincidir con el hash del commit de Github que queramos (no tiene nada que ver con nuestro hash de Nix, toda esa parte pasa inadvertida para el usuario). Vemos cómo nombra al paquete como velocidrone-launcher y descomprime el resultado, que guardó en src = , el .zip:
pname = "velocidrone-launcher";
inherit version src;
nativeBuildInputs = [ unzip ];
unpackPhase = ''
unzip $src
'';
Lo instala, que es básicamente lo que hacía yo a mano cuando tenía que correr Velocidrone antes de encontrar este package.
installPhase = ''
mkdir -p $out/share/velocidrone
cp Launcher launcher.dat $out/share/velocidrone/
chmod +x $out/share/velocidrone/Launcher
'';
Y declara el acceso directo que va en el escritorio, launcher, etc.
desktopItem = makeDesktopItem {
name = "velocidrone";
desktopName = "VelociDrone";
comment = "FPV drone racing simulator";
exec = "velocidrone";
icon = "velocidrone";
categories = [
"Game"
"Simulation"
];
};
Para la mayoría de los programas, con eso sería suficiente, pero no para Velocidrone, ya que tiene dependencias dinámicas y las espera encontrar en un entorno FHS. Esto, podría haberse evitado, si hubiesemos tenido un compilado estático, que a diferencia de un compilado dinámico, las dependencias se compilan junto al binario. En este caso, al no tener acceso al código fuente, compilar Velocidrone per se no es una opción, por eso descargamos Velocidrone compilado. Por ende, más que pasos de compilación, estamos programando un instalador. Lo podés ver como que NixOS maxeó la de facilitar escribir tus instaladores. Por eso es que Iván pudo definir un entorno FHS para el runtime de velocidrone con buildFHSEnv y declarar las librerías que necesita Unity.
buildFHSEnv {
pname = "velocidrone";
inherit version;
targetPkgs = _: [
zlib
libxcb
# ...
sqlite
stdenv.cc.cc.lib
];
Y luego, declara un script que va a usar de wrapper antes de llamar al binario, en este caso el Launcher:
runScript = lib.getExe (
stdenv.mkDerivation {
name = "velocidrone-wrapper";
dontUnpack = true;
installPhase = ''
mkdir -p $out/bin
cat > $out/bin/velocidrone-wrapper <<'SCRIPT'
#!/bin/sh
dir="$HOME/.velocidrone"
mkdir -p "$dir"
cp "${launcher}/share/velocidrone/Launcher" "$dir/Launcher"
cp "${launcher}/share/velocidrone/launcher.dat" "$dir/launcher.dat"
chmod u+rwx "$dir/Launcher"
chmod u+rw "$dir/launcher.dat"
cd "$dir"
exec ./Launcher "$@"
SCRIPT
chmod +x $out/bin/velocidrone-wrapper
'';
meta.mainProgram = "velocidrone-wrapper";
}
Después instala un icono custom con un extraInstallCommands y declara un poco de metadata con meta=. Una vez declarado el package, lo instala usando un Nix overlay que se ve algo así en tu código:
final: prev: {
velocidrone = prev.callPackage ./velocidrone.nix {};
}
Un Overlay, te permite extender el conjunto de paquetes de tu nix, generalmente nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11". Pudiendo así instalar velocidrone como cualquier otro pkgs dentro de tu configuración. Después de crear los archivos pertinentes y un par de modificaciones al installPhase, un nixos-rebuild switch y salió andando.
#!/bin/sh
export UNITY_DISABLE_PLUGINS=1
export SDL_VIDEO_FULLSCREEN_HEAD=0
export SDL_VIDEO_FULLSCREEN_DISPLAY=0
export SDL_VIDEODRIVER=x11
export QT_QPA_PLATFORM=xcb
dir="$HOME/.velocidrone"
Encontrar gordos como Iván, es jodido. NixOS ya es un nicho dentro de Linux, al que le tenés que solapar el de Wayland y adentro el de Hyprland. Se te termina haciendo un diagrama de Venn en donde terminás parado vos, que llegaste hasta acá, un par de gordos más y yo, en la intersección de la locura.
Gracias, ivankovnatsky.