Recent changes to this wiki:

Move the limit forward since Planet Mozilla treats modification time as creation time
diff --git a/tags/mozilla.mdwn b/tags/mozilla.mdwn
index 7883bf0..ff95877 100644
--- a/tags/mozilla.mdwn
+++ b/tags/mozilla.mdwn
@@ -1,4 +1,4 @@
 [[!meta title="pages tagged mozilla"]]
 
 [[!inline pages="tagged(mozilla)" actions="no" archive="yes"
-feedshow=9 feedpages=created_after(posts/letting-someone-ssh-into-your-laptop-using-pagekite)]]
+feedshow=9 feedpages=created_after(posts/restricting-third-party-iframes-sandbox-referrer-feature-policy)]]

Remove aloodo resource since its HTTPS cert expired
diff --git a/local.css b/local.css
index 3f1933d..69f70db 100644
--- a/local.css
+++ b/local.css
@@ -225,17 +225,6 @@ body {
     background-color: transparent;
 }
 
-/* ALOODO
-     - stops it from putting extra empty space at bottom of page
-*/
-iframe[src='//ad.aloodo.com/track/'] {
-    position: absolute;
-    top: 0;
-    left: 0;
-    background-color: #fff;
-}
-
-
 /* ADAPT TO MOBILE */
 
 @media all and (max-width: 79em) {
diff --git a/sidebar.mdwn b/sidebar.mdwn
index 463ac41..d42b2e6 100644
--- a/sidebar.mdwn
+++ b/sidebar.mdwn
@@ -24,5 +24,3 @@
 
 [[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/*"]]
-
-<script src="https://ad.aloodo.com/ad.js" async></script>

Fix image path
diff --git a/posts/how-to-get-direct-webrtc-connection-between-computers.mdwn b/posts/how-to-get-direct-webrtc-connection-between-computers.mdwn
index 74e169b..6608027 100644
--- a/posts/how-to-get-direct-webrtc-connection-between-computers.mdwn
+++ b/posts/how-to-get-direct-webrtc-connection-between-computers.mdwn
@@ -35,7 +35,7 @@ that might interfere with this:
 - [Brave](https://brave.com/clo187): Disable Shields entirely for that page
   (Simple view) or *allow all cookies* for that page (Advanced view).
 
-![](/posts/how-to-get-direct-webrtc-connections-between-computers/brave-shields-cookies.png)
+![](/posts/how-to-get-direct-webrtc-connection-between-computers/brave-shields-cookies.png)
 
 - Firefox: Ensure that `http.network.referer.spoofSource` is set to `false`
   in `about:config`, which it is by default.
@@ -43,7 +43,7 @@ that might interfere with this:
 - [uMatrix](https://github.com/gorhill/uMatrix): The "Spoof `Referer`
   header" option needs to be turned off for that site.
 
-![](/posts/how-to-get-direct-webrtc-connections-between-computers/umatrix-settings.png)
+![](/posts/how-to-get-direct-webrtc-connection-between-computers/umatrix-settings.png)
 
 # Checking the type of peer connection you have
 
@@ -75,13 +75,13 @@ find the one which says:
 - `nominated: true`
 - `writable: true`
 
-![](/posts/how-to-get-direct-webrtc-connections-between-computers/chromium-webrtc-internals-candidatepair.png)
+![](/posts/how-to-get-direct-webrtc-connection-between-computers/chromium-webrtc-internals-candidatepair.png)
 
 Then from the name of that pair (`N6cxxnrr_OEpeash` in the above example)
 find the two matching `RTCIceCandidate` lines (one `local-candidate` and one
 `remote-candidate`) and expand them.
 
-![](/posts/how-to-get-direct-webrtc-connections-between-computers/chromium-webrtc-internals-candidates.png)
+![](/posts/how-to-get-direct-webrtc-connection-between-computers/chromium-webrtc-internals-candidates.png)
 
 In the case of a **direct connection**, I saw the following on the
 `remote-candidate`:
@@ -127,7 +127,7 @@ the [candidate
 type](https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidate/type)
 in brackets.
 
-![](/posts/how-to-get-direct-webrtc-connections-between-computers/firefox-about-webrtc.png)
+![](/posts/how-to-get-direct-webrtc-connection-between-computers/firefox-about-webrtc.png)
 
 # Firewall ports to open to avoid using a relay
 

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

Add WebRTC post
diff --git a/posts/how-to-get-direct-webrtc-connection-between-computers.mdwn b/posts/how-to-get-direct-webrtc-connection-between-computers.mdwn
new file mode 100644
index 0000000..74e169b
--- /dev/null
+++ b/posts/how-to-get-direct-webrtc-connection-between-computers.mdwn
@@ -0,0 +1,176 @@
+[[!meta title="How to get a direct WebRTC connections between two computers"]]
+[[!meta date="2020-03-28T16:55:00.000-07:00"]]
+[[!meta license="[Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)"]]
+
+[WebRTC](https://webrtc.org/) is a standard real-time communication protocol
+built directly into modern web browsers. It enables the creation of video
+conferencing services which do not require participants to download
+additional software. Many services make use of it and it almost always works
+out of the box.
+
+The reason it just works is that it uses a protocol called
+[ICE](https://en.wikipedia.org/wiki/Interactive_Connectivity_Establishment)
+to establish a connection regardless of the network environment. What that
+means however is that in some cases, your video/audio connection will need
+to be relayed (using end-to-end encryption) to the other person via
+third-party
+[TURN](https://en.wikipedia.org/wiki/Traversal_Using_Relays_around_NAT)
+server. In addition to adding extra network latency to your call that relay
+server might overloaded at some point and drop or delay packets coming
+through.
+
+Here's how to tell whether or not your WebRTC calls are being relayed, and
+how to ensure you get a direct connection to the other host.
+
+# Testing basic WebRTC functionality
+
+Before you place a real call, I suggest using the official [test
+page](https://test.webrtc.org/) which will test your camera, microphone and
+network connectivity.
+
+Note that this test page makes use of a Google TURN server which is locked
+to particular HTTP referrers and so you'll need to disable privacy features
+that might interfere with this:
+
+- [Brave](https://brave.com/clo187): Disable Shields entirely for that page
+  (Simple view) or *allow all cookies* for that page (Advanced view).
+
+![](/posts/how-to-get-direct-webrtc-connections-between-computers/brave-shields-cookies.png)
+
+- Firefox: Ensure that `http.network.referer.spoofSource` is set to `false`
+  in `about:config`, which it is by default.
+
+- [uMatrix](https://github.com/gorhill/uMatrix): The "Spoof `Referer`
+  header" option needs to be turned off for that site.
+
+![](/posts/how-to-get-direct-webrtc-connections-between-computers/umatrix-settings.png)
+
+# Checking the type of peer connection you have
+
+Once you know that WebRTC is working in your browser, it's time to establish
+a connection and look at the network configuration that the two peers agreed
+on.
+
+My favorite service at the moment is [Whereby](https://whereby.com)
+(formerly *Appear.in*), so I'm going to use that to connect from two
+different computers:
+
+- `canada` is a laptop behind a regular home router without any port
+  forwarding.
+- `siberia` is a desktop computer in a remote location that is also behind a
+  home router, but in this case its internal IP address (`192.168.1.2`) is
+  set as the [DMZ
+  host](https://en.wikipedia.org/wiki/DMZ_%28computing%29#DMZ_host).
+
+## Chromium
+
+For all Chromium-based browsers, such as Brave, Chrome, Edge, Opera and
+Vivaldi, the debugging page you'll need to open is called
+`chrome://webrtc-internals`.
+
+Look for `RTCIceCandidatePair` lines and expand them one at a time until you
+find the one which says:
+
+- `state: succeeded` (or `state: in-progress`)
+- `nominated: true`
+- `writable: true`
+
+![](/posts/how-to-get-direct-webrtc-connections-between-computers/chromium-webrtc-internals-candidatepair.png)
+
+Then from the name of that pair (`N6cxxnrr_OEpeash` in the above example)
+find the two matching `RTCIceCandidate` lines (one `local-candidate` and one
+`remote-candidate`) and expand them.
+
+![](/posts/how-to-get-direct-webrtc-connections-between-computers/chromium-webrtc-internals-candidates.png)
+
+In the case of a **direct connection**, I saw the following on the
+`remote-candidate`:
+
+- `ip` shows the external IP address of `siberia`
+- `port` shows a random number between 1024 and 65535
+- `candidateType: srflx`
+
+and the following on `local-candidate`:
+
+- `ip` shows the external IP address of `canada`
+- `port` shows a random number between 1024 and 65535
+- `candidateType: prflx`
+
+These [candidate
+types](https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidate/type)
+indicate that a [STUN](https://en.wikipedia.org/wiki/STUN) server was used
+to determine the public-facing IP address and port for each computer, but
+the actual connection between the peers is direct.
+
+On the other hand, for a **relayed/proxied connection**, I saw the following
+on the `remote-candidate` side:
+
+- `ip` shows an IP address belonging to the TURN server
+- `candidateType: relay`
+
+and the same information as before on the `local-candidate`.
+
+## Firefox
+
+If you are using Firefox, the debugging page you want to look at is
+`about:webrtc`.
+
+Expand the top entry under "Session Statistics" and look for the line
+(should be the first one) which says the following in green:
+
+- `ICE State: succeeded`
+- `Nominated: true`
+- `Selected: true`
+
+then look in the "Local Candidate" and "Remote Candidate" sections to find
+the [candidate
+type](https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidate/type)
+in brackets.
+
+![](/posts/how-to-get-direct-webrtc-connections-between-computers/firefox-about-webrtc.png)
+
+# Firewall ports to open to avoid using a relay
+
+In order to get a direct connection to the other WebRTC peer, **one** of the
+two computers (in my case, `siberia`) needs to **open all inbound UDP
+ports** since there doesn't appear to be a way to restrict Chromium or
+Firefox to a smaller port range for incoming WebRTC connections.
+
+This isn't great and so I decided to tighten that up in two ways by:
+
+- restricting incoming UDP traffic to the IP range of `siberia`'s ISP, and
+- explicitly denying incoming to the UDP ports I know are open on `siberia`.
+
+To get the IP range, start with the external IP address of the machine (I'll
+use the IP address of my blog in this example: `66.228.46.55`) and pass it
+to the `whois` command:
+
+    $ whois 66.228.46.55 | grep CIDR
+    CIDR:           66.228.32.0/19
+
+To get the list of open UDP ports on `siberia`, I `ssh`ed into it and ran
+nmap:
+
+    $ sudo nmap -sU localhost
+    
+    Starting Nmap 7.60 ( https://nmap.org ) at 2020-03-28 15:55 PDT
+    Nmap scan report for localhost (127.0.0.1)
+    Host is up (0.000015s latency).
+    Not shown: 994 closed ports
+    PORT      STATE         SERVICE
+    631/udp   open|filtered ipp
+    5060/udp  open|filtered sip
+    5353/udp  open          zeroconf
+    
+    Nmap done: 1 IP address (1 host up) scanned in 190.25 seconds
+
+I ended up with the following in my `/etc/network/iptables.up.rules` (ports
+below 1024 are denied by the default rule and don't need to be included
+here):
+
+    # Deny all known-open high UDP ports before enabling WebRTC for canada
+    -A INPUT -p udp --dport 5060 -j DROP
+    -A INPUT -p udp --dport 5353 -j DROP
+    -A INPUT -s 66.228.32.0/19 -p udp --dport 1024:65535 -j ACCEPT
+
+[[!tag brave]] [[!tag firefox]] [[!tag webrtc]] [[!tag mozilla]] [[!tag debian]] [[!tag videoconf]]
diff --git a/posts/how-to-get-direct-webrtc-connection-between-computers/brave-shields-cookies.png b/posts/how-to-get-direct-webrtc-connection-between-computers/brave-shields-cookies.png
new file mode 100644
index 0000000..ac3de50
Binary files /dev/null and b/posts/how-to-get-direct-webrtc-connection-between-computers/brave-shields-cookies.png differ
diff --git a/posts/how-to-get-direct-webrtc-connection-between-computers/chromium-webrtc-internals-candidatepair.png b/posts/how-to-get-direct-webrtc-connection-between-computers/chromium-webrtc-internals-candidatepair.png
new file mode 100644
index 0000000..d14b1f4
Binary files /dev/null and b/posts/how-to-get-direct-webrtc-connection-between-computers/chromium-webrtc-internals-candidatepair.png differ
diff --git a/posts/how-to-get-direct-webrtc-connection-between-computers/chromium-webrtc-internals-candidates.png b/posts/how-to-get-direct-webrtc-connection-between-computers/chromium-webrtc-internals-candidates.png
new file mode 100644
index 0000000..6f3cdbe
Binary files /dev/null and b/posts/how-to-get-direct-webrtc-connection-between-computers/chromium-webrtc-internals-candidates.png differ
diff --git a/posts/how-to-get-direct-webrtc-connection-between-computers/firefox-about-webrtc.png b/posts/how-to-get-direct-webrtc-connection-between-computers/firefox-about-webrtc.png
new file mode 100644
index 0000000..38d3968
Binary files /dev/null and b/posts/how-to-get-direct-webrtc-connection-between-computers/firefox-about-webrtc.png differ
diff --git a/posts/how-to-get-direct-webrtc-connection-between-computers/umatrix-settings.png b/posts/how-to-get-direct-webrtc-connection-between-computers/umatrix-settings.png
new file mode 100644

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

Create a new videoconf tag
diff --git a/posts/installing-vidyo-on-ubuntu-1804.mdwn b/posts/installing-vidyo-on-ubuntu-1804.mdwn
index 3121a0f..78562bc 100644
--- a/posts/installing-vidyo-on-ubuntu-1804.mdwn
+++ b/posts/installing-vidyo-on-ubuntu-1804.mdwn
@@ -57,4 +57,4 @@ version:
 Finally, launch `VidyoDesktop` and go into the settings to check "Always use
 VidyoProxy".
 
-[[!tag mozilla]] [[!tag vidyo]]
+[[!tag mozilla]] [[!tag vidyo]] [[!tag videoconf]]
diff --git a/posts/peer-to-peer-video-conferencing-using.mdwn b/posts/peer-to-peer-video-conferencing-using.mdwn
index ba0bb85..c14fbfd 100644
--- a/posts/peer-to-peer-video-conferencing-using.mdwn
+++ b/posts/peer-to-peer-video-conferencing-using.mdwn
@@ -63,4 +63,4 @@ Another thing I thought of trying was to switch from [TCP](https://secure.wikime
 Please feel free leave a comment if you can suggest ways of improving my quick 'n dirty solution.
 
 
-[[!tag catalyst]] [[!tag gstreamer]] [[!tag debian]] [[!tag sysadmin]] [[!tag ubuntu]] [[!tag nzoss]] 
+[[!tag catalyst]] [[!tag gstreamer]] [[!tag debian]] [[!tag sysadmin]] [[!tag ubuntu]] [[!tag nzoss]] [[!tag videoconf]]

Comment moderation
diff --git a/posts/fixing-mariadb-innodb-errors-mythtv30/comment_1_bfed4f724f41d877156a4bfd354b6b8a._comment b/posts/fixing-mariadb-innodb-errors-mythtv30/comment_1_bfed4f724f41d877156a4bfd354b6b8a._comment
new file mode 100644
index 0000000..b8e497c
--- /dev/null
+++ b/posts/fixing-mariadb-innodb-errors-mythtv30/comment_1_bfed4f724f41d877156a4bfd354b6b8a._comment
@@ -0,0 +1,19 @@
+[[!comment format=mdwn
+ ip="84.250.233.2"
+ claimedauthor="Marko Mäkelä"
+ url="https://mariadb.com"
+ subject="Some history of the InnoDB ROW_FORMAT"
+ date="2020-03-09T07:49:59Z"
+ content="""
+I implemented the `ROW_FORMAT=DYNAMIC` back in 2004 or 2005 in the InnoDB Plugin for MySQL 5.1, which was available separately from the built-in InnoDB. Later, it became the InnoDB in MySQL 5.5.
+
+The only difference between `ROW_FORMAT=DYNAMIC` and the previous default `ROW_FORMAT=COMPACT` (which I introduced in MySQL 5.0.3) is the storage of long string columns (such as `VARCHAR`, `BLOB`, or `TEXT`). Originally, Heikki Tuuri decided to always store the 768 first bytes of each column inline in the page, so that it would be easier to implement column prefix indexes in secondary indexes. Unsurprisingly, users started to complain, because the unnecessary inline storage would happen unconditionally, whether or not the column is indexed.
+
+For `ROW_FORMAT=DYNAMIC`, I improved InnoDB in such a way that there is no need to store a local prefix. Instead, column prefixes will be stored in undo log records if needed. (An attempt to update many prefix-indexed columns in one statement may fail if the undo log record would not fit in one page.)
+
+In MySQL 5.5, the maximum column prefix index length was increased from 767 (the 768th byte was always 'wasted') to 3072 bytes.
+
+Due to someone's strange idea regarding compatibility, one had to override two configuration parameters to enable the sane behaviour. Originally, the idea was that one could try out the InnoDB Plugin in MySQL 5.1 and be able to return to the built-in InnoDB. I finally changed the parameters `innodb_file_format`, `innodb_large_prefix` to have sane defaults in MySQL 5.7 and removed them in MySQL 8.0. Likewise, In MySQL 5.7 introduced the parameter `innodb_default_row_format=dynamic`, so that it is not necessary to specify a `ROW_FORMAT` when creating tables.
+
+These settings are also present in MariaDB Server starting with version 10.2, which is the first major version released after I joined the company.
+"""]]

Add MythTV InnoDB blog post
diff --git a/posts/fixing-mariadb-innodb-errors-mythtv30.mdwn b/posts/fixing-mariadb-innodb-errors-mythtv30.mdwn
new file mode 100644
index 0000000..303ce46
--- /dev/null
+++ b/posts/fixing-mariadb-innodb-errors-mythtv30.mdwn
@@ -0,0 +1,135 @@
+[[!meta title="Fixing MariaDB InnoDB errors after upgrading to MythTV 30"]]
+[[!meta date="2020-03-07T10:00:00.000-08:00"]]
+[[!meta license="[Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)"]]
+
+After upgrading to [MythTV
+30](https://www.mythtv.org/wiki/Release_Notes_-_30) and MariaDB 10.3.18 on
+[Debian buster](https://www.debian.org/releases/buster/), I noticed the
+following errors in my logs:
+
+    Jan 14 02:00:05 hostname mysqld[846]: 2020-01-14  2:00:05 62 [Warning] InnoDB: Cannot add field `rating` in table `mythconverg`.`internetcontentarticles` because after adding it, the row size is 8617 which is greater than maximum allowed size (8126) for a record on index leaf page.
+    Jan 14 02:00:05 hostname mysqld[846]: 2020-01-14  2:00:05 62 [Warning] InnoDB: Cannot add field `playcommand` in table `mythconverg`.`videometadata` because after adding it, the row size is 8243 which is greater than maximum allowed size (8126) for a record on index leaf page.
+
+The root cause is that the database is using an [InnoDB row
+format](https://mariadb.com/kb/en/troubleshooting-row-size-too-large-errors-with-innodb/)
+that cannot handle the new table sizes.
+
+To fix it, I put the following in `alter_tables.sql`:
+
+    ALTER TABLE archiveitems ROW_FORMAT=DYNAMIC;
+    ALTER TABLE bdbookmark ROW_FORMAT=DYNAMIC;
+    ALTER TABLE callsignnetworkmap ROW_FORMAT=DYNAMIC;
+    ALTER TABLE capturecard ROW_FORMAT=DYNAMIC;
+    ALTER TABLE cardinput ROW_FORMAT=DYNAMIC;
+    ALTER TABLE channel ROW_FORMAT=DYNAMIC;
+    ALTER TABLE channelgroup ROW_FORMAT=DYNAMIC;
+    ALTER TABLE channelgroupnames ROW_FORMAT=DYNAMIC;
+    ALTER TABLE channelscan ROW_FORMAT=DYNAMIC;
+    ALTER TABLE channelscan_channel ROW_FORMAT=DYNAMIC;
+    ALTER TABLE channelscan_dtv_multiplex ROW_FORMAT=DYNAMIC;
+    ALTER TABLE codecparams ROW_FORMAT=DYNAMIC;
+    ALTER TABLE credits ROW_FORMAT=DYNAMIC;
+    ALTER TABLE customexample ROW_FORMAT=DYNAMIC;
+    ALTER TABLE diseqc_config ROW_FORMAT=DYNAMIC;
+    ALTER TABLE diseqc_tree ROW_FORMAT=DYNAMIC;
+    ALTER TABLE displayprofilegroups ROW_FORMAT=DYNAMIC;
+    ALTER TABLE displayprofiles ROW_FORMAT=DYNAMIC;
+    ALTER TABLE dtv_multiplex ROW_FORMAT=DYNAMIC;
+    ALTER TABLE dtv_privatetypes ROW_FORMAT=DYNAMIC;
+    ALTER TABLE dvdbookmark ROW_FORMAT=DYNAMIC;
+    ALTER TABLE dvdinput ROW_FORMAT=DYNAMIC;
+    ALTER TABLE dvdtranscode ROW_FORMAT=DYNAMIC;
+    ALTER TABLE eit_cache ROW_FORMAT=DYNAMIC;
+    ALTER TABLE filemarkup ROW_FORMAT=DYNAMIC;
+    ALTER TABLE gallery_directories ROW_FORMAT=DYNAMIC;
+    ALTER TABLE gallery_files ROW_FORMAT=DYNAMIC;
+    ALTER TABLE gallerymetadata ROW_FORMAT=DYNAMIC;
+    ALTER TABLE housekeeping ROW_FORMAT=DYNAMIC;
+    ALTER TABLE inputgroup ROW_FORMAT=DYNAMIC;
+    ALTER TABLE internetcontent ROW_FORMAT=DYNAMIC;
+    ALTER TABLE internetcontentarticles ROW_FORMAT=DYNAMIC;
+    ALTER TABLE inuseprograms ROW_FORMAT=DYNAMIC;
+    ALTER TABLE iptv_channel ROW_FORMAT=DYNAMIC;
+    ALTER TABLE jobqueue ROW_FORMAT=DYNAMIC;
+    ALTER TABLE jumppoints ROW_FORMAT=DYNAMIC;
+    ALTER TABLE keybindings ROW_FORMAT=DYNAMIC;
+    ALTER TABLE keyword ROW_FORMAT=DYNAMIC;
+    ALTER TABLE livestream ROW_FORMAT=DYNAMIC;
+    ALTER TABLE logging ROW_FORMAT=DYNAMIC;
+    ALTER TABLE music_albumart ROW_FORMAT=DYNAMIC;
+    ALTER TABLE music_albums ROW_FORMAT=DYNAMIC;
+    ALTER TABLE music_artists ROW_FORMAT=DYNAMIC;
+    ALTER TABLE music_directories ROW_FORMAT=DYNAMIC;
+    ALTER TABLE music_genres ROW_FORMAT=DYNAMIC;
+    ALTER TABLE music_playlists ROW_FORMAT=DYNAMIC;
+    ALTER TABLE music_radios ROW_FORMAT=DYNAMIC;
+    ALTER TABLE music_smartplaylist_categories ROW_FORMAT=DYNAMIC;
+    ALTER TABLE music_smartplaylist_items ROW_FORMAT=DYNAMIC;
+    ALTER TABLE music_smartplaylists ROW_FORMAT=DYNAMIC;
+    ALTER TABLE music_songs ROW_FORMAT=DYNAMIC;
+    ALTER TABLE music_stats ROW_FORMAT=DYNAMIC;
+    ALTER TABLE music_streams ROW_FORMAT=DYNAMIC;
+    ALTER TABLE mythlog ROW_FORMAT=DYNAMIC;
+    ALTER TABLE mythweb_sessions ROW_FORMAT=DYNAMIC;
+    ALTER TABLE networkiconmap ROW_FORMAT=DYNAMIC;
+    ALTER TABLE oldfind ROW_FORMAT=DYNAMIC;
+    ALTER TABLE oldprogram ROW_FORMAT=DYNAMIC;
+    ALTER TABLE oldrecorded ROW_FORMAT=DYNAMIC;
+    ALTER TABLE people ROW_FORMAT=DYNAMIC;
+    ALTER TABLE phonecallhistory ROW_FORMAT=DYNAMIC;
+    ALTER TABLE phonedirectory ROW_FORMAT=DYNAMIC;
+    ALTER TABLE pidcache ROW_FORMAT=DYNAMIC;
+    ALTER TABLE playgroup ROW_FORMAT=DYNAMIC;
+    ALTER TABLE powerpriority ROW_FORMAT=DYNAMIC;
+    ALTER TABLE profilegroups ROW_FORMAT=DYNAMIC;
+    ALTER TABLE program ROW_FORMAT=DYNAMIC;
+    ALTER TABLE programgenres ROW_FORMAT=DYNAMIC;
+    ALTER TABLE programrating ROW_FORMAT=DYNAMIC;
+    ALTER TABLE recgrouppassword ROW_FORMAT=DYNAMIC;
+    ALTER TABLE recgroups ROW_FORMAT=DYNAMIC;
+    ALTER TABLE record ROW_FORMAT=DYNAMIC;
+    ALTER TABLE record_tmp ROW_FORMAT=DYNAMIC;
+    ALTER TABLE recorded ROW_FORMAT=DYNAMIC;
+    ALTER TABLE recordedartwork ROW_FORMAT=DYNAMIC;
+    ALTER TABLE recordedcredits ROW_FORMAT=DYNAMIC;
+    ALTER TABLE recordedfile ROW_FORMAT=DYNAMIC;
+    ALTER TABLE recordedmarkup ROW_FORMAT=DYNAMIC;
+    ALTER TABLE recordedprogram ROW_FORMAT=DYNAMIC;
+    ALTER TABLE recordedrating ROW_FORMAT=DYNAMIC;
+    ALTER TABLE recordedseek ROW_FORMAT=DYNAMIC;
+    ALTER TABLE recordfilter ROW_FORMAT=DYNAMIC;
+    ALTER TABLE recordingprofiles ROW_FORMAT=DYNAMIC;
+    ALTER TABLE recordmatch ROW_FORMAT=DYNAMIC;
+    ALTER TABLE scannerfile ROW_FORMAT=DYNAMIC;
+    ALTER TABLE scannerpath ROW_FORMAT=DYNAMIC;
+    ALTER TABLE schemalock ROW_FORMAT=DYNAMIC;
+    ALTER TABLE settings ROW_FORMAT=DYNAMIC;
+    ALTER TABLE storagegroup ROW_FORMAT=DYNAMIC;
+    ALTER TABLE tvchain ROW_FORMAT=DYNAMIC;
+    ALTER TABLE tvosdmenu ROW_FORMAT=DYNAMIC;
+    ALTER TABLE upnpmedia ROW_FORMAT=DYNAMIC;
+    ALTER TABLE user_permissions ROW_FORMAT=DYNAMIC;
+    ALTER TABLE user_sessions ROW_FORMAT=DYNAMIC;
+    ALTER TABLE users ROW_FORMAT=DYNAMIC;
+    ALTER TABLE videocast ROW_FORMAT=DYNAMIC;
+    ALTER TABLE videocategory ROW_FORMAT=DYNAMIC;
+    ALTER TABLE videocollection ROW_FORMAT=DYNAMIC;
+    ALTER TABLE videocountry ROW_FORMAT=DYNAMIC;
+    ALTER TABLE videogenre ROW_FORMAT=DYNAMIC;
+    ALTER TABLE videometadata ROW_FORMAT=DYNAMIC;
+    ALTER TABLE videometadatacast ROW_FORMAT=DYNAMIC;
+    ALTER TABLE videometadatacountry ROW_FORMAT=DYNAMIC;
+    ALTER TABLE videometadatagenre ROW_FORMAT=DYNAMIC;
+    ALTER TABLE videopart ROW_FORMAT=DYNAMIC;
+    ALTER TABLE videopathinfo ROW_FORMAT=DYNAMIC;
+    ALTER TABLE videosource ROW_FORMAT=DYNAMIC;
+    ALTER TABLE videotypes ROW_FORMAT=DYNAMIC;
+    ALTER TABLE weatherdatalayout ROW_FORMAT=DYNAMIC;
+    ALTER TABLE weatherscreens ROW_FORMAT=DYNAMIC;
+    ALTER TABLE weathersourcesettings ROW_FORMAT=DYNAMIC;
+
+and then ran it like this:
+
+    mysql -umythtv -pPassword1 mythconverg < alter_tables.sql
+
+[[!tag mythtv]] [[!tag debian]]

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

Add a post about SIP URIs with VoIP.ms
diff --git a/posts/making-sip-calls-voipms-without-pstn.mdwn b/posts/making-sip-calls-voipms-without-pstn.mdwn
new file mode 100644
index 0000000..ee96aee
--- /dev/null
+++ b/posts/making-sip-calls-voipms-without-pstn.mdwn
@@ -0,0 +1,16 @@
+[[!meta title="Making SIP calls to VoIP.ms subscribers without using the PSTN"]]
+[[!meta date="2020-03-05T19:00:00.000-08:00"]]
+[[!meta license="[Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)"]]
+
+If you want to reach a [VoIP.ms](https://voip.ms/) subscriber from
+[Asterisk](https://www.asterisk.org/) without using the
+[PSTN](https://en.wikipedia.org/wiki/Public_switched_telephone_network),
+there is a way to do so via [SIP
+URIs](https://wiki.voip.ms/article/SIP_URI).
+
+Here's what I added to my `/etc/asterisk/extensions.conf`:
+
+    exten => 1234,1,Set(CALLERID(all)=Francois Marier <5555551234>)
+    exten => 1234,n,Dial(SIP/sip.voip.ms/5555556789)
+
+[[!tag asterisk]] [[!tag voipms]]
diff --git a/posts/sip-encryption-on-voip-ms.mdwn b/posts/sip-encryption-on-voip-ms.mdwn
index 2cb3209..0d99118 100644
--- a/posts/sip-encryption-on-voip-ms.mdwn
+++ b/posts/sip-encryption-on-voip-ms.mdwn
@@ -70,4 +70,4 @@ 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 debian]] [[!tag asterisk]] [[!tag nzoss]] [[!tag letsencrypt]] [[!tag voipms]]

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

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

Add Pi-Star 4.1 upgrade post
diff --git a/posts/upgrade-dmr-hotspot-pistar-4_1.mdwn b/posts/upgrade-dmr-hotspot-pistar-4_1.mdwn
new file mode 100644
index 0000000..4cd908f
--- /dev/null
+++ b/posts/upgrade-dmr-hotspot-pistar-4_1.mdwn
@@ -0,0 +1,99 @@
+[[!meta title="Upgrading a DMR hotspot to Pi-Star 4.1 RC-7"]]
+[[!meta date="2020-03-01T10:30:00.000-08:00"]]
+[[!meta license="[Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)"]]
+
+While the [Pi-Star](http://www.pistar.uk/) DMR gateway has automatic
+updates, the latest release (3.4.17) is still based on an old version of
+Debian ([Debian 8 jessie](https://www.debian.org/releases/jessie/)) which is
+no longer supported. It is however relatively easy to update to the latest
+release candidate of Pi-Star 4.1 (based on [Debian 10
+buster](https://www.debian.org/releases/buster/)), as long as you have a
+spare SD card (4 GB minimum).
+
+# Download the required files and copy to an SD card
+
+First of all, download the [latest RC image](https://www.pistar.uk/beta/)
+and your [local configuration](http://pi-star.local/admin/configure.php)
+(remember the default account name is `pi-star`). I like to also print a
+copy of that settings page since it's much easier to refer to if things go
+wrong.
+
+Then unzip the image and ["burn"
+it](https://askubuntu.com/questions/179437/how-can-i-burn-a-raspberry-pi-image-to-sd-card-from-ubuntu)
+to a new SD card (no need to format it ahead of time):
+
+    sudo dd if=Pi-Star_RPi_V4.1.0-RC7_20-Dec-2019.img of=/dev/sdX status=progress bs=4M
+    sync
+
+where `/dev/sdX` is the device name for the SD card, which you can find in
+the `dmesg` output. **Don't skip the `sync` command** or you may eject the
+card before your computer is done writing to it.
+
+Then unmount the SD card and unplug it from your computer. Plug it back in.
+You should see two drives mounted automatically on your desktop:
+
+- `pistar`
+- `boot`
+
+Copy the configuration zip file you downloaded earlier onto the root of the
+`boot` drive and then eject the drive.
+
+Run `sync` again before actually unplugging the card.
+
+# Boot into the new version
+
+In order to boot into the new version, start by turning off the Pi. Then
+remove the old SD card and insert the new one that you just prepared. That
+new card will become the new OS drive.
+
+Boot the Pi and ideally connect a monitor to the HDMI port so that you can 
+see it boot up and reboot twice before dropping you to a login prompt.
+
+Login using the default credentials:
+
+- Username: `pi-star`
+- Password: `raspberry`
+
+Once logged in use `top` to see if the pi is busy doing anything. Mine was
+in the process of upgrading Debian packages via `unattended-upgrades` which
+made everything (including the web UI) very slow.
+
+You should now be able to access the [web UI](http://pi-star.local/) using
+the above credentials.
+
+# Update to the latest version
+
+From the command line, you can ensure that you are running the latest
+version of Pi-Star by running the following command:
+
+    sudo pistar-upgrade
+
+This updated from `4.1.0-RC7` to `4.1.0-RC8` on my device.
+
+You can also run the following:
+
+    sudo pistar-update
+
+to update the underlying Raspbian OS.
+
+# Check and restore your settings
+
+Once things have settled down, double-check the
+[settings](http://pi-star.local/admin/configure.php) and restore your admin
+password since that was not part of the configuration backup you made earlier.
+
+I had to restore the following settings since they got lost in the process:
+
+- `Auto AP`: `Off`
+- `uPNP`: `Off`
+
+# Roll back to the previous version
+
+If you run into problems, the best option is to roll back to the previous
+version and then try again.
+
+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]]

Comment moderation
diff --git a/posts/encrypting-your-home-directory-using/comment_13_466daae3810506ee699b1b4c421d866a._comment b/posts/encrypting-your-home-directory-using/comment_13_466daae3810506ee699b1b4c421d866a._comment
new file mode 100644
index 0000000..166122b
--- /dev/null
+++ b/posts/encrypting-your-home-directory-using/comment_13_466daae3810506ee699b1b4c421d866a._comment
@@ -0,0 +1,9 @@
+[[!comment format=mdwn
+ ip="98.169.32.134"
+ claimedauthor="jamesob"
+ url="jameso.be"
+ subject="Unmounting /home"
+ date="2020-02-17T19:58:45Z"
+ content="""
+If you get errors during `umount /home`, you may need to end your graphical shell and login as root before mounting /home. On Debian, e.g., you can do this by pressing CTRL+ALT+F{1,2,3,4} at a graphical login prompt before logging in as a regular user, and then logging in as `root` from there. This way, `lsof /home` should return nothing and you should be able to unmount /home without error.
+"""]]

Add post about Fedora 31 LXC containers
diff --git a/posts/fedora31-lxc-setup-on-ubuntu-bionic.mdwn b/posts/fedora31-lxc-setup-on-ubuntu-bionic.mdwn
new file mode 100644
index 0000000..edda488
--- /dev/null
+++ b/posts/fedora31-lxc-setup-on-ubuntu-bionic.mdwn
@@ -0,0 +1,123 @@
+[[!meta title="Fedora 31 LXC setup on Ubuntu Bionic 18.04"]]
+[[!meta date="2020-02-09T21:30:00.000-08:00"]]
+[[!meta license="[Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)"]]
+
+Similarly to what I wrote for [Fedora 29](https://feeding.cloud.geek.nz/posts/fedora29-lxc-setup-on-ubuntu-bionic/),
+here is how I was able to create a [Fedora](https://getfedora.org/) 31 LXC
+container on an Ubuntu 18.04 (bionic) laptop.
+
+# Setting up LXC on Ubuntu
+
+First of all, install lxc:
+
+    apt install lxc
+    echo "veth" >> /etc/modules
+    modprobe veth
+
+turn on bridged networking by putting the following in
+`/etc/sysctl.d/local.conf`:
+
+    net.ipv4.ip_forward=1
+
+and applying it using:
+
+    sysctl -p /etc/sysctl.d/local.conf
+
+Then allow the right traffic in your firewall
+(`/etc/network/iptables.up.rules` in my case):
+
+    # LXC containers
+    -A FORWARD -d 10.0.3.0/24 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
+    -A FORWARD -s 10.0.3.0/24 -j ACCEPT
+    -A INPUT -d 224.0.0.251 -s 10.0.3.1 -j ACCEPT
+    -A INPUT -d 239.255.255.250 -s 10.0.3.1 -j ACCEPT
+    -A INPUT -d 10.0.3.255 -s 10.0.3.1 -j ACCEPT
+    -A INPUT -d 10.0.3.1 -s 10.0.3.0/24 -j ACCEPT
+
+and apply these changes:
+
+    iptables-apply
+
+before restarting the lxc networking:
+
+    systemctl restart lxc-net.service
+
+# Create the container
+
+Once that's in place, you can finally create the Fedora 29 container:
+
+    lxc-create -n fedora31 -t download -- -d fedora -r 31 -a amd64
+
+To see a list of all distros available with the `download` template:
+
+    lxc-create -n foo --template=download -- --list
+
+Once the container has been created, disable AppArmor for it:
+
+    lxc.apparmor.profile = unconfined
+
+since the AppArmor profile [isn't working at the moment](https://github.com/lxc/lxc/issues/2778).
+
+# Logging in as root
+
+Starting the container in one window:
+
+    lxc-start -n fedora31 -F
+
+and attaching to a console:
+
+    lxc-attach -n fedora31
+
+to set a root password:
+
+    passwd
+
+# Logging in as an unprivileged user via ssh
+
+While logged into the console, I tried to install ssh:
+
+    $ dnf install openssh-server
+    Cannot create temporary file - mkstemp: No such file or directory
+
+but it failed because `TMPDIR` is set to a non-existent directory:
+
+    $ echo $TMPDIR
+    /tmp/user/0
+
+I found [a fix](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=728500)
+and ran the following:
+
+    TMPDIR=/tmp dnf install openssh-server
+
+then started the ssh service:
+
+  systemctl start sshd.service
+
+Then I installed a few other packages as root:
+
+    dnf install vim sudo man
+
+and created an unprivileged user with sudo access:
+
+    adduser francois -G wheel
+    passwd francois
+
+I set this in `/etc/ssh/sshd_config`:
+
+    GSSAPIAuthentication no
+
+to prevent [slow ssh logins](https://serverfault.com/questions/815538/long-delay-when-logging-in-with-centos7).
+
+Now login as that user from the console and add an ssh public key:
+
+    mkdir .ssh
+    chmod 700 .ssh
+    echo "<your public key>" > .ssh/authorized_keys
+    chmod 644 .ssh/authorized_keys
+
+You can now login via ssh. The IP address to use can be seen in the output
+of:
+
+    lxc-ls --fancy
+
+[[!tag debian]] [[!tag lxc]] [[!tag nzoss]] [[!tag ubuntu]]

Add Talkwalker
diff --git a/posts/keeping-track-of-what-others-are-saying.mdwn b/posts/keeping-track-of-what-others-are-saying.mdwn
index 8ebf1e4..95ae80c 100644
--- a/posts/keeping-track-of-what-others-are-saying.mdwn
+++ b/posts/keeping-track-of-what-others-are-saying.mdwn
@@ -17,7 +17,16 @@ I have also signed up for the following [Google Alert](http://www.google.com/ale
 
 which emails me anytime someone links to my project's homepage in one of the resources indexed by Google.  
   
+## Talkwalker
 
+In addition to Google Alerts, I also subscribe to the [Talkwalker Alerts](https://www.talkwalker.com/alerts)
+service with the following alert:
+
+- Search query: `safe-rm`
+- Result type: (leave blank for *Everything*)
+- Language: `All languages`
+- How often: `As it happens`
+- How many: `All results`
 
 Anything else I should subscribe to or follow?
 

Convert table to simple list
diff --git a/posts/keeping-track-of-what-others-are-saying.mdwn b/posts/keeping-track-of-what-others-are-saying.mdwn
index 7e367f0..8ebf1e4 100644
--- a/posts/keeping-track-of-what-others-are-saying.mdwn
+++ b/posts/keeping-track-of-what-others-are-saying.mdwn
@@ -10,15 +10,10 @@ Here are a few ways to find out what people are saying online about your project
 
 I have also signed up for the following [Google Alert](http://www.google.com/alerts):
 
-> <table>
-> <tr><td>Search terms:</td><td>`link:safe-rm.org.nz`</td>
-> </tr>
-> <tr><td>Type:</td><td>`Comprehensive`</td>
-> </tr>
-> <tr><td>Deliver to:</td><td>`Email`</td>
-> </tr>
-> <tr><td>How often:</td><td>`as-it-happens`</td>
-> </tr></table>
+- Search terms: `link:safe-rm.org.nz`
+- Type: `Comprehensive`
+- Deliver to: `Email`
+- How often: `as-it-happens`
 
 which emails me anytime someone links to my project's homepage in one of the resources indexed by Google.  
   

Remove defunct services
diff --git a/posts/keeping-track-of-what-others-are-saying.mdwn b/posts/keeping-track-of-what-others-are-saying.mdwn
index 6c41253..7e367f0 100644
--- a/posts/keeping-track-of-what-others-are-saying.mdwn
+++ b/posts/keeping-track-of-what-others-are-saying.mdwn
@@ -6,20 +6,6 @@ There are a few ways to easily keep track of what others are saying about your F
 Here are a few ways to find out what people are saying online about your project.  
   
 
-## RSS Feeds
-
-First of all, here are the RSS feeds I subscribe to for one of my projects ([safe-rm](https://launchpad.net/safe-rm)):
-
-  * [Technorati](http://feeds.technorati.com/search/safe-rm.org.nz) (blog posts)
-  * [Twitter tweets](http://search.twitter.com/search.atom?q=safe-rm.org.nz) (tweets)
-  * [Identica](http://identi.ca/search/notice/rss?q=safe-rm.org.nz) (notices)
-  * [TweetMeme](http://rss.tweetmeme.com/domain/?host=http://safe-rm.org.nz) (tweets)
-  * [BackTweets](http://backtweets.com/search.rss?q=safe-rm.org.nz) (tweets)
-  * [Freshmeat](http://freshmeat.net/projects/safe-rm/comments.atom) (comments)
-  * [Ohloh](http://www.ohloh.net/p/safe-rm/messages.rss) (journal entries)
-
-  
-
 ## Google Alerts
 
 I have also signed up for the following [Google Alert](http://www.google.com/alerts):
@@ -37,15 +23,7 @@ I have also signed up for the following [Google Alert](http://www.google.com/ale
 which emails me anytime someone links to my project's homepage in one of the resources indexed by Google.  
   
 
-## Backtype Alerts
 
-Finally, these [Backtype Alerts](http://www.backtype.com/alerts) notify me anytime safe-rm features in a blog comment:
-
-  * `safe-rm`
-  * `safe-rm.org.nz`
-
-  
-  
 Anything else I should subscribe to or follow?
 
 

Add note about slow SSH logins
diff --git a/posts/fedora29-lxc-setup-on-ubuntu-bionic.mdwn b/posts/fedora29-lxc-setup-on-ubuntu-bionic.mdwn
index 7b87943..be3c649 100644
--- a/posts/fedora29-lxc-setup-on-ubuntu-bionic.mdwn
+++ b/posts/fedora29-lxc-setup-on-ubuntu-bionic.mdwn
@@ -76,6 +76,12 @@ and then create an unprivileged user with sudo access:
     adduser francois -G wheel
     passwd francois
 
+I set this in `/etc/ssh/sshd_config`:
+
+    GSSAPIAuthentication no
+
+to prevent [slow ssh logins](https://serverfault.com/questions/815538/long-delay-when-logging-in-with-centos7).
+
 Now login as that user from the console and add an ssh public key:
 
     mkdir .ssh

Comment moderation
diff --git a/posts/programming-anytone-d878uv-on-linux-using-windows10-and-virtualbox/comment_5_231d5e5a78c9e67489605b40eb2b36f5._comment b/posts/programming-anytone-d878uv-on-linux-using-windows10-and-virtualbox/comment_5_231d5e5a78c9e67489605b40eb2b36f5._comment
new file mode 100644
index 0000000..f758f2c
--- /dev/null
+++ b/posts/programming-anytone-d878uv-on-linux-using-windows10-and-virtualbox/comment_5_231d5e5a78c9e67489605b40eb2b36f5._comment
@@ -0,0 +1,10 @@
+[[!comment format=mdwn
+ username="francois@665656f0ba400877c9b12e8fbb086e45aa01f7c0"
+ nickname="francois"
+ subject="Re: WINE"
+ date="2020-02-05T19:42:16Z"
+ content="""
+> Have you tried running the programming software in WINE?
+
+I have not. If you do end up trying it, I'd be curious to hear about it.
+"""]]

Comment moderation
diff --git a/posts/programming-anytone-d878uv-on-linux-using-windows10-and-virtualbox/comment_4_3f0de17a6d3f39419d9572e50e7d8c27._comment b/posts/programming-anytone-d878uv-on-linux-using-windows10-and-virtualbox/comment_4_3f0de17a6d3f39419d9572e50e7d8c27._comment
new file mode 100644
index 0000000..f7057cb
--- /dev/null
+++ b/posts/programming-anytone-d878uv-on-linux-using-windows10-and-virtualbox/comment_4_3f0de17a6d3f39419d9572e50e7d8c27._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ ip="187.190.248.208"
+ claimedauthor="Mark"
+ subject="WINE"
+ date="2020-02-05T18:55:30Z"
+ content="""
+Have you tried running the programming software in WINE? Just curious since that would eliminate the VBox setup and using Windows. 
+"""]]
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
new file mode 100644
index 0000000..2b8ef36
--- /dev/null
+++ b/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_7_6d782ed81d9fbdfbcc70fdf6da15fbed._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ ip="70.125.19.73"
+ claimedauthor="Gus"
+ 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!
+"""]]

Comment moderation
diff --git a/posts/setting-up-raid-on-existing/comment_18_f58080e8a92469474b205e9b40eb4374._comment b/posts/setting-up-raid-on-existing/comment_18_f58080e8a92469474b205e9b40eb4374._comment
new file mode 100644
index 0000000..8351d96
--- /dev/null
+++ b/posts/setting-up-raid-on-existing/comment_18_f58080e8a92469474b205e9b40eb4374._comment
@@ -0,0 +1,12 @@
+[[!comment format=mdwn
+ ip="198.52.147.213"
+ claimedauthor="Dennis Chang"
+ subject="Ubuntu 18.04.3 LTS update"
+ date="2020-01-19T17:27:30Z"
+ content="""
+Hi,
+
+Thanks for the article! I followed it and it was my primary reference. However, I did encounter two issues while following it that I thought I should leave with you here. I'm using Ubuntu 18.04.3 and fdisk does not have 'fd' to convert a Linux partition to a Linux RAID partition. It is now '29' for a Linux RAID partition. Also, after adding the original disk to the RAID array and rebooting I found myself in grub rescue mode. After researching more I discovered that I was required to install a module 'mdraid1x' in order for grub to be able to read the RAID array and find the kernel in order to boot. Therefore, I had to boot a Live USB Ubuntu system to access the RAID array before chroot and grub-install --modules='mdraid1x' /dev/sda (to both drives). And finally, when I test each drive separately to see if it will boot (it does).
+
+Anyway, thanks for the article.
+"""]]

Update article for buster
diff --git a/posts/usual-server-setup.mdwn b/posts/usual-server-setup.mdwn
index c04e271..5f27fc7 100644
--- a/posts/usual-server-setup.mdwn
+++ b/posts/usual-server-setup.mdwn
@@ -207,12 +207,7 @@ and these to `/etc/rkhunter.conf.local`:
 
     apt install apparmor apparmor-profiles apparmor-profiles-extra libpam-tmpdir
 
-While the harden packages are configuration-free, AppArmor must be [manually enabled](https://wiki.debian.org/AppArmor/HowToUse#Enable_AppArmor):
-
-    perl -pi -e 's,GRUB_CMDLINE_LINUX="(.*)"$,GRUB_CMDLINE_LINUX="$1 apparmor=1 security=apparmor",' /etc/default/grub
-    update-grub
-
-In addition, certain kernel security features can be enabled by putting the
+Certain kernel security features can be enabled by putting the
 following in `/etc/sysctl.d/local.conf` to
 [hide kernel pointers and messages](https://www.kernel.org/doc/Documentation/sysctl/kernel.txt)
 from unprivileged processes:
@@ -238,9 +233,9 @@ and the following to harden the TCP stack:
 
 before reloading these settings using `sysctl -p`.
 
-Sandboxing in apt can be enabled by putting the following in `/etc/apt/apt.conf.d/30-seccomp`:
+[Sandboxing in apt](https://www.debian.org/releases/buster/amd64/release-notes/ch-whats-new.en.html#apt-sandboxing) can be enabled by putting the following in `/etc/apt/apt.conf.d/30-seccomp`:
 
-    APT::Sandbox::Seccomp "true";
+    APT::Sandbox::Seccomp "1";
 
 I also restrict the use of cron to the `root` user by putting the following in `/etc/cron.allow`:
 

Add hotspot article
diff --git a/posts/sharing-wifi-connection-with-network-manager-hotspot.mdwn b/posts/sharing-wifi-connection-with-network-manager-hotspot.mdwn
new file mode 100644
index 0000000..e307e52
--- /dev/null
+++ b/posts/sharing-wifi-connection-with-network-manager-hotspot.mdwn
@@ -0,0 +1,96 @@
+[[!meta title="Sharing your WiFi connection with a NetworkManager hotspot"]]
+[[!meta date="2020-01-15T18:15:00.000-08:00"]]
+[[!meta license="[Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)"]]
+
+In-flight and hotel WiFi can be quite expensive and often insist on charging
+users extra to connect multiple devices. In order to avoid that, it's
+possible to easily create a WiFi hotspot using
+[NetworkManager](https://wiki.gnome.org/Projects/NetworkManager) and a
+[external USB WiFi
+adapter](https://www.thinkpenguin.com/gnu-linux/penguin-wireless-n-usb-adapter-gnu-linux-tpe-n150usb).
+
+# Creating the hotspot
+
+The main trick is to right-click on the NetworkManager icon in the status
+bar and select "Edit Connections..." (**not** "Create New WiFi Network..."
+despite the promising name).
+
+From there click the "+" button in the lower right then "WiFi" as the
+Connection Type. I like to use the computer name as the "Connection name".
+
+In the *WiFi* tab, set the following:
+
+- SSID: machinename_nomap
+- Mode: hotspot
+- Device: (the device name of the USB WiFi adapter)
+
+The `_nomap` suffix is there to opt out of the
+[Google](https://support.google.com/maps/answer/1725632?hl=en) and
+[Mozilla](https://location.services.mozilla.com/optout) location services
+which could allow anybody to lookup sightings of your device around the
+World.
+
+In the *WiFi Security* tab:
+
+- Security: WPA & WPA2 Personal
+- Password: (a 63-character random password generated using `pwgen -s 63`)
+
+While you may think that such a long password is inconvenient, it's now
+possible to add the network automatically by simply [scanning a QR
+code](https://feeding.cloud.geek.nz/posts/encoding-wifi-access-point-passwords-qr-code/)
+on your phone.
+
+In the *IPv4 Settings* tab:
+
+- Method: Shared to other computers
+
+Finally, in the *IPv6 Settings* tab:
+
+- Method: Ignore
+
+I ended up with the following config in
+`/etc/NetworkManager/system-connections/machinename`:
+
+    [connection]
+    id=machinename
+    uuid=<long UUID string>
+    type=wifi
+    interface-name=wl...
+    permissions=
+    timestamp=1578533792
+    
+    [wifi]
+    mac-address=<MAC>
+    mac-address-blacklist=
+    mode=ap
+    seen-bssids=<BSSID>
+    ssid=machinename_nomap
+    
+    [wifi-security]
+    key-mgmt=wpa-psk
+    psk=<63-character password>
+    
+    [ipv4]
+    dns-search=
+    method=shared
+    
+    [ipv6]
+    addr-gen-mode=stable-privacy
+    dns-search=
+    ip6-privacy=0
+    method=ignore
+
+# Firewall rules
+
+In order for the packets to flow correctly, I opened up the following ports
+on my machine's local firewall:
+
+    -A INPUT -s 10.42.0.0/24 -j ACCEPT
+    -A FORWARD -d 10.42.0.0/24 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
+    -A FORWARD -s 10.42.0.0/24 -j ACCEPT
+    -A INPUT -d 224.0.0.251 -s 10.42.0.1 -j ACCEPT
+    -A INPUT -d 239.255.255.250 -s 10.42.0.1 -j ACCEPT
+    -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]]

Allow builds to be interrupted if ran interactively
diff --git a/posts/seeding-brave-browser-sccache.mdwn b/posts/seeding-brave-browser-sccache.mdwn
index 6ccf30f..5697ea9 100644
--- a/posts/seeding-brave-browser-sccache.mdwn
+++ b/posts/seeding-brave-browser-sccache.mdwn
@@ -52,7 +52,7 @@ and here are the contents of that script:
     echo $(date)
     echo "=> Debug build"
     killall sccache || true
-    ionice nice timeout 4h npm run build || ionice nice timeout 4h npm run build
+    ionice nice timeout --foreground 4h npm run build || ionice nice timeout 4h npm run build
     ionice nice ninja -C src/out/Component brave_unit_tests
     ionice nice ninja -C src/out/Component brave_browser_tests
     echo
@@ -60,7 +60,7 @@ and here are the contents of that script:
     echo $(date)
     echo "=> Release build"
     killall sccache || true
-    ionice nice timeout 5h npm run build Release || ionice nice timeout 5h npm run build Release
+    ionice nice timeout --foreground 5h npm run build Release || ionice nice timeout 5h npm run build Release
     ionice nice ninja -C src/out/Release brave_unit_tests
     ionice nice ninja -C src/out/Release brave_browser_tests
     echo

Debug builds are now in src/out/Component by default
diff --git a/posts/seeding-brave-browser-sccache.mdwn b/posts/seeding-brave-browser-sccache.mdwn
index 423f702..6ccf30f 100644
--- a/posts/seeding-brave-browser-sccache.mdwn
+++ b/posts/seeding-brave-browser-sccache.mdwn
@@ -53,12 +53,12 @@ and here are the contents of that script:
     echo "=> Debug build"
     killall sccache || true
     ionice nice timeout 4h npm run build || ionice nice timeout 4h npm run build
-    ionice nice ninja -C src/out/Debug brave_unit_tests
-    ionice nice ninja -C src/out/Debug brave_browser_tests
+    ionice nice ninja -C src/out/Component brave_unit_tests
+    ionice nice ninja -C src/out/Component brave_browser_tests
     echo
     
     echo $(date)
-    echo "=>Release build"
+    echo "=> Release build"
     killall sccache || true
     ionice nice timeout 5h npm run build Release || ionice nice timeout 5h npm run build Release
     ionice nice ninja -C src/out/Release brave_unit_tests

Upgrade links to HTTPS
diff --git a/posts/getting-involved-in-open-source-project.mdwn b/posts/getting-involved-in-open-source-project.mdwn
index 97c5848..0a32b59 100644
--- a/posts/getting-involved-in-open-source-project.mdwn
+++ b/posts/getting-involved-in-open-source-project.mdwn
@@ -1,13 +1,13 @@
 [[!meta title="Getting involved in an Open Source project"]]
 [[!meta date="2009-02-11T11:30:00.001+13:00"]]
-[[!meta license="[Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)"]]
+[[!meta license="[Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/)"]]
 Contributing to an Open Source project is a fantastic way to learn new skills and gain some real work experience. Unlike most of the work done in the proprietary world, the contributions you make to a Free Software project are generally publicly visible and thus can easily be included in a portfolio you show to prospective employers.
 
 In most cases, getting involved in an existing project is quite easy. Most projects need help and would be very happy to count you on board even if you're not a developer. Good places to start:
 
-  * the operating system you use (e.g. [Ubuntu](http://www.ubuntu.com/community/participate) or [Debian](http://wiki.debian.org/GetInvolved-Beta))
-  * a piece of [Free Software](http://www.fsf.org/about/what-is-free-software) you use every day
+  * the operating system you use (e.g. [Ubuntu](https://discourse.ubuntu.com/t/contribute/26) or [Debian](https://wiki.debian.org/GetInvolved-Beta))
+  * a piece of [Free Software](https://www.fsf.org/about/what-is-free-software) you use every day
 
-Of course, if you can't find a project that solves your own particular problem, you may also want to [start a new one](http://feeding.cloud.geek.nz/2008/09/starting-free-software-project.html). You may be surprised at how many people will find your pet project useful and see a [community grow](http://feeding.cloud.geek.nz/2008/09/growing-community-around-your-new-free.html) around it.
+Of course, if you can't find a project that solves your own particular problem, you may also want to [start a new one](https://feeding.cloud.geek.nz/posts/getting-involved-in-open-source-project/). You may be surprised at how many people will find your pet project useful and see a [community grow](https://feeding.cloud.geek.nz/posts/growing-community-around-your-new-free/) around it.
   
 I gave [a presentation](https://archive.org/details/contributing-to-foss_soc) on this topic for the [New Zealand Summer of Code](https://summeroftech.co.nz/).

Fix dead links
diff --git a/posts/getting-involved-in-open-source-project.mdwn b/posts/getting-involved-in-open-source-project.mdwn
index fd7a8a1..97c5848 100644
--- a/posts/getting-involved-in-open-source-project.mdwn
+++ b/posts/getting-involved-in-open-source-project.mdwn
@@ -10,4 +10,4 @@ In most cases, getting involved in an existing project is quite easy. Most proje
 
 Of course, if you can't find a project that solves your own particular problem, you may also want to [start a new one](http://feeding.cloud.geek.nz/2008/09/starting-free-software-project.html). You may be surprised at how many people will find your pet project useful and see a [community grow](http://feeding.cloud.geek.nz/2008/09/growing-community-around-your-new-free.html) around it.
   
-I gave [a presentation](http://summerofcode.blip.tv/file/1760791/) on this topic for the [New Zealand Summer of Code](http://www.summerofcode.co.nz).
+I gave [a presentation](https://archive.org/details/contributing-to-foss_soc) on this topic for the [New Zealand Summer of Code](https://summeroftech.co.nz/).

Remove unnecessary whitespace
diff --git a/posts/getting-involved-in-open-source-project.mdwn b/posts/getting-involved-in-open-source-project.mdwn
index 70ee512..fd7a8a1 100644
--- a/posts/getting-involved-in-open-source-project.mdwn
+++ b/posts/getting-involved-in-open-source-project.mdwn
@@ -1,15 +1,13 @@
 [[!meta title="Getting involved in an Open Source project"]]
 [[!meta date="2009-02-11T11:30:00.001+13:00"]]
 [[!meta license="[Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)"]]
-Contributing to an Open Source project is a fantastic way to learn new skills and gain some real work experience. Unlike most of the work done in the proprietary world, the contributions you make to a Free Software project are generally publicly visible and thus can easily be included in a portfolio you show to prospective employers.  
-  
-In most cases, getting involved in an existing project is quite easy. Most projects need help and would be very happy to count you on board even if you're not a developer. Good places to start:  
+Contributing to an Open Source project is a fantastic way to learn new skills and gain some real work experience. Unlike most of the work done in the proprietary world, the contributions you make to a Free Software project are generally publicly visible and thus can easily be included in a portfolio you show to prospective employers.
+
+In most cases, getting involved in an existing project is quite easy. Most projects need help and would be very happy to count you on board even if you're not a developer. Good places to start:
 
   * the operating system you use (e.g. [Ubuntu](http://www.ubuntu.com/community/participate) or [Debian](http://wiki.debian.org/GetInvolved-Beta))
-  * a piece of [Free Software](http://www.fsf.org/about/what-is-free-software) you use every day  
+  * a piece of [Free Software](http://www.fsf.org/about/what-is-free-software) you use every day
 
-Of course, if you can't find a project that solves your own particular problem, you may also want to [start a new one](http://feeding.cloud.geek.nz/2008/09/starting-free-software-project.html). You may be surprised at how many people will find your pet project useful and see a [community grow](http://feeding.cloud.geek.nz/2008/09/growing-community-around-your-new-free.html) around it.  
+Of course, if you can't find a project that solves your own particular problem, you may also want to [start a new one](http://feeding.cloud.geek.nz/2008/09/starting-free-software-project.html). You may be surprised at how many people will find your pet project useful and see a [community grow](http://feeding.cloud.geek.nz/2008/09/growing-community-around-your-new-free.html) around it.
   
 I gave [a presentation](http://summerofcode.blip.tv/file/1760791/) on this topic for the [New Zealand Summer of Code](http://www.summerofcode.co.nz).
-
-

Update qdisc guidance to fq
This is based on https://www.bufferbloat.net/projects/codel/wiki/#Binary-code-and-kernels-for-Linux-based-operating-systems.
diff --git a/posts/usual-server-setup.mdwn b/posts/usual-server-setup.mdwn
index b048270..c04e271 100644
--- a/posts/usual-server-setup.mdwn
+++ b/posts/usual-server-setup.mdwn
@@ -397,7 +397,7 @@ To [reduce the server's contribution to
 bufferbloat](https://lwn.net/Articles/616241/) I change the default kernel
 queueing discipline (jessie or later) by putting the following in `/etc/sysctl.d/local.conf`:
 
-    net.core.default_qdisc=fq_codel
+    net.core.default_qdisc=fq
 
 and the following to improve congestion control and
 [HTTP/2 prioritization](https://blog.cloudflare.com/http-2-prioritization-with-nginx/):

Add third pass assets
diff --git a/posts/receiving-sstv-images-from-iss/iss_sstv3.png b/posts/receiving-sstv-images-from-iss/iss_sstv3.png
new file mode 100644
index 0000000..dc9a5e8
Binary files /dev/null and b/posts/receiving-sstv-images-from-iss/iss_sstv3.png differ
diff --git a/posts/receiving-sstv-images-from-iss/iss_sstv3.wav b/posts/receiving-sstv-images-from-iss/iss_sstv3.wav
new file mode 100644
index 0000000..3f5178f
Binary files /dev/null and b/posts/receiving-sstv-images-from-iss/iss_sstv3.wav differ

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

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

Create tags for each of my radios
diff --git a/posts/programming-anytone-d878uv-on-linux-using-windows10-and-virtualbox.mdwn b/posts/programming-anytone-d878uv-on-linux-using-windows10-and-virtualbox.mdwn
index 1d0463a..953b794 100644
--- a/posts/programming-anytone-d878uv-on-linux-using-windows10-and-virtualbox.mdwn
+++ b/posts/programming-anytone-d878uv-on-linux-using-windows10-and-virtualbox.mdwn
@@ -75,4 +75,4 @@ since it's much better than the official manual.
 You should be able to use the "Write to radio" menu option without any
 problems once you're done creating your codeplug.
 
-[[!tag ham]]
+[[!tag ham]] [[!tag anytone]]
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 4ddba68..d97037a 100644
--- a/posts/using-kenwood-th-d72a-with-pat-linux-ax25.mdwn
+++ b/posts/using-kenwood-th-d72a-with-pat-linux-ax25.mdwn
@@ -98,4 +98,4 @@ If you can't connect to `VA7EOC-10` on UHF, you could also try the VHF BCFM
 repeater on Mt Seymour, [VE7LAN](http://www.bcfmca.bc.ca/lanvhf.php) (VECTOR
 channel 65).
 
-[[!tag ham]]
+[[!tag ham]] [[!tag kenwood]]

Add post about SSTV on the ISS
diff --git a/posts/receiving-sstv-images-from-iss.mdwn b/posts/receiving-sstv-images-from-iss.mdwn
new file mode 100644
index 0000000..4375796
--- /dev/null
+++ b/posts/receiving-sstv-images-from-iss.mdwn
@@ -0,0 +1,80 @@
+[[!meta title="Receiving slow-scan television images from the International Space Station"]]
+[[!meta date="2020-01-01T16:00:00.000-08:00"]]
+[[!meta license="[Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)"]]
+
+Thanks to a fellow [VECTOR](https://vectorradio.ca) volunteer [Nick
+Doyle](https://www.qrz.com/db/VE7NCD), I found out that the [International
+Space Station](https://en.wikipedia.org/wiki/International_Space_Station)
+would be broadcasting [slow-scan
+television](https://en.wikipedia.org/wiki/Slow-scan_television) images at
+the end of the year. I decided to try and pick those up with my handheld
+radio.
+
+# Planning
+
+From the [official announcement](https://ariss.pzk.org.pl/sstv/), I got the
+frequency (145.800 MHz) and the broadcast times.
+
+Next I had to figure out when the ISS would be passing over my location.
+Most of the ISS tracking websites and applications are aimed at people
+wanting to _see_ the reflection of the sun on the station and so they only
+list the passes during nighttime before the earth casts a shadow that would
+prevent any visual contacts.
+
+Thankfully, Nick found a site which has a option to show all of the passes,
+visible or not and so I was able to get a [list of upcoming passes over
+Vancouver](https://heavens-above.com/PassSummary.aspx?satid=25544&lat=49.2827&lng=-123.1207&loc=Vancouver&alt=0&tz=PST).
+
+# Hardware
+
+From a hardware point-of-view, I didn't have to get any special equipment. I
+used my [Kenwood D72](https://www.kenwood.com/usa/com/amateur/th-d72a/) and
+an external Comet SBB5 mobile antenna.
+
+The only other pieces of equipment I used was a 2.5mm mono adapter which I
+used to connect a 3.5mm male-male audio cable in the speaker port of the
+radio and the microphone input of my computer.
+
+# Software
+
+The software I used for the recording was
+[Audacity](https://www.audacityteam.org/) set to a **sampling rate of 48
+kHz**.
+
+Then I installed
+[qsstv](http://users.telenet.be/on4qz/qsstv/manual/index.html) and
+configured it to [read input from a file instead of the sound
+card](http://users.telenet.be/on4qz/qsstv/faq.html#Audiofile).
+
+# Results
+
+Here is the [audio I
+recorded](/posts/receiving-sstv-images-from-iss/iss_sstv1.wav) from the
+first pass (65 degrees at the highest point) as well as the rendered image:
+
+![](/posts/receiving-sstv-images-from-iss/iss_sstv1.png)
+
+The second pass (60 degrees) was not as successful since I didn't hold the
+squelch open and you can tell from the [audio
+recording](/posts/receiving-sstv-images-from-iss/iss_sstv2.wav) that the
+signal got drowned in noise a couple of times. This is the rendering of that
+second pass:
+
+![](/posts/receiving-sstv-images-from-iss/iss_sstv2.png)
+
+# Tips
+
+The signal came through the squelch for only about a minute at the highest
+point, so I found it best to open the squelch fully (`F`+`Moni`) as soon as
+the bird is visible.
+
+Another thing I did on a third pass (16 degrees at the highest point -- not
+particularly visible) was to plug the speaker out of my radio into a Y
+splitter so that I could connect it to my computer and an external speaker I
+could take outside with me. Since I was able to listen to the audio, I held
+the antenna and tried to **point it** at the [satellite's general
+direction](https://heavens-above.com/orbit.aspx?satid=25544&lat=49.2827&lng=-123.1207&loc=Vancouver&alt=0&tz=PST)
+as well as **varying the orientation** of the antenna to increase the signal
+strength.
+
+[[!tag kenwood]] [[!tag ham]]
diff --git a/posts/receiving-sstv-images-from-iss/iss_sstv1.png b/posts/receiving-sstv-images-from-iss/iss_sstv1.png
new file mode 100644
index 0000000..5026cd0
Binary files /dev/null and b/posts/receiving-sstv-images-from-iss/iss_sstv1.png differ
diff --git a/posts/receiving-sstv-images-from-iss/iss_sstv1.wav b/posts/receiving-sstv-images-from-iss/iss_sstv1.wav
new file mode 100644
index 0000000..173913c
Binary files /dev/null and b/posts/receiving-sstv-images-from-iss/iss_sstv1.wav differ
diff --git a/posts/receiving-sstv-images-from-iss/iss_sstv2.png b/posts/receiving-sstv-images-from-iss/iss_sstv2.png
new file mode 100644
index 0000000..fe0129f
Binary files /dev/null and b/posts/receiving-sstv-images-from-iss/iss_sstv2.png differ
diff --git a/posts/receiving-sstv-images-from-iss/iss_sstv2.wav b/posts/receiving-sstv-images-from-iss/iss_sstv2.wav
new file mode 100644
index 0000000..b9d05c0
Binary files /dev/null and b/posts/receiving-sstv-images-from-iss/iss_sstv2.wav differ

calendar update
diff --git a/archives/2020.mdwn b/archives/2020.mdwn
new file mode 100644
index 0000000..8843a09
--- /dev/null
+++ b/archives/2020.mdwn
@@ -0,0 +1 @@
+[[!calendar type=year year=2020 pages="page(posts/*) and !*/Discussion"]]
diff --git a/archives/2020/01.mdwn b/archives/2020/01.mdwn
new file mode 100644
index 0000000..0577516
--- /dev/null
+++ b/archives/2020/01.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=01 year=2020 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(01) and creation_year(2020) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/archives/2020/02.mdwn b/archives/2020/02.mdwn
new file mode 100644
index 0000000..189d77c
--- /dev/null
+++ b/archives/2020/02.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=02 year=2020 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(02) and creation_year(2020) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/archives/2020/03.mdwn b/archives/2020/03.mdwn
new file mode 100644
index 0000000..63a191c
--- /dev/null
+++ b/archives/2020/03.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=03 year=2020 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(03) and creation_year(2020) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/archives/2020/04.mdwn b/archives/2020/04.mdwn
new file mode 100644
index 0000000..d8be67f
--- /dev/null
+++ b/archives/2020/04.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=04 year=2020 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(04) and creation_year(2020) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/archives/2020/05.mdwn b/archives/2020/05.mdwn
new file mode 100644
index 0000000..92e59fa
--- /dev/null
+++ b/archives/2020/05.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=05 year=2020 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(05) and creation_year(2020) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/archives/2020/06.mdwn b/archives/2020/06.mdwn
new file mode 100644
index 0000000..fff6e79
--- /dev/null
+++ b/archives/2020/06.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=06 year=2020 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(06) and creation_year(2020) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/archives/2020/07.mdwn b/archives/2020/07.mdwn
new file mode 100644
index 0000000..9674bf2
--- /dev/null
+++ b/archives/2020/07.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=07 year=2020 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(07) and creation_year(2020) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/archives/2020/08.mdwn b/archives/2020/08.mdwn
new file mode 100644
index 0000000..19eec13
--- /dev/null
+++ b/archives/2020/08.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=08 year=2020 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(08) and creation_year(2020) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/archives/2020/09.mdwn b/archives/2020/09.mdwn
new file mode 100644
index 0000000..282af8d
--- /dev/null
+++ b/archives/2020/09.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=09 year=2020 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(09) and creation_year(2020) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/archives/2020/10.mdwn b/archives/2020/10.mdwn
new file mode 100644
index 0000000..e459232
--- /dev/null
+++ b/archives/2020/10.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=10 year=2020 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(10) and creation_year(2020) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/archives/2020/11.mdwn b/archives/2020/11.mdwn
new file mode 100644
index 0000000..b462704
--- /dev/null
+++ b/archives/2020/11.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=11 year=2020 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(11) and creation_year(2020) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]
diff --git a/archives/2020/12.mdwn b/archives/2020/12.mdwn
new file mode 100644
index 0000000..2503185
--- /dev/null
+++ b/archives/2020/12.mdwn
@@ -0,0 +1,5 @@
+[[!sidebar content="""
+[[!calendar type=month month=12 year=2020 pages="page(posts/*) and !*/Discussion"]]
+"""]]
+
+[[!inline pages="creation_month(12) and creation_year(2020) and page(posts/*) and !*/Discussion" show=0 feeds=no reverse=yes]]

Comment moderation
diff --git a/posts/encoding-wifi-access-point-passwords-qr-code/comment_3_4f23e4f1119d36cafcd927b51bbd82ea._comment b/posts/encoding-wifi-access-point-passwords-qr-code/comment_3_4f23e4f1119d36cafcd927b51bbd82ea._comment
new file mode 100644
index 0000000..937cf88
--- /dev/null
+++ b/posts/encoding-wifi-access-point-passwords-qr-code/comment_3_4f23e4f1119d36cafcd927b51bbd82ea._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="RobIII"
+ avatar="http://cdn.libravatar.org/avatar/f4862f3fb4e7d4570395c294e2c73345"
+ subject="@Martin"
+ date="2019-12-30T15:22:57Z"
+ content="""
+If you're using a phone without a camera then there's no point in using a QR code anyway, is it?
+"""]]

Add escaping for semicolons in passwords.
diff --git a/posts/encoding-wifi-access-point-passwords-qr-code.mdwn b/posts/encoding-wifi-access-point-passwords-qr-code.mdwn
index 1339937..bffca9d 100644
--- a/posts/encoding-wifi-access-point-passwords-qr-code.mdwn
+++ b/posts/encoding-wifi-access-point-passwords-qr-code.mdwn
@@ -19,9 +19,17 @@ package, run the following:
 substituting `<SSID>` for the name of your WiFi network and `<PASSWORD>` for
 the 63-character password you hopefully generated with `pwgen -s 63`.
 
-The only pitfall I ran into is that if you include a trailing newline character (for example piping
-`echo "..."` into `qrencode` as opposed to `echo -n "..."`) then it will
-fail on both iOS and Android.
+If your password includes a semicolon, then escape it like this:
+
+    "WIFI:T:WPA;S:<SSID>;P:pass\:word;;"
+
+since iOS won't support the following (which works fine on Android):
+
+    'WIFI:T:WPA;S:<SSID>;P:"pass:word";;'
+
+The only other pitfall I ran into is that if you include a trailing newline
+character (for example piping `echo "..."` into `qrencode` as opposed to
+`echo -n "..."`) then it will fail on both iOS and Android.
 
 ## Scanning the QR code
 
diff --git a/posts/encoding-wifi-access-point-passwords-qr-code/comment_2_c0110bb414c3c1298fb6b3abb6d897eb._comment b/posts/encoding-wifi-access-point-passwords-qr-code/comment_2_c0110bb414c3c1298fb6b3abb6d897eb._comment
deleted file mode 100644
index 62552ac..0000000
--- a/posts/encoding-wifi-access-point-passwords-qr-code/comment_2_c0110bb414c3c1298fb6b3abb6d897eb._comment
+++ /dev/null
@@ -1,8 +0,0 @@
-[[!comment format=mdwn
- ip="2001:44b8:3191:3b00:7dba:a0e1:1b6a:313e"
- claimedauthor="Jeremy"
- subject="Semicolon in SSID or password"
- date="2019-12-30T02:33:57Z"
- content="""
-How does this handle semicolons in the SSID or password?
-"""]]

Comment moderation
diff --git a/posts/encoding-wifi-access-point-passwords-qr-code/comment_3_e3c73a6fa17265f79283284c80a6d9ac._comment b/posts/encoding-wifi-access-point-passwords-qr-code/comment_3_e3c73a6fa17265f79283284c80a6d9ac._comment
new file mode 100644
index 0000000..08c9356
--- /dev/null
+++ b/posts/encoding-wifi-access-point-passwords-qr-code/comment_3_e3c73a6fa17265f79283284c80a6d9ac._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ ip="85.220.211.78"
+ claimedauthor="Martin"
+ subject="63 characters?"
+ date="2019-12-30T12:58:46Z"
+ content="""
+Neither my current mobile device (GPD Pocket running Debian bullseye), nor my future one, the Pyra (there is still some hope), feature a camera. With a password of 63 characters, I hope to type everything correctly! :-)
+"""]]

Comment moderation
diff --git a/posts/encoding-wifi-access-point-passwords-qr-code/comment_2_c0110bb414c3c1298fb6b3abb6d897eb._comment b/posts/encoding-wifi-access-point-passwords-qr-code/comment_2_c0110bb414c3c1298fb6b3abb6d897eb._comment
new file mode 100644
index 0000000..62552ac
--- /dev/null
+++ b/posts/encoding-wifi-access-point-passwords-qr-code/comment_2_c0110bb414c3c1298fb6b3abb6d897eb._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ ip="2001:44b8:3191:3b00:7dba:a0e1:1b6a:313e"
+ claimedauthor="Jeremy"
+ subject="Semicolon in SSID or password"
+ date="2019-12-30T02:33:57Z"
+ content="""
+How does this handle semicolons in the SSID or password?
+"""]]

Add latest post to aggregators
diff --git a/posts/encoding-wifi-access-point-passwords-qr-code.mdwn b/posts/encoding-wifi-access-point-passwords-qr-code.mdwn
index 4e3f515..1339937 100644
--- a/posts/encoding-wifi-access-point-passwords-qr-code.mdwn
+++ b/posts/encoding-wifi-access-point-passwords-qr-code.mdwn
@@ -45,4 +45,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 ios]] [[!tag android]] [[!tag security]] [[!tag debian]] [[!tag nzoss]]

Properly format code block
diff --git a/posts/encoding-wifi-access-point-passwords-qr-code/comment_1_995e478c5b1e90c2a0a00f355f8ac3f7._comment b/posts/encoding-wifi-access-point-passwords-qr-code/comment_1_995e478c5b1e90c2a0a00f355f8ac3f7._comment
index 8337a72..362fe86 100644
--- a/posts/encoding-wifi-access-point-passwords-qr-code/comment_1_995e478c5b1e90c2a0a00f355f8ac3f7._comment
+++ b/posts/encoding-wifi-access-point-passwords-qr-code/comment_1_995e478c5b1e90c2a0a00f355f8ac3f7._comment
@@ -3,8 +3,7 @@
  subject="Script it"
  date="2019-12-29T09:18:22Z"
  content="""
-SSID=SSID_GOES_HERE
-pwgen -s 63 > 00wifi.txt
-qrencode -o 00wifi.png \"WIFI:T:WPA;S:${SSID};P:$(cat 00wifi.txt);;\"
-
+    SSID=SSID_GOES_HERE
+    pwgen -s 63 > 00wifi.txt
+    qrencode -o 00wifi.png \"WIFI:T:WPA;S:${SSID};P:$(cat 00wifi.txt);;\"
 """]]

Comment moderation
diff --git a/posts/encoding-wifi-access-point-passwords-qr-code/comment_1_995e478c5b1e90c2a0a00f355f8ac3f7._comment b/posts/encoding-wifi-access-point-passwords-qr-code/comment_1_995e478c5b1e90c2a0a00f355f8ac3f7._comment
new file mode 100644
index 0000000..8337a72
--- /dev/null
+++ b/posts/encoding-wifi-access-point-passwords-qr-code/comment_1_995e478c5b1e90c2a0a00f355f8ac3f7._comment
@@ -0,0 +1,10 @@
+[[!comment format=mdwn
+ ip="98.210.73.233"
+ subject="Script it"
+ date="2019-12-29T09:18:22Z"
+ content="""
+SSID=SSID_GOES_HERE
+pwgen -s 63 > 00wifi.txt
+qrencode -o 00wifi.png \"WIFI:T:WPA;S:${SSID};P:$(cat 00wifi.txt);;\"
+
+"""]]

Specify the Android version which supports this feature.
diff --git a/posts/encoding-wifi-access-point-passwords-qr-code.mdwn b/posts/encoding-wifi-access-point-passwords-qr-code.mdwn
index 66a1588..4e3f515 100644
--- a/posts/encoding-wifi-access-point-passwords-qr-code.mdwn
+++ b/posts/encoding-wifi-access-point-passwords-qr-code.mdwn
@@ -6,7 +6,7 @@ Up until recently, it was a pain to defend againt [WPA2 brute-force
 attacks](https://en.wikipedia.org/wiki/Wi-Fi_Protected_Access#Weak_password)
 by using a random 63-character password (the maximum in
 [WPA-Personal](https://en.wikipedia.org/wiki/Wi-Fi_Protected_Access#Target_users_(authentication_key_distribution))
-mode). Thanks to Android and now iOS 11 supporting reading WiFi passwords
+mode). Thanks to Android 10 and iOS 11 supporting reading WiFi passwords
 from a QR code, this is finally a practical defense.
 
 ## Generating the QR code

Add WiFi QR code post
diff --git a/posts/encoding-wifi-access-point-passwords-qr-code.mdwn b/posts/encoding-wifi-access-point-passwords-qr-code.mdwn
new file mode 100644
index 0000000..66a1588
--- /dev/null
+++ b/posts/encoding-wifi-access-point-passwords-qr-code.mdwn
@@ -0,0 +1,48 @@
+[[!meta title="Encoding your WiFi access point password into a QR code"]]
+[[!meta date="2019-12-28T19:25:00.000-08:00"]]
+[[!meta license="[Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)"]]
+
+Up until recently, it was a pain to defend againt [WPA2 brute-force
+attacks](https://en.wikipedia.org/wiki/Wi-Fi_Protected_Access#Weak_password)
+by using a random 63-character password (the maximum in
+[WPA-Personal](https://en.wikipedia.org/wiki/Wi-Fi_Protected_Access#Target_users_(authentication_key_distribution))
+mode). Thanks to Android and now iOS 11 supporting reading WiFi passwords
+from a QR code, this is finally a practical defense.
+
+## Generating the QR code
+
+After installing the [`qrencode`](https://packages.debian.org/stable/qrencode)
+package, run the following:
+
+    qrencode -o wifi.png "WIFI:T:WPA;S:<SSID>;P:<PASSWORD>;;"
+
+substituting `<SSID>` for the name of your WiFi network and `<PASSWORD>` for
+the 63-character password you hopefully generated with `pwgen -s 63`.
+
+The only pitfall I ran into is that if you include a trailing newline character (for example piping
+`echo "..."` into `qrencode` as opposed to `echo -n "..."`) then it will
+fail on both iOS and Android.
+
+## Scanning the QR code
+
+On iOS, simply open the camera app and scan the QR code to bring up a
+notification which allows you to connect to the WiFi network:
+
+![](/posts/encoding-wifi-access-point-passwords-qr-code/ios_qr_scanner.png)
+
+On Android, go into the WiFi settings and tap on the WiFi network you want
+to join:
+
+![](/posts/encoding-wifi-access-point-passwords-qr-code/android_wifi_settings.png)
+
+then click the QR icon in the password field and scan the code:
+
+![](/posts/encoding-wifi-access-point-passwords-qr-code/android_qr_scanner.png)
+
+## In-browser alternative
+
+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]]
diff --git a/posts/encoding-wifi-access-point-passwords-qr-code/android_qr_scanner.png b/posts/encoding-wifi-access-point-passwords-qr-code/android_qr_scanner.png
new file mode 100644
index 0000000..49e978d
Binary files /dev/null and b/posts/encoding-wifi-access-point-passwords-qr-code/android_qr_scanner.png differ
diff --git a/posts/encoding-wifi-access-point-passwords-qr-code/android_wifi_settings.png b/posts/encoding-wifi-access-point-passwords-qr-code/android_wifi_settings.png
new file mode 100644
index 0000000..4db83ba
Binary files /dev/null and b/posts/encoding-wifi-access-point-passwords-qr-code/android_wifi_settings.png differ
diff --git a/posts/encoding-wifi-access-point-passwords-qr-code/ios_qr_scanner.png b/posts/encoding-wifi-access-point-passwords-qr-code/ios_qr_scanner.png
new file mode 100644
index 0000000..c69937f
Binary files /dev/null and b/posts/encoding-wifi-access-point-passwords-qr-code/ios_qr_scanner.png differ

Fix IP address thanks to Pedro Bezunartea López.
diff --git a/posts/installing-debian-buster-on-gnubee2.mdwn b/posts/installing-debian-buster-on-gnubee2.mdwn
index 3e80d85..5582f71 100644
--- a/posts/installing-debian-buster-on-gnubee2.mdwn
+++ b/posts/installing-debian-buster-on-gnubee2.mdwn
@@ -22,7 +22,7 @@ On another machine, do the following:
 Then plug a network cable between your laptop and the **black network port**
 and plug the USB stick into the GnuBee before rebooting the GnuBee via ssh:
 
-    ssh 192.68.10.0
+    ssh 192.68.10.1
     reboot
 
 If you have a [USB serial
@@ -43,7 +43,7 @@ Now enable SSH access via the built-in [LibreCMC](https://librecmc.org)
 firmware:
 
 1. Plug a network cable between your laptop and the **black network port**.
-2. Open web-based admin panel at <http://192.168.10.0>.
+2. Open web-based admin panel at <http://192.168.10.1>.
 3. Go to *System | Administration*.
 4. Set a root password.
 5. Disable ssh password auth and root password logins.

Add duplicity tag since I mention ssh config tweak
diff --git a/posts/usual-server-setup.mdwn b/posts/usual-server-setup.mdwn
index 79ca04d..b048270 100644
--- a/posts/usual-server-setup.mdwn
+++ b/posts/usual-server-setup.mdwn
@@ -405,4 +405,4 @@ and the following to improve congestion control and
     net.ipv4.tcp_congestion_control = bbr
     net.ipv4.tcp_notsent_lowat = 16384
 
-[[!tag sysadmin]] [[!tag debian]] [[!tag nzoss]]
+[[!tag sysadmin]] [[!tag debian]] [[!tag nzoss]] [[!tag duplicity]]

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

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

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

Add duplicity S3 post
diff --git a/posts/backing-up-to-s3-with-duplicity.mdwn b/posts/backing-up-to-s3-with-duplicity.mdwn
new file mode 100644
index 0000000..0f698c5
--- /dev/null
+++ b/posts/backing-up-to-s3-with-duplicity.mdwn
@@ -0,0 +1,146 @@
+[[!meta title="Backing up to S3 with Duplicity"]]
+[[!meta date="2019-12-22T12:30:00.000-08:00"]]
+[[!meta license="[Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)"]]
+
+Here is how I setup [duplicity](http://duplicity.nongnu.org/) to use S3 as a
+backend while giving duplicity the minimum set of permissions to my Amazon
+Web Services account.
+
+# AWS Security Settings
+
+First of all, I enabled the following [general security
+settings](https://console.aws.amazon.com/iam/home#/security_credentials) in
+my AWS account:
+
+- MFA with a U2F device
+- no root user access keys
+
+Then I set a **password policy** in the [IAM Account
+Settings](https://console.aws.amazon.com/iam/home#/account_settings) and
+**turned off all public access** in the [S3 Account
+Settings](https://s3.console.aws.amazon.com/s3/settings).
+
+# Creating an S3 bucket
+
+As a destination for the backups, I created a new `backup-foobar` [S3
+bucket](https://s3.console.aws.amazon.com/s3/home) keeping all of the
+default options except for the
+[region](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html#concepts-available-regions)
+which I set to `ca-central-1` to ensure that my data would stay in
+Canada.
+
+The bucket name can be anything you want as long as:
+
+- it's not already taken by another AWS user
+- it's a valid hostname (i.e. alphanumeric characters or dashes)
+
+Note that I did _not_ enable S3 server-side encryption since I will be
+encrypting the backups client-side using the support built into duplicity
+instead.
+
+# Creating a restricted user account
+
+Then I went back into the [Identity and Access Managment
+console](https://console.aws.amazon.com/iam/home) and created a new
+`DuplicityBackup` **policy**:
+
+    {
+        "Version": "2012-10-17",
+        "Statement": [
+            {
+                "Sid": "VisualEditor0",
+                "Effect": "Allow",
+                "Action": [
+                    "s3:PutObject",
+                    "s3:GetObject",
+                    "s3:ListBucketMultipartUploads",
+                    "s3:AbortMultipartUpload",
+                    "s3:CreateBucket",
+                    "s3:ListBucket",
+                    "s3:DeleteObject",
+                    "s3:ListMultipartUploadParts"
+                ],
+                "Resource": [
+                    "arn:aws:s3:::backup-foobar",
+                    "arn:aws:s3:::backup-foobar/*"
+                ]
+            },
+            {
+                "Sid": "VisualEditor1",
+                "Effect": "Allow",
+                "Action": "s3:ListAllMyBuckets",
+                "Resource": "*"
+            }
+        ]
+    }
+
+It's unfortunate that the unrestricted `s3:ListAllMyBuckets` permission has
+to be granted, but in my testing, duplicity would error out without it. No
+other permissions were needed.
+
+The next step was to create a new `DuplicityBackupHosts` IAM **group** to which
+I attached the `DuplicityBackup` policy.
+
+Finally, I created a new `machinename` IAM **user**:
+
+- Access: **programmatic only**
+- Group: `DuplicityBackupHosts`
+- Tags: `duplicity=1`
+
+and wrote down the *access key* and the *access key secret*.
+
+# Duplicity settings
+
+Once that's all set, I was able to use duplicity using the following
+options:
+
+- `--s3-use-new-style`: apparently required on non-US regions
+- `--s3-use-ia`: recommended pricing structure for backups
+- `--s3-use-multiprocessing`: speeds up uploading of backup chunks
+
+and used the following remote URL:
+
+    s3://s3.ca-central-1.amazonaws.com/backup-foobar/machinename
+
+which hardcodes the region in order to [work-around the lack of explicit
+region support in
+duplicity](https://bugs.launchpad.net/duplicity/+bug/1003159/comments/7).
+
+I ended up with the following command:
+
+    http_proxy= AWS_ACCESS_KEY_ID=<access_key> AWS_SECRET_ACCESS_KEY=<access_key_secret> PASSPHRASE=<password> duplicity --s3-use-new-style --s3-use-ia --s3-use-multiprocessing --no-print-statistics --verbosity 1 --exclude-device-files --exclude-filelist <exclude_file> --include-filelist <include_file> --exclude '**' / <remote_url>
+
+where `<exclude_file>` is a file which contains the list of paths to keep
+out of my backup:
+
+    /etc/.git
+    /home/francois/.cache
+
+`<include_file>` is a file which contains the list of paths to include
+in the backup:
+
+    /etc
+    /home/francois
+    /usr/local/bin
+    /usr/local/sbin
+    /var/log/apache2
+    /var/www
+
+and `<password>` is a long random string (`pwgen -s 64`) used to encrypt the backups.
+
+# Backup script
+
+Here are two other things I included in my backup script prior to the actual
+backup line listed in the previous section.
+
+The first one deletes files related to failed backups:
+
+    http_proxy= AWS_ACCESS_KEY_ID=<access_key> AWS_SECRET_ACCESS_KEY=<access_key_secret> PASSPHRASE=<password> duplicity cleanup --verbosity 1 --force <remote_url>
+
+and the second deletes old backups (older than 12 days in this example):
+
+    http_proxy= AWS_ACCESS_KEY_ID=<access_key> AWS_SECRET_ACCESS_KEY=<access_key_secret> PASSPHRASE=<password> duplicity remove-older-than 12D --verbosity 1 --force <remote_url>
+
+Feel free to leave a comment if I forgot anything that might be useful!
+
+[[!tag duplicity]] [[!tag sysadmin]] [[!tag backup]] [[!tag aws]] [[!tag s3]]

Disable compression
diff --git a/posts/using-openvpn-on-android-lollipop.mdwn b/posts/using-openvpn-on-android-lollipop.mdwn
index 631c4c8..1c21b8b 100644
--- a/posts/using-openvpn-on-android-lollipop.mdwn
+++ b/posts/using-openvpn-on-android-lollipop.mdwn
@@ -36,6 +36,7 @@ you'll need to use on your phone:
 
 Basic:
 
+- LZO Compression: `NO`
 - Type: `Certificates`
 - CA Certificate: `ca.crt`
 - Client Certificate: `nexus6.crt`

Turn on Reverse Path Filtering as a mitigation for CVE-2019-14899
diff --git a/posts/creating-a-linode-based-vpn-setup-using_openvpn_on_debian_or_ubuntu.mdwn b/posts/creating-a-linode-based-vpn-setup-using_openvpn_on_debian_or_ubuntu.mdwn
index 3f373f3..9cedb93 100644
--- a/posts/creating-a-linode-based-vpn-setup-using_openvpn_on_debian_or_ubuntu.mdwn
+++ b/posts/creating-a-linode-based-vpn-setup-using_openvpn_on_debian_or_ubuntu.mdwn
@@ -117,6 +117,7 @@ Finally, I added the following to these configuration files:
 * `/etc/sysctl.d/openvpn.conf`:
 
       net.ipv4.ip_forward=1
+      net.ipv4.conf.all.rp_filter=1
 
 * `/etc/rc.local` (just before `exit 0`):
 

Need to reconfigure grub-pc too
diff --git a/posts/replacing-a-failed-raid-drive.mdwn b/posts/replacing-a-failed-raid-drive.mdwn
index 9089c9b..84e27b5 100644
--- a/posts/replacing-a-failed-raid-drive.mdwn
+++ b/posts/replacing-a-failed-raid-drive.mdwn
@@ -78,8 +78,9 @@ to re-create it), I had to update two things:
 
 # Ensuring that I can boot with the replacement drive
 
-In order to be able to boot from both drives, I reinstalled the grub boot
-loader onto the replacement drive:
+In order to be able to boot from both drives, I made sure that the
+replacement drive was included in the list from `dpkg-reconfigure grub-pc`
+and then reinstalled the grub boot loader manually onto it:
 
     grub-install /dev/sdb
 

Create a new "testing" heading for the SMART check
diff --git a/posts/replacing-a-failed-raid-drive.mdwn b/posts/replacing-a-failed-raid-drive.mdwn
index 43cf150..9089c9b 100644
--- a/posts/replacing-a-failed-raid-drive.mdwn
+++ b/posts/replacing-a-failed-raid-drive.mdwn
@@ -100,7 +100,10 @@ that everything is fine:
 
     cat /proc/mdstat
 
-Then I ran a full SMART test over the new replacement drive:
+# Testing the new drive
+
+Finally once everything was configured, I ran a full SMART test over the
+new replacement drive:
 
     smartctl -t long /dev/sdb
 

Use referral link for Brave
diff --git a/posts/passwordless-restricted-guest-account-ubuntu.mdwn b/posts/passwordless-restricted-guest-account-ubuntu.mdwn
index d1bcfe4..5ad5b4a 100644
--- a/posts/passwordless-restricted-guest-account-ubuntu.mdwn
+++ b/posts/passwordless-restricted-guest-account-ubuntu.mdwn
@@ -36,7 +36,7 @@ gnome-control-center. I set the following in the privacy section:
 
 ![](/posts/passwordless-restricted-guest-account-ubuntu/privacy-settings.png)
 
-Then I replaced Firefox with [Brave](https://brave.com) in the sidebar,
+Then I replaced Firefox with [Brave](https://brave.com/clo187) in the sidebar,
 set it as the default browser in gnome-control-center:
 
 ![](/posts/passwordless-restricted-guest-account-ubuntu/default-applications.png)

Adjust image contrast
diff --git a/posts/fixing-turris-omnia-wifi-quality/overall.jpg b/posts/fixing-turris-omnia-wifi-quality/overall.jpg
index b36a840..48dfc06 100644
Binary files a/posts/fixing-turris-omnia-wifi-quality/overall.jpg and b/posts/fixing-turris-omnia-wifi-quality/overall.jpg differ
diff --git a/posts/fixing-turris-omnia-wifi-quality/radios.jpg b/posts/fixing-turris-omnia-wifi-quality/radios.jpg
index dd081f8..ce3f594 100644
Binary files a/posts/fixing-turris-omnia-wifi-quality/radios.jpg and b/posts/fixing-turris-omnia-wifi-quality/radios.jpg differ

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

Fix images and tag
diff --git a/posts/fixing-turris-omnia-wifi-quality.mdwn b/posts/fixing-turris-omnia-wifi-quality.mdwn
index 8a1ea52..c4a9b91 100644
--- a/posts/fixing-turris-omnia-wifi-quality.mdwn
+++ b/posts/fixing-turris-omnia-wifi-quality.mdwn
@@ -33,7 +33,7 @@ around inside the unit, as suggested by the poster.
 After opening the case however, I noticed that radios were already laid out
 in the optimal way:
 
-![](/posts/fixing-turris-omnia-wifi-quality/radios.png)
+![](/posts/fixing-turris-omnia-wifi-quality/radios.jpg)
 
 and that USB3 interference wasn't going to be the reason
 for my troubles.
@@ -46,12 +46,12 @@ radio](https://compex.com.sg/shop/wifi-module/802-11ac-wave-1/wle900vx/)
 radio](https://compex.com.sg/shop/wifi-module/802-11n/wle200n2/) (2.4 GHz
 only) was connected to only 2 of the 3 antennas:
 
-![](/posts/fixing-turris-omnia-wifi-quality/overall.png)
+![](/posts/fixing-turris-omnia-wifi-quality/overall.jpg)
 
 To make it possible for antennas 1 and 3 to carry the signal from both
 radios, a duplexer got inserted between the radios and the antenna:
 
-![](/posts/fixing-turris-omnia-wifi-quality/duplexer.png)
+![](/posts/fixing-turris-omnia-wifi-quality/duplexer.jpg)
 
 On one side is the 2.4 antenna port and on the other side is the 5 GHz port.
 
@@ -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]] [[!turris]]
+[[!tag nzoss]] [[!tag turris]]

Add Turris Omnia article
diff --git a/posts/fixing-turris-omnia-wifi-quality.mdwn b/posts/fixing-turris-omnia-wifi-quality.mdwn
new file mode 100644
index 0000000..8a1ea52
--- /dev/null
+++ b/posts/fixing-turris-omnia-wifi-quality.mdwn
@@ -0,0 +1,77 @@
+[[!meta title="Fixing Turris Omnia WiFi Quality"]]
+[[!meta date="2019-11-24T22:30:00.000-08:00"]]
+[[!meta license="[Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)"]]
+
+I was recently hoping to replace an aging proprietary router (upgraded to a
+[Gargoyle](https://www.gargoyle-router.com/) FOSS firmware). After rejecting
+a [popular brand with a disturbing GPL violation
+habit](https://sfconservancy.org/blog/2019/oct/02/cambium-ubiquiti-gpl-violations/),
+I settled on the [Turris Omnia router](https://www.turris.cz/en/omnia/),
+built on free software. Overall, I was pretty satisfied with the fact that
+it is free and comes with automatic updates, but I noticed a problem with
+the WiFi. Specifically, the 5 GHz access point was okay but the 2.4 GHz was
+awful.
+
+## False lead
+
+I initially thought that the 2.4 GHz radio wasn't working, but then I
+realized that putting my phone next to the router would allow it to connect
+and exchange data at a slow-but-steady rate. If I moved the phone more than
+3-4 meters away though, it would disconnect for lack of signal. To be frank,
+the wireless performance was much worse than my original router, even though
+the wired performance was, as expected, amazing:
+
+![](/posts/fixing-turris-omnia-wifi-quality/fast.png)
+
+I looked on the [official support forums](https://forum.turris.cz/) and found this intriguing
+thread about [interference between USB3 and 2.4 GHz
+radios](https://forum.turris.cz/t/slow-wifi-and-bad-signal-finally-solved/7372).
+This sounded a lot like what I was experiencing (working radio but terrible
+signal/interference) and so I decided to see if I could move the radios
+around inside the unit, as suggested by the poster.
+
+After opening the case however, I noticed that radios were already laid out
+in the optimal way:
+
+![](/posts/fixing-turris-omnia-wifi-quality/radios.png)
+
+and that USB3 interference wasn't going to be the reason
+for my troubles.
+
+## Real problem
+
+So I took a good look at the wiring and found that while the the [larger
+radio](https://compex.com.sg/shop/wifi-module/802-11ac-wave-1/wle900vx/)
+(2.4 / 5 GHz dual-bander) was connected to all three antennas, the [smaller
+radio](https://compex.com.sg/shop/wifi-module/802-11n/wle200n2/) (2.4 GHz
+only) was connected to only 2 of the 3 antennas:
+
+![](/posts/fixing-turris-omnia-wifi-quality/overall.png)
+
+To make it possible for antennas 1 and 3 to carry the signal from both
+radios, a duplexer got inserted between the radios and the antenna:
+
+![](/posts/fixing-turris-omnia-wifi-quality/duplexer.png)
+
+On one side is the 2.4 antenna port and on the other side is the 5 GHz port.
+
+Looking at the wiring though, it became clear that **my 2.4 GHz radio was
+connected to the 5 GHz ports of the two duplexers and the 5 GHz radio was
+connected to the 2.4 GHz ports of the duplexers**. This makes sense
+considering that I had okay 5 GHz performance (with one of the three chains
+connected to the right filter) and abysimal 2.4 GHz performance (with none
+of the two chains connected to the right filter).
+
+## Solution
+
+**Swapping the antenna connectors around completely fixed the problem.**
+With the 2.4 GHz radio connected to the 2.4 side of the duplexer and the
+dual-bander connected to the 5 GHz side, I was able to get the performance I
+would expect from such a high-quality router.
+
+Interestingly enough, I found the solution to this problem the same weekend
+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]] [[!turris]]
diff --git a/posts/fixing-turris-omnia-wifi-quality/duplexer.jpg b/posts/fixing-turris-omnia-wifi-quality/duplexer.jpg
new file mode 100644
index 0000000..7a59d75
Binary files /dev/null and b/posts/fixing-turris-omnia-wifi-quality/duplexer.jpg differ
diff --git a/posts/fixing-turris-omnia-wifi-quality/fast.png b/posts/fixing-turris-omnia-wifi-quality/fast.png
new file mode 100644
index 0000000..ebe8d6e
Binary files /dev/null and b/posts/fixing-turris-omnia-wifi-quality/fast.png differ
diff --git a/posts/fixing-turris-omnia-wifi-quality/overall.jpg b/posts/fixing-turris-omnia-wifi-quality/overall.jpg
new file mode 100644
index 0000000..b36a840
Binary files /dev/null and b/posts/fixing-turris-omnia-wifi-quality/overall.jpg differ
diff --git a/posts/fixing-turris-omnia-wifi-quality/radios.jpg b/posts/fixing-turris-omnia-wifi-quality/radios.jpg
new file mode 100644
index 0000000..dd081f8
Binary files /dev/null and b/posts/fixing-turris-omnia-wifi-quality/radios.jpg differ

Comment moderation
diff --git a/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_6_cc0e9e44b01885a989b41cbb417b0032._comment b/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_6_cc0e9e44b01885a989b41cbb417b0032._comment
new file mode 100644
index 0000000..6a89be9
--- /dev/null
+++ b/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_6_cc0e9e44b01885a989b41cbb417b0032._comment
@@ -0,0 +1,9 @@
+[[!comment format=mdwn
+ ip="82.6.2.26"
+ claimedauthor="Marcin"
+ url="mr-coffee.io"
+ subject="Saved my life"
+ date="2019-11-24T20:40:41Z"
+ content="""
+Very helpful, thanks a lot for that post! As a quick note, this also works for the Virtualbox related installation. Ony difference would be to mount the LiveCD in the Settings > Storage > Controller: IDE and start the VM. The other steps work as a charm!
+"""]]

Comment moderation
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
new file mode 100644
index 0000000..70b0845
--- /dev/null
+++ b/posts/recovering-from-unbootable-ubuntu-encrypted-lvm-root-partition/comment_5_0367ad2561d124b47e307502f1b85a96._comment
@@ -0,0 +1,19 @@
+[[!comment format=mdwn
+ ip="2001:14ba:3f0:f600:7107:33f1:dce3:43f5"
+ claimedauthor="linux-girl"
+ 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.
+
+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.
+
+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!
+"""]]

Move dunst to the first section since it's started by startup script
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 279b9fc..9ebdaa7 100644
--- a/posts/creating-a-modern-tiling-desktop-environment-using-i3.mdwn
+++ b/posts/creating-a-modern-tiling-desktop-environment-using-i3.mdwn
@@ -10,6 +10,7 @@ After experimenting with [awesome](http://awesome.naquadah.org/) for 2 years and
 
 As soon as I log into my desktop, my [startup script](https://github.com/fmarier/user-scripts/blob/master/startup) starts a few programs, including:
 
+* [dunst package](https://packages.debian.org/stable/dunst): displays desktop notifications
 * [gnome-settings-daemon](https://packages.debian.org/stable/gnome-settings-daemon): makes GTK applications look nice by applying my preferred theme amongst other things
 * [gnome-keyring-daemon](https://packages.debian.org/stable/gnome-keyring): remembers ssh public keys for the duration of my session
 * [i3lock](https://packages.debian.org/stable/i3lock): locks the screen when I'm not around
@@ -22,15 +23,11 @@ Because of [a bug in gnome-settings-daemon](https://ask.fedoraproject.org/en/que
 
 # Notifications
 
-I use the [dunst package](https://packages.debian.org/stable/dunst) to receive desktop notifications:
-
-    apt install dunst
-
 You will probably also want to set the following in `/etc/xdg/dunst/dunstrc` to ensure that notifications use your default web browser:
 
     browser = /usr/bin/sensible-browser
 
-Here are the keyboard shortcuts you'll need to interact with the notifications that popup:
+Here are the keyboard shortcuts you'll need to interact with the notifications that pop up:
 
 - `Ctrl-Space` to close the current notification
 - `Ctrl-Shift-Space` to close all notifications

Switch from gnome-screensaver to i3lock
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 103ae19..279b9fc 100644
--- a/posts/creating-a-modern-tiling-desktop-environment-using-i3.mdwn
+++ b/posts/creating-a-modern-tiling-desktop-environment-using-i3.mdwn
@@ -12,7 +12,7 @@ As soon as I log into my desktop, my [startup script](https://github.com/fmarier
 
 * [gnome-settings-daemon](https://packages.debian.org/stable/gnome-settings-daemon): makes GTK applications look nice by applying my preferred theme amongst other things
 * [gnome-keyring-daemon](https://packages.debian.org/stable/gnome-keyring): remembers ssh public keys for the duration of my session
-* [gnome-screensaver](https://packages.debian.org/stable/gnome-screensaver): locks the screen when I'm not around
+* [i3lock](https://packages.debian.org/stable/i3lock): locks the screen when I'm not around
 * [nm-applet](https://packages.debian.org/stable/network-manager-gnome): handles wifi and VPN connections
 * [syncthing](https://www.syncthing.net/): keeps my folders synchronized between machines
 
@@ -39,11 +39,9 @@ Here are the keyboard shortcuts you'll need to interact with the notifications t
 
 # Screensaver
 
-In addition, gnome-screensaver didn't automatically lock my screen, so I installed [xautolock](https://packages.debian.org/stable/xautolock) and added it to my startup script:
+To make i3lock automatically lock my screen, I installed [xautolock](https://packages.debian.org/stable/xautolock) and added it to my startup script:
 
-    xautolock -time 30 -locker "gnome-screensaver-command --lock" &
-
-to lock the screen using gnome-screensaver after 30 minutes of inactivity.
+    xautolock -time 30 -locker "i3lock -c 000000 -f" &
 
 I can also trigger it manually using the following shortcut defined in my `~/.i3/config`:
 

Fix broken escaping
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 6980966..103ae19 100644
--- a/posts/creating-a-modern-tiling-desktop-environment-using-i3.mdwn
+++ b/posts/creating-a-modern-tiling-desktop-environment-using-i3.mdwn
@@ -34,7 +34,7 @@ Here are the keyboard shortcuts you'll need to interact with the notifications t
 
 - `Ctrl-Space` to close the current notification
 - `Ctrl-Shift-Space` to close all notifications
-- `Ctrl-\`` to show the last notification
+- `Ctrl-<backtick>` to show the last notification
 - `Ctrl-Shift-.` to show the context menu for the current notification
 
 # Screensaver

I do actually start dunst from my startup script
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 e3a00d7..6980966 100644
--- a/posts/creating-a-modern-tiling-desktop-environment-using-i3.mdwn
+++ b/posts/creating-a-modern-tiling-desktop-environment-using-i3.mdwn
@@ -22,8 +22,7 @@ Because of [a bug in gnome-settings-daemon](https://ask.fedoraproject.org/en/que
 
 # Notifications
 
-While my startup script doesn't run this tool directly, installing the
-[dunst package](https://packages.debian.org/stable/dunst) is required to receive desktop notifications:
+I use the [dunst package](https://packages.debian.org/stable/dunst) to receive desktop notifications:
 
     apt install dunst
 

Add tags to my SurveyMonkey post
diff --git a/posts/restricting-third-party-iframes-sandbox-referrer-feature-policy.mdwn b/posts/restricting-third-party-iframes-sandbox-referrer-feature-policy.mdwn
index a533e4d..bb08db9 100644
--- a/posts/restricting-third-party-iframes-sandbox-referrer-feature-policy.mdwn
+++ b/posts/restricting-third-party-iframes-sandbox-referrer-feature-policy.mdwn
@@ -195,3 +195,5 @@ benefits](https://csp-evaluator.withgoogle.com/).
 
 Embedded enforcement is thefore not a usable security control in this
 particular example until SurveyMonkey gets a stricter CSP policy.
+
+[[!tag web]] [[!tag security]] [[!tag csp]] [[!tag mozilla]]

Add SurveyMonkey blog post
diff --git a/posts/restricting-third-party-iframes-sandbox-referrer-feature-policy.mdwn b/posts/restricting-third-party-iframes-sandbox-referrer-feature-policy.mdwn
new file mode 100644
index 0000000..a533e4d
--- /dev/null
+++ b/posts/restricting-third-party-iframes-sandbox-referrer-feature-policy.mdwn
@@ -0,0 +1,197 @@
+[[!meta title="Restricting third-party iframe widgets using the sandbox attribute, referrer policy and feature policy"]]
+[[!meta date="2019-09-20T20:15:00.000-07:00"]]
+[[!meta license="[Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)"]]
+
+Adding third-party embedded widgets on a website is a common but potentially
+dangerous practice. Thankfully, the web platform offers a few controls that can help
+mitigate the risks. While this post uses the example of an embedded
+SurveyMonkey survey, the principles can be used for all kinds of other widgets.
+
+Note that this is by no means an endorsement of SurveyMonkey's proprietary
+service. If you are looking for a survey product, you should consider a free
+and open source alternative like [LimeSurvey](https://www.limesurvey.org/).
+
+# SurveyMonkey's snippet
+
+In order to embed a [survey on your
+website](https://www.surveymonkey.com/curiosity/how-to-embed-your-survey-on-a-website/),
+the SurveyMonkey interface will tell you to install the following [website
+collector](https://help.surveymonkey.com/articles/en_US/kb/Website-Collector) script:
+
+    <script>(function(t,e,s,n){var
+    o,a,c;t.SMCX=t.SMCX||[],e.getElementById(n)||(o=e.getElementsByTagName(s),a=o[o.length-1],c=e.createElement(s),c.type="text/javascript",c.async=!0,c.id=n,c.src=["https:"===location.protocol?"https://":"http://","widget.surveymonkey.com/collect/website/js/tRaiETqnLgj758hTBazgd9NxKf_2BhnTfDFrN34n_2BjT1Kk0sqrObugJL8ZXdb_2BaREa.js"].join(""),a.parentNode.insertBefore(c,a))})(window,document,"script","smcx-sdk");</script><a
+    style="font: 12px Helvetica, sans-serif; color: #999; text-decoration:
+    none;" href=https://www.surveymonkey.com> Create your own user feedback
+    survey </a>
+
+which can be rewritten in a more understandable form as:
+
+    (
+    function (s) {
+      var scripts, last_script, new_script;
+      window.SMCX = window.SMCX || [],
+      document.getElementById("smcx-sdk") ||
+        (
+          scripts = document.getElementsByTagName("script"),
+          last_script = scripts[scripts.length - 1],
+          new_script = document.createElement("script"),
+          new_script.type = "text/javascript",
+          new_script.async = true,
+          new_script.id = "smcx-sdk",
+          new_script.src =
+            [
+              "https:" === location.protocol ? "https://" : "http://",
+              "widget.surveymonkey.com/collect/website/js/tRaiETqnLgj758hTBazgd9NxKf_2BhnTfDFrN34n_2BjT1Kk0sqrObugJL8ZXdb_2BaREa.js"
+            ].join(""),
+          last_script.parentNode.insertBefore(new_script, last_script)
+        )
+      }
+    )();
+
+The fact that this adds a third-party script dependency to your website is
+problematic because it means that a security vulnerability in their
+infrastructure could lead to a complete compromise of your site, thanks to
+third-party scripts having full control over your website. Security issues
+aside though, this could also enable this third-party to violate your users'
+privacy expectations and extract any information displayed on your site for
+marketing purposes.
+
+However, if you embed the snippet on a test page and inspect it with the
+developer tools, you will find that it actually creates an
+[iframe](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe):
+
+    <iframe
+        width="500"
+        height="500"
+        frameborder="0"
+        allowtransparency="true"
+        src="https://www.surveymonkey.com/r/D3KDY6R?embedded=1"
+    ></iframe>
+
+and you can use that directly on your site without having to load their
+script.
+
+## Mixed content anti-pattern
+
+As an aside, the script snippet they propose makes use of a common front-end
+anti-pattern:
+
+    "https:"===location.protocol?"https://":"http://"
+
+This is presumably meant to avoid inserting an HTTP script element into an
+HTTPS page, since that would be considered [mixed
+content](https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content)
+and get blocked by browsers, however this is entirely unnecessary. One
+should only ever use the HTTPS version of such scripts anyways since an HTTP
+page [never prohibits embedding HTTPS
+content](https://www.w3.org/TR/mixed-content/#categorize-settings-object).
+
+In other words, the above code snippet can be simplified to:
+
+    "https://"
+
+# Restricting iframes
+
+Thanks to defenses which have been added to the web platform recently, there
+are a few things that can be done to constrain iframes.
+
+Firstly, you can choose to hide your full page URL from SurveyMonkey using
+the [referrer
+policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy):
+
+    referrerpolicy="strict-origin"
+
+This mean seem harmless, but page URLs sometimes include sensitive
+information in the URL path or query string, for example, search terms that
+a user might have typed. The `strict-origin` policy will limit the referrer
+to your site's hostname, port and protocol.
+
+Secondly, you can prevent the iframe from being able to access anything
+about its embedding page or to trigger popups and unwanted downloads using
+the [sandbox
+attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#attr-sandbox):
+
+    sandbox="allow-scripts allow-forms"
+
+Ideally, the contents of this attribute would be empty so that all
+restrictions would be active, but SurveyMonkey is a JavaScript application
+and it of course needs to submit a form since that's the purpose of the widget.
+
+Finally, a new experimental capability is making its way into browsers:
+[feature policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Feature_Policy).
+In the context of untrusted iframes, it enables developers to explicitly
+disable [certain powerful
+features](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Feature-Policy#Directives):
+
+    allow="accelerometer 'none';
+           ambient-light-sensor 'none';
+           camera 'none';
+           display-capture 'none';
+           document-domain 'none';
+           fullscreen 'none';
+           geolocation 'none';
+           gyroscope 'none';
+           magnetometer 'none';
+           microphone 'none';
+           midi 'none';
+           payment 'none';
+           usb 'none';
+           vibrate 'none';
+           vr 'none';
+           webauthn 'none'"
+
+Putting it all together, we end up with the following HTML snippet:
+
+    <iframe
+        width="500"
+        height="500"
+        frameborder="0"
+        allowtransparency="true"
+        allow="accelerometer 'none'; ambient-light-sensor 'none';
+               camera 'none'; display-capture 'none';
+               document-domain 'none'; fullscreen 'none';
+               geolocation 'none'; gyroscope 'none'; magnetometer 'none';
+               microphone 'none'; midi 'none'; payment 'none'; usb 'none';
+               vibrate 'none'; vr 'none'; webauthn 'none'"
+        sandbox="allow-scripts allow-forms"
+        referrerpolicy="strict-origin"
+        src="https://www.surveymonkey.com/r/D3KDY6R?embedded=1"
+    ></iframe>
+
+# Content Security Policy
+
+Another advantage of using the iframe directly is that instead of loosening
+your site's [Content Security
+Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) by adding all
+of the following:
+
+* `script-src https://www.surveymonkey.com`
+* `img-src https://www.surveymonkey.com`
+* `frame-src  https://www.surveymonkey.com`
+
+you can limit the extra directives to just the frame controls:
+
+* `frame-src  https://www.surveymonkey.com`
+
+[CSP Embedded Enforcement](https://w3c.github.io/webappsec-cspee/) would be
+another nice mechanism to make use of, but looking at SurveyMonkey's CSP
+policy:
+
+    Content-Security-Policy:
+      default-src https: data: blob: 'unsafe-eval' 'unsafe-inline'
+          wss://*.hotjar.com 'self';
+      img-src https: http: data: blob: 'self';
+      script-src https: 'unsafe-eval' 'unsafe-inline' http://www.google-analytics.com http://ajax.googleapis.com
+          http://bat.bing.com http://static.hotjar.com http://www.googleadservices.com
+          'self';
+      style-src https: 'unsafe-inline' http://secure.surveymonkey.com 'self';
+      report-uri https://csp.surveymonkey.com/report?e=true&c=prod&a=responseweb
+
+it allows the injection of arbitrary Flash files, [inline
+scripts](https://developers.google.com/web/fundamentals/security/csp/#inline_code_is_considered_harmful),
+evals and any other scripts hosted on an HTTPS URL, which means that it
+[doesn't really provide any meaningful security
+benefits](https://csp-evaluator.withgoogle.com/).

(Diff truncated)
Comment moderation
diff --git a/posts/installing-debian-buster-on-gnubee2/comment_2_bc2dba3b221d2d92ae9c306d1be4fc0d._comment b/posts/installing-debian-buster-on-gnubee2/comment_2_bc2dba3b221d2d92ae9c306d1be4fc0d._comment
new file mode 100644
index 0000000..1ea2359
--- /dev/null
+++ b/posts/installing-debian-buster-on-gnubee2/comment_2_bc2dba3b221d2d92ae9c306d1be4fc0d._comment
@@ -0,0 +1,9 @@
+[[!comment format=mdwn
+ ip="165.225.114.98"
+ claimedauthor="Antoine"
+ subject="Same issue with SSH"
+ date="2019-09-09T08:49:57Z"
+ content="""
+Hi, 
+Just stumbled onto this post. I have the same issue with SSH, on a GnuBee PC1. I believe openssh is running (according to OpenMediaVault GUI), but it rejects all connections attempts. Please keep updating this post if you find a solution.
+"""]]
diff --git a/posts/installing-debian-buster-on-gnubee2/comment_3_5d038744e65cf79c39b9fd03b937c812._comment b/posts/installing-debian-buster-on-gnubee2/comment_3_5d038744e65cf79c39b9fd03b937c812._comment
new file mode 100644
index 0000000..7d29cdc
--- /dev/null
+++ b/posts/installing-debian-buster-on-gnubee2/comment_3_5d038744e65cf79c39b9fd03b937c812._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ ip="188.192.119.43"
+ claimedauthor="Hein Osenberg"
+ subject="Works for me"
+ date="2019-09-02T19:21:34Z"
+ content="""
+With Neil Browns new kernel (5.2.8 - see http://neil.brown.name/gnubee/), installed (see README) upgrading to Debian Buster worked perfectly for me. SSH access works out of the box without problems. 
+"""]]
diff --git a/posts/programming-anytone-d878uv-on-linux-using-windows10-and-virtualbox/comment_3_2ce1f54d56c51d14432525478fb3224c._comment b/posts/programming-anytone-d878uv-on-linux-using-windows10-and-virtualbox/comment_3_2ce1f54d56c51d14432525478fb3224c._comment
new file mode 100644
index 0000000..5d28a0d
--- /dev/null
+++ b/posts/programming-anytone-d878uv-on-linux-using-windows10-and-virtualbox/comment_3_2ce1f54d56c51d14432525478fb3224c._comment
@@ -0,0 +1,10 @@
+[[!comment format=mdwn
+ username="francois@665656f0ba400877c9b12e8fbb086e45aa01f7c0"
+ nickname="francois"
+ subject="Re: programming Anytone 878"
+ date="2019-09-02T21:11:37Z"
+ content="""
+> In the instruction you say turn on the radio. How do you do that if it is locked and can be only unlocked by programing?
+
+The radio being factory-locked shouldn't prevent you from turning it on using the right knob. If you're talking about the keypad being locked, then yes that's one of the things that can be [unlocked using the CPS](https://www.youtube.com/watch?v=dzFqEM-yC7A).
+"""]]

Restore accidentally deleted comment
diff --git a/posts/installing-debian-buster-on-gnubee2/comment_1_2a3c537445b6d27e05446e43471ddc81._comment b/posts/installing-debian-buster-on-gnubee2/comment_1_2a3c537445b6d27e05446e43471ddc81._comment
new file mode 100644
index 0000000..83f1c74
--- /dev/null
+++ b/posts/installing-debian-buster-on-gnubee2/comment_1_2a3c537445b6d27e05446e43471ddc81._comment
@@ -0,0 +1,9 @@
+[[!comment format=mdwn
+ ip="188.192.119.43"
+ claimedauthor="Hein Osenberg"
+ subject="Works for me"
+ date="2019-09-02T19:21:34Z"
+ content="""
+With Neil Browns new kernel (5.2.8 - see <http://neil.brown.name/gnubee/>), installed (see README) upgrading to Debian Buster worked perfectly for me. SSH access works
+out of the box without problems.
+"""]]

Comment moderation
diff --git a/posts/poor-mans-raid1-between-ssd-and-hard-drive/comment_5_8e548908487f2d40ea9285800441daf1._comment b/posts/poor-mans-raid1-between-ssd-and-hard-drive/comment_5_8e548908487f2d40ea9285800441daf1._comment
new file mode 100644
index 0000000..6794a3b
--- /dev/null
+++ b/posts/poor-mans-raid1-between-ssd-and-hard-drive/comment_5_8e548908487f2d40ea9285800441daf1._comment
@@ -0,0 +1,17 @@
+[[!comment format=mdwn
+ ip="96.67.192.225"
+ claimedauthor="Alexis Wilke"
+ url="https://linux.m2osw.com/"
+ subject="Your title said RAID1..."
+ date="2019-09-01T23:03:01Z"
+ content="""
+I totally agree that a RAID1 would kill the speed of SSD. But then, your post title says RAID1... :-)
+
+Now, I wanted to mention that if you know the name of your device, you can check whether it's a rotational (HDD) or not (SSD). Here is an example of how you get the flag:
+
+alexis~$ cat /sys/block/sda/queue/rotational
+
+The file is either a 0 (SSD) or a 1 (HDD). What you need is the \"sda\" part of the path. In a shell script, it can be difficult to grab that info. Although a function such as \"df .\" give you the info, it's not that practical.
+
+Anyway, your solution is pretty much what I had in mind. Thank you.
+"""]]
diff --git a/posts/programming-anytone-d878uv-on-linux-using-windows10-and-virtualbox/comment_1_d8c8da58ae4ccdd77c13f738b2606fbd._comment b/posts/programming-anytone-d878uv-on-linux-using-windows10-and-virtualbox/comment_1_d8c8da58ae4ccdd77c13f738b2606fbd._comment
new file mode 100644
index 0000000..b950695
--- /dev/null
+++ b/posts/programming-anytone-d878uv-on-linux-using-windows10-and-virtualbox/comment_1_d8c8da58ae4ccdd77c13f738b2606fbd._comment
@@ -0,0 +1,9 @@
+[[!comment format=mdwn
+ ip="2605:6000:6fc0:52:38c2:3e01:8b8a:e440"
+ claimedauthor="William Hennessy"
+ url="bhennessy2@satx.rr.com"
+ subject="programming Anytone 878"
+ date="2019-09-01T16:06:47Z"
+ content="""
+In the instruction you say turn on the radio.  How do you do that if it is locked and can be only unlocked by programing?
+"""]]
diff --git a/posts/programming-anytone-d878uv-on-linux-using-windows10-and-virtualbox/comment_2_63881d9f9a28457c788c2f3a32b989af._comment b/posts/programming-anytone-d878uv-on-linux-using-windows10-and-virtualbox/comment_2_63881d9f9a28457c788c2f3a32b989af._comment
new file mode 100644
index 0000000..79d6064
--- /dev/null
+++ b/posts/programming-anytone-d878uv-on-linux-using-windows10-and-virtualbox/comment_2_63881d9f9a28457c788c2f3a32b989af._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ ip="2601:280:4100:8254:9a3b:8fff:fec1:f25a"
+ claimedauthor="Chris Keller, K0SWE"
+ subject="Finally, USB works!"
+ date="2019-08-09T11:37:29Z"
+ content="""
+Thanks for writing this down! I've tried a Virtualbox VM several times for programming radios but could never get USB devices to be visible inside the VM. Turns out, I was missing the `vboxusers` group. This should open up a lot of possibilities!
+"""]]

Automatically scale large images
diff --git a/local.css b/local.css
index 068fd89..3f1933d 100644
--- a/local.css
+++ b/local.css
@@ -1,4 +1,9 @@
 /* ikiwiki local style sheet */
+img {
+    max-width: 100%;
+    height: auto;
+}
+
 .blogform, .trail, .inlinefooter .pagelicense, .inlinefooter .tags, .inlinefooter .actions {
     display: none;
 }

Add my restricted passwordless guest account
diff --git a/posts/passwordless-restricted-guest-account-ubuntu.mdwn b/posts/passwordless-restricted-guest-account-ubuntu.mdwn
new file mode 100644
index 0000000..d1bcfe4
--- /dev/null
+++ b/posts/passwordless-restricted-guest-account-ubuntu.mdwn
@@ -0,0 +1,85 @@
+[[!meta title="Passwordless restricted guest account on Ubuntu"]]
+[[!meta date="2019-08-15T20:10:00.000-07:00"]]
+[[!meta license="[Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)"]]
+
+Here's how I created a restricted but *not ephemeral* guest account on an
+Ubuntu 18.04 desktop computer that can be used without a password.
+
+## Create a user that can login without a password
+
+First of all, I created a new user with a random password (using `pwgen -s 64`):
+
+    adduser guest
+
+Then following [these
+instructions](http://ubuntuhandbook.org/index.php/2019/02/enable-passwordless-login-ubuntu-18-04/),
+I created a new group and added the user to it:
+
+    addgroup nopasswdlogin
+    adduser guest nopasswdlogin
+
+In order to let that user login using
+[GDM](https://wiki.gnome.org/Projects/GDM) without a password, I added the
+following to the top of `/etc/pam.d/gdm-password`:
+
+    auth    sufficient      pam_succeed_if.so user ingroup nopasswdlogin
+
+Note that this user is unable to ssh into this machine since it's not part
+of the [`sshuser` group I have setup in my sshd
+configuration](https://feeding.cloud.geek.nz/posts/hardening-ssh-servers/#Whitelist_approach_to_giving_users_ssh_access).
+
+## Privacy settings
+
+In order to reduce the amount of digital traces left between guest sessions,
+I logged into the account using a GNOME session and then opened
+gnome-control-center. I set the following in the privacy section:
+
+![](/posts/passwordless-restricted-guest-account-ubuntu/privacy-settings.png)
+
+Then I replaced Firefox with [Brave](https://brave.com) in the sidebar,
+set it as the default browser in gnome-control-center:
+
+![](/posts/passwordless-restricted-guest-account-ubuntu/default-applications.png)
+
+and configured it to clear everything on exit:
+
+![](/posts/passwordless-restricted-guest-account-ubuntu/brave-clear-on-exit.png)
+
+## Create a password-less system keyring
+
+In order to suppress [prompts to unlock
+gnome-keyring](https://askubuntu.com/questions/867/how-can-i-stop-being-prompted-to-unlock-the-default-keyring-on-boot),
+I opened [seahorse](https://wiki.gnome.org/Apps/Seahorse) and deleted the
+default keyring.
+
+Then I started Brave, which prompted me to create a new keyring so that it
+can save the contents of its password manager securely. I set an **empty
+password** on that new keyring, since I'm not going to be using it.
+
+I also made sure to disable saving of passwords, payment methods and
+addresses in the browser too.
+
+![](/posts/passwordless-restricted-guest-account-ubuntu/brave-passwords.png)
+
+![](/posts/passwordless-restricted-guest-account-ubuntu/brave-payments.png)
+
+![](/posts/passwordless-restricted-guest-account-ubuntu/brave-addresses.png)
+
+## Restrict user account further
+
+Finally, taking an idea from this [similar
+solution](https://askubuntu.com/a/19696/8368), I prevented the user from
+making any system-wide changes by putting the following in
+`/etc/polkit-1/localauthority/50-local.d/10-guest-policy.pkla`:
+
+    [guest-policy]
+    Identity=unix-user:guest
+    Action=*
+    ResultAny=no
+    ResultInactive=no
+    ResultActive=no
+
+If you know of any other restrictions that could be added, please leave a
+comment!
+
+[[!tag ubuntu]] [[!tag debian]] [[!tag nzoss]] [[!tag brave]]
diff --git a/posts/passwordless-restricted-guest-account-ubuntu/brave-addresses.png b/posts/passwordless-restricted-guest-account-ubuntu/brave-addresses.png
new file mode 100644
index 0000000..6911611
Binary files /dev/null and b/posts/passwordless-restricted-guest-account-ubuntu/brave-addresses.png differ
diff --git a/posts/passwordless-restricted-guest-account-ubuntu/brave-clear-on-exit.png b/posts/passwordless-restricted-guest-account-ubuntu/brave-clear-on-exit.png
new file mode 100644
index 0000000..e60c47a
Binary files /dev/null and b/posts/passwordless-restricted-guest-account-ubuntu/brave-clear-on-exit.png differ
diff --git a/posts/passwordless-restricted-guest-account-ubuntu/brave-passwords.png b/posts/passwordless-restricted-guest-account-ubuntu/brave-passwords.png
new file mode 100644
index 0000000..e8795b3
Binary files /dev/null and b/posts/passwordless-restricted-guest-account-ubuntu/brave-passwords.png differ
diff --git a/posts/passwordless-restricted-guest-account-ubuntu/brave-payments.png b/posts/passwordless-restricted-guest-account-ubuntu/brave-payments.png
new file mode 100644
index 0000000..eb2cdab
Binary files /dev/null and b/posts/passwordless-restricted-guest-account-ubuntu/brave-payments.png differ
diff --git a/posts/passwordless-restricted-guest-account-ubuntu/default-applications.png b/posts/passwordless-restricted-guest-account-ubuntu/default-applications.png
new file mode 100644
index 0000000..72e5844
Binary files /dev/null and b/posts/passwordless-restricted-guest-account-ubuntu/default-applications.png differ
diff --git a/posts/passwordless-restricted-guest-account-ubuntu/privacy-settings.png b/posts/passwordless-restricted-guest-account-ubuntu/privacy-settings.png
new file mode 100644
index 0000000..a749079
Binary files /dev/null and b/posts/passwordless-restricted-guest-account-ubuntu/privacy-settings.png differ

Prune old stale files
diff --git a/posts/seeding-brave-browser-sccache.mdwn b/posts/seeding-brave-browser-sccache.mdwn
index d3398a5..423f702 100644
--- a/posts/seeding-brave-browser-sccache.mdwn
+++ b/posts/seeding-brave-browser-sccache.mdwn
@@ -45,6 +45,8 @@ and here are the contents of that script:
     git fetch --prune origin
     git pull
     npm install
+    rm -rf src/brave/*
+    gclient sync -D
     npm run init
     
     echo $(date)

Bump the sccache cache size to match ccache
diff --git a/posts/seeding-brave-browser-sccache.mdwn b/posts/seeding-brave-browser-sccache.mdwn
index 0936c99..d3398a5 100644
--- a/posts/seeding-brave-browser-sccache.mdwn
+++ b/posts/seeding-brave-browser-sccache.mdwn
@@ -72,7 +72,7 @@ It references a `~/.bashrc-brave` file which contains:
     #!/bin/sh
     export PATH="${PATH}:${HOME}/bin:${HOME}/devel/brave-browser/vendor/depot_tools:${HOME}/.cargo/bin"
     export SCCACHE_DIR="${HOME}/.cache/sccache"
-    export SCCACHE_CACHE_SIZE=100G
+    export SCCACHE_CACHE_SIZE=200G
     export NO_AUTH_BOTO_CONFIG="${HOME}/.boto"
 
 ## ccache instead of sccache

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

Switch to ccache
diff --git a/posts/seeding-brave-browser-sccache.mdwn b/posts/seeding-brave-browser-sccache.mdwn
index b4b8a8e..0936c99 100644
--- a/posts/seeding-brave-browser-sccache.mdwn
+++ b/posts/seeding-brave-browser-sccache.mdwn
@@ -75,4 +75,27 @@ It references a `~/.bashrc-brave` file which contains:
     export SCCACHE_CACHE_SIZE=100G
     export NO_AUTH_BOTO_CONFIG="${HOME}/.boto"
 
-[[!tag brave]] [[!tag sccache]]
+## ccache instead of sccache
+
+While I started using sccache for compiling Brave, I recently switched to
+[ccache](https://ccache.dev) as sccache turned out to be fairly
+[unreliable](https://github.com/brave/brave-browser/wiki/sccache-for-faster-builds#troubleshooting-the-install)
+at compiling Chromium.
+
+Switching to `ccache` is easy, simply install the package:
+
+    apt install ccache
+
+and then set the [environment
+variable](https://github.com/brave/brave-browser/wiki/sccache-for-faster-builds#setting-the-environment-variable)
+in `.npmrc`:
+
+    sccache = ccache
+
+Finally, you'll probably want to increase the maximum cache size:
+
+    ccache --max-size=200G
+
+in order to fit all of Chromium/Brave in the cache.
+
+[[!tag brave]] [[!tag sccache]] [[!tag ccache]]

Fix two more possible update/build errors
diff --git a/posts/seeding-brave-browser-sccache.mdwn b/posts/seeding-brave-browser-sccache.mdwn
index 4a11ece..b4b8a8e 100644
--- a/posts/seeding-brave-browser-sccache.mdwn
+++ b/posts/seeding-brave-browser-sccache.mdwn
@@ -38,9 +38,11 @@ and here are the contents of that script:
     rm -rf src/out node_modules src/brave/node_modules
     git clean -f -d
     git checkout HEAD package-lock.json
+    find -name "*.pyc" -delete
     
     echo $(date)
     echo "=> Update repo"
+    git fetch --prune origin
     git pull
     npm install
     npm run init

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

Add GnuBee Debian installation guide
diff --git a/posts/installing-debian-buster-on-gnubee2.mdwn b/posts/installing-debian-buster-on-gnubee2.mdwn
new file mode 100644
index 0000000..3e80d85
--- /dev/null
+++ b/posts/installing-debian-buster-on-gnubee2.mdwn
@@ -0,0 +1,192 @@
+[[!meta title="Installing Debian buster on a GnuBee PC 2"]]
+[[!meta date="2019-07-14T15: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 how I installed [Debian 10 /
+buster](https://www.debian.org/releases/buster/) on my [GnuBee Personal
+Cloud 2](http://gnubee.org/), a free hardware device designed as a network
+file server / [NAS](https://en.wikipedia.org/wiki/Network-attached_storage).
+
+## Flashing the LibreCMC firmware with Debian support
+
+Before we can install Debian, we need a firmware that includes all of the
+necessary tools.
+
+On another machine, do the following:
+
+1. Download the [latest `librecmc-ramips-mt7621-gb-pc1-squashfs-sysupgrade_*.bin`](https://github.com/gnubee-git/gnubee-git.github.io/tree/master/debian).
+2. Mount a **vfat**-formatted USB stick.
+3. Copy the file onto it and rename it to `gnubee.bin`.
+4. Unmount the USB stick
+
+Then plug a network cable between your laptop and the **black network port**
+and plug the USB stick into the GnuBee before rebooting the GnuBee via ssh:
+
+    ssh 192.68.10.0
+    reboot
+
+If you have a [USB serial
+cable](https://github.com/gnubee-git/GnuBee_Docs/blob/master/USB_to_UART/README.md),
+you can use it to monitor the flashing process:
+
+    screen /dev/ttyUSB0 57600
+
+otherwise keep an eye on the [LEDs and wait until they are fully done
+flashing](https://github.com/gnubee-git/GnuBee_Docs/wiki/Install-firmware#via-usb-stick).
+
+## Getting ssh access to LibreCMC
+
+Once the firmware has been updated, turn off the GnuBee manually using the
+power switch and turn it back on.
+
+Now enable SSH access via the built-in [LibreCMC](https://librecmc.org)
+firmware:
+
+1. Plug a network cable between your laptop and the **black network port**.
+2. Open web-based admin panel at <http://192.168.10.0>.
+3. Go to *System | Administration*.
+4. Set a root password.
+5. Disable ssh password auth and root password logins.
+6. Paste in your **RSA** ssh public key.
+7. Click *Save & Apply*.
+8. Go to *Network | Firewall*.
+9. Select "accept" for WAN Input.
+10. Click *Save & Apply*.
+
+Finaly, go to *Network | Interfaces* and note the ipv4 address of the WAN
+port since that will be needed in the next step.
+
+## Installing Debian
+
+The first step is to [install Debian
+jessie](https://github.com/gnubee-git/GnuBee_Docs/wiki/Debian) on the
+GnuBee.
+
+Connect the **blue network port** into your router/switch and ssh into the
+GnuBee using the IP address you noted earlier:
+
+    ssh root@192.168.1.xxx
+
+and the root password you set in the previous section.
+
+Then use `fdisk /dev/sda` to create the following partition layout on the
+first drive:
+
+    Device       Start       End   Sectors   Size Type
+    /dev/sda1     2048   8390655   8388608     4G Linux swap
+    /dev/sda2  8390656 234441614 226050959 107.8G Linux filesystem
+
+Note that I used an 120GB solid-state drive as the system drive in order to
+minimize noise levels.
+
+Then format the swap partition:
+
+    mkswap /dev/sda1
+
+and download the latest version of the jessie installer:
+
+    wget --no-check-certificate https://raw.githubusercontent.com/gnubee-git/GnuBee_Docs/master/GB-PCx/scripts/jessie_3.10.14/debian-jessie-install
+
+(Yes, the `--no-check-certificate` is really unfortunate. Please leave a
+comment if you find a way to work around it.)
+
+The stock installer fails to bring up the correct networking configuration
+on my network and so I have [modified the
+install script](https://github.com/gnubee-git/GnuBee_Docs/pull/102) by changing
+the `eth0.1` blurb to:
+
+    auto eth0.1
+    iface eth0.1 inet static
+        address 192.168.10.1
+        netmask 255.255.255.0
+
+Then you should be able to run the installer succesfully:
+
+    sh ./debian-jessie-install
+
+and reboot:
+
+    reboot
+
+# Restore ssh access in Debian jessie
+
+Once the GnuBee has finished booting, login using the [serial console](https://github.com/gnubee-git/GnuBee_Docs/blob/master/USB_to_UART/README.md):
+
+- username: `root`
+- password: `GnuBee`
+
+and change the root password using `passwd`.
+
+Look for the IPv4 address of `eth0.2` in the output of the `ip addr` command
+and then ssh into the GnuBee from your desktop computer:
+
+    ssh root@192.168.1.xxx  # type password set above
+    mkdir .ssh
+    vim .ssh/authorized_keys  # paste your ed25519 ssh pubkey
+
+## Finish the jessie installation
+
+With this in place, you should be able to ssh into the GnuBee using your
+public key:
+
+    ssh root@192.168.1.172
+
+and then finish the jessie installation:
+
+    wget --no-check-certificate https://raw.githubusercontent.com/gnubee-git/gnubee-git.github.io/master/debian/debian-modules-install
+    bash ./debian-modules-install
+    reboot
+
+After rebooting, I made a few tweaks to make the system more pleasant to
+use:
+
+    update-alternatives --config editor  # choose vim.basic
+    dpkg-reconfigure locales  # enable the locale that your desktop is using
+
+## Upgrade to stretch and then buster
+
+To upgrade to stretch, put this in `/etc/apt/sources.list`:
+
+    deb http://httpredir.debian.org/debian stretch main
+    deb http://httpredir.debian.org/debian stretch-updates main
+    deb http://security.debian.org/ stretch/updates main
+
+Then upgrade the packages:
+
+    apt update
+    apt full-upgrade
+    apt autoremove
+    reboot
+
+To upgrade to buster, put this in `/etc/apt/sources.list`:
+
+    deb http://httpredir.debian.org/debian buster main
+    deb http://httpredir.debian.org/debian buster-updates main
+    deb http://security.debian.org/debian-security buster/updates main
+
+and upgrade the packages:
+
+    apt update
+    apt full-upgrade
+    apt autoremove
+    reboot
+
+## Next steps
+
+At this point, my GnuBee is running the latest version of Debian stable,
+however there are two remaining issues to fix:
+
+1. [openssh-server doesn't
+   work](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=932089) and I am
+   forced to access the GnuBee via the serial interface.
+
+2. The firmware is running an outdated version of the Linux kernel though
+   this is [being worked
+   on](https://groups.google.com/d/topic/gnubee/YVM08lfWUUc/discussion) by
+   community members.
+
+I hope to resolve these issues soon, and will update this blog post once I
+do, but you are more than welcome to leave a comment if you know of a
+solution I may have overlooked.
+
+[[!tag debian]] [[!tag gnubee]] [[!tag nzoss]]

Enable apt sandboxing which is now available in buster
diff --git a/posts/usual-server-setup.mdwn b/posts/usual-server-setup.mdwn
index 07d944d..79ca04d 100644
--- a/posts/usual-server-setup.mdwn
+++ b/posts/usual-server-setup.mdwn
@@ -238,6 +238,10 @@ and the following to harden the TCP stack:
 
 before reloading these settings using `sysctl -p`.
 
+Sandboxing in apt can be enabled by putting the following in `/etc/apt/apt.conf.d/30-seccomp`:
+
+    APT::Sandbox::Seccomp "true";
+
 I also restrict the use of cron to the `root` user by putting the following in `/etc/cron.allow`:
 
     root

Add the replacement for mcelog
diff --git a/posts/usual-server-setup.mdwn b/posts/usual-server-setup.mdwn
index c8627d8..07d944d 100644
--- a/posts/usual-server-setup.mdwn
+++ b/posts/usual-server-setup.mdwn
@@ -9,7 +9,7 @@ how I customize recent releases of Debian on those servers.
 
 # Hardware tests
 
-    apt install memtest86+ smartmontools e2fsprogs
+    apt install memtest86+ smartmontools e2fsprogs rasdaemon
 
 Prior to spending any time configuring a new physical server, I like to
 ensure that the hardware is fine.

Include the contents of ~/.bashrc-brave
diff --git a/posts/seeding-brave-browser-sccache.mdwn b/posts/seeding-brave-browser-sccache.mdwn
index 77bb125..4a11ece 100644
--- a/posts/seeding-brave-browser-sccache.mdwn
+++ b/posts/seeding-brave-browser-sccache.mdwn
@@ -65,4 +65,12 @@ and here are the contents of that script:
     echo "=> Delete build output"
     rm -rf src/out
 
+It references a `~/.bashrc-brave` file which contains:
+
+    #!/bin/sh
+    export PATH="${PATH}:${HOME}/bin:${HOME}/devel/brave-browser/vendor/depot_tools:${HOME}/.cargo/bin"
+    export SCCACHE_DIR="${HOME}/.cache/sccache"
+    export SCCACHE_CACHE_SIZE=100G
+    export NO_AUTH_BOTO_CONFIG="${HOME}/.boto"
+
 [[!tag brave]] [[!tag sccache]]

Add SIP TLS/SRTP post
diff --git a/posts/sip-encryption-on-voip-ms.mdwn b/posts/sip-encryption-on-voip-ms.mdwn
new file mode 100644
index 0000000..2cb3209
--- /dev/null
+++ b/posts/sip-encryption-on-voip-ms.mdwn
@@ -0,0 +1,73 @@
+[[!meta title="SIP Encryption on VoIP.ms"]]
+[[!meta date="2019-07-06T16:00:00.000-07:00"]]
+[[!meta license="[Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)"]]
+
+My [VoIP provider](https://voip.ms) recently added [support for
+TLS/SRTP-based call
+encryption](https://wiki.voip.ms/article/Call_Encryption_-_TLS/SRTP). Here's
+what I did to enable this feature on my
+[Asterisk](https://www.asterisk.org/) server.
+
+First of all, I changed the registration line in `/etc/asterisk/sip.conf` to
+use the "tls" scheme:
+
+    [general]
+    register => tls://mydid:mypassword@servername.voip.ms
+
+then I enabled incoming TCP connections:
+
+    tcpenable=yes
+
+and TLS:
+
+    tlsenable=yes
+    tlscapath=/etc/ssl/certs/
+
+Finally, I changed my provider entry in the same file to:
+
+    [voipms]
+    type=friend
+    host=servername.voip.ms
+    secret=mypassword
+    username=mydid
+    context=from-voipms
+    allow=ulaw
+    allow=g729
+    insecure=port,invite
+    transport=tls
+    encryption=yes
+
+(Note the last two lines.)
+
+The dialplan didn't change and so I still have the following in
+`/etc/asterisk/extensions.conf`:
+
+    [pstn-voipms]
+    exten => _1NXXNXXXXXX,1,Set(CALLERID(all)=Francois Marier <5551234567>)
+    exten => _1NXXNXXXXXX,n,Dial(SIP/voipms/${EXTEN})
+    exten => _1NXXNXXXXXX,n,Hangup()
+    exten => _NXXNXXXXXX,1,Set(CALLERID(all)=Francois Marier <5551234567>)
+    exten => _NXXNXXXXXX,n,Dial(SIP/voipms/1${EXTEN})
+    exten => _NXXNXXXXXX,n,Hangup()
+    exten => _011X.,1,Set(CALLERID(all)=Francois Marier <5551234567>)
+    exten => _011X.,n,Authenticate(1234) ; require password for international calls
+    exten => _011X.,n,Dial(SIP/voipms/${EXTEN})
+    exten => _011X.,n,Hangup(16)
+
+## Server certificate
+
+The only thing I still need to fix is to make this error message go away 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
+`/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).
+
+[[!tag debian]] [[!tag asterisk]] [[!tag nzoss]] [[!tag letsencrypt]]

Add distro-info to get EOL info
https://askubuntu.com/a/1126933
diff --git a/posts/usual-server-setup.mdwn b/posts/usual-server-setup.mdwn
index 8a9cf7a..c8627d8 100644
--- a/posts/usual-server-setup.mdwn
+++ b/posts/usual-server-setup.mdwn
@@ -261,7 +261,7 @@ The above packages are all about catching mistakes (such as
 
 # Package updates
 
-    apt install apticron unattended-upgrades deborphan debfoster apt-listchanges reboot-notifier popularity-contest needrestart debian-security-support
+    apt install apticron unattended-upgrades deborphan debfoster apt-listchanges reboot-notifier popularity-contest needrestart debian-security-support distro-info
 
 These tools help me keep packages up to date and remove unnecessary or
 obsolete packages from servers. On Rackspace servers, a small [configuration

Use the Cloudflare NTP server
https://blog.cloudflare.com/secure-time/
diff --git a/posts/time-synchronization-with-ntp-and-systemd.mdwn b/posts/time-synchronization-with-ntp-and-systemd.mdwn
index 3f54067..f548d26 100644
--- a/posts/time-synchronization-with-ntp-and-systemd.mdwn
+++ b/posts/time-synchronization-with-ntp-and-systemd.mdwn
@@ -60,7 +60,7 @@ you and put it in `/etc/systemd/timesyncd.conf`. For example, mine reads
 like this:
 
     [Time]
-    NTP=ca.pool.ntp.org
+    NTP=time.cloudflare.com
 
 before restarting the daemon:
 

Add LXC post for OpenSUSE 15 on Ubuntu 18.04
diff --git a/posts/opensuse15-lxc-setup-on-ubuntu-bionic.mdwn b/posts/opensuse15-lxc-setup-on-ubuntu-bionic.mdwn
new file mode 100644
index 0000000..9e45f32
--- /dev/null
+++ b/posts/opensuse15-lxc-setup-on-ubuntu-bionic.mdwn
@@ -0,0 +1,101 @@
+[[!meta title="OpenSUSE 15 LXC setup on Ubuntu Bionic 18.04"]]
+[[!meta date="2019-06-14T20:15:00.000-07:00"]]
+[[!meta license="[Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)"]]
+
+Similarly to what I wrote for [Fedora](https://feeding.cloud.geek.nz/posts/fedora29-lxc-setup-on-ubuntu-bionic/),
+here is how I was able to create an [OpenSUSE](https://www.opensuse.org) 15 LXC
+container on an Ubuntu 18.04 (bionic) laptop.
+
+# Setting up LXC on Ubuntu
+
+First of all, install lxc:
+
+    apt install lxc
+    echo "veth" >> /etc/modules
+    modprobe veth
+
+turn on bridged networking by putting the following in
+`/etc/sysctl.d/local.conf`:
+
+    net.ipv4.ip_forward=1
+
+and applying it using:
+
+    sysctl -p /etc/sysctl.d/local.conf
+
+Then allow the right traffic in your firewall
+(`/etc/network/iptables.up.rules` in my case):
+
+    # LXC containers
+    -A FORWARD -d 10.0.3.0/24 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
+    -A FORWARD -s 10.0.3.0/24 -j ACCEPT
+    -A INPUT -d 224.0.0.251 -s 10.0.3.1 -j ACCEPT
+    -A INPUT -d 239.255.255.250 -s 10.0.3.1 -j ACCEPT
+    -A INPUT -d 10.0.3.255 -s 10.0.3.1 -j ACCEPT
+    -A INPUT -d 10.0.3.1 -s 10.0.3.0/24 -j ACCEPT
+
+and apply these changes:
+
+    iptables-apply
+
+before restarting the lxc networking:
+
+    systemctl restart lxc-net.service
+
+# Creating the container
+
+Once that's in place, you can finally create the OpenSUSE 15 container:
+
+    lxc-create -n opensuse15 -t download -- -d opensuse -r 15 -a amd64
+
+To see a list of all distros available with the `download` template:
+
+    lxc-create -n foo --template=download -- --list
+
+# Logging in as root
+
+Start up the container and get a login console:
+
+    lxc-start -n opensuse15 -F
+
+In another terminal, set a password for the root user:
+
+    lxc-attach -n opensuse15 passwd
+
+You can now use this password to log into the console you started earlier.
+
+# Logging in as an unprivileged user via ssh
+
+As root, install a few packages:
+
+    zypper install vim openssh sudo man
+    systemctl start sshd
+    systemctl enable sshd
+
+and then create an unprivileged user:
+
+    useradd francois
+    passwd francois
+    cd /home
+    mkdir francois
+    chown francois:100 francois/
+
+and give that user [sudo access](https://en.opensuse.org/SDB:Administer_with_sudo):
+
+    visudo  # uncomment "wheel" line
+    groupadd wheel
+    usermod -aG wheel francois
+
+Now login as that user from the console and add an ssh public key:
+
+    mkdir .ssh
+    chmod 700 .ssh
+    echo "<your public key>" > .ssh/authorized_keys
+    chmod 644 .ssh/authorized_keys
+
+You can now login via ssh. The IP address to use can be seen in the output
+of:
+
+    lxc-ls --fancy
+
+[[!tag debian]] [[!tag lxc]] [[!tag nzoss]] [[!tag ubuntu]]

Comment moderation
diff --git a/posts/installing-vidyo-on-ubuntu-1804/comment_3_ea0a0f985040e8679fed6f98209126f1._comment b/posts/installing-vidyo-on-ubuntu-1804/comment_3_ea0a0f985040e8679fed6f98209126f1._comment
new file mode 100644
index 0000000..3482398
--- /dev/null
+++ b/posts/installing-vidyo-on-ubuntu-1804/comment_3_ea0a0f985040e8679fed6f98209126f1._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ ip="2001:610:120:3000::192:161"
+ claimedauthor="Frans Schreuder"
+ subject="Topicons plus"
+ date="2019-06-14T06:33:04Z"
+ content="""
+The instruction works, but one important thing is missing. On Ubuntu with Gnome (Both 18.04 and 19.04) you will need the gnome extention TopIcons Plus installed in order to launch VidyoDesktop, otherwise the application will fail to start (at least for me).
+"""]]

Fix typo
diff --git a/posts/installing-vidyo-on-ubuntu-1804/comment_2_93c96cdc7713032646438fe0a172a56c._comment b/posts/installing-vidyo-on-ubuntu-1804/comment_2_93c96cdc7713032646438fe0a172a56c._comment
index c735c96..6d9f427 100644
--- a/posts/installing-vidyo-on-ubuntu-1804/comment_2_93c96cdc7713032646438fe0a172a56c._comment
+++ b/posts/installing-vidyo-on-ubuntu-1804/comment_2_93c96cdc7713032646438fe0a172a56c._comment
@@ -4,5 +4,5 @@
  subject="Re: comment 1"
  date="2018-11-08T06:32:12Z"
  content="""
-I'm not sure why you're saying that it's sloppy for a system-wide binary to be owned by root. That's both [the policy in Debian](https://www.debian.org/doc/debian-policy/ch-files.html#permissions-and-owners) and also it prevents an ordinary user from tampering a binary that could be used by other users.
+I'm not sure why you're saying that it's sloppy for a system-wide binary to be owned by root. That's both [the policy in Debian](https://www.debian.org/doc/debian-policy/ch-files.html#permissions-and-owners) and also it prevents an ordinary user from tampering with a binary that could be used by other users.
 """]]

Disable modelines in vim
https://nvd.nist.gov/vuln/detail/CVE-2019-12735
diff --git a/posts/usual-server-setup.mdwn b/posts/usual-server-setup.mdwn
index 9f5bad3..8a9cf7a 100644
--- a/posts/usual-server-setup.mdwn
+++ b/posts/usual-server-setup.mdwn
@@ -64,6 +64,7 @@ following to `/etc/vim/vimrc.local`:
     syntax on
     set background=dark
     set visualbell
+    set nomodeline
 
 # ssh
 

More thorough cleaning of old builds / checkouts
diff --git a/posts/seeding-brave-browser-sccache.mdwn b/posts/seeding-brave-browser-sccache.mdwn
index aef0e28..77bb125 100644
--- a/posts/seeding-brave-browser-sccache.mdwn
+++ b/posts/seeding-brave-browser-sccache.mdwn
@@ -34,16 +34,18 @@ and here are the contents of that script:
     echo
     
     echo $(date)
+    echo "=> Clean up repo and delete old build output"
+    rm -rf src/out node_modules src/brave/node_modules
+    git clean -f -d
+    git checkout HEAD package-lock.json
+    
+    echo $(date)
     echo "=> Update repo"
     git pull
     npm install
     npm run init
     
     echo $(date)
-    echo "=> Delete any old build output"
-    rm -rf src/out
-    
-    echo $(date)
     echo "=> Debug build"
     killall sccache || true
     ionice nice timeout 4h npm run build || ionice nice timeout 4h npm run build

Comment moderation
diff --git a/posts/setting-up-a-network-scanner-using-sane/comment_10_10aacafabba32f9596e18f4163fe9fd9._comment b/posts/setting-up-a-network-scanner-using-sane/comment_10_10aacafabba32f9596e18f4163fe9fd9._comment
new file mode 100644
index 0000000..49b0938
--- /dev/null
+++ b/posts/setting-up-a-network-scanner-using-sane/comment_10_10aacafabba32f9596e18f4163fe9fd9._comment
@@ -0,0 +1,141 @@
+[[!comment format=mdwn
+ ip="88.207.218.2"
+ claimedauthor="Anonymous Coward"
+ subject="Revised configuration necessary for saned under systemd"
+ date="2019-05-29T21:11:00Z"
+ content="""
+After encoutering numerous problems in setting up SANED network access with systemd on a Linux Mint 18.3 , here are some important points 
+
+1) make sure you do not have saned configured to run under inetd or xinetd as might be the case from an upgraded installation
+
+2) systemd needs a file for socket and an instance service file.  To avoid loss of customizations with package upgrades, put these in /etc/systemd/system not overwrite those in /lib/systemd/system
+
+    #*****************************************************************************#
+    #|
+    #|  file : /etc/systemd/system/saned.socket
+    #|
+    #*---------------------------------------------------------------------------*#
+
+    [Unit]
+    Description=SANED network daemon activation socket
+
+    [Socket]
+    Accept=yes
+    ListenStream=6566
+    MaxConnections=1
+
+    [Install]
+    WantedBy=sockets.target
+
+    #*****************************************************************************#
+
+The second item it needs is an instance service file NOT a service file.  This means that the file name contains an \"@\" as in saned@.service and the contents
+
+    #*****************************************************************************#
+    #|
+    #|  file : /etc/systemd/system/saned@.service
+    #|
+    #*---------------------------------------------------------------------------*#
+
+    [Unit]
+    Description=SANE network daemon instance %i
+    Documentation=man:saned(8)
+    After=local-fs.target network-online.target
+    Requires=saned.socket
+
+    [Service]
+    Environment=SANE_CONFIG_DIR=/etc/sane.d
+    ExecStart=/usr/sbin/saned
+    Group=saned
+    User=saned
+    StandardInput=null
+    StandardOutput=syslog
+    StandardError=syslog
+
+    [Install]
+    Also=saned.socket
+
+    #*****************************************************************************#
+
+
+If you want to do debugging you can add additional Environment lines
+
+    Environment=SANE_DEBUG_DLL=255
+    Environment=SANE_DEBUG_NET=255
+
+For completeness, to mirror the setup in /lib/systemd/system and to emphasize the point when looking in /etc/systemd/system that the file is not missing and that saned@.service is not misnamed, symbolically link  /dev/null to /etc/lib/systemd/system/saned.service
+
+     /etc/systemd/system/saned.service -> /dev/null
+
+This is unecessary because of the link present in /lib/systemd/system, but it makes it clear when looking at the /etc/systemd/system directory the configuration being used.
+
+Do a systemctl enable of saned.socket and it will create a symbolic link under the socket.target.wants directory
+
+/etc/services should have the entry
+
+     sane-port		 6566/tcp	sane saned
+    
+Ensure that /proc/sys/net/ipv6/bindv6only is set to 0 (and not set to 1 due to some hack from an old bindipv6only.conf file /etc/sysctl.conf.d) if you are wanting network connections on IPv4 to work.
+
+It may be necessary to ensure that the \"net\" featured is turned on in /etc/sane.d/dll.d configuration file for your scanner if it is some non-standard configuration.
+
+In your /etc/sane.d/saned.conf ensure your have a \"localhost\" entry -- 127.0.0.1 should also work but when saned starts up it says checking for localhost, so this is to be consistent.
+Then add the host names or network IP address ranges permitted to access the service.
+
+Adjust your firewall rules if necessary.
+
+You do not need to add the host IP address to the /etc/sane.d/net.conf file, this will result in you being offered both a local and a network connection to the scanner from the host, so keep it simple for the server host, but do add the server host IP address or name to the net.conf file on the client hosts which need to access the service.
+
+If you then do a systemctl start saned.socket followed by a systemctl status saned.socket you should see
+
+     saned.socket - SANED network daemon activation socket
+       Loaded: loaded (/etc/systemd/system/saned.socket; enabled; vendor preset: enabled)
+       Active: active (listening) since Wed 2019-05-29 20:12:40 BST; 30min ago
+       Listen: [::]:6566 (Stream)
+     Accepted: 21; Connected: 0
+
+Now where the \"magic\" comes in, is that when a connection is made on the socket, systemd fires up an **instance** of the saned service using the socket name as the instance name
+(which is why the standard input is set to \"null\" and NOT \"socket\" in the saned@.service file).
+
+So if when a remote connection is made you do
+
+    systemctl -all -l --no-pager | grep saned you should see
+
+      saned@21-192.168.21.12:6566-192.168.11.12:49314.service                                    loaded    active   running   SANE network daemon instance 3 (192.168.11.12:49314)
+      system-saned.slice                                                                        loaded    active   active    system-saned.slice
+      saned.socket                                                                              loaded    active   listening SANED network daemon activation socket
+
+where the instance number increases by one for each connection.
+
+If you want to advertise the service via Avahi, you could add a service file under /etc/avahi/servvices
+
+    <?xml version=\"1.0\" standalone='no'?>
+    <!DOCTYPE service-group SYSTEM \"avahi-service.dtd\">
+
+    <!-- #********************************************************************# -->
+    <!-- #|                                                                  |# -->
+    <!-- #|  file : /etc/avahi/services/saned.service                        |# -->
+    <!-- #|                                                                  |# -->
+    <!-- #|__________________________________________________________________|# -->
+
+    <service-group>
+
+	     <name replace-wildcards=\"yes\">%h.example.COM Network Scanning</name>
+
+        	<service>
+        		<domain-name>local</domain-name>
+        		<host-name>server_name.local</host-name>
+         		<port>6566</port>
+         		<type>_scanner._tcp</type>
+        	</service>
+
+      </service-group>
+
+     <!-- #********************************************************************# -->
+
+
+replacing example.COM and server_name as appropriate.  The value for \"type\" was taken from
+
+     http://www.dns-sd.org/ServiceTypes.html
+
+"""]]

Simplify sha256sum instructions
https://linuxmint.com/verify.php
diff --git a/posts/installing-ubuntu-bionic-on-encrypted-raid1.mdwn b/posts/installing-ubuntu-bionic-on-encrypted-raid1.mdwn
index b07b47d..9b983b5 100644
--- a/posts/installing-ubuntu-bionic-on-encrypted-raid1.mdwn
+++ b/posts/installing-ubuntu-bionic-on-encrypted-raid1.mdwn
@@ -36,10 +36,8 @@ signature](https://tutorials.ubuntu.com/tutorial/tutorial-how-to-verify-ubuntu):
 
 3. Verify the hash of the ISO file:
 
-        $ sha256sum ubuntu-18.04.2-server-amd64.iso 
-        a2cb36dc010d98ad9253ea5ad5a07fd6b409e3412c48f1860536970b073c98f5  ubuntu-18.04.2-server-amd64.iso
-        $ grep ubuntu-18.04.2-server-amd64.iso SHA256SUMS
-        a2cb36dc010d98ad9253ea5ad5a07fd6b409e3412c48f1860536970b073c98f5 *ubuntu-18.04.2-server-amd64.iso
+        $ sha256sum --ignore-missing -c SHA256SUMS
+        ubuntu-18.04.2-server-amd64.iso: OK
 
 Then copy it to a USB drive:
 

Add extra heading
diff --git a/posts/installing-ubuntu-bionic-on-encrypted-raid1.mdwn b/posts/installing-ubuntu-bionic-on-encrypted-raid1.mdwn
index a5acfd1..b07b47d 100644
--- a/posts/installing-ubuntu-bionic-on-encrypted-raid1.mdwn
+++ b/posts/installing-ubuntu-bionic-on-encrypted-raid1.mdwn
@@ -47,6 +47,8 @@ Then copy it to a USB drive:
 
 and boot with it.
 
+## Manual partitioning
+
 Inside the installer, use manual partitioning to:
 
 1. Configure the physical partitions.

Comment moderation
diff --git a/posts/installing-ubuntu-bionic-on-encrypted-raid1/comment_1_d178526dcf96e252ab0196c59c93d1b0._comment b/posts/installing-ubuntu-bionic-on-encrypted-raid1/comment_1_d178526dcf96e252ab0196c59c93d1b0._comment
new file mode 100644
index 0000000..4193372
--- /dev/null
+++ b/posts/installing-ubuntu-bionic-on-encrypted-raid1/comment_1_d178526dcf96e252ab0196c59c93d1b0._comment
@@ -0,0 +1,10 @@
+[[!comment format=mdwn
+ ip="82.141.154.4"
+ claimedauthor="random from planet debian"
+ subject="LVM"
+ date="2019-05-24T09:43:23Z"
+ content="""
+If you set up [MD-raid> LUKS > LVM > {pool/swap, pool/root etc}] stack then you get one PW prompt and suspend to disk still works...
+
+br: a random guy
+"""]]
diff --git a/posts/mercurial-commit-series-phabricator-using-arcanist/comment_2_4b6c0f885e5dea08a3d6d4cf4e2793a9._comment b/posts/mercurial-commit-series-phabricator-using-arcanist/comment_2_4b6c0f885e5dea08a3d6d4cf4e2793a9._comment
new file mode 100644
index 0000000..7c9bf9b
--- /dev/null
+++ b/posts/mercurial-commit-series-phabricator-using-arcanist/comment_2_4b6c0f885e5dea08a3d6d4cf4e2793a9._comment
@@ -0,0 +1,12 @@
+[[!comment format=mdwn
+ ip="83.56.36.123"
+ claimedauthor="leplatrem"
+ subject="How to manage updates after review?"
+ date="2019-05-08T10:00:16Z"
+ content="""
+After everything is submitted, I made some changes to my commits.
+
+When I run `arc diff` for a particular commit, the process is really confusing. For example, with `arc diff --update DXXX`, I get prompted with the whole list of revisions... 
+
+
+"""]]

Expand the dunst comments and turn them into a "notifications" section
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 4dc4b8e..e3a00d7 100644
--- a/posts/creating-a-modern-tiling-desktop-environment-using-i3.mdwn
+++ b/posts/creating-a-modern-tiling-desktop-environment-using-i3.mdwn
@@ -20,11 +20,24 @@ Because of [a bug in gnome-settings-daemon](https://ask.fedoraproject.org/en/que
 
     dconf write /org/gnome/settings-daemon/plugins/cursor/active false
 
+# Notifications
+
 While my startup script doesn't run this tool directly, installing the
 [dunst package](https://packages.debian.org/stable/dunst) is required to receive desktop notifications:
 
     apt install dunst
 
+You will probably also want to set the following in `/etc/xdg/dunst/dunstrc` to ensure that notifications use your default web browser:
+
+    browser = /usr/bin/sensible-browser
+
+Here are the keyboard shortcuts you'll need to interact with the notifications that popup:
+
+- `Ctrl-Space` to close the current notification
+- `Ctrl-Shift-Space` to close all notifications
+- `Ctrl-\`` to show the last notification
+- `Ctrl-Shift-.` to show the context menu for the current notification
+
 # Screensaver
 
 In addition, gnome-screensaver didn't automatically lock my screen, so I installed [xautolock](https://packages.debian.org/stable/xautolock) and added it to my startup script:

Add re-try logic and compile tests too
diff --git a/posts/seeding-brave-browser-sccache.mdwn b/posts/seeding-brave-browser-sccache.mdwn
index ea742c1..aef0e28 100644
--- a/posts/seeding-brave-browser-sccache.mdwn
+++ b/posts/seeding-brave-browser-sccache.mdwn
@@ -45,12 +45,18 @@ and here are the contents of that script:
     
     echo $(date)
     echo "=> Debug build"
-    ionice nice timeout 4h npm run build
+    killall sccache || true
+    ionice nice timeout 4h npm run build || ionice nice timeout 4h npm run build
+    ionice nice ninja -C src/out/Debug brave_unit_tests
+    ionice nice ninja -C src/out/Debug brave_browser_tests
     echo
     
     echo $(date)
     echo "=>Release build"
-    ionice nice timeout 5h npm run build Release
+    killall sccache || true
+    ionice nice timeout 5h npm run build Release || ionice nice timeout 5h npm run build Release
+    ionice nice ninja -C src/out/Release brave_unit_tests
+    ionice nice ninja -C src/out/Release brave_browser_tests
     echo
     
     echo $(date)

Add my RAID1+LUKS post on Ubuntu
diff --git a/posts/installing-ubuntu-bionic-on-encrypted-raid1.mdwn b/posts/installing-ubuntu-bionic-on-encrypted-raid1.mdwn
new file mode 100644
index 0000000..a5acfd1
--- /dev/null
+++ b/posts/installing-ubuntu-bionic-on-encrypted-raid1.mdwn
@@ -0,0 +1,186 @@
+[[!meta title="Installing Ubuntu 18.04 using both full-disk encryption and RAID1"]]
+[[!meta date="2019-05-22T21: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 setup a desktop computer with two SSDs using a software RAID1 and
+full-disk encryption (i.e. [LUKS](https://en.wikipedia.org/wiki/LUKS)).
+Since this is not a supported configuration in Ubuntu desktop, I had to use
+the server installation medium.
+
+This is my version of these [excellent
+instructions](https://askubuntu.com/questions/1066028/install-ubuntu-18-04-desktop-with-raid-1-and-lvm-on-machine-with-uefi-bios#1066041).
+
+## Server installer
+
+Start by downloading the [alternate server
+installer](http://cdimage.ubuntu.com/ubuntu/releases/bionic/release/) and
+[verifying its
+signature](https://tutorials.ubuntu.com/tutorial/tutorial-how-to-verify-ubuntu):
+
+1. Download the required files:
+
+        wget http://cdimage.ubuntu.com/ubuntu/releases/bionic/release/ubuntu-18.04.2-server-amd64.iso
+        wget http://cdimage.ubuntu.com/ubuntu/releases/bionic/release/SHA256SUMS
+        wget http://cdimage.ubuntu.com/ubuntu/releases/bionic/release/SHA256SUMS.gpg
+
+2. Verify the signature on the hash file:
+
+        $ gpg --keyid-format long --keyserver hkps://keyserver.ubuntu.com --recv-keys 0xD94AA3F0EFE21092
+        $ gpg --verify SHA256SUMS.gpg SHA256SUMS
+        gpg: Signature made Fri Feb 15 08:32:38 2019 PST
+        gpg:                using RSA key D94AA3F0EFE21092
+        gpg: Good signature from "Ubuntu CD Image Automatic Signing Key (2012) <cdimage@ubuntu.com>" [undefined]
+        gpg: WARNING: This key is not certified with a trusted signature!
+        gpg:          There is no indication that the signature belongs to the owner.
+        Primary key fingerprint: 8439 38DF 228D 22F7 B374  2BC0 D94A A3F0 EFE2 1092
+
+3. Verify the hash of the ISO file:
+
+        $ sha256sum ubuntu-18.04.2-server-amd64.iso 
+        a2cb36dc010d98ad9253ea5ad5a07fd6b409e3412c48f1860536970b073c98f5  ubuntu-18.04.2-server-amd64.iso
+        $ grep ubuntu-18.04.2-server-amd64.iso SHA256SUMS
+        a2cb36dc010d98ad9253ea5ad5a07fd6b409e3412c48f1860536970b073c98f5 *ubuntu-18.04.2-server-amd64.iso
+
+Then copy it to a USB drive:
+
+    dd if=ubuntu-18.04.2-server-amd64.iso of=/dev/sdX
+
+and boot with it.
+
+Inside the installer, use manual partitioning to:
+
+1. Configure the physical partitions.
+2. Configure the RAID array second.
+2. Configure the encrypted partitions last
+
+Here's the exact configuration I used:
+
+- `/dev/sda1` is 512 MB and used as the EFI parition
+- `/dev/sdb1` is 512 MB but **not used for anything**
+- `/dev/sda2` and `/dev/sdb2` are both 4 GB (RAID)
+- `/dev/sda3` and `/dev/sdb3` are both 512 MB (RAID)
+- `/dev/sda4` and `/dev/sdb4` use up the rest of the disk (RAID)
+
+I only set `/dev/sda2` as the EFI partition because I found that **adding a
+second EFI partition would break the installer**.
+
+I created the following RAID1 arrays:
+
+- `/dev/sda2` and `/dev/sdb2` for `/dev/md2`
+- `/dev/sda3` and `/dev/sdb3` for `/dev/md0`
+- `/dev/sda4` and `/dev/sdb4` for `/dev/md1`
+
+I used `/dev/md0` as my **unencrypted** `/boot` partition.
+
+Then I created the following LUKS partitions:
+
+- `md1_crypt` as the `/` partition using `/dev/md1`
+- `md2_crypt` as the *swap* partition (4 GB) with a **random
+  encryption key** using `/dev/md2`
+
+## Post-installation configuration
+
+Once your new system is up, sync the EFI partitions using DD:
+
+    dd if=/dev/sda1 of=/dev/sdb1
+
+and create a second EFI boot entry:
+
+    efibootmgr -c -d /dev/sdb -p 1 -L "ubuntu2" -l \EFI\ubuntu\shimx64.efi
+
+Ensure that the RAID drives are fully sync'ed by keeping an eye on
+`/prod/mdstat` and then reboot, selecting "ubuntu2" in the UEFI/BIOS menu.
+
+Once you have rebooted, remove the following package to speed up future boots:
+
+    apt purge btrfs-progs
+
+To switch to the desktop variant of Ubuntu, install these meta-packages:
+
+    apt install ubuntu-desktop gnome
+
+then use `debfoster` to remove unnecessary packages (in particular the ones
+that only come with the default Ubuntu server installation).
+
+## Fixing booting with degraded RAID arrays
+
+Since I have run into [RAID startup problems in the
+past](https://feeding.cloud.geek.nz/posts/the-perils-of-raid-and-full-disk-encryption-on-ubuntu/),
+I expected having to fix up a few things to make degraded RAID arrays
+boot correctly.
+
+I did not use [LVM](https://en.wikipedia.org/wiki/LVM2) since I
+didn't really feel the need to add yet another layer of abstraction of top
+of my setup, but I found that the `lvm2` package must still be installed:
+
+    apt install lvm2
+
+with `use_lvmetad = 0` in `/etc/lvm/lvm.conf`.
+
+Then in order to automatically bring up the RAID arrays with 1 out of 2
+drives, I added the following script in
+`/etc/initramfs-tools/scripts/local-top/cryptraid`:
+
+     #!/bin/sh
+     PREREQ="mdadm"
+     prereqs()
+     {
+          echo "$PREREQ"
+     }
+     case $1 in
+     prereqs)
+          prereqs
+          exit 0
+          ;;
+     esac
+     
+     mdadm --run /dev/md0
+     mdadm --run /dev/md1
+     mdadm --run /dev/md2
+
+before making that script executable:
+
+    chmod +x /etc/initramfs-tools/scripts/local-top/cryptraid
+
+and refreshing the initramfs:
+
+    update-initramfs -u -k all
+
+## Disable suspend-to-disk
+
+Since I use a [random encryption key for the swap
+partition](https://feeding.cloud.geek.nz/posts/encrypted-swap-partition-on/)
+(to avoid having a second password prompt at boot time), it means that
+suspend-to-disk is not going to work and so I disabled it by putting the
+following in `/etc/initramfs-tools/conf.d/resume`:
+
+    RESUME=none
+
+and by adding `noresume` to the `GRUB_CMDLINE_LINUX` variable in
+`/etc/default/grub` before applying these changes:
+
+    update-grub
+    update-initramfs -u -k all
+
+## Test your configuration
+
+With all of this in place, you should be able to do a final test of your
+setup:
+
+1. Shutdown the computer and unplug the second drive.
+2. Boot with only the first drive.
+3. Shutdown the computer and plug the second drive back in.
+4. Boot with both drives and re-add the second drive to the RAID array:
+
+        mdadm /dev/md0 -a /dev/sdb3
+        mdadm /dev/md1 -a /dev/sdb4
+        mdadm /dev/md2 -a /dev/sdb2
+
+5. Wait until the RAID is done re-syncing and shutdown the computer.
+6. Repeat steps 2-5 with the first drive unplugged instead of the second.
+7. Reboot with both drives plugged in.
+
+At this point, you have a working setup that will gracefully degrade to a
+one-drive RAID array should one of your drives fail.
+
+[[!tag debian]] [[!tag nzoss]] [[!tag ubuntu]] [[!tag raid]]

Fix list command
diff --git a/posts/fedora29-lxc-setup-on-ubuntu-bionic.mdwn b/posts/fedora29-lxc-setup-on-ubuntu-bionic.mdwn
index b7c4b22..7b87943 100644
--- a/posts/fedora29-lxc-setup-on-ubuntu-bionic.mdwn
+++ b/posts/fedora29-lxc-setup-on-ubuntu-bionic.mdwn
@@ -51,7 +51,7 @@ Once that's in place, you can finally create the Fedora 29 container:
 
 To see a list of all distros available with the `download` template:
 
-    lxc-create --template=download -- --list
+    lxc-create -n foo --template=download -- --list
 
 # Logging in as root