Recent changes to this wiki:

Revert "Display the "Ubuntu" tag in the sidebar."
This reverts commit d436199d9039b2181fc6d753689d16110c30706c.
This tag is far too noisy and takes over the whole tag cloud.
diff --git a/sidebar.mdwn b/sidebar.mdwn
index 9935378..5c09f50 100644
--- a/sidebar.mdwn
+++ b/sidebar.mdwn
@@ -23,4 +23,4 @@
 [[Recent Comments|comments]]
 
 [[Tags]]:
-[[!pagestats pages="tags/* and !tags/debian and !tags/catalyst and !tags/mozilla and !tags/nzoss and !tags/sysadmin and !tags/mahara" among="posts/*"]]
+[[!pagestats pages="tags/* and !tags/debian and !tags/catalyst and !tags/mozilla and !tags/ubuntu and !tags/nzoss and !tags/sysadmin and !tags/mahara" among="posts/*"]]

Display the "Ubuntu" tag in the sidebar.
The blog aggregator I originally used it for is long gone.
diff --git a/sidebar.mdwn b/sidebar.mdwn
index 5c09f50..9935378 100644
--- a/sidebar.mdwn
+++ b/sidebar.mdwn
@@ -23,4 +23,4 @@
 [[Recent Comments|comments]]
 
 [[Tags]]:
-[[!pagestats pages="tags/* and !tags/debian and !tags/catalyst and !tags/mozilla and !tags/ubuntu and !tags/nzoss and !tags/sysadmin and !tags/mahara" among="posts/*"]]
+[[!pagestats pages="tags/* and !tags/debian and !tags/catalyst and !tags/mozilla and !tags/nzoss and !tags/sysadmin and !tags/mahara" among="posts/*"]]

Add more posts to the NetworkManager tag.
diff --git a/posts/sharing-wifi-connection-with-network-manager-hotspot.mdwn b/posts/sharing-wifi-connection-with-network-manager-hotspot.mdwn
index 82585ac..b0dcd5f 100644
--- a/posts/sharing-wifi-connection-with-network-manager-hotspot.mdwn
+++ b/posts/sharing-wifi-connection-with-network-manager-hotspot.mdwn
@@ -93,4 +93,4 @@ on my machine's local firewall:
     -A INPUT -d 10.42.0.255 -s 10.42.0.1 -j ACCEPT
     -A INPUT -d 10.42.0.1 -s 10.42.0.0/24 -j ACCEPT
 
-[[!tag nzoss]] [[!tag gnome]] [[!tag debian]] [[!tag wifi]]
+[[!tag gnome]] [[!tag debian]] [[!tag wifi]] [[!tag networkmanager]]
diff --git a/posts/upgrading-from-ubuntu-bionic-to-focal.mdwn b/posts/upgrading-from-ubuntu-bionic-to-focal.mdwn
index b828492..748a802 100644
--- a/posts/upgrading-from-ubuntu-bionic-to-focal.mdwn
+++ b/posts/upgrading-from-ubuntu-bionic-to-focal.mdwn
@@ -99,4 +99,4 @@ and then re-creating all initramfs:
 
 seemed to do the trick.
 
-[[!tag ubuntu]] [[!tag systemd]] [[!tag raid]]
+[[!tag ubuntu]] [[!tag systemd]] [[!tag raid]] [[!tag networkmanager]]

creating tag page tags/networkmanager
diff --git a/tags/networkmanager.mdwn b/tags/networkmanager.mdwn
new file mode 100644
index 0000000..7564660
--- /dev/null
+++ b/tags/networkmanager.mdwn
@@ -0,0 +1,4 @@
+[[!meta title="pages tagged networkmanager"]]
+
+[[!inline pages="tagged(networkmanager)" actions="no" archive="yes"
+feedshow=10]]

Add a new NetworkManager tag.
diff --git a/posts/using-iptables-with-network-manager.mdwn b/posts/using-iptables-with-network-manager.mdwn
index 030c598..43ebf47 100644
--- a/posts/using-iptables-with-network-manager.mdwn
+++ b/posts/using-iptables-with-network-manager.mdwn
@@ -63,4 +63,4 @@ Looking at `/var/log/iptables.log`, you'll be able to confirm that
 it is being called correctly for each network interface as they
 are started.
 
-[[!tag nzoss]] [[!tag debian]] [[!tag iptables]]
+[[!tag debian]] [[!tag iptables]] [[!tag networkmanager]]

Comment moderation
diff --git a/posts/creating-kodi-media-pc-raspberry-pi/comment_4_49f6f33218d532169f4c2e1eb730d5be._comment b/posts/creating-kodi-media-pc-raspberry-pi/comment_4_49f6f33218d532169f4c2e1eb730d5be._comment
new file mode 100644
index 0000000..0d2a9ea
--- /dev/null
+++ b/posts/creating-kodi-media-pc-raspberry-pi/comment_4_49f6f33218d532169f4c2e1eb730d5be._comment
@@ -0,0 +1,10 @@
+[[!comment format=mdwn
+ username="francois@665656f0ba400877c9b12e8fbb086e45aa01f7c0"
+ nickname="francois"
+ subject="Re: Debian for RaspberryPi"
+ date="2021-02-15T19:28:34Z"
+ content="""
+> Why did you install Raspbian, and not the pure Debian build for arm64? https://raspi.debian.net/
+
+Interesting. I was not aware there were images for stock Debian.
+"""]]

Comment moderation
diff --git a/posts/creating-kodi-media-pc-raspberry-pi/comment_3_568b0682ca4c9331a219756063470d75._comment b/posts/creating-kodi-media-pc-raspberry-pi/comment_3_568b0682ca4c9331a219756063470d75._comment
new file mode 100644
index 0000000..466bf2a
--- /dev/null
+++ b/posts/creating-kodi-media-pc-raspberry-pi/comment_3_568b0682ca4c9331a219756063470d75._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ ip="96.23.12.217"
+ claimedauthor="mgregoire"
+ subject="Debian for RaspberryPi"
+ date="2021-02-15T18:42:31Z"
+ content="""
+Why did you install Raspbian, and not the pure Debian build for arm64?  [[https://raspi.debian.net/]]
+"""]]

Comment moderation
diff --git a/posts/creating-kodi-media-pc-raspberry-pi/comment_2_2fabcd228593d113ea70ce386dbfc347._comment b/posts/creating-kodi-media-pc-raspberry-pi/comment_2_2fabcd228593d113ea70ce386dbfc347._comment
new file mode 100644
index 0000000..bdebfce
--- /dev/null
+++ b/posts/creating-kodi-media-pc-raspberry-pi/comment_2_2fabcd228593d113ea70ce386dbfc347._comment
@@ -0,0 +1,12 @@
+[[!comment format=mdwn
+ username="francois@665656f0ba400877c9b12e8fbb086e45aa01f7c0"
+ nickname="francois"
+ subject="Re: The "pi" user"
+ date="2021-02-14T20:33:58Z"
+ content="""
+> This may be a stupid question, but why don't you remove the \"pi\" user altogether?
+
+That's a fair question. The primary reason is that I would need to customize more things since that user is already setup for everything to just work.
+
+Once it's de-fanged (no sudo access, no ssh access, random password), it's probably not very dangerous.
+"""]]

Comment moderation
diff --git a/posts/creating-kodi-media-pc-raspberry-pi/comment_1_e40e667c1684fed5c3ed6791077b80c8._comment b/posts/creating-kodi-media-pc-raspberry-pi/comment_1_e40e667c1684fed5c3ed6791077b80c8._comment
new file mode 100644
index 0000000..f920aed
--- /dev/null
+++ b/posts/creating-kodi-media-pc-raspberry-pi/comment_1_e40e667c1684fed5c3ed6791077b80c8._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ ip="2001:780:107:b::b"
+ claimedauthor="Matthias Urlichs"
+ subject="The "pi" user"
+ date="2021-02-14T11:35:32Z"
+ content="""
+This may be a stupid question, but why don't you remove the \"pi\" user altogether?
+"""]]

creating tag page tags/kodi
diff --git a/tags/kodi.mdwn b/tags/kodi.mdwn
new file mode 100644
index 0000000..e2113c2
--- /dev/null
+++ b/tags/kodi.mdwn
@@ -0,0 +1,4 @@
+[[!meta title="pages tagged kodi"]]
+
+[[!inline pages="tagged(kodi)" actions="no" archive="yes"
+feedshow=10]]

creating tag page tags/raspberrypi
diff --git a/tags/raspberrypi.mdwn b/tags/raspberrypi.mdwn
new file mode 100644
index 0000000..8ba4ac8
--- /dev/null
+++ b/tags/raspberrypi.mdwn
@@ -0,0 +1,4 @@
+[[!meta title="pages tagged raspberrypi"]]
+
+[[!inline pages="tagged(raspberrypi)" actions="no" archive="yes"
+feedshow=10]]

creating tag page tags/nfs
diff --git a/tags/nfs.mdwn b/tags/nfs.mdwn
new file mode 100644
index 0000000..51171e4
--- /dev/null
+++ b/tags/nfs.mdwn
@@ -0,0 +1,4 @@
+[[!meta title="pages tagged nfs"]]
+
+[[!inline pages="tagged(nfs)" actions="no" archive="yes"
+feedshow=10]]

Add Kodi blog post.
diff --git a/posts/creating-kodi-media-pc-raspberry-pi.mdwn b/posts/creating-kodi-media-pc-raspberry-pi.mdwn
new file mode 100644
index 0000000..2c14dc5
--- /dev/null
+++ b/posts/creating-kodi-media-pc-raspberry-pi.mdwn
@@ -0,0 +1,180 @@
+[[!meta title="Creating a Kodia media PC using a Raspberry Pi 4"]]
+[[!meta date="2021-02-13T19:25:00.000-08:00"]]
+[[!meta license="[Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)"]]
+
+Here's how I set up a media PC using [Kodi](https://kodi.tv/) (formerly XMBC) and a
+[Raspberry Pi 4](https://www.raspberrypi.org/products/raspberry-pi-4-model-b/).
+
+## Hardware
+
+The hardware is fairly straightforward, but here's what I ended up getting:
+
+- [Raspberry Pi 4 board](https://www.amazon.ca/gp/product/B089GNJNDS): I
+  went for the maximum amount of RAM (8 GB).
+- [SD-card](https://www.amazon.ca/gp/product/B07DKGP6RB): Since I'm not
+  going to store any media on here, 32 GB is plenty.
+- [HDMI to **micro**-HDMI cable](https://www.amazon.ca/gp/product/B00Z07JYLE)
+- [Case and power supply](https://www.amazon.ca/gp/product/B08CRDKX6T)
+
+You'll probably want to add a [remote
+control](https://kodi.wiki/view/Remote_controls) to that setup. I used an
+old [Streamzap](http://www.streamzap.com/consumer/pc_remote/index.php) I had
+lying around.
+
+## Installing the OS on the SD-card
+
+Plug the SD card into a computer using a USB adapter.
+
+Download the [imager](https://www.raspberrypi.org/software/) and use it to
+install **Raspbian** on the SDcard.
+
+Then you can simply plug the SD card into the Pi and boot.
+
+## System configuration
+
+Using `sudo raspi-config`, I changed the following:
+
+- Set hostname (*System Options*)
+- Wait for network at boot (*System Options*): [needed for NFS](https://raspberrypi.stackexchange.com/a/53147)
+- Disable screen blanking (*Display Options*)
+- Enable ssh (*Interface Options*)
+- Configure locale, timezone and keyboard (*Localisation Options*)
+- Set WiFi country (*Localisation Options*)
+
+Then I [enabled automatic updates](https://raspberrypi.stackexchange.com/a/102350):
+
+    apt install unattended-upgrades anacron
+
+    echo 'Unattended-Upgrade::Origins-Pattern {
+            "origin=Debian,codename=${distro_codename},label=Debian";
+            "origin=Debian,codename=${distro_codename},label=Debian-Security";
+            "origin=Raspbian,codename=${distro_codename},label=Raspbian";
+            "origin=Raspberry Pi Foundation,codename=${distro_codename},label=Raspberry Pi Foundation";
+    };' | sudo tee /etc/apt/apt.conf.d/51unattended-upgrades-raspbian
+
+
+### Headless setup
+
+Should you need to do the setup without a monitor, you can enable ssh by
+inserting the SD card into a computer and then creating an empty file called
+`ssh` in the boot partition.
+
+Plug it into your router and boot it up. Check the IP that it received by
+looking at the active DHCP leases in your router's admin panel.
+
+Then login:
+
+    ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no pi@192.168.1.xxx
+
+using the default password of `raspberry`.
+
+## Hardening
+
+In order to secure the Pi, I followed most of the [steps I usually take when
+setting up a new Linux
+server](https://feeding.cloud.geek.nz/posts/usual-server-setup/).
+
+I created a new user account for admin and ssh access:
+
+    adduser francois
+    addgroup sshuser
+    adduser francois sshuser
+    adduser francois sudo
+
+and changed the `pi` user password to a random one:
+
+    pwgen -sy 32
+    sudo passwd pi
+
+before removing its admin permissions:
+
+    deluser pi adm
+    deluser pi sudo
+    deluser pi dialout
+    deluser pi cdrom
+    deluser pi lpadmin
+
+Finally, I enabled the [Uncomplicated Firewall](https://launchpad.net/ufw)
+by installing its package:
+
+    apt install ufw
+
+and only allowing ssh connections.
+
+After starting ufw using `systemctl start ufw.service`, you can check that
+it's configured as expected using `ufw status`. It should display the
+following:
+
+    Status: active
+    
+    To                         Action      From
+    --                         ------      ----
+    22/tcp                     ALLOW       Anywhere
+    22/tcp (v6)                ALLOW       Anywhere (v6)
+
+## Installing Kodi
+
+Kodi is very straightforward to install since it's now part of the Raspbian repositories:
+
+    apt install kodi
+
+To make it start at boot/login, while still being able to exit and use other
+apps if needed:
+
+    cp /etc/xdg/lxsession/LXDE-pi/autostart ~/.config/lxsession/LXDE-pi/
+    echo "@kodi" >> ~/.config/lxsession/LXDE-pi/autostart
+
+## Network File System
+
+In order to avoid having to have all media storage connected directly to the
+Pi via USB, I setup an
+[NFS](https://en.wikipedia.org/wiki/Network_File_System) share over my local
+network.
+
+First, give static IP allocations to the server and the Pi in your DHCP
+server, then add it to the `/etc/hosts` file on your NFS server:
+
+    192.168.1.3    pi
+
+Install the NFS server package:
+
+    apt instal nfs-kernel-server
+
+Setup the directories to share in `/etc/exports`:
+
+    /pub/movies    pi(ro,insecure,all_squash,subtree_check)
+    /pub/tv_shows  pi(ro,insecure,all_squash,subtree_check)
+
+Open the right ports on your firewall by putting this in `/etc/network/iptables.up.rules`:
+
+    -A INPUT -s 192.168.1.3 -p udp -j ACCEPT
+    -A INPUT -s 192.168.1.0/24 -p tcp --dport 111 -j ACCEPT
+    -A INPUT -s 192.168.1.0/24 -p udp --dport 111 -j ACCEPT
+    -A INPUT -s 192.168.1.0/24 -p udp --dport 123 -j ACCEPT
+    -A INPUT -s 192.168.1.0/24 -p tcp --dport 600:1124 -j ACCEPT
+    -A INPUT -s 192.168.1.0/24 -p udp --dport 600:1124 -j ACCEPT
+    -A INPUT -s 192.168.1.0/24 -p tcp --dport 2049 -j ACCEPT
+    -A INPUT -s 192.168.1.0/24 -p udp --dport 2049 -j ACCEPT
+
+Finally, apply all of these changes:
+
+    iptables-apply
+    systemctl restart nfs-kernel-server.service
+
+On the Pi, put the server's static IP in `/etc/hosts`:
+
+    192.168.1.2    fileserver
+
+and this in `/etc/fstab`:
+
+    fileserver:/data/movies  /kodi/movies  nfs  ro,bg,hard,noatime,async,nolock  0  0
+    fileserver:/data/tv      /kodi/tv      nfs  ro,bg,hard,noatime,async,nolock  0  0
+
+Then create the mount points and mount everything:
+
+    mkdir -p /kodi/movies
+    mkdir /kodi/tv
+    mount /kodi/movies
+    mount /kodi/tv
+
+[[!tag raspberrypi]] [[!tag kodi]] [[!tag debian]] [[!tag nfs]]
diff --git a/posts/upgrade-dmr-hotspot-pistar-4_1.mdwn b/posts/upgrade-dmr-hotspot-pistar-4_1.mdwn
index 4cd908f..ef746a5 100644
--- a/posts/upgrade-dmr-hotspot-pistar-4_1.mdwn
+++ b/posts/upgrade-dmr-hotspot-pistar-4_1.mdwn
@@ -96,4 +96,4 @@ As long as you didn't reuse the original SD card for this upgrade, rolling
 back to version 3.4.17 simply involves shutting down the pi and then
 swapping the new SD card for the old one and then starting it up again.
 
-[[!tag dmr]] [[!tag ham]] [[!tag pistar]]
+[[!tag dmr]] [[!tag ham]] [[!tag pistar]] [[!tag raspberrypi]]

Purge sendmail too in case that was installed instead of exim4.
For some reason, I saw sendmail installed on Raspbian instead of exim4.
diff --git a/posts/usual-server-setup.mdwn b/posts/usual-server-setup.mdwn
index 20bfe4b..73cb69b 100644
--- a/posts/usual-server-setup.mdwn
+++ b/posts/usual-server-setup.mdwn
@@ -322,7 +322,7 @@ and then run:
 # Mail
 
     apt install postfix libsasl2-modules
-    apt purge exim4-base exim4-daemon-light exim4-config
+    apt purge exim4-base exim4-daemon-light exim4-config sendmail sendmail-bin sendmail-base sendmail-cf
 
 Configuring mail properly is tricky but the following has worked for me.
 

Add missing apt-file call.
diff --git a/posts/usual-server-setup.mdwn b/posts/usual-server-setup.mdwn
index 632b7ae..20bfe4b 100644
--- a/posts/usual-server-setup.mdwn
+++ b/posts/usual-server-setup.mdwn
@@ -291,6 +291,7 @@ Also, [`command-not-found` won't work until you update the apt cache](https://bu
 
     apt update
     update-command-not-found
+    apt-file update
 
 # Apache configuration
 

Set the default editor as soon as possible.
diff --git a/posts/usual-server-setup.mdwn b/posts/usual-server-setup.mdwn
index a8412ea..632b7ae 100644
--- a/posts/usual-server-setup.mdwn
+++ b/posts/usual-server-setup.mdwn
@@ -26,6 +26,19 @@ Then I check the hard drives using:
 
     apt install etckeeper git sudo vim
 
+Since I use vim for all of my configuration file editing, I make it the
+default editor:
+
+    update-alternatives --config editor
+
+and I turn on syntax highlighting and visual beeping globally by adding the
+following to `/etc/vim/vimrc.local`:
+
+    syntax on
+    set background=dark
+    set visualbell
+    set nomodeline
+
 To keep track of the configuration changes I make in `/etc/`, I use etckeeper
 to keep that directory in a git repository and make the following changes to
 the default `/etc/etckeeper/etckeeper.conf`:
@@ -59,19 +72,6 @@ default debconf level to medium:
 
     dpkg-reconfigure debconf
 
-Since I use vim for all of my configuration file editing, I make it the
-default editor:
-
-    update-alternatives --config editor
-
-and I turn on syntax highlighting and visual beeping globally by adding the
-following to `/etc/vim/vimrc.local`:
-
-    syntax on
-    set background=dark
-    set visualbell
-    set nomodeline
-
 # ssh
 
     apt install openssh-server mosh fail2ban

creating tag page tags/wifi
diff --git a/tags/wifi.mdwn b/tags/wifi.mdwn
new file mode 100644
index 0000000..98f2e25
--- /dev/null
+++ b/tags/wifi.mdwn
@@ -0,0 +1,4 @@
+[[!meta title="pages tagged wifi"]]
+
+[[!inline pages="tagged(wifi)" actions="no" archive="yes"
+feedshow=10]]

Add a new wifi tag.
diff --git a/posts/encoding-wifi-access-point-passwords-qr-code.mdwn b/posts/encoding-wifi-access-point-passwords-qr-code.mdwn
index bffca9d..dbc91d1 100644
--- a/posts/encoding-wifi-access-point-passwords-qr-code.mdwn
+++ b/posts/encoding-wifi-access-point-passwords-qr-code.mdwn
@@ -53,4 +53,4 @@ If you can't do this locally for some reason, there is also an [in-browser
 QR code generator](https://qifi.org/) with [source code
 available](https://github.com/evgeni/qifi).
 
-[[!tag ios]] [[!tag android]] [[!tag security]] [[!tag debian]] [[!tag nzoss]]
+[[!tag ios]] [[!tag android]] [[!tag security]] [[!tag debian]] [[!tag nzoss]] [[!tag wifi]]
diff --git a/posts/fixing-turris-omnia-wifi-quality.mdwn b/posts/fixing-turris-omnia-wifi-quality.mdwn
index c4a9b91..70219ad 100644
--- a/posts/fixing-turris-omnia-wifi-quality.mdwn
+++ b/posts/fixing-turris-omnia-wifi-quality.mdwn
@@ -74,4 +74,4 @@ as I passed my advanced [amateur radio license
 exam](https://launchpad.net/canadian-ham-exam). I guess that was a good way
 to put the course material into practice!
 
-[[!tag nzoss]] [[!tag turris]]
+[[!tag nzoss]] [[!tag turris]] [[!tag wifi]]
diff --git a/posts/setting-wifi-regulatory-domain-linux-openwrt.mdwn b/posts/setting-wifi-regulatory-domain-linux-openwrt.mdwn
index e65b8ad..8c9f2bb 100644
--- a/posts/setting-wifi-regulatory-domain-linux-openwrt.mdwn
+++ b/posts/setting-wifi-regulatory-domain-linux-openwrt.mdwn
@@ -59,4 +59,4 @@ if you have the hardware, otherwise
 [WiFi Analyzer](https://f-droid.org/repository/browse/?fdid=com.vrem.wifianalyzer)
 is a good choice on Android.
 
-[[!tag debian]] [[!tag openwrt]] [[!tag gargoyle]]
+[[!tag debian]] [[!tag openwrt]] [[!tag gargoyle]] [[!tag wifi]]
diff --git a/posts/sharing-wifi-connection-with-network-manager-hotspot.mdwn b/posts/sharing-wifi-connection-with-network-manager-hotspot.mdwn
index e307e52..82585ac 100644
--- a/posts/sharing-wifi-connection-with-network-manager-hotspot.mdwn
+++ b/posts/sharing-wifi-connection-with-network-manager-hotspot.mdwn
@@ -93,4 +93,4 @@ on my machine's local firewall:
     -A INPUT -d 10.42.0.255 -s 10.42.0.1 -j ACCEPT
     -A INPUT -d 10.42.0.1 -s 10.42.0.0/24 -j ACCEPT
 
-[[!tag nzoss]] [[!tag gnome]] [[!tag debian]]
+[[!tag nzoss]] [[!tag gnome]] [[!tag debian]] [[!tag wifi]]
diff --git a/posts/using-all-5ghz-wifi-frequencies-in-gargoyle-router.mdwn b/posts/using-all-5ghz-wifi-frequencies-in-gargoyle-router.mdwn
index c65cae1..5590f51 100644
--- a/posts/using-all-5ghz-wifi-frequencies-in-gargoyle-router.mdwn
+++ b/posts/using-all-5ghz-wifi-frequencies-in-gargoyle-router.mdwn
@@ -67,4 +67,4 @@ If you are interested, there is a lot more information about how all of this
 works in the
 [kernel documentation for the wireless stack](https://wireless.wiki.kernel.org/en/developers/regulatory/processing_rules#country_definition).
 
-[[!tag debian]] [[!tag nzoss]] [[!tag openwrt]] [[!tag gargoyle]]
+[[!tag debian]] [[!tag nzoss]] [[!tag openwrt]] [[!tag gargoyle]] [[!tag wifi]]
diff --git a/posts/using-gogo-wifi-linux.mdwn b/posts/using-gogo-wifi-linux.mdwn
index 8dfe92f..4fdb969 100644
--- a/posts/using-gogo-wifi-linux.mdwn
+++ b/posts/using-gogo-wifi-linux.mdwn
@@ -40,4 +40,4 @@ You may want to create an email template for this so that you can fire off a
 quick email to them as soon as you connect. I will probably write a script
 for it next time I use this service.
 
-[[!tag debian]] [[!tag firefox]]
+[[!tag debian]] [[!tag firefox]] [[!tag wifi]]

Increased backup frequency.
diff --git a/posts/automated-mythtv-maintenance-tasks.mdwn b/posts/automated-mythtv-maintenance-tasks.mdwn
index 43cce7c..3ae6566 100644
--- a/posts/automated-mythtv-maintenance-tasks.mdwn
+++ b/posts/automated-mythtv-maintenance-tasks.mdwn
@@ -8,11 +8,12 @@ server.
 
 The first part performs a [database backup](https://www.mythtv.org/wiki/User_Manual:Periodic_Maintenance#The_database):
 
-    5 1 * * *  mythtv  /usr/share/mythtv/mythconverg_backup.pl
+    5 1,13 * * *  mythtv  /usr/share/mythtv/mythconverg_backup.pl
 
 which I previously configured by putting the following in `/home/mythtv/.mythtv/backuprc`:
 
     DBBackupDirectory=/var/backups/mythtv
+    rotate=15
 
 and creating a new directory for it:
 

Fix typo.
diff --git a/posts/recovering-from-corrupt-mariadb-index-page.mdwn b/posts/recovering-from-corrupt-mariadb-index-page.mdwn
index 650c89f..ec909ce 100644
--- a/posts/recovering-from-corrupt-mariadb-index-page.mdwn
+++ b/posts/recovering-from-corrupt-mariadb-index-page.mdwn
@@ -61,7 +61,7 @@ restoring from backup.
 
 First of all, I shut down MythTV as `root`:
 
-    kilall mythfrontend
+    killall mythfrontend
     systemctl stop mythtv-status.service
     systemctl stop mythtv-backend.service
 

Comment moderation
diff --git a/posts/fixing-turris-omnia-wifi-quality/comment_1_bceb9a70d0604063d2ce138a668e309a._comment b/posts/fixing-turris-omnia-wifi-quality/comment_1_bceb9a70d0604063d2ce138a668e309a._comment
new file mode 100644
index 0000000..3a3cc16
--- /dev/null
+++ b/posts/fixing-turris-omnia-wifi-quality/comment_1_bceb9a70d0604063d2ce138a668e309a._comment
@@ -0,0 +1,12 @@
+[[!comment format=mdwn
+ ip="2a01:c22:886a:1200:ccf4:eec2:481:46ae"
+ claimedauthor="Till"
+ subject="Thanks a lot!"
+ date="2021-01-31T00:53:55Z"
+ content="""
+Thank you so much for sharing your findings about the swapped pigtail cables in your Turris Omnia. I bought a TO just recently and set it up tonight. I noticed that I got signal levels of just -79 dBm in 2.4 GHz even when I was just 1 m away from the device. At the same time & place, I received 5 GHz signal with about -34 dBm.
+
+Without your post and detailed description it would probably have taken me days to find out whats going on...
+
+
+"""]]
diff --git a/posts/ipv6-and-openvpn-on-linode/comment_1_dae3978e13f51bdd6654e5cfafb7d53e._comment b/posts/ipv6-and-openvpn-on-linode/comment_1_dae3978e13f51bdd6654e5cfafb7d53e._comment
new file mode 100644
index 0000000..15ca82a
--- /dev/null
+++ b/posts/ipv6-and-openvpn-on-linode/comment_1_dae3978e13f51bdd6654e5cfafb7d53e._comment
@@ -0,0 +1,15 @@
+[[!comment format=mdwn
+ ip="2406:1e00:b910:7704::1006"
+ subject="Multiple Prefixes"
+ date="2021-02-01T00:48:09Z"
+ content="""
+This works well, and clients will be assigned an address in the prefix.
+
+However, some applications want clients to talk to each other through the tunnel.
+
+For IPv4, they could just use each other's 10.8.0.x addresses, but with this configuration, the prefix is not a private value and therefore subject to (unlikely but possible) change.
+
+A solution is to use IPv6 ULAs, but then clients can't reach the IPv6 internet without prefix translation. In reality, we want to be able to give each client an IPv4 10.8.0.x address, an IPv6 GUA, and an IPv6 ULA. 
+
+How do we extend this configuration to achieve that? 
+"""]]

Comment moderation
diff --git a/posts/programming-anytone-d878uv-on-linux-using-windows10-and-virtualbox/comment_9_b06ee049460f9a4d182a670523892490._comment b/posts/programming-anytone-d878uv-on-linux-using-windows10-and-virtualbox/comment_9_b06ee049460f9a4d182a670523892490._comment
new file mode 100644
index 0000000..d58a7f4
--- /dev/null
+++ b/posts/programming-anytone-d878uv-on-linux-using-windows10-and-virtualbox/comment_9_b06ee049460f9a4d182a670523892490._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ ip="67.61.210.79"
+ claimedauthor="Ben"
+ subject="USB/Com Port"
+ date="2021-01-25T03:36:43Z"
+ content="""
+Did you do anything to configure the USB or COM port? I've tried both but couldn't read from the device. 
+"""]]

Fix filename.
diff --git a/posts/programming-dmr-radio-with-cps/ve7rag_bc2.png b/posts/programming-dmr-radio-with-cps/ve7rag-bc2.png
similarity index 100%
rename from posts/programming-dmr-radio-with-cps/ve7rag_bc2.png
rename to posts/programming-dmr-radio-with-cps/ve7rag-bc2.png

Add DMR post on AnyTone.
diff --git a/posts/programming-dmr-radio-with-cps.mdwn b/posts/programming-dmr-radio-with-cps.mdwn
new file mode 100644
index 0000000..d23cf8d
--- /dev/null
+++ b/posts/programming-dmr-radio-with-cps.mdwn
@@ -0,0 +1,184 @@
+[[!meta title="Programming a DMR radio with its CPS"]]
+[[!meta date="2021-01-04T23:40:00.000-08:00"]]
+[[!meta license="[Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)"]]
+
+Here are some notes I took around programming my [AnyTone AT-D878UV
+radio](https://www.bridgecomsystems.com/collections/amateur-handheld-radios/products/anytone-at-d878uv-dual-band-dmr-handheld-radio-w-gps-programming-cable)
+to operate on [DMR](https://en.wikipedia.org/wiki/Digital_mobile_radio)
+using the [CPS
+software](https://feeding.cloud.geek.nz/posts/programming-anytone-d878uv-on-linux-using-windows10-and-virtualbox/)
+that comes with it.
+
+Note that you can always [tune in to a VFO channel by
+hand](https://www.youtube.com/watch?v=VHapE2wqSMI) if you haven't had time
+to add it to your codeplug yet.
+
+# DMR terminology
+
+First of all, the [terminology of
+DMR](https://www.jeffreykopcak.com/2017/05/10/dmr-in-amateur-radio-terminology/)
+is quite different from that of the regular analog FM world.
+
+Here are the basic terms:
+
+- **Frequency**: *same meaning as in the analog world*
+- **Repeater**: *same meaning as in the analog world*
+- **Timeslot**: Each frequency is split into two timeslots (1 and 2) and what
+  that means that there can be two simultaneous transmissions on each frequency.
+- **Color code**: This is the digital equivalent of a CTCSS tone (sometimes
+  called privacy tone) in that using the incorrect code means that you will
+  tie up one of the timeslots on the frequency, but nobody else will hear
+  you. These are [not actually named after
+  colors](https://amateurradionotes.com/dmr.htm#colorcodes), but are instead
+  just numerical IDs from 0 to 15.
+
+There are two different identification mechanisms (both are required):
+
+- **Callsign**: This is the same identifier issued to you by your country's
+  amateur radio authority. Mine is [VA7GPL](https://www.qrz.com/lookup?tquery=VA7GPL&mode=callsign).
+- **Radio ID**: This is a unique numerical ID tied to your callsign which
+  you must [register for](https://radioid.net/register) ahead of time and
+  program into your radio. Mine is
+  [3027260](https://database.radioid.net/database/view?id=3027260#!).
+
+The following is where this digital mode becomes most interesting:
+
+- **Talkgroup**: a "chat room" where everything you say will be heard by
+  anybody listening to that talkgroup
+- **Network**: a group of repeaters connected together over the Internet
+  (typically) and sharing a common list of talkgroups
+- **Hotspot**: a personal simplex device which allows you to connect to a
+  network with your handheld and access all of the talkgroups available on
+  that network
+
+The most [active](https://app.brandmeisteractivity.live/) network these days
+is [Brandmeister](https://brandmeister.network/), but there are several others.
+
+- **Access**: This can either be *Always on* which means that a talkgroup
+  will be permanently broadcasting on a timeslot and frequency, or *PTT*
+  which means a talkgroup will not be broadcast until it is first "woken up"
+  by pressing the push-to-talk button and then will broadcast for a certain
+  amount of time before going to sleep again.
+- **Channel**: As in the analog world, this is what you select on your radio
+  when you want to talk to a group of people. In the digital world however, it
+  is tied not only to a frequency (and timeslot) and tone (color code), but
+  also to a specific talkgroup.
+
+Ultimately what you want to do when you program your radio is to find the
+talkgroups you are interested in (from the list offered by your local
+repeater) and then assign them to specific channel numbers on your radio.
+More on that later.
+
+# Callsign and Radio IDs
+
+Before we get to talkgroups, let's set your callsign and [Radio
+ID](https://radioid.net/database/search):
+
+![](/posts/programming-dmr-radio-with-cps/radioid.png)
+
+Then you need to download the latest list of Radio IDs so that your radio
+can display people's names and callsigns instead of just their numerical IDs.
+
+One approach is to only [download the list of users who recently
+talked](https://brandmeister.network/?page=contactsexport) on talkgroups you
+are interested in. For example, I used to download the contacts for the
+following talkgroups:
+```
+91,93,95,913,937,3026,3027,302,30271,30272,530,5301,5302,5303,5304,3100,3153,31330
+```
+but these days, what I normally do is to just download the [entire worldwide
+database](https://www.radioid.net/database/dumps) (`user.csv`) since my
+radio still has enough storage (200k entries) for it.
+
+In order for the `user.csv` file to work with the AnyTone CPS, it needs to
+have particular columns and use the DOS end-of-line characters (`apt install
+dos2unix` if you want to do it manually). I wrote a
+[script](https://github.com/fmarier/user-scripts/blob/master/dmr-users) to
+do all of the work for me.
+
+If you use [dmrconfig](https://github.com/sergev/dmrconfig) to program this
+radio instead, then the conversion is unnecessary. The `user.csv` file can
+be used directly, however it will be truncated due to an [incorrect limit
+hard-coded in the software](https://github.com/sergev/dmrconfig/issues/50).
+
+# Talkgroups
+
+Next, you need to pick the talkgroups you would like to allocate to specific
+channels on your radio.
+
+Start by looking at the documentation for your local repeaters (e.g.
+[VE7RAG](http://www.bcfmca.bc.ca/dmr.php) and
+[VE7NWR](http://www.nwarc.org/cms/?q=node/330) in the Vancouver area).
+
+In addition to telling you the listen and transmit *frequencies* of the
+repeater (again, this works the same way as with analog FM), these will tell
+you which *talkgroups* are available and what *timeslots* and *color codes*
+they have been set to. It will also tell you the type of *access* for each
+of these talkgroups.
+
+This is how I programmed a channel:
+
+![](/posts/programming-dmr-radio-with-cps/ve7rag-bc2.png)
+
+and a talkgroup on the VE7RAG repeater in my radio:
+
+![](/posts/programming-dmr-radio-with-cps/tg-bc2.png)
+
+If you don't have a local repeater with DMR capability, or if you want to
+access talkgroups available on a different network, then you will need to
+get a DMR hotspot such as one that's compatible with the
+[Pi-Star](https://www.pistar.uk/) software.
+
+This is an excerpt from the programming I created for the talkgroups
+I made available through my hotspot:
+
+![](/posts/programming-dmr-radio-with-cps/brandmeister-nz-national.png)
+![](/posts/programming-dmr-radio-with-cps/tg-zl-national.png)
+
+One of the unfortunate limitations of the CPS software for the AnyTone 878
+is that talkgroup numbers are globally unique identifiers. This means that
+if TG1234 (hypothetical example) is *Ragchew 3000* on DMR-MARC but
+*Iceland-wide chat* on Brandmeister, then you can't have two copies of it
+with different names. The solution I found for this was to give that
+talkgroup the name "TG1234" instead of "Ragchew3k" or "Iceland". I use a
+more memorable name for non-conflicting talkgroups, but for the problematic
+ones, I simply repeat the talkgroup number.
+
+# Simplex
+
+Talkgroups are not required to operate on DMR. Just like analog FM, you can
+talk to another person point-to-point using a simplex channel.
+
+The convention for all simplex channels is the following:
+
+- Talkgroup: `99`
+- Color code: `1`
+- Timeslot: `1`
+- Admit criteria: `Always`
+- In Call Criteria: `TX` or `Always`
+
+![](/posts/programming-dmr-radio-with-cps/tg-simplex.png)
+
+After talking to the [British Columbia Amateur Radio Coordination
+Council](https://bcarcc.org/), I found that the following frequency ranges are
+most suitable for DMR simplex:
+
+- 145.710-145.790 MHz (simplex digital transmissions)
+- 446.000-446.975 MHz (all simplex modes)
+
+The [VECTOR
+list](https://docs.google.com/spreadsheets/d/1XNRLnQWY1AJiQQy1umiEBTf4oBMyhrjOc0CIhlLm4pI/edit#gid=0)
+identifies two frequencies in particular:
+
+- 446.075 MHz
+- 446.500 MHz
+
+![](/posts/programming-dmr-radio-with-cps/vector-dmr-simplex2.png)
+
+# Learn more
+
+If you'd like to learn more about DMR, I would suggest you start with [this
+excellent guide](http://www.k4usd.org/guide.pdf) (also [mirrored
+here](https://www.raqi.ca/~ve2rae/dmr/Amateur_Radio_Guide_to_DMR.pdf)).
+
+[[!tag ham]] [[!tag anytone]]
diff --git a/posts/programming-dmr-radio-with-cps/brandmeister-nz-national.png b/posts/programming-dmr-radio-with-cps/brandmeister-nz-national.png
new file mode 100644
index 0000000..f00db55
Binary files /dev/null and b/posts/programming-dmr-radio-with-cps/brandmeister-nz-national.png differ
diff --git a/posts/programming-dmr-radio-with-cps/radioid.png b/posts/programming-dmr-radio-with-cps/radioid.png
new file mode 100644
index 0000000..69cee8f
Binary files /dev/null and b/posts/programming-dmr-radio-with-cps/radioid.png differ
diff --git a/posts/programming-dmr-radio-with-cps/tg-bc2.png b/posts/programming-dmr-radio-with-cps/tg-bc2.png
new file mode 100644

(Diff truncated)
calendar update
diff --git a/archives/2021.mdwn b/archives/2021.mdwn
new file mode 100644
index 0000000..325ac2d
--- /dev/null
+++ b/archives/2021.mdwn
@@ -0,0 +1 @@
+[[!calendar type=year year=2021 pages="page(posts/*) and !*/Discussion"]]
diff --git a/archives/2021/01.mdwn b/archives/2021/01.mdwn
new file mode 100644
index 0000000..9b5bb5d
--- /dev/null
+++ b/archives/2021/01.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=01 year=2021 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(01) and creation_year(2021) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/archives/2021/02.mdwn b/archives/2021/02.mdwn
new file mode 100644
index 0000000..3903fe8
--- /dev/null
+++ b/archives/2021/02.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=02 year=2021 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(02) and creation_year(2021) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/archives/2021/03.mdwn b/archives/2021/03.mdwn
new file mode 100644
index 0000000..0c7f1e7
--- /dev/null
+++ b/archives/2021/03.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=03 year=2021 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(03) and creation_year(2021) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/archives/2021/04.mdwn b/archives/2021/04.mdwn
new file mode 100644
index 0000000..5fb95cb
--- /dev/null
+++ b/archives/2021/04.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=04 year=2021 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(04) and creation_year(2021) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/archives/2021/05.mdwn b/archives/2021/05.mdwn
new file mode 100644
index 0000000..ec47bca
--- /dev/null
+++ b/archives/2021/05.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=05 year=2021 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(05) and creation_year(2021) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/archives/2021/06.mdwn b/archives/2021/06.mdwn
new file mode 100644
index 0000000..8af1077
--- /dev/null
+++ b/archives/2021/06.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=06 year=2021 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(06) and creation_year(2021) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/archives/2021/07.mdwn b/archives/2021/07.mdwn
new file mode 100644
index 0000000..54ccb3d
--- /dev/null
+++ b/archives/2021/07.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=07 year=2021 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(07) and creation_year(2021) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/archives/2021/08.mdwn b/archives/2021/08.mdwn
new file mode 100644
index 0000000..d6242ff
--- /dev/null
+++ b/archives/2021/08.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=08 year=2021 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(08) and creation_year(2021) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/archives/2021/09.mdwn b/archives/2021/09.mdwn
new file mode 100644
index 0000000..a8d92cb
--- /dev/null
+++ b/archives/2021/09.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=09 year=2021 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(09) and creation_year(2021) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/archives/2021/10.mdwn b/archives/2021/10.mdwn
new file mode 100644
index 0000000..2514206
--- /dev/null
+++ b/archives/2021/10.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=10 year=2021 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(10) and creation_year(2021) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/archives/2021/11.mdwn b/archives/2021/11.mdwn
new file mode 100644
index 0000000..9c5f9fd
--- /dev/null
+++ b/archives/2021/11.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=11 year=2021 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(11) and creation_year(2021) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/archives/2021/12.mdwn b/archives/2021/12.mdwn
new file mode 100644
index 0000000..4132294
--- /dev/null
+++ b/archives/2021/12.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=12 year=2021 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(12) and creation_year(2021) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]

Replace dead link with archive.org copy.
diff --git a/posts/looking-back-on-starting-libravatar.mdwn b/posts/looking-back-on-starting-libravatar.mdwn
index fe6a5e7..7641386 100644
--- a/posts/looking-back-on-starting-libravatar.mdwn
+++ b/posts/looking-back-on-starting-libravatar.mdwn
@@ -110,7 +110,7 @@ When I started the project in 2011, I had a few goals:
   [DebConf](http://penta.debconf.org/dc10_schedule///////events/682.en.html)
   [twice](https://summit.debconf.org/debconf14/meeting/16/outsourcing-your-webapp-maintenance-to-debian/),
   [linux.conf.au](https://www.youtube.com/watch?v=ufkYjt9HV64) and
-  [OSCON](https://conferences.oreilly.com/oscon/oscon2011/public/schedule/detail/18773).
+  [OSCON](https://web.archive.org/web/20161005202936/http://conferences.oreilly.com/oscon/oscon2011/public/schedule/detail/18773).
 
 - Career-wise, I wanted to move beyond PHP development, which I successfully
   achieved by working for a [new client](https://logger.paua.org.nz/) while

Fix typo.
diff --git a/posts/looking-back-on-starting-libravatar.mdwn b/posts/looking-back-on-starting-libravatar.mdwn
index cb10af1..fe6a5e7 100644
--- a/posts/looking-back-on-starting-libravatar.mdwn
+++ b/posts/looking-back-on-starting-libravatar.mdwn
@@ -161,7 +161,7 @@ project over the years:
 - The Wellington Perl Mongers for their invaluable feedback on an early prototype.
 - The `#equifoss` group for their ongoing suppport and numerous ideas.
 - Nigel Babu and Melissa Draper for producing the first (and only) project
-  stikers, as well as Chris Cormack for spreading so effectively.
+  stickers, as well as Chris Cormack for spreading them so effectively.
 - Adolfo Jayme, Alfredo Hernández, Anthony Harrington, Asier Iturralde
   Sarasola, Besnik, Beto1917, Daniel Neis, Eduardo Battaglia, Fernando P
   Silveira, Gabriele Castagneti, Heimen Stoffels, Iñaki Arenaza, Jakob

Remove blog that's no longer updated.
diff --git a/posts/list-planet-linux-australia-blogs/linuxaustralia.opml b/posts/list-planet-linux-australia-blogs/linuxaustralia.opml
index 715ed99..72ad571 100644
--- a/posts/list-planet-linux-australia-blogs/linuxaustralia.opml
+++ b/posts/list-planet-linux-australia-blogs/linuxaustralia.opml
@@ -19,7 +19,6 @@
    <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://www.csamuel.org/feed" htmlUrl="https://www.csamuel.org" fetchInterval="30" id="1432669172" description="Computers, science, archaeology and other random burblings" title="Chris Samuel" maxArticleAge="60" comment="" copyright="" type="rss" text="Chris Samuel"/>
    <outline maxArticleAge="60" copyright="" xmlUrl="https://blog.christophersmart.com/feed/" fetchInterval="30" useCustomFetchInterval="false" logoHeight="32" description="Fortiter Et Recte" version="RSS" text="Chris Smart" logoWidth="32" logoUrl="https://blog.christophersmart.com/wp-content/uploads/2008/08/cropped-csmart-32x32.jpg" archiveMode="globalDefault" maxArticleNumber="1000" comment="" type="rss" title="Chris Smart" htmlUrl="https://blog.christophersmart.com" id="1309301898"/>
    <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://chrisjrn.com/feed.xml" htmlUrl="https://chrisjrn.com/" fetchInterval="30" id="2199337231" description="Christopher Neugebauer&amp;apos;s site" title="Chris Neugebauer" maxArticleAge="60" comment="" copyright="" type="rss" text="Chris Neugebauer"/>
-   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="http://chris.yeoh.info/feed/" htmlUrl="http://chris.yeoh.info" fetchInterval="30" id="2446739425" description="" title="Chris Yeoh" maxArticleAge="60" comment="" copyright="" type="rss" text="Chris Yeoh"/>
    <outline maxArticleAge="60" copyright="" xmlUrl="https://clintonroy.wordpress.com/feed/" fetchInterval="30" useCustomFetchInterval="false" logoHeight="31" description="An Open Source Software Engineer" version="RSS" text="Clinton Roy" logoWidth="88" logoUrl="https://s0.wp.com/i/buttonw-com.png" archiveMode="globalDefault" maxArticleNumber="1000" comment="" type="rss" title="Clinton Roy" htmlUrl="https://clintonroy.wordpress.com" id="165453513"/>
    <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://feeds.feedburner.com/ColinCharlesLA" htmlUrl="http://www.bytebot.net/blog" fetchInterval="30" id="306929162" description="&amp;quot;Imagine. Create. Execute.&amp;quot; Rough notes on technology, media, travel, business, work, and my other interests." title="Colin Charles" maxArticleAge="60" comment="" copyright="" type="rss" text="Colin Charles"/>
    <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="http://blog.kfish.org/feeds/posts/default" htmlUrl="http://blog.kfish.org/" fetchInterval="30" id="2458778784" description="&lt;strong>Making free software for embedded/mobile multimedia, web video and computer music; in C and Haskell, using algebra, type theory and git!&lt;/strong>" title="Conrad Parker" maxArticleAge="60" comment="" copyright="" type="rss" text="Conrad Parker"/>

Add Planet LA post.
diff --git a/posts/list-planet-linux-australia-blogs.mdwn b/posts/list-planet-linux-australia-blogs.mdwn
new file mode 100644
index 0000000..bf4c2a9
--- /dev/null
+++ b/posts/list-planet-linux-australia-blogs.mdwn
@@ -0,0 +1,81 @@
+[[!meta title="List of Planet Linux Australia blogs"]]
+[[!meta date="2020-12-10T23:30:00.000-08:00"]]
+[[!meta license="[Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)"]]
+
+I've been following [Planet Linux Australia](https://planet.linux.org.au/)
+for many years and discovered many interesting FOSS blogs through it. I was
+sad to see that it got [shut down a few weeks
+ago](https://linux.org.au/saying-farewell-to-planet-linux-australia/) and so
+I decided to manually add all of the feeds to my RSS reader to avoid missing
+posts from people I have been indirectly following for years.
+
+Since all feeds have been removed from the site, I recovered the list of
+blogs available from an [old copy of the
+site](https://web.archive.org/web/20200228054023/https://planet.linux.org.au/)
+preserved by the [Internet Archive](https://archive.org).
+
+Here is the [resulting `.opml`
+file](/posts/list-planet-linux-australia-blogs/linuxaustralia.opml) if you'd
+like to subscribe.
+
+## Changes
+
+Once I had the full list, I removed all blogs that are gone, empty or broken
+(e.g. domain not resolving, returning a 404, various database or server
+errors).
+
+I updated the URLs of a few blogs which had moved but hadn't updated their
+feeds on the planet. I also updated the name of a blogger who was still
+listed under a previous last name.
+
+Finally, I removed LA-specific tags from feeds since these are unlikely to
+be used again.
+
+## Work-arounds
+
+The following LiveJournal feeds didn't work in my RSS reader but opened fine
+in a browser:
+
+- <https://ninjafoo.livejournal.com/data/rss>
+- <https://certifiedwaif.livejournal.com/data/rss>
+- <https://tau-iota-mu-c.livejournal.com/data/rss>
+- <https://lathiat.livejournal.com/data/rss>
+
+However since none of them have them updated in the last 7 years, I just
+left them out.
+
+A couple appear to be impossible to [fetch over
+Tor](https://feeding.cloud.geek.nz/posts/things-that-work-well-with-tor/),
+presumably due to a [Cloudflare
+setting](https://community.cloudflare.com/t/block-tor-and-vpns-from-accessing-my-website/134730/2):
+
+- <https://www.jamver.id.au/cgi-bin/blosxom.cgi/index.rss>
+- <https://www.rwhitby.net/feed>
+- <https://blog.khax.net/feed/>
+- <https://dalts.com/index.xml>
+- <https://pento.net/feed/>
+
+Since only the last two have been updated in the last 9 years, I added these
+to Feedburner and added the following "proxied" URLs to my reader:
+
+- <https://feeds.feedburner.com/SteveDaltonLA>
+- <https://feeds.feedburner.com/GaryPendergastLA>
+
+Similarly, I couldn't fetch the following over Tor for some other reasons:
+
+- <http://algorithm.com.au/blog/files/rss.xml>
+- <http://soundadvice.id.au/blog/index.atom>
+- <http://www.bytebot.net/blog/archives/category/databases/feed>
+- <http://levlafayette.com/blog/feed>
+- <http://www.rowetel.com/?feed=rss2>
+- <https://hamishtaylor.com.au/photekgraddft-hamish-taylors-blog?format=rss>
+
+I excluded the first two which haven't been updated in 6 years and proxied
+the other ones:
+
+- <https://feeds.feedburner.com/ColinCharlesLA>
+- <https://feeds.feedburner.com/LevLafayetteLA>
+- <https://feeds.feedburner.com/DavidRoweLA>
+- <https://feeds.feedburner.com/HamishTaylorLA>
+
+[[!tag tor]]
diff --git a/posts/list-planet-linux-australia-blogs/linuxaustralia.opml b/posts/list-planet-linux-australia-blogs/linuxaustralia.opml
new file mode 100644
index 0000000..715ed99
--- /dev/null
+++ b/posts/list-planet-linux-australia-blogs/linuxaustralia.opml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<opml version="1.0">
+ <head>
+  <text/>
+ </head>
+ <body>
+  <outline isOpen="true" id="2064586897" text="Planet Linux Australia">
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://adrianchadd.blogspot.com/feeds/posts/default" htmlUrl="https://adrianchadd.blogspot.com/" fetchInterval="30" id="2136324713" description="" title="Adrian Chadd" maxArticleAge="60" comment="" copyright="" type="rss" text="Adrian Chadd"/>
+   <outline maxArticleAge="60" copyright="" xmlUrl="https://feeds2.feedburner.com/symphonious" fetchInterval="30" useCustomFetchInterval="false" logoHeight="0" description="Living in a state of accord." version="RSS" text="Adrian Sutton" logoWidth="0" logoUrl="https://www.symphonious.net/wp-atom.php" archiveMode="globalDefault" maxArticleNumber="1000" comment="" type="rss" title="Adrian Sutton" htmlUrl="https://www.symphonious.net" id="3352462773"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://etymon.blogspot.com/atom.xml" htmlUrl="https://etymon.blogspot.com/" fetchInterval="30" id="1279987017" description="\Et&quot;y*mon\, n.; pl. E. Etymons, Gr. Etyma. &#xd;&#xa;&lt;br>&#xd;&#xa;[L., fr. Gr. 'e`tymon the true literal sense of a word according to its derivation, an etymon]" title="Andrae Muys" maxArticleAge="60" comment="" copyright="" type="rss" text="Andrae Muys"/>
+   <outline maxArticleAge="60" copyright="" xmlUrl="https://ajdlinux.wordpress.com/feed/atom/" fetchInterval="30" useCustomFetchInterval="false" logoHeight="0" description="...one PC at a time. Just another person&amp;apos;s views on Linux, life and other things..." version="RSS" text="Andrew Donnellan" logoWidth="0" logoUrl="https://ajdlinux.wordpress.com/wp-atom.php" archiveMode="globalDefault" maxArticleNumber="1000" comment="" type="rss" title="Andrew Donnellan" htmlUrl="https://ajdlinux.wordpress.com" id="3265789158"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="http://blog.andrew.net.au/index.rss" htmlUrl="http://blog.andrew.net.au" fetchInterval="30" id="364764844" description="Andrew Pollock's blog." title="Andrew Pollock" maxArticleAge="60" comment="" copyright="" type="rss" text="Andrew Pollock"/>
+   <outline maxArticleAge="60" copyright="" xmlUrl="http://blog.etc.gen.nz/feeds/index.rss2" fetchInterval="30" useCustomFetchInterval="false" logoHeight="21" description="This is a blog, it is it is." version="RSS" text="Andrew Ruthven" logoWidth="100" logoUrl="http://blog.etc.gen.nz/templates/2k11/img/s9y_banner_small.png" archiveMode="globalDefault" maxArticleNumber="1000" comment="" type="rss" title="Andrew Ruthven" htmlUrl="http://blog.etc.gen.nz/" id="1575153193"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://lentz.com.au/feed" htmlUrl="https://lentz.com.au" fetchInterval="30" id="3044188428" description="The Lentz tribe in Brisbane" title="Arjen Lentz" maxArticleAge="60" comment="" copyright="" type="rss" text="Arjen Lentz"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="http://benno.id.au/blog/feed/" htmlUrl="http://benno.id.au/blog/" fetchInterval="30" id="3923796910" description="Systems design and other random tech stuff" title="Ben Leslie" maxArticleAge="60" comment="" copyright="Ben Leslie 2006-09" type="rss" text="Ben Leslie"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://monkeyiq.blogspot.com/feeds/posts/default" htmlUrl="https://monkeyiq.blogspot.com/" fetchInterval="30" id="220149261" description="C++, Linux, libferris and embedded development. Yet another blog from yet another NARG." title="Ben Martin" maxArticleAge="60" comment="" copyright="" type="rss" text="Ben Martin"/>
+   <outline maxArticleAge="60" copyright="" xmlUrl="https://bluehackers.org/feed/atom" fetchInterval="30" useCustomFetchInterval="false" logoHeight="0" description="because we&amp;apos;re not just geeks - we&amp;apos;re humans." version="RSS" text="BlueHackers" logoWidth="0" logoUrl="https://bluehackers.org/wp-atom.php" archiveMode="globalDefault" maxArticleNumber="1000" comment="" type="rss" title="BlueHackers" htmlUrl="https://bluehackers.org" id="3703814559"/>
+   <outline maxArticleAge="60" copyright="" xmlUrl="https://brendanscott.wordpress.com/feed/" fetchInterval="30" useCustomFetchInterval="false" logoHeight="31" description="Comments on Life and Law, Free Software, Open Source and Intellectual Monopolies" version="RSS" text="Brendan Scott" logoWidth="88" logoUrl="https://s0.wp.com/i/buttonw-com.png" archiveMode="globalDefault" maxArticleNumber="1000" comment="" type="rss" title="Brendan Scott" htmlUrl="https://brendanscott.wordpress.com" id="2903001798"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://www.csamuel.org/feed" htmlUrl="https://www.csamuel.org" fetchInterval="30" id="1432669172" description="Computers, science, archaeology and other random burblings" title="Chris Samuel" maxArticleAge="60" comment="" copyright="" type="rss" text="Chris Samuel"/>
+   <outline maxArticleAge="60" copyright="" xmlUrl="https://blog.christophersmart.com/feed/" fetchInterval="30" useCustomFetchInterval="false" logoHeight="32" description="Fortiter Et Recte" version="RSS" text="Chris Smart" logoWidth="32" logoUrl="https://blog.christophersmart.com/wp-content/uploads/2008/08/cropped-csmart-32x32.jpg" archiveMode="globalDefault" maxArticleNumber="1000" comment="" type="rss" title="Chris Smart" htmlUrl="https://blog.christophersmart.com" id="1309301898"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://chrisjrn.com/feed.xml" htmlUrl="https://chrisjrn.com/" fetchInterval="30" id="2199337231" description="Christopher Neugebauer&amp;apos;s site" title="Chris Neugebauer" maxArticleAge="60" comment="" copyright="" type="rss" text="Chris Neugebauer"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="http://chris.yeoh.info/feed/" htmlUrl="http://chris.yeoh.info" fetchInterval="30" id="2446739425" description="" title="Chris Yeoh" maxArticleAge="60" comment="" copyright="" type="rss" text="Chris Yeoh"/>
+   <outline maxArticleAge="60" copyright="" xmlUrl="https://clintonroy.wordpress.com/feed/" fetchInterval="30" useCustomFetchInterval="false" logoHeight="31" description="An Open Source Software Engineer" version="RSS" text="Clinton Roy" logoWidth="88" logoUrl="https://s0.wp.com/i/buttonw-com.png" archiveMode="globalDefault" maxArticleNumber="1000" comment="" type="rss" title="Clinton Roy" htmlUrl="https://clintonroy.wordpress.com" id="165453513"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://feeds.feedburner.com/ColinCharlesLA" htmlUrl="http://www.bytebot.net/blog" fetchInterval="30" id="306929162" description="&amp;quot;Imagine. Create. Execute.&amp;quot; Rough notes on technology, media, travel, business, work, and my other interests." title="Colin Charles" maxArticleAge="60" comment="" copyright="" type="rss" text="Colin Charles"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="http://blog.kfish.org/feeds/posts/default" htmlUrl="http://blog.kfish.org/" fetchInterval="30" id="2458778784" description="&lt;strong>Making free software for embedded/mobile multimedia, web video and computer music; in C and Haskell, using algebra, type theory and git!&lt;/strong>" title="Conrad Parker" maxArticleAge="60" comment="" copyright="" type="rss" text="Conrad Parker"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="http://blog.taz.net.au/feed/" htmlUrl="http://blog.taz.net.au" fetchInterval="30" id="2716947403" description="Tech Notes And Miscellaneous Thoughts" title="Craig Sanders" maxArticleAge="60" comment="" copyright="" type="rss" text="Craig Sanders"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://mcwhirter.com.au/index.rss" htmlUrl="http://mcwhirter.com.au//" fetchInterval="30" id="1608228020" description="mcwhirter.com.au" title="Craige McWhirter" maxArticleAge="60" comment="" copyright="" type="rss" text="Craige McWhirter"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://blogs.gnome.org/danni/feed/" htmlUrl="https://blogs.gnome.org/danni" fetchInterval="30" id="3530083293" description="Danielle's technical stuffs" title="Danielle Madeley" maxArticleAge="60" comment="" copyright="" type="rss" text="Danielle Madeley"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://feeds.feedburner.com/skwashd" htmlUrl="https://davehall.com.au/blog/dave" fetchInterval="30" id="1677023889" description="" title="Dave Hall" maxArticleAge="60" comment="" copyright="" type="rss" text="Dave Hall"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://feeds.feedburner.com/DavidRoweLA" htmlUrl="https://www.rowetel.com" fetchInterval="30" id="2518525852" description="open telephony software, hardware, critical thinking" title="David Rowe" maxArticleAge="60" comment="" copyright="" type="rss" text="David Rowe"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="http://www.mega-nerd.com/erikd/Blog/index.rss" htmlUrl="http://www.mega-nerd.com/erikd/Blog" fetchInterval="30" id="3531593969" description="An ocassional rant" title="Erik de Castro Lopo" maxArticleAge="60" comment="" copyright="Copyright 2006-2010 Erik de Castro Lopo" type="rss" text="Erik de Castro Lopo"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://feeding.cloud.geek.nz/index.rss" htmlUrl="http://feeding.cloud.geek.nz/" fetchInterval="30" id="1229046837" description="Feeding the Cloud" title="Francois Marier" maxArticleAge="60" comment="" copyright="" type="rss" text="Francois Marier"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="http://noronha.id.au/feed/" htmlUrl="https://noronha.id.au" fetchInterval="30" id="595433577" description="Life is boring if things &amp;quot;Just Work&amp;quot; !" title="Gabriel Noronha" maxArticleAge="60" comment="" copyright="" type="rss" text="Gabriel Noronha"/>
+   <outline maxArticleAge="60" copyright="" xmlUrl="https://feeds.feedburner.com/GaryPendergastLA" fetchInterval="30" useCustomFetchInterval="false" logoHeight="32" description="I'm on the Internet" version="RSS" text="Gary Pendergast" logoWidth="32" logoUrl="https://pento.net/wp-content/uploads/2014/10/1ad9e5c98d81c6815a65dab5b6e1f669-54515cee_site_icon-32x32.png" archiveMode="globalDefault" maxArticleNumber="1000" comment="" type="rss" title="Gary Pendergast" htmlUrl="https://pento.net" id="1806746700"/>
+   <outline maxArticleAge="60" copyright="" xmlUrl="https://gdt.dreamwidth.org/data/rss" fetchInterval="30" useCustomFetchInterval="false" logoHeight="100" description="Postcards from Semaphore - Dreamwidth Studios" version="RSS" text="Glen Turner" logoWidth="100" logoUrl="https://v.dreamwidth.org/12224960/2733095" archiveMode="globalDefault" maxArticleNumber="1000" comment="" type="rss" title="Glen Turner" htmlUrl="https://gdt.dreamwidth.org/" id="727997966"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://teapartiesandunicorns.blogspot.com/feeds/posts/default" htmlUrl="https://teapartiesandunicorns.blogspot.com/" fetchInterval="30" id="1788590842" description="Creating awesome software" title="Greg Black" maxArticleAge="60" comment="" copyright="" type="rss" text="Greg Black"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://feeds.feedburner.com/HamishTaylorLA" htmlUrl="https://hamishtaylor.com.au/photekgraddft-hamish-taylors-blog/" fetchInterval="30" id="1478909113" description="" title="Hamish Taylor" maxArticleAge="60" comment="" copyright="" type="rss" text="Hamish Taylor"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="http://blemings.org/hugh/blog/blosxom.cgi/index.rss" htmlUrl="http://blemings.org/hugh/blog/blosxom.cgi/" fetchInterval="30" id="801275812" description="A Diary and Miscellaneous ramblings" title="Hugh Blemings" maxArticleAge="60" comment="" copyright="" type="rss" text="Hugh Blemings"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://feeds.feedburner.com/technovelty" htmlUrl="https://www.technovelty.org/" fetchInterval="30" id="3154827406" description="" title="Ian Wienand" maxArticleAge="60" comment="" copyright="" type="rss" text="Ian Wienand"/>
+   <outline maxArticleAge="60" copyright="" xmlUrl="https://blog.james.rcpt.to/category/computing/linux/feed/" fetchInterval="30" useCustomFetchInterval="false" logoHeight="32" description="Scribblings of a Techie" version="RSS" text="James Bromberger" logoWidth="32" logoUrl="/wp-content/uploads/2016/01/cropped-james-bromberger-02-32x32.jpg" archiveMode="globalDefault" maxArticleNumber="1000" comment="" type="rss" title="James Bromberger" htmlUrl="" id="92157731"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://spacepants.org/blog/index.xml" htmlUrl="https://spacepants.org/blog/" fetchInterval="30" id="336648069" description="Recent content in spaceblog on spacepants.org" title="Jamie Wilkinson" maxArticleAge="60" comment="" copyright="All content Copyright © 2002-2020 Jamie Wilkinson.&#xa;Entries in this blog are licensed under the Creative Commons Attribution-Sharealike v2 License." type="rss" text="Jamie Wilkinson"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://noraisin.net/diary/?feed=rss2" htmlUrl="https://noraisin.net/diary" fetchInterval="30" id="3209333186" description="Coding blog - GStreamer, OpenHMD mostly" title="Jan Schmidt" maxArticleAge="60" comment="" copyright="" type="rss" text="Jan Schmidt"/>
+   <outline maxArticleAge="60" copyright="" xmlUrl="https://nooks.livejournal.com/data/rss" fetchInterval="30" useCustomFetchInterval="false" logoHeight="100" description="Jason Parker-Burlingham - LiveJournal.com" version="RSS" text="Jason Parker-Burlingham" logoWidth="100" logoUrl="https://l-userpic.livejournal.com/44632593/1388669" archiveMode="globalDefault" maxArticleNumber="1000" comment="" type="rss" title="Jason Parker-Burlingham" htmlUrl="https://nooks.livejournal.com/" id="1800194004"/>
+   <outline maxArticleAge="60" copyright="" xmlUrl="https://bethesignal.org/feed/atom/" fetchInterval="30" useCustomFetchInterval="false" logoHeight="0" description="" version="RSS" text="Jeff Waugh" logoWidth="0" logoUrl="https://bethesignal.org/wp-atom.php" archiveMode="globalDefault" maxArticleNumber="1000" comment="" type="rss" title="Jeff Waugh" htmlUrl="https://bethesignal.org" id="4057446467"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="http://jk.ozlabs.org/feeds/rss/" htmlUrl="http://jk.ozlabs.org/" fetchInterval="30" id="3040733194" description="Updates to blog, photos and code from jk.ozlabs.org" title="Jeremy Kerr" maxArticleAge="60" comment="" copyright="" type="rss" text="Jeremy Kerr"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://jeremy.visser.name/index.xml" htmlUrl="https://jeremy.visser.name/" fetchInterval="30" id="2659616076" description="Recent content in Jeremy Visser — Hacker, Sysadmin, Bassoonist on Jeremy Visser" title="Jeremy Visser" maxArticleAge="60" comment="" copyright="Licensed under a Creative Commons Attribution-Share Alike 4.0 License. http://creativecommons.org/licenses/by-sa/4.0/" type="rss" text="Jeremy Visser"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="http://brnz.org/hbr/?feed=rss2" htmlUrl="http://brnz.org/hbr" fetchInterval="30" id="986775896" description="hint for Branch (r-form)" title="Jonathan Adamczewski" maxArticleAge="60" comment="" copyright="" type="rss" text="Jonathan Adamczewski"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="http://noisymime.org/blog/feed/" htmlUrl="http://noisymime.org/blog" fetchInterval="30" id="3235908358" description="My ancient blog archive" title="Josh Stewart" maxArticleAge="60" comment="" copyright="" type="rss" text="Josh Stewart"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://josh.people.rcbops.com/feed/" htmlUrl="https://josh.hesketh.net.au" fetchInterval="30" id="1049289463" description="Open Source, Linux, OpenStack, Mechatronics, Blog(?)" title="Joshua Hesketh" maxArticleAge="60" comment="" copyright="" type="rss" text="Joshua Hesketh"/>
+   <outline maxArticleAge="60" copyright="" xmlUrl="https://laptop006.livejournal.com/data/rss" fetchInterval="30" useCustomFetchInterval="false" logoHeight="100" description="Julien Goodwin - LiveJournal.com" version="RSS" text="Julien Goodwin" logoWidth="84" logoUrl="https://l-userpic.livejournal.com/37672818/8254271" archiveMode="globalDefault" maxArticleNumber="1000" comment="" type="rss" title="Julien Goodwin" htmlUrl="https://laptop006.livejournal.com/" id="4031182726"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://infinitedigits.blogspot.com/feeds/posts/default" htmlUrl="https://infinitedigits.blogspot.com/" fetchInterval="30" id="694601602" description="" title="Leah Duncan" maxArticleAge="60" comment="" copyright="" type="rss" text="Leah Duncan"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://leonbrooks.blogspot.com/atom.xml" htmlUrl="https://leonbrooks.blogspot.com/" fetchInterval="30" id="1627579536" description="Linux Advocate in Western Australia" title="Leon Brooks" maxArticleAge="60" comment="" copyright="" type="rss" text="Leon Brooks"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://feeds.feedburner.com/LevLafayetteLA" htmlUrl="http://levlafayette.com/blog" fetchInterval="30" id="2267109132" description="" title="Lev Lafayette" maxArticleAge="60" comment="" copyright="" type="rss" text="Lev Lafayette"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://linux.org.au/feed/" htmlUrl="https://linux.org.au" fetchInterval="30" id="2846462728" description="Representing Free Software and Open Source Communities" title="Linux Australia" maxArticleAge="60" comment="" copyright="" type="rss" text="Linux Australia"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://www.hezmatt.org/~mpalmer/blog/rss.xml" htmlUrl="//www.hezmatt.org/~mpalmer/blog/" fetchInterval="30" id="3161894866" description="The Thoughts of Matt Palmer" title="Matt Palmer" maxArticleAge="60" comment="" copyright="" type="rss" text="Matt Palmer"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://oliver.net.au/?feed=rss2" htmlUrl="https://oliver.net.au" fetchInterval="30" id="1903446338" description="Linux, life and programming" title="Matthew Oliver" maxArticleAge="60" comment="" copyright="" type="rss" text="Matthew Oliver"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://feeds.feedburner.com/Founds" htmlUrl="http://blog.dataparksearch.org" fetchInterval="30" id="1647832837" description="Just DataparkSearch weblog" title="Maxim Zakharov" maxArticleAge="60" comment="" copyright="" type="rss" text="Maxim Zakharov"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://lifelog.michaeldavies.org/feeds/posts/default" htmlUrl="https://lifelog.michaeldavies.org/" fetchInterval="30" id="1634131735" description="" title="Michael Davies" maxArticleAge="60" comment="" copyright="" type="rss" text="Michael Davies"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="http://neuling.org/mikey/index.cgi/index.rss" htmlUrl="http://neuling.org/mikey/index.cgi" fetchInterval="30" id="1093304945" description="Look at me, Look at me, Look at me!!!" title="Michael Neuling" maxArticleAge="60" comment="" copyright="" type="rss" text="Michael Neuling"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://www.madebymikal.com/feed/" htmlUrl="https://www.madebymikal.com" fetchInterval="30" id="1291538766" description="Content here is by Michael Still, mikal@stillhq.com." title="Michael Still" maxArticleAge="60" comment="" copyright="" type="rss" text="Michael Still"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="http://www.debianslashrules.org/index.rss" htmlUrl="http://www.debianslashrules.org/" fetchInterval="30" id="502043467" description="Thoughts from no-one important" title="Mike Beattie" maxArticleAge="60" comment="" copyright="" type="rss" text="Mike Beattie"/>
+   <outline maxArticleAge="60" copyright="" xmlUrl="https://openstem.com.au/feed" fetchInterval="30" useCustomFetchInterval="false" logoHeight="32" description="Helping students engage." version="RSS" text="OpenSTEM" logoWidth="32" logoUrl="https://openstem.com.au/wp-content/uploads/2015/04/owl-icon-512x512-552b5a4dv1_site_icon-100x100.png" archiveMode="globalDefault" maxArticleNumber="1000" comment="" type="rss" title="OpenSTEM" htmlUrl="https://openstem.com.au" id="1858510813"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://cafuego.net/rss.xml" htmlUrl="https://cafuego.net/" fetchInterval="30" id="3436238507" description="" title="Peter Lieverdink" maxArticleAge="60" comment="" copyright="" type="rss" text="Peter Lieverdink"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="http://pipka.org/feed/" htmlUrl="http://pipka.org" fetchInterval="30" id="1312671929" description="What are we doing today Brain? Taking over the world like we always do." title="Pia Andrews" maxArticleAge="60" comment="" copyright="" type="rss" text="Pia Andrews"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="http://www.mechanicalcat.net/richard/log/Python/rss" htmlUrl="http://www.mechanicalcat.net/richard/log/Python" fetchInterval="30" id="2028029603" description="Richard Jones' Log: Python" title="Richard Jones" maxArticleAge="60" comment="" copyright="" type="rss" text="Richard Jones"/>
+   <outline maxArticleAge="60" copyright="" xmlUrl="https://rbtcollins.wordpress.com/feed/" fetchInterval="30" useCustomFetchInterval="false" logoHeight="31" description="Someone near you is coding" version="RSS" text="Robert Collins" logoWidth="88" logoUrl="https://s0.wp.com/i/buttonw-com.png" archiveMode="globalDefault" maxArticleNumber="1000" comment="" type="rss" title="Robert Collins" htmlUrl="https://rbtcollins.wordpress.com" id="1719739607"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://etbe.coker.com.au/feed/" htmlUrl="https://etbe.coker.com.au" fetchInterval="30" id="2377874160" description="Linux, politics, and other interesting things" title="Russell Coker" maxArticleAge="60" comment="" copyright="" type="rss" text="Russell Coker"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://rusty.ozlabs.org/?feed=rss2" htmlUrl="https://rusty.ozlabs.org" fetchInterval="30" id="3091376666" description="Stealing From Smart People" title="Rusty Russell" maxArticleAge="60" comment="" copyright="" type="rss" text="Rusty Russell"/>
+   <outline maxArticleAge="60" copyright="" xmlUrl="https://sswam.wordpress.com/feed/" fetchInterval="30" useCustomFetchInterval="false" logoHeight="31" description="" version="RSS" text="Sam Watkins" logoWidth="88" logoUrl="https://s0.wp.com/i/buttonw-com.png" archiveMode="globalDefault" maxArticleNumber="1000" comment="" type="rss" title="Sam Watkins" htmlUrl="https://sswam.wordpress.com" id="2958449255"/>
+   <outline maxArticleAge="60" copyright="" xmlUrl="https://gingertech.net/feed/atom/" fetchInterval="30" useCustomFetchInterval="false" logoHeight="0" description="Silvia&amp;apos;s blog" version="RSS" text="Silvia Pfeiffer" logoWidth="0" logoUrl="https://gingertech.net/wp-atom.php" archiveMode="globalDefault" maxArticleNumber="1000" comment="" type="rss" title="Silvia Pfeiffer" htmlUrl="https://gingertech.net" id="3688564650"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://horms.net/blog/rss.xml" htmlUrl="http://horms.net/" fetchInterval="30" id="2116203352" description="Horms Solutions - Free and Open Source Softwrae Development" title="Simon Horms" maxArticleAge="60" comment="" copyright="" type="rss" text="Simon Horms"/>
+   <outline maxArticleAge="60" copyright="" xmlUrl="https://blog.darkmere.gen.nz/feed/" fetchInterval="30" useCustomFetchInterval="false" logoHeight="32" description="New Zealand, Sysadmin, Linux, Curry, Chess" version="RSS" text="Simon Lyall" logoWidth="32" logoUrl="https://blog.darkmere.gen.nz/wp-content/uploads/2015/04/mrlyall-thin-60x60.png" archiveMode="globalDefault" maxArticleNumber="1000" comment="" type="rss" title="Simon Lyall" htmlUrl="https://blog.darkmere.gen.nz" id="2569062528"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://feeds.feedburner.com/SteveDaltonLA" htmlUrl="https://dalts.com/" fetchInterval="30" id="2513729190" description="Recent content on Kind Acts of Randomness" title="Steve Dalton" maxArticleAge="60" comment="" copyright="" type="rss" text="Steve Dalton"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="http://svana.org/sjh/diary//index.rss" htmlUrl="http://svana.org/sjh/diary" fetchInterval="30" id="712173389" description="mtb / vegan / running / linux / canberra / cycling / etc" title="Steven Hanley" maxArticleAge="60" comment="" copyright="" type="rss" text="Steven Hanley"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://www.flamingspork.com/blog/feed/" htmlUrl="https://www.flamingspork.com/blog" fetchInterval="30" id="1079836154" description="Ramblings which occasionally resemble reality. This is the blog of Stewart Smith." title="Stewart Smith" maxArticleAge="60" comment="" copyright="" type="rss" text="Stewart Smith"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://feeds.feedburner.com/BlahBlahWoofWoof" htmlUrl="https://timriley.info" fetchInterval="30" id="3941210372" description="" title="Tim Riley" maxArticleAge="60" comment="" copyright="" type="rss" text="Tim Riley"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://ourobengr.com/feed/" htmlUrl="https://ourobengr.com" fetchInterval="30" id="1545419881" description="An engineer eating his own tail" title="Tim Serong" maxArticleAge="60" comment="" copyright="" type="rss" text="Tim Serong"/>
+   <outline version="RSS" archiveMode="globalDefault" useCustomFetchInterval="false" maxArticleNumber="1000" xmlUrl="https://sthbrx.github.io/atom.xml" htmlUrl="https://sthbrx.github.io/" fetchInterval="30" id="2212733042" description="A Power Technical Blog" title="sthbrx - a POWER technical blog" maxArticleAge="60" comment="" copyright="" type="rss" text="sthbrx - a POWER technical blog"/>
+  </outline>
+ </body>
+</opml>
+

Add ads.txt post.
diff --git a/posts/opting-your-domain-out-of-programmatic-advertising.mdwn b/posts/opting-your-domain-out-of-programmatic-advertising.mdwn
new file mode 100644
index 0000000..c8483e3
--- /dev/null
+++ b/posts/opting-your-domain-out-of-programmatic-advertising.mdwn
@@ -0,0 +1,64 @@
+[[!meta title="Opting your domain out of programmatic advertising"]]
+[[!meta date="2020-12-08T00:10:00.000-08:00"]]
+[[!meta license="[Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)"]]
+
+A few years ago, the advertising industry introduced the [`ads.txt`
+project](https://iabtechlab.com/ads-txt-about) in order to defend against
+widespread *domain spoofing* vulnerabilities in [programmatic
+advertising](https://en.wikipedia.org/wiki/Programmatic_advertising).
+
+I decided to use this technology to opt out of having ads sold for my
+domains, at least through ad exchanges which perform this check, by hosting
+a text file containing this:
+
+    contact=ads@fmarier.org
+
+at the following locations:
+
+- <https://feeding.cloud.geek.nz/ads.txt>
+- <https://fmarier.org/ads.txt>
+
+(In order to get this to work on my blog, running
+[Ikiwiki](https://ikiwiki.info/) on
+[Branchable](https://www.branchable.com/), I had to disable the [txt
+plugin](https://ikiwiki.info/plugins/txt/) in order to get `ads.txt` to be
+served as a plain text file instead of being automatically rendered as
+HTML.)
+
+## Specification
+
+The key parts of the [specification](https://iabtechlab.com/ads-txt/) for our purposes are:
+
+> [3.1] If the server response indicates the resource does not exist (HTTP
+Status Code 404), the advertising system can assume no declarations exist
+and that no advertising system is unauthorized to buy and sell ads on the
+website.
+
+> [3.2.1] Some publishers may choose to not authorize any advertising system
+by publishing an empty ads.txt file, indicating that no advertising system
+is authorized to buy and sell ads on the website. So that consuming systems
+properly read and interpret the empty file (differentiating between web
+servers returning error pages for the /ads.txt URL), at least one properly
+formatted line must be included which adheres to the format specification
+described above.
+
+As you can see, the specification sadly ignores
+[RFC8615](https://tools.ietf.org/html/rfc8615) and requires that the
+`ads.txt` file be present directly in the root of your web server, like the
+venerable [`robots.txt`](http://www.robotstxt.org/) file, but unlike the
+newer [`security.txt`](https://securitytxt.org/) standard.
+
+If you don't want to provide an email address in your `ads.txt` file, the
+specification recommends using the following line verbatim:
+
+    placeholder.example.com, placeholder, DIRECT, placeholder
+
+## Validation
+
+A number of online validators exist, but I used the following to
+double-check my setup:
+
+- <https://adstxt.guru/validator/url/?url=https%3A%2F%2Ffeeding.cloud.geek.nz%2Fads.txt>
+- <https://www.adstxtgenerator.com/validator/results/?site=feeding.cloud.geek.nz>
+
+[[!tag web]] [[!tag debian]]

Attempt to fix the tag cloud.
https://ikiwiki.info/ikiwiki/directive/pagestats/
diff --git a/sidebar.mdwn b/sidebar.mdwn
index 52a5d66..5c09f50 100644
--- a/sidebar.mdwn
+++ b/sidebar.mdwn
@@ -23,4 +23,4 @@
 [[Recent Comments|comments]]
 
 [[Tags]]:
-[[!pagestats pages="./tags/* and !tags/debian and !tags/catalyst and !tags/mozilla and !tags/ubuntu and !tags/nzoss and !tags/sysadmin and !tags/mahara" among="./posts/*"]]
+[[!pagestats pages="tags/* and !tags/debian and !tags/catalyst and !tags/mozilla and !tags/ubuntu and !tags/nzoss and !tags/sysadmin and !tags/mahara" among="posts/*"]]

Add ads.txt file.
https://iabtechlab.com/ads-txt/
diff --git a/ads.txt b/ads.txt
new file mode 100644
index 0000000..a4035fd
--- /dev/null
+++ b/ads.txt
@@ -0,0 +1 @@
+contact=ads@fmarier.org

Add pack corruption post for Restic.
diff --git a/posts/removing-corrupted-data-pack-restic-backup.mdwn b/posts/removing-corrupted-data-pack-restic-backup.mdwn
new file mode 100644
index 0000000..a1fd591
--- /dev/null
+++ b/posts/removing-corrupted-data-pack-restic-backup.mdwn
@@ -0,0 +1,89 @@
+[[!meta title="Removing a corrupted data pack in a Restic backup"]]
+[[!meta date="2020-11-22T11:30:00.000-08:00"]]
+[[!meta license="[Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)"]]
+
+I recently ran into a corrupted data pack in a [Restic](https://restic.net/)
+backup on my
+[GnuBee](https://feeding.cloud.geek.nz/posts/backing-up-to-gnubee2/). It led
+to consistent failures during the `prune` operation:
+
+    incomplete pack file (will be removed): b45afb51749c0778de6a54942d62d361acf87b513c02c27fd2d32b730e174f2e
+    incomplete pack file (will be removed): c71452fa91413b49ea67e228c1afdc8d9343164d3c989ab48f3dd868641db113
+    incomplete pack file (will be removed): 10bf128be565a5dc4a46fc2fc5c18b12ed2e77899e7043b28ce6604e575d1463
+    incomplete pack file (will be removed): df282c9e64b225c2664dc6d89d1859af94f35936e87e5941cee99b8fbefd7620
+    incomplete pack file (will be removed): 1de20e74aac7ac239489e6767ec29822ffe52e1f2d7f61c3ec86e64e31984919
+    hash does not match id: want 8fac6efe99f2a103b0c9c57293a245f25aeac4146d0e07c2ab540d91f23d3bb5, got 2818331716e8a5dd64a610d1a4f85c970fd8ae92f891d64625beaaa6072e1b84
+    github.com/restic/restic/internal/repository.Repack
+            github.com/restic/restic/internal/repository/repack.go:37
+    main.pruneRepository
+            github.com/restic/restic/cmd/restic/cmd_prune.go:242
+    main.runPrune
+            github.com/restic/restic/cmd/restic/cmd_prune.go:62
+    main.glob..func19
+            github.com/restic/restic/cmd/restic/cmd_prune.go:27
+    github.com/spf13/cobra.(*Command).execute
+            github.com/spf13/cobra/command.go:838
+    github.com/spf13/cobra.(*Command).ExecuteC
+            github.com/spf13/cobra/command.go:943
+    github.com/spf13/cobra.(*Command).Execute
+            github.com/spf13/cobra/command.go:883
+    main.main
+            github.com/restic/restic/cmd/restic/main.go:86
+    runtime.main
+            runtime/proc.go:204
+    runtime.goexit
+            runtime/asm_amd64.s:1374
+
+Thanks to the [excellent support
+forum](https://forum.restic.net/t/restic-prune-issue/3098/2), I was able to
+resolve this issue by dropping a single snapshot.
+
+First, I identified the snapshot which contained the offending pack:
+
+    $ restic -r sftp:hostname.local: find --pack 8fac6efe99f2a103b0c9c57293a245f25aeac4146d0e07c2ab540d91f23d3bb5
+    repository b0b0516c opened successfully, password is correct
+    Found blob 2beffa460d4e8ca4ee6bf56df279d1a858824f5cf6edc41a394499510aa5af9e
+     ... in file /home/francois/.local/share/akregator/Archive/http___udd.debian.org_dmd_feed_
+         (tree 602b373abedca01f0b007fea17aa5ad2c8f4d11f1786dd06574068bf41e32020)
+     ... in snapshot 5535dc9d (2020-06-30 08:34:41)
+
+Then, I could simply drop that snapshot:
+
+    $ restic -r sftp:hostname.local: forget 5535dc9d
+    repository b0b0516c opened successfully, password is correct
+    [0:00] 100.00%  1 / 1 files deleted
+
+and run the `prune` command to remove the snapshot, as well as the incomplete
+packs that were also mentioned in the above output but could never be
+removed due to the other error:
+
+    $ restic -r sftp:hostname.local: prune
+    repository b0b0516c opened successfully, password is correct
+    counting files in repo
+    building new index for repo
+    [20:11] 100.00%  77439 / 77439 packs
+    incomplete pack file (will be removed): b45afb51749c0778de6a54942d62d361acf87b513c02c27fd2d32b730e174f2e
+    incomplete pack file (will be removed): c71452fa91413b49ea67e228c1afdc8d9343164d3c989ab48f3dd868641db113
+    incomplete pack file (will be removed): 10bf128be565a5dc4a46fc2fc5c18b12ed2e77899e7043b28ce6604e575d1463
+    incomplete pack file (will be removed): df282c9e64b225c2664dc6d89d1859af94f35936e87e5941cee99b8fbefd7620
+    incomplete pack file (will be removed): 1de20e74aac7ac239489e6767ec29822ffe52e1f2d7f61c3ec86e64e31984919
+    repository contains 77434 packs (2384522 blobs) with 367.648 GiB
+    processed 2384522 blobs: 1165510 duplicate blobs, 47.331 GiB duplicate
+    load all snapshots
+    find data that is still in use for 15 snapshots
+    [1:11] 100.00%  15 / 15 snapshots
+    found 1006062 of 2384522 data blobs still in use, removing 1378460 blobs
+    will remove 5 invalid files
+    will delete 13728 packs and rewrite 15140 packs, this frees 142.285 GiB
+    [4:58:20] 100.00%  15140 / 15140 packs rewritten
+    counting files in repo
+    [18:58] 100.00%  50164 / 50164 packs
+    finding old index files
+    saved new indexes as [340cb68f 91ff77ef ee21a086 3e5fa853 084b5d4b 3b8d5b7a d5c385b4 5eff0be3 2cebb212 5e0d9244 29a36849 8251dcee 85db6fa2 29ed23f6 fb306aba 6ee289eb 0a74829d]
+    remove 190 old index files
+    [0:00] 100.00%  190 / 190 files deleted
+    remove 28868 old packs
+    [1:23] 100.00%  28868 / 28868 files deleted
+    done
+
+[[!tag restic]] [[!tag debian]]

Comment moderation
diff --git a/posts/time-synchronization-with-ntp-and-systemd/comment_7_8bb06960594c910c4b8d70cbe3f94490._comment b/posts/time-synchronization-with-ntp-and-systemd/comment_7_8bb06960594c910c4b8d70cbe3f94490._comment
new file mode 100644
index 0000000..5176515
--- /dev/null
+++ b/posts/time-synchronization-with-ntp-and-systemd/comment_7_8bb06960594c910c4b8d70cbe3f94490._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ ip="78.12.2.79"
+ claimedauthor="Alfio M."
+ subject="It worked on Raspberry PI 3 "
+ date="2020-11-11T08:58:23Z"
+ content="""
+Thank you François, your guide helped me to solve this issue on a Raspberry PI 3 running Raspbian Buster.
+"""]]

creating tag page tags/mysql
diff --git a/tags/mysql.mdwn b/tags/mysql.mdwn
new file mode 100644
index 0000000..0e3e3dc
--- /dev/null
+++ b/tags/mysql.mdwn
@@ -0,0 +1,4 @@
+[[!meta title="pages tagged mysql"]]
+
+[[!inline pages="tagged(mysql)" actions="no" archive="yes"
+feedshow=10]]

Add post about recovering from corrupt MariaDB index page.
Also create a new MySQL tag.
diff --git a/posts/fixing-mariadb-innodb-errors-mythtv30.mdwn b/posts/fixing-mariadb-innodb-errors-mythtv30.mdwn
index 303ce46..fa670dc 100644
--- a/posts/fixing-mariadb-innodb-errors-mythtv30.mdwn
+++ b/posts/fixing-mariadb-innodb-errors-mythtv30.mdwn
@@ -132,4 +132,4 @@ and then ran it like this:
 
     mysql -umythtv -pPassword1 mythconverg < alter_tables.sql
 
-[[!tag mythtv]] [[!tag debian]]
+[[!tag mythtv]] [[!tag debian]] [[!tag mysql]]
diff --git a/posts/recovering-from-corrupt-mariadb-index-page.mdwn b/posts/recovering-from-corrupt-mariadb-index-page.mdwn
new file mode 100644
index 0000000..650c89f
--- /dev/null
+++ b/posts/recovering-from-corrupt-mariadb-index-page.mdwn
@@ -0,0 +1,113 @@
+[[!meta title="Recovering from a corrupt MariaDB index page"]]
+[[!meta date="2020-11-01T15:10:00.000-08:00"]]
+[[!meta license="[Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)"]]
+
+I ran into a corrupt [MariaDB](https://mariadb.org/) index page the other day and had to
+restore my [MythTV database](https://www.mythtv.org/wiki/Database) from the
+automatic backups I make as part of my [regular maintainance tasks](https://feeding.cloud.geek.nz/posts/automated-mythtv-maintenance-tasks/).
+
+## Signs of trouble
+
+My troubles started when my daily backup failed on this line:
+
+    mysqldump --opt mythconverg -umythtv -pPASSWORD > mythconverg-200200923T1117.sql
+
+with this error message:
+
+    mysqldump: Error 1034: Index for table 'recordedseek' is corrupt; try to repair it when dumping table `recordedseek` at row: 4059895
+
+Comparing the dump that was just created to the database dumps in
+`/var/backups/mythtv/`, it was clear that it was incomplete since it was
+about 100 MB smaller.
+
+I first tried a gentle `OPTIMIZE TABLE recordedseek` as suggested in [this
+StackExchange
+answer](https://dba.stackexchange.com/questions/31701/finding-and-fixing-innodb-index-corruption/35190#35190)
+but that caused the database to segfault:
+
+    mysqld[9141]: 2020-09-23 15:02:46 0 [ERROR] InnoDB: Database page corruption on disk or a failed file read of tablespace mythconverg/recordedseek page [page id: space=115871, page number=11373]. You may have to recover from a backup.
+    mysqld[9141]: 2020-09-23 15:02:46 0 [Note] InnoDB: Page dump in ascii and hex (16384 bytes):
+    mysqld[9141]:  len 16384; hex 06177fa70000...
+    mysqld[9141]:  C     K     c      {\;
+    mysqld[9141]: InnoDB: End of page dump
+    mysqld[9141]: 2020-09-23 15:02:46 0 [Note] InnoDB: Uncompressed page, stored checksum in field1 102203303, calculated checksums for field1: crc32 806650270, innodb 1139779342,  page type 17855 == INDEX.none 3735928559, stored checksum in field2 102203303, calculated checksums for field2: crc32 806650270, innodb 3322209073, none 3735928559,  page LSN 148 2450029404, low 4 bytes of LSN at page end 2450029404, page number (if stored to page already) 11373, space id (if created with >= MySQL-4.1.1 and stored already) 115871
+    mysqld[9141]: 2020-09-23 15:02:46 0 [Note] InnoDB: Page may be an index page where index id is 697207
+    mysqld[9141]: 2020-09-23 15:02:46 0 [Note] InnoDB: Index 697207 is `PRIMARY` in table `mythconverg`.`recordedseek`
+    mysqld[9141]: 2020-09-23 15:02:46 0 [Note] InnoDB: It is also possible that your operating system has corrupted its own file cache and rebooting your computer removes the error. If the corrupt page is an index page. You can also try to fix the corruption by dumping, dropping, and reimporting the corrupt table. You can use CHECK TABLE to scan your table for corruption. Please refer to https://mariadb.com/kb/en/library/innodb-recovery-modes/ for information about forcing recovery.
+    mysqld[9141]: 200923 15:02:46 2020-09-23 15:02:46 0 [ERROR] InnoDB: Failed to read file './mythconverg/recordedseek.ibd' at offset 11373: Page read from tablespace is corrupted.
+    mysqld[9141]: [ERROR] mysqld got signal 11 ;
+    mysqld[9141]: Core pattern: |/lib/systemd/systemd-coredump %P %u %g %s %t 9223372036854775808 %h ...
+    kernel: [820233.893658] mysqld[9186]: segfault at 90 ip 0000557a229f6d90 sp 00007f69e82e2dc0 error 4 in mysqld[557a224ef000+803000]
+    kernel: [820233.893665] Code: c4 20 83 bd e4 eb ff ff 44 48 89 ...
+    systemd[1]: mariadb.service: Main process exited, code=killed, status=11/SEGV
+    systemd[1]: mariadb.service: Failed with result 'signal'.
+    systemd-coredump[9240]: Process 9141 (mysqld) of user 107 dumped core.#012#012Stack trace of thread 9186: ...
+    systemd[1]: mariadb.service: Service RestartSec=5s expired, scheduling restart.
+    systemd[1]: mariadb.service: Scheduled restart job, restart counter is at 1.
+    mysqld[9260]: 2020-09-23 15:02:52 0 [Warning] Could not increase number of max_open_files to more than 16364 (request: 32186)
+    mysqld[9260]: 2020-09-23 15:02:53 0 [Note] InnoDB: Starting crash recovery from checkpoint LSN=638234502026
+    ...
+    mysqld[9260]: 2020-09-23 15:02:53 0 [Note] InnoDB: Recovered page [page id: space=115875, page number=5363] from the doublewrite buffer.
+    mysqld[9260]: 2020-09-23 15:02:53 0 [Note] InnoDB: Starting final batch to recover 2 pages from redo log.
+    mysqld[9260]: 2020-09-23 15:02:53 0 [Note] InnoDB: Waiting for purge to start
+    mysqld[9260]: 2020-09-23 15:02:53 0 [Note] Recovering after a crash using tc.log
+    mysqld[9260]: 2020-09-23 15:02:53 0 [Note] Starting crash recovery...
+    mysqld[9260]: 2020-09-23 15:02:53 0 [Note] Crash recovery finished.
+
+and so I went with the nuclear option of dropping the MythTV database and
+restoring from backup.
+
+## Dropping the corrupt database
+
+First of all, I shut down MythTV as `root`:
+
+    kilall mythfrontend
+    systemctl stop mythtv-status.service
+    systemctl stop mythtv-backend.service
+
+and took a full copy of my MariaDB databases just in case:
+
+    systemctl stop mariadb.service
+    cd /var/lib
+    apack /root/var-lib-mysql-20200923T1215.tgz mysql/
+    systemctl start mariadb.service
+
+before dropping the MythTV databse (`mythconverg`):
+
+    $ mysql -pPASSWORD
+    
+    MariaDB [(none)]> show databases;
+    +--------------------+
+    | Database           |
+    +--------------------+
+    | information_schema |
+    | mysql              |
+    | mythconverg        |
+    | performance_schema |
+    +--------------------+
+    4 rows in set (0.000 sec)
+    
+    MariaDB [(none)]> drop database mythconverg;
+    Query OK, 114 rows affected (25.564 sec)
+    
+    MariaDB [(none)]> quit
+    Bye
+
+## Restoring from backup
+
+Then I [re-created an empty database](https://www.mythtv.org/wiki/Database_Setup#Debian.2FUbuntu.2FMint):
+
+    mysql -pPASSWORD < /usr/share/mythtv/sql/mc.sql
+
+and [restored the last DB
+dump](https://www.mythtv.org/wiki/Database_Backup_and_Restore#Database_Restore)
+prior to the detection of the corruption:
+
+    sudo -i -u mythtv
+    /usr/share/mythtv/mythconverg_restore.pl --directory /var/backups/mythtv --filename mythconverg-1350-20200923010502.sql.gz
+
+In order to restart everything properly, I simply rebooted the machine:
+
+    systemctl reboot
+
+[[!tag mythtv]] [[!tag mysql]]

Add daily reboot to prevent the network from dropping.
diff --git a/posts/installing-debian-buster-on-gnubee2.mdwn b/posts/installing-debian-buster-on-gnubee2.mdwn
index 7773915..a268f9f 100644
--- a/posts/installing-debian-buster-on-gnubee2.mdwn
+++ b/posts/installing-debian-buster-on-gnubee2.mdwn
@@ -286,4 +286,16 @@ I also added the following to `/etc/.gitignore` to make
 since `fake-hwclock` unfortunately [keeps its data file in
 `/etc/`](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=782314).
 
+## Fixing network drops
+
+I regularly see the GnuBee drop its network connections and nothing short of
+a reboot will fix it in my experience. Not sure whether that's a
+driver/kernel problem, but I decided to try to prevent it by scheduling
+a daily reboot in `/etc/cron.d/reboot-daily`:
+
+    30 18 * * *	root	/bin/grep --quiet finish= /proc/mdstat || /bin/systemctl reboot
+
+The extra check is there to ensure that the machine doesn't reboot if it's
+busy re-synchronizing the RAID array.
+
 [[!tag debian]] [[!tag gnubee]]

Let avahi-daemon handle hostname registration.
diff --git a/posts/upgrading-from-ubuntu-bionic-to-focal.mdwn b/posts/upgrading-from-ubuntu-bionic-to-focal.mdwn
index 4179bf1..b828492 100644
--- a/posts/upgrading-from-ubuntu-bionic-to-focal.mdwn
+++ b/posts/upgrading-from-ubuntu-bionic-to-focal.mdwn
@@ -60,10 +60,10 @@ systemd-resolved and its `/etc/resolv.conf` symlink to `/run/systemd/resolve/stu
 I added the following in a new `/etc/NetworkManager/conf.d/mdns.conf` file:
 
     [connection]
-    connection.mdns=2
+    connection.mdns=1
 
-which instructs NetworkManager to [register the hostname and resolve mDNS](https://developer.gnome.org/NetworkManager/stable/settings-connection.html)
-on all network interfaces it manages.
+which instructs NetworkManager to [resolve mDNS](https://developer.gnome.org/NetworkManager/stable/settings-connection.html)
+on all network interfaces it manages but not register a hostname since that's done by [avahi-daemon](https://launchpad.net/ubuntu/+source/avahi).
 
 Then I enabled mDNS globally in systemd-resolved by setting the following
 in `/etc/systemd/resolved.conf`:

Mention the `/etc/resolv.conf` symlink for systemd-resolved.
diff --git a/posts/upgrading-from-ubuntu-bionic-to-focal.mdwn b/posts/upgrading-from-ubuntu-bionic-to-focal.mdwn
index 2e33582..4179bf1 100644
--- a/posts/upgrading-from-ubuntu-bionic-to-focal.mdwn
+++ b/posts/upgrading-from-ubuntu-bionic-to-focal.mdwn
@@ -54,7 +54,8 @@ but not with systemd:
     Global: no
     Link 2 (enp4s0): no
 
-The [best solution I found](https://askubuntu.com/questions/1279792/local-hostname-resolution-is-slow-on-20-04) didn't involve disabling systemd-resolved.
+The [best solution I found](https://askubuntu.com/questions/1279792/local-hostname-resolution-is-slow-on-20-04) involves keeping
+systemd-resolved and its `/etc/resolv.conf` symlink to `/run/systemd/resolve/stub-resolv.conf`.
 
 I added the following in a new `/etc/NetworkManager/conf.d/mdns.conf` file:
 

Improve network setup instructions.
diff --git a/posts/upgrading-from-ubuntu-bionic-to-focal.mdwn b/posts/upgrading-from-ubuntu-bionic-to-focal.mdwn
index f488668..2e33582 100644
--- a/posts/upgrading-from-ubuntu-bionic-to-focal.mdwn
+++ b/posts/upgrading-from-ubuntu-bionic-to-focal.mdwn
@@ -26,7 +26,7 @@ same time:
 
     apt remove safe-rm
 
-## Network problems (netplan.io)
+## Network problems
 
 After the upgrade, my network settings weren't really working properly and
 so I started by switching from
@@ -35,9 +35,10 @@ so I started by switching from
 be the preferred way of configuring the network on Ubuntu now.
 
 Then I found out that netplan.io is
-[incompatible](https://bugs.launchpad.net/netplan/+bug/1830507) with
-[systemd-resolved's handling of `.local`
+[not automatically enabling](https://bugs.launchpad.net/netplan/+bug/1830507) the
+[systemd-resolved handling of `.local`
 hostnames](https://unix.stackexchange.com/questions/459991/how-to-configure-systemd-resolved-for-mdns-multicast-dns-on-local-network/574006#574006).
+
 I would be able to resolve a hostname using
 [avahi](https://launchpad.net/ubuntu/+source/avahi):
 
@@ -49,24 +50,13 @@ but not with systemd:
     $ systemd-resolve machine.local
     machine.local: resolve call failed: 'machine.local' not found
 
-The best work-around I found was to [disable systemd-resolved](https://askubuntu.com/questions/907246/how-to-disable-systemd-resolved-in-ubuntu/907249#907249):
-
-    systemctl stop systemd-resolved.service
-    systemctl disable systemd-resolved.service
-    rm /etc/resolv.conf
-
-## Network problems (NetworkManager)
-
-On a different machine, running NetworkManager instead of netplan.io,
-I used a [different solution](https://askubuntu.com/questions/1279792/local-hostname-resolution-is-slow-on-20-04) which didn't involve disabling systemd-resolved.
-
-Before making these changes, I could see that mdns was disabled in systemd-resolved:
-
     $ resolvectl mdns
     Global: no
     Link 2 (enp4s0): no
 
-To fix it, I added the following in a new `/etc/NetworkManager/conf.d/mdns.conf` file:
+The [best solution I found](https://askubuntu.com/questions/1279792/local-hostname-resolution-is-slow-on-20-04) didn't involve disabling systemd-resolved.
+
+I added the following in a new `/etc/NetworkManager/conf.d/mdns.conf` file:
 
     [connection]
     connection.mdns=2

Add mDNS solution for NetworkManager.
diff --git a/posts/upgrading-from-ubuntu-bionic-to-focal.mdwn b/posts/upgrading-from-ubuntu-bionic-to-focal.mdwn
index db1e646..f488668 100644
--- a/posts/upgrading-from-ubuntu-bionic-to-focal.mdwn
+++ b/posts/upgrading-from-ubuntu-bionic-to-focal.mdwn
@@ -26,7 +26,7 @@ same time:
 
     apt remove safe-rm
 
-## Network problems
+## Network problems (netplan.io)
 
 After the upgrade, my network settings weren't really working properly and
 so I started by switching from
@@ -55,6 +55,41 @@ The best work-around I found was to [disable systemd-resolved](https://askubuntu
     systemctl disable systemd-resolved.service
     rm /etc/resolv.conf
 
+## Network problems (NetworkManager)
+
+On a different machine, running NetworkManager instead of netplan.io,
+I used a [different solution](https://askubuntu.com/questions/1279792/local-hostname-resolution-is-slow-on-20-04) which didn't involve disabling systemd-resolved.
+
+Before making these changes, I could see that mdns was disabled in systemd-resolved:
+
+    $ resolvectl mdns
+    Global: no
+    Link 2 (enp4s0): no
+
+To fix it, I added the following in a new `/etc/NetworkManager/conf.d/mdns.conf` file:
+
+    [connection]
+    connection.mdns=2
+
+which instructs NetworkManager to [register the hostname and resolve mDNS](https://developer.gnome.org/NetworkManager/stable/settings-connection.html)
+on all network interfaces it manages.
+
+Then I enabled mDNS globally in systemd-resolved by setting the following
+in `/etc/systemd/resolved.conf`:
+
+    MulticastDNS=yes
+
+before restarting both services:
+
+    systemctl restart NetworkManager.service systemd-resolved.service
+
+With that in place, `.local` hostnames are resolved properly and I can
+see that mDNS is fully enabled:
+
+    $ resolvectl mdns
+    Global: yes
+    Link 2 (enp4s0): yes
+
 ## Boot problems
 
 For some reason I was able to boot with the kernel I got as part of the

Comment moderation
diff --git a/posts/encrypting-your-home-directory-using/comment_12_f97b9af438b6098eaeefed07c93965ca._comment b/posts/encrypting-your-home-directory-using/comment_12_f97b9af438b6098eaeefed07c93965ca._comment
new file mode 100644
index 0000000..5a39658
--- /dev/null
+++ b/posts/encrypting-your-home-directory-using/comment_12_f97b9af438b6098eaeefed07c93965ca._comment
@@ -0,0 +1,10 @@
+[[!comment format=mdwn
+ username="francois@665656f0ba400877c9b12e8fbb086e45aa01f7c0"
+ nickname="francois"
+ subject="Re: comment 14"
+ date="2020-10-30T21:58:19Z"
+ content="""
+> Thanks for the info, but where do you enter the password when using this method?
+
+As long as your home directory is mounted automatically via `/etc/fstab`, you should be prompted for the password at boot time.
+"""]]

Comment moderation
diff --git a/posts/encrypting-your-home-directory-using/comment_11_faa30b967d333be1b081f48059431007._comment b/posts/encrypting-your-home-directory-using/comment_11_faa30b967d333be1b081f48059431007._comment
new file mode 100644
index 0000000..abe75c5
--- /dev/null
+++ b/posts/encrypting-your-home-directory-using/comment_11_faa30b967d333be1b081f48059431007._comment
@@ -0,0 +1,7 @@
+[[!comment format=mdwn
+ ip="95.86.237.24"
+ subject="comment 14"
+ date="2020-10-30T20:38:10Z"
+ content="""
+Thanks for the info, but where do you enter the password when using this method?
+"""]]

Remove redundant comments.
diff --git a/posts/encrypting-your-home-directory-using/comment_3_e763425321329cabb2a3a82d8d234cbd._comment b/posts/encrypting-your-home-directory-using/comment_3_e763425321329cabb2a3a82d8d234cbd._comment
deleted file mode 100644
index 1d3bd7c..0000000
--- a/posts/encrypting-your-home-directory-using/comment_3_e763425321329cabb2a3a82d8d234cbd._comment
+++ /dev/null
@@ -1,10 +0,0 @@
-[[!comment format=mdwn
- username="http://www.blogger.com/profile/17642321912779274665"
- nickname="jak"
- subject=""
- date="2008-05-24T21:34:00.000+12:00"
- content="""
-You might want to use cp -a /homebackup/{.*,*} /home to also copy dot-files.
-
-
-"""]]
diff --git a/posts/encrypting-your-home-directory-using/comment_5_226684dfe4bdd17438dbde62977e5abb._comment b/posts/encrypting-your-home-directory-using/comment_5_226684dfe4bdd17438dbde62977e5abb._comment
deleted file mode 100644
index dd171bb..0000000
--- a/posts/encrypting-your-home-directory-using/comment_5_226684dfe4bdd17438dbde62977e5abb._comment
+++ /dev/null
@@ -1,9 +0,0 @@
-[[!comment format=mdwn
- claimedauthor="Mike"
- subject=""
- date="2008-05-24T23:10:00.000+12:00"
- content="""
-Nice idea in principle but you might want to change the instructions slightly so that the user doesn't lose all their dotfiles in the process.
-
-
-"""]]
diff --git a/posts/encrypting-your-home-directory-using/comment_9_db3e029e4c901ef109a1fc29df4b4e4b._comment b/posts/encrypting-your-home-directory-using/comment_9_db3e029e4c901ef109a1fc29df4b4e4b._comment
deleted file mode 100644
index c215b5f..0000000
--- a/posts/encrypting-your-home-directory-using/comment_9_db3e029e4c901ef109a1fc29df4b4e4b._comment
+++ /dev/null
@@ -1,9 +0,0 @@
-[[!comment format=mdwn
- claimedauthor="Anonymous"
- subject=""
- date="2008-05-25T02:46:00.000+12:00"
- content="""
-The first step could be simpler: 'cp -a /home /homebackup'. Also, in response to the post that you should use 'cp -a /homebackup/{.*,*} /home' to get back dotfiles -- not only is this usually unnecessary, since dotfiles are usually in /home/USERNAME/, not directly in /home/, but because cp -a is recursive, '/homebackup/.*' includes '/homebackup/..'. Don't do it; it will copy the entire contents of your filesystem into '/home'.
-
-
-"""]]

Mention mouse cursor problem on Pop!_OS 20.04.
diff --git a/posts/creating-a-modern-tiling-desktop-environment-using-i3.mdwn b/posts/creating-a-modern-tiling-desktop-environment-using-i3.mdwn
index 9ebdaa7..0c88861 100644
--- a/posts/creating-a-modern-tiling-desktop-environment-using-i3.mdwn
+++ b/posts/creating-a-modern-tiling-desktop-environment-using-i3.mdwn
@@ -162,4 +162,14 @@ If you find your systray on the wrong display after plugging an external monitor
 
 and then restarting i3.
 
+# Mouse cursor
+
+On [Pop!\_OS](https://pop.system76.com/) 20.04, I ran into a problem where only the mouse was using the
+default Xorg cursor instead of a styled and scalable one.
+
+This was fixed using [this work-around](https://www.reddit.com/r/i3wm/comments/edz4a5/mouse_cursor_size/):
+
+    mkdir -p ~/.icons/default
+    cp -r /usr/share/icons/Pop/* ~/.icons/default/
+
 [[!tag debian]] [[!tag i3]] [[!tag gnome]] [[!tag nzoss]] [[!tag systemd]]

Add GnuBee root partition copy post.
diff --git a/posts/copying-gnubee-root-partition-to-another-drive.mdwn b/posts/copying-gnubee-root-partition-to-another-drive.mdwn
new file mode 100644
index 0000000..6ad9432
--- /dev/null
+++ b/posts/copying-gnubee-root-partition-to-another-drive.mdwn
@@ -0,0 +1,138 @@
+[[!meta title="Copying a GnuBee's root partition onto a new drive"]]
+[[!meta date="2020-10-24T16:30:00.000-07:00"]]
+[[!meta license="[Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)"]]
+
+Here is the process I followed when I moved my GnuBee's root partition from
+one flaky Kingston SSD drive to a brand new Samsung SSD.
+
+It was relatively straightforward, but there are two key points:
+
+1. Make sure you label the root partition `GNUBEE-ROOT`.
+2. Make sure you copy the network configuration from the SSD, not the
+   `tmpfs` mount.
+
+# Copy the partition table
+
+First, with both drives plugged in, I replicated the partition table of the
+first drive (`/dev/sda`):
+
+    # fdisk -l /dev/sda
+    Disk /dev/sda: 111.8 GiB, 120034123776 bytes, 234441648 sectors
+    Disk model: KINGSTON SA400S3
+    Units: sectors of 1 * 512 = 512 bytes
+    Sector size (logical/physical): 512 bytes / 512 bytes
+    I/O size (minimum/optimal): 512 bytes / 512 bytes
+    Disklabel type: gpt
+    Disk identifier: 799CD830-526B-42CE-8EE7-8C94EF098D46
+    
+    Device       Start       End   Sectors   Size Type
+    /dev/sda1     2048   8390655   8388608     4G Linux swap
+    /dev/sda2  8390656 234441614 226050959 107.8G Linux filesystem
+
+onto the second drive (`/dev/sde`):
+
+    # fdisk /dev/sde
+    
+    Welcome to fdisk (util-linux 2.33.1).
+    Changes will remain in memory only, until you decide to write them.
+    Be careful before using the write command.
+    
+    Device does not contain a recognized partition table.
+    Created a new DOS disklabel with disk identifier 0xd011eaba.
+    
+    Command (m for help): g
+    Created a new GPT disklabel (GUID: 83F70325-5BE0-034E-A9E1-1965FEFD8E9F).
+    
+    Command (m for help): n
+    Partition number (1-128, default 1): 
+    First sector (2048-488397134, default 2048): 
+    Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-488397134, default 488397134): +4G
+    
+    Created a new partition 1 of type 'Linux filesystem' and of size 4 GiB.
+    
+    Command (m for help): t
+    Selected partition 1
+    Partition type (type L to list all types): 19
+    Changed type of partition 'Linux filesystem' to 'Linux swap'.
+    
+    Command (m for help): n
+    Partition number (2-128, default 2): 
+    First sector (8390656-488397134, default 8390656): 
+    Last sector, +/-sectors or +/-size{K,M,G,T,P} (8390656-488397134, default 488397134): 234441614
+    
+    Created a new partition 2 of type 'Linux filesystem' and of size 107.8 GiB.
+    
+    Command (m for help): p
+    Disk /dev/sde: 232.9 GiB, 250059350016 bytes, 488397168 sectors
+    Disk model: Samsung SSD 860 
+    Units: sectors of 1 * 512 = 512 bytes
+    Sector size (logical/physical): 512 bytes / 512 bytes
+    I/O size (minimum/optimal): 512 bytes / 512 bytes
+    Disklabel type: gpt
+    Disk identifier: 83F70325-5BE0-034E-A9E1-1965FEFD8E9F
+    
+    Device       Start       End   Sectors   Size Type
+    /dev/sde1     2048   8390655   8388608     4G Linux swap
+    /dev/sde2  8390656 234441614 226050959 107.8G Linux filesystem
+    
+    Command (m for help): w
+    The partition table has been altered.
+    Calling ioctl() to re-read partition table.
+    Syncing disks.
+
+I wasted a large amount of space on the second drive, but that was on
+purpose in case I decide to later on move to a
+[RAID-1](https://en.wikipedia.org/wiki/Standard_RAID_levels#RAID_1) root
+partition with the Kingston SSD.
+
+# Format the partitions
+
+Second, I formated the new partitions:
+
+    # mkswap /dev/sde1 
+    Setting up swapspace version 1, size = 4 GiB (4294963200 bytes)
+    no label, UUID=7a85fbce-2493-45c1-a548-4ec6e827ec29
+
+    # mkfs.ext4 /dev/sde2 
+    mke2fs 1.44.5 (15-Dec-2018)
+    Discarding device blocks: done                            
+    Creating filesystem with 28256369 4k blocks and 7069696 inodes
+    Filesystem UUID: 732a76df-d369-4e7b-857a-dd55fd461bbc
+    Superblock backups stored on blocks: 
+        32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208, 
+        4096000, 7962624, 11239424, 20480000, 23887872
+    
+    Allocating group tables: done                            
+    Writing inode tables: done                            
+    Creating journal (131072 blocks): done
+    Writing superblocks and filesystem accounting information: done   
+
+and labeled the root partition so that the GnuBee can pick it up as it boots
+up:
+
+    e2label /dev/sde2 GNUBEE-ROOT
+
+since `GNUBEE-ROOT` is what [`uboot` will be looking
+for](http://gnubee.org/background_info.html#using-debian-or-openmediavault).
+
+# Copy the data over
+
+Finally, I copied the data over from the original drive to the new one:
+
+    # umount /etc/network
+    # mkdir /mnt/root
+    # mount /dev/sde2 /mnt/root
+    # rsync -aHx --delete --exclude=/dev/* --exclude=/proc/* --exclude=/sys/* --exclude=/tmp/* --exclude=/mnt/* --exclude=/lost+found/* --exclude=/media/* --exclude=/rom/* /* /mnt/root/
+    # sync
+
+Note that if you don't unmount `/etc/network/`, you'll be copying the
+override provided at boot time instead of the underlying config that's on
+the root partition. The reason that this matters is that the [script that
+renames the network interfaces to `ethblack` and
+`ethblue`](https://github.com/neilbrown/gnubee-tools/blob/64e98ce8352d799b22967bbd2681f325e683b70d/initramfs/init#L77-L88)
+expects specific files in order to produce a working network configuration.
+If you copy the final *modified* config files then you end up with an
+bind-mounted empty directory as `/etc/network`, and the network interfaces
+can't be brought up successfully.
+
+[[!tag gnubee]]

Comment moderation
diff --git a/posts/using-letsencrypt-cert-with-asterisk/comment_1_7d1810b82ce0d3f0e0904e5f0c62d535._comment b/posts/using-letsencrypt-cert-with-asterisk/comment_1_7d1810b82ce0d3f0e0904e5f0c62d535._comment
new file mode 100644
index 0000000..bda51ba
--- /dev/null
+++ b/posts/using-letsencrypt-cert-with-asterisk/comment_1_7d1810b82ce0d3f0e0904e5f0c62d535._comment
@@ -0,0 +1,11 @@
+[[!comment format=mdwn
+ ip="2001:2003:f04b:7800:fd0f:a515:b7e0:67e5"
+ claimedauthor="Steve Kemp"
+ url="https://steve.kemp.fi/"
+ subject="Alternative .."
+ date="2020-10-18T14:26:50Z"
+ content="""
+That's not a bad solution, but it might be simpler to use a client which handles a DNS-challenge.
+
+With DNS-challenges you don't have to worry about webservers, or internal/external firewalling.
+"""]]

Add post about Let's Encrypt cert with Asterisk.
diff --git a/posts/sip-encryption-on-voip-ms.mdwn b/posts/sip-encryption-on-voip-ms.mdwn
index 37697ae..f7ca9c2 100644
--- a/posts/sip-encryption-on-voip-ms.mdwn
+++ b/posts/sip-encryption-on-voip-ms.mdwn
@@ -56,19 +56,26 @@ The dialplan didn't change and so I still have the following in
 
 ## Server certificate
 
-The only thing I still need to fix is to make this error message go away in
-my logs:
+After setting everything up, I saw the following error in my logs:
 
     asterisk[8691]: ERROR[8691]: tcptls.c:966 in __ssl_setup: TLS/SSL error loading cert file. <asterisk.pem>
 
-It appears to be related to the fact that I didn't set `tlscertfile` in
+due to the fact that I didn't set `tlscertfile` in
 `/etc/asterisk/sip.conf` and that it's using its default value of
 `asterisk.pem`, a non-existent file.
 
-Since my Asterisk server is only acting as a TLS *client*, and not a TLS
-*server*, there's probably no harm in not having a certificate. That said,
-it looks pretty easy to [use a Let's Encrypt cert with
-Asterisk](https://community.asterisk.org/t/has-anyone-used-letsencrypt-to-setup-ssl-for-asterisk/67145/6).
+I initially thought that since my Asterisk server is only acting as a TLS
+*client*, and not a TLS *server*, there's probably no harm in not having a
+certificate. In practice however, my TLS connection was a little unreliable
+and it would regularly fail with the following TLS errors:
+
+    asterisk[534775]: ERROR[534879]: iostream.c:538 in ast_iostream_close: SSL_shutdown() failed: error:00000005:lib(0):func(0):DH lib, Underlying BIO error: Broken pipe
+    asterisk[534775]: ERROR[610289]: tcptls.c:553 in ast_tcptls_client_start: Unable to connect SIP socket to w.x.y.z:5061: Connection refused
+    asterisk[534775]: ERROR[610289]: tcptls.c:553 in ast_tcptls_client_start: Unable to connect SIP socket to w.x.y.z:5061: Connection reset by peer
+
+I therefore decided to [setup a Let's Encrypt certificate in
+Asterisk](https://feeding.cloud.geek.nz/posts/using-letsencrypt-cert-with-asterisk/)
+to eliminate the original error.
 
 ## Firewall
 
@@ -88,4 +95,4 @@ recommendations](https://wiki.voip.ms/article/Firewall) in
 where `w.x.y.z` is the IP address of `servername.voip.ms` as returned by
 `dig +short servername.voip.ms`.
 
-[[!tag debian]] [[!tag asterisk]] [[!tag letsencrypt]] [[!tag voipms]]
+[[!tag debian]] [[!tag asterisk]] [[!tag ssl]] [[!tag voipms]]
diff --git a/posts/using-letsencrypt-cert-with-asterisk.mdwn b/posts/using-letsencrypt-cert-with-asterisk.mdwn
new file mode 100644
index 0000000..fcd3350
--- /dev/null
+++ b/posts/using-letsencrypt-cert-with-asterisk.mdwn
@@ -0,0 +1,86 @@
+[[!meta title="Using a Let's Encrypt TLS certificate with Asterisk 16.2"]]
+[[!meta date="2020-10-17T17:45:00.000-07:00"]]
+[[!meta license="[Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)"]]
+
+In order to fix the following error after setting up [SIP
+TLS](https://feeding.cloud.geek.nz/posts/sip-encryption-on-voip-ms/) in
+[Asterisk](https://www.asterisk.org/) 16.2:
+
+    asterisk[8691]: ERROR[8691]: tcptls.c:966 in __ssl_setup: TLS/SSL error loading cert file. <asterisk.pem>
+
+I created a [Let's Encrypt](https://letsencrypt.org/) certificate using
+[certbot](https://certbot.eff.org/):
+
+    apt install certbot
+    certbot certonly --standalone -d hostname.example.com
+
+To enable the `asterisk` user to load the certificate successfuly (it
+doesn't permission to access to the certificates under `/etc/letsencrypt/`),
+I copied it to the right directory:
+
+    cp /etc/letsencrypt/live/hostname.example.com/privkey.pem /etc/asterisk/asterisk.key
+    cp /etc/letsencrypt/live/hostname.example.com/fullchain.pem /etc/asterisk/asterisk.cert
+    chown asterisk:asterisk /etc/asterisk/asterisk.cert /etc/asterisk/asterisk.key
+    chmod go-rwx /etc/asterisk/asterisk.cert /etc/asterisk/asterisk.key
+
+Then I set the following variables in `/etc/asterisk/sip.conf`:
+
+    tlscertfile=/etc/asterisk/asterisk.cert
+    tlsprivatekey=/etc/asterisk/asterisk.key
+
+## Automatic renewal
+
+The machine on which I run asterisk has a tricky Apache setup:
+
+- a webserver is running on port 80
+- port 80 is restricted to the local network
+
+This meant that the certbot domain ownership checks would get blocked by the
+firewall, and I couldn't open that port without exposing the private
+webserver to the Internet.
+
+So I ended up disabling the built-in certbot renewal mechanism:
+
+    systemctl disable certbot.timer certbot.service
+    systemctl stop certbot.timer certbot.service
+
+and then writing my own script in `/etc/cron.daily/certbot-francois`:
+
+    #!/bin/bash
+    TEMPFILE=`mktemp`
+    
+    # Stop Apache and backup firewall.
+    /bin/systemctl stop apache2.service
+    /usr/sbin/iptables-save > $TEMPFILE
+    
+    # Open up port 80 to the whole world.
+    /usr/sbin/iptables -D INPUT -j LOGDROP
+    /usr/sbin/iptables -A INPUT -p tcp --dport 80 -j ACCEPT
+    /usr/sbin/iptables -A INPUT -j LOGDROP
+    
+    # Renew all certs.
+    /usr/bin/certbot renew --quiet
+    
+    # Restore firewall and restart Apache.
+    /usr/sbin/iptables -D INPUT -p tcp --dport 80 -j ACCEPT
+    /usr/sbin/iptables-restore < $TEMPFILE
+    /bin/systemctl start apache2.service
+    
+    # Copy certificate into asterisk.
+    cp /etc/letsencrypt/live/hostname.example.com/privkey.pem /etc/asterisk/asterisk.key
+    cp /etc/letsencrypt/live/hostname.example.com/fullchain.pem /etc/asterisk/asterisk.cert
+    chown asterisk:asterisk /etc/asterisk/asterisk.cert /etc/asterisk/asterisk.key
+    chmod go-rwx /etc/asterisk/asterisk.cert /etc/asterisk/asterisk.key
+    /bin/systemctl restart asterisk.service
+    
+    # Commit changes to etckeeper.
+    pushd /etc/ > /dev/null
+    /usr/bin/git add letsencrypt asterisk
+    DIFFSTAT="$(/usr/bin/git diff --cached --stat)"
+    if [ -n "$DIFFSTAT" ] ; then
+        /usr/bin/git commit --quiet -m "Renewed letsencrypt certs."
+        echo "$DIFFSTAT"
+    fi
+    popd > /dev/null
+
+[[!tag letsencrypt]] [[!tag asterisk]] [[!tag debian]] [[!tag ssl]]

Remove now erroneous word.
This should have been part of 1860c102c323e5f0c5085e1ca6a3d36ea4024d89.
diff --git a/posts/making-apache-website-available-tor-onion-service.mdwn b/posts/making-apache-website-available-tor-onion-service.mdwn
index 70a985e..bd21dd8 100644
--- a/posts/making-apache-website-available-tor-onion-service.mdwn
+++ b/posts/making-apache-website-available-tor-onion-service.mdwn
@@ -109,7 +109,7 @@ and available in [Brave Nightly](https://brave.com/download-nightly/)):
 
 ![](/posts/making-apache-website-available-tor-onion-service/onion-location.png)
 
-Testing that the `Alt-Svc` is working also required using the [Tor Browser](https://www.torproject.org/download/)
+Testing that the `Alt-Svc` is working required using the [Tor Browser](https://www.torproject.org/download/)
 since that's [not yet supported in
 Brave](https://github.com/brave/brave-browser/issues/1121):
 

Use Brave Nightly to test Onion-Location header.
diff --git a/posts/making-apache-website-available-tor-onion-service.mdwn b/posts/making-apache-website-available-tor-onion-service.mdwn
index 88d7f71..70a985e 100644
--- a/posts/making-apache-website-available-tor-onion-service.mdwn
+++ b/posts/making-apache-website-available-tor-onion-service.mdwn
@@ -102,14 +102,14 @@ window](https://support.brave.com/hc/en-us/articles/360018121491-What-is-a-Priva
 - <http://ixrdj3iwwhkuau5tby5jh3a536a2rdhpbdbu6ldhng43r47kim7a3lid.onion/>
 - <https://ixrdj3iwwhkuau5tby5jh3a536a2rdhpbdbu6ldhng43r47kim7a3lid.onion/> (a TLS certificate error is expected)
 
-I also checked using the [Tor Browser](https://www.torproject.org/download/)
-that the [`Onion-Location`
-header](https://community.torproject.org/onion-services/advanced/onion-location/)
-is correctly recognized and triggers the display of a button in the URL bar:
+I also checked that the main URL (<https://fmarier.org/>) exposes a working
+[`Onion-Location` header](https://community.torproject.org/onion-services/advanced/onion-location/)
+which triggers the display of a button in the URL bar (recently [merged](https://github.com/brave/brave-core/pull/6762)
+and available in [Brave Nightly](https://brave.com/download-nightly/)):
 
 ![](/posts/making-apache-website-available-tor-onion-service/onion-location.png)
 
-Testing that the `Alt-Svc` is working also required using the Tor Browser
+Testing that the `Alt-Svc` is working also required using the [Tor Browser](https://www.torproject.org/download/)
 since that's [not yet supported in
 Brave](https://github.com/brave/brave-browser/issues/1121):
 
diff --git a/posts/making-apache-website-available-tor-onion-service/onion-location.png b/posts/making-apache-website-available-tor-onion-service/onion-location.png
index f735bcf..04f28a6 100644
Binary files a/posts/making-apache-website-available-tor-onion-service/onion-location.png and b/posts/making-apache-website-available-tor-onion-service/onion-location.png differ

Add Tor Onion Service post.
diff --git a/posts/making-apache-website-available-tor-onion-service.mdwn b/posts/making-apache-website-available-tor-onion-service.mdwn
new file mode 100644
index 0000000..88d7f71
--- /dev/null
+++ b/posts/making-apache-website-available-tor-onion-service.mdwn
@@ -0,0 +1,148 @@
+[[!meta title="Making an Apache website available as a Tor Onion Service"]]
+[[!meta date="2020-10-13T20:45:00.000-07:00"]]
+[[!meta license="[Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)"]]
+
+As part of the [#MoreOnionsPorFavor
+campaign](https://blog.torproject.org/more-onions-porfavor), I decided to
+follow [`brave.com`'s lead](https://brave.com/new-onion-service/) and make
+my [homepage](https://fmarier.org) available as a [Tor onion
+service](https://2019.www.torproject.org/docs/tor-onion-service.html.en).
+
+## Tor daemon setup
+
+I started by installing the Tor daemon locally:
+
+    apt install tor
+
+and then setting the following in `/etc/tor/torrc`:
+
+    SocksPort 0
+    SocksPolicy reject *
+    HiddenServiceDir /var/lib/tor/hidden_service/
+    HiddenServicePort 80 [2600:3c04::f03c:91ff:fe8c:61ac]:80
+    HiddenServicePort 443 [2600:3c04::f03c:91ff:fe8c:61ac]:443
+    HiddenServiceVersion 3
+    HiddenServiceNonAnonymousMode 1
+    HiddenServiceSingleHopMode 1
+
+in order to create a [version 3 onion
+service](https://blog.torproject.org/v2-deprecation-timeline) without
+actually running a [Tor relay](https://community.torproject.org/relay/).
+
+Note that since I am making a public website available over Tor, I do not
+need the location of the website to be hidden and so I used the same
+settings as
+[Cloudflare](https://blog.cloudflare.com/cloudflare-onion-service/) in their
+public Tor proxy.
+
+Also, I explicitly used the **external IPv6 address** of my server in the
+configuration in order to prevent [localhost
+bypasses](https://riseup.net/en/security/network-security/tor/onionservices-best-practices#be-careful-of-localhost-bypasses).
+
+After restarting the Tor daemon to reload the configuration file:
+
+    systemctl restart tor.service
+
+I looked for the address of my onion service:
+
+    $ cat /var/lib/tor/hidden_service/hostname 
+    ixrdj3iwwhkuau5tby5jh3a536a2rdhpbdbu6ldhng43r47kim7a3lid.onion
+
+## Apache configuration
+
+Next, I enabled a few required Apache modules:
+
+    a2enmod mpm_event
+    a2enmod http2
+    a2enmod headers
+
+and configured my Apache vhosts in `/etc/apache2/sites-enabled/www.conf`:
+
+    <VirtualHost *:443>
+        ServerName fmarier.org
+        ServerAlias ixrdj3iwwhkuau5tby5jh3a536a2rdhpbdbu6ldhng43r47kim7a3lid.onion
+    
+        Protocols h2, http/1.1
+        Header set Onion-Location "http://ixrdj3iwwhkuau5tby5jh3a536a2rdhpbdbu6ldhng43r47kim7a3lid.onion%{REQUEST_URI}s"
+        Header set alt-svc 'h2="ixrdj3iwwhkuau5tby5jh3a536a2rdhpbdbu6ldhng43r47kim7a3lid.onion:443"; ma=315360000; persist=1'
+        Header add Strict-Transport-Security: "max-age=63072000"
+    
+        Include /etc/fmarier-org/www-common.include
+    
+        SSLEngine On
+        SSLCertificateFile /etc/letsencrypt/live/fmarier.org/fullchain.pem
+        SSLCertificateKeyFile /etc/letsencrypt/live/fmarier.org/privkey.pem
+    </VirtualHost>
+    
+    <VirtualHost *:80>
+        ServerName fmarier.org
+        Redirect permanent / https://fmarier.org/
+    </VirtualHost>
+         
+    <VirtualHost *:80>
+        ServerName ixrdj3iwwhkuau5tby5jh3a536a2rdhpbdbu6ldhng43r47kim7a3lid.onion
+        Include /etc/fmarier-org/www-common.include
+    </VirtualHost>
+
+Note that `/etc/fmarier-org/www-common.include` contains all of the
+configuration options that are common to both the HTTP and the HTTPS sites
+(e.g. document root, caching headers, aliases, etc.).
+
+Finally, I restarted Apache:
+
+    apache2ctl configtest
+    systemctl restart apache2.service
+
+## Testing
+
+In order to test that my website is correctly available at its `.onion`
+address, I opened the following URLs in a [Brave Tor
+window](https://support.brave.com/hc/en-us/articles/360018121491-What-is-a-Private-Window-with-Tor-):
+
+- <http://ixrdj3iwwhkuau5tby5jh3a536a2rdhpbdbu6ldhng43r47kim7a3lid.onion/>
+- <https://ixrdj3iwwhkuau5tby5jh3a536a2rdhpbdbu6ldhng43r47kim7a3lid.onion/> (a TLS certificate error is expected)
+
+I also checked using the [Tor Browser](https://www.torproject.org/download/)
+that the [`Onion-Location`
+header](https://community.torproject.org/onion-services/advanced/onion-location/)
+is correctly recognized and triggers the display of a button in the URL bar:
+
+![](/posts/making-apache-website-available-tor-onion-service/onion-location.png)
+
+Testing that the `Alt-Svc` is working also required using the Tor Browser
+since that's [not yet supported in
+Brave](https://github.com/brave/brave-browser/issues/1121):
+
+1. Open <https://fmarier.org>.
+2. Wait 30 seconds.
+3. Reload the page.
+
+On the server side, I saw the following:
+
+    2a0b:f4c2:2::1 - - [14/Oct/2020:02:42:20 +0000] "GET / HTTP/2.0" 200 2696 "-" "Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.0"
+    2600:3c04::f03c:91ff:fe8c:61ac - - [14/Oct/2020:02:42:53 +0000] "GET / HTTP/2.0" 200 2696 "-" "Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.0"
+
+That first IP address is from a Tor exit node:
+
+    $ whois 2a0b:f4c2:2::1
+    ...
+    inet6num:       2a0b:f4c2::/40
+    netname:        MK-TOR-EXIT
+    remarks:        -----------------------------------
+    remarks:        This network is used for Tor Exits.
+    remarks:        We do not have any logs at all.
+    remarks:        For more information please visit:
+    remarks:        https://www.torproject.org
+
+which indicates that the first request was **not using** the `.onion`
+address.
+
+The second IP address is the one for my server:
+
+    $ dig +short -x 2600:3c04::f03c:91ff:fe8c:61ac
+    hafnarfjordur.fmarier.org.
+
+which indicates that the second request to Apache came from the Tor relay
+running on my server, hence **using** the `.onion` address.
+
+[[!tag debian]] [[!tag tor]] [[!tag apache]] [[!tag brave]]
diff --git a/posts/making-apache-website-available-tor-onion-service/onion-location.png b/posts/making-apache-website-available-tor-onion-service/onion-location.png
new file mode 100644
index 0000000..f735bcf
Binary files /dev/null and b/posts/making-apache-website-available-tor-onion-service/onion-location.png differ

Add post on focal upgrade.
diff --git a/posts/upgrading-from-ubuntu-bionic-to-focal.mdwn b/posts/upgrading-from-ubuntu-bionic-to-focal.mdwn
new file mode 100644
index 0000000..db1e646
--- /dev/null
+++ b/posts/upgrading-from-ubuntu-bionic-to-focal.mdwn
@@ -0,0 +1,76 @@
+[[!meta title="Upgrading from Ubuntu 18.04 bionic to 20.04 focal"]]
+[[!meta date="2020-10-11T12:30:00.000-07:00"]]
+[[!meta license="[Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)"]]
+
+I recently upgraded from Ubuntu 18.04.5 (bionic) to 20.04.1 (focal) and it
+was one of the roughest Ubuntu upgrades I've gone through in a while. Here
+are the notes I took on avoiding or fixing the problems I ran into.
+
+## Preparation
+
+Before going through the upgrade, I disabled the configurations which I know
+interfere with the process:
+
+- Enable [etckeeper](https://launchpad.net/ubuntu/+source/etckeeper) *auto-commits before install* by putting the
+  following in `/etc/etckeeper/etckeeper.conf`:
+
+        AVOID_COMMIT_BEFORE_INSTALL=0
+
+- Remount `/tmp` as exectuable:
+
+        mount -o remount,exec /tmp
+
+Another step I should have taken but didn't, was to temporarily remove [safe-rm](https://launchpad.net/ubuntu/+source/safe-rm)
+since it [caused some problems](https://bugs.launchpad.net/ubuntu/+source/ubuntu-release-upgrader/+bug/1893724) related to a Perl upgrade happening at the
+same time:
+
+    apt remove safe-rm
+
+## Network problems
+
+After the upgrade, my network settings weren't really working properly and
+so I started by switching from
+[ifupdown](https://launchpad.net/ubuntu/+source/ifupdown) to
+[netplan.io](https://launchpad.net/ubuntu/+source/netplan.io) which seems to
+be the preferred way of configuring the network on Ubuntu now.
+
+Then I found out that netplan.io is
+[incompatible](https://bugs.launchpad.net/netplan/+bug/1830507) with
+[systemd-resolved's handling of `.local`
+hostnames](https://unix.stackexchange.com/questions/459991/how-to-configure-systemd-resolved-for-mdns-multicast-dns-on-local-network/574006#574006).
+I would be able to resolve a hostname using
+[avahi](https://launchpad.net/ubuntu/+source/avahi):
+
+    $ avahi-resolve --name machine.local
+    machine.local	192.168.1.5
+
+but not with systemd:
+
+    $ systemd-resolve machine.local
+    machine.local: resolve call failed: 'machine.local' not found
+
+The best work-around I found was to [disable systemd-resolved](https://askubuntu.com/questions/907246/how-to-disable-systemd-resolved-in-ubuntu/907249#907249):
+
+    systemctl stop systemd-resolved.service
+    systemctl disable systemd-resolved.service
+    rm /etc/resolv.conf
+
+## Boot problems
+
+For some reason I was able to boot with the kernel I got as part of the
+focal update, but a later kernel update rendered my machine unbootable.
+
+Adding some missing [RAID](https://en.wikipedia.org/wiki/RAID)-related modules to
+`/etc/initramfs-tools/modules`:
+
+    raid1
+    dmraid
+    md-raid1
+
+and then re-creating all initramfs:
+
+    update-initramfs -u -k all
+
+seemed to do the trick.
+
+[[!tag ubuntu]] [[!tag systemd]] [[!tag raid]]

Link to the right upstream homepage.
The `iperf` package is IPerf2 (https://iperf2.sourceforge.io/) whereas `iperf3`
is iPerf (https://iperf.fr/).
diff --git a/posts/npr-modem-setup-testing-linux.mdwn b/posts/npr-modem-setup-testing-linux.mdwn
index c2b1318..c4fa330 100644
--- a/posts/npr-modem-setup-testing-linux.mdwn
+++ b/posts/npr-modem-setup-testing-linux.mdwn
@@ -128,7 +128,7 @@ two machines, I decided to run a quick test to measure the available
 bandwidth in an ideal setting (i.e. the two antennas very close to each
 other).
 
-On both computers, I installed [iperf](https://iperf.fr/):
+On both computers, I installed [iperf](https://iperf2.sourceforge.io/):
 
     apt install iperf
 

Comment moderation
diff --git a/posts/repairing-corrupt-ext4-root-partition/comment_1_212a8a6ad1ed4a94c76d530f00631934._comment b/posts/repairing-corrupt-ext4-root-partition/comment_1_212a8a6ad1ed4a94c76d530f00631934._comment
new file mode 100644
index 0000000..e769999
--- /dev/null
+++ b/posts/repairing-corrupt-ext4-root-partition/comment_1_212a8a6ad1ed4a94c76d530f00631934._comment
@@ -0,0 +1,13 @@
+[[!comment format=mdwn
+ ip="2a01:261:355:1d00:6ef0:49ff:fe08:ef01"
+ claimedauthor="Tomaž"
+ url="https://www.tablix.org/~avian/blog/"
+ subject="Suggestions about filesystem corruption"
+ date="2020-09-27T11:19:20Z"
+ content="""
+Hi. Two things you might want to check, if you haven't already.
+
+See if the \"UDMA_CRC_Error_Count\" or \"CRC_Error_Count\" attribute (199) reported by your smartctl is >0 and slowly increasing over time. It's not marked as an error by smartctl and it's easy to miss. It's an indication of a flaky SATA bus connection and I've seen this cause filesystem corruption (I'm guessing because every once in a while CRC will randomly end up OK for a corrupted command).
+
+The other thing is to check if you're running \"fstrim\". Some SSDs are known to have bugs with that and you might be running a kernel that doesn't yet have a workaround or blacklist for your particular model or SSD firmware version. See [[https://github.com/torvalds/linux/blob/master/drivers/ata/libata-core.c#L3774]].
+"""]]

Add necessary firewall rules.
diff --git a/posts/sip-encryption-on-voip-ms.mdwn b/posts/sip-encryption-on-voip-ms.mdwn
index 0d99118..37697ae 100644
--- a/posts/sip-encryption-on-voip-ms.mdwn
+++ b/posts/sip-encryption-on-voip-ms.mdwn
@@ -70,4 +70,22 @@ Since my Asterisk server is only acting as a TLS *client*, and not a TLS
 it looks pretty easy to [use a Let's Encrypt cert with
 Asterisk](https://community.asterisk.org/t/has-anyone-used-letsencrypt-to-setup-ssl-for-asterisk/67145/6).
 
-[[!tag debian]] [[!tag asterisk]] [[!tag nzoss]] [[!tag letsencrypt]] [[!tag voipms]]
+## Firewall
+
+This originally appeared not to be necessary, but I found that I ran into a
+number of intermittent connection errors such as:
+
+    asterisk[1280841]: ERROR[1537920]: tcptls.c:553 in ast_tcptls_client_start: Unable to connect SIP socket to w.x.y.z:5061: Connection reset by peer
+
+and so I put the [official firewall
+recommendations](https://wiki.voip.ms/article/Firewall) in
+`/etc/network/iptables.up.rules`:
+
+    # SIP and RTP on TCP/UDP (servername.voip.ms)
+    -A INPUT -s w.x.y.z/32 -p tcp --dport 5061 -j ACCEPT
+    -A INPUT -s w.x.y.z/32 -p udp --sport 5004:5005 --dport 10001:20000 -j ACCEPT
+
+where `w.x.y.z` is the IP address of `servername.voip.ms` as returned by
+`dig +short servername.voip.ms`.
+
+[[!tag debian]] [[!tag asterisk]] [[!tag letsencrypt]] [[!tag voipms]]

creating tag page tags/ext4
diff --git a/tags/ext4.mdwn b/tags/ext4.mdwn
new file mode 100644
index 0000000..57c407f
--- /dev/null
+++ b/tags/ext4.mdwn
@@ -0,0 +1,4 @@
+[[!meta title="pages tagged ext4"]]
+
+[[!inline pages="tagged(ext4)" actions="no" archive="yes"
+feedshow=10]]

Add post about ext4 root partition corruption.
diff --git a/posts/repairing-corrupt-ext4-root-partition.mdwn b/posts/repairing-corrupt-ext4-root-partition.mdwn
new file mode 100644
index 0000000..00d3ef7
--- /dev/null
+++ b/posts/repairing-corrupt-ext4-root-partition.mdwn
@@ -0,0 +1,112 @@
+[[!meta title="Repairing a corrupt ext4 root partition"]]
+[[!meta date="2020-09-26T12:45:00.000-07:00"]]
+[[!meta license="[Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)"]]
+
+I ran into filesystem corruption
+([ext4](https://en.wikipedia.org/wiki/Ext4)) on the root partition of my
+[backup server](https://feeding.cloud.geek.nz/posts/backing-up-to-gnubee2/)
+which caused it to go into read-only mode. Since it's the root partition,
+it's not possible to unmount it and repair it while it's running. Normally I
+would boot from an [Ubuntu live CD / USB
+stick](https://ubuntu.com/download/alternative-downloads), but in this case
+the machine is using the
+[`mipsel`](https://en.wikipedia.org/wiki/MIPS_architecture) architecture and
+so that's not an option.
+
+# Repair using a USB enclosure
+
+I had to pull the shutdown the server and then pull the SSD drive out. I
+then moved it to an external USB enclosure and connected it to my laptop.
+
+I started with an automatic filesystem repair:
+
+    fsck.ext4 -pf /dev/sde2
+
+which failed for some reason and so I moved to an interactive repair:
+
+    fsck.ext4 -f /dev/sde2
+
+Once all of the errors were fixed, I ran a full surface scan to update the
+list of bad blocks:
+
+    fsck.ext4 -c /dev/sde2
+
+Finally, I forced another check to make sure that everything was fixed at
+the filesystem level:
+
+    fsck.ext4 -f /dev/sde2
+
+# Fix invalid alternate GPT
+
+The other thing I noticed is this messge in my `dmesg` log:
+
+    scsi 8:0:0:0: Direct-Access     KINGSTON  SA400S37120     SBFK PQ: 0 ANSI: 6
+    sd 8:0:0:0: Attached scsi generic sg4 type 0
+    sd 8:0:0:0: [sde] 234441644 512-byte logical blocks: (120 GB/112 GiB)
+    sd 8:0:0:0: [sde] Write Protect is off
+    sd 8:0:0:0: [sde] Mode Sense: 31 00 00 00
+    sd 8:0:0:0: [sde] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
+    sd 8:0:0:0: [sde] Optimal transfer size 33553920 bytes
+    Alternate GPT is invalid, using primary GPT.
+     sde: sde1 sde2
+
+I therefore checked to see if the partition table looked fine and got the
+following:
+
+    $ fdisk -l /dev/sde
+    GPT PMBR size mismatch (234441643 != 234441647) will be corrected by write.
+    The backup GPT table is not on the end of the device. This problem will be corrected by write.
+    Disk /dev/sde: 111.8 GiB, 120034123776 bytes, 234441648 sectors
+    Disk model: KINGSTON SA400S3
+    Units: sectors of 1 * 512 = 512 bytes
+    Sector size (logical/physical): 512 bytes / 512 bytes
+    I/O size (minimum/optimal): 512 bytes / 512 bytes
+    Disklabel type: gpt
+    Disk identifier: 799CD830-526B-42CE-8EE7-8C94EF098D46
+    
+    Device       Start       End   Sectors   Size Type
+    /dev/sde1     2048   8390655   8388608     4G Linux swap
+    /dev/sde2  8390656 234441614 226050959 107.8G Linux filesystem
+
+It turns out that all I had to do, since only the backup / alternate GPT
+partition table was corrupt and the primary one was fine, was to re-write
+the partition table:
+
+    $ fdisk /dev/sde
+    
+    Welcome to fdisk (util-linux 2.33.1).
+    Changes will remain in memory only, until you decide to write them.
+    Be careful before using the write command.
+    
+    GPT PMBR size mismatch (234441643 != 234441647) will be corrected by write.
+    The backup GPT table is not on the end of the device. This problem will be corrected by write.
+    
+    Command (m for help): w
+    
+    The partition table has been altered.
+    Syncing disks.
+
+# Run SMART checks
+
+Since I still didn't know what caused the filesystem corruption in the first
+place, I decided to do one last check:
+[SMART](https://en.wikipedia.org/wiki/S.M.A.R.T.) errors.
+
+I couldn't do this via the USB enclosure since the SMART commands aren't
+forwarded to the drive and so I popped the drive back into the backup
+server and booted it up.
+
+First, I checked whether any SMART errors had been reported using
+[smartmontools](https://www.smartmontools.org/):
+
+    smartctl -a /dev/sda
+
+That didn't show any errors and so I kicked off an extended test:
+
+    smartctl -t long /dev/sda
+
+which ran for 30 minutes and then passed without any errors.
+
+The mystery remains unsolved.
+
+[[!tag gnubee]] [[!tag smart]] [[!tag ext4]] [[!tag debian]]

Create a new ext4 tag.
diff --git a/posts/encrypting-your-home-directory-using.mdwn b/posts/encrypting-your-home-directory-using.mdwn
index 9f2e21e..44e3f99 100644
--- a/posts/encrypting-your-home-directory-using.mdwn
+++ b/posts/encrypting-your-home-directory-using.mdwn
@@ -33,4 +33,4 @@ If you happen to have `/home` on a separate partition already (`/dev/sda5` in th
 
 That's it. Now to fully secure your laptop against theft, you should think about an [encrypted backup strategy](http://packages.debian.org/sid/duplicity) for your data...
 
-[[!tag debian]] [[!tag sysadmin]] [[!tag ubuntu]] [[!tag luks]]
+[[!tag debian]] [[!tag sysadmin]] [[!tag ubuntu]] [[!tag luks]] [[!tag ext4]]
diff --git a/posts/manually-expanding-raid1-array-ubuntu.mdwn b/posts/manually-expanding-raid1-array-ubuntu.mdwn
index 97712c6..58df32d 100644
--- a/posts/manually-expanding-raid1-array-ubuntu.mdwn
+++ b/posts/manually-expanding-raid1-array-ubuntu.mdwn
@@ -148,4 +148,4 @@ The last step was to regenerate the initramfs:
 before rebooting into something that looks exactly like the original RAID1
 array but with twice the size.
 
-[[!tag nzoss]] [[!tag sysadmin]] [[!tag debian]] [[!tag raid]] [[!tag ubuntu]] [[!tag luks]]
+[[!tag ext4]] [[!tag sysadmin]] [[!tag debian]] [[!tag raid]] [[!tag ubuntu]] [[!tag luks]]
diff --git a/posts/raid1-alternative-for-ssd-drives.mdwn b/posts/raid1-alternative-for-ssd-drives.mdwn
index 2176781..a29371b 100644
--- a/posts/raid1-alternative-for-ssd-drives.mdwn
+++ b/posts/raid1-alternative-for-ssd-drives.mdwn
@@ -56,4 +56,4 @@ Finally, after reading this [excellent LWN article](http://lwn.net/Articles/4084
   
 Is there anything else I should be doing to make sure I get the most out of my SSD?
 
-[[!tag grub]] [[!tag debian]] [[!tag sysadmin]] [[!tag ubuntu]] [[!tag nzoss]] [[!tag raid]]
+[[!tag grub]] [[!tag debian]] [[!tag sysadmin]] [[!tag ubuntu]] [[!tag raid]] [[!tag ext4]]
diff --git a/posts/setting-up-raid-on-existing.mdwn b/posts/setting-up-raid-on-existing.mdwn
index a1a40d5..e02a640 100644
--- a/posts/setting-up-raid-on-existing.mdwn
+++ b/posts/setting-up-raid-on-existing.mdwn
@@ -217,4 +217,4 @@ Something else you should seriously consider is to install the `smartmontools` p
 
 These checks, performed by the hard disk controllers directly, could warn you of imminent failures ahead of time. Personally, when I start seeing errors in the SMART log (`smartctl -a /dev/sda`), I order a new drive straight away.
 
-[[!tag grub]] [[!tag raid]] [[!tag debian]] [[!tag sysadmin]] [[!tag ubuntu]] [[!tag nzoss]]
+[[!tag grub]] [[!tag raid]] [[!tag debian]] [[!tag sysadmin]] [[!tag ubuntu]] [[!tag ext4]]
diff --git a/posts/two-tier-encryption-strategy-archiving.mdwn b/posts/two-tier-encryption-strategy-archiving.mdwn
index 7502669..7f68ae0 100644
--- a/posts/two-tier-encryption-strategy-archiving.mdwn
+++ b/posts/two-tier-encryption-strategy-archiving.mdwn
@@ -47,4 +47,4 @@ and:
 
     cryptmount -u archives
 
-[[!tag catalyst]] [[!tag debian]] [[!tag sysadmin]] [[!tag security]] [[!tag ubuntu]] [[!tag cryptmount]]
+[[!tag ext4]] [[!tag debian]] [[!tag sysadmin]] [[!tag security]] [[!tag ubuntu]] [[!tag cryptmount]]

Poor man's RAID-1 on the GnuBee.
diff --git a/posts/backing-up-to-gnubee2.mdwn b/posts/backing-up-to-gnubee2.mdwn
index 2226a34..3fcac7c 100644
--- a/posts/backing-up-to-gnubee2.mdwn
+++ b/posts/backing-up-to-gnubee2.mdwn
@@ -62,6 +62,28 @@ and added the following to `/etc/fstab`:
 
     /dev/md127 /mnt/data/ ext4 noatime,nodiratime 0 2
 
+### Keeping a copy of the root partition
+
+In order to survive a failing SSD drive, I could have bought a second SSD
+and gone for a
+[RAID-1](https://en.wikipedia.org/wiki/Standard_RAID_levels#RAID_1) setup.
+Instead, I went for a cheaper option, a [poor man's
+RAID-1](https://feeding.cloud.geek.nz/posts/poor-mans-raid1-between-ssd-and-hard-drive/),
+where I will have to reinstall the machine but it will be very quick and I
+won't lose any of my configuration.
+
+The way that it works is that I periodically sync the contents of the root
+partition onto the RAID-5 array using a cronjob in `/etc/cron.d/hdd-sync`:
+
+    0 10 * * *     root    /usr/local/sbin/ssd_root_backup
+
+which runs the `/usr/local/sbin/ssd_root_backup` script:
+
+    #!/bin/sh
+    nocache nice ionice -c3 rsync -aHx --delete --exclude=/dev/* --exclude=/proc/* --exclude=/sys/* --exclude=/tmp/* --exclude=/mnt/* --exclude=/lost+found/* --exclude=/media/* --exclude=/var/tmp/* /* /mnt/data/root/
+
+### Drive spin down
+
 To reduce unnecessary noise and reduce power consumption, I also installed
 [hdparm](https://sourceforge.net/projects/hdparm/):
 
@@ -86,6 +108,8 @@ and then reloaded the configuration:
 
      /usr/lib/pm-utils/power.d/95hdparm-apm resume
 
+### Monitoring drive health
+
 Finally I setup [smartmontools](https://www.smartmontools.org/) by putting
 the following in `/etc/smartd.conf`:
 

Remove superfluous words.
diff --git a/posts/npr-modem-setup-testing-linux.mdwn b/posts/npr-modem-setup-testing-linux.mdwn
index 8ef6ca2..c2b1318 100644
--- a/posts/npr-modem-setup-testing-linux.mdwn
+++ b/posts/npr-modem-setup-testing-linux.mdwn
@@ -76,7 +76,7 @@ and confirmed that they were able to successfully connect to each other:
 
 # Monitoring RF
 
-To monitor what is happening on the air and check and quickly determine
+To monitor what is happening on the air and quickly determine
 whether or not the modems are chatting, you can use a [software-defined
 radio](https://www.nooelec.com/store/sdr/sdr-receivers/nesdr/nesdr-mini.html)
 along with [gqrx](https://gqrx.dk/) with the following settings:

Small fixes to NPR post.
diff --git a/posts/npr-modem-setup-testing-linux.mdwn b/posts/npr-modem-setup-testing-linux.mdwn
index e9f4088..8ef6ca2 100644
--- a/posts/npr-modem-setup-testing-linux.mdwn
+++ b/posts/npr-modem-setup-testing-linux.mdwn
@@ -1,9 +1,9 @@
-[[!meta title="Setting and testing an NPR modem on Linux"]]
-[[!meta date="2020-09-17T23:20:00.000-07:00"]]
+[[!meta title="Setting up and testing an NPR modem on Linux"]]
+[[!meta date="2020-09-17T23:35:00.000-07:00"]]
 [[!meta license="[Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)"]]
 
-After acquiring a [New Packet Radio
-modem](https://hackaday.io/project/164092-npr-new-packet-radio) on behalf of
+After acquiring a pair of [New Packet Radio
+modems](https://hackaday.io/project/164092-npr-new-packet-radio) on behalf of
 [VECTOR](https://vectorradio.ca), I set it up on my Linux machine and ran
 some basic tests to check whether it could achieve the advertised 500 kbps
 transfer rates, which are much higher than

creating tag page tags/iperf
diff --git a/tags/iperf.mdwn b/tags/iperf.mdwn
new file mode 100644
index 0000000..6ab9b02
--- /dev/null
+++ b/tags/iperf.mdwn
@@ -0,0 +1,4 @@
+[[!meta title="pages tagged iperf"]]
+
+[[!inline pages="tagged(iperf)" actions="no" archive="yes"
+feedshow=10]]

creating tag page tags/npr
diff --git a/tags/npr.mdwn b/tags/npr.mdwn
new file mode 100644
index 0000000..ac08df6
--- /dev/null
+++ b/tags/npr.mdwn
@@ -0,0 +1,4 @@
+[[!meta title="pages tagged npr"]]
+
+[[!inline pages="tagged(npr)" actions="no" archive="yes"
+feedshow=10]]

Add NPR setup post.
diff --git a/posts/npr-modem-setup-testing-linux.mdwn b/posts/npr-modem-setup-testing-linux.mdwn
new file mode 100644
index 0000000..e9f4088
--- /dev/null
+++ b/posts/npr-modem-setup-testing-linux.mdwn
@@ -0,0 +1,245 @@
+[[!meta title="Setting and testing an NPR modem on Linux"]]
+[[!meta date="2020-09-17T23:20:00.000-07:00"]]
+[[!meta license="[Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)"]]
+
+After acquiring a [New Packet Radio
+modem](https://hackaday.io/project/164092-npr-new-packet-radio) on behalf of
+[VECTOR](https://vectorradio.ca), I set it up on my Linux machine and ran
+some basic tests to check whether it could achieve the advertised 500 kbps
+transfer rates, which are much higher than
+[AX25](https://en.wikipedia.org/wiki/AX.25)) packet radio.
+
+The exact equipment I used was:
+
+- [NPR-70 v05 modems](https://elekitsorparts.com/product/npr-70-modem-by-f4hdk-new-packet-radio-over-70cm-band-amateur-radio-packet-radio)
+- [Bingfu Dual Band antennas](https://www.amazon.ca/gp/product/B07WPWK5JK/)
+- [Alinco DM-330MV power supply](https://www.radioworld.ca/ali-dm330mvt)
+
+![](/posts/npr-modem-setup-testing-linux/physical_setup.jpg)
+
+# Radio setup
+
+After connecting the modems to the power supply and their respective
+antennas, I connected both modems to my laptop via micro-USB cables and used
+[minicom](https://salsa.debian.org/minicom-team/minicom) to connect to their
+console on `/dev/ttyACM[01]`:
+
+    minicom -8 -b 921600 -D /dev/ttyACM0
+    minicom -8 -b 921600 -D /dev/ttyACM1
+
+To confirm that the firmware was the latest one, I used the following command:
+
+    ready> version
+    firmware: 2020_02_23
+    freq band: 70cm
+
+then I immediately turned off the radio:
+
+    radio off
+
+which can be verified with:
+
+    status
+
+Following the [British Columbia 70 cm band
+plan](http://bcarcc.org/440planA.pdf), I picked the following frequency,
+modulation (bandwidth of 360 kHz), and power (0.05 W):
+
+    set frequency 433.500
+    set modulation 22
+    set RF_power 7
+
+and then did the rest of the configuration for the master:
+
+    set callsign VA7GPL_0
+    set is_master yes
+    set DHCP_active no
+    set telnet_active no
+
+and the client:
+
+    set callsign VA7GPL_1
+    set is_master no
+    set DHCP_active yes
+    set telnet_active no
+
+and that was enough to get the two modems to talk to one another.
+
+On both of them, I ran the following:
+
+    save
+    reboot
+
+and confirmed that they were able to successfully connect to each other:
+
+    who
+
+# Monitoring RF
+
+To monitor what is happening on the air and check and quickly determine
+whether or not the modems are chatting, you can use a [software-defined
+radio](https://www.nooelec.com/store/sdr/sdr-receivers/nesdr/nesdr-mini.html)
+along with [gqrx](https://gqrx.dk/) with the following settings:
+
+    frequency: 433.500 MHz
+    filter width: user (80k)
+    filter shape: normal
+    mode: Raw I/Q
+
+I found it quite helpful to keep this running the whole time I was working
+with these modems. The background "keep alive" sounds are quite distinct
+from the heavy traffic sounds.
+
+# IP setup
+
+The radio bits out of the way, I turned to the networking configuration.
+
+On the master, I set the following so that I could connect the master to my
+home network (`192.168.1.0/24`) without conflicts: 
+
+    set def_route_active yes
+    set DNS_active no
+    set modem_IP 192.168.1.254
+    set IP_begin 192.168.1.225
+    set master_IP_size 29
+    set netmask 255.255.255.0
+
+(My router's DHCP server is configured to allocate dynamic IP addresses from
+`192.168.1.100` to `192.168.1.224`.)
+
+At this point, I connected my laptop to the client using a
+[CAT-5](https://en.wikipedia.org/wiki/Category_5_cable) network cable and
+the master to the ethernet switch, essentially following *Annex 5* of the
+[Advanced User
+Guide](https://cdn.hackaday.io/files/1640927020512128/NPR_advanced_guide_v2.14.pdf).
+
+My laptop got assigned IP address `192.168.1.225` and so I used another
+computer on the same network to ping my laptop via the NPR modems:
+
+    ping 192.168.1.225
+
+This gave me a round-trip time of around 150-250 ms.
+
+# Performance test
+
+Having successfully established an
+[IP](https://en.wikipedia.org/wiki/Internet_Protocol) connection between the
+two machines, I decided to run a quick test to measure the available
+bandwidth in an ideal setting (i.e. the two antennas very close to each
+other).
+
+On both computers, I installed [iperf](https://iperf.fr/):
+
+    apt install iperf
+
+and then setup the iperf server on my desktop computer:
+
+    sudo iptables -A INPUT -s 192.168.1.0/24 -p TCP --dport 5001 -j ACCEPT
+    sudo iptables -A INPUT -s 192.168.1.0/24 -u UDP --dport 5001 -j ACCEPT
+    iperf --server
+
+On the laptop, I set the MTU to `750` in NetworkManager:
+
+![](/posts/npr-modem-setup-testing-linux/mtu-750-networkmanager.png)
+
+and restarted the network.
+
+Then I created a new user account (`npr` with a uid of `1001`):
+
+    sudo adduser npr
+
+and made sure that only that account could access the network by running the
+following as `root`:
+
+    # Flush all chains.
+    iptables -F
+    
+    # Set defaults policies.
+    iptables -P INPUT DROP
+    iptables -P OUTPUT DROP
+    iptables -P FORWARD DROP
+    
+    # Don't block localhost and ICMP traffic.
+    iptables -A INPUT -i lo -j ACCEPT
+    iptables -A INPUT -p icmp -j ACCEPT
+    iptables -A OUTPUT -o lo -j ACCEPT
+    
+    # Don't re-evaluate already accepted connections.
+    iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
+    iptables -A OUTPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
+    
+    # Allow connections to/from the test user.
+    iptables -A OUTPUT -m owner --uid-owner 1001 -m conntrack --ctstate NEW -j ACCEPT
+    
+    # Log anything that gets blocked.
+    iptables -A INPUT -j LOG
+    iptables -A OUTPUT -j LOG
+    iptables -A FORWARD -j LOG
+
+then I started the test as the `npr` user:
+
+    sudo -i -u npr
+    iperf --client 192.168.1.8
+
+# Results
+
+The results were as good as advertised both with modulation 22 (360 kHz
+bandwidth):
+
+    $ iperf --client 192.168.1.8 --time 30
+    ------------------------------------------------------------
+    Client connecting to 192.168.1.8, TCP port 5001
+    TCP window size: 85.0 KByte (default)
+    ------------------------------------------------------------
+    [  3] local 192.168.1.225 port 58462 connected with 192.168.1.8 port 5001

(Diff truncated)
Comment moderation
diff --git a/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_10_88b2fb718e4fb1b9b1f2c4f6ff9b0128._comment b/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_10_88b2fb718e4fb1b9b1f2c4f6ff9b0128._comment
new file mode 100644
index 0000000..9354a9c
--- /dev/null
+++ b/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_10_88b2fb718e4fb1b9b1f2c4f6ff9b0128._comment
@@ -0,0 +1,13 @@
+[[!comment format=mdwn
+ ip="80.123.19.32"
+ claimedauthor="FlascheLeer"
+ subject="comment 10"
+ date="2020-09-08T09:48:42Z"
+ content="""
+This helped a lot. Thanks!
+For a younger Ubuntu (20.04), I also had to mount /sys:
+
+    mount --rbind /sys /mnt/sys/
+
+I didn't try the mount -o bind method with sys.
+"""]]

Link to the Debian bug report for user services.
diff --git a/posts/home-music-server-with-mpd.mdwn b/posts/home-music-server-with-mpd.mdwn
index 2682895..38fba01 100644
--- a/posts/home-music-server-with-mpd.mdwn
+++ b/posts/home-music-server-with-mpd.mdwn
@@ -29,8 +29,7 @@ then open `/etc/mpd.conf` and set these:
 
 Note that you can find the right sound device on your machine using the `aplay -L` command.
 
-Since this is a headless system setup, it may be necessary to disable the
-user service:
+Since this is a headless system setup, it may be necessary to [disable the user service](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=959693):
 
     rm /etc/xdg/autostart/mpd.desktop
     systemctl --global disable mpd.service

Use the correct sound device in mpd.conf.
diff --git a/posts/home-music-server-with-mpd.mdwn b/posts/home-music-server-with-mpd.mdwn
index d46a7ea..2682895 100644
--- a/posts/home-music-server-with-mpd.mdwn
+++ b/posts/home-music-server-with-mpd.mdwn
@@ -23,10 +23,12 @@ then open `/etc/mpd.conf` and set these:
     audio_output {
        type       "alsa"
        name       "My ALSA Device"
-       device     "hw:0,0"
+       device     "hw:CARD=DAC,DEV=0"
        mixer_type "software"
     }
 
+Note that you can find the right sound device on your machine using the `aplay -L` command.
+
 Since this is a headless system setup, it may be necessary to disable the
 user service:
 

Remove zeroconf since it doesn't work with systemd sockets
Sep 06 11:40 : zeroconf: No global port, disabling zeroconf
diff --git a/posts/home-music-server-with-mpd.mdwn b/posts/home-music-server-with-mpd.mdwn
index cdac277..d46a7ea 100644
--- a/posts/home-music-server-with-mpd.mdwn
+++ b/posts/home-music-server-with-mpd.mdwn
@@ -18,7 +18,6 @@ then open `/etc/mpd.conf` and set these:
     music_directory    "/path/to/music/"
     bind_to_address    "0.0.0.0"
     bind_to_address    "/run/mpd/socket"
-    zeroconf_enabled   "yes"
     password           "Password1"
     
     audio_output {

Disable user service interfering with main mpd service.
diff --git a/posts/home-music-server-with-mpd.mdwn b/posts/home-music-server-with-mpd.mdwn
index 3f03efd..cdac277 100644
--- a/posts/home-music-server-with-mpd.mdwn
+++ b/posts/home-music-server-with-mpd.mdwn
@@ -28,6 +28,24 @@ then open `/etc/mpd.conf` and set these:
        mixer_type "software"
     }
 
+Since this is a headless system setup, it may be necessary to disable the
+user service:
+
+    rm /etc/xdg/autostart/mpd.desktop
+    systemctl --global disable mpd.service
+
+in order to prevent systemd from launching the mpd service whenever a user
+logs in, leading to error messages like:
+
+    systemd[324808]: mpd.socket: Failed to create listening socket ([::]:6600): Address already in use
+    systemd[324808]: mpd.socket: Failed to listen on sockets: Address already in use
+    systemd[324808]: mpd.socket: Failed with result 'resources'.
+    systemd[324808]: Failed to listen on mpd.socket.
+    mpd[324823]: exception: failed to open log file "/var/log/mpd/mpd.log" (config line 39): Permission denied
+    systemd[324808]: mpd.service: Main process exited, code=exited, status=1/FAILURE
+    systemd[324808]: mpd.service: Failed with result 'exit-code'.
+    systemd[324808]: Failed to start Music Player Daemon.
+
 Once all of that is in place, restart the mpd daemon:
 
     systemctl restart mpd.service

Simplify and fix the Apache configuration.
diff --git a/posts/home-music-server-with-mpd.mdwn b/posts/home-music-server-with-mpd.mdwn
index 949dd5a..3f03efd 100644
--- a/posts/home-music-server-with-mpd.mdwn
+++ b/posts/home-music-server-with-mpd.mdwn
@@ -72,22 +72,21 @@ from a local web server I have installed
 
     apt install apache2
 
-and configured it to serve the covers by putting the following in
-`/etc/apache2/conf-available/mpd.conf`:
+and configured it to serve the covers by putting the following in the
+default vhost section of `/etc/apache2/sites-available/000-default.conf`:
 
+    Alias /music /path/to/music
+    
     <Directory /path/to/music>
+        Options -MultiViews -Indexes
         AllowOverride None
-        Require all granted
+        Order allow,deny
+        allow from all
     </Directory>
 
-and then the following line in the default vhost section of
-`/etc/apache2/sites-available/000-default.conf`:
-
-    Alias /music /path/to/music
-
-Finally, I enabled the new configuration and restarted Apache:
+Finally, I enabled the new vhost and restarted Apache:
 
-    a2enconf mpd.conf
+    a2ensite 000-default
     systemctl restart apache2.service
 
 # Clients

Switch to alsa to simplify headless operation
diff --git a/posts/home-music-server-with-mpd.mdwn b/posts/home-music-server-with-mpd.mdwn
index 6f443bb..949dd5a 100644
--- a/posts/home-music-server-with-mpd.mdwn
+++ b/posts/home-music-server-with-mpd.mdwn
@@ -16,47 +16,21 @@ Start by installing the server and the client package:
 then open `/etc/mpd.conf` and set these:
 
     music_directory    "/path/to/music/"
-    bind_to_address    "192.168.1.2"
+    bind_to_address    "0.0.0.0"
     bind_to_address    "/run/mpd/socket"
     zeroconf_enabled   "yes"
     password           "Password1"
-
-before replacing the alsa output:
-
-    audio_output {
-       type    "alsa"
-       name    "My ALSA Device"
-    }
-
-with a pulseaudio one:
-
+    
     audio_output {
-       type    "pulse"
-       name    "Pulseaudio Output"
-       server  "127.0.0.1"
+       type       "alsa"
+       name       "My ALSA Device"
+       device     "hw:0,0"
+       mixer_type "software"
     }
 
-and exposing pulseaudio to localhost via `/etc/pulse/default.pa`:
-
-    ### Network access (may be configured with paprefs, so leave this commented
-    ### here if you plan to use paprefs)
-    load-module module-native-protocol-tcp auth-ip-acl=127.0.0.1
-
-In order for the automatic detection (zeroconf) of your music server
-to work, you need to [prevent systemd from creating the network
-socket](https://www.mail-archive.com/mpd-devel@musicpd.org/msg00239.html):
-
-    systemctl stop mpd.service
-    systemctl stop mpd.socket
-    systemctl disable mpd.socket
-
-otherwise you'll see this in `/var/log/mpd/mpd.log`:
-
-    zeroconf: No global port, disabling zeroconf
-
-Once all of that is in place, start the mpd daemon:
+Once all of that is in place, restart the mpd daemon:
 
-    systemctl start mpd.service
+    systemctl restart mpd.service
 
 and create an index of your music files:
 

Comment moderation
diff --git a/posts/programming-anytone-d878uv-on-linux-using-windows10-and-virtualbox/comment_8_c6382c5a5eb077a8992dbeffb9dc6f6e._comment b/posts/programming-anytone-d878uv-on-linux-using-windows10-and-virtualbox/comment_8_c6382c5a5eb077a8992dbeffb9dc6f6e._comment
new file mode 100644
index 0000000..7e08d41
--- /dev/null
+++ b/posts/programming-anytone-d878uv-on-linux-using-windows10-and-virtualbox/comment_8_c6382c5a5eb077a8992dbeffb9dc6f6e._comment
@@ -0,0 +1,14 @@
+[[!comment format=mdwn
+ username="francois@665656f0ba400877c9b12e8fbb086e45aa01f7c0"
+ nickname="francois"
+ subject="Re: Wine for Anytone 878"
+ date="2020-09-01T16:15:33Z"
+ content="""
+> If I understand you correctly, Wine does not let CPS read or write to the Anytone 878.
+
+I have not tried Wine so I can't comment on this.
+
+> Does that mean we need to purchase a Windows 10 license and run it from VirtualBox?
+
+You could probably use one of the [Windows 10 IE / Legacy Edge testing VMs](https://developer.microsoft.com/en-us/microsoft-edge/tools/vms/) that Microsoft offers for free for 90 days.
+"""]]

Comment moderation
diff --git a/posts/programming-anytone-d878uv-on-linux-using-windows10-and-virtualbox/comment_7_d9d686bb1c13a519639d76276da07451._comment b/posts/programming-anytone-d878uv-on-linux-using-windows10-and-virtualbox/comment_7_d9d686bb1c13a519639d76276da07451._comment
new file mode 100644
index 0000000..99d5d93
--- /dev/null
+++ b/posts/programming-anytone-d878uv-on-linux-using-windows10-and-virtualbox/comment_7_d9d686bb1c13a519639d76276da07451._comment
@@ -0,0 +1,9 @@
+[[!comment format=mdwn
+ ip="134.223.230.152"
+ claimedauthor="Glen Flint"
+ url="GlenFlint@aol.com"
+ subject="Wine for Anytone 878"
+ date="2020-09-01T14:53:09Z"
+ content="""
+If I understand you correctly, Wine does not let CPS read or write to the Anytone 878.  Does that mean we need to purchase a Windows 10 license and run it from VirtualBox?  There is a different version of CPS for Windows 7.  Does that work better with Wine?
+"""]]

Comment moderation
diff --git a/posts/restricting-outgoing-webapp-requests-using-squid-proxy/comment_1_66de753ab892687677eb8740d4913f74._comment b/posts/restricting-outgoing-webapp-requests-using-squid-proxy/comment_1_66de753ab892687677eb8740d4913f74._comment
new file mode 100644
index 0000000..82dee38
--- /dev/null
+++ b/posts/restricting-outgoing-webapp-requests-using-squid-proxy/comment_1_66de753ab892687677eb8740d4913f74._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ ip="167.123.240.150"
+ claimedauthor="Thrawn"
+ subject="How to minimise Squid overhead?"
+ date="2020-08-27T23:28:48Z"
+ content="""
+This type of filtering could be very useful for one of our applications, but there are concerns about the overhead of running an extra process on our servers, and I notice that Squid's FAQ says it uses memory fairly aggressively to improve caching. How would we configure it to discard all of the caching (and associated memory usage) and just do IP filtering?
+"""]]

Disable the /server-status Apache endpoint
https://httpd.apache.org/docs/2.4/mod/mod_status.html
diff --git a/posts/usual-server-setup.mdwn b/posts/usual-server-setup.mdwn
index 353a62d..a8412ea 100644
--- a/posts/usual-server-setup.mdwn
+++ b/posts/usual-server-setup.mdwn
@@ -296,6 +296,7 @@ Also, [`command-not-found` won't work until you update the apt cache](https://bu
 
     apt install apache2
     a2enmod mpm_event
+    a2dismod status
 
 While configuring apache is often specific to each server and the services
 that will be running on it, there are a few common changes I make.

Add fake-hwclock to the GnuBee instructions.
diff --git a/posts/installing-debian-buster-on-gnubee2.mdwn b/posts/installing-debian-buster-on-gnubee2.mdwn
index 04185d0..7773915 100644
--- a/posts/installing-debian-buster-on-gnubee2.mdwn
+++ b/posts/installing-debian-buster-on-gnubee2.mdwn
@@ -247,4 +247,43 @@ following contents:
     ExecStart=
     ExecStart=-/sbin/agetty -o '-p -- \\u' 57600 %I $TERM
 
+## Fixing the hardware clock between restarts
+
+When the GnuBee boots, you may have noticed that the clock is wrong until
+`systemd-timesyncd` updates the time using
+[NTP](https://en.wikipedia.org/wiki/Network_Time_Protocol). This leads to
+messages like these:
+
+    Aug 23 02:46:15 hostname systemd-fsck[839]: GNUBEE-ROOT: Superblock last mount time is in the future.
+    Aug 23 02:46:15 hostname systemd-fsck[839]: #011(by less than a day, probably due to the hardware clock being incorrectly set)
+    ...
+    Aug 23 02:46:41 hostname systemd[1]: systemd-fsckd.service: Succeeded.
+    Aug 23 13:04:30 hostname systemd-timesyncd[1309]: Synchronized to time server for the first time 162.159.200.1:123 (time.cloudflare.com).
+
+and unnecessary executions of `fsck`.
+
+Often these hardware issues are due to a lack of a battery to keep the clock
+alive while the unit is powered down. In order to work around this, I
+installed the [`fake-hwclock`
+package](https://packages.debian.org/buster/fake-hwclock) and then edited
+the `/lib/systemd/system/fake-hwclock.service` file to change the following
+line from:
+
+    Before=sysinit.target
+
+to:
+
+    Before=sysinit.target systemd-fsck-root.service
+
+so that the clock is [restored before the filesystem
+check](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=908504).
+
+I also added the following to `/etc/.gitignore` to make
+[`etckeeper`](https://packages.debian.org/buster/etckeeper) happy:
+
+    /fake-hwclock.data
+
+since `fake-hwclock` unfortunately [keeps its data file in
+`/etc/`](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=782314).
+
 [[!tag debian]] [[!tag gnubee]]

Comment moderation
diff --git a/posts/running-your-own-xmpp-server-debian-ubuntu/comment_7_64da47f3eb6457603a4ce1db5fcc814a._comment b/posts/running-your-own-xmpp-server-debian-ubuntu/comment_7_64da47f3eb6457603a4ce1db5fcc814a._comment
new file mode 100644
index 0000000..bd5507d
--- /dev/null
+++ b/posts/running-your-own-xmpp-server-debian-ubuntu/comment_7_64da47f3eb6457603a4ce1db5fcc814a._comment
@@ -0,0 +1,9 @@
+[[!comment format=mdwn
+ username="cheako+feeding_cloud_geek_nz@7d91c66ae019b345d8af95e5b431b39b51b58fdc"
+ nickname="cheako+feeding_cloud_geek_nz"
+ avatar="http://cdn.libravatar.org/avatar/f1612673dd5b6775c359139483ca389e"
+ subject="About the DNS records you showed."
+ date="2020-08-18T19:37:52Z"
+ content="""
+Keep in mind that CNAME redirects \"every\" record type lookup elsewhere.  Your TLZ will have records that these hostnames should not.  For example SOA, NS, and the TXT/spf.  So using CNAME in that way should be discouraged, it's most defiantly not what you want.
+"""]]

Comment moderation
diff --git a/posts/time-synchronization-with-ntp-and-systemd/comment_6_c15a5d1faef39093c04def602cc68b10._comment b/posts/time-synchronization-with-ntp-and-systemd/comment_6_c15a5d1faef39093c04def602cc68b10._comment
new file mode 100644
index 0000000..29e0df3
--- /dev/null
+++ b/posts/time-synchronization-with-ntp-and-systemd/comment_6_c15a5d1faef39093c04def602cc68b10._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ ip="71.10.210.18"
+ claimedauthor="Jeff"
+ subject="systemD time synchronization has a long way to go"
+ date="2020-08-17T14:01:45Z"
+ content="""
+Since systemd only does basic time synchronization, I think it's really, *really* misleading to say, \"there is no need to run the full-fledged ntpd daemon anymore.\"  I can think of several uses for time-slewing, and persistent time carry over between boots is necessary.
+"""]]

Comment moderation
diff --git a/posts/extend-gpg-key-expiry/comment_1_e47e857d19697a36db39356e098db51e._comment b/posts/extend-gpg-key-expiry/comment_1_e47e857d19697a36db39356e098db51e._comment
new file mode 100644
index 0000000..af86e8e
--- /dev/null
+++ b/posts/extend-gpg-key-expiry/comment_1_e47e857d19697a36db39356e098db51e._comment
@@ -0,0 +1,16 @@
+[[!comment format=mdwn
+ ip="2001:16b8:205e:cc00:f080:3d2a:c76a:4b0a"
+ subject="Quicker method"
+ date="2020-07-31T09:27:53Z"
+ content="""
+There's a quicker method if you just want to extend the expiration date:
+
+    gpg --quick-set-expire KEYID PERIOD
+
+…and for the subkeys:
+
+    gpg --quick-set-expire KEYID PERIOD '*'
+
+
+PS: Did you know you could lint your PGP keys? The [hopenpgp-tools](https://salsa.debian.org/clint/hopenpgp-tools) include `hokey lint`.
+"""]]

Use the right post title
diff --git a/posts/set-default-web-browser-debian-ubuntu.mdwn b/posts/set-default-web-browser-debian-ubuntu.mdwn
index 9b9056c..7b46927 100644
--- a/posts/set-default-web-browser-debian-ubuntu.mdwn
+++ b/posts/set-default-web-browser-debian-ubuntu.mdwn
@@ -1,4 +1,4 @@
-[[!meta title="Extending GPG key expiry"]]
+[[!meta title="Setting the default web browser on Debian and Ubuntu"]]
 [[!meta date="2020-08-07T21:10:00.000-07:00"]]
 [[!meta license="[Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)"]]
 

Add a post abouf setting a default browser on Debian
diff --git a/posts/set-default-web-browser-debian-ubuntu.mdwn b/posts/set-default-web-browser-debian-ubuntu.mdwn
new file mode 100644
index 0000000..9b9056c
--- /dev/null
+++ b/posts/set-default-web-browser-debian-ubuntu.mdwn
@@ -0,0 +1,84 @@
+[[!meta title="Extending GPG key expiry"]]
+[[!meta date="2020-08-07T21:10:00.000-07:00"]]
+[[!meta license="[Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)"]]
+
+If you are wondering what your default web browser is set to on a
+Debian-based system, there are several things to look at:
+
+    $ xdg-settings get default-web-browser
+    brave-browser.desktop
+    
+    $ xdg-mime query default x-scheme-handler/http
+    brave-browser.desktop
+    
+    $ xdg-mime query default x-scheme-handler/https
+    brave-browser.desktop
+    
+    $ ls -l /etc/alternatives/x-www-browser
+    lrwxrwxrwx 1 root root 29 Jul  5  2019 /etc/alternatives/x-www-browser -> /usr/bin/brave-browser-stable*
+    
+    $ ls -l /etc/alternatives/gnome-www-browser
+    lrwxrwxrwx 1 root root 29 Jul  5  2019 /etc/alternatives/gnome-www-browser -> /usr/bin/brave-browser-stable*
+
+## Debian-specific tools
+
+The contents of `/etc/alternatives/` is system-wide defaults and must
+therefore be set as `root`:
+
+    sudo update-alternatives --config x-www-browser
+    sudo update-alternatives --config gnome-www-browser
+
+The `sensible-browser` tool (from the [`sensible-utils`
+package](https://packages.debian.org/stable/sensible-utils)) will use these
+to automatically launch the most appropriate web browser depending on the
+desktop environment.
+
+## Standard MIME tools
+
+The others can be changed as a normal user. Using `xdg-settings`:
+
+    xdg-settings set default-web-browser brave-browser-beta.desktop
+
+will also change what the two `xdg-mime` commands return:
+
+    $ xdg-mime query default x-scheme-handler/http
+    brave-browser-beta.desktop
+    
+    $ xdg-mime query default x-scheme-handler/https
+    brave-browser-beta.desktop
+
+since it puts the following in `~/.config/mimeapps.list`:
+
+    [Default Applications]
+    text/html=brave-browser-beta.desktop
+    x-scheme-handler/http=brave-browser-beta.desktop
+    x-scheme-handler/https=brave-browser-beta.desktop
+    x-scheme-handler/about=brave-browser-beta.desktop
+    x-scheme-handler/unknown=brave-browser-beta.desktop
+
+Note that if you delete these entries, then the system-wide defaults,
+defined in `/etc/mailcap`, will be used, as provided by the
+[`mime-support` package](https://packages.debian.org/stable/mime-support).
+
+Changing the `x-scheme-handler/http` (or `x-scheme-handler/https`)
+association directly using:
+
+    xdg-mime default brave-browser-nightly.desktop x-scheme-handler/http
+
+will only change that particular one. I suppose this means you could have
+one browser for [insecure HTTP
+sites](https://blog.mozilla.org/security/2015/04/30/deprecating-non-secure-http/)
+(hopefully with [HTTPS Everywhere
+installed](https://www.eff.org/https-everywhere)) and one for HTTPS sites though
+I'm not sure why anybody would want that.
+
+## Summary
+
+In short, if you want to set your default browser everywhere (using
+[Brave](https://brave.com) in this example), do the following:
+
+    sudo update-alternatives --config x-www-browser
+    sudo update-alternatives --config gnome-www-browser
+    xdg-settings set default-web-browser brave-browser.desktop
+
+[[!tag debian]] [[!tag brave]]

Make sure IP addresses are never cached.
diff --git a/posts/displaying-ip-address-apache-server-side-includes.mdwn b/posts/displaying-ip-address-apache-server-side-includes.mdwn
index bc95e85..7008f65 100644
--- a/posts/displaying-ip-address-apache-server-side-includes.mdwn
+++ b/posts/displaying-ip-address-apache-server-side-includes.mdwn
@@ -37,6 +37,7 @@ options to a `Location` or `Directory` section:
         SSLRequireSSL
         Header set Content-Security-Policy: "default-src 'none'"
         Header set X-Content-Type-Options: "nosniff"
+        Header set Cache-Control "max-age=0, no-cache, no-store, must-revalidate"
     </Location>
 
 before adding the necessary modules:

Sync up with the script I use
diff --git a/posts/seeding-brave-browser-sccache.mdwn b/posts/seeding-brave-browser-sccache.mdwn
index 5697ea9..2292322 100644
--- a/posts/seeding-brave-browser-sccache.mdwn
+++ b/posts/seeding-brave-browser-sccache.mdwn
@@ -46,7 +46,10 @@ and here are the contents of that script:
     git pull
     npm install
     rm -rf src/brave/*
+    git -C src/third_party/devtools-frontend/src/ reset --hard
     gclient sync -D
+    git -C src/brave pull
+    git -C src/brave reset --hard
     npm run init
     
     echo $(date)

Tag a few more GPG-related posts
diff --git a/posts/encrypted-mailing-list-on-debian-and-ubuntu.mdwn b/posts/encrypted-mailing-list-on-debian-and-ubuntu.mdwn
index e1de05e..0953ff6 100644
--- a/posts/encrypted-mailing-list-on-debian-and-ubuntu.mdwn
+++ b/posts/encrypted-mailing-list-on-debian-and-ubuntu.mdwn
@@ -107,4 +107,4 @@ it should be signed by the list admin.
 
 After that, anybody requesting the list key will get your signature as well.
 
-[[!tag debian]] [[!tag security]] [[!tag nzoss]]
+[[!tag debian]] [[!tag security]] [[!tag gpg]]
diff --git a/posts/mutts-openpgp-support-and-firegpg.mdwn b/posts/mutts-openpgp-support-and-firegpg.mdwn
index 7174089..3a5730e 100644
--- a/posts/mutts-openpgp-support-and-firegpg.mdwn
+++ b/posts/mutts-openpgp-support-and-firegpg.mdwn
@@ -32,4 +32,4 @@ However, this didn't actually work with FireGPG and the way that it puts encrypt
 
 
 
-[[!tag mutt]] [[!tag catalyst]] [[!tag debian]] [[!tag sysadmin]] [[!tag ubuntu]] [[!tag email]]
+[[!tag mutt]] [[!tag catalyst]] [[!tag debian]] [[!tag sysadmin]] [[!tag ubuntu]] [[!tag email]] [[!tag gpg]]
diff --git a/posts/things-that-work-well-with-tor.mdwn b/posts/things-that-work-well-with-tor.mdwn
index 110a615..d28bd0c 100644
--- a/posts/things-that-work-well-with-tor.mdwn
+++ b/posts/things-that-work-well-with-tor.mdwn
@@ -139,4 +139,4 @@ I can take advantage of GMail's excellent caching and preloading and run the
 whole thing over Tor by setting that entire browser profile to run its
 traffic through the Tor SOCKS proxy on port `9050`.
 
-[[!tag debian]] [[!tag privacy]] [[!tag tor]] [[!tag nzoss]] [[!tag mozilla]] [[!tag xmpp]] [[!tag gmail]]
+[[!tag debian]] [[!tag privacy]] [[!tag tor]] [[!tag gpg]] [[!tag mozilla]] [[!tag xmpp]] [[!tag gmail]]

Also need to upload key to Debian keyserver
diff --git a/posts/extend-gpg-key-expiry.mdwn b/posts/extend-gpg-key-expiry.mdwn
index ce5bd3b..75fd062 100644
--- a/posts/extend-gpg-key-expiry.mdwn
+++ b/posts/extend-gpg-key-expiry.mdwn
@@ -13,8 +13,9 @@ Update the expiry on the main key and the subkey:
     > expire
     > save
 
-Upload the updated key to the keyserver:
+Upload the updated key to the keyservers:
 
     gpg --export KEYID | curl -T - https://keys.openpgp.org
+    gpg --keyserver keyring.debian.org --send-keys KEYID
 
 [[!tag debian]] [[!tag gpg]]

Remove link to Identica account.
diff --git a/sidebar.mdwn b/sidebar.mdwn
index d42b2e6..52a5d66 100644
--- a/sidebar.mdwn
+++ b/sidebar.mdwn
@@ -13,7 +13,7 @@
 <br><a href="mailto:francois@fmarier.org">francois@fmarier.org</a>
 <br>Free and Open Source software developer
 <br>
-<br>[Twitter](https://twitter.com/fmarier) / [Identica](https://identi.ca/fmarier)
+<br>[Twitter](https://twitter.com/fmarier)
 <br>[Linked In](https://linkedin.com/in/fmarier)
 
 # More from this blog

creating tag page tags/gpg
diff --git a/tags/gpg.mdwn b/tags/gpg.mdwn
new file mode 100644
index 0000000..c594b70
--- /dev/null
+++ b/tags/gpg.mdwn
@@ -0,0 +1,4 @@
+[[!meta title="pages tagged gpg"]]
+
+[[!inline pages="tagged(gpg)" actions="no" archive="yes"
+feedshow=10]]

Add GPG key expiry blog post
diff --git a/posts/extend-gpg-key-expiry.mdwn b/posts/extend-gpg-key-expiry.mdwn
new file mode 100644
index 0000000..ce5bd3b
--- /dev/null
+++ b/posts/extend-gpg-key-expiry.mdwn
@@ -0,0 +1,20 @@
+[[!meta title="Extending GPG key expiry"]]
+[[!meta date="2020-07-30T20:45:00.000-07:00"]]
+[[!meta license="[Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)"]]
+
+Extending the expiry on a GPG key is not very hard, but it's easy to forget
+a step. Here's how I did my last expiry bump.
+
+Update the expiry on the main key and the subkey:
+
+    gpg --edit-key KEYID
+    > expire
+    > key 1
+    > expire
+    > save
+
+Upload the updated key to the keyserver:
+
+    gpg --export KEYID | curl -T - https://keys.openpgp.org
+
+[[!tag debian]] [[!tag gpg]]

Mention the tmd710-tncsetup package now in Debian.
diff --git a/posts/using-kenwood-th-d72a-with-pat-linux-ax25.mdwn b/posts/using-kenwood-th-d72a-with-pat-linux-ax25.mdwn
index a4f8910..e37609a 100644
--- a/posts/using-kenwood-th-d72a-with-pat-linux-ax25.mdwn
+++ b/posts/using-kenwood-th-d72a-with-pat-linux-ax25.mdwn
@@ -51,11 +51,18 @@ correctly:
    mentioned in a comment in `/etc/default/ax25`:
 
         gcc -o tmd710_tncsetup tmd710_tncsetup.c
+        sudo cp tmd710_tncsetup /usr/local/bin
+
+   Note: on a [Debian bullseye](https://www.debian.org/releases/bullseye/) or later system, all you need to do is install the [`tmd710-tncsetup` package](https://packages.debian.org/bullseye/tmd710-tncsetup):
+
+        apt install tmd710-tncsetup
 
 7. Add the `tmd710_tncsetup` script in `/etc/default/ax25` and use these command
    line parameters (`-B 0` specifies band A, use `-B 1` for band B):
 
-        tmd710_tncsetup -B 0 -S $DEV -b $HBAUD -s
+        /usr/local/bin/tmd710_tncsetup -B 0 -S $DEV -b $HBAUD -s
+
+   Note: the path is `/usr/bin/tmd710_tncsetup` if using the official Debian package.
 
 8. Start ax25 driver:
 

Rephrase a sentence that's now slightly obsolete
diff --git a/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition.mdwn b/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition.mdwn
index 57de28e..9304921 100644
--- a/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition.mdwn
+++ b/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition.mdwn
@@ -81,8 +81,7 @@ Then "enter" the root partition using:
 
     chroot /mnt
 
-and make sure that the [lvm2](https://launchpad.net/ubuntu/+source/lvm2)
-package is installed:
+and make sure that you have the necessary packages installed:
 
     apt install lvm2 cryptsetup-initramfs
 

Improve user comment formatting
diff --git a/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_1_dbf4ae9f9fe087f9b03cfb0961a4fe57._comment b/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_1_dbf4ae9f9fe087f9b03cfb0961a4fe57._comment
index 76a00f1..6624804 100644
--- a/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_1_dbf4ae9f9fe087f9b03cfb0961a4fe57._comment
+++ b/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_1_dbf4ae9f9fe087f9b03cfb0961a4fe57._comment
@@ -5,7 +5,7 @@
  subject="Without using a live instance"
  date="2018-05-06T20:54:15Z"
  content="""
-I successfully used your recommended approach without booting via USB. This can be accomplished by selecting to boot into a previous kernel via the Grub boot menu during startup, and then (without the need to mount local partitions) simply ensure the latest version of lvm2 is installed and regenerating the initramfs for all of the installed kernels (as recommended). I also have a fully encrypted drive configuration and found no issues when performing these steps. 
+I successfully used your recommended approach without booting via USB. This can be accomplished by selecting to boot into a previous kernel via the Grub boot menu during startup, and then (without the need to mount local partitions) simply ensure the latest version of `lvm2` is installed and regenerating the initramfs for all of the installed kernels (as recommended). I also have a fully encrypted drive configuration and found no issues when performing these steps. 
 
 Thank you for putting this article together. While I normally find the forums to be of great assistance, this issue was not one that is easy to find real working solutions for. Keep up the great work.
 """]]
diff --git a/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_2_344f04840164a73701084d11ef52358c._comment b/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_2_344f04840164a73701084d11ef52358c._comment
index 8ce3c8b..bac5d01 100644
--- a/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_2_344f04840164a73701084d11ef52358c._comment
+++ b/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_2_344f04840164a73701084d11ef52358c._comment
@@ -6,7 +6,7 @@
  content="""
 I wanted to make sure the next time it happens I could recover quickly with just the LiveCD available.
 
-I wrote it to detect the correct name from the /mnt/etc/crypttab to ensure the `update-initramfs` command can properly update. 
+I wrote it to detect the correct name from the `/mnt/etc/crypttab` to ensure the `update-initramfs` command can properly update. 
 
-https://gist.github.com/dragon788/e777ba64d373210e4f6306ad40ee0e80
+<https://gist.github.com/dragon788/e777ba64d373210e4f6306ad40ee0e80>
 """]]
diff --git a/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_3_cbd36f2900e966992f874221a5182e8e._comment b/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_3_cbd36f2900e966992f874221a5182e8e._comment
index ebafd4c..4708577 100644
--- a/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_3_cbd36f2900e966992f874221a5182e8e._comment
+++ b/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_3_cbd36f2900e966992f874221a5182e8e._comment
@@ -6,7 +6,7 @@
  content="""
 I got the same problem after upgrading to 18.04, I don't use LVM but Btrfs, all I had to change was
 
-```apt install btrfs-progs```
+    apt install btrfs-progs
 
 Everything else was exactly the same.
 
diff --git a/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_4_0dcba6e86d49f32540ebb57d54fc49e4._comment b/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_4_0dcba6e86d49f32540ebb57d54fc49e4._comment
index 2e03196..5ae7ec8 100644
--- a/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_4_0dcba6e86d49f32540ebb57d54fc49e4._comment
+++ b/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_4_0dcba6e86d49f32540ebb57d54fc49e4._comment
@@ -4,11 +4,11 @@
  subject="Worked for me with minor tweaks"
  date="2019-01-07T01:22:19Z"
  content="""
-I didn't need to install lvm2, as it was on my unbootable system.  I also had some minor partition/volume differences.
+I didn't need to install `lvm2`, as it was on my unbootable system.  I also had some minor partition/volume differences.
 
 My issue is documented at the [Ubuntu forums](https://ubuntuforums.org/showthread.php?t=2409754)
 
-That all said, **I did have a major issue with DNS resolution not functioning** after this was done.  I'm wondering if \"update-initramfs\" lead to this issue specifically (I made other changes I can't recall clearly).
+That all said, **I did have a major issue with DNS resolution not functioning** after this was done.  I'm wondering if `update-initramfs` lead to this issue specifically (I made other changes I can't recall clearly).
 
 If other experience loss of DNS via systemd.resolved failure, please note it here and on my post in the Ubuntu Forums.  My fix is listed there, although I'm effectively disabling systemd.resolved.
 """]]
diff --git a/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_5_0367ad2561d124b47e307502f1b85a96._comment b/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_5_0367ad2561d124b47e307502f1b85a96._comment
index 70b0845..1fcec11 100644
--- a/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_5_0367ad2561d124b47e307502f1b85a96._comment
+++ b/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_5_0367ad2561d124b47e307502f1b85a96._comment
@@ -4,16 +4,15 @@
  subject="This really woks with minor change"
  date="2019-11-22T08:09:19Z"
  content="""
-First I recovered the /etc/fstab and /etc/crypttab from the backup with backup-tool because I had tried something and messed up these files.
+First I recovered the `/etc/fstab` and `/etc/crypttab` from the backup with backup-tool because I had tried something and messed up these files.
 
-Then I followed these instructions but I left the command 'vgchange -ay' out. Reason for that was because after that I couldn't mount my partitions to anything. Without it mounting was done nicely and the rest of the steps could be done.
+Then I followed these instructions but I left the command `vgchange -ay` out. Reason for that was because after that I couldn't mount my partitions to anything. Without it mounting was done nicely and the rest of the steps could be done.
 
 It had the consequence that in the end I couldn't unmount and close the partition but that wasn't in this instruction and so I paid no attention to that.
 
-I encountered the problem when updating the initramfs (I was missing some firmware library and it gave some warnings). Solution to that was found here: https://askubuntu.com/questions/832524/possible-missing-frmware-lib-firmware-i915/832528 and the updating of initramfs were done without warnings.
+I encountered the problem when updating the initramfs (I was missing some firmware library and it gave some warnings). Solution to that was [found here](https://askubuntu.com/questions/832524/possible-missing-frmware-lib-firmware-i915/832528) and the updating of initramfs were done without warnings.
 
 In the end I prayed a little and rebooted and everything was fine after these changes and now I can log in to my ubuntu again. 
 
-
 Thanks for clear instructions!
 """]]
diff --git a/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_7_6d782ed81d9fbdfbcc70fdf6da15fbed._comment b/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_7_6d782ed81d9fbdfbcc70fdf6da15fbed._comment
index 2b8ef36..45d5fa2 100644
--- a/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_7_6d782ed81d9fbdfbcc70fdf6da15fbed._comment
+++ b/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_7_6d782ed81d9fbdfbcc70fdf6da15fbed._comment
@@ -4,5 +4,5 @@
  subject="Boot to earlier kernel worked better"
  date="2020-02-05T02:19:59Z"
  content="""
-As William (William — 13:54, 06 May 2018) did, I booted to the preceding kernel version, and as I logged in, I saw LivePatch flash by saying it had just updated something. I used apt to update everything and restarted, my machine is now runnign like a Swiss watch!
+As William (William — 13:54, 06 May 2018) did, I booted to the preceding kernel version, and as I logged in, I saw LivePatch flash by saying it had just updated something. I used `apt` to update everything and restarted, my machine is now runnign like a Swiss watch!
 """]]
diff --git a/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_8_556ade9bd0b423bbba3a4791ce49b6c2._comment b/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_8_556ade9bd0b423bbba3a4791ce49b6c2._comment
index 135bc6d..87154e7 100644
--- a/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_8_556ade9bd0b423bbba3a4791ce49b6c2._comment
+++ b/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_8_556ade9bd0b423bbba3a4791ce49b6c2._comment
@@ -6,9 +6,8 @@
 Hello,
 
 i have tried out your solution after failing about the one which did not help you also. 
-But seems i am stuck in this update nightmare: https://askubuntu.com/questions/1256247/ubuntu-20-kernel-upgrade-encrypted-volume-group-cannot-be-found-crypttab-em
+But seems i am stuck in [this update nightmare](https://askubuntu.com/questions/1256247/ubuntu-20-kernel-upgrade-encrypted-volume-group-cannot-be-found-crypttab-em).
 
 If you have any idea how to solve this i would be very greatful! 
 
-
 """]]
diff --git a/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_9_f534393495aa28bd0f034b20d2ae0704._comment b/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_9_f534393495aa28bd0f034b20d2ae0704._comment
index 19cf755..e27f1ed 100644
--- a/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_9_f534393495aa28bd0f034b20d2ae0704._comment
+++ b/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_9_f534393495aa28bd0f034b20d2ae0704._comment
@@ -7,8 +7,8 @@
 Had a very similar issue after an update of Ubuntu 20.04 on a Dell XPS13 (2020).
 Searched for hours, the solution was actually super easy.
 
-reboot and go to BIOS using \"fn and F2\"  
-BIOS > System Configuration > Sata Operation > switch to \"AHCI\" from \"RAID On\"
+1. reboot and go to BIOS using \"fn and F2\"  
+2. BIOS > System Configuration > Sata Operation > switch to \"AHCI\" from \"RAID On\"
 
 For some reason, this BIOS setting was switched.
 """]]

Add missing package based on Vitaalz's comment
diff --git a/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition.mdwn b/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition.mdwn
index 471bd2a..57de28e 100644
--- a/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition.mdwn
+++ b/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition.mdwn
@@ -84,7 +84,7 @@ Then "enter" the root partition using:
 and make sure that the [lvm2](https://launchpad.net/ubuntu/+source/lvm2)
 package is installed:
 
-    apt install lvm2
+    apt install lvm2 cryptsetup-initramfs
 
 before regenerating the initramfs for all of the installed kernels:
 
diff --git a/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_10_a1eef6d3212d51402a4817e2e2432ec9._comment b/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_10_a1eef6d3212d51402a4817e2e2432ec9._comment
deleted file mode 100644
index b1c5f0b..0000000
--- a/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_10_a1eef6d3212d51402a4817e2e2432ec9._comment
+++ /dev/null
@@ -1,10 +0,0 @@
-[[!comment format=mdwn
- ip="62.63.132.50"
- claimedauthor="Vitaalz"
- subject="comment 10"
- date="2020-07-29T14:32:15Z"
- content="""
-On Debian-based systems before running `update-initramfs` command make sure that `cryptsetup-initramfs` package is installed. If not, install it first:
-
-`apt-get install -y cryptsetup-initramfs`
-"""]]

Comment moderation
diff --git a/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_10_a1eef6d3212d51402a4817e2e2432ec9._comment b/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_10_a1eef6d3212d51402a4817e2e2432ec9._comment
new file mode 100644
index 0000000..b1c5f0b
--- /dev/null
+++ b/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_10_a1eef6d3212d51402a4817e2e2432ec9._comment
@@ -0,0 +1,10 @@
+[[!comment format=mdwn
+ ip="62.63.132.50"
+ claimedauthor="Vitaalz"
+ subject="comment 10"
+ date="2020-07-29T14:32:15Z"
+ content="""
+On Debian-based systems before running `update-initramfs` command make sure that `cryptsetup-initramfs` package is installed. If not, install it first:
+
+`apt-get install -y cryptsetup-initramfs`
+"""]]

Comment moderation
diff --git a/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_9_f534393495aa28bd0f034b20d2ae0704._comment b/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_9_f534393495aa28bd0f034b20d2ae0704._comment
new file mode 100644
index 0000000..19cf755
--- /dev/null
+++ b/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_9_f534393495aa28bd0f034b20d2ae0704._comment
@@ -0,0 +1,14 @@
+[[!comment format=mdwn
+ ip="81.164.136.123"
+ claimedauthor="Koen"
+ subject="comment 9"
+ date="2020-07-14T16:13:52Z"
+ content="""
+Had a very similar issue after an update of Ubuntu 20.04 on a Dell XPS13 (2020).
+Searched for hours, the solution was actually super easy.
+
+reboot and go to BIOS using \"fn and F2\"  
+BIOS > System Configuration > Sata Operation > switch to \"AHCI\" from \"RAID On\"
+
+For some reason, this BIOS setting was switched.
+"""]]

Stopping the etckeeper timer is also necessary to fully disable it
diff --git a/posts/usual-server-setup.mdwn b/posts/usual-server-setup.mdwn
index a29424f..353a62d 100644
--- a/posts/usual-server-setup.mdwn
+++ b/posts/usual-server-setup.mdwn
@@ -51,6 +51,7 @@ and this in `/etc/.git/config`:
 Note that in order to fully turn off auto-commits, it's also necessary
 to run the following:
 
+    systemctl stop etckeeper.timer
     systemctl disable etckeeper.timer
 
 To get more control over the various packages I install, I change the

Comment moderation
diff --git a/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_8_556ade9bd0b423bbba3a4791ce49b6c2._comment b/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_8_556ade9bd0b423bbba3a4791ce49b6c2._comment
new file mode 100644
index 0000000..135bc6d
--- /dev/null
+++ b/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_8_556ade9bd0b423bbba3a4791ce49b6c2._comment
@@ -0,0 +1,14 @@
+[[!comment format=mdwn
+ ip="88.153.228.176"
+ subject="recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition"
+ date="2020-07-05T10:49:43Z"
+ content="""
+Hello,
+
+i have tried out your solution after failing about the one which did not help you also. 
+But seems i am stuck in this update nightmare: https://askubuntu.com/questions/1256247/ubuntu-20-kernel-upgrade-encrypted-volume-group-cannot-be-found-crypttab-em
+
+If you have any idea how to solve this i would be very greatful! 
+
+
+"""]]

Refer to the correct step
diff --git a/posts/running-your-own-xmpp-server-debian-ubuntu.mdwn b/posts/running-your-own-xmpp-server-debian-ubuntu.mdwn
index 52c35e1..cfbf1b9 100644
--- a/posts/running-your-own-xmpp-server-debian-ubuntu.mdwn
+++ b/posts/running-your-own-xmpp-server-debian-ubuntu.mdwn
@@ -118,7 +118,7 @@ to requests after running for a while:
 
 Note that if you'd like to be able to talk to contacts via the GMail XMPP
 server, you will unfortunately need to change the `s2s_use_starttls`
-setting in step 3 to the following:
+setting in step 4 to the following:
 
       s2s_use_starttls: optional
 

Fix formatting
diff --git a/posts/running-your-own-xmpp-server-debian-ubuntu.mdwn b/posts/running-your-own-xmpp-server-debian-ubuntu.mdwn
index d4db455..52c35e1 100644
--- a/posts/running-your-own-xmpp-server-debian-ubuntu.mdwn
+++ b/posts/running-your-own-xmpp-server-debian-ubuntu.mdwn
@@ -58,14 +58,16 @@ to solve the [Pidgin](http://pidgin.im) "Not authorized" connection problems.
 
 2. Set the following in `/etc/ejabberd/ejabberd.yml`:
 
-      acl:
-        admin:
-           user:
-               - "admin@fmarier.org"
-      hosts:
-        - "fmarier.org"
-      auth_password_format: scram
-      fqdn: "jabber-gw.fmarier.org"
+       acl:
+         admin:
+            user:
+                - "admin@fmarier.org"
+       
+       hosts:
+         - "fmarier.org"
+       
+       auth_password_format: scram
+       fqdn: "jabber-gw.fmarier.org"
 
 3. Copy the SSL certificate into the `/etc/ejabberd/` directory and set the
 permissions correctly:
@@ -75,21 +77,21 @@ permissions correctly:
 
 4. Improve the client-to-server and server-to-server TLS configuration:
 
-      define_macro:
-        # ...
-        'DH_FILE': "/etc/ejabberd/dhparams.pem"
+       define_macro:
+         # ...
+         'DH_FILE': "/etc/ejabberd/dhparams.pem"
+       
+       c2s_dhfile: 'DH_FILE'
+       s2s_dhfile: 'DH_FILE'
+       
+       listen:
+         -
+           port: 5222
+           ip: "::"
+           module: ejabberd_c2s
+           starttls_required: true
       
-      c2s_dhfile: 'DH_FILE'
-      s2s_dhfile: 'DH_FILE'
-      
-      listen:
-        -
-          port: 5222
-          ip: "::"
-          module: ejabberd_c2s
-          starttls_required: true
-      
-      s2s_use_starttls: required
+       s2s_use_starttls: required
 
 5. Create the required `dhparams.pem` file:
 

Switch to Apache authenticator and add echo.fmarier.org domain
diff --git a/posts/running-your-own-xmpp-server-debian-ubuntu.mdwn b/posts/running-your-own-xmpp-server-debian-ubuntu.mdwn
index 95184cc..d4db455 100644
--- a/posts/running-your-own-xmpp-server-debian-ubuntu.mdwn
+++ b/posts/running-your-own-xmpp-server-debian-ubuntu.mdwn
@@ -14,22 +14,23 @@ put everything together.
 My personal domain is `fmarier.org` and so I created the following DNS
 records:
 
+    echo                 CNAME    fmarier.org.
     jabber-gw            CNAME    fmarier.org.
     _xmpp-client._tcp    SRV      5 0 5222 jabber-gw.fmarier.org.
     _xmpp-server._tcp    SRV 	  5 0 5269 jabber-gw.fmarier.org.
 
-Then I went to get a free TLS certificate for `jabber-gw.fmarier.org` and `fmarier.org`.
+Then I went to get a free TLS certificate for the above.
 
 ## Let's Encrypt
 
 The easiest way to get a certificate is to install [certbot](https://certbot.eff.org/):
 
-    apt install certbot
+    apt install certbot python3-certbot-apache
 
 Then, shutdown your existing webserver if you have one running and request
 a cert like this:
 
-    certbot certonly -d jabber-gw.fmarier.org,fmarier.org --standalone
+    certbot --duplicate certonly --apache -d jabber-gw.fmarier.org -d echo.fmarier.org -d fmarier.org
 
 Once you have the cert, you can merge the private and public keys
 into the file that ejabberd expects:

Remove CertSpotter (no longer free) and add ejabberd tag
diff --git a/posts/automatically-renewing-letsencrypt-certs-on-debian-using-certbot.mdwn b/posts/automatically-renewing-letsencrypt-certs-on-debian-using-certbot.mdwn
index 084aeb2..6d6f623 100644
--- a/posts/automatically-renewing-letsencrypt-certs-on-debian-using-certbot.mdwn
+++ b/posts/automatically-renewing-letsencrypt-certs-on-debian-using-certbot.mdwn
@@ -62,18 +62,9 @@ monitor my domains once a day:
 
     ssl-cert-check -s fmarier.org -p 443 -q -a -e francois@fmarier.org
 
-I also signed up with [Cert Spotter](https://sslmate.com/certspotter/) which
-watches the
-[Certificate Transparency](https://www.certificate-transparency.org/) log
-and notifies me of any newly-issued certificates for my domains.
-
-In other words, I get notified:
-
-- if my cronjob fails and a cert is about to expire, or
-- as soon as a new cert is issued.
-
 The whole thing seems to work well, but if there's anything I could be doing
 better, feel free to leave a comment!
 
 [[!tag nzoss]] [[!tag sysadmin]] [[!tag debian]] [[!tag mozilla]]
 [[!tag ubuntu]] [[!tag ssl]] [[!tag apache]] [[!tag letsencrypt]]
+[[!tag ejabberd]]