Table of Contents
#! emacs –script ;; -- org -- (find-file load-file-name) (org-mode) (org-babel-tangle) (kill-emacs)
1. Introduction
This guide is a literate program to help you bootstrap a working guix home install onto your Librem 5 running PureOS. Guix System does not yet support the Librem 5, but you can still install the guix package manager on the Librem 5. I detail here everything I touched in PureOS, and leave the rest of the guix hacking to you. The Librem 5 has only ~30GiB of internal memory, so this process puts guix, as well as all of your personal files, on a μSD, which I assume from here on that you have already inserted. There is no garuntee that it will work, especially as systems change and get updated, and you should understand/test each line before running it. In theory, you should be able to open your new Librem 5 and obtain this program by running (in ~):
sudo bash -c "apt update && apt upgrade -y && apt install emacs -y" emacs --eval '(eww "zacchae.us/guix-usd.org")' -f org-mode
Convenience blocks for getting more screen realestate in gui (use C-c C-c).
(tool-bar-mode -1) (menu-bar-mode -1) (toggle-frame-fullscreen)
or tui (launching emacs with -nw)
(xterm-mouse-mode) (menu-bar-mode -1)
Make this buffer a local copy and enable edits
(write-file "~/guix-usd.org") (kill-buffer "guix-usd.org") (find-file "~/guix-usd.org")
and generate the script files
(org-babel-tangle)
In theory, you should be able to run the following to get a working guix home, but this is not recommended until a later, more stable version of guix-usd.org. For now, this file is more of a reference.
./guix-usd.org ./guix-usd.bootstrap ./guix-usd.configure
This should leave you with a functional home.scm which you can start from. However, you may end up with a bricked device that must be re-flashed, which is difficult to do from Guix. There will be no warnings printed from the code, and I unashamedly pass "force" flags to make dangerous operations go through. I do try to highlight dangers in the text here, so it is "slightly" safer if you read along and do things manually. Additionally, some operations are performed (like appending to /etc/crypttab) that may break your system if run twice without reverting files. If by some miracle, you follow this guide and end up with a working device, you will have a setup like the following:
- At boot, when you decrypt your Librem 5's eMMC memory (i.e. /), it will attempt to decrypt your μSD using a key file located in /etc.
- If the μSD decrypts successfully, then it will mount btrfs subvolumes from the decrypted μSD at /home, /gnu, and /var/guix.
- This means your home, the guix store, and current state of guix are all stored on your μSD.
- If the μSD fails to decrypt, then:
- /gnu and /var/guix will fail to mount.
- guix will be unusable
- /home will be mounted from a file
- /gnu and /var/guix will fail to mount.
1.1. Final Warning
This guide has you modify the following files: /etc/fstab /etc/crypttab
Optionally modify the following: /lib/systemd/system/guix-daemon.service /etc/defaults/keyboard /etc/passwd
Create the following files: /etc/luks-keys/* /gnu /var/guix
And completely destroy any data on your μSD.
This is your final warning to do a back up and pray.
2. Initial setup
We will be setting up the entire μSD as a singular encrypted device with a btrfs filesystem and subvolumes to hold guix and your files
sudo bash -c "apt update && apt upgrade -y && apt install btrfs-progs guix"
The μSD encryption keys will be in /etc/luks-keys, and we will need mount points for guix folders /gnu and /var/guix
sudo mkdir /etc/luks-keys /gnu /var/guix sudo chmod o-rwx /etc/luks-keys
3. Encrypting your μSD
To my knowledge, there is no way to prompt the user for a second encryption passphrase for the μSD at boot, hence the only way to decrypt the μSD at boot is to have a key file stored in your file tree. This means that even if you remove your μSD, if you unlock your phone, then the keys to your μSD could be extracted from your phone. Additionally, the following will erase any data which was previously on the μSD.
sudo dd if=/dev/urandom of=/etc/luks-keys/disk_secret_key bs=512 count=8 echo Encrypting device. Enter passphrase below (a few times). echo This passphrase will be usable in addition to the key file at: echo /etc/luks-key/disk_secret_key sudo umount /dev/sda* sudo cryptsetup luksFormat /dev/sda --batch-mode sudo cryptsetup luksAddKey /dev/sda /etc/luks-keys/disk_secret_key
4. Setting up your btr file system
Open (map) the encrypted μSD, reformat the contents as btrfs, mount the mapped device, and create subvolumes for /home, /gnu, and /var/guix. Additionally, copy over all files from your home directory as a starting point for your new home directory.
sudo cryptsetup open /dev/sda crypt_sd --key-file=/etc/luks-keys/disk_secret_key sudo mkfs.btrfs --force /dev/mapper/crypt_sd -L btros sudo mount LABEL=btros /mnt sudo btrfs subvolume create /mnt/home sudo btrfs subvolume create /mnt/gnu sudo btrfs subvolume create /mnt/varguix sudo cp -a /home/purism /mnt/home/ sudo umount /mnt
5. Decrypting/Mounting at boot
The following describes the steps to decrypt and mount your now-set-up μSD at boot.
5.1. Decrypting the μSD
To decrypt at boot, append the necessary entry to /etc/crypttab
echo crypt_sd UUID=$(sudo cryptsetup luksUUID /dev/sda) \ /etc/luks-keys/disk_secret_key nofail,luks \ | sudo tee -a /etc/crypttab
This will use the luks UUID to identify the μSD, and the 'nofail' flag ensures the system will still boot if μSD is broken or missing.
5.2. mounting
[Unit] Description=mount decrypted home if μSD exists Before=user.slice guix-daemon.service gnu-store.mount guix-publish.service Requires=systemd-cryptsetup@crypt_sd.service After=systemd-cryptsetup@crypt_sd.service [Service] Type=oneshot ExecStart=/usr/bin/mount /dev/mapper/crypt_sd /home -o subvol=home ExecStart=/usr/bin/mount /dev/mapper/crypt_sd /gnu -o subvol=gnu ExecStart=/usr/bin/mount /dev/mapper/crypt_sd /var/guix -o subvol=varguix ExecStop=/usr/bin/umount /home RemainAfterExit=True [Install] WantedBy=local-fs-pre.target
cp guix-usd.service /etc/systemd/system/ systemctl daemon-reload systemctl enable guix-usd
6. Setting up guix home
This section assumes you have successfully done all the steps described above and rebooted. First, you should do a guix pull as your user, and additionally as root.
su -c "guix pull" purism /home/purism/.config/guix/current/bin/guix pull guix archive --authorize < \ ~root/.config/guix/current/share/guix/bordeaux.guix.gnu.org.pub
Now that root's guix has been setup, you should modify the systemd unit file to use root's guix-daemon.
sed -i 's/\/usr\/bin\/guix-daemon/\/var\/guix\/profiles\/per-user\/root\/current-guix\/bin\/guix-daemon/' /lib/systemd/system/guix-daemon.service systemctl daemon-reload systemctl restart guix-daemon
This isn't strictly necessary, but I like that I can update guix-daemon with sudo -i guix pull. This is also helpful because the current (as of this writing) version of guix installed by apt does not check bordeaux.guix.gnu.org for substitutes, something extremely necessary for tiny arm devices that have little RAM. You can, however, enable bordeaux.guix.gnu.org by adding "–substitute-urls='https://bordeaux.guix.gnu.org https://ci.guix.gnu.org'" to the "ExecStart" feild of /lib/systemd/system/guix-daemon.service instead.
One other problem I came across is that guix home environment variable initialization fails to properly deal with empty variables. There are ways to fix this by modifying guix-home, but looking at https://issues.guix.gnu.org/61982, it seems that they have decided to make changes to how guix is installed on foreign distros. This means even if you guix pull after this is patched, guix home will probably not work properly until the next major release is adopted in your apt repo.
Specifically, ~/.guix-home/setup-environment will assign to the empty XDGCONFIGDIRS variable without adding the default "/etc/xdg" value, so programs like phosh will not find /etc/xdg and fail to start. This means if you naively install guix home on your new Librem 5, you will get a black screen on boot…
To address this, I add the following line to my shell environment to set XDGCONFIGDIRS to the default value in the case that it is empty.
("XDG_CONFIG_DIRS" . "$([ -z $XDG_CONFIG_DIRS ] 2> /dev/null && echo /etc/xdg || echo $XDG_CONFIG_DIRS)")
From here you should be able to use the following starter home.scm at ./home.scm (not tested with bash).
(use-modules (gnu packages) (gnu services) (gnu home services shells) (guix gexp)) (home-environment (packages (map specification->package (list "emacs" "screen" "glibc-locales" "syncthing" "guile"))) (services (list (service home-bash-service-type (home-bash-configuration (environment-variables '(("XDG_CONFIG_DIRS" . "$([ -z $XDG_CONFIG_DIRS ] 2> /dev/null && echo /etc/xdg || echo $XDG_CONFIG_DIRS)"))))) (service home-zsh-service-type (home-zsh-configuration (environment-variables '(("GUIX_LOCPATH" . "$HOME/.guix-home/profile/lib/locale"))) ;(zshrc (list (plain-file "zshrc" "source $HOME/.guix-home/setup-environment\n"))) (zlogin (list (plain-file "zlogin" "XDG_CONFIG_DIRS=\"$XDG_CONFIG_DIRS\":/etc/xdg\n"))))))))
And initialize your first home environment!
su -c 'guix home reconfigure home.scm' purism
6.1. Another note on substitutes
If trying to run guix home still results in many packages trying to build, it could be that there was a recent push to the guix repo which caused a lot of rebuilds that the build farm can't keep up with. The only solution I know of is to manually check https://ci.guix.gnu.org/jobset/master. Go there and find a commit COMMIT right before a commit which resulted in a large number of builds. Then run
guix pull --commit=COMMIT --allow-downgrades
This should revert your guix version to one with more substitutes available.
7. Finishing Touches
7.1. Changing UI scaling
I do a lot of things at a terminal or emacs. I want my phone screen to be like a real screen. For this, I change the UI scaling of the builtin screen from 2 to 1.75 like so:
echo '[output:DSI-1] scale = 1.75' >> /usr/share/phosh/phoc.ini
See gnome-control-panel->Displays->select your monitor-> zoom dropdown for different zoom options
7.2. Changing console keyboard layout
I like to use the dvorak layout with caps-lock as another control key. For the gui, you need to change your layout in settings, download gnome-tweaks:
apt install gnome-tweaks
And enable "Caps Lock is also Ctrl" in gnome-tweaks>Keyboard & Mouse>additional Layout Options>Caps Lock behavior. To make the same layout available from a tty, I ran:
sudo apt install console-data echo ' XKBMODEL="pc105" XKBLAYOUT="us" XKBVARIANT="dvorak" XKBOPTIONS="ctrl:nocaps" BACKSPACE="guess" ' | sudo tee /etc/default/keyboard
7.3. toggle fullscreen
gsettings set org.gnome.desktop.wm.keybindings toggle-fullscreen "['F11']"
7.4. Using zsh
I personally like zsh over bash for interactive prompts. To change to zsh, run:
sudo apt install zsh echo Need user passwd to change shell to zsh chsh -s /bin/zsh
echo l5 > /etc/hostname
8. Re-flahsing from Guix
If you messed something up, and know what you did, you may want to try fixing your problem using Jumpdrive instead: https://github.com/dreemurrs-embedded/Jumpdrive This will allow you to mount your powered off Librem like a flash drive and make changes to fix your device.
I don't have any automated solution for flashing a librem from guix, but I have figured out how to do it. To do so, I mostly followed the instructions at https://developer.puri.sm/Librem5/Development_Environment/Phone/Troubleshooting/Reflashing_the_Phone.html. First, I cloned the librem 5 flash image repository
git clone https://source.puri.sm/Librem5/librem5-flash-image
The main hiccup here is that the jenkins package required by the flashing script has not been packaged for guix. To make it work, I manually made changes to scripts/librem5-flash-image: comment lines 23-27, 461-484, and add `ubootboard = 'librem5'` to line 485 (line numbers may have changed since).
Then, I manually tried to traverse https://arm01.puri.sm/ to find some images, and downloaded the necessary images
mkdir imdir wget https://arm01.puri.sm/job/Images/job/Image%20Build/14006/artifact/librem5r4.img.xz xz -d librem5r4.img.xz mv librem5r4.img imdir wget https://arm01.puri.sm/job/u-boot_builds/job/uboot_librem5_build/lastSuccessfulBuild/artifact/output/uboot-librem5/u-boot-librem5.imx mv u-boot-librem5.imx imdir/
Then, from a ROOT SHELL, I activated a guix shell, and downloaded necessary packages from pip:
cd librem5-flash-image guix shel python gcc musl python-requests binutils usbutils uuu --pure python3 -m venv venv --upgrade-deps --system-site-packages source ./venv/bin/activate pip3 install tqdm pyyaml ./scripts/librem5-flash-image --skip-download --dir ./imdir
9. connecting to the device
Now spawn a syncthing daemon, and take note of the
syncthing
9.1. setting up home
git init git pull .stsync/git/home .config/config.org guix home reconfigure .config/guix/home.scm
9.2. secured devices
9.2.1. obtaining encypted passwords
git clone .stsync/git/store .password-store
9.2.2. obtainiing encryption keys
Because your home directory is synced across all devices, even possibly compromized devices (which therefore don't have your encryption keys), it is unwise to sync such files via the same syncthing folder. An ideal way to accomplish this is with an auxhilary syncthing folder. In the future, I will add an automated way to utilize syncthing for such a task, but for now, you should transfer them over manually on, say, a USB drive, on a preferably luks encrypted partition.
10. planned future content
- better syncthing automation
- changing username
- fewer apt dependencies
- switch to guix installer from apt
- auto screen change
- change screen timout
- exwm?
- guix usd service extends syncthing service
- controls pureos install
- make syncthing useable as root. Make user passeable?