diff --git a/CHANGELOG b/CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,10 @@
+propellor (5.17) unstable; urgency=medium
+
+  * Apt: Enable the non-free-firmware section, when used with Debian
+    bookworm or newer.
+
+ -- Joey Hess <id@joeyh.name>  Sat, 10 Jun 2023 09:49:05 -0400
+
 propellor (5.16) unstable; urgency=medium
 
   * Debootstrap: Added UseOldGpgKeyring constructor to DebootstrapConfig,
diff --git a/config-freebsd.hs b/config-freebsd.hs
--- a/config-freebsd.hs
+++ b/config-freebsd.hs
@@ -59,7 +59,7 @@
 -- A generic webserver in a Docker container.
 webserverContainer :: Docker.Container
 webserverContainer = Docker.container "webserver" (Docker.latestImage "debian") $ props
-	& osDebian' KFreeBSD (Stable "buster") X86_64
+	& osDebian' KFreeBSD (Stable "bookworm") X86_64
 	& Apt.stdSourcesList
 	& Docker.publish "80:80"
 	& Docker.volume "/var/www:/var/www"
diff --git a/debian/changelog b/debian/changelog
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+propellor (5.17) unstable; urgency=medium
+
+  * Apt: Enable the non-free-firmware section, when used with Debian
+    bookworm or newer.
+
+ -- Joey Hess <id@joeyh.name>  Sat, 10 Jun 2023 09:49:05 -0400
+
 propellor (5.16) unstable; urgency=medium
 
   * Debootstrap: Added UseOldGpgKeyring constructor to DebootstrapConfig,
diff --git a/joeyconfig.hs b/joeyconfig.hs
--- a/joeyconfig.hs
+++ b/joeyconfig.hs
@@ -48,6 +48,7 @@
 	, dragon
 	, oyster
 	, house
+	, sky
 	, kite
 	, sparrow
 	, beaver
@@ -101,8 +102,7 @@
 
 house :: Host
 house = host "house.lan" $ props
-	& standardSystem Testing ARMHF
-		[ "Home router and arm git-annex build box." ]
+	& standardSystem Testing ARMHF [ "House mail computer." ]
 	& Apt.removed ["rsyslog"]
 	
 	& cubietech_Cubietruck
@@ -118,7 +118,7 @@
 	& JoeySites.cubieTruckOneWire
 	& Systemd.persistentJournal
 	& Apt.installed ["firmware-atheros"]
-	& Apt.serviceInstalledRunning "ntp" -- no hardware clock
+	& Apt.serviceInstalledRunning "systemd-timesyncd" -- no hardware clock
 	& bootstrappedFrom GitRepoOutsideChroot
 	& Ssh.hostKeys hostContext
 		[ (SshEd25519, "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIS/hDYq1MAxfOBf49htym3BOYlx4Gk9SDpiHjv7u6IC")
@@ -132,13 +132,56 @@
 		hosts
 		(Context "house.joeyh.name")
 		(SshEd25519, "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMAmVYddg/RgCbIj+cLcEiddeFXaYFnbEJ3uGj9G/EyV joey@honeybee")
-	& JoeySites.connectStarlinkDish
-	& JoeySites.homeRouter
+	
+	-- Use iwd to connect to wifi on whatever usb wifi is connected.
+	& Apt.installed ["iwd"]
+	& File.hasContent "/var/lib/iwd/.known_network.freq"
+		[ "[7085c93b-9e5f-5cdb-a13d-bf72db7c3adf]"
+		, "name=/var/lib/iwd//hollow.open"
+		, "list= 2457"
+		]
+	& File.hasContent "/var/lib/iwd/hollow.open"
+		[ "[IPv4]"
+		, "Address=10.1.1.2"
+		, "Netmask=255.255.255.0"
+		, "Gateway=10.1.1.1"
+		, "Broadcast=10.1.1.255"
+		, "DNS=10.1.1.1"
+		]
+	& Systemd.enabled "iwd"
+	& "/etc/resolv.conf" `File.containsLine` "search lan"
+
 	& JoeySites.homeNAS
 	& Apt.installed ["mtr-tiny", "iftop", "screen", "nmap"]
 	-- Currently manually building the xr_usb_serial module.
 	& Apt.installed ["linux-headers-armmp-lpae"]
 	& Postfix.satellite
+
+sky :: Host
+sky = host "sky.lan" $ props
+	& standardSystem Testing ARMHF [ "Wifi router." ]
+	& Apt.removed ["rsyslog"]
+	
+	& Apt.installed ["raspi-firmware", "firmware-misc-nonfree"]
+	& Apt.serviceInstalledRunning "systemd-timesyncd" -- no hardware clock
+
+	& Ssh.hostKeys hostContext
+		[ (SshEcdsa, "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBG19v7K59SzFp/OntM9iyhlKOj2pShFSPJeqR1aiYhPF2NqztcmsY6WvIDqh6jmaISnyV1IqZZ60zvGTVRoOyMY=")
+		]
+	& Ssh.userKeys (User "joey") hostContext
+		[ (SshEd25519, "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOmwEfM5qTjA7xuJgygEgHfa1Y/WrRBpu7xBY8D82ul+")
+		]
+
+	& JoeySites.connectStarlinkDish ifs
+	& JoeySites.homeRouter ifs "hollow" JoeySites.hostapd2GhzConfig_mt76
+	& Apt.installed ["mtr-tiny", "iftop", "screen", "nmap", "net-tools"]
+	& Postfix.satellite
+  where
+	ifs = JoeySites.Interfaces
+		{ JoeySites.ethernetInterface = "eth0"
+		, JoeySites.wifiInterface = "wlan0"
+		, JoeySites.wifiInterfaceOld = "wlan1"
+		}
 
 -- This is not a complete description of kite, since it's a
 -- multiuser system with eg, user passwords that are not deployed
diff --git a/propellor.cabal b/propellor.cabal
--- a/propellor.cabal
+++ b/propellor.cabal
@@ -1,11 +1,11 @@
 Name: propellor
-Version: 5.16
+Version: 5.17
 Cabal-Version: 1.20
 License: BSD2
 Maintainer: Joey Hess <id@joeyh.name>
 Author: Joey Hess
 Stability: Stable
-Copyright: 2014 Joey Hess
+Copyright: 2014-2023 Joey Hess
 License-File: LICENSE
 Build-Type: Simple
 Homepage: https://propellor.branchable.com/
diff --git a/src/Propellor/Info.hs b/src/Propellor/Info.hs
--- a/src/Propellor/Info.hs
+++ b/src/Propellor/Info.hs
@@ -91,13 +91,13 @@
 -- It also lets the type checker know that all the properties of the
 -- host must support Debian.
 --
--- >	& osDebian (Stable "buster") X86_64
+-- >	& osDebian (Stable "bookworm") X86_64
 osDebian :: DebianSuite -> Architecture -> Property (HasInfo + Debian)
 osDebian = osDebian' Linux
 
 -- Use to specify a different `DebianKernel` than the default `Linux`
 --
--- >	& osDebian' KFreeBSD (Stable "buster") X86_64
+-- >	& osDebian' KFreeBSD (Stable "bookworm") X86_64
 osDebian' :: DebianKernel -> DebianSuite -> Architecture -> Property (HasInfo + Debian)
 osDebian' kernel suite arch = tightenTargets $ os (System (Debian kernel suite) arch)
 
diff --git a/src/Propellor/Property/Apt.hs b/src/Propellor/Property/Apt.hs
--- a/src/Propellor/Property/Apt.hs
+++ b/src/Propellor/Property/Apt.hs
@@ -82,8 +82,26 @@
 	("deb":rest) -> unwords $ "deb-src" : rest
 	_ -> ""
 
-stdSections :: [Section]
-stdSections = ["main", "contrib", "non-free"]
+stdSections :: DebianSuite -> [Section]
+stdSections s = ["main", "contrib", "non-free"] ++ case s of
+	Stable r | r `elem` oldstables -> []
+	-- Suite added in debian bookworm.
+	_ -> ["non-free-firmware"]
+  where
+	oldstables = 
+		[ "bullseye"
+		, "buster"
+		, "stretch"
+		, "jessie"
+		, "wheezy"
+		, "lenny"
+		, "etch"
+		, "sarge"
+		, "woody"
+		, "potato"
+		, "slink"
+		, "hamm"
+		]
 
 binandsrc :: String -> SourcesGenerator
 binandsrc url suite = catMaybes
@@ -95,14 +113,14 @@
 	, srcLine <$> bl
 	]
   where
-	l = debLine (showSuite suite) url stdSections
+	l = debLine (showSuite suite) url (stdSections suite)
 	bl = do
 		bs <- backportSuite suite
-		return $ debLine bs url stdSections
+		return $ debLine bs url (stdSections suite)
 	-- formerly known as 'volatile'
 	sul = do
 		sus <- stableUpdatesSuite suite
-		return $ debLine sus url stdSections
+		return $ debLine sus url (stdSections suite)
 
 stdArchiveLines :: Propellor SourcesGenerator
 stdArchiveLines = return . binandsrc =<< getMirror
@@ -111,7 +129,7 @@
 securityUpdates :: SourcesGenerator
 securityUpdates suite
 	| isStable suite =
-		let l = "deb http://security.debian.org/debian-security " ++ securitysuite ++ " " ++ unwords stdSections
+		let l = "deb http://security.debian.org/debian-security " ++ securitysuite ++ " " ++ unwords (stdSections suite)
 		in [l, srcLine l]
 	| otherwise = []
   where
diff --git a/src/Propellor/Property/Sbuild.hs b/src/Propellor/Property/Sbuild.hs
--- a/src/Propellor/Property/Sbuild.hs
+++ b/src/Propellor/Property/Sbuild.hs
@@ -21,7 +21,7 @@
 Suggested usage in @config.hs@:
 
 >  mybox = host "mybox.example.com" $ props
->  	& osDebian (Stable "buster") X86_64
+>  	& osDebian (Stable "bookworm") X86_64
 >  	& Apt.useLocalCacher
 >  	& sidSchrootBuilt
 >  	& Sbuild.usableBy (User "spwhitton")
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
@@ -755,15 +755,6 @@
 		, "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")
 	& Apt.installed ["i2c-tools"]
@@ -777,7 +768,6 @@
 		`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"
@@ -878,15 +868,15 @@
 			createSymbolicLink d "/var/www/html"
 		)
 
-homerouterWifiInterface :: String
-homerouterWifiInterface = "wlx00c0ca82eb78"
-
-homerouterWifiInterfaceOld :: String
-homerouterWifiInterfaceOld = "wlx9cefd5fcd6f3"
+data Interfaces = Interfaces
+	{ ethernetInterface :: String
+	, wifiInterface :: String
+	, wifiInterfaceOld :: String
+	}
 
 -- Connect to the starlink dish directly (no starlink router)
-connectStarlinkDish :: Property DebianLike
-connectStarlinkDish = propertyList "connected via starlink dish" $ props
+connectStarlinkDish :: Interfaces -> Property DebianLike
+connectStarlinkDish ifs = propertyList "connected via starlink dish" $ props
 	-- Use dhcpcd for ipv6 prefix delegation to the wifi interface.
 	& Apt.installed ["dhcpcd"]
 	-- dhcpcd is run by ifup on boot. When the daemon was enabled,
@@ -895,33 +885,33 @@
 	& Systemd.stopped "dhcpcd"
 	& Systemd.masked "dhcpcd"
 	& "/etc/dhcpcd.conf" `File.containsLine`
-		("ia_pd 1 " ++ homerouterWifiInterface)
+		("ia_pd 1 " ++ wifiInterface ifs)
 	& "/etc/dhcpcd.conf" `File.lacksLine`
-		("ia_pd 1 " ++ homerouterWifiInterfaceOld)
-	& Network.dhcp "end0"
+		("ia_pd 1 " ++ wifiInterfaceOld ifs)
+	& Network.dhcp (ethernetInterface ifs)
 		`requires` Network.cleanInterfacesFile
 
 -- Connect to the starlink router with its ethernet adapter.
 --
 -- Static route because with dhcp it sometimes fails to get an address from
 -- starlink.
-connectStarlinkRouter :: Property DebianLike
-connectStarlinkRouter = propertyList "connected via starlink router" $ props
-	& Network.static "end0" (IPv4 "192.168.1.62")
+connectStarlinkRouter :: Interfaces -> Property DebianLike
+connectStarlinkRouter ifs = propertyList "connected via starlink router" $ props
+	& Network.static (ethernetInterface ifs) (IPv4 "192.168.1.62")
 		(Just (Network.Gateway (IPv4 "192.168.1.1")))
 		`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
+homeRouter :: Interfaces -> String -> HostapdConfig -> Property DebianLike
+homeRouter ifs wifinetworkname (HostapdConfig hostapdconfig) = propertyList "home router" $ props
+	& File.notPresent (Network.interfaceDFile (wifiInterfaceOld ifs))
+	& Network.static (wifiInterface ifs) (IPv4 "10.1.1.1") Nothing
 		`requires` Network.cleanInterfacesFile
 	& Apt.installed ["hostapd"]
 	& File.hasContent "/etc/hostapd/hostapd.conf"
-			([ "interface=" ++ homerouterWifiInterface
-			, "ssid=house"
-			] ++ hostapd2GhzConfig)
+			([ "interface=" ++ wifiInterface ifs
+			, "ssid=" ++ wifinetworkname
+			] ++ hostapdconfig)
 		`requires` File.dirExists "/etc/hostapd"
 		`requires` File.hasContent "/etc/default/hostapd"
 			[ "DAEMON_CONF=/etc/hostapd/hostapd.conf" ]
@@ -937,13 +927,14 @@
 	& File.hasContent "/etc/dnsmasq.conf"
 		[ "domain-needed"
 		, "bogus-priv"
-		, "interface=" ++ homerouterWifiInterface
+		, "interface=" ++ wifiInterface ifs
 		, "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=/house.lan/10.1.1.1"
+		, "address=/sky.lan/10.1.1.1"
+		, "address=/house.lan/10.1.1.2"
 		-- allow accessing starlink dish when it's not online yet
 		, "address=/dishy.starlink.com/192.168.100.1"
 		]
@@ -952,12 +943,12 @@
 	-- forward too far, causing that lease to not be valid.
 	& "/etc/default/dnsmasq" `File.containsLine` "DNSMASQ_OPTS=\"--dhcp-authoritative\""
 		`onChange` Service.restarted "dnsmasq"
-	& ipmasq homerouterWifiInterface
+	& ipmasq (wifiInterface ifs)
 	& Apt.installed ["radvd"]
 	-- This needs ipv6 prefix delegation to the wifi interface to be
 	-- enabled.
 	& File.hasContent "/etc/radvd.conf"
-		[ "interface " ++ homerouterWifiInterface ++ " {"
+		[ "interface " ++ wifiInterface ifs ++ " {"
 		, "  AdvSendAdvert on;"
 		, "  MinRtrAdvInterval 3;"
 		, "  MaxRtrAdvInterval 10;"
@@ -969,6 +960,7 @@
 		, "};"
 		]
 		`onChange` Service.restarted "radvd"
+	& "/etc/sysctl.conf" `File.containsLine` "net.ipv6.conf.all.forwarding=1"
 
 -- | Enable IP masqerading, on whatever other interfaces come up, besides the
 -- provided intif.
@@ -1245,14 +1237,31 @@
 noExim = Apt.removed ["exim4", "exim4-base", "exim4-daemon-light"]
 	`onChange` Apt.autoRemove
 
-hostapd2GhzConfig :: [String]
-hostapd2GhzConfig = 
+data HostapdConfig = HostapdConfig [String]
+
+hostapd2GhzConfig :: HostapdConfig
+hostapd2GhzConfig = HostapdConfig
 	[ "hw_mode=g"
-	, "channel=8"
+	, "channel=5"
+	, "country_code=US"
+	, "ieee80211d=1"
+	, "ieee80211n=1"
+	, "wmm_enabled=1"
 	]
 
-hostapd5GhzConfig :: [String]
-hostapd5GhzConfig = 
+-- For wifi adapters such as the Alfa AWUS036ACHM
+--
+-- Note that for maximum speed, this needs channel 5 or 6.
+-- This should make it be capable of 150 Mb/s.
+hostapd2GhzConfig_mt76 :: HostapdConfig
+hostapd2GhzConfig_mt76 = HostapdConfig $ c ++ 
+	[ "ht_capab=[HT40+][HT40-][GF][SHORT-GI-20][SHORT-GI-40]"
+	]
+  where
+	HostapdConfig c = hostapd2GhzConfig
+
+hostapd5GhzConfig :: HostapdConfig
+hostapd5GhzConfig = HostapdConfig
 	[ "hw_mode=a"
 	, "channel=36"
 	, "country_code=US"
diff --git a/src/Propellor/Types/OS.hs b/src/Propellor/Types/OS.hs
--- a/src/Propellor/Types/OS.hs
+++ b/src/Propellor/Types/OS.hs
@@ -59,7 +59,7 @@
 	deriving (Show, Eq)
 
 -- | Debian has several rolling suites, and a number of stable releases,
--- such as Stable "buster".
+-- such as Stable "bookworm".
 data DebianSuite = Experimental | Unstable | Testing | Stable Release
 	deriving (Show, Eq)
 
