diff --git a/CHANGELOG b/CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,33 @@
+propellor (5.14) unstable; urgency=medium
+
+  * Removed Propellor.Property.OpenID module, since the simpleid package
+    got removed from Debian.
+  * Renamed Linode.mlocateEnabled to Linode.locateEnabled, and made it also
+    make sure plocate is enabled, if it's installed rather than mlocate.
+    (API change)
+  * Debootstrap.built: Stop using the deprecated qemu-debootstrap
+    for bootstrapping foreign architectures. It is not needed since qemu
+    2.12, which, at least on Debian, enabled the 
+    binfmt-support --fix-binary option.
+  * Qemu.removeHostEmulationBinary: Removed this property, which is no
+    longer necessary since qemu-debootstrap is not used.
+    (API change)
+  * Flashkernel.installed: Deal with a behavior change in flash-kernel,
+    which will fail to install if is not configured first.
+  * Systemd.resolvConfed is no longer enabled by default in systemd
+    containers. It is probably not necessary on current systems the way it
+    was back in 2014, and this avoids a problem where the host's
+    /etc/resolv.conf gets overwritten when systemd is managing the
+    container's /etc/resolv.conf. 
+  * Borg: To support borg 1.2, run borg compact to free up space after pruning.
+  * Add lower bounds on async and split.
+    Thanks, Simon Jakobi
+  * propellor.cabal: Deduplicate exposed modules, which fixes a build
+    problem with recent cabal versions.
+    Thanks, Simon Jakobi
+
+ -- Joey Hess <id@joeyh.name>  Sat, 15 Oct 2022 14:40:31 -0400
+
 propellor (5.13) unstable; urgency=medium
 
   * Network: Added support for network interfaces with several address
diff --git a/Makefile b/Makefile
--- a/Makefile
+++ b/Makefile
@@ -45,11 +45,8 @@
 	find . -name \*.o -exec rm {} \;
 	find . -name \*.hi -exec rm {} \;
 
-# hothasktags chokes on some template haskell etc, so ignore errors
-# duplicate tags with Propellor.Property. removed from the start, as we
-# often import qualified by just the module base name.
 tags:
-	@find . | grep -v /.git/ | grep -v /tmp/ | grep -v dist/ | grep -v /doc/ | egrep '\.hs$$' | xargs hothasktags 2>/dev/null | perl -ne 'print; s/Propellor\.Property\.//; print' | sort > tags || true
+	@hasktags . -c 2>/dev/null || true
 
 configured: propellor.cabal
 	@if [ "$(CABAL)" = ./Setup ]; then ghc --make Setup; fi
diff --git a/config.hs b/config.hs
--- a/config.hs
+++ b/config.hs
@@ -25,7 +25,6 @@
 import qualified Propellor.Property.Locale as Locale
 import qualified Propellor.Property.Grub as Grub
 import qualified Propellor.Property.Borg as Borg
-import qualified Propellor.Property.OpenId as OpenId
 import qualified Propellor.Property.Systemd as Systemd
 import qualified Propellor.Property.Journald as Journald
 import qualified Propellor.Property.Fail2Ban as Fail2Ban
@@ -47,7 +46,7 @@
 	, dragon
 	, oyster
 	, orca
-	, honeybee
+	, house
 	, kite
 	, beaver
 	, sow
@@ -78,8 +77,8 @@
 	& Ssh.userKeys (User "joey") hostContext
 		[ (SshEd25519, "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICfFntnesZcYz2B2T41ay45igfckXRSh5uVffkuCQkLv joey@darkstar")
 		]
-	& imageBuiltFor honeybee
-		(RawDiskImage "/srv/honeybee.img")
+	& imageBuiltFor house
+		(RawDiskImage "/srv/house.img")
 		(Debootstrapped mempty)
 
 dragon :: Host
@@ -120,8 +119,8 @@
 		GitAnnexBuilder.standardAutoBuilder
 		Testing ARM64 Nothing (Cron.Times "1 * * * *") "4h")
 
-honeybee :: Host
-honeybee = host "honeybee.kitenet.net" $ props
+house :: Host
+house = host "house.lan" $ props
 	& standardSystem Testing ARMHF
 		[ "Home router and arm git-annex build box." ]
 	& Apt.removed ["rsyslog"]
@@ -134,7 +133,7 @@
 		)
 	& JoeySites.cubieTruckOneWire
 	& Systemd.persistentJournal
-	& Apt.installed ["firmware-misc-nonfree"] -- wifi
+	& Apt.installed ["firmware-atheros"]
 	& Apt.serviceInstalledRunning "ntp" -- no hardware clock
 	& bootstrappedFrom GitRepoOutsideChroot
 	& Ssh.hostKeys hostContext
@@ -149,9 +148,12 @@
 		hosts
 		(Context "house.joeyh.name")
 		(SshEd25519, "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMAmVYddg/RgCbIj+cLcEiddeFXaYFnbEJ3uGj9G/EyV joey@honeybee")
+	& JoeySites.connectStarlinkRouter
 	& JoeySites.homeRouter
 	& JoeySites.homeNAS
-	& Apt.installed ["mtr-tiny", "iftop", "screen"]
+	& Apt.installed ["mtr-tiny", "iftop", "screen", "nmap"]
+	-- Currently manually building the xr_usb_serial module.
+	& Apt.installed ["linux-headers-armmp-lpae"]
 	& Postfix.satellite
 
 	& check (not <$> hasContainerCapability Systemd.FilesystemContained) 
@@ -170,7 +172,10 @@
 kite = host "kite.kitenet.net" $ props
 	& standardSystemUnhardened Testing X86_64 [ "Welcome to kite!" ]
 	& ipv4 "66.228.36.95"
-	& ipv6 "2600:3c03::f03c:91ff:fe73:b0d2"
+	-- disabled because I've had complaints of mail bouncing, and it
+	-- seems mail servers are only trying ipv6 and then bouncing if
+	-- they have an ipv6 routing issue.
+	-- & ipv6 "2600:3c03::f03c:91ff:fe73:b0d2"
 	& alias "kitenet.net"
 	& Ssh.hostKeys (Context "kitenet.net")
 		[ (SshDsa, "ssh-dss AAAAB3NzaC1kc3MAAACBAO9tnPUT4p+9z7K6/OYuiBNHaij4Nzv5YVBih1vMl+ALz0gYAj8RWJzXmqp5buFAyfgOoLw+H9s1bBS01Sy3i07Dm6cx1fWG4RXL/E/3w1tavX99GD2bBxDBu890ebA5Tp+eFRJkS9+JwSvFiF6CP7NbVjifCagoUO56Ig048RwDAAAAFQDPY2xM3q6KwsVQliel23nrd0rV2QAAAIEAga3hj1hL00rYPNnAUzT8GAaSP62S4W68lusErH+KPbsMwFBFY/Ib1FVf8k6Zn6dZLh/HH/RtJi0JwdzPI1IFW+lwVbKfwBvhQ1lw9cH2rs1UIVgi7Wxdgfy8gEWxf+QIqn62wG+Ulf/HkWGvTrRpoJqlYRNS/gnOWj9Z/4s99koAAACBAM/uJIo2I0nK15wXiTYs/NYUZA7wcErugFn70TRbSgduIFH6U/CQa3rgHJw9DCPCQJLq7pwCnFH7too/qaK+czDk04PsgqV0+Jc7957gU5miPg50d60eJMctHV4eQ1FpwmGGfXxRBR9k2ZvikWYatYir3L6/x1ir7M0bA9IzNU45")
@@ -183,7 +188,7 @@
 	& Apt.installed ["linux-image-amd64"]
 	& Apt.serviceInstalledRunning "swapspace"
 	& Linode.serialGrub
-	& Linode.mlocateEnabled
+	& Linode.locateEnabled
 	& Apt.unattendedUpgrades
 	& Systemd.installed
 	& Systemd.persistentJournal
@@ -240,7 +245,7 @@
 	& Apt.installed
 		[ "git-annex", "myrepos"
 		, "build-essential", "make"
-		, "rss2email", "archivemail"
+		, "rss2email", "chewmail"
 		, "devscripts"
 		-- Some users have zsh as their login shell.
 		, "zsh"
@@ -253,15 +258,10 @@
 	& JoeySites.kgbServer
 	
 	& Systemd.nspawned ancientKitenet
-	! Systemd.nspawned openidProvider
 	
 	& alias "podcatcher.kitenet.net"
 	& JoeySites.podcatcher
 
-	& JoeySites.scrollBox
-	& alias "scroll.joeyh.name"
-	& alias "us.scroll.joeyh.name"
-
 	& alias "ns4.kitenet.net"
 	& myDnsPrimary "kitenet.net"
 		[ (RelDomain "mouse-onion", CNAME $ AbsDomain "htieo6yu2qtcn2j3.onion")
@@ -271,13 +271,12 @@
 		]
 	& myDnsPrimary "joeyh.name" []
 	& myDnsPrimary "ikiwiki.info" []
-	& myDnsPrimary "olduse.net"
+	! myDnsPrimary "olduse.net"
 		[ (RelDomain "article", CNAME $ AbsDomain "virgil.koldfront.dk")
 		]
 	! myDnsPrimary "quarantimer.app" []
 	& alias "ns4.branchable.com"
 	& branchableSecondary
-	& Dns.secondaryFor ["animx"] hosts "animx.eu.org"
 	-- Use its own name server (amoung other things this avoids
 	-- spamassassin URIBL_BLOCKED.
 	& "/etc/resolv.conf" `File.hasContent`
@@ -332,8 +331,6 @@
 
 	-- All the websites I host at branchable that don't use
 	-- branchable.com dns.
-	& alias "olduse.net"
-	& alias "www.olduse.net"
 	& alias "www.kitenet.net"
 	& alias "joeyh.name"
 	& alias "www.joeyh.name"
@@ -377,17 +374,6 @@
 	p = Port 1994
 	hn = "ancient.kitenet.net"
 
--- My own openid provider. Uses php, so containerized for security
--- and administrative sanity.
-openidProvider :: Systemd.Container
-openidProvider = Systemd.debContainer "openid-provider" $ props
-	-- simpleid is not in buster
-	& standardContainer (Stable "stretch")
-	& alias hn
-	& OpenId.providerFor [User "joey", User "liw"] hn (Just (Port 8086))
-  where
-	hn = "openid.kitenet.net"
-
 type Motd = [String]
 
 -- This is my standard system setup.
@@ -452,8 +438,6 @@
 		& Ssh.hostPubKey SshEd25519 "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB7yTEBGfQYdwG/oeL+U9XPMIh/dW7XNs9T+M79YIOrd"
 	, host "ns6.gandi.net" $ props
 		& ipv4 "217.70.177.40"
-	, host "animx" $ props
-		& ipv4 "76.7.174.49"
 	]
 
 
diff --git a/debian/changelog b/debian/changelog
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,33 @@
+propellor (5.14) unstable; urgency=medium
+
+  * Removed Propellor.Property.OpenID module, since the simpleid package
+    got removed from Debian.
+  * Renamed Linode.mlocateEnabled to Linode.locateEnabled, and made it also
+    make sure plocate is enabled, if it's installed rather than mlocate.
+    (API change)
+  * Debootstrap.built: Stop using the deprecated qemu-debootstrap
+    for bootstrapping foreign architectures. It is not needed since qemu
+    2.12, which, at least on Debian, enabled the 
+    binfmt-support --fix-binary option.
+  * Qemu.removeHostEmulationBinary: Removed this property, which is no
+    longer necessary since qemu-debootstrap is not used.
+    (API change)
+  * Flashkernel.installed: Deal with a behavior change in flash-kernel,
+    which will fail to install if is not configured first.
+  * Systemd.resolvConfed is no longer enabled by default in systemd
+    containers. It is probably not necessary on current systems the way it
+    was back in 2014, and this avoids a problem where the host's
+    /etc/resolv.conf gets overwritten when systemd is managing the
+    container's /etc/resolv.conf. 
+  * Borg: To support borg 1.2, run borg compact to free up space after pruning.
+  * Add lower bounds on async and split.
+    Thanks, Simon Jakobi
+  * propellor.cabal: Deduplicate exposed modules, which fixes a build
+    problem with recent cabal versions.
+    Thanks, Simon Jakobi
+
+ -- Joey Hess <id@joeyh.name>  Sat, 15 Oct 2022 14:40:31 -0400
+
 propellor (5.13) unstable; urgency=medium
 
   * Network: Added support for network interfaces with several address
diff --git a/executables/propellor-config.hs b/executables/propellor-config.hs
--- a/executables/propellor-config.hs
+++ b/executables/propellor-config.hs
@@ -25,7 +25,6 @@
 import qualified Propellor.Property.Locale as Locale
 import qualified Propellor.Property.Grub as Grub
 import qualified Propellor.Property.Borg as Borg
-import qualified Propellor.Property.OpenId as OpenId
 import qualified Propellor.Property.Systemd as Systemd
 import qualified Propellor.Property.Journald as Journald
 import qualified Propellor.Property.Fail2Ban as Fail2Ban
@@ -47,7 +46,7 @@
 	, dragon
 	, oyster
 	, orca
-	, honeybee
+	, house
 	, kite
 	, beaver
 	, sow
@@ -78,8 +77,8 @@
 	& Ssh.userKeys (User "joey") hostContext
 		[ (SshEd25519, "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICfFntnesZcYz2B2T41ay45igfckXRSh5uVffkuCQkLv joey@darkstar")
 		]
-	& imageBuiltFor honeybee
-		(RawDiskImage "/srv/honeybee.img")
+	& imageBuiltFor house
+		(RawDiskImage "/srv/house.img")
 		(Debootstrapped mempty)
 
 dragon :: Host
@@ -120,8 +119,8 @@
 		GitAnnexBuilder.standardAutoBuilder
 		Testing ARM64 Nothing (Cron.Times "1 * * * *") "4h")
 
-honeybee :: Host
-honeybee = host "honeybee.kitenet.net" $ props
+house :: Host
+house = host "house.lan" $ props
 	& standardSystem Testing ARMHF
 		[ "Home router and arm git-annex build box." ]
 	& Apt.removed ["rsyslog"]
@@ -134,7 +133,7 @@
 		)
 	& JoeySites.cubieTruckOneWire
 	& Systemd.persistentJournal
-	& Apt.installed ["firmware-misc-nonfree"] -- wifi
+	& Apt.installed ["firmware-atheros"]
 	& Apt.serviceInstalledRunning "ntp" -- no hardware clock
 	& bootstrappedFrom GitRepoOutsideChroot
 	& Ssh.hostKeys hostContext
@@ -149,9 +148,12 @@
 		hosts
 		(Context "house.joeyh.name")
 		(SshEd25519, "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMAmVYddg/RgCbIj+cLcEiddeFXaYFnbEJ3uGj9G/EyV joey@honeybee")
+	& JoeySites.connectStarlinkRouter
 	& JoeySites.homeRouter
 	& JoeySites.homeNAS
-	& Apt.installed ["mtr-tiny", "iftop", "screen"]
+	& Apt.installed ["mtr-tiny", "iftop", "screen", "nmap"]
+	-- Currently manually building the xr_usb_serial module.
+	& Apt.installed ["linux-headers-armmp-lpae"]
 	& Postfix.satellite
 
 	& check (not <$> hasContainerCapability Systemd.FilesystemContained) 
@@ -170,7 +172,10 @@
 kite = host "kite.kitenet.net" $ props
 	& standardSystemUnhardened Testing X86_64 [ "Welcome to kite!" ]
 	& ipv4 "66.228.36.95"
-	& ipv6 "2600:3c03::f03c:91ff:fe73:b0d2"
+	-- disabled because I've had complaints of mail bouncing, and it
+	-- seems mail servers are only trying ipv6 and then bouncing if
+	-- they have an ipv6 routing issue.
+	-- & ipv6 "2600:3c03::f03c:91ff:fe73:b0d2"
 	& alias "kitenet.net"
 	& Ssh.hostKeys (Context "kitenet.net")
 		[ (SshDsa, "ssh-dss AAAAB3NzaC1kc3MAAACBAO9tnPUT4p+9z7K6/OYuiBNHaij4Nzv5YVBih1vMl+ALz0gYAj8RWJzXmqp5buFAyfgOoLw+H9s1bBS01Sy3i07Dm6cx1fWG4RXL/E/3w1tavX99GD2bBxDBu890ebA5Tp+eFRJkS9+JwSvFiF6CP7NbVjifCagoUO56Ig048RwDAAAAFQDPY2xM3q6KwsVQliel23nrd0rV2QAAAIEAga3hj1hL00rYPNnAUzT8GAaSP62S4W68lusErH+KPbsMwFBFY/Ib1FVf8k6Zn6dZLh/HH/RtJi0JwdzPI1IFW+lwVbKfwBvhQ1lw9cH2rs1UIVgi7Wxdgfy8gEWxf+QIqn62wG+Ulf/HkWGvTrRpoJqlYRNS/gnOWj9Z/4s99koAAACBAM/uJIo2I0nK15wXiTYs/NYUZA7wcErugFn70TRbSgduIFH6U/CQa3rgHJw9DCPCQJLq7pwCnFH7too/qaK+czDk04PsgqV0+Jc7957gU5miPg50d60eJMctHV4eQ1FpwmGGfXxRBR9k2ZvikWYatYir3L6/x1ir7M0bA9IzNU45")
@@ -183,7 +188,7 @@
 	& Apt.installed ["linux-image-amd64"]
 	& Apt.serviceInstalledRunning "swapspace"
 	& Linode.serialGrub
-	& Linode.mlocateEnabled
+	& Linode.locateEnabled
 	& Apt.unattendedUpgrades
 	& Systemd.installed
 	& Systemd.persistentJournal
@@ -240,7 +245,7 @@
 	& Apt.installed
 		[ "git-annex", "myrepos"
 		, "build-essential", "make"
-		, "rss2email", "archivemail"
+		, "rss2email", "chewmail"
 		, "devscripts"
 		-- Some users have zsh as their login shell.
 		, "zsh"
@@ -253,15 +258,10 @@
 	& JoeySites.kgbServer
 	
 	& Systemd.nspawned ancientKitenet
-	! Systemd.nspawned openidProvider
 	
 	& alias "podcatcher.kitenet.net"
 	& JoeySites.podcatcher
 
-	& JoeySites.scrollBox
-	& alias "scroll.joeyh.name"
-	& alias "us.scroll.joeyh.name"
-
 	& alias "ns4.kitenet.net"
 	& myDnsPrimary "kitenet.net"
 		[ (RelDomain "mouse-onion", CNAME $ AbsDomain "htieo6yu2qtcn2j3.onion")
@@ -271,13 +271,12 @@
 		]
 	& myDnsPrimary "joeyh.name" []
 	& myDnsPrimary "ikiwiki.info" []
-	& myDnsPrimary "olduse.net"
+	! myDnsPrimary "olduse.net"
 		[ (RelDomain "article", CNAME $ AbsDomain "virgil.koldfront.dk")
 		]
 	! myDnsPrimary "quarantimer.app" []
 	& alias "ns4.branchable.com"
 	& branchableSecondary
-	& Dns.secondaryFor ["animx"] hosts "animx.eu.org"
 	-- Use its own name server (amoung other things this avoids
 	-- spamassassin URIBL_BLOCKED.
 	& "/etc/resolv.conf" `File.hasContent`
@@ -332,8 +331,6 @@
 
 	-- All the websites I host at branchable that don't use
 	-- branchable.com dns.
-	& alias "olduse.net"
-	& alias "www.olduse.net"
 	& alias "www.kitenet.net"
 	& alias "joeyh.name"
 	& alias "www.joeyh.name"
@@ -377,17 +374,6 @@
 	p = Port 1994
 	hn = "ancient.kitenet.net"
 
--- My own openid provider. Uses php, so containerized for security
--- and administrative sanity.
-openidProvider :: Systemd.Container
-openidProvider = Systemd.debContainer "openid-provider" $ props
-	-- simpleid is not in buster
-	& standardContainer (Stable "stretch")
-	& alias hn
-	& OpenId.providerFor [User "joey", User "liw"] hn (Just (Port 8086))
-  where
-	hn = "openid.kitenet.net"
-
 type Motd = [String]
 
 -- This is my standard system setup.
@@ -452,8 +438,6 @@
 		& Ssh.hostPubKey SshEd25519 "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB7yTEBGfQYdwG/oeL+U9XPMIh/dW7XNs9T+M79YIOrd"
 	, host "ns6.gandi.net" $ props
 		& ipv4 "217.70.177.40"
-	, host "animx" $ props
-		& ipv4 "76.7.174.49"
 	]
 
 
diff --git a/joeyconfig.hs b/joeyconfig.hs
--- a/joeyconfig.hs
+++ b/joeyconfig.hs
@@ -25,7 +25,6 @@
 import qualified Propellor.Property.Locale as Locale
 import qualified Propellor.Property.Grub as Grub
 import qualified Propellor.Property.Borg as Borg
-import qualified Propellor.Property.OpenId as OpenId
 import qualified Propellor.Property.Systemd as Systemd
 import qualified Propellor.Property.Journald as Journald
 import qualified Propellor.Property.Fail2Ban as Fail2Ban
@@ -47,7 +46,7 @@
 	, dragon
 	, oyster
 	, orca
-	, honeybee
+	, house
 	, kite
 	, beaver
 	, sow
@@ -78,8 +77,8 @@
 	& Ssh.userKeys (User "joey") hostContext
 		[ (SshEd25519, "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICfFntnesZcYz2B2T41ay45igfckXRSh5uVffkuCQkLv joey@darkstar")
 		]
-	& imageBuiltFor honeybee
-		(RawDiskImage "/srv/honeybee.img")
+	& imageBuiltFor house
+		(RawDiskImage "/srv/house.img")
 		(Debootstrapped mempty)
 
 dragon :: Host
@@ -120,8 +119,8 @@
 		GitAnnexBuilder.standardAutoBuilder
 		Testing ARM64 Nothing (Cron.Times "1 * * * *") "4h")
 
-honeybee :: Host
-honeybee = host "honeybee.kitenet.net" $ props
+house :: Host
+house = host "house.lan" $ props
 	& standardSystem Testing ARMHF
 		[ "Home router and arm git-annex build box." ]
 	& Apt.removed ["rsyslog"]
@@ -134,7 +133,7 @@
 		)
 	& JoeySites.cubieTruckOneWire
 	& Systemd.persistentJournal
-	& Apt.installed ["firmware-misc-nonfree"] -- wifi
+	& Apt.installed ["firmware-atheros"]
 	& Apt.serviceInstalledRunning "ntp" -- no hardware clock
 	& bootstrappedFrom GitRepoOutsideChroot
 	& Ssh.hostKeys hostContext
@@ -149,9 +148,12 @@
 		hosts
 		(Context "house.joeyh.name")
 		(SshEd25519, "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMAmVYddg/RgCbIj+cLcEiddeFXaYFnbEJ3uGj9G/EyV joey@honeybee")
+	& JoeySites.connectStarlinkRouter
 	& JoeySites.homeRouter
 	& JoeySites.homeNAS
-	& Apt.installed ["mtr-tiny", "iftop", "screen"]
+	& Apt.installed ["mtr-tiny", "iftop", "screen", "nmap"]
+	-- Currently manually building the xr_usb_serial module.
+	& Apt.installed ["linux-headers-armmp-lpae"]
 	& Postfix.satellite
 
 	& check (not <$> hasContainerCapability Systemd.FilesystemContained) 
@@ -170,7 +172,10 @@
 kite = host "kite.kitenet.net" $ props
 	& standardSystemUnhardened Testing X86_64 [ "Welcome to kite!" ]
 	& ipv4 "66.228.36.95"
-	& ipv6 "2600:3c03::f03c:91ff:fe73:b0d2"
+	-- disabled because I've had complaints of mail bouncing, and it
+	-- seems mail servers are only trying ipv6 and then bouncing if
+	-- they have an ipv6 routing issue.
+	-- & ipv6 "2600:3c03::f03c:91ff:fe73:b0d2"
 	& alias "kitenet.net"
 	& Ssh.hostKeys (Context "kitenet.net")
 		[ (SshDsa, "ssh-dss AAAAB3NzaC1kc3MAAACBAO9tnPUT4p+9z7K6/OYuiBNHaij4Nzv5YVBih1vMl+ALz0gYAj8RWJzXmqp5buFAyfgOoLw+H9s1bBS01Sy3i07Dm6cx1fWG4RXL/E/3w1tavX99GD2bBxDBu890ebA5Tp+eFRJkS9+JwSvFiF6CP7NbVjifCagoUO56Ig048RwDAAAAFQDPY2xM3q6KwsVQliel23nrd0rV2QAAAIEAga3hj1hL00rYPNnAUzT8GAaSP62S4W68lusErH+KPbsMwFBFY/Ib1FVf8k6Zn6dZLh/HH/RtJi0JwdzPI1IFW+lwVbKfwBvhQ1lw9cH2rs1UIVgi7Wxdgfy8gEWxf+QIqn62wG+Ulf/HkWGvTrRpoJqlYRNS/gnOWj9Z/4s99koAAACBAM/uJIo2I0nK15wXiTYs/NYUZA7wcErugFn70TRbSgduIFH6U/CQa3rgHJw9DCPCQJLq7pwCnFH7too/qaK+czDk04PsgqV0+Jc7957gU5miPg50d60eJMctHV4eQ1FpwmGGfXxRBR9k2ZvikWYatYir3L6/x1ir7M0bA9IzNU45")
@@ -183,7 +188,7 @@
 	& Apt.installed ["linux-image-amd64"]
 	& Apt.serviceInstalledRunning "swapspace"
 	& Linode.serialGrub
-	& Linode.mlocateEnabled
+	& Linode.locateEnabled
 	& Apt.unattendedUpgrades
 	& Systemd.installed
 	& Systemd.persistentJournal
@@ -240,7 +245,7 @@
 	& Apt.installed
 		[ "git-annex", "myrepos"
 		, "build-essential", "make"
-		, "rss2email", "archivemail"
+		, "rss2email", "chewmail"
 		, "devscripts"
 		-- Some users have zsh as their login shell.
 		, "zsh"
@@ -253,15 +258,10 @@
 	& JoeySites.kgbServer
 	
 	& Systemd.nspawned ancientKitenet
-	! Systemd.nspawned openidProvider
 	
 	& alias "podcatcher.kitenet.net"
 	& JoeySites.podcatcher
 
-	& JoeySites.scrollBox
-	& alias "scroll.joeyh.name"
-	& alias "us.scroll.joeyh.name"
-
 	& alias "ns4.kitenet.net"
 	& myDnsPrimary "kitenet.net"
 		[ (RelDomain "mouse-onion", CNAME $ AbsDomain "htieo6yu2qtcn2j3.onion")
@@ -271,13 +271,12 @@
 		]
 	& myDnsPrimary "joeyh.name" []
 	& myDnsPrimary "ikiwiki.info" []
-	& myDnsPrimary "olduse.net"
+	! myDnsPrimary "olduse.net"
 		[ (RelDomain "article", CNAME $ AbsDomain "virgil.koldfront.dk")
 		]
 	! myDnsPrimary "quarantimer.app" []
 	& alias "ns4.branchable.com"
 	& branchableSecondary
-	& Dns.secondaryFor ["animx"] hosts "animx.eu.org"
 	-- Use its own name server (amoung other things this avoids
 	-- spamassassin URIBL_BLOCKED.
 	& "/etc/resolv.conf" `File.hasContent`
@@ -332,8 +331,6 @@
 
 	-- All the websites I host at branchable that don't use
 	-- branchable.com dns.
-	& alias "olduse.net"
-	& alias "www.olduse.net"
 	& alias "www.kitenet.net"
 	& alias "joeyh.name"
 	& alias "www.joeyh.name"
@@ -377,17 +374,6 @@
 	p = Port 1994
 	hn = "ancient.kitenet.net"
 
--- My own openid provider. Uses php, so containerized for security
--- and administrative sanity.
-openidProvider :: Systemd.Container
-openidProvider = Systemd.debContainer "openid-provider" $ props
-	-- simpleid is not in buster
-	& standardContainer (Stable "stretch")
-	& alias hn
-	& OpenId.providerFor [User "joey", User "liw"] hn (Just (Port 8086))
-  where
-	hn = "openid.kitenet.net"
-
 type Motd = [String]
 
 -- This is my standard system setup.
@@ -452,8 +438,6 @@
 		& Ssh.hostPubKey SshEd25519 "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB7yTEBGfQYdwG/oeL+U9XPMIh/dW7XNs9T+M79YIOrd"
 	, host "ns6.gandi.net" $ props
 		& ipv4 "217.70.177.40"
-	, host "animx" $ props
-		& ipv4 "76.7.174.49"
 	]
 
 
diff --git a/propellor.cabal b/propellor.cabal
--- a/propellor.cabal
+++ b/propellor.cabal
@@ -1,5 +1,5 @@
 Name: propellor
-Version: 5.13
+Version: 5.14
 Cabal-Version: 1.20
 License: BSD2
 Maintainer: Joey Hess <id@joeyh.name>
@@ -47,9 +47,10 @@
     -- propellor needs to support the ghc shipped in Debian stable,
     -- and also only depends on packages in Debian stable.
     base >= 4.9, base < 5,
-    directory, filepath, IfElse, process, bytestring, hslogger, split,
-    unix, unix-compat, ansi-terminal, containers (>= 0.5), network, async,
-    time, mtl, transformers, exceptions (>= 0.6), stm, text, hashable
+    directory, filepath, IfElse, process, bytestring, hslogger,
+    split (>= 0.2.0.0), unix, unix-compat, ansi-terminal, containers (>= 0.5),
+    network, async (>= 2.0.0.0), time, mtl, transformers, exceptions (>= 0.6),
+    stm, text, hashable
   if flag(WithTypeErrors)
     Build-Depends: type-errors
     CPP-Options: -DWITH_TYPE_ERRORS
@@ -117,7 +118,6 @@
     Propellor.Property.Network
     Propellor.Property.Nginx
     Propellor.Property.Obnam
-    Propellor.Property.OpenId
     Propellor.Property.Openssl
     Propellor.Property.OS
     Propellor.Property.Pacman
@@ -223,7 +223,6 @@
     Utility.Process.NonConcurrent
     Utility.Process.Transcript
     Utility.SafeCommand
-    Utility.Scheduled
     Utility.Scheduled
     Utility.Split
     Utility.SystemDirectory
diff --git a/src/Propellor/Property/Borg.hs b/src/Propellor/Property/Borg.hs
--- a/src/Propellor/Property/Borg.hs
+++ b/src/Propellor/Property/Borg.hs
@@ -225,7 +225,7 @@
 	backupcmd = intercalate "&&" $ concat
 		[ concatMap exportenv (runBorgEnv repo)
 		, [createCommand]
-		, if null kp then [] else [pruneCommand]
+		, if null kp then [] else [pruneCommand, compactCommand]
 		]
 	exportenv (k, v) = 
 		[ k ++ "=" ++ shellEscape v
@@ -238,10 +238,17 @@
 		[ Param (repoLoc repo ++ "::{now}")
 		, File dir
 		]
-	pruneCommand = unwords $ ("borg" : pruneCommandParams)
+	pruneCommand = unwords ("borg" : pruneCommandParams)
 	pruneCommandParams = map shellEscape $ toCommand $ runBorgParam repo "prune" $
 		[ Param (repoLoc repo) ]
 		++ map keepParam kp
+	-- borg compact is needed after version 1.2 to actually free
+	-- pruned space, but is not supported by older versions.
+	compactCommand = "(" ++ unwords ("borg" : compactCommandParams)
+		++ " 2>/dev/null || true)"
+	compactCommandParams = 
+		map shellEscape $ toCommand $ runBorgParam repo "compact" $
+			[ Param (repoLoc repo) ]
 
 -- | Constructs an BorgParam that specifies which old backup generations to
 -- keep. By default, all generations are kept. However, when this parameter is
diff --git a/src/Propellor/Property/Debootstrap.hs b/src/Propellor/Property/Debootstrap.hs
--- a/src/Propellor/Property/Debootstrap.hs
+++ b/src/Propellor/Property/Debootstrap.hs
@@ -77,10 +77,6 @@
 -- When the System is architecture that the kernel does not support,
 -- it can still be bootstrapped using emulation. This is determined
 -- by checking `supportsArch`, or can be configured with `UseEmulation`.
---
--- When emulation is used, the chroot will have an additional binary 
--- installed in it. To get a completelty clean chroot (eg for producing a
--- bootable disk image), use the `removeHostEmulationBinary` property.
 built :: FilePath -> System -> DebootstrapConfig -> Property Linux
 built target system@(System _ targetarch) config =
 	withOS ("debootstrapped " ++ target) go
@@ -116,9 +112,7 @@
 			] ++ case debootstrapMirror config of
 				Just u -> [Param u]
 				Nothing -> []
-		cmd <- if useEmulation config
-			then pure "qemu-debootstrap"
-			else fromMaybe "debootstrap" <$> programPath
+		cmd <- fromMaybe "debootstrap" <$> programPath
 		de <- case debootstrapProxy config of
 			Just u -> addEntry "http_proxy" u <$> standardPathEnv
 			Nothing -> standardPathEnv
diff --git a/src/Propellor/Property/DiskImage.hs b/src/Propellor/Property/DiskImage.hs
--- a/src/Propellor/Property/DiskImage.hs
+++ b/src/Propellor/Property/DiskImage.hs
@@ -32,7 +32,6 @@
 import qualified Propellor.Property.Grub as Grub
 import qualified Propellor.Property.File as File
 import qualified Propellor.Property.Apt as Apt
-import qualified Propellor.Property.Qemu as Qemu
 import qualified Propellor.Property.FlashKernel as FlashKernel
 import Propellor.Property.Parted
 import Propellor.Property.Fstab (SwapPartition(..), genFstab)
@@ -413,7 +412,6 @@
 		liftIO $ allowservices top
 		ensureProperty w $ 
 			final img top devs
-				`before` Qemu.removeHostEmulationBinary top
 
 	-- Ordered lexographically by mount point, so / comes before /usr
 	-- comes before /usr/local
diff --git a/src/Propellor/Property/FlashKernel.hs b/src/Propellor/Property/FlashKernel.hs
--- a/src/Propellor/Property/FlashKernel.hs
+++ b/src/Propellor/Property/FlashKernel.hs
@@ -22,10 +22,11 @@
 installed :: Machine -> Property (HasInfo + DebianLike)
 installed machine = setInfoProperty go (toInfo [FlashKernelInstalled])
   where
-	go = "/etc/flash-kernel/machine" `File.hasContent` [machine]
+	go = Apt.installed ["flash-kernel"]
+		`requires` configured
 		`onChange` flashKernel
+	configured = ("/etc/flash-kernel/machine" `File.hasContent` [machine])
 		`requires` File.dirExists "/etc/flash-kernel"
-		`requires` Apt.installed ["flash-kernel"]
 
 -- | Runs flash-kernel with whatever machine `installed` configured.
 flashKernel :: Property DebianLike
diff --git a/src/Propellor/Property/HostingProvider/Linode.hs b/src/Propellor/Property/HostingProvider/Linode.hs
--- a/src/Propellor/Property/HostingProvider/Linode.hs
+++ b/src/Propellor/Property/HostingProvider/Linode.hs
@@ -25,9 +25,14 @@
 chainPVGrub = Grub.chainPVGrub "hd0" "xen/xvda"
 
 -- | Linode disables mlocate's cron job's execute permissions,
--- presumably to avoid disk IO. This ensures it's executable.
-mlocateEnabled :: Property DebianLike
-mlocateEnabled = tightenTargets $
-	"/etc/cron.daily/mlocate"
-		`File.mode` combineModes (readModes ++ executeModes)
-
+-- presumably to avoid disk IO. This ensures it's executable,
+-- if it's installed. It does the same for its replacement plocate,
+-- in cae Linode starts messing with that.
+locateEnabled :: Property DebianLike
+locateEnabled = tightenTargets $
+	propertyList "locate enabled" $ props
+		& go "/etc/cron.daily/mlocate"
+		& go "/etc/cron.daily/plocate"
+  where
+	go f = check (doesFileExist f)
+			(f `File.mode` combineModes (readModes ++ executeModes))
diff --git a/src/Propellor/Property/OpenId.hs b/src/Propellor/Property/OpenId.hs
deleted file mode 100644
--- a/src/Propellor/Property/OpenId.hs
+++ /dev/null
@@ -1,50 +0,0 @@
-module Propellor.Property.OpenId where
-
-import Propellor.Base
-import qualified Propellor.Property.File as File
-import qualified Propellor.Property.Apt as Apt
-import qualified Propellor.Property.Apache as Apache
-
-import Data.List
-
--- | Openid provider, using the simpleid PHP CGI, with apache.
---
--- Runs on usual port by default. When a nonstandard port is specified,
--- apache is limited to listening only on that port. Warning: Specifying
--- a port won't compose well with other apache properties on the same
--- host.
---
--- It's probably a good idea to put this property inside a docker or
--- systemd-nspawn container.
-providerFor :: [User] -> HostName -> Maybe Port -> Property (HasInfo + DebianLike)
-providerFor users hn mp = propertyList desc $ props
-	& Apt.serviceInstalledRunning "apache2"
-	& apacheconfigured
-	& Apt.installed ["simpleid"]
-		`onChange` Apache.restarted
-	& File.fileProperty (desc ++ " configured")
-		(map setbaseurl) "/etc/simpleid/config.inc"
-	& propertyList desc (toProps $ map identfile users)
-  where
-	baseurl = hn ++ case mp of
-		Nothing -> ""
-		Just p -> ':' : val p
-	url = "http://"++baseurl++"/simpleid"
-	desc = "openid provider " ++ url
-	setbaseurl l
-		| "SIMPLEID_BASE_URL" `isInfixOf` l =
-			"define('SIMPLEID_BASE_URL', '"++url++"');"
-		| otherwise = l
-
-	apacheconfigured = case mp of
-		Nothing -> setupRevertableProperty $
-			Apache.virtualHost hn (Port 80) "/var/www/html"
-		Just p -> propertyList desc $ props
-			& Apache.listenPorts [p]
-			& Apache.virtualHost hn p "/var/www/html"
-
-	-- the identities directory controls access, so open up
-	-- file mode
-	identfile (User u) = File.hasPrivContentExposed
-		(concat [ "/var/lib/simpleid/identities/", u, ".identity" ])
-		(Context baseurl)
diff --git a/src/Propellor/Property/Qemu.hs b/src/Propellor/Property/Qemu.hs
--- a/src/Propellor/Property/Qemu.hs
+++ b/src/Propellor/Property/Qemu.hs
@@ -5,11 +5,6 @@
 
 -- | Installs qemu user mode emulation binaries, built statically,
 -- which allow foreign binaries to run directly.
---
--- Note that this is not necessary after qemu 2.12~rc3+dfsg-1.
--- See http://bugs.debian.org/868030
--- It's currently always done to support older versions, but
--- could be skipped with the newer version.
 foreignBinariesEmulated :: RevertableProperty Linux Linux
 foreignBinariesEmulated = (setup <!> cleanup)
 	`describe` "foreign binary emulation"
@@ -17,19 +12,6 @@
 	setup = Apt.installed p `pickOS` unsupportedOS
 	cleanup = Apt.removed p `pickOS` unsupportedOS
 	p = ["qemu-user-static"]
-
--- | Removes qemu user mode emulation binary for the host CPU.
--- This binary is copied into a chroot by qemu-debootstrap, and is not
--- part of any package.
---
--- Note that removing the binary will prevent using the chroot on the host
--- system.
---
--- The FilePath is the path to the top of the chroot.
-removeHostEmulationBinary :: FilePath -> Property Linux
-removeHostEmulationBinary top = tightenTargets $ 
-	scriptProperty ["rm -f " ++ top ++ "/usr/bin/qemu-*-static"]
-		`assume` MadeChange
 
 -- | Check if the given System supports an Architecture.
 --
diff --git a/src/Propellor/Property/SiteSpecific/GitAnnexBuilder.hs b/src/Propellor/Property/SiteSpecific/GitAnnexBuilder.hs
--- a/src/Propellor/Property/SiteSpecific/GitAnnexBuilder.hs
+++ b/src/Propellor/Property/SiteSpecific/GitAnnexBuilder.hs
@@ -183,7 +183,9 @@
 		& baseautobuilder suite arch flavor
 		-- Works around ghc crash with parallel builds on arm.
 		& File.dirExists (homedir </> ".cabal")
+		& File.ownerGroup (homedir </> ".cabal") (User "builder") (Group "builder")
 		& (homedir </> ".cabal" </> "config")
 			`File.containsLine` "jobs: 1"
+		& File.ownerGroup (homedir </> ".cabal" </> "config") (User "builder") (Group "builder")
 		-- Work around https://github.com/systemd/systemd/issues/7135
 		& Systemd.containerCfg "--system-call-filter=set_tls"
diff --git a/src/Propellor/Property/SiteSpecific/JoeySites.hs b/src/Propellor/Property/SiteSpecific/JoeySites.hs
--- a/src/Propellor/Property/SiteSpecific/JoeySites.hs
+++ b/src/Propellor/Property/SiteSpecific/JoeySites.hs
@@ -30,73 +30,6 @@
 import Data.List
 import System.Posix.Files
 
-scrollBox :: Property (HasInfo + DebianLike)
-scrollBox = propertyList "scroll server" $ props
-	& User.accountFor (User "scroll")
-	& Git.cloned (User "scroll") "git://git.kitenet.net/scroll" (d </> "scroll") Nothing
-	& Apt.installed ["ghc", "make", "cabal-install", "libghc-vector-dev",
-		"libghc-bytestring-dev", "libghc-mtl-dev", "libghc-ncurses-dev",
-		"libghc-random-dev", "libghc-monad-loops-dev", "libghc-text-dev",
-		"libghc-ifelse-dev", "libghc-case-insensitive-dev",
-		"libghc-data-default-dev", "libghc-optparse-applicative-dev"]
-	& userScriptProperty (User "scroll")
-		[ "cd " ++ d </> "scroll"
-		, "git pull"
-		, "cabal configure"
-		, "make"
-		]
-		`assume` MadeChange
-	& s `File.hasContent`
-		[ "#!/bin/sh"
-		, "set -e"
-		, "echo Preparing to run scroll!"
-		, "cd " ++ d
-		, "mkdir -p tmp"
-		, "TMPDIR= t=$(tempfile -d tmp)"
-		, "export t"
-		, "rm -f \"$t\""
-		, "mkdir \"$t\""
-		, "cd \"$t\""
-		, "echo"
-		, "echo Note that games on this server are time-limited to 2 hours"
-		, "echo 'Need more time? Run scroll locally instead!'"
-		, "echo"
-		, "echo Press Enter to start the game."
-		, "read me"
-		, "SHELL=/bin/sh script --timing=timing -c " ++ g
-		] `onChange` (s `File.mode` (combineModes (ownerWriteMode:readModes ++ executeModes)))
-	& g `File.hasContent`
-		[ "#!/bin/sh"
-		, "if ! timeout --kill-after 1m --foreground 2h ../../scroll/scroll; then"
-		, "echo Scroll seems to have ended unexpectedly. Possibly a bug.."
-		, "else"
-		, "echo Thanks for playing scroll! https://joeyh.name/code/scroll/"
-		, "fi"
-		, "echo Your game was recorded, as ID:$(basename \"$t\")"
-		, "echo if you would like to talk about how it went, email scroll@joeyh.name"
-		, "read line"
-		] `onChange` (g `File.mode` (combineModes (ownerWriteMode:readModes ++ executeModes)))
-	-- prevent port forwarding etc by not letting scroll log in via ssh
-	& Ssh.sshdConfig `File.containsLine` ("DenyUsers scroll")
-		`onChange` Ssh.restarted
-	& User.shellSetTo (User "scroll") s
-	& User.hasPassword (User "scroll")
-	-- telnetd attracted password crackers, so disabled
-	& Apt.removed ["telnetd"]
-	& Apt.installed ["shellinabox"]
-	& File.hasContent "/etc/default/shellinabox"
-		[ "# Deployed by propellor"
-		, "SHELLINABOX_DAEMON_START=1"
-		, "SHELLINABOX_PORT=4242"
-		, "SHELLINABOX_ARGS=\"--disable-ssl --no-beep --service=:scroll:scroll:" ++ d ++ ":" ++ s ++ "\""
-		]
-		`onChange` Service.restarted "shellinabox"
-	& Service.running "shellinabox"
-  where
-	d = "/home/scroll"
-	s = d </> "login.sh"
-	g = d </> "game.sh"
-
 kgbServer :: Property (HasInfo + DebianLike)
 kgbServer = propertyList desc $ props
 	& Apt.serviceInstalledRunning "kgb-bot"
@@ -312,7 +245,7 @@
 podcatcher :: Property DebianLike
 podcatcher = Cron.niceJob "podcatcher run hourly" (Cron.Times "55 * * * *")
 	(User "joey") "/home/joey/lib/sound/podcasts"
-	"xargs git-annex importfeed -c annex.genmetadata=true < feeds; mr --quiet update"
+	"timeout 2h xargs git-annex importfeed -c annex.genmetadata=true < feeds; mr --quiet update"
 	`requires` Apt.installed ["git-annex", "myrepos"]
 
 spamdEnabled :: Property DebianLike
@@ -457,7 +390,7 @@
 		, "smtpd_tls_received_header = yes"
 		, "smtpd_use_tls = yes"
 		, "smtpd_tls_ask_ccert = yes"
-		, "smtpd_tls_session_cache_database = sdbm:/etc/postfix/smtpd_scache"
+		, "smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache"
 
 		, "# TLS setup -- client"
 		, "smtp_tls_CAfile = /etc/ssl/certs/joeyca.pem"
@@ -465,7 +398,7 @@
 		, "smtp_tls_key_file = /etc/ssl/private/postfix.pem"
 		, "smtp_tls_loglevel = 1"
 		, "smtp_use_tls = yes"
-		, "smtp_tls_session_cache_database = sdbm:/etc/postfix/smtp_scache"
+		, "smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache"
 
 		, "# Allow larger attachments, up to 200 mb."
 		, "# (Avoid setting too high; the postfix queue must have"
@@ -520,13 +453,6 @@
 		`onChange` (imapalpinescript `File.mode`
 			combineModes (readModes ++ executeModes))
 		`describe` "imap script for pine"
-	-- XXX temporarily disabled installing as it's not available in
-	-- debian unstable any longer. Need to upgrade to mailman3
-	-- at some point. (nontrivial)
-	-- & Apt.serviceInstalledRunning "mailman"
-	-- Override the default http url. (Only affects new lists.)
-	& "/etc/mailman/mm_cfg.py" `File.containsLine`
-		"DEFAULT_URL_PATTERN = 'https://%s/cgi-bin/mailman/'"
 
 	& Postfix.service ssmtp
 
@@ -592,7 +518,7 @@
 		, "smtp_sasl_auth_enable = yes"
 		, "smtp_tls_security_level = encrypt"
 		, "smtp_sasl_tls_security_options = noanonymous"
-		, "relayhost = [kitenet.net]"
+		, "relayhost = kitenet.net:587"
 		, "smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd"
 		]
 		`onChange` Postfix.reloaded
@@ -697,27 +623,6 @@
 		, "  AllowOverride None"
 		, Apache.allowAll
 		, "</Directory>"
-		, "ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/"
-
-		-- for mailman cgi scripts
-		, "<Directory /usr/lib/cgi-bin>"
-		, "  AllowOverride None"
-		, "  Options ExecCGI"
-		, Apache.allowAll
-		, "</Directory>"
-		, "Alias /pipermail/ /var/lib/mailman/archives/public/"
-		, "<Directory /var/lib/mailman/archives/public/>"
-		, "  Options Indexes MultiViews FollowSymlinks"
-		, "  AllowOverride None"
-		, Apache.allowAll
-		, "</Directory>"
-		, "Alias /images/ /usr/share/images/"
-		, "<Directory /usr/share/images/>"
-		, "  Options Indexes MultiViews"
-		, "  AllowOverride None"
-		, Apache.allowAll
-		, "</Directory>"
-
 		, "RewriteEngine On"
 		, "# Force hostname to kitenet.net"
 		, "RewriteCond %{HTTP_HOST} !^kitenet\\.net [NC]"
@@ -753,7 +658,6 @@
 		, "# Old ikiwiki filenames for kitenet.net wiki."
 		, "rewritecond $1 !^/~"
 		, "rewritecond $1 !^/doc/"
-		, "rewritecond $1 !^/pipermail/"
 		, "rewritecond $1 !^/cgi-bin/"
 		, "rewritecond $1 !.*/index$"
 		, "rewriterule (.+).html$ $1/ [r]"
@@ -829,8 +733,6 @@
 	& Apache.installed
 	& Apt.installed ["libmodbus-dev", "rrdtool", "rsync"]
 	& Git.cloned user "https://git.joeyh.name/git/joey/house.git" d Nothing
-	& Git.cloned user "https://git.joeyh.name/git/reactive-banana-automation.git" (d </> "reactive-banana-automation") Nothing
-	& Git.cloned user "https://git.joeyh.name/git/haskell-libmodbus.git" (d </> "haskell-libmodbus") Nothing
 	& websitesymlink
 	& build
 	& Systemd.enabled setupservicename
@@ -846,6 +748,20 @@
 		`requires` watchdogserviceinstalled
 		`onChange` Systemd.started watchdogservicename
 	& Apt.serviceInstalledRunning "watchdog"
+	& "/etc/watchdog.conf" `File.containsLines`
+		[ "watchdog-device = /dev/watchdog0"
+		, "watchdog-timeout = 16" -- maximum supported by cubietruck
+		, "interval = 1"
+		]
+		`onChange` Service.reloaded "watchdog"
+	-- Hardcode one of the pool server IPs because ntp only retries
+	-- DNS resolution after an hour when it's down initially due to
+	-- starlink not being up.
+	& "/etc/ntp.conf" `File.containsLines`
+		[ "server 50.205.244.20"
+		]
+		`onChange` Service.reloaded "ntpsec"
+	-- Comes after so it does not set relayhost but uses the setting 
 	& User.hasGroup user (Group "dialout")
 	& Group.exists (Group "gpio") Nothing
 	& User.hasGroup user (Group "gpio")
@@ -860,16 +776,14 @@
 		`requires` File.dirExists (takeDirectory sshkeyfile)
 		`requires` Ssh.knownHost hosts "kitenet.net" user
 	& File.hasPrivContentExposed "/etc/darksky-forecast-url" anyContext
+	& File.containsLine "/etc/dhcp/dhclient.conf" "send host-name = \"house\";"
   where
 	d = "/home/joey/house"
 	sshkeyfile = d </> ".ssh/key"
 	build = check (not <$> doesFileExist (d </> "controller")) $
 		userScriptProperty (User "joey")
-			[ "cd " ++ d </> "reactive-banana-automation"
-			, "cabal install"
-			, "cd " ++ d </> "haskell-libmodbus"
-			, "cabal install"
-			, "cd " ++ d
+			[ "cd " ++ d
+			, "cabal update"
 			, "make"
 			]
 		`assume` MadeChange
@@ -964,25 +878,28 @@
 		)
 
 homerouterWifiInterface :: String
-homerouterWifiInterface = "wlx9cefd5fcd6f3"
+homerouterWifiInterface = "wlx00c0ca82eb78"
 
 homerouterWifiInterfaceOld :: String
-homerouterWifiInterfaceOld = "wlx7cdd90753b9f"
+homerouterWifiInterfaceOld = "wlx00c0cab064eb"
 
--- My home router, running hostapd and dnsmasq,
--- with eth0 connected to a satellite modem, and a fallback ppp connection.
-homeRouter :: Property (HasInfo + DebianLike)
+-- Connect to the starlink router with its ethernet adapter.
+connectStarlinkRouter :: Property DebianLike
+connectStarlinkRouter = propertyList "connected via starlink router" $ props
+	& Network.dhcp "eth0"
+		`requires` Network.cleanInterfacesFile
+
+-- My home router, running hostapd and dnsmasq.
+homeRouter :: Property DebianLike
 homeRouter = propertyList "home router" $ props
 	& File.notPresent (Network.interfaceDFile homerouterWifiInterfaceOld)
 	& Network.static homerouterWifiInterface (IPv4 "10.1.1.1") Nothing
 		`requires` Network.cleanInterfacesFile
 	& Apt.installed ["hostapd"]
 	& File.hasContent "/etc/hostapd/hostapd.conf"
-			[ "interface=" ++ homerouterWifiInterface
+			([ "interface=" ++ homerouterWifiInterface
 			, "ssid=house"
-			, "hw_mode=g"
-			, "channel=8"
-			]
+			] ++ hostapd2GhzConfig)
 		`requires` File.dirExists "/etc/hostapd"
 		`requires` File.hasContent "/etc/default/hostapd"
 			[ "DAEMON_CONF=/etc/hostapd/hostapd.conf" ]
@@ -1000,42 +917,20 @@
 		, "bogus-priv"
 		, "interface=" ++ homerouterWifiInterface
 		, "interface=eth0"
-		, "domain=kitenet.net"
+		, "domain=lan"
 		-- lease time is short because the house
 		-- controller wants to know when clients disconnect
 		, "dhcp-range=10.1.1.100,10.1.1.150,10m"
 		, "no-hosts"
 		, "address=/honeybee.kitenet.net/10.1.1.1"
-		, "address=/house.kitenet.net/10.1.1.1"
-		, "dhcp-host=0c:98:38:80:6a:f9,10.1.1.134,android-kodama"
+		, "address=/house.lan/10.1.1.1"
 		]
 		`onChange` Service.restarted "dnsmasq"
+	-- Avoid DHCPNAK of lease obtained at boot, after NTP slews clock
+	-- forward too far, causing that least to not be valid.
+	& "/etc/default/dnsmasq" `File.containsLine` "DNSMASQ_OPTS=\"--dhcp-authoritative\""
+		`onChange` Service.restarted "dnsmasq"
 	& ipmasq homerouterWifiInterface
-	& Network.static' "eth0" (IPv4 "192.168.1.100")
-		(Just (Network.Gateway (IPv4 "192.168.1.1")))
-		-- When satellite is down, fall back to dialup
-		[ ("pre-up", "poff -a || true")
-		, ("post-down", "pon")
-		-- ethernet autonegotiation with satellite receiver 
-		-- sometimes fails
-		, ("ethernet-autoneg", "off")
-		, ("link-speed", "100")
-		, ("link-duplex", "full")
-		]
-		`requires` Network.cleanInterfacesFile
-		`requires` Apt.installed ["ethtool"]
-	& Apt.installed ["ppp"]
-		`before` File.hasContent "/etc/ppp/peers/provider"
-			[ "user \"joeyh@arczip.com\""
-			, "connect \"/usr/sbin/chat -v -f /etc/chatscripts/pap -T 3825441\""
-			, "/dev/ttyACM0"
-			, "115200"
-			, "noipdefault"
-			, "defaultroute"
-			, "persist"
-			, "noauth"
-			]
-		`before` File.hasPrivContent "/etc/ppp/pap-secrets" (Context "joeyh@arczip.com")
 
 -- | Enable IP masqerading, on whatever other interfaces come up, besides the
 -- provided intif.
@@ -1053,16 +948,9 @@
 	, "echo 1 > /proc/sys/net/ipv4/ip_forward"
 	]
 	`before` scriptmode ifupscript
-	`before` File.dirExists (takeDirectory pppupscript)
-	`before` File.hasContent pppupscript
-		[ "#!/bin/sh"
-		, "IFACE=$PPP_IFACE " ++ ifupscript
-		]
-	`before` scriptmode pppupscript
 	`requires` Apt.installed ["iptables"]
   where
 	ifupscript = "/etc/network/if-up.d/ipmasq"
-	pppupscript = "/etc/ppp/ip-up.d/ipmasq"
 	scriptmode f = f `File.mode` combineModes (readModes ++ executeModes)
 
 laptopSoftware :: Property DebianLike
@@ -1126,7 +1014,7 @@
 	dtsinstalled = File.hasContent "/etc/easy-peasy-devicetree-squeezy/my.dts" mydts
 		`requires` File.dirExists "/etc/easy-peasy-devicetree-squeezy"
 	utilityinstalled = Git.cloned (User "root") "https://git.joeyh.name/git/easy-peasy-devicetree-squeezy.git" "/usr/local/easy-peasy-devicetree-squeezy" Nothing
-		`onChange` File.isSymlinkedTo "/usr/local/bin/easy-peasy-devicetree-squeezy" (File.LinkTarget "/usr/local/easy-peasy-devicetree-squeezy/easy-peasy-devicetree-squeezy")
+		`onChange` File.isSymlinkedTo "/usr/sbin/easy-peasy-devicetree-squeezy" (File.LinkTarget "/usr/local/easy-peasy-devicetree-squeezy/easy-peasy-devicetree-squeezy")
 		`requires` Apt.installed ["pv", "device-tree-compiler", "cpp", "linux-source"]
 	utilitysetup = check (not <$> doesFileExist dtb) $ 
 		cmdProperty "easy-peasy-devicetree-squeezy"
@@ -1318,3 +1206,21 @@
 noExim :: Property DebianLike
 noExim = Apt.removed ["exim4", "exim4-base", "exim4-daemon-light"]
 	`onChange` Apt.autoRemove
+
+hostapd2GhzConfig :: [String]
+hostapd2GhzConfig = 
+	[ "hw_mode=g"
+	, "channel=8"
+	]
+
+hostapd5GhzConfig :: [String]
+hostapd5GhzConfig = 
+	[ "hw_mode=a"
+	, "channel=36"
+	, "country_code=US"
+	, "ieee80211d=1"
+	, "ieee80211n=1"
+	, "ieee80211ac=1"
+	, "wmm_enabled=1"
+	]
+
diff --git a/src/Propellor/Property/Systemd.hs b/src/Propellor/Property/Systemd.hs
--- a/src/Propellor/Property/Systemd.hs
+++ b/src/Propellor/Property/Systemd.hs
@@ -231,7 +231,6 @@
 container name mkchroot =
 	let c = Container name chroot h
 	in setContainerProps c $ containerProps c
-		&^ resolvConfed
 		&^ linkJournal
   where
 	chroot = mkchroot (containerDir name)
@@ -403,7 +402,10 @@
 
 -- | Bind mounts </etc/resolv.conf> from the host into the container.
 --
--- This property is enabled by default. Revert it to disable it.
+-- This is not necessary when systemd configures the container's
+-- resolv.conf on its own. This used to be enabled by default, but when
+-- systemd did also configure the container's resolv.conf, that could
+-- modify the host's resolv.conf.
 resolvConfed :: RevertableProperty (HasInfo + Linux) (HasInfo + Linux)
 resolvConfed = containerCfg "bind=/etc/resolv.conf"
 
