Title: Reproducible clean $HOME in OpenBSD using impermanence
Author: Solène
Date: 15 March 2022
Tags: openbsd reproducible nixos unix
Description: Bringing some NixOS tech to OpenBSD

# Introduction

Let me present you my latest project: home-impermanence, under this
name is a reference to the NixOS community project impermanence.  The
name may not be obvious about what it is doing, let me explain.
NixOS wiki about Impermanence, a community module
home-impermanence for OpenBSD
The original goal of impermanence in NixOS is to have a fully
reproducible system mounted on tmpfs where only user-defined files and
directories are hooked into the temporary file system to be persistent
(such as /home, /var/lib and some /etc files for instance).  Why this
is something achievable on NixOS, on OpenBSD side we are far from
having the tooling to go that deep so I wrote home-impermanence that
allows an user to just do that at their $HOME level.

What does it mean exactly? When you start your system, your $HOME
directory will be mounted with an empty memory based file system (using
mfs) and symbolic links to files and directories listed in the
configuration file will be done in your $HOME.  Every time you reboot,
you will have the exact same set of files, extra files created
meanwhile will be lost.  When you hold a $HOME directory for long, you
know you get many directories and files created in various ~/.config or
~/.local or directly as dotfiles in the top level of the home
directory, with impermanence you can get ride of all the noise.

A benefit is that you can run software as if it was their first run, in
some software upgrade you will avoid old settings that would create
troubles, or settings that would disturb a whole class of applications
(like a gtk setting affecting all gtk programs), with impermanence the
user can decide exactly what should remain across reboots or disappear.

# Implementation

My implementation is a Perl script relying on some libraries packaged
on OpenBSD, it will run as root from a rc service and the settings done
in rc.conf.local.  It will read the configuration file from the
persistent directory holding the user data and create symlinks in the
target directory to the files and directories, doing some sanitizing in
the process to prevent listed files to be included in listed
directories which would nest symlinks incorrectly.

I chose Perl because it's a stable language, OpenBSD ships with Perl
and the very few dependencies required were already available in the
ports tree.

The program could easily be ported to Linux, FreeBSD and maybe NetBSD,
the mount_mfs calls could be replaced by a mount_tmpfs and the
directories symlinks could be done with a mount_bind or mount_nullfs
which we don't have on OpenBSD, if someone wants to port my project to
another system I could help adding the required logic.

# How to use

I wrote a complete README file explaining the installation and
configuration process, for full instructions refer to this document and
the man page that ships with home-impermanence.
home-impermanence README
## Installation

Quick method:

```
git clone https://tildegit.org/solene/home-impermanence/
cd home-impermanence
doas make install
doas rcctl enable impermanence
doas rcctl set impermanence flags -u user -d /home/persist/
doas install -d /home/persist/
```

From now, you may want to make things quickly, logout from your user
and run these commands, this will move your user directory and prepare
the mountpoint.

```
mv /home/user /home/persist/user
install -d -o user -g wheel /home/user
```

Now, it's time to configure impermanence before running it.

## Configuration

Reusing the paths from the installation example, the configuration file
should be in /home/persist/user/impermanence.yml , the file must be
using YAML formatting.  Here is my personal configuration file that you
can use as a base.

```configuration yaml file
size: 500m
files:
  - .Xdefaults
  - .Xresources
  - .bashrc
  - .gitconfig
  - .kshrc
  - .profile
  - .xsession
  - .tmux.conf
  - .config/kwalletrc
directories:
  - .claws-mail
  - .config/Thunar
  - .config/asciinema
  - .config/gajim
  - .config/kak
  - .config/keepassxc
  - .config/lagrange
  - .config/mpv
  - .config/musikcube
  - .config/openttd
  - .config/xfce4
  - .config/zim
  - .local/share/cozy
  - .local/share/gajim
  - .local/share/ibus-typing-booster
  - .local/share/kwalletd
  - .mozilla
  - .ssh
  - Documents
  - Downloads
  - Music
  - bin
  - dev
  - notes
  - tmp
```

When you think you are done, start the impermanence rc service with
rcctl start impermanence and log-in.  You should see all the symlinks
you defined in your configuration file.

## Result

Here is the content of my $HOME directory when I use impermanence.

```
solene@daru ~> ls -la
total 104
drwxr-xr-x   8 solene  wheel    1024 Mar 15 12:10 .
drwxr-xr-x  17 root    wheel     512 Mar 14 15:36 ..
-rw-------   1 solene  wheel     165 Mar 15 09:08 .ICEauthority
-rw-------   1 solene  solene     53 Mar 15 09:08 .Xauthority
lrwxr-xr-x   1 root    wheel      34 Mar 15 09:08 .Xdefaults -> /home/permanent//solene/.Xdefaults
lrwxr-xr-x   1 root    wheel      35 Mar 15 09:08 .Xresources -> /home/permanent//solene/.Xresources
-rw-r--r--   1 solene  wheel      48 Mar 15 12:07 .aspell.en.prepl
-rw-r--r--   1 solene  wheel      42 Mar 15 12:07 .aspell.en.pws
lrwxr-xr-x   1 root    wheel      31 Mar 15 09:08 .bashrc -> /home/permanent//solene/.bashrc
drwxr-xr-x   9 solene  wheel     512 Mar 15 12:10 .cache
lrwxr-xr-x   1 root    wheel      35 Mar 15 09:08 .claws-mail -> /home/permanent//solene/.claws-mail
drwx------   8 solene  wheel     512 Mar 15 12:27 .config
drwx------   3 solene  wheel     512 Mar 15 09:08 .dbus
lrwxr-xr-x   1 root    wheel      34 Mar 15 09:08 .gitconfig -> /home/permanent//solene/.gitconfig
drwx------   3 solene  wheel     512 Mar 15 12:32 .gnupg
lrwxr-xr-x   1 root    wheel      30 Mar 15 09:08 .kshrc -> /home/permanent//solene/.kshrc
drwx------   3 solene  wheel     512 Mar 15 09:08 .local
lrwxr-xr-x   1 root    wheel      32 Mar 15 09:08 .mozilla -> /home/permanent//solene/.mozilla
lrwxr-xr-x   1 root    wheel      32 Mar 15 09:08 .profile -> /home/permanent//solene/.profile
lrwxr-xr-x   1 solene  wheel      30 Mar 15 12:10 .sbclrc -> /home/permanent/solene/.sbclrc
drwxr-xr-x   2 solene  wheel     512 Mar 15 09:08 .sndio
lrwxr-xr-x   1 root    wheel      28 Mar 15 09:08 .ssh -> /home/permanent//solene/.ssh
lrwxr-xr-x   1 root    wheel      34 Mar 15 09:08 .tmux.conf -> /home/permanent//solene/.tmux.conf
lrwxr-xr-x   1 root    wheel      33 Mar 15 09:08 .xsession -> /home/permanent//solene/.xsession
-rw-------   1 solene  wheel   25273 Mar 15 13:26 .xsession-errors
lrwxr-xr-x   1 root    wheel      33 Mar 15 09:08 Documents -> /home/permanent//solene/Documents
lrwxr-xr-x   1 root    wheel      33 Mar 15 09:08 Downloads -> /home/permanent//solene/Downloads
lrwxr-xr-x   1 root    wheel      30 Mar 15 09:08 HANGAR -> /home/permanent//solene/HANGAR
lrwxr-xr-x   1 root    wheel      27 Mar 15 09:08 dev -> /home/permanent//solene/dev
lrwxr-xr-x   1 root    wheel      29 Mar 15 09:08 notes -> /home/permanent//solene/notes
lrwxr-xr-x   1 root    wheel      33 Mar 15 09:08 quicklisp -> /home/permanent//solene/quicklisp
lrwxr-xr-x   1 root    wheel      27 Mar 15 09:08 tmp -> /home/permanent//solene/tmp
```

## Rollback

If you want to rollback it's easy, disable impermanence, move
/home/persist/user to /home/user and you are done.

# Conclusion

I really don't want to go back to not using impermanence since I tried
it on NixOS.  I thought implementing it only for $HOME would be good
enough as a start and started thinking about it, made a proof of
concept to see if the symbolic links method was enough to make it work,
and it was!

I hope you will enjoy this as much as I do, feel free to contact me if
you need some help understanding the setup.