diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index a787d03..de8a07b 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- - uses: cachix/install-nix-action@v14
+ - uses: cachix/install-nix-action@v17
- uses: cachix/cachix-action@v10
with:
name: shajra
diff --git a/README.md b/README.md
index 87ead08..6276c1a 100644
--- a/README.md
+++ b/README.md
@@ -1,17 +1,16 @@
- [About the project](#sec-1)
- [The mappings](#sec-2)
- - [Model 01 “shajra” keymap](#sec-2-1)
+ - [Model 100 and Model 01 “shajra” keymap](#sec-2-1)
- [Ergodox EZ “shajra” keymap (Moonlander similar)](#sec-2-2)
- [Using these key mappings](#sec-3)
- [1. Install Nix on your GNU/Linux distribution](#sec-3-1)
- - [2. Set up Cachix](#sec-3-2)
- - [3. Make sure your udev rules are set](#sec-3-3)
- - [4. For Kaleidoscope, join the necessary OS group](#sec-3-4)
- - [5. Unplug and replug your keyboard](#sec-3-5)
- - [6. Get the code and run it](#sec-3-6)
- - [Flashing an Ergodox EZ keyboard](#sec-3-6-1)
- - [Flashing a Moonlander keyboard](#sec-3-6-2)
- - [Flashing a Keyboardio Model 01 keyboard](#sec-3-6-3)
+ - [2. Make sure your udev rules are set](#sec-3-2)
+ - [4. For Kaleidoscope, join the necessary OS group](#sec-3-3)
+ - [5. Unplug and replug your keyboard](#sec-3-4)
+ - [6. Flash your keyboard](#sec-3-5)
+ - [Flashing an Ergodox EZ keyboard](#sec-3-5-1)
+ - [Flashing a Moonlander keyboard](#sec-3-5-2)
+ - [Flashing a Keyboardio keyboard](#sec-3-5-3)
- [Reverting to the factory default mapping](#sec-4)
- [Customization](#sec-5)
- [Customizing keymaps](#sec-5-1)
@@ -24,30 +23,38 @@
# About the project
-This project has the “shajra” keyboard mappings for three ergonomic split keyboards:
+This project has the “shajra” keyboard mappings for four ergonomic split keyboards:
-- [Keyboardio's Model 01](https://shop.keyboard.io), programmed with [Kaleidoscope](https://github.com/keyboardio/Kaleidoscope) firmware.
-- [ZSA Technology Labs' Ergodox EZ](https://ergodox-ez.com), programmed with [QMK](https://docs.qmk.fm) firmware
+- [Keyboardio's Model 100](https://shop.keyboard.io), programmed with [Kaleidoscope](https://github.com/keyboardio/Kaleidoscope) firmware
+- [Keyboardio's Model 01 (discontinued)](https://shop.keyboard.io), also programmed with [Kaleidoscope](https://github.com/keyboardio/Kaleidoscope)
- [ZSA Technology Labs' Moonlander](https://www.zsa.io/moonlander/), programmed with [QMK](https://docs.qmk.fm) firmware
+- [ZSA Technology Labs' Ergodox EZ](https://ergodox-ez.com), also programmed with [QMK](https://docs.qmk.fm).
-Beyond the keymap, this project offers some streamlined automation with [Nix](https://nixos.org/nix) that you can use for your own keymap. This automation works for GNU/Linux only (sorry, not MacOS or Windows). See [the provided documentation on Nix](doc/nix.md) for more on what Nix is, why we're motivated to use it, and how to get set up with it for this project.
+Beyond the keymap, this project offers some streamlined automation with [Nix](https://nixos.org/nix) that you can use for your own keymap. Or you can use this automation to return your keyboard to its factory default.
+
+This automation works for GNU/Linux only (sorry, not MacOS or Windows). If you're new to Nix this project bundles a few guides to get you started:
+
+- [Introduction to Nix and motivations to use it](doc/nix-introduction.md)
+- [Nix installation and configuration guide](doc/nix-installation.md)
+- [Nix end user guide](doc/nix-usage-flakes.md)
+- [Introduction to the Nix programming language](doc/nix-language.md)
The rest of this document discusses using this automation. To get the most out of the keymap itself, you may be interested in the [design document](doc/design.md) explaining the motivation behind the mapping.
# The mappings
-The “shajra” keymaps for these keyboards are extremely similar, which works out well because the physical layouts of these keyboards are also similar. We can more easily switch from one keyboard to another, and retain the design benefits of the mapping.
+The “shajra” keymaps for all four keyboards are extremely similar, which works out well because the physical layouts of these keyboards are also similar. We can more easily switch from one keyboard to another, and retain the design benefits of the mapping.
-## Model 01 “shajra” keymap
+## Model 100 and Model 01 “shajra” keymap
-![img](doc/model-01-shajra-layout.png)
+![img](doc/model-100-shajra-layout.png)
## Ergodox EZ “shajra” keymap (Moonlander similar)
-Note the Moonlander keyboard is almost an identical layout to the EZ, and not illustrated here. There are just two two less keys on the thumb cluster. The leads to not having either Home or End on the base layer for the Moonlander. And the "application menu" keycodes are moved to the bottom-outer corners.
-
![img](doc/ergodox-ez-shajra-layout.png)
+Note the Moonlander keyboard is almost an identical layout to the EZ, and not illustrated here. There are just two less keys on the thumb cluster. This leads to not having either Home or End on the base layer for the Moonlander. And the "application menu" keycodes are moved to the bottom-outer corners.
+
# Using these key mappings
This project only supports a GNU/Linux operating system (sorry, not MacOS or Windows) with the [Nix package manager](https://nixos.org/nix) installed.
@@ -63,76 +70,50 @@ The following steps will get your keyboard flashed.
## 1. Install Nix on your GNU/Linux distribution
-> **NOTE:** You don't need this step if you're running NixOS, which comes with Nix baked in.
-
-If you don't already have Nix, [the official installation script](https://nixos.org/learn.html) should work on a variety of UNIX-like operating systems:
-
-```bash
-sh <(curl -L https://nixos.org/nix/install) --daemon
-```
-
-After installation, you may have to exit your terminal session and log back in to have environment variables configured to put Nix executables on your `PATH`.
-
-The `--daemon` switch installs Nix in the recommended multi-user mode. This requires the script to run commands with `sudo`. The script fairly verbosely reports everything it does and touches. If you later want to uninstall Nix, you can run the installation script again, and it will tell you what to do to get back to a clean state.
-
-The Nix manual describes [other methods of installing Nix](https://nixos.org/nix/manual/#chap-installation) that may suit you more.
-
-## 2. Set up Cachix
-
-It's recommended to configure Nix to use shajra.cachix.org as a Nix *substitutor*. This project pushes built Nix packages to [Cachix](https://cachix.org) as part of its continuous integration. Once configured, Nix will pull down these pre-built packages instead of building them locally (potentially saving a lot of time). This augments the default substitutor that pulls from cache.nixos.org.
-
-You can configure shajra.cachix.org as a substitutor with the following command:
+If you don't have Nix installed and configured on your system, follow the [instructions provided](doc/nix-installation.md). As discussed, setting up Cachix and enabling the experimental *flakes* feature are both recommended.
-```sh
-nix run \
- --file https://cachix.org/api/v1/install \
- cachix \
- --command cachix use shajra
-```
-
-Cachix is a service that anyone can use. You can call this command later to add substitutors for someone else using Cachix, replacing "shajra" with their cache's name.
-
-If you've just run a multi-user Nix installation and are not yet a trusted user in `/etc/nix/nix.conf`, this command may not work. But it will report back some options to proceed.
-
-One option sets you up as a trusted user, and installs Cachix configuration for Nix locally at `~/.config/nix/nix.conf`. This configuration will be available immediately, and any subsequent invocation of Nix commands will take advantage of the Cachix cache.
-
-You can alternatively configure Cachix as a substitutor globally by running the above command as a root user (say with `sudo`), which sets up Cachix directly in `/etc/nix/nix.conf`. The invocation may give further instructions upon completion.
-
-## 3. Make sure your udev rules are set
+## 2. Make sure your udev rules are set
To program either keyboard with a new mapping, you need to augment your OS configuration with new udev rules.
The following are recommended rules for each keyboard:
# For Ergodox EZ
- ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789B]?", \
- ENV{ID_MM_DEVICE_IGNORE}="1"
- ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789A]?", \
- ENV{MTP_NO_PROBE}="1"
- SUBSYSTEMS=="usb", ATTRS{idVendor}=="16c0", \
- ATTRS{idProduct}=="04[789ABCD]?", MODE:="0666"
- KERNEL=="ttyACM*", ATTRS{idVendor}=="16c0", \
- ATTRS{idProduct}=="04[789B]?", MODE:="0666"
+ ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789B]?", ENV{ID_MM_DEVICE_IGNORE}="1"
+ ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789A]?", ENV{MTP_NO_PROBE}="1"
+ SUBSYSTEMS=="usb", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789ABCD]?", TAG+="uaccess"
+ KERNEL=="ttyACM*", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789B]?", TAG+="uaccess"
# For Moonlander
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", \
- MODE:="0666", SYMLINK+="stm32_dfu"
+ TAG+="uaccess", SYMLINK+="stm32_dfu"
+
+ # For Model 100
+ SUBSYSTEMS=="usb", ATTRS{idVendor}=="3496", ATTRS{idProduct}=="0005", \
+ SYMLINK+="model100", ENV{ID_MM_DEVICE_IGNORE}:="1", \
+ ENV{ID_MM_CANDIDATE}:="0", MODE="0666", TAG+="uaccess", TAG+="seat"
+
+ SUBSYSTEMS=="usb", ATTRS{idVendor}=="3496", ATTRS{idProduct}=="0006", \
+ SYMLINK+="model100", ENV{ID_MM_DEVICE_IGNORE}:="1", \
+ ENV{ID_MM_CANDIDATE}:="0", MODE="0666", TAG+="uaccess", TAG+="seat"
# For Model 01
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="2300", \
- SYMLINK+="model01", ENV{ID_MM_DEVICE_IGNORE}:="1", \
- ENV{ID_MM_CANDIDATE}:="0", TAG+="uaccess", TAG+="seat"
+ SYMLINK+="model01", ENV{ID_MM_DEVICE_IGNORE}:="1", \
+ ENV{ID_MM_CANDIDATE}:="0", MODE="0666", TAG+="uaccess", TAG+="seat"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="2301", \
- SYMLINK+="model01", ENV{ID_MM_DEVICE_IGNORE}:="1", \
- ENV{ID_MM_CANDIDATE}:="0", TAG+="uaccess", TAG+="seat"
+ SYMLINK+="model01", ENV{ID_MM_DEVICE_IGNORE}:="1", \
+ ENV{ID_MM_CANDIDATE}:="0", MODE="0666", TAG+="uaccess", TAG+="seat"
-These settings should correspond to the official documentation of tools and libraries used by this project:
+These settings should roughly correspond to the official documentation of tools and libraries used by this project:
- [QMK documentation for configuring Halfkey bootloader used by the Ergodox EZ](https://docs.qmk.fm/#/flashing?id=halfkay)
- [Teensy CLI documentation, used internally for flashing the Ergodox EZ](https://www.pjrc.com/teensy/loader_cli.html)
- [Wally CLI, used internally for flashing the Moonlander](https://github.com/zsa/wally/blob/master/dist/linux64/50-wally.rules)
- [Kaleidoscope documentation](https://kaleidoscope.readthedocs.io/en/latest/setup_toolchain.html#a-name-arduino-linux-a-install-arduino-on-linux)
+Note, if the `MODE="0666"` settings above are not set, you may require root-privileges (sudo) to call the flashing scripts.
+
Each distribution is different, but on many GNU/Linux systems, udev rules are put in a file in `/etc/udev/rules.d` with a ".rules" extension.
On some systems, you can activate these rules with the following commands:
@@ -143,7 +124,7 @@ udevadm control --reload-rules udevadm trigger
Or just restart the computer.
-## 4. For Kaleidoscope, join the necessary OS group
+## 4. For Kaleidoscope, join the necessary OS group
> ***NOTE:*** You don't need this step if you're flashing the Ergodox EZ or Moonlander.
@@ -172,81 +153,60 @@ groups | grep dialout
users wheel video dialout docker
-## 5. Unplug and replug your keyboard
+## 5. Unplug and replug your keyboard
Unplug your keyboard(s) and plug them back in, to make sure everything's set to program. Rebooting your computer is probably overkill, but would probably work too.
-## 6. Get the code and run it
+## 6. Flash your keyboard
-Clone this code base and go into the directory:
-
-```sh
-cd $SOME_WORKING_DIR
-clone https://github.com/shajra/shajra-keyboards.git
-cd shajra-keyboards
-```
+There are four scripts provided by this project:
-Note, the first time you run the commands described below, you'll see Nix doing a lot of downloading and compiling. After that, subsequent invocations should be quicker with less output.
+- `flash-ergodoxez`
+- `flash-model01`
+- `flash-model100`
+- `flash-moonlander`
-### Flashing an Ergodox EZ keyboard
+Calling these scripts without any arguments will flash your respective keyboard with the "shajra" keymap. There is a "–factory" switch to flash your keyboard back to a factory default keymap.
-You can run the following to flash your Ergodox EZ with the new keymap, pressing the reset button when prompted (access the reset button with an unbent paperclip inserted into the small hole in the top right corner of the right keyboard half):
+If you enabled flakes in your Nix installation, you can run these scripts without installing them. Here's an invocation illustrating doing so with `flash-ergodoxez`:
```sh
-./flash-ergodoxez
+nix run github:shajra/shajra-keyboards#flash-ergodoxez
```
Flashing ZSA Technology Lab's Ergodox EZ (custom "shajra" keymap)
=================================================================
- FLASH SOURCE: /nix/store/9y2980lnbwkfx89dqzwwnnpgr2zg0va4-qmk-custom-shajra-src
- FLASH BINARY: /nix/store/16l1vw1vpdqsysw7xlh48iqnbrvagsvs-ergodoxez-custom-shajra.hex
+ FLASH SOURCE: /nix/store/i9lfaqmm36mlqdbvn1wpnv95n5q931lg-qmk-custom-shajra-src
+ FLASH BINARY: /nix/store/alafhrwv03fp1lhlrzs5412ydxkigfm1-ergodoxez-custom-shajra.hex
- Teensy Loader, Command Line, Version 2.1
- Read "/nix/store/16l1vw1vpdqsysw7xlh48iqnbrvagsvs-ergodoxez-custom-shajra.hex": 22288 bytes, 69.1% usage
- Waiting for Teensy device...
- (hint: press the reset button)
+ ⠋ Press the reset button of your keyboard.
-### Flashing a Moonlander keyboard
+The same works for the other three flashing scripts. Just replace "flash-ergodoxez" in the invocation above with the script of your choice.
-You can run the following to flash your Moonlander with the new keymap, pressing the reset button when prompted (access the reset button with an unbent paperclip inserted into the small hole in the top left corner of the left keyboard half):
+Alternatively, the four flashing scripts are provided at the root of this project. Just clone this repository, and you can call them directly:
```sh
-./flash-moonlander
+cd $SOME_WORKING_DIR
+clone https://github.com/shajra/shajra-keyboards.git
+cd shajra-keyboards
+./flash-ergodoxez
```
-
- Flashing ZSA Technology Lab's Moonlander (custom "shajra" keymap)
- =================================================================
-
- FLASH SOURCE: /nix/store/3lrbcyaa031p9xk2jasdiyngar5gc1lr-qmk-custom-shajra-src
- FLASH BINARY: /nix/store/hpqsc7gqzq9l2vb7j7c2mjkf992646ca-moonlander-custom-shajra.bin
-
- ⠋ Press the reset button of your keyboard.
+Note, the first time you run the commands described below, you'll see Nix doing a lot of downloading and compiling. After that, subsequent invocations should be quicker with less output.
-### Flashing a Keyboardio Model 01 keyboard
+### Flashing an Ergodox EZ keyboard
-You can run the following to flash your Keyboardio Model 01, holding down the `Prog` key and then pressing `Enter` when prompted:
+When flashing with `flash-ergodoxez`, you will be prompted to press a reset button. Access this button with an unbent paperclip inserted into the small hole in the top right corner of the right keyboard half.
-```sh
-./flash-model01
-```
+### Flashing a Moonlander keyboard
-
- Flashing Keyboardio's Model 01 (custom "shajra" keymap)
- =======================================================
-
- FLASH SOURCE: /nix/store/pzxrj5y7cg9kvc3hl7vlqhm86n5as471-model01-custom-shajra-src
- FLASH BINARY: /nix/store/ajdy8yy25zyywhb07na490n27ihjcjsi-model01-custom-shajra-hex
- DETECTED PORT: /dev/ttyACM0
-
- To flash your keyboard, you must hold down the 'Prog' key.
- While holding the 'Prog' key, press 'Enter', but continue to
- hold the 'Prog' key. You can release it once flashing has
- started, and the key glows red.
-
- Do these steps now, or Ctrl-C to quit...
+When flashing with `flash-ergodoxez`, you will be prompted to press a reset button. Access this button with an unbent paperclip inserted into the small hole in the top left corner of the left keyboard half.
+
+### Flashing a Keyboardio keyboard
+
+When flashing with `flash-model100` or `flash-model01`, you will be prompted with instructions to hold down the `Prog` key, and then press `Enter`.
The `Prog` key is hardwired to be the top-left-most key of the Keyboardio Model 01, but the `Enter` key can be remapped. If you forget where the `Enter` has been mapped to on your Keyboard, you can hit `Enter` on another connected keyboard.
@@ -272,33 +232,38 @@ The used keymap source code is copied into `/nix/store`, and the invocation of t
## Development
-This project relies heavily on Nix, primarily to help deal with all the complexity of setting up dependencies.
-
-The [provided documentation on Nix](doc/nix.md) introduces Nix and how to use it in the context of this project.
+This project relies heavily on Nix, primarily to help deal with all the complexity of setting up dependencies. The development of this project also relies on an experimental feature of Nix called *flakes*, not required to use the project. See the included [introduction to Nix](doc/nix-introduction.md) if you're new to Nix or flakes.
If you want to check that everything builds before flashing your keyboard, you can build locally everything built by this project's continuous integration:
```sh
-nix build --no-link --file nix/ci.nix \
- && nix path-info --file nix/ci.nix
+tree $(nix build --no-link --print-out-paths) 2>/dev/null
```
- /nix/store/16l1vw1vpdqsysw7xlh48iqnbrvagsvs-ergodoxez-custom-shajra.hex
- /nix/store/1mz3bf1q871qwp0xsrip04fh0j5vr2i0-moonlander-custom-shajra-flash
- /nix/store/1ya4yka8hzjjya90fvfs67495bzhwbyp-moonlander-factory-flash
- /nix/store/2jyin75s0kziz5bm3fpzdm6fac060a7q-model01-factory-hex
- /nix/store/3l03cv7skh4f56axanxr4kdx374x7065-model01-factory-flash
- /nix/store/4sa2ghfdgmw2xpfz4qjx91zy2kpirf55-flash-model01
- /nix/store/8qw30diqjm41gkv7g693kb9jam88hac8-model01-custom-shajra-flash
- /nix/store/ajdy8yy25zyywhb07na490n27ihjcjsi-model01-custom-shajra-hex
- /nix/store/gyjgdy246a39gmf7bwxxj3c1c2f83rw8-ergodoxez-factory-flash
- /nix/store/hpqsc7gqzq9l2vb7j7c2mjkf992646ca-moonlander-custom-shajra.bin
- /nix/store/k2sxmw5zj4fwddydrvckyx2mrr8sp6nq-ergodoxez-factory.hex
- /nix/store/k8m4i8k5k7i1562ky3g0xhwhm2p9i7ka-ergodoxez-custom-shajra-flash
- /nix/store/kdkxmk0gdpf65n5yp1b8mvb4pvm01n6f-flash-moonlander
- /nix/store/pc4r14p779hhbc9zllmpmf0s8648as6m-moonlander-factory.bin
- /nix/store/q3f88df3iykql3zdykl15pgc8qwx6y8m-flash-ergodoxez
- /nix/store/vlsmhg30fh50pazvbcd2wy25lc8wija0-shajra-keyboards-licenses
+ /nix/store/pkygbrzrl837872zn3w3q4q2wljpd5hv-shajra-keyboards-ci
+ ├── build-ergodoxez-custom-shajra-flash -> /nix/store/xb8w4zfk94k319q4nmh6s401m2kr3b01-ergodoxez-custom-shajra-flash
+ ├── build-ergodoxez-custom-shajra-hex -> /nix/store/alafhrwv03fp1lhlrzs5412ydxkigfm1-ergodoxez-custom-shajra.hex
+ ├── build-ergodoxez-factory-flash -> /nix/store/2nphw8ncjd34wb77zq1maip3xlkmj848-ergodoxez-factory-flash
+ ├── build-ergodoxez-factory-hex -> /nix/store/v8y8nbsc8f964bab7q4ars30avh80mvj-ergodoxez-factory.hex
+ ├── build-model01-custom-shajra-flash -> /nix/store/34494dhdq2gx4hi53jimvjmqljkyn50k-model01-custom-shajra-flash
+ ├── build-model01-custom-shajra-hex -> /nix/store/fnmriz6zfnsvmmn00rgv9cnrhlrrrlv1-model01-custom-shajra-hex
+ ├── build-model01-factory-flash -> /nix/store/r04a7n4kidafp1d1vyn8hihnnp7vx33n-model01-factory-flash
+ ├── build-model01-factory-hex -> /nix/store/hrvhyn8784zapapmp45dmd5k56fi4pax-model01-factory-hex
+ ├── build-model100-custom-shajra-flash -> /nix/store/sx7w3db5s7wg31zvhh1qkfqmh4qaz46l-model100-custom-shajra-flash
+ ├── build-model100-custom-shajra-hex -> /nix/store/h86pwv92wh6jbw5w96c9q3in1qnd27bs-model100-custom-shajra-hex
+ ├── build-model100-factory-flash -> /nix/store/kq7l0nyibhki8wg1pw2n504ng3pcx2na-model100-factory-flash
+ ├── build-model100-factory-hex -> /nix/store/9i22w0924cx1rm4fwxl88bwjrxnn8cv1-model100-factory-hex
+ ├── build-moonlander-custom-shajra-flash -> /nix/store/mrvs0s5ayi829n79pn9vfh9zyn3lzhqp-moonlander-custom-shajra-flash
+ ├── build-moonlander-custom-shajra-hex -> /nix/store/pmfj6pcyrrqmm75cr45rgak9yqjnc8nv-moonlander-custom-shajra.bin
+ ├── build-moonlander-factory-flash -> /nix/store/d92zdnx8l2mpx33klrvjnmdjs3lwylfi-moonlander-factory-flash
+ ├── build-moonlander-factory-hex -> /nix/store/1206ymbcancln1nqxd7ihv8hpn0ndf81-moonlander-factory.bin
+ ├── flash-ergodoxez -> /nix/store/5nqd92zrhra7rrbq9jxq83sy9hchp2hp-flash-ergodoxez
+ ├── flash-model01 -> /nix/store/3j29yy8h9ikvafii5y8iy225hg5f2sjn-flash-model01
+ ├── flash-model100 -> /nix/store/q19y0rapng0immn749hdk1i48dk4j3wi-flash-model100
+ ├── flash-moonlander -> /nix/store/r4pkms4xsn8snpcp2pp56p5vi3m4v6vm-flash-moonlander
+ └── licenses -> /nix/store/6nryh742rmj5xpn8ikgwdlq5xsn9nkr0-shajra-keyboards-licenses
+
+ 18 directories, 4 files
# Release
diff --git a/README.org b/README.org
index 9396ff5..589f308 100644
--- a/README.org
+++ b/README.org
@@ -1,23 +1,30 @@
#+title: Ergonomic Keyboard “shajra” Mappings (with Nix)
-#+setupfile: doc/links.org
+#+setupfile: doc/internal/links.org
#+options: ^:nil
[[https://github.com/shajra/shajra-keyboards/actions][https://github.com/shajra/shajra-keyboards/workflows/CI/badge.svg]]
* About the project
-This project has the “shajra” keyboard mappings for three ergonomic split
+This project has the “shajra” keyboard mappings for four ergonomic split
keyboards:
-- [[keyboardio][Keyboardio's Model 01]], programmed with [[kaleidoscope][Kaleidoscope]] firmware.
-- [[ergodox-ez][ZSA Technology Labs' Ergodox EZ]], programmed with [[qmk][QMK]] firmware
+- [[keyboardio][Keyboardio's Model 100]], programmed with [[kaleidoscope][Kaleidoscope]] firmware
+- [[keyboardio][Keyboardio's Model 01 (discontinued)]], also programmed with [[kaleidoscope][Kaleidoscope]]
- [[moonlander][ZSA Technology Labs' Moonlander]], programmed with [[qmk][QMK]] firmware
+- [[ergodox-ez][ZSA Technology Labs' Ergodox EZ]], also programmed with [[qmk][QMK]].
Beyond the keymap, this project offers some streamlined automation with [[nix][Nix]] that
-you can use for your own keymap. This automation works for GNU/Linux only
-(sorry, not MacOS or Windows). See [[file:doc/nix.org][the provided documentation on Nix]] for more on
-what Nix is, why we're motivated to use it, and how to get set up with it for
-this project.
+you can use for your own keymap. Or you can use this automation to return your
+keyboard to its factory default.
+
+This automation works for GNU/Linux only (sorry, not MacOS or Windows). If
+you're new to Nix this project bundles a few guides to get you started:
+
+- [[file:doc/nix-introduction.org][Introduction to Nix and motivations to use it]]
+- [[file:doc/nix-installation.org][Nix installation and configuration guide]]
+- [[file:doc/nix-usage-flakes.org][Nix end user guide]]
+- [[file:doc/nix-language.org][Introduction to the Nix programming language]]
The rest of this document discusses using this automation. To get the most out
of the keymap itself, you may be interested in the [[file:doc/design.org][design document]] explaining
@@ -25,24 +32,24 @@ the motivation behind the mapping.
* The mappings
-The “shajra” keymaps for these keyboards are extremely similar, which works out
-well because the physical layouts of these keyboards are also similar. We can
-more easily switch from one keyboard to another, and retain the design benefits
-of the mapping.
+The “shajra” keymaps for all four keyboards are extremely similar, which works
+out well because the physical layouts of these keyboards are also similar. We
+can more easily switch from one keyboard to another, and retain the design
+benefits of the mapping.
-** Model 01 “shajra” keymap
+** Model 100 and Model 01 “shajra” keymap
-[[file:doc/model-01-shajra-layout.png]]
+[[file:doc/model-100-shajra-layout.png]]
** Ergodox EZ “shajra” keymap (Moonlander similar)
-Note the Moonlander keyboard is almost an identical layout to the EZ, and not
-illustrated here. There are just two two less keys on the thumb cluster. The
-leads to not having either Home or End on the base layer for the Moonlander. And
-the "application menu" keycodes are moved to the bottom-outer corners.
-
[[file:doc/ergodox-ez-shajra-layout.png]]
+Note the Moonlander keyboard is almost an identical layout to the EZ, and not
+illustrated here. There are just two less keys on the thumb cluster. This leads
+to not having either Home or End on the base layer for the Moonlander. And the
+"application menu" keycodes are moved to the bottom-outer corners.
+
* Using these key mappings
This project only supports a GNU/Linux operating system (sorry, not MacOS or
@@ -64,13 +71,11 @@ The following steps will get your keyboard flashed.
** 1. Install Nix on your GNU/Linux distribution
-#+include: "doc/nix.org::*Nix package manager setup" :only-contents t
+If you don't have Nix installed and configured on your system, follow the
+[[file:doc/nix-installation.org][instructions provided]]. As discussed, setting up Cachix and enabling the
+experimental /flakes/ feature are both recommended.
-** 2. Set up Cachix
-
-#+include: "doc/nix.org::*Cache setup" :only-contents t
-
-** 3. Make sure your udev rules are set
+** 2. Make sure your udev rules are set
To program either keyboard with a new mapping, you need to augment your OS
configuration with new udev rules.
@@ -78,37 +83,45 @@ configuration with new udev rules.
The following are recommended rules for each keyboard:
#+begin_example
- # For Ergodox EZ
- ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789B]?", \
- ENV{ID_MM_DEVICE_IGNORE}="1"
- ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789A]?", \
- ENV{MTP_NO_PROBE}="1"
- SUBSYSTEMS=="usb", ATTRS{idVendor}=="16c0", \
- ATTRS{idProduct}=="04[789ABCD]?", MODE:="0666"
- KERNEL=="ttyACM*", ATTRS{idVendor}=="16c0", \
- ATTRS{idProduct}=="04[789B]?", MODE:="0666"
-
- # For Moonlander
- SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", \
- MODE:="0666", SYMLINK+="stm32_dfu"
-
- # For Model 01
- SUBSYSTEMS=="usb", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="2300", \
- SYMLINK+="model01", ENV{ID_MM_DEVICE_IGNORE}:="1", \
- ENV{ID_MM_CANDIDATE}:="0", TAG+="uaccess", TAG+="seat"
- SUBSYSTEMS=="usb", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="2301", \
- SYMLINK+="model01", ENV{ID_MM_DEVICE_IGNORE}:="1", \
- ENV{ID_MM_CANDIDATE}:="0", TAG+="uaccess", TAG+="seat"
+# For Ergodox EZ
+ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789B]?", ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789A]?", ENV{MTP_NO_PROBE}="1"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789ABCD]?", TAG+="uaccess"
+KERNEL=="ttyACM*", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789B]?", TAG+="uaccess"
+
+# For Moonlander
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", \
+ TAG+="uaccess", SYMLINK+="stm32_dfu"
+
+# For Model 100
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="3496", ATTRS{idProduct}=="0005", \
+ SYMLINK+="model100", ENV{ID_MM_DEVICE_IGNORE}:="1", \
+ ENV{ID_MM_CANDIDATE}:="0", MODE="0666", TAG+="uaccess", TAG+="seat"
+
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="3496", ATTRS{idProduct}=="0006", \
+ SYMLINK+="model100", ENV{ID_MM_DEVICE_IGNORE}:="1", \
+ ENV{ID_MM_CANDIDATE}:="0", MODE="0666", TAG+="uaccess", TAG+="seat"
+
+# For Model 01
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="2300", \
+ SYMLINK+="model01", ENV{ID_MM_DEVICE_IGNORE}:="1", \
+ ENV{ID_MM_CANDIDATE}:="0", MODE="0666", TAG+="uaccess", TAG+="seat"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="2301", \
+ SYMLINK+="model01", ENV{ID_MM_DEVICE_IGNORE}:="1", \
+ ENV{ID_MM_CANDIDATE}:="0", MODE="0666", TAG+="uaccess", TAG+="seat"
#+end_example
-These settings should correspond to the official documentation of tools and
-libraries used by this project:
+These settings should roughly correspond to the official documentation of tools
+and libraries used by this project:
- [[qmk-setup][QMK documentation for configuring Halfkey bootloader used by the Ergodox EZ]]
- [[teensy][Teensy CLI documentation, used internally for flashing the Ergodox EZ]]
- [[wally-udev][Wally CLI, used internally for flashing the Moonlander]]
- [[kaleidoscope-setup][Kaleidoscope documentation]]
+Note, if the ~MODE="0666"~ settings above are not set, you may require
+root-privileges (sudo) to call the flashing scripts.
+
Each distribution is different, but on many GNU/Linux systems, udev rules are
put in a file in =/etc/udev/rules.d= with a ".rules" extension.
@@ -166,112 +179,74 @@ Unplug your keyboard(s) and plug them back in, to make sure everything's set to
program. Rebooting your computer is probably overkill, but would probably work
too.
-** 6. Get the code and run it
+** 6. Flash your keyboard
-Clone this code base and go into the directory:
+There are four scripts provided by this project:
+- =flash-ergodoxez=
+- =flash-model01=
+- =flash-model100=
+- =flash-moonlander=
-#+begin_src sh :eval no
-cd $SOME_WORKING_DIR
-clone https://github.com/shajra/shajra-keyboards.git
-cd shajra-keyboards
-#+end_src
-
-Note, the first time you run the commands described below, you'll see Nix doing
-a lot of downloading and compiling. After that, subsequent invocations should be
-quicker with less output.
-
-*** Flashing an Ergodox EZ keyboard
+Calling these scripts without any arguments will flash your respective keyboard
+with the "shajra" keymap. There is a "--factory" switch to flash your keyboard
+back to a factory default keymap.
-You can run the following to flash your Ergodox EZ with the new keymap, pressing
-the reset button when prompted (access the reset button with an unbent paperclip
-inserted into the small hole in the top right corner of the right keyboard
-half):
+If you enabled flakes in your Nix installation, you can run these scripts
+without installing them. Here's an invocation illustrating doing so with
+=flash-ergodoxez=:
#+begin_src sh :eval no
-./flash-ergodoxez
+nix run github:shajra/shajra-keyboards#flash-ergodoxez
#+end_src
#+name: flash-ergodoxez
#+begin_src bash :results output :exports results
-head -11 < <(./flash-ergodoxez)
+head -c350 < <(./flash-ergodoxez) | ansifilter
#+end_src
#+results: flash-ergodoxez
-#+begin_example
-
-Flashing ZSA Technology Lab's Ergodox EZ (custom "shajra" keymap)
-=================================================================
-
-FLASH SOURCE: /nix/store/9y2980lnbwkfx89dqzwwnnpgr2zg0va4-qmk-custom-shajra-src
-FLASH BINARY: /nix/store/16l1vw1vpdqsysw7xlh48iqnbrvagsvs-ergodoxez-custom-shajra.hex
-
-Teensy Loader, Command Line, Version 2.1
-Read "/nix/store/16l1vw1vpdqsysw7xlh48iqnbrvagsvs-ergodoxez-custom-shajra.hex": 22288 bytes, 69.1% usage
-Waiting for Teensy device...
- (hint: press the reset button)
-#+end_example
-
-#+name: flash-ergodoxez-cleanup
-#+begin_src sh :results output silent :exports none
-pkill teensy-loader
-#+end_src
-
-*** Flashing a Moonlander keyboard
-
-You can run the following to flash your Moonlander with the new keymap, pressing
-the reset button when prompted (access the reset button with an unbent paperclip
-inserted into the small hole in the top left corner of the left keyboard half):
-
-#+begin_src sh :eval no
-./flash-moonlander
-#+end_src
-
-#+name: flash-moonlander
-#+begin_src bash :results output :exports results
-head -c350 < <(./flash-moonlander) | ansifilter
-#+end_src
-
-#+results: flash-moonlander
:
-: Flashing ZSA Technology Lab's Moonlander (custom "shajra" keymap)
+: Flashing ZSA Technology Lab's Ergodox EZ (custom "shajra" keymap)
: =================================================================
:
-: FLASH SOURCE: /nix/store/3lrbcyaa031p9xk2jasdiyngar5gc1lr-qmk-custom-shajra-src
-: FLASH BINARY: /nix/store/hpqsc7gqzq9l2vb7j7c2mjkf992646ca-moonlander-custom-shajra.bin
+: FLASH SOURCE: /nix/store/i9lfaqmm36mlqdbvn1wpnv95n5q931lg-qmk-custom-shajra-src
+: FLASH BINARY: /nix/store/alafhrwv03fp1lhlrzs5412ydxkigfm1-ergodoxez-custom-shajra.hex
:
: ⠋ Press the reset button of your keyboard.
-*** Flashing a Keyboardio Model 01 keyboard
+The same works for the other three flashing scripts. Just replace
+"flash-ergodoxez" in the invocation above with the script of your choice.
-You can run the following to flash your Keyboardio Model 01, holding down the
-=Prog= key and then pressing =Enter= when prompted:
+Alternatively, the four flashing scripts are provided at the root of this
+project. Just clone this repository, and you can call them directly:
#+begin_src sh :eval no
-./flash-model01
+cd $SOME_WORKING_DIR
+clone https://github.com/shajra/shajra-keyboards.git
+cd shajra-keyboards
+./flash-ergodoxez
#+end_src
-#+name: flash-model01
-#+begin_src bash :results output :exports results
-head -14 < <(./flash-model01)
-#+end_src
+Note, the first time you run the commands described below, you'll see Nix doing
+a lot of downloading and compiling. After that, subsequent invocations should be
+quicker with less output.
-#+results: flash-model01
-#+begin_example
+*** Flashing an Ergodox EZ keyboard
+
+When flashing with =flash-ergodoxez=, you will be prompted to press a reset
+button. Access this button with an unbent paperclip inserted into the small
+hole in the top right corner of the right keyboard half.
-Flashing Keyboardio's Model 01 (custom "shajra" keymap)
-=======================================================
+*** Flashing a Moonlander keyboard
-FLASH SOURCE: /nix/store/pzxrj5y7cg9kvc3hl7vlqhm86n5as471-model01-custom-shajra-src
-FLASH BINARY: /nix/store/ajdy8yy25zyywhb07na490n27ihjcjsi-model01-custom-shajra-hex
-DETECTED PORT: /dev/ttyACM0
+When flashing with =flash-ergodoxez=, you will be prompted to press a reset
+button. Access this button with an unbent paperclip inserted into the small
+hole in the top left corner of the left keyboard half.
-To flash your keyboard, you must hold down the 'Prog' key.
-While holding the 'Prog' key, press 'Enter', but continue to
-hold the 'Prog' key. You can release it once flashing has
-started, and the key glows red.
+*** Flashing a Keyboardio keyboard
-Do these steps now, or Ctrl-C to quit...
-#+end_example
+When flashing with =flash-model100= or =flash-model01=, you will be prompted
+with instructions to hold down the =Prog= key, and then press =Enter=.
The =Prog= key is hardwired to be the top-left-most key of the Keyboardio Model
01, but the =Enter= key can be remapped. If you forget where the =Enter= has
@@ -316,38 +291,45 @@ you'd normally use if following the QMK or Kaleidoscope documentation manually.
** Development
This project relies heavily on Nix, primarily to help deal with all the
-complexity of setting up dependencies.
-
-The [[file:doc/nix.org][provided documentation on Nix]] introduces Nix and how to use it in the
-context of this project.
+complexity of setting up dependencies. The development of this project also
+relies on an experimental feature of Nix called /flakes/, not required to use
+the project. See the included [[file:doc/nix-introduction.org][introduction to Nix]] if you're new to Nix or
+flakes.
If you want to check that everything builds before flashing your keyboard, you
can build locally everything built by this project's continuous integration:
#+name: nix-build
#+begin_src sh :results output :exports both
-nix build --no-link --file nix/ci.nix \
- && nix path-info --file nix/ci.nix
+tree $(nix build --no-link --print-out-paths) 2>/dev/null
#+end_src
#+results: nix-build
#+begin_example
-/nix/store/16l1vw1vpdqsysw7xlh48iqnbrvagsvs-ergodoxez-custom-shajra.hex
-/nix/store/1mz3bf1q871qwp0xsrip04fh0j5vr2i0-moonlander-custom-shajra-flash
-/nix/store/1ya4yka8hzjjya90fvfs67495bzhwbyp-moonlander-factory-flash
-/nix/store/2jyin75s0kziz5bm3fpzdm6fac060a7q-model01-factory-hex
-/nix/store/3l03cv7skh4f56axanxr4kdx374x7065-model01-factory-flash
-/nix/store/4sa2ghfdgmw2xpfz4qjx91zy2kpirf55-flash-model01
-/nix/store/8qw30diqjm41gkv7g693kb9jam88hac8-model01-custom-shajra-flash
-/nix/store/ajdy8yy25zyywhb07na490n27ihjcjsi-model01-custom-shajra-hex
-/nix/store/gyjgdy246a39gmf7bwxxj3c1c2f83rw8-ergodoxez-factory-flash
-/nix/store/hpqsc7gqzq9l2vb7j7c2mjkf992646ca-moonlander-custom-shajra.bin
-/nix/store/k2sxmw5zj4fwddydrvckyx2mrr8sp6nq-ergodoxez-factory.hex
-/nix/store/k8m4i8k5k7i1562ky3g0xhwhm2p9i7ka-ergodoxez-custom-shajra-flash
-/nix/store/kdkxmk0gdpf65n5yp1b8mvb4pvm01n6f-flash-moonlander
-/nix/store/pc4r14p779hhbc9zllmpmf0s8648as6m-moonlander-factory.bin
-/nix/store/q3f88df3iykql3zdykl15pgc8qwx6y8m-flash-ergodoxez
-/nix/store/vlsmhg30fh50pazvbcd2wy25lc8wija0-shajra-keyboards-licenses
+/nix/store/pkygbrzrl837872zn3w3q4q2wljpd5hv-shajra-keyboards-ci
+├── build-ergodoxez-custom-shajra-flash -> /nix/store/xb8w4zfk94k319q4nmh6s401m2kr3b01-ergodoxez-custom-shajra-flash
+├── build-ergodoxez-custom-shajra-hex -> /nix/store/alafhrwv03fp1lhlrzs5412ydxkigfm1-ergodoxez-custom-shajra.hex
+├── build-ergodoxez-factory-flash -> /nix/store/2nphw8ncjd34wb77zq1maip3xlkmj848-ergodoxez-factory-flash
+├── build-ergodoxez-factory-hex -> /nix/store/v8y8nbsc8f964bab7q4ars30avh80mvj-ergodoxez-factory.hex
+├── build-model01-custom-shajra-flash -> /nix/store/34494dhdq2gx4hi53jimvjmqljkyn50k-model01-custom-shajra-flash
+├── build-model01-custom-shajra-hex -> /nix/store/fnmriz6zfnsvmmn00rgv9cnrhlrrrlv1-model01-custom-shajra-hex
+├── build-model01-factory-flash -> /nix/store/r04a7n4kidafp1d1vyn8hihnnp7vx33n-model01-factory-flash
+├── build-model01-factory-hex -> /nix/store/hrvhyn8784zapapmp45dmd5k56fi4pax-model01-factory-hex
+├── build-model100-custom-shajra-flash -> /nix/store/sx7w3db5s7wg31zvhh1qkfqmh4qaz46l-model100-custom-shajra-flash
+├── build-model100-custom-shajra-hex -> /nix/store/h86pwv92wh6jbw5w96c9q3in1qnd27bs-model100-custom-shajra-hex
+├── build-model100-factory-flash -> /nix/store/kq7l0nyibhki8wg1pw2n504ng3pcx2na-model100-factory-flash
+├── build-model100-factory-hex -> /nix/store/9i22w0924cx1rm4fwxl88bwjrxnn8cv1-model100-factory-hex
+├── build-moonlander-custom-shajra-flash -> /nix/store/mrvs0s5ayi829n79pn9vfh9zyn3lzhqp-moonlander-custom-shajra-flash
+├── build-moonlander-custom-shajra-hex -> /nix/store/pmfj6pcyrrqmm75cr45rgak9yqjnc8nv-moonlander-custom-shajra.bin
+├── build-moonlander-factory-flash -> /nix/store/d92zdnx8l2mpx33klrvjnmdjs3lwylfi-moonlander-factory-flash
+├── build-moonlander-factory-hex -> /nix/store/1206ymbcancln1nqxd7ihv8hpn0ndf81-moonlander-factory.bin
+├── flash-ergodoxez -> /nix/store/5nqd92zrhra7rrbq9jxq83sy9hchp2hp-flash-ergodoxez
+├── flash-model01 -> /nix/store/3j29yy8h9ikvafii5y8iy225hg5f2sjn-flash-model01
+├── flash-model100 -> /nix/store/q19y0rapng0immn749hdk1i48dk4j3wi-flash-model100
+├── flash-moonlander -> /nix/store/r4pkms4xsn8snpcp2pp56p5vi3m4v6vm-flash-moonlander
+└── licenses -> /nix/store/6nryh742rmj5xpn8ikgwdlq5xsn9nkr0-shajra-keyboards-licenses
+
+18 directories, 4 files
#+end_example
* Release
diff --git a/config.nix b/config.nix
index b96cad6..7376034 100644
--- a/config.nix
+++ b/config.nix
@@ -3,6 +3,8 @@
default.ergodoxez.keymaps = keymaps/ergodox_ez;
default.model01.keymap = "shajra";
default.model01.keymaps = keymaps/model_01;
+ default.model100.keymap = "shajra";
+ default.model100.keymaps = keymaps/model_100;
default.moonlander.keymap = "shajra";
default.moonlander.keymaps = keymaps/moonlander;
}
diff --git a/default.nix b/default.nix
index d228cd3..407a66f 100644
--- a/default.nix
+++ b/default.nix
@@ -1 +1 @@
-(import ./nix {}).distribution
+(import nix/compat.nix).defaultNix
diff --git a/doc/design.md b/doc/design.md
index f0b1ad5..d2aa252 100644
--- a/doc/design.md
+++ b/doc/design.md
@@ -56,7 +56,7 @@ For reference, below are diagrams of the keymaps for the Model 01 and Ergodox EZ
## Model 01 "shajra" keymap
-![img](./model-01-shajra-layout.png)
+![img](./model-100-shajra-layout.png)
## Ergodox EZ "shajra" keymap
diff --git a/doc/design.org b/doc/design.org
index e63dcdb..7a7ab8d 100644
--- a/doc/design.org
+++ b/doc/design.org
@@ -1,5 +1,5 @@
#+title: Design of the "shajra" Keymap
-#+setupfile: links.org
+#+setupfile: internal/links.org
* Designing a keyboard mapping
@@ -57,7 +57,7 @@ EZ.
** Model 01 "shajra" keymap
-[[file:./model-01-shajra-layout.png]]
+[[file:./model-100-shajra-layout.png]]
** Ergodox EZ "shajra" keymap
diff --git a/doc/links.org b/doc/internal/links.org
similarity index 61%
rename from doc/links.org
rename to doc/internal/links.org
index 8a465e4..794cb88 100644
--- a/doc/links.org
+++ b/doc/internal/links.org
@@ -1,6 +1,8 @@
#+link: cachix https://cachix.org
+#+link: docstring https://en.wikipedia.org/wiki/Docstring
#+link: ergodox-ez https://ergodox-ez.com
-#+link: github-actions https://github.com/shajra/shajra-keyboards/actions
+#+link: fhs https://www.pathname.com/fhs/
+#+link: github-actions https://github.com/shajra/nix-project/actions
#+link: kaleidoscope https://github.com/keyboardio/Kaleidoscope
#+link: kaleidoscope-oneshot https://kaleidoscope.readthedocs.io/en/latest/plugins/OneShot.html
#+link: kaleidoscope-qukey https://kaleidoscope.readthedocs.io/en/latest/plugins/Qukeys.html
@@ -9,20 +11,23 @@
#+link: keyboardio https://shop.keyboard.io
#+link: key-compose https://en.wikipedia.org/wiki/Compose_key
#+link: moonlander https://www.zsa.io/moonlander/
-#+link: niv https://github.com/nmattia/niv
#+link: nix https://nixos.org/nix
-#+link: nix-darwin https://daiderd.com/nix-darwin
-#+link: nix-env https://nixos.org/nix/manual/#sec-nix-env
-#+link: nix-expr https://nixos.org/nix/manual/#ch-expression-language
-#+link: nix-install https://nixos.org/nix/manual/#chap-installation
+#+link: nix-flake-compat https://github.com/edolstra/flake-compat
+#+link: nix-install-manual https://nixos.org/manual/nix/stable/installation/installation.html
+#+link: nix-install-quick https://nixos.org/download.html#download-nix
+#+link: nix-language-manual https://nixos.org/manual/nix/stable/language/index.html
+#+link: nix-language-tutorial https://nixos.org/guides/nix-language.html
#+link: nix-learn https://nixos.org/learn.html
#+link: nixos https://nixos.org
+#+link: nixos-search https://search.nixos.org/packages
#+link: nixpkgs https://github.com/NixOS/nixpkgs
-#+link: nix-tutorial https://github.com/shajra/example-nix/tree/master/tutorials/0-nix-intro
+#+link: nixpkgs-manual https://nixos.org/nixpkgs/manual
+#+link: nix-uninstall https://nixos.org/manual/nix/stable/installation/installing-binary.html#uninstalling
#+link: qmk https://docs.qmk.fm
#+link: qmk-modtap https://docs.qmk.fm/#/mod_tap
#+link: qmk-oneshot https://docs.qmk.fm/#/one_shot_keys
#+link: qmk-setup https://docs.qmk.fm/#/flashing?id=halfkay
+#+link: repl https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop
#+link: signature_plastics https://pimpmykeyboard.com/key-cap-family-specs
#+link: teensy https://www.pjrc.com/teensy/loader_cli.html
#+link: vim https://www.vim.org
diff --git a/doc/internal/nix-introduction-compat.org b/doc/internal/nix-introduction-compat.org
new file mode 100644
index 0000000..a73fad4
--- /dev/null
+++ b/doc/internal/nix-introduction-compat.org
@@ -0,0 +1,63 @@
+#+begin_comment
+DO NOT READ. Some documentation for this project is generated from snippets of
+text like this one. Whether it gets woven into the final project is
+project-dependent.
+#+end_comment
+
+* Helping non-flakes users
+
+A few users make work in organizations or contribute to projects that disallow
+experimental features such as flakes.
+
+To buffer this compromise, this project uses and encourages the use of the
+[[nix-flake-compat][flake-compat]] project, which enables an end user who has opted not to enable
+flakes to at least access the flake's contents, packages or otherwise.
+
+With flake-compat, end users will have a normal (non-flake) Nix expression they
+can evaluate. However, since dependencies are managed with flakes, the project
+maintainer must have flakes enabled to manage dependencies (for example,
+updating to the latest dependencies with =nix flake update=).
+
+* Documenting an end user experience
+
+To deal with the transition of the Nix community to flake, this project provides
+two user guides:
+
+- [[file:nix-usage-flakes.org][Nix Usage with Flakes (Recommended) ]]
+- [[file:nix-usage-noflakes.org][Nix Usage without Flakes]]
+
+Links generally steer users to the recommended guide, which then links users to
+the non-flakes guide if they have the interest or need.
+
+The non-flakes guide intentionally avoids commands like =nix-shell= and
+=nix-channel=. These commands lead users to setting the =NIX_PATH= environment
+variable, which can lead to unreliable builds.
+
+Though this guide avoid the =flakes= experimental feature, it still invites end
+users to use the experimental =nix-command= to get the following subcommands:
+- =nix search=
+- =nix shell=
+- =nix run=
+
+In general, the non-flakes guide only explains usage of experimental =nix=
+subcommands when there exist no other alternatives, or when the alternatives are
+considered worse for new users.
+
+=nix search= simply has no good alternative within the set of non-experimental
+Nix tools, but it's too useful to not tell users about. Again, this is an
+example of the Nix community leading users to experimental features.
+
+=nix shell= and =nix run= are shown as improved alternatives to =nix-shell=.
+=nix-shell= is a complicated tool that has been historically used for a lot of
+different purposes:
+- debugging the build environments of packages
+- creating a developer environment for a package (=nix develop= does this
+ better, but for only for flakes)
+- entering a shell with Nix-build executables on the path (=nix shell= does this
+ better)
+- running arbitrary commands with Nix-build executables on the path (=nix run=
+ does this better)
+
+To cover all of these scenarios, =nix-shell= became so complex it is hard to
+explain to new users. =nix-shell= is really only best for debugging builds,
+which is beyond the scope of the documentation provided by this project.
diff --git a/doc/internal/nix-introduction-include.org b/doc/internal/nix-introduction-include.org
new file mode 100644
index 0000000..24fda53
--- /dev/null
+++ b/doc/internal/nix-introduction-include.org
@@ -0,0 +1 @@
+#+include: "nix-introduction-compat.org"
diff --git a/doc/internal/nix-usage-flakes-include.org b/doc/internal/nix-usage-flakes-include.org
new file mode 100644
index 0000000..d928db3
--- /dev/null
+++ b/doc/internal/nix-usage-flakes-include.org
@@ -0,0 +1 @@
+#+include: "nix-usage-flakes-optional.org"
diff --git a/doc/internal/nix-usage-flakes-optional.org b/doc/internal/nix-usage-flakes-optional.org
new file mode 100644
index 0000000..38d2a94
--- /dev/null
+++ b/doc/internal/nix-usage-flakes-optional.org
@@ -0,0 +1,16 @@
+#+begin_comment
+DO NOT READ. Some documentation for this project is generated from snippets of
+text like this one. Whether it gets woven into the final project is
+project-dependent.
+#+end_comment
+
+This project supports a still-experimental feature of Nix called /flakes/, which
+this guide shows users how to use. [[file:nix-usage-noflakes.org][Another guide]] explains how to do
+everything illustrated in this document, but without flakes.
+
+#+begin_quote
+*_NOTE:_* If you're new to flakes, please read the provided [[file:nix-introduction.org][supplemental
+introduction to Nix]] to understand the experimental nature of flakes and how it
+may or may not affect you. Hopefully you'll find these trade-offs acceptable so
+you can take advantage of the improved experience flakes offer.
+#+end_quote
diff --git a/doc/internal/nix-usage-flakes-required.org b/doc/internal/nix-usage-flakes-required.org
new file mode 100644
index 0000000..72ee6fb
--- /dev/null
+++ b/doc/internal/nix-usage-flakes-required.org
@@ -0,0 +1,9 @@
+#+begin_comment
+DO NOT READ. Some documentation for this project is generated from snippets of
+text like this one. Whether it gets woven into the final project is
+project-dependent.
+#+end_comment
+
+This project requires an experimental feature of Nix called /flakes/. To
+understand more about what flakes are and the consequences of using a
+still-experimental feature of Nix, please see the provided [[file:nix-introduction.org][introduction to Nix]].
diff --git a/doc/internal/params.el b/doc/internal/params.el
new file mode 100644
index 0000000..2e892a9
--- /dev/null
+++ b/doc/internal/params.el
@@ -0,0 +1,15 @@
+((package-attr-short . "licenses-thirdparty")
+ (package-attr-long . "packages.x86_64-linux.licenses-thirdparty")
+ (package-name . "shajra-keyboards-licenses")
+ (package-type . "executable")
+ (package-target-short . "shajra-keyboards-licenses")
+ (package-target-long . "bin/shajra-keyboards-licenses")
+ (run-attr-short . "licenses-thirdparty")
+ (run-attr-long . "packages.x86_64-linux.licenses-thirdparty")
+ ;; run-name must be the same as run-target-short
+ ;; run-type must be "executable"
+ (run-target-short . "shajra-keyboards-licenses")
+ ;; run-target-long will always prefix "bin/"
+ (nix-latest . "2.19")
+ (nixos-latest . "23.11")
+ (platforms . "Linux on x86-64 machines"))
diff --git a/doc/model-01-shajra-layout.png b/doc/model-100-shajra-layout.png
similarity index 100%
rename from doc/model-01-shajra-layout.png
rename to doc/model-100-shajra-layout.png
diff --git a/doc/nix-installation.md b/doc/nix-installation.md
new file mode 100644
index 0000000..1d49741
--- /dev/null
+++ b/doc/nix-installation.md
@@ -0,0 +1,114 @@
+- [About this document](#sec-1)
+- [Prerequisites](#sec-2)
+- [Level of commitment/risk](#sec-3)
+- [Nix package manager installation](#sec-4)
+- [Cache setup](#sec-5)
+- [Setting up experimental features](#sec-6)
+
+
+# About this document
+
+This document explains how to
+
+- install [the Nix package manager](https://nixos.org/nix)
+- set up Nix to download pre-built packages from a cache (optionally)
+- set up the Nix's experimental flakes feature (optionally)
+
+If you're unsure if you want to enable flakes or not, read the provided [introduction to Nix](nix-introduction.md).
+
+# Prerequisites
+
+This project supports Linux on x86-64 machines.
+
+All we need to use this project is to install Nix, which this document covers. Nix can be installed on a variety of Linux and Mac systems. Nix can also be installed in Windows via the Windows Subsystem for Linux (WSL). Installation on WSL may involve steps not covered in this documentation, though.
+
+Note, some users may be using [NixOS](https://nixos.org), a Linux operating system built on top of Nix. Those users already have Nix and don't need to install it separately. To use this project, you don't need to use NixOS as well.
+
+# Level of commitment/risk
+
+Unless you're on NixOS, you're likely already using another package manager for your operating system already (APT, DNF, etc.). You don't have to worry about Nix or packages installed by Nix conflicting with anything already on your system. Running Nix along side other package managers is safe.
+
+All the files of a Nix package are located under `/nix` a directory, well isolated from any other package manager. Nix won't touch critical directories under `/usr` or `/var`. Nix then symlinks files under `/nix` to your home directory under dot-files like `~/.nix-profile`. There is also some light configuration under `/etc/nix`.
+
+Hopefully this alleviates any worry about installing a complex program on your machine. Uninstallation is not too much more than deleting everything under `/nix`.
+
+# Nix package manager installation
+
+> **NOTE:** You don't need this step if you're running NixOS, which comes with Nix baked in.
+
+If you don't already have Nix, [the official installation script](https://nixos.org/download.html#download-nix) should work on a variety of UNIX-like operating systems. If you're okay with the script calling `sudo` you can install Nix on a non-WSL machine with the following recommended command:
+
+```bash
+sh <(curl -L https://nixos.org/nix/install) --daemon
+```
+
+The `--daemon` switch installs Nix in the multi-user mode, which is generally recommended (single-user installation with `--no-daemon` instead is recommended for WSL). The script reports everything it does and touches.
+
+After installation, you may have to exit your terminal session and log back in to have environment variables configured to put Nix executables on your `PATH`.
+
+The Nix manual describes [other methods of installing Nix](https://nixos.org/manual/nix/stable/installation/installation.html) that may suit you more. If you later want to uninstall Nix, see the [uninstallation steps documented in the Nix manual](https://nixos.org/manual/nix/stable/installation/installing-binary.html#uninstalling).
+
+# Cache setup
+
+This project pushes built Nix packages to [Cachix](https://cachix.org) as part of its [continuous integration](https://github.com/shajra/nix-project/actions). It's recommended to configure Nix to use shajra.cachix.org as a Nix *substituter*. Once configured, Nix will pull down pre-built packages from Cachix, instead of building them locally (potentially saving a lot of time). This augments Nix's default substituter that pulls from cache.nixos.org.
+
+You can configure shajra.cachix.org as a supplemental substituter with the following command:
+
+```sh
+nix run \
+ --file https://cachix.org/api/v1/install \
+ cachix \
+ --command cachix use shajra
+```
+
+Cachix is a service that anyone can use. You can call this command later to add substituters for someone else using Cachix, replacing “shajra” with their cache's name.
+
+If you've just run a multi-user Nix installation and are not yet a trusted user in `/etc/nix/nix.conf`, this command may not work. But it will report back some options to proceed.
+
+One option sets you up as a trusted user, and installs Cachix configuration for Nix locally at `~/.config/nix/nix.conf`. This configuration will be available immediately, and any subsequent invocation of Nix commands will take advantage of the Cachix cache.
+
+You can alternatively configure Cachix as a substituter globally by running the above command as a root user (say with `sudo`), which sets up Cachix directly in `/etc/nix/nix.conf`. The invocation may give further instructions upon completion.
+
+# Setting up experimental features
+
+This project can take advantage of two experimental Nix features:
+
+- `nix-command`
+- `flakes`
+
+The provided [introduction to Nix](nix-introduction.md) covers in detail what these features are and can help you decide whether you want to enable them.
+
+As you can guess, the `flakes` feature enables flakes functionality in Nix. The `nix-command` feature enables a variety of subcommands of Nix's newer `nix` command-line tool, some of which allow us to work with flakes.
+
+If you don't enable experimental features globally, there is a switch to enable features local to just a single command-line invocation. For example, too use flakes-related commands we call `nix --extra-experimental-features 'nix-command flakes' …`. For users not enabling these features globally, this can be useful to set to a shell alias. Here's an example that works in most POSIX-compliant shells:
+
+```sh
+alias nix-flakes = nix --extra-experimental-features 'nix-command flakes'
+```
+
+As discussed in the introduction, `nix-command` is actually enabled by default. You don't need to enable it explicitly (though you could disable it).
+
+To use flakes there are two things we need to do:
+
+1. make sure the version of Nix we're on is at least 2.4
+2. enable both the `nix-command` and `flakes` experimental features.
+
+Since the latest release of Nix is already at 2.19, if you installed Nix recently as per the instructions above, you should be on a recent-enough version:
+
+```sh
+nix --version
+```
+
+ nix (Nix) 2.18.1
+
+The easiest way to turn on experimental features is to create a file `~/.config/nix/nix.conf` if it doesn't already exist, and in it put the following line:
+
+```text
+experimental-features = nix-command flakes
+```
+
+Then you should see that the appropriate features are enabled:
+
+```sh
+nix show-config | grep experimental-features
+```
diff --git a/doc/nix-installation.org b/doc/nix-installation.org
new file mode 100644
index 0000000..9f29613
--- /dev/null
+++ b/doc/nix-installation.org
@@ -0,0 +1,187 @@
+#+title: Nix Installation
+#+setupfile: internal/links.org
+
+* Org-mode setup :noexport:
+
+This document is written in a project-agnostic way so it can be copied to other
+projects that use Nix.
+
+** Variables
+
+We set variables in =internal/params.el= and access those settings with the
+following macros and source code blocks (using Noweb).
+
+#+name: get
+#+begin_src emacs-lisp :var arg="" :eval yes :results silent
+(alist-get (intern arg) (car (read-from-string (f-read "internal/params.el"))))
+#+end_src
+
+#+macro: get (eval (concat $2 (alist-get (intern $1) (car (read-from-string (f-read "internal/params.el")))) $3))
+#+macro: nix-latest {{{get(nix-latest)}}}
+#+macro: nixos-latest {{{get(nixos-latest)}}}
+#+macro: nixos-branch {{{get(nixos-latest,=nixos-,=)}}}
+#+macro: platforms {{{get(platforms)}}}
+
+* About this document
+
+This document explains how to
+- install [[nix][the Nix package manager]]
+- set up Nix to download pre-built packages from a cache (optionally)
+- set up the Nix's experimental flakes feature (optionally)
+
+If you're unsure if you want to enable flakes or not, read the provided
+[[file:nix-introduction.org][introduction to Nix]].
+
+* Prerequisites
+
+This project supports {{{platforms}}}.
+
+All we need to use this project is to install Nix, which this document covers.
+Nix can be installed on a variety of Linux and Mac systems. Nix can also be
+installed in Windows via the Windows Subsystem for Linux (WSL). Installation on
+WSL may involve steps not covered in this documentation, though.
+
+Note, some users may be using [[nixos][NixOS]], a Linux operating system built on top of
+Nix. Those users already have Nix and don't need to install it separately. To
+use this project, you don't need to use NixOS as well.
+
+* Level of commitment/risk
+
+Unless you're on NixOS, you're likely already using another package manager for
+your operating system already (APT, DNF, etc.). You don't have to worry about
+Nix or packages installed by Nix conflicting with anything already on your
+system. Running Nix along side other package managers is safe.
+
+All the files of a Nix package are located under =/nix= a directory, well
+isolated from any other package manager. Nix won't touch critical directories
+under =/usr= or =/var=. Nix then symlinks files under =/nix= to your home
+directory under dot-files like =~/.nix-profile=. There is also some light
+configuration under =/etc/nix=.
+
+Hopefully this alleviates any worry about installing a complex program on your
+machine. Uninstallation is not too much more than deleting everything under
+=/nix=.
+
+* Nix package manager installation
+
+#+begin_quote
+*_NOTE:_* You don't need this step if you're running NixOS, which comes with Nix
+baked in.
+#+end_quote
+
+If you don't already have Nix, [[nix-install-quick][the official installation script]] should work on a
+variety of UNIX-like operating systems. If you're okay with the script calling
+=sudo= you can install Nix on a non-WSL machine with the following recommended
+command:
+
+#+begin_src bash :eval no
+sh <(curl -L https://nixos.org/nix/install) --daemon
+#+end_src
+
+The =--daemon= switch installs Nix in the multi-user mode, which is generally
+recommended (single-user installation with =--no-daemon= instead is recommended
+for WSL). The script reports everything it does and touches.
+
+After installation, you may have to exit your terminal session and log back in
+to have environment variables configured to put Nix executables on your =PATH=.
+
+The Nix manual describes [[nix-install-manual][other methods of installing Nix]] that may suit you more.
+If you later want to uninstall Nix, see the [[nix-uninstall][uninstallation steps documented in
+the Nix manual]].
+
+* Cache setup
+
+This project pushes built Nix packages to [[cachix][Cachix]] as part of its [[github-actions][continuous
+integration]]. It's recommended to configure Nix to use shajra.cachix.org as a
+Nix /substituter/. Once configured, Nix will pull down pre-built packages from
+Cachix, instead of building them locally (potentially saving a lot of time).
+This augments Nix's default substituter that pulls from cache.nixos.org.
+
+You can configure shajra.cachix.org as a supplemental substituter with the
+following command:
+
+#+begin_src sh :eval no
+nix run \
+ --file https://cachix.org/api/v1/install \
+ cachix \
+ --command cachix use shajra
+#+end_src
+
+Cachix is a service that anyone can use. You can call this command later to add
+substituters for someone else using Cachix, replacing “shajra” with their
+cache's name.
+
+If you've just run a multi-user Nix installation and are not yet a trusted user
+in =/etc/nix/nix.conf=, this command may not work. But it will report back some
+options to proceed.
+
+One option sets you up as a trusted user, and installs Cachix configuration for
+Nix locally at =~/.config/nix/nix.conf=. This configuration will be available
+immediately, and any subsequent invocation of Nix commands will take advantage
+of the Cachix cache.
+
+You can alternatively configure Cachix as a substituter globally by running the
+above command as a root user (say with =sudo=), which sets up Cachix directly in
+=/etc/nix/nix.conf=. The invocation may give further instructions upon
+completion.
+
+* Setting up experimental features
+
+This project can take advantage of two experimental Nix features:
+- =nix-command=
+- =flakes=
+
+The provided [[file:nix-introduction.org][introduction to Nix]] covers in detail what these features are and
+can help you decide whether you want to enable them.
+
+As you can guess, the =flakes= feature enables flakes functionality in Nix. The
+=nix-command= feature enables a variety of subcommands of Nix's newer =nix=
+command-line tool, some of which allow us to work with flakes.
+
+If you don't enable experimental features globally, there is a switch to enable
+features local to just a single command-line invocation. For example, too use
+flakes-related commands we call ~nix --extra-experimental-features 'nix-command
+flakes' …~. For users not enabling these features globally, this can be useful
+to set to a shell alias. Here's an example that works in most POSIX-compliant
+shells:
+
+#+begin_src sh :eval no
+alias nix-flakes = nix --extra-experimental-features 'nix-command flakes'
+#+end_src
+
+As discussed in the introduction, =nix-command= is actually enabled by default.
+You don't need to enable it explicitly (though you could disable it).
+
+To use flakes there are two things we need to do:
+1. make sure the version of Nix we're on is at least 2.4
+2. enable both the =nix-command= and =flakes= experimental features.
+
+Since the latest release of Nix is already at {{{nix-latest}}}, if you installed
+Nix recently as per the instructions above, you should be on a recent-enough
+version:
+
+#+name: nix-version
+#+begin_src sh :results output :exports both
+nix --version
+#+end_src
+
+#+RESULTS: nix-version
+: nix (Nix) 2.18.1
+
+The easiest way to turn on experimental features is to create a file
+=~/.config/nix/nix.conf= if it doesn't already exist, and in it put the
+following line:
+
+#+begin_src text :eval no
+experimental-features = nix-command flakes
+#+end_src
+
+Then you should see that the appropriate features are enabled:
+
+#+name: nix-show-config
+#+begin_src sh :results output :export both
+nix show-config | grep experimental-features
+#+end_src
+
+#+RESULTS: nix-show-config
+: experimental-features = flakes nix-command
diff --git a/doc/nix-introduction.md b/doc/nix-introduction.md
new file mode 100644
index 0000000..01ef97c
--- /dev/null
+++ b/doc/nix-introduction.md
@@ -0,0 +1,247 @@
+- [About this document](#sec-1)
+- [Problems addressed by Nix](#sec-2)
+ - [Managed build](#sec-2-1)
+ - [Reliable build](#sec-2-2)
+ - [Reliable deployment](#sec-2-3)
+ - [Version conflicts](#sec-2-4)
+ - [Polyglot programming](#sec-2-5)
+ - [Complete distributed cache of builds](#sec-2-6)
+- [Nix at a high level](#sec-3)
+ - [Nix the package mangager](#sec-3-1)
+ - [Nix the build system](#sec-3-2)
+ - [Nixpkgs](#sec-3-3)
+- [Frustrations acknowledged](#sec-4)
+ - [Nixpkgs takes time to learn](#sec-4-1)
+ - [Confusion of stability](#sec-4-2)
+ - [Nix 2.0 and the new `nix` command](#sec-4-2-1)
+ - [Flakes as an experiment](#sec-4-2-2)
+- [Encouraging development with flakes](#sec-5)
+- [Helping non-flakes users](#sec-6)
+- [Documenting an end user experience](#sec-7)
+
+
+# About this document
+
+This document introduces the [Nix package manager](https://nixos.org/nix) and highlights some motivations to use Nix. It also covers tradeoffs not only of using Nix, but experimental features in Nix such as one called *flakes*.
+
+This document tries to capture enthusiasm while being honest about frustrations. Nix is amazing, and a clear pioneer of an architectural approach that users will come to demand in the future. However, users need clear information up front where they are likely to face challenges.
+
+# Problems addressed by Nix
+
+The following sections cover various problems that Nix's architecture addresses.
+
+## Managed build
+
+When dealing with a new software project, wrangling dependencies can be a chore. Modern build systems for specific programming languages often don't manage system dependencies. For example, Python's `pip install` will download and install needed Python dependencies, but may fail if the system doesn't provide C shared libraries needed for foreign function calls. Complicating matters, different operating systems have different names for these system packages and install them with different commands (`apt`, `dnf`, etc.). This makes automation difficult. Consequently, many software projects only provide documentation as a surrogate for automation, which creates even more room for error.
+
+## Reliable build
+
+Some projects might have all the automation necessary for building, but due to subtle differences among systems, what builds on one system might not build on another.
+
+For example, environment variables often can influence the behavior of the commands called by scripts. It's hard to lock down these variables on every system where something might be built.
+
+## Reliable deployment
+
+Once we've built some software and are ready to deploy it, it's not always obvious how to copy this built software to another system. For example, if the software dynamically links to system libraries, we need to know whether those libraries are on the system we intend to copy to.
+
+## Version conflicts
+
+Another complication we face is when an operating system only allows one version of a system library to be installed at a time. When this happens, we have to make difficult choices if we need two programs that require different versions of a system dependency.
+
+## Polyglot programming
+
+It's also tedious to synthesize libraries and programs from different language ecosystems to make a new program for a unified user experience. For example, the world of machine learning programming often requires the mixing C/C++, Python, and even basic shell scripts. These hybrid applications have a tendency to be fragile.
+
+## Complete distributed cache of builds
+
+Various build systems provide repositories for pre-built packages, which helps users save time by downloading packages instead of building them. What we really want is this experience, but unified across all programming language ecosystems and system dependencies.
+
+Note, this is what traditional package managers like DNF and APT accomplish. But there's an ergonomic difficulty to turning all software into standard Linux packages. To start, there are too many Linux distributions with too many package managers. Secondly, most of the package managers require adherence to a set of policies for everything to work well together. For example, many distributions respect the [Filesystem Hierarchy Standard (FHS)](https://www.pathname.com/fhs/). Confusion around policies have led many developers to steer away from package managers and towards container-based technologies like Docker, despite the overhead and drawbacks of containers.
+
+# Nix at a high level
+
+Nix addresses all the problems discussed above.
+
+To build or install any project, we should be able to start with only the Nix package manager installed. No other library or system dependency should be required to be installed or configured.
+
+Even if we have a library or system dependency installed, it shouldn't interfere with any build or installation we want to do.
+
+Our build should get everything we need, all the way down to system-level dependencies, irrespective of which programming language the dependencies has been authored in. If anything has been pre-built, we should download a cached result.
+
+Above and beyond the problems discussed above, Nix has a precisely deterministic build, broadly guaranteeing reproducibility. If the package builds on one system, it should build on all systems, irrespective of what's installed or not. Furthermore, multiple systems building the same package independently will often produce bit-for-bit identical builds.
+
+Nix also is able to conveniently copy the transitive closure of a package all its dependencies ergonomically from one system to another.
+
+In broad strokes, Nix is a technology that falls into two categories:
+
+- package manager
+- build tool.
+
+## Nix the package mangager
+
+As a package manager, Nix does what most package managers do. Nix provides a suite of command-line tools to search registries of known packages, as well as install and uninstall them.
+
+Packages can provide both executables and plain files alike. Installation just entails putting these files into a good location for both the package manager and the user. Nix has an elegant way of storing everything under `/nix/store`, discussed more below.
+
+Importantly, the Nix package manager doesn't differentiate between system-level installations and user-level installations. All builds and installations are by nature hermetic and can't conflict with one another.
+
+As a convenience, Nix has tools to help users put the executables provided by packages provided on their environment's `PATH`. This way, users don't have to deal with finding executables to call installed in `/nix/store`.
+
+## Nix the build system
+
+Nix conjoins the features of a package manager with those of a build tool. If a package or any of its dependencies (including low-level system dependencies) aren't found in a *Nix substituter*, they are built locally. Otherwise, the pre-built package and dependencies cached in the Nix substituter are downloaded rather than built. All we need to build or download any package is the Nix package manager and a network connection.
+
+Every Nix package is specified by a *Nix expression*, written in a small programming language also called Nix. This expression specifies everything needed to build the package down to the system-level. These expressions are saved in files with a ".nix" extension.
+
+Some software provides these Nix expressions alongside as part of their source. If some software doesn't provide a Nix expression, you can always use an externally authored expression.
+
+What makes Nix special is that these expressions specify a way to build that's
+
+- precise
+- repeatable
+- guaranteed not to conflict with anything already installed
+
+For some, it's easy to miss the degree to which Nix-built packages are precise and repeatable. If you build a package from a Nix expression on one system, and then build the same expression on a system of the same architecture, you should get the same result. In most cases, the built artifacts will be identical bit-for-bit.
+
+This degree of precision is accomplished by a system of thorough hashing. In Nix, the dependencies needed to build packages are also themselves Nix packages. Every Nix expression has an associated hash that is calculated from the hashes of package's dependencies and build instructions. When we change this dependency (even if only by a single bit), the hash for the Nix expression changes. This cascades to a different calculated hash for any package relying on this dependency. But if nothing changes, the same hashes will be calculated on all systems.
+
+The repeatability and precision of Nix forms the basis of how substituters are trusted as caching services across the world. It also allows us to trust remote builds more easily, without worrying about deviations of environment configuration.
+
+Nix has central a substituter at , but there are third-party ones as well, like [Cachix](https://cachix.org). Before building a package, the hash for the package is calculated. If any configured substituter has a build for the hash, it's pulled down as a substitute. A certificate-based protocol is used to establish trust of substituters. Between this protocol, and the algorithm for calculating hashes in Nix, you can have confidence that a package pulled from a substituter will be identical to what you would have built locally.
+
+Finally, all packages are stored in `/nix/store` by their hash. This simple scheme allows us to have multiple versions of the same package installed with no conflicts. References to dependencies all point back to the desired version in `/nix/store` they need. This is not to say that running multiple programs concurrently based on different versions can't cause problems, but at least the flexibility to do so is in the user's hands.
+
+## Nixpkgs
+
+Nix expressions help us create extremely controlled environments within which we can build packages precisely. However, Nix still calls the conventional build tools of various programming language ecosystems. Under the cover, Nix is ultimately an precisely controlled execution of Bash scripts orchestrating these tools.
+
+To keep the Nix expressions for each package concise, the Nix community curates a [Git repository of Nix expressions called Nixpkgs](https://github.com/NixOS/nixpkgs). Most Nix expressions for packages will start with a snapshot of Nixpkgs as a dependency, which provides library support to help keep Nix expressions compact.
+
+This way, the complexity of shell scripting and calls to language-specific tooling can be kept largely hidden away from general Nix authors. The Nix language lets us work with packages from any language ecosystem in a uniform way.
+
+# Frustrations acknowledged
+
+Having covered so many of Nix's strengths, it's important to be aware of some problems the Nix community is still working through.
+
+## Nixpkgs takes time to learn
+
+There are parts of Nix that are notably simple. For example, there's an elegance to the hashing calculation and how `/nix/store` is used. Furthermore the Nix language itself has a small footprint, which eases learning it.
+
+However, because of the complexity of all the programming language ecosystems out there, there are a *lot* of supporting libraries in Nixpkgs to understand. There's over two million lines of Nix in Nixpkgs, some auto-generated, increasing the odds of getting lost in it.
+
+The [official Nixpkgs manual](https://nixos.org/nixpkgs/manual) only seems to cover a fraction of what package authors really need to know. Invariably, people seem to master Nix by exploring the source code of Nixpkgs, supplemented by example projects for reference. You can get surprisingly far mimicking code you find in Nixpkgs that packages something similar to what you have in front of you. But understanding what's actually going on so you avoid simple mistakes can take some time.
+
+Various people have attempted to fill the gap with documentation and tutorials. Even this document you're reading now is one such attempt. However, we're missing searchable index of all the critical functions in Nixpkgs for people to explore. Something as simple as parsed [docstrings](https://en.wikipedia.org/wiki/Docstring) as an extension of the Nix language would go a long way, which would be far easier to implement than something more involved like a type system for the Nix language.
+
+## Confusion of stability
+
+The Nix community seems divided into the following camps:
+
+- those who want new features and fixes to known grievances
+- those who want stable systems based on Nix in industrial settings.
+
+It's not necessary for these groups to be at odds. Unfortunately, Nix has released new experimental features in a way that has created confusion of how to build stable systems with Nix.
+
+### Nix 2.0 and the new `nix` command
+
+An early complaint of Nix was the non-intuitiveness of Nix's original assortment of command-line tools. To address this, Nix 2.0 introduced a unifying tool called `nix`. Despite appreciable improvements in user experience, the newer `nix` command has taken some time for it to get enough functionality to actually replace the older tools (`nix-build`, `nix-shell`, `nix-store`, etc.). For a while, it's ended up yet another tool to learn.
+
+If you look at the manpage for `nix` there's a clear warning at the top:
+
+> Warning: This program is experimental and its interface is subject to change.
+
+This warning has been there since 2018 when Nix 2.0 released.
+
+However, `nix repl` is the only way to get to a [REPL session](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop) in Nix, which is an important tool for any programming language. The previous tool providing a REPL (`nix-repl`) has been removed from Nixpkgs.
+
+This means that technically, the community is strongly encouraging, if not forcing, users to use an experimental tool and providing little guidance on how to use Nix with some assurance of stability. This is important for industrial users who script solutions against the `nix` command-line tools.
+
+Eventually, with the release of Nix 2.4, experimental features were turned into flags that needed to be explicitly enabled by users. One of these flags was `nix-command`, which now gates users from any subcommand of `nix` beyond `nix repl`. However, because so many users were already using the new `nix` command, the experimental `nix-command` feature is enabled by default if no experimental features have been configured explicitly.
+
+In other words, Nix ships with an experimental feature enabled by default.
+
+This almost indicates that the new `nix` command isn't too unstable. Except, Nix 2.4 did indeed change the API of `nix` subcommands.
+
+In practice, the `nix` subcommands are relatively reliable. They are well-written and functionally robust. But the core maintainers are reserving the right to change input parameterization and output formatting.
+
+They communicate this risk only with the warning atop the manpage, which most users have been training one another to ignore.
+
+### Flakes as an experiment
+
+Though Nix expressions have an incredible potential to be precise and reproducible, there has always been some backdoors to break the reliability of builds. For example, Nix expressions have the potential to evaluate differently depending on the setting of some environment variables like `NIX_PATH`.
+
+The motivation for these relaxations of determinism has been to have a quick way to let personal computing users have a convenient way to manage their environments. Some people are careful to avoid accidentally having non-deterministic builds. Still, accidents have occurred frequently enough for the community to want better. It's frustrating to have a broken build because someone else set an environment variable incorrectly.
+
+Nix 2.4 corrected for this by introducing an experimental feature called *flakes*. Users still have a largely ergonomic way to manage their environments, but builds are more strictly deterministic. Determinism is a large reason many turn to Nix in the first place. A nice benefit of strictly enforced determinism is the ability to cache evaluations of Nix expressions, which can be expensive to compute.
+
+All this is generally good news. Flakes address problems that industrial users of Nix have long had to deal with.
+
+However, flakes are an experimental feature that users need to explicitly enable. Similar to the `nix` command, the inputs and outputs of flake-related subcommands might change slightly. Such changes have already happened.
+
+On top of this, because flakes are experimental, documentation of flakes is fractured in the official documentation. It almost seems like the Nix developers are delaying proper documentation until there's a declaration of stability. A preferred alternative would be developing documentation more concurrently with the implementation, using the comprehensibility of the documentation to inform the design of the software. Features that are too hard to explain expose good opportunities for redesign.
+
+All this puts industrial Nix users in an annoying place. Not using flakes and instead coaching coworkers and customers on how to use Nix safely
+
+- increases the likelihood of defects as people make honest mistakes
+- reduces the likelihood of adoption, because people get frustrated with poor ergonomics and difficulty understanding nuances and corner cases.
+
+However, if industrial users move to flakes to address these problems we have the following problems:
+
+- we have to be ready for the flakes API to change as it's technically experimental
+- we have to accept some added training hurdles since documentation of flakes is tucked behind documentation of non-flakes usage.
+
+# Encouraging development with flakes
+
+This project encourages the development of Nix projects using flakes. The benefits seem to outweigh the risks of instability. This is not a choice made lightly, and this document is an exercise of due diligence to inform users of compromises.
+
+Flakes are absolutely the future in Nix. They significantly address prior pains. Furthermore, enough people across the world are using them that we have some confidence that the Nix commands are reliable as implemented. The core contributors have just been very slow to commit to the user interfaces and experience.
+
+There might be documentation and training hurdles with flakes, but it's not actually much better not using flakes. This is why this project includes documentation and guides on Nix itself.
+
+It's also important to keep the risks of using experimental features in perspective. Industrial users are more likely to script heavily against `nix` commands than personal users. Upgrading anything risks small breaks to address. For some industrial users, such breaks are insufferable in aggregate, even if manageable individually. This leads to a desire to only use software that has been officially released as stable and supported. Still, if you've read documents like this one, and feel the history and risks have been well explained, using flakes might be the best option, even with industrial scripting that might break.
+
+Usage of flakes outside scripting has almost no risk at all. By calling `nix` with a few extra arguments `--extra-experimental-features 'nix-command flakes'` we can access flakes commands for single invocations, without needing to enable flakes globally. You can even make an alias for your shell that might look like the following:
+
+```sh
+alias nix-flakes = nix --extra-experimental-features 'nix-command flakes'
+```
+
+This way there's less to type interactively. Just don't script against this command, and there's no worry of scripts breaking if the flakes API changes.
+
+# Helping non-flakes users
+
+A few users make work in organizations or contribute to projects that disallow experimental features such as flakes.
+
+To buffer this compromise, this project uses and encourages the use of the [flake-compat](https://github.com/edolstra/flake-compat) project, which enables an end user who has opted not to enable flakes to at least access the flake's contents, packages or otherwise.
+
+With flake-compat, end users will have a normal (non-flake) Nix expression they can evaluate. However, since dependencies are managed with flakes, the project maintainer must have flakes enabled to manage dependencies (for example, updating to the latest dependencies with `nix flake update`).
+
+# Documenting an end user experience
+
+To deal with the transition of the Nix community to flake, this project provides two user guides:
+
+- [Nix Usage with Flakes (Recommended) ](nix-usage-flakes.md)
+- [Nix Usage without Flakes](nix-usage-noflakes.md)
+
+Links generally steer users to the recommended guide, which then links users to the non-flakes guide if they have the interest or need.
+
+The non-flakes guide intentionally avoids commands like `nix-shell` and `nix-channel`. These commands lead users to setting the `NIX_PATH` environment variable, which can lead to unreliable builds.
+
+Though this guide avoid the `flakes` experimental feature, it still invites end users to use the experimental `nix-command` to get the following subcommands:
+
+- `nix search`
+- `nix shell`
+- `nix run`
+
+In general, the non-flakes guide only explains usage of experimental `nix` subcommands when there exist no other alternatives, or when the alternatives are considered worse for new users.
+
+`nix search` simply has no good alternative within the set of non-experimental Nix tools, but it's too useful to not tell users about. Again, this is an example of the Nix community leading users to experimental features.
+
+`nix shell` and `nix run` are shown as improved alternatives to `nix-shell`. `nix-shell` is a complicated tool that has been historically used for a lot of different purposes:
+
+- debugging the build environments of packages
+- creating a developer environment for a package (`nix develop` does this better, but for only for flakes)
+- entering a shell with Nix-build executables on the path (`nix shell` does this better)
+- running arbitrary commands with Nix-build executables on the path (`nix run` does this better)
+
+To cover all of these scenarios, `nix-shell` became so complex it is hard to explain to new users. `nix-shell` is really only best for debugging builds, which is beyond the scope of the documentation provided by this project.
diff --git a/doc/nix-introduction.org b/doc/nix-introduction.org
new file mode 100644
index 0000000..71d193e
--- /dev/null
+++ b/doc/nix-introduction.org
@@ -0,0 +1,383 @@
+#+title: Deciding to use Nix
+#+setupfile: internal/links.org
+
+* About this document
+
+This document introduces the [[nix][Nix package manager]] and highlights some motivations
+to use Nix. It also covers tradeoffs not only of using Nix, but experimental
+features in Nix such as one called /flakes/.
+
+This document tries to capture enthusiasm while being honest about frustrations.
+Nix is amazing, and a clear pioneer of an architectural approach that users will
+come to demand in the future. However, users need clear information up front
+where they are likely to face challenges.
+
+* Problems addressed by Nix
+
+The following sections cover various problems that Nix's architecture addresses.
+
+** Managed build
+
+When dealing with a new software project, wrangling dependencies can be a chore.
+Modern build systems for specific programming languages often don't manage
+system dependencies. For example, Python's =pip install= will download and
+install needed Python dependencies, but may fail if the system doesn't provide C
+shared libraries needed for foreign function calls. Complicating matters,
+different operating systems have different names for these system packages and
+install them with different commands (=apt=, =dnf=, etc.). This makes automation
+difficult. Consequently, many software projects only provide documentation as a
+surrogate for automation, which creates even more room for error.
+
+** Reliable build
+
+Some projects might have all the automation necessary for building, but due to
+subtle differences among systems, what builds on one system might not build on
+another.
+
+For example, environment variables often can influence the behavior of the
+commands called by scripts. It's hard to lock down these variables on every
+system where something might be built.
+
+** Reliable deployment
+
+Once we've built some software and are ready to deploy it, it's not always
+obvious how to copy this built software to another system. For example, if the
+software dynamically links to system libraries, we need to know whether those
+libraries are on the system we intend to copy to.
+
+** Version conflicts
+
+Another complication we face is when an operating system only allows one version
+of a system library to be installed at a time. When this happens, we have to
+make difficult choices if we need two programs that require different versions
+of a system dependency.
+
+** Polyglot programming
+
+It's also tedious to synthesize libraries and programs from different language
+ecosystems to make a new program for a unified user experience. For example, the
+world of machine learning programming often requires the mixing C/C++, Python,
+and even basic shell scripts. These hybrid applications have a tendency to be
+fragile.
+
+** Complete distributed cache of builds
+
+Various build systems provide repositories for pre-built packages, which helps
+users save time by downloading packages instead of building them. What we really
+want is this experience, but unified across all programming language ecosystems
+and system dependencies.
+
+Note, this is what traditional package managers like DNF and APT accomplish. But
+there's an ergonomic difficulty to turning all software into standard Linux
+packages. To start, there are too many Linux distributions with too many package
+managers. Secondly, most of the package managers require adherence to a set of
+policies for everything to work well together. For example, many distributions
+respect the [[fhs][Filesystem Hierarchy Standard (FHS)]]. Confusion around policies have
+led many developers to steer away from package managers and towards
+container-based technologies like Docker, despite the overhead and drawbacks of
+containers.
+
+* Nix at a high level
+
+Nix addresses all the problems discussed above.
+
+To build or install any project, we should be able to start with only the Nix
+package manager installed. No other library or system dependency should be
+required to be installed or configured.
+
+Even if we have a library or system dependency installed, it shouldn't interfere
+with any build or installation we want to do.
+
+Our build should get everything we need, all the way down to system-level
+dependencies, irrespective of which programming language the dependencies has
+been authored in. If anything has been pre-built, we should download a cached
+result.
+
+Above and beyond the problems discussed above, Nix has a precisely deterministic
+build, broadly guaranteeing reproducibility. If the package builds on one
+system, it should build on all systems, irrespective of what's installed or not.
+Furthermore, multiple systems building the same package independently will often
+produce bit-for-bit identical builds.
+
+Nix also is able to conveniently copy the transitive closure of a package all
+its dependencies ergonomically from one system to another.
+
+In broad strokes, Nix is a technology that falls into two categories:
+
+- package manager
+- build tool.
+
+** Nix the package mangager
+
+As a package manager, Nix does what most package managers do. Nix provides a
+suite of command-line tools to search registries of known packages, as well as
+install and uninstall them.
+
+Packages can provide both executables and plain files alike. Installation just
+entails putting these files into a good location for both the package manager
+and the user. Nix has an elegant way of storing everything under =/nix/store=,
+discussed more below.
+
+Importantly, the Nix package manager doesn't differentiate between system-level
+installations and user-level installations. All builds and installations are by
+nature hermetic and can't conflict with one another.
+
+As a convenience, Nix has tools to help users put the executables provided by
+packages provided on their environment's =PATH=. This way, users don't have to
+deal with finding executables to call installed in =/nix/store=.
+
+** Nix the build system
+
+Nix conjoins the features of a package manager with those of a build tool. If a
+package or any of its dependencies (including low-level system dependencies)
+aren't found in a /Nix substituter/, they are built locally. Otherwise, the
+pre-built package and dependencies cached in the Nix substituter are downloaded
+rather than built. All we need to build or download any package is the Nix
+package manager and a network connection.
+
+Every Nix package is specified by a /Nix expression/, written in a small
+programming language also called Nix. This expression specifies everything
+needed to build the package down to the system-level. These expressions are
+saved in files with a ".nix" extension.
+
+Some software provides these Nix expressions alongside as part of their source.
+If some software doesn't provide a Nix expression, you can always use an
+externally authored expression.
+
+What makes Nix special is that these expressions specify a way to build that's
+
+- precise
+- repeatable
+- guaranteed not to conflict with anything already installed
+
+For some, it's easy to miss the degree to which Nix-built packages are precise
+and repeatable. If you build a package from a Nix expression on one system, and
+then build the same expression on a system of the same architecture, you should
+get the same result. In most cases, the built artifacts will be identical
+bit-for-bit.
+
+This degree of precision is accomplished by a system of thorough hashing. In
+Nix, the dependencies needed to build packages are also themselves Nix packages.
+Every Nix expression has an associated hash that is calculated from the hashes
+of package's dependencies and build instructions. When we change this dependency
+(even if only by a single bit), the hash for the Nix expression changes. This
+cascades to a different calculated hash for any package relying on this
+dependency. But if nothing changes, the same hashes will be calculated on all
+systems.
+
+The repeatability and precision of Nix forms the basis of how substituters are
+trusted as caching services across the world. It also allows us to trust remote
+builds more easily, without worrying about deviations of environment
+configuration.
+
+Nix has central a substituter at https://cache.nixos.org, but there are
+third-party ones as well, like [[cachix][Cachix]]. Before building a package, the hash for
+the package is calculated. If any configured substituter has a build for the
+hash, it's pulled down as a substitute. A certificate-based protocol is used to
+establish trust of substituters. Between this protocol, and the algorithm for
+calculating hashes in Nix, you can have confidence that a package pulled from a
+substituter will be identical to what you would have built locally.
+
+Finally, all packages are stored in =/nix/store= by their hash. This simple
+scheme allows us to have multiple versions of the same package installed with no
+conflicts. References to dependencies all point back to the desired version in
+=/nix/store= they need. This is not to say that running multiple programs
+concurrently based on different versions can't cause problems, but at least the
+flexibility to do so is in the user's hands.
+
+** Nixpkgs
+
+Nix expressions help us create extremely controlled environments within which we
+can build packages precisely. However, Nix still calls the conventional build
+tools of various programming language ecosystems. Under the cover, Nix is
+ultimately an precisely controlled execution of Bash scripts orchestrating these
+tools.
+
+To keep the Nix expressions for each package concise, the Nix community curates
+a [[nixpkgs][Git repository of Nix expressions called Nixpkgs]]. Most Nix expressions for
+packages will start with a snapshot of Nixpkgs as a dependency, which provides
+library support to help keep Nix expressions compact.
+
+This way, the complexity of shell scripting and calls to language-specific
+tooling can be kept largely hidden away from general Nix authors. The Nix
+language lets us work with packages from any language ecosystem in a uniform
+way.
+
+* Frustrations acknowledged
+
+Having covered so many of Nix's strengths, it's important to be aware of some
+problems the Nix community is still working through.
+
+** Nixpkgs takes time to learn
+
+There are parts of Nix that are notably simple. For example, there's an elegance
+to the hashing calculation and how =/nix/store= is used. Furthermore the Nix
+language itself has a small footprint, which eases learning it.
+
+However, because of the complexity of all the programming language ecosystems
+out there, there are a /lot/ of supporting libraries in Nixpkgs to understand.
+There's over two million lines of Nix in Nixpkgs, some auto-generated,
+increasing the odds of getting lost in it.
+
+The [[nixpkgs-manual][official Nixpkgs manual]] only seems to cover a fraction of what package
+authors really need to know. Invariably, people seem to master Nix by exploring
+the source code of Nixpkgs, supplemented by example projects for reference. You
+can get surprisingly far mimicking code you find in Nixpkgs that packages
+something similar to what you have in front of you. But understanding what's
+actually going on so you avoid simple mistakes can take some time.
+
+Various people have attempted to fill the gap with documentation and tutorials.
+Even this document you're reading now is one such attempt. However, we're
+missing searchable index of all the critical functions in Nixpkgs for people to
+explore. Something as simple as parsed [[docstring][docstrings]] as an extension of the Nix
+language would go a long way, which would be far easier to implement than
+something more involved like a type system for the Nix language.
+
+** Confusion of stability
+
+The Nix community seems divided into the following camps:
+
+- those who want new features and fixes to known grievances
+- those who want stable systems based on Nix in industrial settings.
+
+It's not necessary for these groups to be at odds. Unfortunately, Nix has
+released new experimental features in a way that has created confusion of how to
+build stable systems with Nix.
+
+*** Nix 2.0 and the new =nix= command
+
+An early complaint of Nix was the non-intuitiveness of Nix's original assortment
+of command-line tools. To address this, Nix 2.0 introduced a unifying tool
+called =nix=. Despite appreciable improvements in user experience, the newer
+=nix= command has taken some time for it to get enough functionality to actually
+replace the older tools (=nix-build=, =nix-shell=, =nix-store=, etc.). For a
+while, it's ended up yet another tool to learn.
+
+If you look at the manpage for =nix= there's a clear warning at the top:
+
+#+BEGIN_QUOTE
+Warning: This program is experimental and its interface is subject to change.
+#+END_QUOTE
+
+This warning has been there since 2018 when Nix 2.0 released.
+
+However, =nix repl= is the only way to get to a [[repl][REPL session]] in Nix, which is an
+important tool for any programming language. The previous tool providing a REPL
+(=nix-repl=) has been removed from Nixpkgs.
+
+This means that technically, the community is strongly encouraging, if not
+forcing, users to use an experimental tool and providing little guidance on how
+to use Nix with some assurance of stability. This is important for industrial
+users who script solutions against the =nix= command-line tools.
+
+Eventually, with the release of Nix 2.4, experimental features were turned into
+flags that needed to be explicitly enabled by users. One of these flags was
+=nix-command=, which now gates users from any subcommand of =nix= beyond =nix
+repl=. However, because so many users were already using the new =nix= command,
+the experimental =nix-command= feature is enabled by default if no experimental
+features have been configured explicitly.
+
+In other words, Nix ships with an experimental feature enabled by default.
+
+This almost indicates that the new =nix= command isn't too unstable. Except, Nix
+2.4 did indeed change the API of =nix= subcommands.
+
+In practice, the =nix= subcommands are relatively reliable. They are
+well-written and functionally robust. But the core maintainers are reserving the
+right to change input parameterization and output formatting.
+
+They communicate this risk only with the warning atop the manpage, which most
+users have been training one another to ignore.
+
+*** Flakes as an experiment
+
+Though Nix expressions have an incredible potential to be precise and
+reproducible, there has always been some backdoors to break the reliability of
+builds. For example, Nix expressions have the potential to evaluate differently
+depending on the setting of some environment variables like =NIX_PATH=.
+
+The motivation for these relaxations of determinism has been to have a quick way
+to let personal computing users have a convenient way to manage their
+environments. Some people are careful to avoid accidentally having
+non-deterministic builds. Still, accidents have occurred frequently enough for
+the community to want better. It's frustrating to have a broken build because
+someone else set an environment variable incorrectly.
+
+Nix 2.4 corrected for this by introducing an experimental feature called
+/flakes/. Users still have a largely ergonomic way to manage their environments,
+but builds are more strictly deterministic. Determinism is a large reason many
+turn to Nix in the first place. A nice benefit of strictly enforced determinism
+is the ability to cache evaluations of Nix expressions, which can be expensive
+to compute.
+
+All this is generally good news. Flakes address problems that industrial users
+of Nix have long had to deal with.
+
+However, flakes are an experimental feature that users need to explicitly
+enable. Similar to the =nix= command, the inputs and outputs of flake-related
+subcommands might change slightly. Such changes have already happened.
+
+On top of this, because flakes are experimental, documentation of flakes is
+fractured in the official documentation. It almost seems like the Nix developers
+are delaying proper documentation until there's a declaration of stability. A
+preferred alternative would be developing documentation more concurrently with
+the implementation, using the comprehensibility of the documentation to inform
+the design of the software. Features that are too hard to explain expose good
+opportunities for redesign.
+
+All this puts industrial Nix users in an annoying place. Not using flakes and
+instead coaching coworkers and customers on how to use Nix safely
+
+- increases the likelihood of defects as people make honest mistakes
+- reduces the likelihood of adoption, because people get frustrated with poor
+ ergonomics and difficulty understanding nuances and corner cases.
+
+However, if industrial users move to flakes to address these problems we have
+the following problems:
+
+- we have to be ready for the flakes API to change as it's technically
+ experimental
+- we have to accept some added training hurdles since documentation of flakes is
+ tucked behind documentation of non-flakes usage.
+
+* Encouraging development with flakes
+
+This project encourages the development of Nix projects using flakes. The
+benefits seem to outweigh the risks of instability. This is not a choice made
+lightly, and this document is an exercise of due diligence to inform users of
+compromises.
+
+Flakes are absolutely the future in Nix. They significantly address prior pains.
+Furthermore, enough people across the world are using them that we have some
+confidence that the Nix commands are reliable as implemented. The core
+contributors have just been very slow to commit to the user interfaces and
+experience.
+
+There might be documentation and training hurdles with flakes, but it's not
+actually much better not using flakes. This is why this project includes
+documentation and guides on Nix itself.
+
+It's also important to keep the risks of using experimental features in
+perspective. Industrial users are more likely to script heavily against =nix=
+commands than personal users. Upgrading anything risks small breaks to address.
+For some industrial users, such breaks are insufferable in aggregate, even if
+manageable individually. This leads to a desire to only use software that has
+been officially released as stable and supported. Still, if you've read
+documents like this one, and feel the history and risks have been well
+explained, using flakes might be the best option, even with industrial scripting
+that might break.
+
+Usage of flakes outside scripting has almost no risk at all. By calling =nix=
+with a few extra arguments ~--extra-experimental-features 'nix-command flakes'~
+we can access flakes commands for single invocations, without needing to enable
+flakes globally. You can even make an alias for your shell that might look like
+the following:
+
+#+begin_src sh :eval no
+alias nix-flakes = nix --extra-experimental-features 'nix-command flakes'
+#+end_src
+
+This way there's less to type interactively. Just don't script against this
+command, and there's no worry of scripts breaking if the flakes API changes.
+
+#+include: "internal/nix-introduction-include.org"
diff --git a/doc/nix-language.md b/doc/nix-language.md
new file mode 100644
index 0000000..1b92432
--- /dev/null
+++ b/doc/nix-language.md
@@ -0,0 +1,449 @@
+- [About this document](#sec-1)
+- [Prerequisites](#sec-2)
+- [Primitive literals](#sec-3)
+- [Strings](#sec-4)
+- [Let-expressions](#sec-5)
+- [String interpolation](#sec-6)
+- [Functions](#sec-7)
+- [Lists](#sec-8)
+- [Attribute sets](#sec-9)
+- [Paths](#sec-10)
+- [Mutable (dangerous) path references](#sec-11)
+- [Other (mutable) dangers](#sec-12)
+- [Importing](#sec-13)
+
+
+# About this document
+
+This document is a quick introduction to the Nix programming language.
+
+You can use the [Nix](https://nixos.org/nix) command-line tools without understanding the programming language (also called Nix). However, learning the Nix language, will allow you to develop your own Nix packages and read the source code of others. A lot of this code is in [Nixpkgs](https://github.com/NixOS/nixpkgs), a centralized repository of Nix code for the entire Nix ecosystem.
+
+The Nix community has recently developed [a tutorial for the language](https://nixos.org/guides/nix-language.html), which may be a good alternative or supplement to this document.
+
+This document is no substitute for the [official Nix language documentation](https://nixos.org/manual/nix/stable/language/index.html), which is not that long for a programming language. Nix does not have much syntax relative to other general-purpose programming languages.
+
+# Prerequisites
+
+You can read this document without following along on your own computer.
+
+If you do want to follow along, you need to [install Nix](nix-installation.md). When installing Nix you will need to have the `nix-command` experimental feature enabled to follow along with this document. You won't need `flakes` enabled, though.
+
+# Primitive literals
+
+We can play around with the Nix language with the `nix eval` command. As with many languages, we can use Nix as a simple calculator by passing Nix expressions to it:
+
+```sh
+nix eval --expr '1 + 1'
+```
+
+ 2
+
+Note, we just have to quote our entire expression for a shell invocation. For an interactive session where this quoting isn't needed, you can use the `nix repl` command.
+
+Nix supports a variety of types you'd expect for a programming language, and we get some literal syntax for typical primitives:
+
+```sh
+nix eval --expr 'builtins.typeOf 1'
+nix eval --expr 'builtins.typeOf 1.0'
+nix eval --expr 'builtins.typeOf true'
+```
+
+ "int"
+ "float"
+ "bool"
+
+Though not covered here in great detail, primitives support the typical operations one would expect. For instance, we have `||`, `&&`, and `!` for boolean values. Numeric values have typical arithmetic operators of `+`, `-`, `/`, and `*`. And we can compare all values with `==`.
+
+# Strings
+
+As you may expect from other languages, Nix supports string literals with the conventional double quote syntax:
+
+```sh
+nix eval --expr 'builtins.typeOf "hello"'
+```
+
+ "string"
+
+Nix also supports multi-line strings with two consecutive single quotes:
+
+```sh
+nix eval --expr "''
+ line 1
+ line 2
+ line 3 ''"
+```
+
+ " line 1\n line 2\nline 3 "
+
+The left-most token in any line establishes a left margin. In the example above, this is `line 3`. Beyond these strings, Nix does not have syntactically significant whitespace.
+
+We concatenate strings with the `+` operator:
+
+```sh
+nix eval --expr '"a" + "b"'
+```
+
+ "ab"
+
+# Let-expressions
+
+Because of Nix's foundation as a “functional” programming language, you can't repeatedly bind values to variables as you may in other “imperative” languages. When we bind a value to a name, it's permanently bound for the entire scope the name exists within. We manage these scopes of bound names with *let-expressions*:
+
+```sh
+nix eval --expr 'let a = 1; b = 2; in a + b'
+```
+
+ 3
+
+In this example, we've bound `a` to 1, and `b` to 2, but only for the scope of the expression that follows the `in` keyword, `a + b`.
+
+As illustrated below, we can't rebind a name:
+
+```sh
+nix eval --expr 'let a = 1; a = 2; in a' 2>&1 || true
+```
+
+ error: attribute 'a' already defined at «string»:1:5
+
+ at «string»:1:12:
+
+ 1| let a = 1; a = 2;
+ | ^
+
+Note that semicolons are mandatory in all Nix forms that have them, including let-expressions. Because of Nix's strict parsing you can neither elide semicolons, nor put extra ones.
+
+# String interpolation
+
+Sometimes we build up small code snippets inline in a Nix expression, so it's useful to have string interpolation support. Similar to shell scripting, the syntax for this follows:
+
+```sh
+nix eval --expr '
+ let foo = "Foo";
+ bar = "Bar";
+ in "${foo + bar} is a terrible name"'
+```
+
+ "FooBar is a terrible name"
+
+String interpolation is supported by both normal and multi-line strings.
+
+You can only interpolate strings into strings. For instance, interpolating an integer won't work:
+
+```sh
+nix eval --expr '
+ let a_number = 42;
+ in "${a_number} is a terrible number"' 2>&1 || true
+```
+
+ error:
+ … while evaluating a path segment
+
+ at «string»:3:9:
+
+ 2| let a_number = 42;
+ 3| in "${a_number} is a terrible number"
+ | ^
+
+ error: cannot coerce an integer to a string
+
+We can use a builtin `toString` function to coerce types to strings:
+
+```sh
+nix eval --expr '
+ let a_number = 42;
+ in "${builtins.toString a_number} is a terrible number"' 2>&1 || true
+```
+
+ "42 is a terrible number"
+
+Note that unlike shell scripts, the curly braces are not optional for string interpolation in Nix. This works out in our favor if we're writing shell scripts inline in a Nix expression, because we can use `$name` for shell string interpolation and `${nix_expr}` for Nix string interpolation. If this is not enough, within multiline strings, we can suppress interpolation with by using `''${…}` instead of just `${…}`.
+
+This is a little tedious to illustrate in a shell example without dealing with quote delimiting. In the following example, the shell will interpolate, but not the final Nix expression because we have `''$` instead of just `$` in our final string:
+
+```sh
+FROM_SHELL='${NOT_EXPANDED_BY_NIX}'
+nix eval --expr "''In ''$FROM_SHELL expansion is prevented.''"
+```
+
+ "In \${NOT_EXPANDED_BY_NIX} expansion is prevented."
+
+Here's the same example without the suppression of the interpolation:
+
+```sh
+FROM_SHELL='${EXPANDED_BY_NIX}'
+nix eval --expr "''In $FROM_SHELL expansion still happens.''" 2>&1 || true
+```
+
+ error: undefined variable 'EXPANDED_BY_NIX'
+
+ at «string»:1:8:
+
+ 1| ''In ${EXPANDED_BY_NIX} expansion still happens.''
+ | ^
+
+# Functions
+
+Nix has first class functions. Nix's functions take in only one argument at a time, and use a colon to separate the parameter name from the body of the function. Furthermore, Nix uses whitespace for function application:
+
+```sh
+nix eval --expr 'builtins.typeOf (a: a + 1)'
+nix eval --expr '(a: a + 1) 2'
+```
+
+ "lambda"
+ 3
+
+Since functions take only one argument at a time, we encode n-ary functions with functions returning functions:
+
+```sh
+nix eval --expr '(a: b: a + b) 1 2'
+```
+
+ 3
+
+In this case, when we apply `1` to `a: b: a + b`, we get another function. When we apply `2` to this resultant function, we finally get our answer `3`.
+
+If you've heard of *currying a function* in other languages with n-ary functions, you may recognize this technique.
+
+# Lists
+
+Nix also has list literals, which use square brackets and are whitespace-delimited:
+
+```sh
+nix eval --expr 'builtins.typeOf [1 2 3 4 5]'
+```
+
+ "list"
+
+We can append lists together with the `++` operator:
+
+```sh
+nix eval --expr '[1 2] ++ [3 4]'
+```
+
+ [ 1 2 3 4 ]
+
+The elements of a list in Nix do not have to be the same type, and lists can be nested:
+
+```sh
+nix eval --expr '[1 "hello" [true]]'
+```
+
+ [ 1 "hello" [ true ] ]
+
+# Attribute sets
+
+Very importantly, Nix has a kind of map called an *attribute set* that is specialized to have textual indices called *attributes* that index values of arbitrary types. It uses the following syntax:
+
+```sh
+nix eval --expr 'builtins.typeOf { a = 1; b = 2; }'
+nix eval --expr '{ a = 1; b = 2; }'
+nix eval --expr '{ a = 1; b = 2; }.b'
+```
+
+ "set"
+ { a = 1; b = 2; }
+ 2
+
+Note, `builtins` is just an attribute set that is in scope by default. And `typeOf` is just an attribute that maps to a function that returns a string indicating the type of the argument.
+
+Often used in Nix expressions, we can overlay sets on top of each other with the `//` operator:
+
+```sh
+nix eval --expr '{ a = 1; b = 2; } // { b = 3; c = 4; }'
+```
+
+ { a = 1; b = 3; c = 4; }
+
+Additionally, we can prefix set literals with the `rec` keyword to get recursive sets. Recursive sets allow values in a set to reference attributes by name:
+
+```sh
+nix eval --expr 'rec { a = b; b = 2; }.a'
+```
+
+ 2
+
+Without the `rec` keyword, we'd get an error:
+
+```sh
+nix eval --expr '{ a = b; b = 2; }.a' 2>&1 || true
+```
+
+ error: undefined variable 'b'
+
+ at «string»:1:7:
+
+ 1| { a = b; b = 2; }.a
+ | ^
+
+If a function accepts an attribute set as an argument, we can have Nix destructure the set as a convenience with the following pattern syntax:
+
+```sh
+nix eval --expr '({ a, b }: a + b ) { a = 1; b = 2; }'
+```
+
+ 3
+
+This basic pattern syntax is rigid, and we can't pass in a attribute set with attributes that don't match the pattern:
+
+```sh
+nix eval --expr '({ a }: a + 2 ) { a = 3; b = 4; }' 2>&1 || true
+```
+
+ error:
+ … from call site
+
+ at «string»:1:1:
+
+ 1| ({ a }: a + 2 ) { a = 3; b = 4; }
+ | ^
+
+ error: function 'anonymous lambda' called with unexpected argument 'b'
+
+ at «string»:1:2:
+
+ 1| ({ a }: a + 2 ) { a = 3; b = 4; }
+ | ^
+ Did you mean a?
+
+If we want to relax the destructuring to accept sets with other attributes we can use a “…” form:
+
+```sh
+nix eval --expr '({ a, ...}: a + 2 ) { a = 3; b = 4; }'
+```
+
+ 5
+
+When destructuring, we can still bind the whole set to a name if we want to using a “@” form.
+
+```sh
+nix eval --expr '(s@{ a, b }: a + s.b ) { a = 2; b = 3; }'
+```
+
+ 5
+
+Attribute sets also support an additional syntactic convenience when pulling in locally bound values as attributes, which comes up a lot in Nix. For example, consider the way we're using `a = a` here:
+
+```sh
+nix eval --expr 'let a = 3; in { a = a; }'
+```
+
+ { a = 3; }
+
+Rather than worrying about spelling the same name correctly both sides of the ‘=’ for an attribute setting, we can use the `inherit` keyword:
+
+```sh
+nix eval --expr 'let a = 3; in { inherit a; }'
+```
+
+ { a = 3; }
+
+# Paths
+
+Because the Nix language was designed for building packages, file paths come up frequently in Nix expressions. Nix conveniently has a *path* type, indicated by identifiers with at least one slash:
+
+```sh
+nix eval --expr 'builtins.typeOf /some/filepath'
+nix eval --expr '/some/filepath'
+```
+
+ "path"
+ /some/filepath
+
+# Mutable (dangerous) path references
+
+> **WARNING:** This section discusses a language feature of Nix that should be avoided in Nix expressions. Use of this feature can lead to subtle build breakages depending on how you've set the `NIX_PATH` environment variable. This section is included only to explain the feature if you encounter it in other's code.
+
+Up until now, all the Nix expressions we've seen have been purely deterministic. `1 + 1` will always evaluate to `2`. This is a valuable property for a build tool. If a Nix expression describes how to build a package, we want to build it consistently every time.
+
+Unfortunately, Nix has a special environment variable `NIX_PATH` which can provide mutable path references. For expressions that use the syntax described in this section, Nix expressions may reference paths that could change dynamically based on how `NIX_PATH` has been set. Builds relying on this are intrinsically non-deterministic.
+
+`NIX_PATH` is a legacy environment variable, that the ecosystem is slowly working to phase out. To assist with this, the `nix` command requires an `--impure` switch to evaluate an expressions that access mutable paths.
+
+As with `PATH`, the settings within `NIX_PATH` are colon-delimited, with earlier settings taking precedence over later ones. There are two forms for setting `NIX_PATH`:
+
+- `=`
+- ``
+
+In a Nix expression, we can use an angle bracket syntax to search `NIX_PATH` for an existent file path. Here's an example of using `NIX_PATH` to set the name “temporary” to `/tmp`, which we then access with ``:
+
+```sh
+NIX_PATH=temporary=/tmp nix eval --impure --expr ''
+```
+
+ /tmp
+
+If we create some files or folders there:
+
+```sh
+mkdir --parents /tmp/some.d/path
+```
+
+Then we can access them using our name as a path prefix in our angle brackets:
+
+```sh
+NIX_PATH=temporary=/tmp nix eval --impure --expr ''
+```
+
+ /tmp/some.d/path
+
+If we use the second form for `NIX_PATH`, can specify directories without a name. These directories are then used as candidate prefixes until an existent path if found. For example, we can consider `/tmp` as a path prefix when looking up `some.d/path` to find `/tmp/some.d/path`:
+
+```sh
+NIX_PATH=/tmp nix eval --impure --expr ''
+```
+
+ /tmp/some.d/path
+
+Now you know about the angle bracket syntax, but please never use it. It's generally caused the Nix community grief. The Nix community looks bad when a build system advertising deterministic builds fails to do so. There's almost always a better way to accomplish what you might with mutable path references.
+
+# Other (mutable) dangers
+
+There's other ways to have mutable references in Nix, but the angle notation discussed in the prior section is the most common found across various projects and legacy documentation.
+
+For instance, it's possible to use some functions found on the `builtins` set to fetch files from the internet. Here's one such example:
+
+```sh
+nix eval --impure --expr '
+ builtins.fetchGit {
+ url = "https://github.com/NixOS/patchelf";
+ }
+'
+```
+
+ {
+ lastModified = 1695801302;
+ lastModifiedDate = "20230927075502";
+ narHash = "sha256-pP/DBhsYFpYQ7RqB4+1Iy9B1jPlC1rNT3aZhhr1Z9EU=";
+ outPath = "/nix/store/i0rgmq3dd0407n5b3jxkvs60gw3qpz1d-source";
+ rev = "917ea45b79de04f69059f42a8e2621f7caeae1c9";
+ revCount = 842;
+ shortRev = "917ea45";
+ submodules = false;
+ }
+
+Notice that because we're referencing a URL on the internet that might change `nix eval` forces us to use the `--impure` switch to perform this evaluation.
+
+In general, exercise caution any time calling a nix command with `--impure`.
+
+# Importing
+
+We can import paths. If the path is a file, it's loaded as a Nix expression. If it's a directory, a file called “default.nix” is loaded within it.
+
+The Nixpkgs source code, for example, has a `default.nix` file at its root, so we can import a path directly to it
+
+Here's a small example that creates two files, one that imports the other:
+
+```sh
+mkdir nix_example
+
+echo '{ a = 3; }' > nix_example/set.nix
+echo '(import ./set.nix).a' > nix_example/default.nix
+
+nix eval --file ./nix_example
+```
+
+ 3
+
+We also see here use of the `--file` switch with `nix`. This is useful when an expression is saved in a file.
diff --git a/doc/nix-language.org b/doc/nix-language.org
new file mode 100644
index 0000000..7fcdadf
--- /dev/null
+++ b/doc/nix-language.org
@@ -0,0 +1,590 @@
+#+title: The Nix Language
+#+setupfile: internal/links.org
+
+* Org-mode setup :noexport:
+
+#+name: cleanup
+#+begin_src sh :dir .. :results output silent :noweb yes
+rm --recursive --force nix_example
+#+end_src
+
+* About this document
+
+This document is a quick introduction to the Nix programming language.
+
+You can use the [[nix][Nix]] command-line tools without understanding the programming
+language (also called Nix). However, learning the Nix language, will allow you
+to develop your own Nix packages and read the source code of others. A lot of
+this code is in [[nixpkgs][Nixpkgs]], a centralized repository of Nix code for the entire Nix
+ecosystem.
+
+The Nix community has recently developed [[nix-language-tutorial][a tutorial for the language]], which may
+be a good alternative or supplement to this document.
+
+This document is no substitute for the [[nix-language-manual][official Nix language documentation]],
+which is not that long for a programming language. Nix does not have much syntax
+relative to other general-purpose programming languages.
+
+* Prerequisites
+
+You can read this document without following along on your own computer.
+
+If you do want to follow along, you need to [[file:nix-installation.org][install Nix]]. When installing Nix you
+will need to have the =nix-command= experimental feature enabled to follow along
+with this document. You won't need =flakes= enabled, though.
+
+* Primitive literals
+
+We can play around with the Nix language with the =nix eval= command. As with
+many languages, we can use Nix as a simple calculator by passing Nix expressions
+to it:
+
+#+begin_src sh :results output :exports both
+nix eval --expr '1 + 1'
+#+end_src
+
+#+RESULTS:
+: 2
+
+Note, we just have to quote our entire expression for a shell invocation. For an
+interactive session where this quoting isn't needed, you can use the =nix repl=
+command.
+
+Nix supports a variety of types you'd expect for a programming language, and we
+get some literal syntax for typical primitives:
+
+#+begin_src sh :results output :exports both
+nix eval --expr 'builtins.typeOf 1'
+nix eval --expr 'builtins.typeOf 1.0'
+nix eval --expr 'builtins.typeOf true'
+#+end_src
+
+#+RESULTS:
+: "int"
+: "float"
+: "bool"
+
+Though not covered here in great detail, primitives support the typical
+operations one would expect. For instance, we have =||=, =&&=, and =!= for
+boolean values. Numeric values have typical arithmetic operators of =+=, =-=,
+=/=, and =*=. And we can compare all values with ====.
+
+* Strings
+
+As you may expect from other languages, Nix supports string literals with the
+conventional double quote syntax:
+
+#+begin_src sh :results output :exports both
+nix eval --expr 'builtins.typeOf "hello"'
+#+end_src
+
+#+RESULTS:
+: "string"
+
+Nix also supports multi-line strings with two consecutive single quotes:
+
+#+begin_src sh :results output :exports both
+nix eval --expr "''
+ line 1
+ line 2
+ line 3 ''"
+#+end_src
+
+#+RESULTS:
+: " line 1\n line 2\nline 3 "
+
+The left-most token in any line establishes a left margin. In the example above,
+this is ~line 3~. Beyond these strings, Nix does not have syntactically
+significant whitespace.
+
+We concatenate strings with the =+= operator:
+
+#+begin_src sh :results output :exports both
+nix eval --expr '"a" + "b"'
+#+end_src
+
+#+RESULTS:
+: "ab"
+
+* Let-expressions
+
+Because of Nix's foundation as a “functional” programming language, you can't
+repeatedly bind values to variables as you may in other “imperative” languages.
+When we bind a value to a name, it's permanently bound for the entire scope the
+name exists within. We manage these scopes of bound names with
+/let-expressions/:
+
+#+begin_src sh :results output :exports both
+nix eval --expr 'let a = 1; b = 2; in a + b'
+#+end_src
+
+#+RESULTS:
+: 3
+
+In this example, we've bound =a= to 1, and =b= to 2, but only for the scope of
+the expression that follows the =in= keyword, =a + b=.
+
+As illustrated below, we can't rebind a name:
+
+#+begin_src sh :results output :exports both
+nix eval --expr 'let a = 1; a = 2; in a' 2>&1 || true
+#+end_src
+
+#+RESULTS:
+: error: attribute 'a' already defined at «string»:1:5
+:
+: at «string»:1:12:
+:
+: 1| let a = 1; a = 2;
+: | ^
+
+Note that semicolons are mandatory in all Nix forms that have them, including
+let-expressions. Because of Nix's strict parsing you can neither elide
+semicolons, nor put extra ones.
+
+* String interpolation
+
+Sometimes we build up small code snippets inline in a Nix expression, so it's
+useful to have string interpolation support. Similar to shell scripting, the
+syntax for this follows:
+
+#+begin_src sh :results output :exports both
+nix eval --expr '
+ let foo = "Foo";
+ bar = "Bar";
+ in "${foo + bar} is a terrible name"'
+#+end_src
+
+#+RESULTS:
+: "FooBar is a terrible name"
+
+String interpolation is supported by both normal and multi-line strings.
+
+You can only interpolate strings into strings. For instance, interpolating an
+integer won't work:
+
+#+begin_src sh :results output :exports both
+nix eval --expr '
+ let a_number = 42;
+ in "${a_number} is a terrible number"' 2>&1 || true
+#+end_src
+
+#+RESULTS:
+#+begin_example
+error:
+ … while evaluating a path segment
+
+ at «string»:3:9:
+
+ 2| let a_number = 42;
+ 3| in "${a_number} is a terrible number"
+ | ^
+
+ error: cannot coerce an integer to a string
+#+end_example
+
+We can use a builtin =toString= function to coerce types to strings:
+
+#+begin_src sh :results output :exports both
+nix eval --expr '
+ let a_number = 42;
+ in "${builtins.toString a_number} is a terrible number"' 2>&1 || true
+#+end_src
+
+#+RESULTS:
+: "42 is a terrible number"
+
+Note that unlike shell scripts, the curly braces are not optional for string
+interpolation in Nix. This works out in our favor if we're writing shell scripts
+inline in a Nix expression, because we can use ~$name~ for shell string
+interpolation and ~${nix_expr}~ for Nix string interpolation. If this is not
+enough, within multiline strings, we can suppress interpolation with by using
+=''${…}= instead of just =${…}=.
+
+This is a little tedious to illustrate in a shell example without dealing with
+quote delimiting. In the following example, the shell will interpolate, but not
+the final Nix expression because we have =''$= instead of just =$= in our final
+string:
+
+#+begin_src sh :results output :exports both
+FROM_SHELL='${NOT_EXPANDED_BY_NIX}'
+nix eval --expr "''In ''$FROM_SHELL expansion is prevented.''"
+#+end_src
+
+#+RESULTS:
+: "In \${NOT_EXPANDED_BY_NIX} expansion is prevented."
+
+Here's the same example without the suppression of the interpolation:
+
+#+begin_src sh :results output :exports both
+FROM_SHELL='${EXPANDED_BY_NIX}'
+nix eval --expr "''In $FROM_SHELL expansion still happens.''" 2>&1 || true
+#+end_src
+
+#+RESULTS:
+: error: undefined variable 'EXPANDED_BY_NIX'
+:
+: at «string»:1:8:
+:
+: 1| ''In ${EXPANDED_BY_NIX} expansion still happens.''
+: | ^
+
+* Functions
+
+Nix has first class functions. Nix's functions take in only one argument at a
+time, and use a colon to separate the parameter name from the body of the
+function. Furthermore, Nix uses whitespace for function application:
+
+#+begin_src sh :results output :exports both
+nix eval --expr 'builtins.typeOf (a: a + 1)'
+nix eval --expr '(a: a + 1) 2'
+#+end_src
+
+#+RESULTS:
+: "lambda"
+: 3
+
+Since functions take only one argument at a time, we encode n-ary functions with
+functions returning functions:
+
+#+begin_src sh :results output :exports both
+nix eval --expr '(a: b: a + b) 1 2'
+#+end_src
+
+#+RESULTS:
+: 3
+
+In this case, when we apply ~1~ to ~a: b: a + b~, we get another function. When
+we apply ~2~ to this resultant function, we finally get our answer ~3~.
+
+If you've heard of /currying a function/ in other languages with n-ary
+functions, you may recognize this technique.
+
+* Lists
+
+Nix also has list literals, which use square brackets and are
+whitespace-delimited:
+
+#+begin_src sh :results output :exports both
+nix eval --expr 'builtins.typeOf [1 2 3 4 5]'
+#+end_src
+
+#+RESULTS:
+: "list"
+
+We can append lists together with the =++= operator:
+
+#+begin_src sh :results output :exports both
+nix eval --expr '[1 2] ++ [3 4]'
+#+end_src
+
+#+RESULTS:
+: [ 1 2 3 4 ]
+
+The elements of a list in Nix do not have to be the same type, and lists can be
+nested:
+
+#+begin_src sh :results output :exports both
+nix eval --expr '[1 "hello" [true]]'
+#+end_src
+
+#+RESULTS:
+: [ 1 "hello" [ true ] ]
+
+* Attribute sets
+
+Very importantly, Nix has a kind of map called an /attribute set/ that is
+specialized to have textual indices called /attributes/ that index values of
+arbitrary types. It uses the following syntax:
+
+#+begin_src sh :results output :exports both
+nix eval --expr 'builtins.typeOf { a = 1; b = 2; }'
+nix eval --expr '{ a = 1; b = 2; }'
+nix eval --expr '{ a = 1; b = 2; }.b'
+#+end_src
+
+#+RESULTS:
+: "set"
+: { a = 1; b = 2; }
+: 2
+
+Note, =builtins= is just an attribute set that is in scope by default. And
+=typeOf= is just an attribute that maps to a function that returns a string
+indicating the type of the argument.
+
+Often used in Nix expressions, we can overlay sets on top of each other with the
+=//= operator:
+
+#+begin_src sh :results output :exports both
+nix eval --expr '{ a = 1; b = 2; } // { b = 3; c = 4; }'
+#+end_src
+
+#+RESULTS:
+: { a = 1; b = 3; c = 4; }
+
+Additionally, we can prefix set literals with the =rec= keyword to get recursive
+sets. Recursive sets allow values in a set to reference attributes by name:
+
+#+begin_src sh :results output :exports both
+nix eval --expr 'rec { a = b; b = 2; }.a'
+#+end_src
+
+#+RESULTS:
+: 2
+
+Without the =rec= keyword, we'd get an error:
+
+#+begin_src sh :results output :exports both
+nix eval --expr '{ a = b; b = 2; }.a' 2>&1 || true
+#+end_src
+
+#+RESULTS:
+: error: undefined variable 'b'
+:
+: at «string»:1:7:
+:
+: 1| { a = b; b = 2; }.a
+: | ^
+
+If a function accepts an attribute set as an argument, we can have Nix
+destructure the set as a convenience with the following pattern syntax:
+
+#+begin_src sh :results output :exports both
+nix eval --expr '({ a, b }: a + b ) { a = 1; b = 2; }'
+#+end_src
+
+#+RESULTS:
+: 3
+
+This basic pattern syntax is rigid, and we can't pass in a attribute set with
+attributes that don't match the pattern:
+
+#+begin_src sh :results output :exports both
+nix eval --expr '({ a }: a + 2 ) { a = 3; b = 4; }' 2>&1 || true
+#+end_src
+
+#+RESULTS:
+#+begin_example
+error:
+ … from call site
+
+ at «string»:1:1:
+
+ 1| ({ a }: a + 2 ) { a = 3; b = 4; }
+ | ^
+
+ error: function 'anonymous lambda' called with unexpected argument 'b'
+
+ at «string»:1:2:
+
+ 1| ({ a }: a + 2 ) { a = 3; b = 4; }
+ | ^
+ Did you mean a?
+#+end_example
+
+If we want to relax the destructuring to accept sets with other attributes we
+can use a “...” form:
+
+#+begin_src sh :results output :exports both
+nix eval --expr '({ a, ...}: a + 2 ) { a = 3; b = 4; }'
+#+end_src
+
+#+RESULTS:
+: 5
+
+When destructuring, we can still bind the whole set to a name if we want to
+using a “@” form.
+
+#+begin_src sh :results output :exports both
+nix eval --expr '(s@{ a, b }: a + s.b ) { a = 2; b = 3; }'
+#+end_src
+
+#+RESULTS:
+: 5
+
+Attribute sets also support an additional syntactic convenience when pulling in
+locally bound values as attributes, which comes up a lot in Nix. For example,
+consider the way we're using ~a = a~ here:
+
+#+begin_src sh :results output :exports both
+nix eval --expr 'let a = 3; in { a = a; }'
+#+end_src
+
+#+RESULTS:
+: { a = 3; }
+
+Rather than worrying about spelling the same name correctly both sides of
+the ‘=’ for an attribute setting, we can use the =inherit= keyword:
+
+#+begin_src sh :results output :exports both
+nix eval --expr 'let a = 3; in { inherit a; }'
+#+end_src
+
+#+RESULTS:
+: { a = 3; }
+
+* Paths
+
+Because the Nix language was designed for building packages, file paths come up
+frequently in Nix expressions. Nix conveniently has a /path/ type, indicated by
+identifiers with at least one slash:
+
+#+begin_src sh :results output :exports both
+nix eval --expr 'builtins.typeOf /some/filepath'
+nix eval --expr '/some/filepath'
+#+end_src
+
+#+RESULTS:
+: "path"
+: /some/filepath
+
+* Mutable (dangerous) path references
+
+#+begin_quote
+*WARNING:* This section discusses a language feature of Nix that should be
+avoided in Nix expressions. Use of this feature can lead to subtle build
+breakages depending on how you've set the =NIX_PATH= environment variable. This
+section is included only to explain the feature if you encounter it in other's
+code.
+#+end_quote
+
+Up until now, all the Nix expressions we've seen have been purely deterministic.
+=1 + 1= will always evaluate to =2=. This is a valuable property for a build
+tool. If a Nix expression describes how to build a package, we want to build
+it consistently every time.
+
+Unfortunately, Nix has a special environment variable =NIX_PATH= which can
+provide mutable path references. For expressions that use the syntax described
+in this section, Nix expressions may reference paths that could change
+dynamically based on how =NIX_PATH= has been set. Builds relying on this are
+intrinsically non-deterministic.
+
+=NIX_PATH= is a legacy environment variable, that the ecosystem is slowly
+working to phase out. To assist with this, the =nix= command requires an
+=--impure= switch to evaluate an expressions that access mutable paths.
+
+As with =PATH=, the settings within =NIX_PATH= are colon-delimited, with earlier
+settings taking precedence over later ones. There are two forms for setting
+=NIX_PATH=:
+
+- ~=~
+- ~~
+
+In a Nix expression, we can use an angle bracket syntax to search =NIX_PATH= for
+an existent file path. Here's an example of using =NIX_PATH= to set the name
+“temporary” to =/tmp=, which we then access with ~~:
+
+#+begin_src sh :results output :exports both
+NIX_PATH=temporary=/tmp nix eval --impure --expr ''
+#+end_src
+
+#+RESULTS:
+: /tmp
+
+If we create some files or folders there:
+
+#+begin_src sh :results silent
+mkdir --parents /tmp/some.d/path
+#+end_src
+
+Then we can access them using our name as a path prefix in our angle
+brackets:
+
+#+begin_src sh :results output :exports both
+NIX_PATH=temporary=/tmp nix eval --impure --expr ''
+#+end_src
+
+#+RESULTS:
+: /tmp/some.d/path
+
+If we use the second form for =NIX_PATH=, can specify directories without a
+name. These directories are then used as candidate prefixes until an existent
+path if found. For example, we can consider =/tmp= as a path prefix when looking
+up =some.d/path= to find =/tmp/some.d/path=:
+
+#+begin_src sh :results output :exports both
+NIX_PATH=/tmp nix eval --impure --expr ''
+#+end_src
+
+#+RESULTS:
+: /tmp/some.d/path
+
+Now you know about the angle bracket syntax, but please never use it. It's
+generally caused the Nix community grief. The Nix community looks bad when a
+build system advertising deterministic builds fails to do so. There's almost
+always a better way to accomplish what you might with mutable path references.
+
+* Other (mutable) dangers
+
+There's other ways to have mutable references in Nix, but the angle notation
+discussed in the prior section is the most common found across various projects
+and legacy documentation.
+
+For instance, it's possible to use some functions found on the =builtins= set to
+fetch files from the internet. Here's one such example:
+
+#+begin_src sh :eval no :exports code
+nix eval --impure --expr '
+ builtins.fetchGit {
+ url = "https://github.com/NixOS/patchelf";
+ }
+'
+#+end_src
+
+#+begin_src sh :results output :exports results
+nix eval --impure --expr '
+ builtins.fetchGit {
+ url = "https://github.com/NixOS/patchelf";
+ }
+' | nixfmt
+#+end_src
+
+#+RESULTS:
+#+begin_example
+{
+ lastModified = 1695801302;
+ lastModifiedDate = "20230927075502";
+ narHash = "sha256-pP/DBhsYFpYQ7RqB4+1Iy9B1jPlC1rNT3aZhhr1Z9EU=";
+ outPath = "/nix/store/i0rgmq3dd0407n5b3jxkvs60gw3qpz1d-source";
+ rev = "917ea45b79de04f69059f42a8e2621f7caeae1c9";
+ revCount = 842;
+ shortRev = "917ea45";
+ submodules = false;
+}
+#+end_example
+
+Notice that because we're referencing a URL on the internet that might change
+=nix eval= forces us to use the =--impure= switch to perform this evaluation.
+
+In general, exercise caution any time calling a nix command with =--impure=.
+
+* Importing
+
+We can import paths. If the path is a file, it's loaded as a Nix expression. If
+it's a directory, a file called “default.nix” is loaded within it.
+
+The Nixpkgs source code, for example, has a =default.nix= file at its root, so
+we can import a path directly to it
+
+Here's a small example that creates two files, one that imports the other:
+
+#+begin_src sh :dir .. :results output :exports both
+mkdir nix_example
+
+echo '{ a = 3; }' > nix_example/set.nix
+echo '(import ./set.nix).a' > nix_example/default.nix
+
+nix eval --file ./nix_example
+#+end_src
+
+#+RESULTS:
+: 3
+
+We also see here use of the =--file= switch with =nix=. This is useful when an
+expression is saved in a file.
+
+* Org-mode Cleanup :noexport:
+
+Cleans directory for run:
+
+#+call: cleanup()
diff --git a/doc/nix-usage-flakes.md b/doc/nix-usage-flakes.md
new file mode 100644
index 0000000..2120f51
--- /dev/null
+++ b/doc/nix-usage-flakes.md
@@ -0,0 +1,473 @@
+- [About this document](#sec-1)
+- [How this project uses Nix](#sec-2)
+- [Prerequisites](#sec-3)
+- [Working with Nix](#sec-4)
+ - [Referencing flake projects](#sec-4-1)
+ - [Inspecting flake outputs](#sec-4-2)
+ - [Referencing flake outputs](#sec-4-3)
+ - [Searching flakes for packages](#sec-4-4)
+ - [Building installables](#sec-4-5)
+ - [Running commands in a shell](#sec-4-6)
+ - [Running installables](#sec-4-7)
+ - [`nix run` and `nix shell` with remote flakes](#sec-4-8)
+ - [Installing and uninstalling programs](#sec-4-9)
+ - [Garbage collection](#sec-4-10)
+- [Next steps](#sec-5)
+
+
+# About this document
+
+This document explains how to take advantage of software provided by Nix for people new to [the Nix package manager](https://nixos.org/nix). This guide uses this project for examples, but it focused on introducing general Nix usage, which applies to other projects using Nix as well.
+
+This project supports a still-experimental feature of Nix called *flakes*, which this guide shows users how to use. [Another guide](nix-usage-noflakes.md) explains how to do everything illustrated in this document, but without flakes.
+
+> **NOTE:** If you're new to flakes, please read the provided [supplemental introduction to Nix](nix-introduction.md) to understand the experimental nature of flakes and how it may or may not affect you. Hopefully you'll find these trade-offs acceptable so you can take advantage of the improved experience flakes offer.
+
+# How this project uses Nix
+
+This project uses Nix to download all necessary dependencies and build everything from source. In this regard, Nix is helpful as not just a package manager, but also a build tool. Nix helps us get from raw source files to not only built executables, but all the way to a Nix package, which we can install with Nix if we like.
+
+Within this project, the various files with a `.nix` extension are Nix files, each of which contains an expression written in the [Nix expression language](https://nixos.org/manual/nix/stable/language/index.html) used by the Nix package manager to specify packages. If you get proficient with this language, you can use these expressions as a starting point to compose your own packages beyond what's provided in this project.
+
+# Prerequisites
+
+If you're new to Nix consider reading the provided [introduction](nix-introduction.md).
+
+This project supports Linux on x86-64 machines.
+
+That may affect your ability to follow along with examples.
+
+Otherwise, see the provided [Nix installation and configuration guide](nix-installation.md) if you have not yet set Nix up.
+
+To continue following this usage guide, you will need Nix's experimental flakes feature. You can enable this globally, or use an alias such as the following:
+
+```sh
+alias nix-flakes = nix --extra-experimental-features 'nix-command flakes'
+```
+
+# Working with Nix
+
+Though covering Nix comprehensively is beyond the scope of this document, we'll go over a few commands illustrating some usage of Nix with this project.
+
+## Referencing flake projects
+
+Most of this document illustrates use of the `nix` command, which provides a number of subcommands and centralizes Nix usage.
+
+Many of the `nix` subcommands accept references to flake-enabled projects. A flake is written as just a Nix expression saved in a file named `flake.nix`. This file should be at the root of a project. We can reference both local and remote flake projects.
+
+Here's some common forms we can use to reference flake projects:
+
+| Syntax | Location |
+|-------------------------------- |-------------------------------------------------------------- |
+| `.` | flake in the current directory |
+| `` | flake in some other filepath (must have a slash) |
+| `` | reference to flake from the registry (see `nix registry list`) |
+| `git+` | latest flake in the default branch of a Git repository |
+| `git+?ref=` | latest flake in a branch of a Git repository |
+| `git+?rev=` | flake in a specific commit of a Git repository |
+| `github:/` | latest flake in the default branch of a GitHub repository |
+| `github://` | latest flake in a branch of a GitHub repository |
+| `github://` | flake in a specific commit of a GitHub repository |
+
+This table introduces an angle-bracket notation for syntactic forms with components that change with context. This notation is used throughout this document.
+
+Referencing local flake projects is easy enough with filepaths. But the URL-like notation for remote flake projects can get a touch verbose to type out. Furthermore, some of these references are not fixed. For example, Git branches point to different commits over time.
+
+To manage flake references, Nix provides a flakes registry. Upon installation this registry is prepopulated with some global entries:
+
+```sh
+nix registry list
+```
+
+ …
+ global flake:templates github:NixOS/templates
+ global flake:patchelf github:NixOS/patchelf
+ global flake:poetry2nix github:nix-community/poetry2nix
+ global flake:nix-serve github:edolstra/nix-serve
+ global flake:nickel github:tweag/nickel
+ global flake:bundlers github:NixOS/bundlers
+ global flake:pridefetch github:SpyHoodle/pridefetch
+ global flake:systems github:nix-systems/default
+ global flake:helix github:helix-editor/helix
+ global flake:sops-nix github:Mic92/sops-nix
+
+For example, rather than referencing the flake on the `nixpkgs-unstable` branch of the Nixpkgs GitHub repository with `github:NixOS/nixpkgs/nixpkgs-unstable`, we can just use the simple identifier `nixpkgs`.
+
+If we want to point to a different branch, but still use an identifier from the registry, we can by extending it with the branch. For example, the flakes identifier `nixpkgs` is the same as `nixpkgs/nixpkgs-ustable`, but we can also use `nixpkgs/nixos-23.11` to override the branch and point to the NixOS 23.11 release branch.
+
+Note, registries have mutable references, but for some of these references Nix knows how to repeatably rebuild the snapshot referenced. For example, when referencing a GitHub repository via a registry reference, Nix will take note of the commit ID of the snapshot retrieved.
+
+## Inspecting flake outputs
+
+A flake can provide a variety of outputs that can be used in different contexts. A few of these outputs include the packages we can build and install.
+
+We can use `nix flake show` to see the outputs provided by any flake, local or remote, by providing a flake reference discussed in the previous section. Here's an example of inspecting the flake of this project locally:
+
+```sh
+nix flake show .
+```
+
+ git+file:///home/tnks/src/shajra/shajra-keyboards
+ ├───apps
+ │ └───x86_64-linux
+ │ ├───default: app
+ │ ├───flash-ergodoxez: app
+ │ ├───flash-model01: app
+ │ ├───flash-model100: app
+ │ ├───flash-moonlander: app
+ │ └───licenses-thirdparty: app
+ ├───legacyPackages
+ │ └───x86_64-linux omitted (use '--legacy' to show)
+ ├───overlays
+ │ └───default: Nixpkgs overlay
+ └───packages
+ └───x86_64-linux
+ ├───default: package 'shajra-keyboards-ci'
+ ├───flash-ergodoxez: package 'flash-ergodoxez'
+ ├───flash-model01: package 'flash-model01'
+ ├───flash-model100: package 'flash-model100'
+ ├───flash-moonlander: package 'flash-moonlander'
+ └───licenses-thirdparty: package 'shajra-keyboards-licenses'
+
+Flake outputs are a organized in a tree of *attributes*. References to paths of attributes are dot-delimited. There is a standard schema for the output attribute tree of flake. It's permitted to have outputs outside this schema.
+
+This document mostly focuses on packages provided by the `packages` output attribute. Notice that a flake provides packages for different (but often not all) system architectures.
+
+For commands like `nix flake show` that expect a flake reference as an argument, `.` is assumed as default if an argument isn't provided. So `nix flake show` is equivalent to `nix flake show .`.
+
+## Referencing flake outputs
+
+Many of the `nix` subcommands work with references to flakes outputs. These references are called *installables*. There are many types of installables (hence the general name). In this document, we'll hone in on the following forms:
+
+- `` to select a default output from
+- `#