# Nix et NixOS

⚠️ Livre incomplet

# Introduction

Introduction à Nix, comment l'installer, ce que c'est, des exemples d'utilisation et d'autres infos.

# Keskesé

Il y a pas mal de choses dans l'écosystème Nix et on va découvrir les détails des composants plus en détail plus tard dans ce guide.

Le nom **Nix** fait référence à deux choses : language et un gestionnaire de paquets. **NixOS** en revanche fait référence au système d'exploitation utilisant Nix (le langage et le gestinonaire de paquets)

## Fun facts sur Nix
* Nix est le plus gros gestionnaire de paquet en terme de nombre de paquets à jour ET de nombre de paquet tout cours avec plus de **80 000** paquets et plus de **55 000** paquets en dernière version (en comparaison Ubuntu a ~35000 paquets dont ~18000 en dernière version)
* Le cache de Nix (qui contient tous les builds des différents paquets) fait plus de **400TiB** (presque un demi petabyte) et le maintient de toute l'infrastructure du serveur de cache coûte **9000€/mois**
* Nix existe depuis **2003** (en comparaison *pacman* existe depuis 2002, *apt* depuis 1998 et *apk* (de alpine) depuis 2005)

## Nix (le gestionnaire de paquets)
Le gestionnaire de paquet Nix est très différents des autres gestionnaires de paquets :

* Il est **multi-version** et très **fiable**, ce qui signifie que l'on peut avoir simultanément plusieurs version d'un même logiciel. Cela empèche donc tout problème d'*enfer des dépendences*.
* Tous les paquets ont des **dépendences complètes**, tous les paquets étant installés dans un dossier et étant isolé du reste lors du build, on a l'assurance que chaque paquet a la liste complète de ses dépendences spécifiée)
* Nix supporte le **multi-utilisateur** ce qui signifie qu'il n'y a pas besoin d'un accès admin pour y installer des choses, également le cache reste commun, ce qui signifie que si utilisateur A demande le paquet X puis que utilisatrice B demande le même paquet alors celui-ci ne sera pas de nouveau téléchargé/build.
* Les changements sont **atomiques**, ce qui signifie que l'on peut tout à fait mettre à jour uniquement un logiciel mais pas les autres sans créer de conflit. 
* Un **historique** est gardé de tous les changements, ainsi il est très facile de retourner à état précédent en cas de problème
* Nix possède aussi un **garbage collector**, ce qui signifie que lorsque l'on supprime un paquet, celui-ci n'est que déréférencé mais le fichier est toujours présent sur le système ce qui permet de développer très facilement. Cependant quand il faut faire de la place le garbage collector va supprimer la totalité des logiciels qui ne sont plus référencé nulle part.
* Nix est **déterministe** et **reproductible**, une certaine configuration et une certaine dérivation doit toujours arriver à l'exact même résultat, ce qui assure donc que si quelquechose marche sur une machine, elle fonctionnera sur toutes les machines de la même architecture.
* Nix supporte à la fois l'installation **précompilée** et l'installation **depuis la source**. Un cache est disponible pour télécharger tous les logiciels (c'est la méthode par défault), cependant il est également possible d'installer tout depuis la source (comme dans Gentoo). 
* Nix permet aussi **l'installation temporaire** de paquets, c'est à dire le développement d'environment dans lequel on peut spécifier la liste des logiciels à utiliser sans pour autant affecter le reste du système.
* Nix est **multi-distribution et multi-plateforme**, il fonctionne sur toutes les distributions Linux ainsi que sur macOS. Il a également des ports pour BSD et Windows (avec WSL ou Cygwin)

## Nix (le langage)
Nix est le langage utilisé pour créer les paquets (appelés *dérivations* mais on verra ça plus tard) mais également pour gérer les configruations ou même créer des outils permettant de créer des dérivations plus facilement.

* Nix est **spécifique à un domaine d'utilisation**, c'est un language pour le gestionnaire de paquet et rien d'autre, ce n'est pas un langage multi-usage.
* Nix est un langage **déclaratif**, Il n'y a pas de notion d'exécution d'étapes séquentielles. Les dépendances entre les opérations ne sont établies qu'à partir des données.
* Nix est **pure**, toutes les valeurs sont immuables
* Nix est un langage **fonctionnel**, les fonctions sont des valeurs comme les autres et peuvent elle même être retournées par d'autres fonctions
* Nix est **paresseux**, les expressions sont seulement calculées quand elles sont demandées (ainsi il on a des milliers d'expressions et que l'on en demande qu'une, cela ne ralentis rien du tout car seule celle que l'on va demander sera calculée)
* Nix fonctionne avec des **types dynamiques**, il n'y a donc pas besoin de préciser les types dans le code

## NixOS
* NixOS utilise **Nix** comme gestionnaire de paquet mais aussi comme langage de configuration de l'entièreté du système
* Il est possible de choisir différentes configurations et de passer d'une à l'autre très facilement dès le début du démarrage
* Il possède tous les autres avantages de Nix.

# Pourquoi Nix ? (exemples)

Nix a beaucoup d'utilisations possibles pour les développeurs et les adminsys. Mais aussi certains pour les utilisateurs Linux qui aiment expérimenter.

* Bénéficier du plus grand repository du monde (il y a également un équivalent à l'AUR s'appellant le NUR et qui n'est pas compté dans les chiffres)
* Ne pas avoir à se soucier des dépénences des paquets, on peut installer tout ce que l'on veut sans qu'il y ai le moindre conflit de versions
* Sur NixOS, ne pas avoir à se soucier de quoi que ce soit car toute config peut être rollback
* Ne pas avoir à utiliser root pour installer des paquets 
* Essayer des logiciels sans pour autant les installer dans son environnement (avec `nix-shell`)
* Créer des environnements de build ou de développement (plus besoin d'utiliser Docker ou virtualenv pour ça) (avec `nix-shell` ou `nix develop`) ainsi il est très aisé de pouvoir utiliser n'importe quel version de n'importe quoi sans devoir se soucier de quoi que ce soit.
* Décrire un environnement de manière déclarative dans un fichier de config, ainsi tous les autres collaborateur·ice·s pourront avoir l'exact même environnement de build et de développement que vous (fini les "ça marche sur mon ordi")
* Créer des images docker très minimales (sans avoir à utiliser de Dockerfile), reproductible (un même Dockerfile n'arrive pas toujours à la même image à cause des updates, cependant un même flake.nix arrivera toujours au même résultat bit pour bit)
* Créer une image cloud de manière déclarative (imaginez "coder" votre serveur en quelques lignes)
* Faire des tests de ses configurations

## Démos
Vous pouvez trouver des démos des exemples donnés juste avant :

* Sur le site [nixos.org](https://nixos.org)
* Sur la chaine YouTube [OpenTechLab](https://www.youtube.com/@OpenTechLab)

# Sources et références

Si vous souhaitez trouver l'information à sa source (c'est de là que viennent toutes mes infos pour ce guide), vous pouvez les trouver aux liens suivants :

* Le [manuel de NixOS](https://nixos.org/manual/nixos/stable/) pour tout ce qui est relatif à NixOS
* Le [manuel de NixPkgs](https://nixos.org/manual/nixpkgs/stable) pour tout ce qui est relatif aux paquets provenant de nixpkgs
* Le [manuel de Nix](https://nixos.org/manual/nix/stable/) pour tout ce qui est relatif au langage ou au gestionnaire de paquet
* Les [Nix Pills](https://nixos.org/guides/nix-pills/) pour une explication globale mais bas-niveau du fonctionnement de Nix
* La [man page de home-manager](https://nix-community.github.io/home-manager/) pour tout ce qui est relatif à home-manager 
* Le site [nix.dev](https://nix.dev/) qui a des liens vers les différentes documentations citées ci-dessus + certains guides plus spécifique comme celui pour les flakes
* La chaine YouTube [OpenTechLab](https://www.youtube.com/@OpenTechLab), qui a fait à ce jour 2 vidéos sur Nix : une générale et une plus spécifique pour la création d'image docker
* La chaine YouTube [grok.computer](https://www.youtube.com/@grok.computer) qui a fait à ce jour 3 vidéos sur Nix, très courte expliquant le langage, NixOS et les flakes
* Le site [nixos.org](https://nixos.org) pour des petites démos, des liens vers d'autres documentations ou ressources relative à Nix

# Théorie

Comment fonctionne Nix. Je trouve ça intéressant d’abord parler de la théorie avant de passer à la pratique, car ça aide à mieux apprécier et comprendre Nix.

# Le langage

Tout d'abord il est intéressant de comprendre comment fonctionne le langage Nix car c'est vraiment la base de tout le reste. On va donc passer en revue les bases nécessaires de la programmation fonctionnelle et la syntaxe de Nix. 

On va d'abord passer en revue toutes les bases du langage, puis on va voir la fonction `derivation`, c'est là que ça va devenir vraiment intéressant.

> **Tip** : Utilisez la commande `nix repl` pour tester la syntaxe de Nix et les exemples donnés ici.

## Types de données

Voici la liste des types primitifs et comment ils sont représentés en Nix :

| Type | Description | Exemple |
| --- | --- | --- |
| String | Chaine de caractère | `''hello world''` ou `"hello world"` |
| Nombre | Nombre entier ou non | `123` ou `12.4` |
| Chemin | Chemin vers un fichier ou dossier (doit contenir un `/`) | `./builder.sh` ou `/bin/bash` |
| Booléen | Vrai ou faux | `true` ou `false` |
| Null | Rien du tout | `null` |

Ensuite voici les autres types :

| Type | Description | Exemple |
| --- | --- | --- |
| Liste | Liste d'expressions Nix (notez qu'il n'y a pas de `,` mais seulement un espace) | `[ 123 ./foo.bar "abc" ]` |
| Ensemble d'attributs | Association de clé-valeur | `{ x = 123; y = 56; file = ./builder.sh; name = "something"; }`,  `{ x = 123; y = 56; }.x` retourne 123 |


## Les différentes expressions
* Les commentaires se font soit sur une ligne avec `#`, soit avec `/* */`

```nix
nix-repl> # hello world
          /* hello world */
          1 + 1
2
```

* Ensemble d'attributs récursifs qui permettent de référencer d'autres attributs du même set depuis le set lui-même

```nix
# x référence y, et y référence 123
rec {
  x = y;
  y = 123;
}.x # retourne 123
```

* Expressions "let-in" qui permet de définir des variables ou fonctions locales qui sont ensuite utilisées dans le "in"

```nix
# Le let défini les variables locales x et y
let
  x = "foo";
  y = "bar";

# Qui sont ensuite utilisée dans le in, ce qui donne donc "foobar"
in x + y
```

* L'héritage des attributs avec `inherit` permet d'éviter de se répèter

```nix
#  Variable locale x et y
let 
  x = 123; 
  y = 456;

# Dans un set
in { 
  inherit x y; # équivaut à dire "x = x; y = y;"
  # soit définir l'attribut "x" du nouveau set comme étant la valeur de la variable locale "x" définie dans let
} # ceci retournera donc { x = 123; y = 456; }
```

```nix
# Donc
let
  x = 123;
  y = 456;
in { inherit x y; }

# est la même chose que
let 
  x = 123;
  y = 456;
in {
  x = x;
  y = y;
}
```

* On peut aussi créer des fonctions que l'on peut mettre dans des variables

```nix
# concat est le nom de la variable
# x et y sont les paramètres de la fonction
# x + y est le corps de la fonction
nix-repl> concat = x: y: x + y

# On peut appeller la fonction en donnant le nom de la fonction, puis ses attributs séparés par des espaces
nix-repl> concat "hello " "world"
"hello world"

# Mais on peut aussi répondre à moitié à la fonction (pour plus d'info, renseignez-vous sur la programmation fonctionnelle
nix-repl> half-concat = concat "hello"
nix-repl> half-concat "world"
"hello world"

# Mais on peut aussi utiliser des sets d'arguments lorsque l'on veut tous les attributs d'un coup
nix-repl> concat = { a, b }: a + b
nix-repl> concat { a = "hello "; b = "world"; }
"hello world"
```

* Les conditions ne se font que par des genre de ternaires `if then else`

```nix
nix-repl> if true then "hello" else "foo"
"hello"
```

* Pour empécher encore plus la redondance de code, on peut utiliser `with` qui va en quelque sorte "transformer" les attributs d'un set en variables locales

```nix
# Ceci va retourner { a = "foo"; b = "bar"; }
let
  my-set = { x = "foo"; y = "bar"; };
in
  with my-set; {
    a = x;
    b = y;
  }

# Ceci va aussi retourner { a = "foo"; b = "bar"; }
let 
  my-set = { x = "foo"; y = "bar"; };
in
  {
    a = my-set.x;
    b = my-set.y;
  }
```

* Pour simplifier les choses il est aussi possible d'utiliser l'interpolation de strings

```nix
nix-repl> foo = "hello"
nix-repl> bar = "world"

# ...pour les chemins de fichiers ou dossier
nix-repl> ./${foo}-${bar}.nix
/home/snowcode/hello-world.nix

# ...pour les strings
nix-repl> "${foo}"
"hello"
nix-repl> ''${bar}''
"world"

# ...pour les clé des attributs 
nix-repl> { ${foo} = "foo"; ${bar} = "bar"; }
{ hello = "foo"; world = "bar"; }
```

## Quelques opérations
Par convention ici les set d'attributs seront appellé `attrset`, les noms des clés seront appellé `attrpath`.

| Opération | Syntaxe | Retourne |
| --- | --- | --- | 
| Sélection d'un attribut | `attrset.attrpath` | La valeur |
| Test si a un certain attribut | `attrset ? attrpath` | Booléen |
| Concaténation de string | `string + string` | String |
| Concaténation de path | `path + path` | Path |
| Concaténation de path et string | `path + string` | Path |
| Concaténation de string et path | `string + path` | String |
| Combiner un set avec un autre (le deuxième à priorité si conflit) | `attrset1 // attrset2` | Set d'attributs |
| Comparaisons | `< == > <= >= !=` | Booléen |
| Opérateurs booléens | `\|\| && !` | Booléen |

# Les dérivations

Les dérivations dans Nix sont la base des builds des applications. Elle se basent sur la fonction `derivation`, cependant vous n'utiliserez probablement jamais cette fonction directement car elle est bien trop basique. C'est pour cela que des fonctions tel que `mkDerivation` du package `stdenv` sont là, ce sont des fonctions basée sur `derivation` mais qui rendent les choses plus simple et plus sûres pour les mainteneurs de paquets.

Nous allons voir ici comment la fonction de base `derivation` fonctionne, ce qu'elle fait et comment les builds fonctionnent dans Nix. 

## La syntaxe de `derivation`
Cette fonction prends comme argument un set d'attribut avec les attributs suivant : 

* `system`, qui précise l'architecture du paquet
* `name`, qui précise le nom du paquet
* `builder` qui est le programme utilisé pour construire le paquet (cela peut être une autre dérivation, ou un chemin de fichier)
* (optionnel) `args` qui est une liste des arguments à passer au *builder*
* (optionnel) `outputs` qui spécifie la liste symbolique des outputs (par exemple `outputs = [ "lib" "doc" ]`. Par défault, Nix crée une variable d'environnement `$out` qui spécifie la localisation du dossier dans le Nix store, mais on peut très bien en ajouter d'autres tel que `$lib` ou `$doc`

## Ce qui se passe lors d'un build d'une dérivation
Dans cet exemple regardons ce qu'il se passe si on build la dérivation suivante : 

```nix
nix-repl> :l <nixpkgs>
nix-repl> my-derivation = derivation { name = "foo"; builder = "${bash}/bin/bash"; args = [ ./builder.sh ]; system = builtins.currentSystem; }
nix-repl> :b my-derivation
```

### 1. Déplacement de tous les fichiers nécessaires dans le store
Tout d'abord Nix va détecter que `./builder.sh` est un chemin de fichier dans les arguments et va le passer dans le Nix Store. Pour cela il va le hash d'une certaine manière et y ajouter le nom du fichier à la fin. 

Il en va de même si on précise un dossier de source, etc.

### 2. Création d'un `.drv`
Ensuite Nix va créer un fichier `.drv` dans le nix store. 


Si on utilise la commande `nix show-derivation` sur ce fichier, on peut le lire, ce qui nous donne ceci : 

```bash
[snowcode@snowcode ~]$ nix show-derivation /nix/store/yg5ps2gvx39l38w90iyxss0xalcqka0y-foo.drv
{
  "/nix/store/yg5ps2gvx39l38w90iyxss0xalcqka0y-foo.drv": {
    "args": [
      "/nix/store/qfs6b6gcq9xi6gpf11vribfir8xn2nln-builder.sh"
    ],
    "builder": "/nix/store/561wgc73s0x1250hrgp7jm22hhv7yfln-bash-5.2-p15/bin/bash",
    "env": {
      "builder": "/nix/store/561wgc73s0x1250hrgp7jm22hhv7yfln-bash-5.2-p15/bin/bash",
      "name": "foo",
      "out": "/nix/store/vk7mk50mqswblpi88l86lf40bmrlffs1-foo",
      "system": "x86_64-linux"
    },
    "inputDrvs": {
      "/nix/store/0hnjp6s8k71xm62157v37zg3qzwvl8lx-bash-5.2-p15.drv": [
        "out"
      ]
    },
    "inputSrcs": [
      "/nix/store/qfs6b6gcq9xi6gpf11vribfir8xn2nln-builder.sh"
    ],
    "outputs": {
      "out": {
        "path": "/nix/store/vk7mk50mqswblpi88l86lf40bmrlffs1-foo"
      }
    },
    "system": "x86_64-linux"
  }
}
```

Ce fichier contient toutes les informations pour build un programme, sans tout les machins de Nix. Il contient les choses suivantes : 

* Les "out paths", comme on en a parlé avant par défault il n'y en a qu'un c'est le `$out`. Le hash est calculé en faisant un hash spécifique du .drv ayant un out path vide. 
* Les dérivations en input et les fichiers de source, ici nous avons bash (car nous y avons fait référence dans la dérivation) et le builder.sh (car nous avons mis son chemin). Nix va donc récupérer leur chemin dans le Nix store
* Le système, l'exécutable (builder) et ses arguments
* Les variables d'environnement qui sont passées au builder

Tous les chemins fonctionnant avec des hash des différentes dérivations et fichiers, cela assure donc qu'une même dérivation donnera toujours le même résultat quoi qu'il arrive. 

### 3. Le build de la dérivation
Ensuite grâce au fichier intermédiaire `.drv`, Nix n'a plus qu'a ajouter les variables d'environnement et exécuter le builder avec les bons arguments (et uniquement celles là).

Une fois cela fait, un raccourcis vers le dossier de la dérivation dans le Nix store est créé sous le nom de "result" dans le dossier courrant.

# Profiles

Les profiles sont là pour isoler la configuration de chaque utilisateur·ice et permettre de revenir en arrière à tout moment. Chaque profile (et chaque *génération* de ce profile est un raccourcis vers `/nix/var/nix/profiles/`.

A chaque changement dans l'environement de l'utilisateur·ice (avec `nix-env` par exemple), une nouvelle génération est crée depuis la précédente. 

[![](https://books.snowcode.ovh/uploads/images/gallery/2023-07/scaled-1680-/image-1689011925965.png)](https://books.snowcode.ovh/uploads/images/gallery/2023-07/image-1689011925965.png)

Pour illustrer tout ceci, voici ce qu'il se passe lorsque Carol installe Svn :

1. La dérivation est installée dans le Nix store (soit en la compilant soit en la téléchargeant depuis le cache)
2. Une nouvelle dérivation est crée depuis la génération précédente de Carol, dans cette nouvelle dérivation `svn` est lié dans un dossier `bin` (pour imiter la structure des fichiers de Linux)
3. Cette nouvelle dérivation est liée dans le dossier `/nix/var/nix/profiles` comme une nouvelle dérivation de Carol (par exemple `carol-24-link`)
4. Cette nouvelle dérivation est liée dans le profile de carol (`carol`)
5. Enfin le profile de carol est liée dans son dossier `~/.nix-profile`
6. Enfin tous les fichiers dans `~/.nix-profile/bin` sont ajouté à son `$PATH` pour être accessible à Carol. Carol peut maintenant utiliser Svn.

Maintenant lorsque Carol veut rollback vers sa génération précédente, il suffit simplement de remplacer le lien de `carol` vers `carol-23-link` à la place de `carol-24-link`. 

Ainsi faire un rollback se fait en une seule opération. Par contre ce système à le désavantage de prendre beaucoup de place car rien n'est supprimé. Dans la page suivante, nous allons voir comment le "garbage collector" fonctionne pour pouvoir faire de la place.

# Garbage Collector

Maintenant je pense qu'il est intéressant de se demander ce qu'il se passe lorsque l'on veut faire de l'espace sur le système. 

Ce qu'il se passe pour faire de l'espace c'est que lorsque un élément du Nix store est lié quelque part, un lien se fait dans le dossier `/nix/var/nix/gcroots/`. Ainsi lorsque l'on lance le garbage collector, il va supprimer tous les éléments qui ne sont pas référencé dans le dossier `gcroots`. 

Pour toujours permettre un rollback, tant qu'un profile a des générations comportant certains paquets, ces paquets sont aussi des `gcroots` et ne peuvent donc pas être supprimé avant que leur génération associée soit supprimée. 

Lorsque l'on lance `nix-collect-garbage` c'est qu'il va regarder récursivement tous les raccourcis vers le Nix Store qui sont dans gcroots, pour chaque fichier n'y étant pas il va le déplacer dans le dossier `/nix/store/trash`. Une fois tous transféré, les éléments sont supprimés.

# Channels et nixpkgs

Les *channels* sont les repos de Nix. Un channel c'est simplement une URL qui pointe vers un endroit ayant plein d'expressions Nix et un manifeste. Les channels les plus populaires (et probablement les seuls à vraiment être utilisé) sont ceux de *nixpkgs* (*nixpkgs unstable* et *nixpkgs 23.05* par exemple)

Globalement Nixpkgs est un repo git dont chaque branche est un channel. On "s'abonne" à un channel et à chaque fois que l'on veut télécharger un paquet, on va d'abord essayer d'aller chercher sa dérivation dans ses channels. 

Plus tard on va parler des *flakes* qui sont un moyen alternatif de proposer des paquets Nix de manière décentralisée (contrairement aux channels).

## Overlays
Il est possible d'appliquer des modifications à Nixpkgs via des *overlays* . Cela peut permettre d'ajouter de nouveau paquets ou de modifier des existants.

# Le cache (ou "nix store partagé")

Pour éviter de build plusieurs fois le même fichier et de perdre du temps à ça, il est possible de mettre en place un "cache", le nix store partagé. C'est à dire que tous les fichiers compilés, identifiés par leur hash de dérivation est stoqué et qu'avant d'essayer de build quelque chose, Nix va d'abord aller voir si le fichier déjà compilé n'existe pas déjà.

Ce système existe déjà pour tous les paquets de *nixpkgs*, c'est le `cache.nixos.org`. Ce cache fait plus de 400TiB et devient compliqué à maintenir (les couts de serveur s'élèvent à 108 000€ par an). Mais c'est grâce à ce cache que Nix peut permettre d'a la fois build les paquet depuis leur source, et d'à la fois télécharger directement leur fichiers binaires.

Certains membres de la communauté pensent peut'être à essayer de décentraliser le cache mais cela est toujours en discussion et rien n'est encore concret.

Pour le moment les 3 seuls moyen officiel de partager un nix store c'est par SSH, HTTP et S3.

# Flakes

Les Nix Flakes sont des dossiers (généralement des repo git) ayant à leur racine un fichier `flake.nix`. Ce fichier indique des "inputs" et des "outputs". Ainsi il permet de prendre des expressions Nix en entrée et donner toutes sortes de sorties.

Par exemple, on peut construire un flake qui:

* Setup un environnement de dev 
* Build le paquet
* Crée une image docker minimales
* Build et effectue des tests sur le résultat
* Crée des configurations NixOS
* Crée des overlays de nixpkgs

et bien d'autres.

Un flake a également l'avantage par rapport aux channels, d'être vraiment reproductible. Lorsqu'un flake est créé et lancé pour la première fois, un fichier `flake.lock` est automatiquement créé. Ce fichier va spécifier chaque dépendence au commit prêt. Contrairement aux channels de Nixpkgs qui sont spécifiés par "release" et non par commit.

Les flakes bénéficient aussi de très bons outils (flake-utils et les commandes nix). Les flake-utils donnent des outils puissant pour créer des flakes et les commandes nix sont des outils très puissant et simple pour *utiliser* ces flakes.

Ainsi par exemple : 

* `nix build` va build la dérivation se trouvant dans l'output nomé `defaultPackage.<system>` ou `packages.<system>.default`
* `nix shell` va entrer dans un shell où le package/derivation dit "default" est accessible dans le `$PATH`
* `nix run` va lancer le binaire du package "default" qui correspond au nom de la dérivation
* `nix develop` qui va donner tous les outils de développement pour build le paquet et développer dans le `$PATH` (va d'abord chercher après `devShells.<system>.default` ou `devShell.<system>` avant de chercher pour le package default.
* `nix flake show` donne la liste de tous les outputs d'un flake
* `nix flake info` donne des métadonnées générales sur le flake
* `nix flake new` crée un nouveau flake
* `nix flake update` met à jour le fichier `nix.lock`
* `nix flake check` qui effectue les checks sur le flake
* `nix profile install` pour installer le defaultPackage du flake dans son profile 
* `nix profile list` pour voir la liste des flakes installés
* `nix profile remove <id>` pour supprimer un flake de son profile

Je vais expliquer plus en détail comment ça fonctionne dans la partie pratique.

Il est aussi possible de créer des alias et d'utiliser `nixpkgs` via les flakes. Les alias peuvent être défini grâce au *flake registery*. Par exemple le flake `nixpkgs` est un alias pour `github:NixOS/nixpkgs/nixpkgs-unstable`. Et imaginons que je veut installer cowsay temporairement via un flake je peux simplement faire.

```bash
nix shell nixpkgs#cowsay
```