Recent changes to this wiki:

todo
diff --git a/doc/todo/Propellor.Property.Versioned_support_asymmetric_RevertableProperty_types.mdwn b/doc/todo/Propellor.Property.Versioned_support_asymmetric_RevertableProperty_types.mdwn
new file mode 100644
index 0000000..c60cd4d
--- /dev/null
+++ b/doc/todo/Propellor.Property.Versioned_support_asymmetric_RevertableProperty_types.mdwn
@@ -0,0 +1,7 @@
+Currently, this module requires `RevertableProperty t t`.
+That can be annoying, it would be good to support at least
+`RevertablePropery (HasInfo + t) t` and ideally all 
+`RevertableProperty t1 t2`
+
+There should be no reason that can't be done; I was just having
+problems getting the type checker happy on the day I wrote it. --[[Joey]]

add news item for propellor 4.4.0
diff --git a/doc/news/version_4.3.2.mdwn b/doc/news/version_4.3.2.mdwn
deleted file mode 100644
index 1a85d2d..0000000
--- a/doc/news/version_4.3.2.mdwn
+++ /dev/null
@@ -1,3 +0,0 @@
-propellor 4.3.2 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
-   * Really include Propellor.Property.FreeDesktop."""]]
\ No newline at end of file
diff --git a/doc/news/version_4.4.0.mdwn b/doc/news/version_4.4.0.mdwn
new file mode 100644
index 0000000..db86c53
--- /dev/null
+++ b/doc/news/version_4.4.0.mdwn
@@ -0,0 +1,8 @@
+propellor 4.4.0 released with [[!toggle text="these changes"]]
+[[!toggleable text="""
+   * Propellor.Property.Timezone: New module, contributed by Sean Whitton.
+   * Propellor.Property.Sudo.enabledFor: Made revertable.
+     (minor API change)
+   * Propellor.Property.LightDM.autoLogin: Made revertable.
+     (minor API change)
+   * Propellor.Property.Conffile: Added lacksIniSetting."""]]
\ No newline at end of file

Propellor.Property.Timezone: New module, contributed by Sean Whitton.
diff --git a/debian/changelog b/debian/changelog
index a60c5f1..517151d 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+propellor (4.3.5) UNRELEASED; urgency=medium
+
+  * Propellor.Property.Timezone: New module, contributed by Sean Whitton.
+
+ -- Joey Hess <id@joeyh.name>  Sun, 16 Jul 2017 12:07:15 -0400
+
 propellor (4.3.4) unstable; urgency=medium
 
   * Propellor.Property.Versioned: New module which allows different
diff --git a/doc/todo/merge_request:_Timezone.hs.mdwn b/doc/todo/merge_request:_Timezone.hs.mdwn
index 3f67da2..a8ba3ea 100644
--- a/doc/todo/merge_request:_Timezone.hs.mdwn
+++ b/doc/todo/merge_request:_Timezone.hs.mdwn
@@ -5,3 +5,5 @@ Adds `Timezone.configured`.
 I think that this works fine on stretch, but on Jessie there is some oddness.  For example, if you set the timezone of a host to `US/Arizona`, the apt reconfiguration will put `America/Phoenix` in /etc/timezone, resulting in the property reporting a change every time that it is run.  I think this is harmless.
 
 --spwhitton
+
+> [[merged|done]] --[[Joey]]
diff --git a/doc/todo/merge_request:_Timezone.hs/comment_1_9cfb5e48940e58f2064cbb5edf462c06._comment b/doc/todo/merge_request:_Timezone.hs/comment_1_9cfb5e48940e58f2064cbb5edf462c06._comment
new file mode 100644
index 0000000..026b13d
--- /dev/null
+++ b/doc/todo/merge_request:_Timezone.hs/comment_1_9cfb5e48940e58f2064cbb5edf462c06._comment
@@ -0,0 +1,15 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2017-07-16T15:57:20Z"
+ content="""
+I generally consider properties that do work every time to be a minor bug.
+
+I wonder if it would be better to preseed tzdata rather than writing the
+config file. I observe the same substitution from eg, US/Eastern to
+America/New_York in the file when reconfiguring noninteractively,
+but reconfiguring interactively I can select US/Eastern and that gets
+into the file.
+
+Anyway, merged as this is certianly a good starting point.
+"""]]
diff --git a/propellor.cabal b/propellor.cabal
index 733dda3..43a3ab5 100644
--- a/propellor.cabal
+++ b/propellor.cabal
@@ -150,6 +150,7 @@ Library
     Propellor.Property.Sudo
     Propellor.Property.Systemd
     Propellor.Property.Systemd.Core
+    Propellor.Property.Timezone
     Propellor.Property.Tor
     Propellor.Property.Unbound
     Propellor.Property.User

add merge request
diff --git a/doc/todo/merge_request:_Timezone.hs.mdwn b/doc/todo/merge_request:_Timezone.hs.mdwn
new file mode 100644
index 0000000..3f67da2
--- /dev/null
+++ b/doc/todo/merge_request:_Timezone.hs.mdwn
@@ -0,0 +1,7 @@
+Please consider merging branch `timezone` of repo `https://git.spwhitton.name/propellor`.
+
+Adds `Timezone.configured`.
+
+I think that this works fine on stretch, but on Jessie there is some oddness.  For example, if you set the timezone of a host to `US/Arizona`, the apt reconfiguration will put `America/Phoenix` in /etc/timezone, resulting in the property reporting a change every time that it is run.  I think this is harmless.
+
+--spwhitton

remove old announcment
diff --git a/doc/news/Linux.Conf.Au.presentation.mdwn b/doc/news/Linux.Conf.Au.presentation.mdwn
deleted file mode 100644
index 5418097..0000000
--- a/doc/news/Linux.Conf.Au.presentation.mdwn
+++ /dev/null
@@ -1,5 +0,0 @@
-<video controls src="http://mirror.linux.org.au/pub/linux.conf.au/2017/Type_driven_configuration_management_with_Propellor.webm"></video>
-
-[video](http://mirror.linux.org.au/pub/linux.conf.au/2017/Type_driven_configuration_management_with_Propellor.webm)
-
-Also see this writeup in [Linux Weekly News](https://lwn.net/Articles/713653/)

add news item for propellor 4.3.4
diff --git a/doc/news/version_4.3.1.mdwn b/doc/news/version_4.3.1.mdwn
deleted file mode 100644
index 5c07307..0000000
--- a/doc/news/version_4.3.1.mdwn
+++ /dev/null
@@ -1,4 +0,0 @@
-propellor 4.3.1 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
-   * Added Propellor.Property.FreeDesktop module.
-   * Added reservedSpacePercentage to the PartSpec EDSL."""]]
\ No newline at end of file
diff --git a/doc/news/version_4.3.4.mdwn b/doc/news/version_4.3.4.mdwn
new file mode 100644
index 0000000..5d30840
--- /dev/null
+++ b/doc/news/version_4.3.4.mdwn
@@ -0,0 +1,7 @@
+propellor 4.3.4 released with [[!toggle text="these changes"]]
+[[!toggleable text="""
+   * Propellor.Property.Versioned: New module which allows different
+     versions of a property or host to be written down in a propellor config
+     file. Has many applications, including staged upgrades and rollbacks.
+   * LightDM.autoLogin: Use [Seat:*] rather than the old [SeatDefaults].
+     The new name has been supported since lightdm 1.15."""]]
\ No newline at end of file

convert todo item into a merge request
diff --git a/doc/todo/sbuild_setup_should_use_apt-cacher-ng.mdwn b/doc/todo/sbuild_setup_should_use_apt-cacher-ng.mdwn
index 450416f..62f619d 100644
--- a/doc/todo/sbuild_setup_should_use_apt-cacher-ng.mdwn
+++ b/doc/todo/sbuild_setup_should_use_apt-cacher-ng.mdwn
@@ -1,11 +1,20 @@
-The properties in `Sbuild.hs` should use apt-cacher-ng instead of a bind mount of `/var/cache/apt/archives`.  This has at least three advantages:
+Please consider merging branch `apt-cacher-ng` of repo `https://git.spwhitton.name/propellor`.
 
-1. more than one instance of sbuild can run at once
+Sample text for changelog/description of changes:
 
-2. sbuild can run even if apt is doing something else with its cache
-
-3. the `piupartsConf` properties are no longer needed.
+    * Add Apt.proxy property to set a host's apt proxy.
+    * Add Apt.useLocalCacher property to set up apt-cacher-ng.
+    * Rework Sbuild properties to use apt proxies/cachers instead of bind-mounting
+      the host's apt cache.  This makes it possible to run more than one build at
+      a time, and lets sbuild run even if apt's cache is locked by the host's apt.
+      - If Apt.proxy is set, it is assumed that the proxy does some sort of
+        caching, and sbuild chroots are set up to use the same proxy.
+      - If Apt.proxy is not set, we install apt-cacher-ng, and point sbuild
+        chroots at the local apt cacher.
+      - Drop Sbuild.piupartsConfFor, Sbuild.piupartsConf, Sbuild.shareAptCache
+        (API change)
+        No longer needed now that we are using apt proxies/cachers.
+      - Update sample config in haddock for Propellor.Property.Sbuild.
+        Please compare both your config.hs and your ~/.sbuildrc against the haddock.
 
 --spwhitton
-
-[[!tag user/spwhitton]]

add Propellor.Property.Versioned
New module which allows different versions of a property or host to be
written down in a propellor config file. Has many applications,
including staged upgrades and rollbacks.
Note that it currently only supports RevertableProperty that has the same
metatypes for its setup and cleanup sides. And, each RevertableProperty in
a version definition needs to have the same metatypes as the others too.
I tried a couple of times to add support for differing metatypes, but
it got beyond my avilities to do.
This commit was sponsored by Jeff Goeke-Smith on Patreon.
diff --git a/debian/changelog b/debian/changelog
index cce3338..e7ec04b 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,5 +1,8 @@
 propellor (4.3.4) UNRELEASED; urgency=medium
 
+  * Propellor.Property.Versioned: New module which allows different
+    versions of a property or host to be written down in a propellor config
+    file. Has many applications, including staged upgrades and rollbacks.
   * LightDM.autoLogin: Use [Seat:*] rather than the old [SeatDefaults].
     The new name has been supported since lightdm 1.15.
 
diff --git a/doc/todo/differential_update_via_RevertableProperty.mdwn b/doc/todo/differential_update_via_RevertableProperty.mdwn
index 6d65c91..3eb9bc7 100644
--- a/doc/todo/differential_update_via_RevertableProperty.mdwn
+++ b/doc/todo/differential_update_via_RevertableProperty.mdwn
@@ -101,33 +101,23 @@ Is, perhaps:
 	data Version = A | B | C
 		deriving (Enum, Ord)
 
-	foo :: Versioned Host
+	foo :: Versioned Hoso
 	foo = versionedHost "foo" $ do
 		ver A someprop
-			<|> inVersion [B, C] otherprop
+			<|> othervers otherprop
 		ver A somerevertableprop
-		ver [B, C] somethingelse
+		ver [B, C] newprop
 
 That's ... pretty ok, would hit as least some of the use cases described
 above. Seems to need a Reader+Writer monad to implement it,
 without passing the Version around explicitly. 
 
-Is it allowable for `somethingelse` to not be revertable?
+Is it allowable for `newprop` to not be revertable?
 Once `foo` gets that property, it is never removed if we're moving only
-forwars. On the other hand, perhaps the user will want to roll back to
+forwards. On the other hand, perhaps the user will want to roll back to
 version A. Allowing rollbacks seems good, so `inVersion` should only
 accept `RevertableProperty`.
 
-Here's another situation where reversion is not needed:
-
-	foo = versionedHost "foo" $ do
-		ver A (someprop :: Property)
-			<|> ver [B, C] (someprop :: Property)
-
-That feels like an edge case.. And the only way that propellor could tell
-reversion is not needed there is if it could compare the two sides of the
-`<|>`, and there's no Eq.
-
 Another interesting case is this:
 
 	foo = versionedHost "foo" $ do
@@ -151,5 +141,6 @@ examples above. And that allows composition of properties with versioning:
 
 	someprop :: Versioned (Property DebianLike)
 	someprop = versionedProperty $ do
-		ver A foo
-		ver [B, C] bar
+		ver A foo <|> ver [B, C] bar
+
+> [[done]] in Propellor.Property.Versioned. --[[Joey]]
diff --git a/propellor.cabal b/propellor.cabal
index 3c2477b..1bcc161 100644
--- a/propellor.cabal
+++ b/propellor.cabal
@@ -154,6 +154,7 @@ Library
     Propellor.Property.Unbound
     Propellor.Property.User
     Propellor.Property.Uwsgi
+    Propellor.Property.Versioned
     Propellor.Property.XFCE
     Propellor.Property.ZFS
     Propellor.Property.ZFS.Process
diff --git a/src/Propellor/Property/Versioned.hs b/src/Propellor/Property/Versioned.hs
new file mode 100644
index 0000000..d6517ab
--- /dev/null
+++ b/src/Propellor/Property/Versioned.hs
@@ -0,0 +1,112 @@
+{-# LANGUAGE RankNTypes, FlexibleContexts, TypeFamilies #-}
+
+-- | Versioned properties and hosts.
+--
+-- When importing and using this module, you will need to enable some
+-- language extensions:
+--
+-- > {-# LANGUAGE RankNTypes, FlexibleContexts, TypeFamilies #-}
+--
+-- This module takes advantage of `RevertableProperty` to let propellor
+-- switch cleanly between versions. The way it works is all revertable
+-- properties for other versions than the current version are first
+-- reverted, and  then propellor ensures the property for the current
+-- version. This method should work for any combination of revertable
+-- properties.
+--
+-- For example:
+-- 
+-- > demo :: Versioned Int (RevertableProperty DebianLike DebianLike)
+-- > demo ver =
+-- >    ver (   (== 1) --> Apache.modEnabled "foo"
+-- >		`requires` Apache.modEnabled "foosupport"
+-- >	    <|> (== 2) --> Apache.modEnabled "bar"
+-- > 	    <|> (> 2)  --> Apache.modEnabled "baz"
+-- >        )
+-- >
+-- > foo :: Host
+-- > foo = host "foo.example.com" $ props
+-- > 	& demo `version` (2 :: Int)
+--
+-- Similarly, a whole Host can be versioned. For example:
+--
+-- > bar :: Versioned Int Host
+-- > bar ver = host "bar.example.com" $ props
+-- >	& osDebian Unstable X86_64
+-- > 	& ver (   (== 1) --> Apache.modEnabled "foo"
+-- > 	      <|> (== 2) --> Apache.modEnabled "bar"
+-- > 	      )
+-- > 	& ver ( (>= 2) --> Apt.unattendedUpgrades )
+--
+-- Note that some versioning of revertable properties may cause
+-- propellor to do a lot of unnecessary work each time it's run.
+-- Here's an example of such a problem:
+--
+-- > slow :: Versioned Int -> RevertableProperty DebianLike DebianLike
+-- > slow ver =
+-- > 	ver (   (== 1) --> (Apt.installed "foo" <!> Apt.removed "foo")
+-- >	    <|> (== 2) --> (Apt.installed "bar" <!> Apt.removed "bar")
+-- >        )
+--
+-- Suppose that package bar depends on package foo. Then at version 2,
+-- propellor will remove package foo in order to revert version 1, only
+-- to re-install it since version 2 also needs it installed.
+
+module Propellor.Property.Versioned (Versioned, version, (-->), (<|>)) where
+
+import Propellor
+
+-- | Something that has multiple versions of type `v`.
+type Versioned v t = VersionedBy v -> t
+
+type VersionedBy v
+	= forall metatypes. Combines (RevertableProperty metatypes metatypes) (RevertableProperty metatypes metatypes)
+	=> (CombinedType (RevertableProperty metatypes metatypes) (RevertableProperty metatypes metatypes) ~ RevertableProperty metatypes metatypes)
+	=> (VerSpec v metatypes -> RevertableProperty metatypes metatypes)
+
+-- | Access a particular version of a Versioned value.
+version :: (Versioned v t) -> v -> t
+version f v = f (processVerSpec v)
+
+-- A specification of versions.
+--
+-- Why is this not a simple list like
+-- [(v -> Bool, RevertableProperty metatypes metatypes)] ?
+-- Using a list would mean the empty list would need to be dealt with,
+-- and processVerSpec does not have a Monoid instance for
+-- RevertableProperty metatypes metatypes in scope, and due to the way the
+-- Versioned type works, the compiler cannot find such an instance.
+--
+-- Also, using this data type allows a nice syntax for creating
+-- VerSpecs, via the `<&>` and `alt` functions.
+data VerSpec v metatypes
+	= Base (v -> Bool, RevertableProperty metatypes metatypes)
+	| More (v -> Bool, RevertableProperty metatypes metatypes) (VerSpec v metatypes)
+
+processVerSpec 
+	:: Combines (RevertableProperty metatypes metatypes) (RevertableProperty metatypes metatypes)
+	=> (CombinedType (RevertableProperty metatypes metatypes) (RevertableProperty metatypes metatypes) ~ RevertableProperty metatypes metatypes)
+	=> v
+	-> VerSpec v metatypes
+	-> RevertableProperty metatypes metatypes
+processVerSpec v (Base (c, p))
+	| c v = p
+	| otherwise = revert p
+processVerSpec v (More (c, p) vs)
+	| c v = processVerSpec v vs `before` p
+	| otherwise = revert p `before` processVerSpec v vs
+
+-- | Specify a function that checks the version, and what
+-- `RevertableProperty` to use if the version matches.
+(-->) :: (v -> Bool) -> RevertableProperty metatypes metatypes -> VerSpec v metatypes
+c --> p = Base (c, p)
+
+-- | Add an alternate version.
+(<|>) :: VerSpec v metatypes -> VerSpec v metatypes -> VerSpec v metatypes 
+Base a <|> Base b = More a (Base b)
+Base a <|> More b c = More a (More b c)
+More b c <|> Base a  = More a (More b c)
+More a b <|> More c d = More a (More c (b <|> d))
+
+infixl 8 -->
+infixl 2 <|>

Added a comment
diff --git a/doc/forum/Why_downloading_package_list_from_hackage.haskell.org__63__/comment_6_ceddc6d118b7ea71ec8f498960a5fe97._comment b/doc/forum/Why_downloading_package_list_from_hackage.haskell.org__63__/comment_6_ceddc6d118b7ea71ec8f498960a5fe97._comment
new file mode 100644
index 0000000..32ed86f
--- /dev/null
+++ b/doc/forum/Why_downloading_package_list_from_hackage.haskell.org__63__/comment_6_ceddc6d118b7ea71ec8f498960a5fe97._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="gueux"
+ avatar="http://cdn.libravatar.org/avatar/2982bac2c2cd94ab3860efb189deafc8"
+ subject="comment 6"
+ date="2017-07-14T11:16:10Z"
+ content="""
+(I did not try to build propellor again on this 128Mo host yet, though)
+"""]]

Added a comment
diff --git a/doc/forum/Why_downloading_package_list_from_hackage.haskell.org__63__/comment_5_61d7ef8a61ac7b922c810825d794da5f._comment b/doc/forum/Why_downloading_package_list_from_hackage.haskell.org__63__/comment_5_61d7ef8a61ac7b922c810825d794da5f._comment
new file mode 100644
index 0000000..35c894b
--- /dev/null
+++ b/doc/forum/Why_downloading_package_list_from_hackage.haskell.org__63__/comment_5_61d7ef8a61ac7b922c810825d794da5f._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="gueux"
+ avatar="http://cdn.libravatar.org/avatar/2982bac2c2cd94ab3860efb189deafc8"
+ subject="comment 5"
+ date="2017-07-14T10:58:33Z"
+ content="""
+The new \"bootstrapWith (Robustly Stack)\" and \"bootstrapWith OSOnly\" properties completely address my concerns. Thanks!
+"""]]

rollbacks
diff --git a/doc/todo/differential_update_via_RevertableProperty.mdwn b/doc/todo/differential_update_via_RevertableProperty.mdwn
index 6c21794..6d65c91 100644
--- a/doc/todo/differential_update_via_RevertableProperty.mdwn
+++ b/doc/todo/differential_update_via_RevertableProperty.mdwn
@@ -99,7 +99,7 @@ fooisfoo = foo ==# foo
 Is, perhaps:
 
 	data Version = A | B | C
-		deriving (Ord)
+		deriving (Enum, Ord)
 
 	foo :: Versioned Host
 	foo = versionedHost "foo" $ do
@@ -112,9 +112,11 @@ That's ... pretty ok, would hit as least some of the use cases described
 above. Seems to need a Reader+Writer monad to implement it,
 without passing the Version around explicitly. 
 
-Note that it's allowable for `somethingelse` to not be revertable,
-since once `foo` gets that property, it is never removed.
-So, `inVersion` has to accept both `RevertableProperty` and `Property`.
+Is it allowable for `somethingelse` to not be revertable?
+Once `foo` gets that property, it is never removed if we're moving only
+forwars. On the other hand, perhaps the user will want to roll back to
+version A. Allowing rollbacks seems good, so `inVersion` should only
+accept `RevertableProperty`.
 
 Here's another situation where reversion is not needed:
 

wow wow wow
diff --git a/doc/todo/differential_update_via_RevertableProperty.mdwn b/doc/todo/differential_update_via_RevertableProperty.mdwn
index 79afebe..6c21794 100644
--- a/doc/todo/differential_update_via_RevertableProperty.mdwn
+++ b/doc/todo/differential_update_via_RevertableProperty.mdwn
@@ -91,3 +91,63 @@ foo = do
 fooisfoo :: Bool
 fooisfoo = foo ==# foo
 </pre>
+
+-----
+
+## the best we can do without Eq
+
+Is, perhaps:
+
+	data Version = A | B | C
+		deriving (Ord)
+
+	foo :: Versioned Host
+	foo = versionedHost "foo" $ do
+		ver A someprop
+			<|> inVersion [B, C] otherprop
+		ver A somerevertableprop
+		ver [B, C] somethingelse
+
+That's ... pretty ok, would hit as least some of the use cases described
+above. Seems to need a Reader+Writer monad to implement it,
+without passing the Version around explicitly. 
+
+Note that it's allowable for `somethingelse` to not be revertable,
+since once `foo` gets that property, it is never removed.
+So, `inVersion` has to accept both `RevertableProperty` and `Property`.
+
+Here's another situation where reversion is not needed:
+
+	foo = versionedHost "foo" $ do
+		ver A (someprop :: Property)
+			<|> ver [B, C] (someprop :: Property)
+
+That feels like an edge case.. And the only way that propellor could tell
+reversion is not needed there is if it could compare the two sides of the
+`<|>`, and there's no Eq.
+
+Another interesting case is this:
+
+	foo = versionedHost "foo" $ do
+		ver A bar
+		always otherprop
+		ver [B, C] bar
+	
+Is version A of foo identical to verion B? If so, this should be allowed to
+compile even when `bar` cannot be reverted. On the other hand, perhaps
+ordering of the properties matters, in which case the systems are subtly
+different, and there's no way to get from A to B.
+
+It's certianly possible for ordering to matter in propellor properties, 
+although it's generally a bug when it does. So, it seems ok for this
+case to be rejected.
+
+As well as `Versioned Host`, it would be possible to have 
+`Versioned (Property metatypes)`.
+Indeed, that would make sense to he used internally in the 
+examples above. And that allows composition of properties with versioning:
+
+	someprop :: Versioned (Property DebianLike)
+	someprop = versionedProperty $ do
+		ver A foo
+		ver [B, C] bar

idea
diff --git a/doc/todo/differential_update_via_RevertableProperty.mdwn b/doc/todo/differential_update_via_RevertableProperty.mdwn
new file mode 100644
index 0000000..79afebe
--- /dev/null
+++ b/doc/todo/differential_update_via_RevertableProperty.mdwn
@@ -0,0 +1,93 @@
+Long ago, nomeata pointed out that RevertableProperty required the user to
+keep track of different versions of a Host, in a way that should be able to
+be automated. When the user decides to revert a RevertableProperty, they
+have to keep the reverted property on the Host until propellor runs there,
+and only then can remove it.
+
+What if instead, there was a way to store the old version of a Host
+somewhere. Let's not worry about where or how, but assume we have 
+`(old, new) :: (Host, Host)`
+
+Propellor could compare `old` and `new`, and if it finds a
+RevertableProperty in `old` that is not in `new`, add it in reverted form
+to `new'`.
+
+Also, if propellor finds a Property in `old` that is not in `new`, it can
+tell the user that this Property needs to be reverted, but cannot be, so
+`new` won't fully describe the state of the host. --[[Joey]]
+
+----
+
+There are a lot of ways such a capability could be used, especially if
+there were a way to pull the old version of a Host out of a previous
+version of config.hs or something like that. But leaving aside such magic,
+here are some nice use cases:
+
+* Suppose we want to generate several disk images, which are somewhat
+  similar, but differ in some properties. Rather than building a separate
+  chroot for each, we can build a chroot for the first, update the first
+  disk image, compare that with the second and update the chroot
+  accordingly, and so on.
+* When propellor is used to build a OS installer disk image, that installer
+  could know the properties used to create it, and the properties of the
+  system that is desired to be installed. To install, it can rsync the
+  installer disk contents to `/target` and then run propellor in `/target`,
+  differentially updating it as needed.
+
+----
+
+Here's the catch: It can't be implemented currently! The comparison of
+properties needs an `Eq` instance for Property (and RevertableProperty).
+But, a property contains an action in the IO monad, which can't have an
+`Eq` instance, and so there's no good way to compare properties.
+
+Making propellor use an ESDL could get us `Eq`. But it would make it rather
+clumsy to write properties, something like this. 
+
+<pre>
+appendfoo f = WriteFile f (ListAppend "foo" (ReadFile f))
+</pre>
+
+(Perhaps a deeply embedded DSL would be better.)
+
+Could a Free monad get us `Eq`? Well, there can apparently be free monads that
+have an `Eq` instance, but I tried building one for a simple teletype, and
+failed, which does not bode well. Here's the code; this fails to compile
+because of a missing instance `(Eq1 ((->) String))`, and of course comparing
+functions for equality is not generally feasible.
+
+<pre>
+{-# LANGUAGE FlexibleContexts, UndecidableInstances #-}
+
+import Control.Monad.Free
+import Prelude.Extras
+
+data TeletypeF x
+        = PutStrLn String x
+        | GetLine (String -> x)
+
+instance Functor TeletypeF where
+        fmap f (PutStrLn str x) = PutStrLn str (f x)
+        fmap f (GetLine k) = GetLine (f . k)
+
+instance (Eq1 ((->) String)) => Eq1 TeletypeF  where
+        PutStrLn a x ==# PutStrLn b y = a == b && x == y
+        GetLine a ==# GetLine b = a ==# b
+
+type Teletype = Free TeletypeF
+
+putStrLn' :: String -> Teletype ()
+putStrLn' str = liftF $ PutStrLn str ()
+
+getLine' :: Teletype String
+getLine' = liftF $ GetLine id
+
+foo :: Teletype ()
+foo = do
+        putStrLn' "name?"
+        name <- getLine'
+        putStrLn' ("hello, " ++ name)
+
+fooisfoo :: Bool
+fooisfoo = foo ==# foo
+</pre>

add news item for propellor 4.3.3
diff --git a/doc/news/version_3.3.1.mdwn b/doc/news/version_3.3.1.mdwn
deleted file mode 100644
index 84ab72f..0000000
--- a/doc/news/version_3.3.1.mdwn
+++ /dev/null
@@ -1,8 +0,0 @@
-propellor 3.3.1 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
-   * Apt: Removed the mirrors.kernel.org line from stdSourcesList etc.
-     The mirror CDN has a new implementation that should avoid the problems
-     with httpredir that made an extra mirror sometimes be needed.
-   * Switch Debian CDN address to deb.debian.org.
-   * Tor.hiddenService: Fix bug in torrc's HiddenServicePort configuration.
-     Thanks, Félix Sipma"""]]
\ No newline at end of file
diff --git a/doc/news/version_4.3.3.mdwn b/doc/news/version_4.3.3.mdwn
new file mode 100644
index 0000000..b51ab5d
--- /dev/null
+++ b/doc/news/version_4.3.3.mdwn
@@ -0,0 +1,17 @@
+propellor 4.3.3 released with [[!toggle text="these changes"]]
+[[!toggleable text="""
+   * Hosts can be configured to build propellor using stack, by adding
+     a property:
+         &amp; bootstrapWith (Robustly Stack)
+   * Hosts can be configured to build propellor using cabal, but using
+     only packages installed from the operating system. This
+     will work on eg Debian:
+         &amp; bootstrapWith OSOnly
+   * Iproved fix for bug that sometimes made --spin fail with
+     "fatal: Couldn't find remote ref HEAD". The previous fix didn't work
+     reliably.
+   * User: add systemGroup and use it for systemAccountFor'
+     Thanks, Félix Sipma.
+   * Export a Restic.backup' property.
+     Thanks, Félix Sipma.
+   * Updated stack config to lts-8.22."""]]
\ No newline at end of file

close
diff --git a/doc/todo/spin_failure_HEAD.mdwn b/doc/todo/spin_failure_HEAD.mdwn
index ceea152..1a591b3 100644
--- a/doc/todo/spin_failure_HEAD.mdwn
+++ b/doc/todo/spin_failure_HEAD.mdwn
@@ -126,3 +126,5 @@ Sending privdata (73139 bytes) to kite.kitenet.net ... done
 
 > > > I thought I'd fixed this by disabling buffering of stdin, but 
 > > > it seems not.
+
+> > > > Seems really [[done]] at last! --[[Joey]]

reopen bug
diff --git a/doc/todo/spin_failure_HEAD.mdwn b/doc/todo/spin_failure_HEAD.mdwn
index f838e46..ceea152 100644
--- a/doc/todo/spin_failure_HEAD.mdwn
+++ b/doc/todo/spin_failure_HEAD.mdwn
@@ -124,4 +124,5 @@ Sending privdata (73139 bytes) to kite.kitenet.net ... done
 	        l2 <- hGetLine bob
 	        print l2
 
-> > [[fixed|done]] --[[Joey]]
+> > > I thought I'd fixed this by disabling buffering of stdin, but 
+> > > it seems not.

add bootstrapWith property to support stack and more
* Hosts can be configured to build propellor using stack, by adding
a property:
& bootstrapWith (Robustly Stack)
* Hosts can be configured to build propellor using cabal, but using
only packages installed from the operating system. This
will work on eg Debian:
& bootstrapWith OSOnly
propellor build its config using stack. (This does not affect
how propellor is bootstrapped on a host by "propellor --spin host".)
This has not yet been tested at all! But should probably work fine.
This is based on earlier work by Arnaud Bailly, who made
Propellor.Bootstrap use stack without parameterization.
In Arnaud's patch, stack was installed using wget, but that
only worked on linux-x86_64 and was insecure. I instead chose
to use the distribution packages of stack, like is done for cabal.
Debian stack has haskell-stack now, and it's getting into many
distributions.
This commit was sponsored by Francois Marier on Patreon.
diff --git a/debian/changelog b/debian/changelog
index 24552b2..be0285f 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -2,6 +2,13 @@ propellor (4.3.1) UNRELEASED; urgency=medium
 
   * Added Propellor.Property.FreeDesktop module.
   * Added reservedSpacePercentage to the PartSpec EDSL.
+  * Hosts can be configured to build propellor using stack, by adding
+    a property:
+	& bootstrapWith (Robustly Stack)
+  * Hosts can be configured to build propellor using cabal, but using
+    only packages installed from the operating system. This
+    will work on eg Debian:
+	& bootstrapWith OSOnly
 
  -- Joey Hess <id@joeyh.name>  Wed, 05 Jul 2017 22:57:42 -0400
 
@@ -473,7 +480,8 @@ propellor (3.0.0) unstable; urgency=medium
     a clone of propellor's git repository, or a minimal config, and will
     configure propellor to use a gpg key.
   * Stack support. "git config propellor.buildsystem stack" will make
-    propellor build its config using stack.
+    propellor build its config using stack. (This does not affect
+    how propellor is bootstrapped on a host by "propellor --spin host".)
   * When propellor is installed using stack, propellor --init will
     automatically set propellor.buildsystem=stack.
 
diff --git a/doc/README.mdwn b/doc/README.mdwn
index ea622b3..a4a38c5 100644
--- a/doc/README.mdwn
+++ b/doc/README.mdwn
@@ -41,6 +41,8 @@ see [configuration for the Haskell newbie](https://propellor.branchable.com/hask
 1. Get propellor installed on your development machine (ie, laptop).
      `cabal install propellor`
           or
+     `stack install propellor`
+          or
      `apt-get install propellor`
 2. Run `propellor --init` ; this will set up a `~/.propellor/` git
    repository for you.
diff --git a/doc/todo/use_stack_for_remote_building_propellor.mdwn b/doc/todo/use_stack_for_remote_building_propellor.mdwn
index 265596d..8c8751e 100644
--- a/doc/todo/use_stack_for_remote_building_propellor.mdwn
+++ b/doc/todo/use_stack_for_remote_building_propellor.mdwn
@@ -1,3 +1,16 @@
 Among other features [stack](https://github.com/commercialhaskell/stack/) provides a clean and deep dependency management system that even takes care of installing toolchain (ghc, alex, happy, cabal...) in a segregated environment. Building remote propellor with stack would remove the limitation that code should be compilable with stock ghc from package manager. I have done some preliminary work on this feature in my [github clone](https://github.com/abailly/propellor) for propellor, currently from 2.17.2 branch (I wanted to reuse existing properties). The code is mostly in [Bootstrap](https://github.com/abailly/propellor/blob/master/src/Propellor/Bootstrap.hs) and is currently limited to linux systems. Adapting to FreeBsd should be straightforward as this is supported by slack and there are native builds available. 
 
 If there is interest in such a feature I would be happy to move it to HEAD and provide a patch.
+
+> I've implemented a bootstrapWith property, which can be added to a Host
+> to make it use stack:
+> 
+> 	& bootstrapWith (Robustly Stack)
+> 
+> So, for a propellor install that uses stack entirely, use
+> `stack install propellor` to install it to your laptop,
+> use `propellor --init` to set up `~/.propellor/config,hs`,
+> and in the config file, add the above property to all your 
+> Hosts (perhaps using `map` ..).
+> 
+> I feel that's enough to call this [[done]]. --[[Joey]]
diff --git a/src/Propellor/Bootstrap.hs b/src/Propellor/Bootstrap.hs
index 4b3f2da..baf36e4 100644
--- a/src/Propellor/Bootstrap.hs
+++ b/src/Propellor/Bootstrap.hs
@@ -1,4 +1,8 @@
 module Propellor.Bootstrap (
+	Bootstrapper(..),
+	Builder(..),
+	defaultBootstrapper,
+	getBootstrapper,
 	bootstrapPropellorCommand,
 	checkBinaryCommand,
 	installGitCommand,
@@ -16,71 +20,120 @@ import Data.List
 
 type ShellCommand = String
 
+-- | Different ways that Propellor's dependencies can be installed,
+-- and propellor can be built. The default is `Robustly Cabal`
+--
+-- `Robustly Cabal` and `Robustly Stack` use the OS's native packages
+-- as much as possible to install Cabal, Stack, and propellor's build
+-- dependencies. When necessary, dependencies are built from source
+-- using Cabal or Stack rather than using the OS's native packages.
+--
+-- `OSOnly` uses the OS's native packages of Cabal and all of propellor's
+-- build dependencies. It may not work on all systems.
+data Bootstrapper = Robustly Builder | OSOnly
+	deriving (Show)
+
+data Builder = Cabal | Stack
+	deriving (Show)
+
+defaultBootstrapper :: Bootstrapper
+defaultBootstrapper = Robustly Cabal
+
+-- | Gets the Bootstrapper for the Host propellor is running on.
+getBootstrapper :: Propellor Bootstrapper
+getBootstrapper = go <$> askInfo
+  where
+	go NoInfoVal = defaultBootstrapper
+	go (InfoVal bs) = bs
+
+getBuilder :: Bootstrapper -> Builder
+getBuilder (Robustly b) = b
+getBuilder OSOnly = Cabal
+
 -- Shell command line to ensure propellor is bootstrapped and ready to run.
 -- Should be run inside the propellor config dir, and will install
 -- all necessary build dependencies and build propellor.
-bootstrapPropellorCommand :: Maybe System -> ShellCommand
-bootstrapPropellorCommand msys = checkDepsCommand msys ++
+bootstrapPropellorCommand :: Bootstrapper -> Maybe System -> ShellCommand
+bootstrapPropellorCommand bs msys = checkDepsCommand bs msys ++
 	"&& if ! test -x ./propellor; then "
-		++ buildCommand ++
-	"; fi;" ++ checkBinaryCommand
+		++ buildCommand bs ++
+	"; fi;" ++ checkBinaryCommand bs
 
 -- Use propellor --check to detect if the local propellor binary has
 -- stopped working (eg due to library changes), and must be rebuilt.
-checkBinaryCommand :: ShellCommand
-checkBinaryCommand = "if test -x ./propellor && ! ./propellor --check; then " ++ go ++ "; fi"
+checkBinaryCommand :: Bootstrapper -> ShellCommand
+checkBinaryCommand bs = "if test -x ./propellor && ! ./propellor --check; then " ++ go (getBuilder bs) ++ "; fi"
   where
-	go = intercalate " && "
+	go Cabal = intercalate " && "
 		[ "cabal clean"
-		, buildCommand
+		, buildCommand bs
+		]
+	go Stack = intercalate " && "
+		[ "stack clean"
+		, buildCommand bs
 		]
 
-buildCommand :: ShellCommand
-buildCommand = intercalate " && "
-	[ "cabal configure"
-	, "cabal build propellor-config"
-	, "ln -sf dist/build/propellor-config/propellor-config propellor"
-	]
+buildCommand :: Bootstrapper -> ShellCommand
+buildCommand bs = intercalate " && " (go (getBuilder bs))
+  where
+	go Cabal =
+		[ "cabal configure"
+		, "cabal build propellor-config"
+		, "ln -sf dist/build/propellor-config/propellor-config propellor"
+		]
+	go Stack =
+		[ "stack build :propellor-config"
+		, "ln -sf $(stack path --dist-dir)/build/propellor-config propellor"
+		]
 
 -- Run cabal configure to check if all dependencies are installed;
 -- if not, run the depsCommand.
-checkDepsCommand :: Maybe System -> ShellCommand
-checkDepsCommand sys = "if ! cabal configure >/dev/null 2>&1; then " ++ depsCommand sys ++ "; fi"
+checkDepsCommand :: Bootstrapper -> Maybe System -> ShellCommand
+checkDepsCommand bs sys = go (getBuilder bs)
+  where
+	go Cabal = "if ! cabal configure >/dev/null 2>&1; then " ++ depsCommand bs sys ++ "; fi"
+	go Stack = "if ! stack --version >/dev/null 2>&1; then " ++ depsCommand bs sys ++ "; fi"
 
--- Install build dependencies of propellor.
---
--- First, try to install ghc, cabal, gnupg, and all haskell libraries that
--- propellor uses from OS packages.
+-- Install build dependencies of propellor, using the specified
+-- Bootstrapper.
 --
+-- When bootstrapping Robustly, first try to install the builder, 
+-- and all haskell libraries that propellor uses from OS packages.
 -- Some packages may not be available in some versions of Debian
 -- (eg, Debian wheezy lacks async), or propellor may need a newer version.
--- So, as a second step, cabal is used to install all dependencies.
+-- So, as a second step, any other dependencies are installed from source
+-- using the builder.
 --
 -- Note: May succeed and leave some deps not installed.
-depsCommand :: Maybe System -> ShellCommand
-depsCommand msys = "( " ++ intercalate " ; " (concat [osinstall, cabalinstall]) ++ " ) || true"
+depsCommand :: Bootstrapper -> Maybe System -> ShellCommand
+depsCommand bs msys = "( " ++ intercalate " ; " (go bs) ++ ") || true"
   where
-	osinstall = case msys of
-		Just (System (FreeBSD _) _) -> map pkginstall fbsddeps
-		Just (System (ArchLinux) _) -> map pacmaninstall archlinuxdeps
-		Just (System (Debian _ _) _) -> useapt
-		Just (System (Buntish _) _) -> useapt
-		-- assume a debian derived system when not specified
-		Nothing -> useapt
-
-	useapt = "apt-get update" : map aptinstall debdeps
-

(Diff truncated)
add news item for propellor 4.3.2
diff --git a/doc/news/version_4.3.0.mdwn b/doc/news/version_4.3.0.mdwn
deleted file mode 100644
index f300b98..0000000
--- a/doc/news/version_4.3.0.mdwn
+++ /dev/null
@@ -1,6 +0,0 @@
-propellor 4.3.0 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
-   * DiskImage: Removed grubBooted; properties that used to need it as a
-     parameter now look at Info about the bootloader that is installed in
-     the chroot that the disk image is created from.
-     (API change)"""]]
\ No newline at end of file
diff --git a/doc/news/version_4.3.2.mdwn b/doc/news/version_4.3.2.mdwn
new file mode 100644
index 0000000..1a85d2d
--- /dev/null
+++ b/doc/news/version_4.3.2.mdwn
@@ -0,0 +1,3 @@
+propellor 4.3.2 released with [[!toggle text="these changes"]]
+[[!toggleable text="""
+   * Really include Propellor.Property.FreeDesktop."""]]
\ No newline at end of file

add news item for propellor 4.3.1
diff --git a/doc/news/version_3.3.0.mdwn b/doc/news/version_3.3.0.mdwn
deleted file mode 100644
index 19bd566..0000000
--- a/doc/news/version_3.3.0.mdwn
+++ /dev/null
@@ -1,26 +0,0 @@
-propellor 3.3.0 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
-   * Arch Linux is now supported by Propellor!
-     Thanks to Zihao Wang for this port.
-   * Added Propellor.Property.Pacman for Arch's package manager.
-     Maintained by Zihao Wang.
-   * The types of some properties changed; eg from Property DebianLike
-     to Property (DebianLike + ArchLinux). Also, DebianLike and Linux
-     are no longer type synonyms; propellor now knows that Linux includes
-     ArchLinux. This could require updates to code, so is a minor API change.
-   * GHC's fileSystemEncoding is used for all String IO, to avoid
-     encoding-related crashes in eg, Propellor.Property.File.
-   * Add --build option to simply build config.hs.
-   * More informative usage message. Thanks, Daniel Brooks
-   * Tor.hiddenService' added to support multiple ports.
-     Thanks, Félix Sipma.
-   * Apt.noPDiffs added.
-     Thanks, Sean Whitton.
-   * stack.yaml: Compile with GHC 8.0.1 against lts-7.16.
-     Thanks, Andrew Cowie.
-   * Added Propellor.Property.File.configFileName and related functions
-     to generate good filenames for config directories.
-   * Added Apt.suiteAvailablePinned, Apt.pinnedTo.
-     Thanks, Sean Whitton.
-   * Added File.containsBlock
-     Thanks, Sean Whitton."""]]
\ No newline at end of file
diff --git a/doc/news/version_4.3.1.mdwn b/doc/news/version_4.3.1.mdwn
new file mode 100644
index 0000000..5c07307
--- /dev/null
+++ b/doc/news/version_4.3.1.mdwn
@@ -0,0 +1,4 @@
+propellor 4.3.1 released with [[!toggle text="these changes"]]
+[[!toggleable text="""
+   * Added Propellor.Property.FreeDesktop module.
+   * Added reservedSpacePercentage to the PartSpec EDSL."""]]
\ No newline at end of file

add news item for propellor 4.3.0
diff --git a/doc/news/version_3.2.3.mdwn b/doc/news/version_3.2.3.mdwn
deleted file mode 100644
index 3689a90..0000000
--- a/doc/news/version_3.2.3.mdwn
+++ /dev/null
@@ -1,9 +0,0 @@
-propellor 3.2.3 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
-   * Improve extraction of gpg secret key id list, to work with gpg 2.1.
-   * The propellor wrapper checks if ./config.hs exists; if so it runs
-     using the configuration in the current directory, rather than
-     ~/.propellor/config.hs
-   * Debootstap: Fix too tight permissions lock down of debootstrapped
-     chroots, which prevented non-root users from doing anything in the
-     chroot."""]]
\ No newline at end of file
diff --git a/doc/news/version_4.2.0.mdwn b/doc/news/version_4.2.0.mdwn
deleted file mode 100644
index 6ccb23f..0000000
--- a/doc/news/version_4.2.0.mdwn
+++ /dev/null
@@ -1,13 +0,0 @@
-propellor 4.2.0 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
-   * DiskImage.grubBooted no longer takes a BIOS parameter,
-     and no longer implicitly adds Grub.installed to the properties of
-     the disk image. If you used DiskImage.grubBooted, you'll need to update
-     your propellor configuration, removing the BIOS parameter from
-     grubBooted and adding a Grub.installed property to the disk image, eg:
-         &amp; Grub.installed PC
-     (API change)
-   * Grub.installed: Avoid running update-grub when used in a chroot, since
-     it will get confused.
-   * DiskImage.Finalization: Simplified this type since it does not need to
-     be used to install packages anymore. (API change)"""]]
\ No newline at end of file
diff --git a/doc/news/version_4.3.0.mdwn b/doc/news/version_4.3.0.mdwn
new file mode 100644
index 0000000..f300b98
--- /dev/null
+++ b/doc/news/version_4.3.0.mdwn
@@ -0,0 +1,6 @@
+propellor 4.3.0 released with [[!toggle text="these changes"]]
+[[!toggleable text="""
+   * DiskImage: Removed grubBooted; properties that used to need it as a
+     parameter now look at Info about the bootloader that is installed in
+     the chroot that the disk image is created from.
+     (API change)"""]]
\ No newline at end of file

add news item for propellor 4.2.0
diff --git a/doc/news/version_4.1.0.mdwn b/doc/news/version_4.1.0.mdwn
deleted file mode 100644
index 7cc5e36..0000000
--- a/doc/news/version_4.1.0.mdwn
+++ /dev/null
@@ -1,26 +0,0 @@
-propellor 4.1.0 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
-   * User.hasInsecurePassword makes sure shadow passwords are enabled,
-     so if the insecure password is later changed, the new password won't be
-     exposed.
-   * Bugfix: Apache.httpsVirtualHost' must create ssl/hn/ dir earlier
-     Thanks, Sean Whitton.
-   * Bootstrap.clonedFrom: Fix bug that broke copying .git/config into
-     chroot.
-   * Diskimage.imageExists: Align disk image size to multiple of 4096
-     sector size, since some programs (such as VBoxManage convertdd)
-     refuse to operate on disk images not aligned to a sector size.
-   * Bootstrap.bootstrappedFrom: Fix bug that caused propellor to only
-     be built from the bootstrapped config the first time.
-   * Bootstrap.bootstrappedFrom: Avoid doing anything when not run in a
-     chroot.
-   * When provisioning a container, output was buffered until the whole
-     process was done; now output will be displayed immediately.
-   * LightDM.autoLogin: Make it require LightDM.installed.
-     (minor API change as the type changed)
-   * Propellor.Property.XFCE added with some useful properties for the
-     desktop environment.
-   * Added File.applyPath property.
-   * Added File.checkOverwrite.
-   * File.isCopyOf: Fix bug that prevented this property from working
-     when the destination file did not yet exist."""]]
\ No newline at end of file
diff --git a/doc/news/version_4.2.0.mdwn b/doc/news/version_4.2.0.mdwn
new file mode 100644
index 0000000..6ccb23f
--- /dev/null
+++ b/doc/news/version_4.2.0.mdwn
@@ -0,0 +1,13 @@
+propellor 4.2.0 released with [[!toggle text="these changes"]]
+[[!toggleable text="""
+   * DiskImage.grubBooted no longer takes a BIOS parameter,
+     and no longer implicitly adds Grub.installed to the properties of
+     the disk image. If you used DiskImage.grubBooted, you'll need to update
+     your propellor configuration, removing the BIOS parameter from
+     grubBooted and adding a Grub.installed property to the disk image, eg:
+         &amp; Grub.installed PC
+     (API change)
+   * Grub.installed: Avoid running update-grub when used in a chroot, since
+     it will get confused.
+   * DiskImage.Finalization: Simplified this type since it does not need to
+     be used to install packages anymore. (API change)"""]]
\ No newline at end of file

add news item for propellor 4.1.0
diff --git a/doc/news/version_4.0.6.mdwn b/doc/news/version_4.0.6.mdwn
deleted file mode 100644
index a57c39c..0000000
--- a/doc/news/version_4.0.6.mdwn
+++ /dev/null
@@ -1,5 +0,0 @@
-propellor 4.0.6 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
-   * Fix bug that sometimes made --spin fail with
-     "fatal: Couldn't find remote ref HEAD"
-   * Display error and warning messages to stderr, not stdout."""]]
\ No newline at end of file
diff --git a/doc/news/version_4.1.0.mdwn b/doc/news/version_4.1.0.mdwn
new file mode 100644
index 0000000..7cc5e36
--- /dev/null
+++ b/doc/news/version_4.1.0.mdwn
@@ -0,0 +1,26 @@
+propellor 4.1.0 released with [[!toggle text="these changes"]]
+[[!toggleable text="""
+   * User.hasInsecurePassword makes sure shadow passwords are enabled,
+     so if the insecure password is later changed, the new password won't be
+     exposed.
+   * Bugfix: Apache.httpsVirtualHost' must create ssl/hn/ dir earlier
+     Thanks, Sean Whitton.
+   * Bootstrap.clonedFrom: Fix bug that broke copying .git/config into
+     chroot.
+   * Diskimage.imageExists: Align disk image size to multiple of 4096
+     sector size, since some programs (such as VBoxManage convertdd)
+     refuse to operate on disk images not aligned to a sector size.
+   * Bootstrap.bootstrappedFrom: Fix bug that caused propellor to only
+     be built from the bootstrapped config the first time.
+   * Bootstrap.bootstrappedFrom: Avoid doing anything when not run in a
+     chroot.
+   * When provisioning a container, output was buffered until the whole
+     process was done; now output will be displayed immediately.
+   * LightDM.autoLogin: Make it require LightDM.installed.
+     (minor API change as the type changed)
+   * Propellor.Property.XFCE added with some useful properties for the
+     desktop environment.
+   * Added File.applyPath property.
+   * Added File.checkOverwrite.
+   * File.isCopyOf: Fix bug that prevented this property from working
+     when the destination file did not yet exist."""]]
\ No newline at end of file

update debian stable suite
diff --git a/doc/haskell_newbie.mdwn b/doc/haskell_newbie.mdwn
index dc3c54a..8f3b60c 100644
--- a/doc/haskell_newbie.mdwn
+++ b/doc/haskell_newbie.mdwn
@@ -53,7 +53,7 @@ mylaptop = host "mylaptop.example.com" $ props
 
 myserver :: Host
 myserver = host "server.example.com" $ props
-	& osDebian (Stable "jessie") X86_64
+	& osDebian (Stable "stretch") X86_64
 	& Apt.stdSourcesList
 	& Apt.installed ["ssh"]
 """]]

add news item for propellor 4.0.6
diff --git a/doc/news/version_4.0.5.mdwn b/doc/news/version_4.0.5.mdwn
deleted file mode 100644
index 0b746ca..0000000
--- a/doc/news/version_4.0.5.mdwn
+++ /dev/null
@@ -1,4 +0,0 @@
-propellor 4.0.5 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
-   * Switch cabal file from Extensions to Default-Extensions to fix
-     new picky hackage rejection."""]]
\ No newline at end of file
diff --git a/doc/news/version_4.0.6.mdwn b/doc/news/version_4.0.6.mdwn
new file mode 100644
index 0000000..a57c39c
--- /dev/null
+++ b/doc/news/version_4.0.6.mdwn
@@ -0,0 +1,5 @@
+propellor 4.0.6 released with [[!toggle text="these changes"]]
+[[!toggleable text="""
+   * Fix bug that sometimes made --spin fail with
+     "fatal: Couldn't find remote ref HEAD"
+   * Display error and warning messages to stderr, not stdout."""]]
\ No newline at end of file

Fix bug that sometimes made --spin fail with "fatal: Couldn't find remote ref HEAD"
Tricky stdin buffering problem.
An easier fix would have been:
hSetBuffering stdin NoBuffering
But that approach is less robust; even with NoBuffering, anything that
uses hLookAhead causes 1 byte of buffering. And, any reads from stdin
before hSetBuffering would still cause the problem. Instead, I used a
bigger hammer that will always work. It involves a bit more CPU work,
but this is data that is already being fed through ssh; copying it one
more time won't cause a measurable performance impact.
This commit was sponsored by Jack Hill on Patreon.
diff --git a/debian/changelog b/debian/changelog
index d26e007..086c82c 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,5 +1,7 @@
 propellor (4.0.6) UNRELEASED; urgency=medium
 
+  * Fix bug that sometimes made --spin fail with 
+    "fatal: Couldn't find remote ref HEAD"
   * Display error and warning messages to stderr, not stdout.
 
  -- Joey Hess <id@joeyh.name>  Sun, 18 Jun 2017 16:19:41 -0400
diff --git a/doc/todo/spin_failure_HEAD.mdwn b/doc/todo/spin_failure_HEAD.mdwn
index af525f6..f838e46 100644
--- a/doc/todo/spin_failure_HEAD.mdwn
+++ b/doc/todo/spin_failure_HEAD.mdwn
@@ -51,7 +51,6 @@ Sending privdata (73139 bytes) to kite.kitenet.net ... done
 [2017-06-18 16:27:13 EDT] received marked GITPUSH
 [2017-06-18 16:27:13 EDT] command line:  GitPush 11 12
 16:27:13.953638 pkt-line.c:80           packet:        fetch< 3a3c8a731d169a2768dd243581803dcb7b275049 HEAD\0multi_ack thin-pack side-band side-band-64k ofs-delta shallow deepen-since deepen-not deepen-relative no-progress include-tag multi_ack_detailed symref=HEAD:refs/heads/joeyconfig agent=git/2.11.0
-16:27:13.953638 pkt-line.c:80           packet:        fetch< 3a3c8a731d169a2768dd243581803dcb7b275049 HEAD\0multi_ack thin-pack side-band side-band-64k ofs-delta shallow deepen-since deepen-not deepen-relative no-progress include-tag multi_ack_detailed symref=HEAD:refs/heads/joeyconfig agent=git/2.11.0
 16:27:13.953781 pkt-line.c:80           packet:        fetch< 86b077b7a21efd5484dfaeee3c31fc5f3c151f6c refs/heads/confpairs
 16:27:13.953789 pkt-line.c:80           packet:        fetch< e03e4bf0f1e557f87d1fe7e01a6de7866296fce6 refs/heads/d-i
 16:27:13.953795 pkt-line.c:80           packet:        fetch< 3a3c8a731d169a2768dd243581803dcb7b275049 refs/heads/joeyconfig
@@ -94,7 +93,35 @@ Sending privdata (73139 bytes) to kite.kitenet.net ... done
 > > * Could be in gitPushHelper, perhaps it's failing to write
 > >   some of the first lines somehow?
 > > * Could be something on the remote side is consuming stdin
-> >   that is not supposed to, and eats some of the protocol.a
+> >   that is not supposed to, and eats some of the protocol.
+> > 
 > > 
 > > I added debug dumping to gitPushHelper, and it seems to be
-> > reading the same truncated data.
+> > reading the same truncated data, so it seems the problem is not there.
+> > 
+> > Aha! The problem comes from stdin/stdInput confusion here:
+
+		req NeedGitPush gitPushMarker $ \_ -> do
+			hin <- dup stdInput
+			hout <- dup stdOutput
+			hClose stdin
+			hClose stdout
+
+> > A line read from stdin just before the dup gets the first line of the protocol
+> > as expected. But reading from stdInput starts with a later line. 
+> > Apparently data is being buffered in the stdin Handle, so gitPushHelper,
+> > which reads from the Fd, does not see it.
+> >
+> > Here's a simple test case. Feeding this 2 lines on stdin will
+> > print the first and then fail with "hGetLine: end of file".
+> > The second line is lost in the buffer. This test case behaves
+> > like that reliably, so I'm surprised propellor only fails sometimes.
+
+	main = do
+	        l <- hGetLine stdin
+	        print l
+	        bob <- fdToHandle stdInput
+	        l2 <- hGetLine bob
+	        print l2
+
+> > [[fixed|done]] --[[Joey]]
diff --git a/src/Propellor/Spin.hs b/src/Propellor/Spin.hs
index d0ce4d0..cc5fa0e 100644
--- a/src/Propellor/Spin.hs
+++ b/src/Propellor/Spin.hs
@@ -186,26 +186,8 @@ update forhost = do
 		writeFileProtected privfile
 
 	whenM hasGitRepo $
-		req NeedGitPush gitPushMarker $ \_ -> do
-			hin <- dup stdInput
-			hout <- dup stdOutput
-			hClose stdin
-			hClose stdout
-			-- Not using git pull because git 2.5.0 badly
-			-- broke its option parser.
-			unlessM (boolSystemNonConcurrent "git" (pullparams hin hout)) $
-				errorMessage "git fetch from client failed"
-			unlessM (boolSystemNonConcurrent "git" [Param "merge", Param "FETCH_HEAD"]) $
-				errorMessage "git merge from client failed"
+		gitPullFromUpdateServer
   where
-	pullparams hin hout =
-		[ Param "fetch"
-		, Param "--progress"
-		, Param "--upload-pack"
-		, Param $ "./propellor --gitpush " ++ show hin ++ " " ++ show hout
-		, Param "."
-		]
-
 	-- When --spin --relay is run, get a privdata file
 	-- to be relayed to the target host.
 	privfile = maybe privDataLocal privDataRelay forhost
@@ -336,29 +318,6 @@ sendPrecompiled hn = void $ actionMessage "Uploading locally compiled propellor
 		, "rm -f " ++ remotetarball
 		]
 
--- Shim for git push over the propellor ssh channel.
--- Reads from stdin and sends it to hout;
--- reads from hin and sends it to stdout.
-gitPushHelper :: Fd -> Fd -> IO ()
-gitPushHelper hin hout = void $ fromstdin `concurrently` tostdout
-  where
-	fromstdin = do
-		h <- fdToHandle hout
-		connect stdin h
-	tostdout = do
-		h <- fdToHandle hin
-		connect h stdout
-	connect fromh toh = do
-		b <- B.hGetSome fromh 40960
-		if B.null b
-			then do
-				hClose fromh
-				hClose toh
-			else do
-				B.hPut toh b
-				hFlush toh
-				connect fromh toh
-
 mergeSpin :: IO ()
 mergeSpin = do
 	branch <- getCurrentBranch
@@ -386,3 +345,62 @@ findLastNonSpinCommit = do
 
 spinCommitMessage :: String
 spinCommitMessage = "propellor spin"
+
+-- Stdin and stdout are connected to the updateServer over ssh.
+-- Request that it run git upload-pack, and connect that up to a git fetch
+-- to receive the data.
+gitPullFromUpdateServer :: IO ()
+gitPullFromUpdateServer = req NeedGitPush gitPushMarker $ \_ -> do
+	-- IO involving stdin can cause data to be buffered in the Handle
+	-- (even when it's set NoBuffering), but we need to pass a FD to 
+	-- git fetch containing all of stdin after the gitPushMarker,
+	-- including any that has been buffered.
+	--
+	-- To do so, create a pipe, and forward stdin, including any
+	-- buffered part, through it.
+	(pread, pwrite) <- System.Posix.IO.createPipe
+	hwrite <- fdToHandle pwrite
+	_ <- async $ stdin *>* hwrite
+	let hin = pread
+	hout <- dup stdOutput
+	hClose stdout
+	-- Not using git pull because git 2.5.0 badly
+	-- broke its option parser.
+	unlessM (boolSystemNonConcurrent "git" (fetchparams hin hout)) $
+		errorMessage "git fetch from client failed"
+	unlessM (boolSystemNonConcurrent "git" [Param "merge", Param "FETCH_HEAD"]) $
+		errorMessage "git merge from client failed"
+  where
+	fetchparams hin hout =
+		[ Param "fetch"
+		, Param "--progress"
+		, Param "--upload-pack"
+		, Param $ "./propellor --gitpush " ++ show hin ++ " " ++ show hout
+		, Param "."
+		]
+
+-- Shim for git push over the propellor ssh channel.
+-- Reads from stdin and sends it to hout;
+-- reads from hin and sends it to stdout.
+gitPushHelper :: Fd -> Fd -> IO ()
+gitPushHelper hin hout = void $ fromstdin `concurrently` tostdout
+  where
+	fromstdin = do
+		h <- fdToHandle hout
+		stdin *>* h
+	tostdout = do
+		h <- fdToHandle hin
+		h *>* stdout
+
+-- Forward data from one handle to another.
+(*>*) :: Handle -> Handle -> IO ()
+fromh *>* toh = do
+	b <- B.hGetSome fromh 40960
+	if B.null b
+		then do
+			hClose fromh
+			hClose toh
+		else do
+			B.hPut toh b
+			hFlush toh
+			fromh *>* toh

propellor spin
diff --git a/doc/todo/spin_failure_HEAD.mdwn b/doc/todo/spin_failure_HEAD.mdwn
index b1ec4de..af525f6 100644
--- a/doc/todo/spin_failure_HEAD.mdwn
+++ b/doc/todo/spin_failure_HEAD.mdwn
@@ -91,7 +91,10 @@ Sending privdata (73139 bytes) to kite.kitenet.net ... done
 > > 
 > > * Fairly sure it's not on the local side's sendGitUpdate, 
 > >   where "git upload-pack ." is simply run and fed over ssh.
-> > * Could be in gitPushHelper, perhaps it's truncating
-> >   data somehow?
+> > * Could be in gitPushHelper, perhaps it's failing to write
+> >   some of the first lines somehow?
 > > * Could be something on the remote side is consuming stdin
-> >   that is not supposed to, and eats some of the protocol.
+> >   that is not supposed to, and eats some of the protocol.a
+> > 
+> > I added debug dumping to gitPushHelper, and it seems to be
+> > reading the same truncated data.

propellor spin
diff --git a/doc/todo/spin_failure_HEAD.mdwn b/doc/todo/spin_failure_HEAD.mdwn
index 6013db6..b1ec4de 100644
--- a/doc/todo/spin_failure_HEAD.mdwn
+++ b/doc/todo/spin_failure_HEAD.mdwn
@@ -88,3 +88,10 @@ Sending privdata (73139 bytes) to kite.kitenet.net ... done
 > > of git protocol were not sent.
 > >
 > > Question now is, where is the mangling happening?
+> > 
+> > * Fairly sure it's not on the local side's sendGitUpdate, 
+> >   where "git upload-pack ." is simply run and fed over ssh.
+> > * Could be in gitPushHelper, perhaps it's truncating
+> >   data somehow?
+> > * Could be something on the remote side is consuming stdin
+> >   that is not supposed to, and eats some of the protocol.

propellor spin
diff --git a/doc/todo/spin_failure_HEAD.mdwn b/doc/todo/spin_failure_HEAD.mdwn
index 39b64cb..6013db6 100644
--- a/doc/todo/spin_failure_HEAD.mdwn
+++ b/doc/todo/spin_failure_HEAD.mdwn
@@ -39,8 +39,8 @@ but the rest of the propellor runs doesn't happen. --[[Joey]]
 > > outputConcurrent. After I fixed it to use errorConcurrent,
 > > it displayed the "git fetch from client failed" error message instead.
 > > 
-> > Next step is probably to enable `GIT_TRACE_PACKET` debugging.
-> > I did so on kite, but then propellor --spin succeeded.
+> > Next step is probably to enable `GIT_TRACE_PACKET` debugging
+> > of the git fetch. I did so on kite, but then propellor --spin succeeded.
 > > Here's the debug output I got when it worked, for later comparison
 > > next time it fails. Note the HEAD ref is given first thing.
 
@@ -51,10 +51,40 @@ Sending privdata (73139 bytes) to kite.kitenet.net ... done
 [2017-06-18 16:27:13 EDT] received marked GITPUSH
 [2017-06-18 16:27:13 EDT] command line:  GitPush 11 12
 16:27:13.953638 pkt-line.c:80           packet:        fetch< 3a3c8a731d169a2768dd243581803dcb7b275049 HEAD\0multi_ack thin-pack side-band side-band-64k ofs-delta shallow deepen-since deepen-not deepen-relative no-progress include-tag multi_ack_detailed symref=HEAD:refs/heads/joeyconfig agent=git/2.11.0
-(omitted 364 lines of refs)
+16:27:13.953638 pkt-line.c:80           packet:        fetch< 3a3c8a731d169a2768dd243581803dcb7b275049 HEAD\0multi_ack thin-pack side-band side-band-64k ofs-delta shallow deepen-since deepen-not deepen-relative no-progress include-tag multi_ack_detailed symref=HEAD:refs/heads/joeyconfig agent=git/2.11.0
+16:27:13.953781 pkt-line.c:80           packet:        fetch< 86b077b7a21efd5484dfaeee3c31fc5f3c151f6c refs/heads/confpairs
+16:27:13.953789 pkt-line.c:80           packet:        fetch< e03e4bf0f1e557f87d1fe7e01a6de7866296fce6 refs/heads/d-i
+16:27:13.953795 pkt-line.c:80           packet:        fetch< 3a3c8a731d169a2768dd243581803dcb7b275049 refs/heads/joeyconfig
+16:27:13.953801 pkt-line.c:80           packet:        fetch< ee56d3793be3a8c0c268d8afdc642ef92b879269 refs/heads/master
+16:27:13.953807 pkt-line.c:80           packet:        fetch< 51be061c90ca7539d7f8b804007cd9942f316860 refs/heads/precompiled
+16:27:13.953812 pkt-line.c:80           packet:        fetch< 48c0e1107ea4a89a22e71c1cba0bdc238d119a9f refs/heads/resourceconflict
+16:27:13.953818 pkt-line.c:80           packet:        fetch< dbfac89a85485f8ca2107792a3ce964c06adefbf refs/heads/typed-os-requirements
+16:27:13.953823 pkt-line.c:80           packet:        fetch< 96a4fcf180885788959d7dc136dbef544270fa81 refs/heads/wip-bytestring-privdata
+16:27:13.953829 pkt-line.c:80           packet:        fetch< ee35c58303221ddb4c83c33eb12a52c59cd482c2 refs/remotes/abailly/master
+16:27:13.953834 pkt-line.c:80           packet:        fetch< baf65fa9fff4b8451ba7f1ee129484723a8deb9b refs/remotes/db48x/fstab-swap
+16:27:13.953839 pkt-line.c:80           packet:        fetch< 7d8f9dbf60f8ab345a75c4ee4f8c457d0fde5b43 refs/remotes/db48x/git-in-emtpy-directory
+16:27:13.953844 pkt-line.c:80           packet:        fetch< 17abde8439d17d49676f549f357f45eb2adce868 refs/remotes/db48x/master
+16:27:13.953849 pkt-line.c:80           packet:        fetch< de50503e4dbdea853e899f01e8828cf4f454dd57 refs/remotes/dgit/dgit/sid
+(omitted 300+ lines of refs)
 16:27:14.352945 pkt-line.c:80           packet:        fetch< 0000
 From .
  * branch              HEAD       -> FETCH_HEAD
 16:27:14.379922 pkt-line.c:80           packet:        fetch> 0000
 Sending git update to kite.kitenet.net ... done
 </pre>
+
+> > Aha! My next spin failed again, with this debug:
+
+<pre>
+Sending privdata (73139 bytes) to kite.kitenet.net ... done
+[2017-06-18 16:31:15 EDT] received marked PRIVDATA 
+[2017-06-18 16:31:15 EDT] requested marked GITPUSH
+[2017-06-18 16:31:16 EDT] received marked GITPUSH
+[2017-06-18 16:31:16 EDT] command line:  GitPush 11 12
+16:31:16.361717 pkt-line.c:80           packet:        fetch< 17abde8439d17d49676f549f357f45eb2adce868 refs/remotes/db48x/master
+<pre>
+
+> > So there's an actual protocol error here; the first 13 lines
+> > of git protocol were not sent.
+> >
+> > Question now is, where is the mangling happening?

propellor spin
diff --git a/doc/todo/spin_failure_HEAD.mdwn b/doc/todo/spin_failure_HEAD.mdwn
index 7c4976a..39b64cb 100644
--- a/doc/todo/spin_failure_HEAD.mdwn
+++ b/doc/todo/spin_failure_HEAD.mdwn
@@ -39,4 +39,22 @@ but the rest of the propellor runs doesn't happen. --[[Joey]]
 > > outputConcurrent. After I fixed it to use errorConcurrent,
 > > it displayed the "git fetch from client failed" error message instead.
 > > 
-> > 
+> > Next step is probably to enable `GIT_TRACE_PACKET` debugging.
+> > I did so on kite, but then propellor --spin succeeded.
+> > Here's the debug output I got when it worked, for later comparison
+> > next time it fails. Note the HEAD ref is given first thing.
+
+<pre>
+Sending privdata (73139 bytes) to kite.kitenet.net ... done
+[2017-06-18 16:27:12 EDT] received marked PRIVDATA 
+[2017-06-18 16:27:12 EDT] requested marked GITPUSH
+[2017-06-18 16:27:13 EDT] received marked GITPUSH
+[2017-06-18 16:27:13 EDT] command line:  GitPush 11 12
+16:27:13.953638 pkt-line.c:80           packet:        fetch< 3a3c8a731d169a2768dd243581803dcb7b275049 HEAD\0multi_ack thin-pack side-band side-band-64k ofs-delta shallow deepen-since deepen-not deepen-relative no-progress include-tag multi_ack_detailed symref=HEAD:refs/heads/joeyconfig agent=git/2.11.0
+(omitted 364 lines of refs)
+16:27:14.352945 pkt-line.c:80           packet:        fetch< 0000
+From .
+ * branch              HEAD       -> FETCH_HEAD
+16:27:14.379922 pkt-line.c:80           packet:        fetch> 0000
+Sending git update to kite.kitenet.net ... done
+</pre>

propellor spin
diff --git a/doc/todo/spin_failure_HEAD.mdwn b/doc/todo/spin_failure_HEAD.mdwn
index f693b4a..7c4976a 100644
--- a/doc/todo/spin_failure_HEAD.mdwn
+++ b/doc/todo/spin_failure_HEAD.mdwn
@@ -15,3 +15,28 @@ but the rest of the propellor runs doesn't happen. --[[Joey]]
 > oldstable on it (new install).
 > 
 > So, transient and/or network-related.. --[[Joey]]
+
+> > Happening again spinning kite over satelite, but not other hosts. 
+> > I enabled propellor.debug, and here's what it showed on kite:
+
+	Sending privdata (73139 bytes) to kite.kitenet.net ... done
+	[2017-06-18 16:01:08 EDT] received marked PRIVDATA 
+	[2017-06-18 16:01:08 EDT] requested marked GITPUSH
+	[2017-06-18 16:01:11 EDT] received marked GITPUSH
+	[2017-06-18 16:01:11 EDT] command line:  GitPush 11 12
+	fatal: Couldn't find remote ref HEAD
+	propellor: <stdout>: hPutStr: illegal operation (handle is closed)
+	fatal: The remote end hung up unexpectedly
+	Sending git update to kite.kitenet.net ... failed
+
+> > Seem that what's failing is "git fetch" when propellor
+> > runs it with --upload-pack used to run propellor --gitpush.
+> > 
+> > The "fatal: Couldn't find remote ref HEAD" comes from git fetch,
+> > I think when no HEAD is in the list of remote refs.
+> > 
+> > The hPutStr error was a red herring; errorMessage was using
+> > outputConcurrent. After I fixed it to use errorConcurrent,
+> > it displayed the "git fetch from client failed" error message instead.
+> > 
+> > 

todo: sbuild should use apt-cacher-ng
diff --git a/doc/todo/sbuild_setup_should_use_apt-cacher-ng.mdwn b/doc/todo/sbuild_setup_should_use_apt-cacher-ng.mdwn
new file mode 100644
index 0000000..450416f
--- /dev/null
+++ b/doc/todo/sbuild_setup_should_use_apt-cacher-ng.mdwn
@@ -0,0 +1,11 @@
+The properties in `Sbuild.hs` should use apt-cacher-ng instead of a bind mount of `/var/cache/apt/archives`.  This has at least three advantages:
+
+1. more than one instance of sbuild can run at once
+
+2. sbuild can run even if apt is doing something else with its cache
+
+3. the `piupartsConf` properties are no longer needed.
+
+--spwhitton
+
+[[!tag user/spwhitton]]

add news item for propellor 4.0.5
diff --git a/doc/news/version_4.0.4.mdwn b/doc/news/version_4.0.4.mdwn
deleted file mode 100644
index 411af6d..0000000
--- a/doc/news/version_4.0.4.mdwn
+++ /dev/null
@@ -1,5 +0,0 @@
-propellor 4.0.4 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
-   * Propellor.Property.Restic added for yet another backup program.
-     Thanks, Félix Sipma.
-   * Removed dependency on MissingH, instead depends on split and hashable."""]]
\ No newline at end of file
diff --git a/doc/news/version_4.0.5.mdwn b/doc/news/version_4.0.5.mdwn
new file mode 100644
index 0000000..0b746ca
--- /dev/null
+++ b/doc/news/version_4.0.5.mdwn
@@ -0,0 +1,4 @@
+propellor 4.0.5 released with [[!toggle text="these changes"]]
+[[!toggleable text="""
+   * Switch cabal file from Extensions to Default-Extensions to fix
+     new picky hackage rejection."""]]
\ No newline at end of file

add news item for propellor 4.0.4
diff --git a/doc/news/version_4.0.3.mdwn b/doc/news/version_4.0.3.mdwn
deleted file mode 100644
index eb46728..0000000
--- a/doc/news/version_4.0.3.mdwn
+++ /dev/null
@@ -1,6 +0,0 @@
-propellor 4.0.3 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
-   * Added Fstab.listed, Fstab.swap, and Mount.swapOn properties.
-     Thanks, Daniel Brooks.
-   * Added Propellor.Property.Bootstrap, which can be used to make
-     disk images contain their own installation of propellor."""]]
\ No newline at end of file
diff --git a/doc/news/version_4.0.4.mdwn b/doc/news/version_4.0.4.mdwn
new file mode 100644
index 0000000..411af6d
--- /dev/null
+++ b/doc/news/version_4.0.4.mdwn
@@ -0,0 +1,5 @@
+propellor 4.0.4 released with [[!toggle text="these changes"]]
+[[!toggleable text="""
+   * Propellor.Property.Restic added for yet another backup program.
+     Thanks, Félix Sipma.
+   * Removed dependency on MissingH, instead depends on split and hashable."""]]
\ No newline at end of file

Added a comment
diff --git a/doc/forum/Using_propellor_for_continers_only/comment_5_8552ce821f5a3b386cb9e6ad417670ec._comment b/doc/forum/Using_propellor_for_continers_only/comment_5_8552ce821f5a3b386cb9e6ad417670ec._comment
new file mode 100644
index 0000000..0d9904c
--- /dev/null
+++ b/doc/forum/Using_propellor_for_continers_only/comment_5_8552ce821f5a3b386cb9e6ad417670ec._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="bardur.arantsson"
+ avatar="http://cdn.libravatar.org/avatar/a0be0039b44d33262b7ae650a0803ad5"
+ subject="comment 5"
+ date="2017-05-25T06:13:14Z"
+ content="""
+I'm not sure I understand all of that, but it sounds like I'll be fighting an uphill battle :). Maybe I'll try something shake-based instead. Thanks for the help.
+"""]]

update
diff --git a/doc/todo/spin_failure_HEAD.mdwn b/doc/todo/spin_failure_HEAD.mdwn
index c131591..f693b4a 100644
--- a/doc/todo/spin_failure_HEAD.mdwn
+++ b/doc/todo/spin_failure_HEAD.mdwn
@@ -8,3 +8,10 @@ Seen recently on 2 hosts:
 
 Despite the error, HEAD seems to be updated to the commit that is being spun,
 but the rest of the propellor runs doesn't happen. --[[Joey]]
+
+> This was happening spinning kite at my Mom's, but not from home.
+
+> Earlier, it was happening spinning clam from home, when clam had debian
+> oldstable on it (new install).
+> 
+> So, transient and/or network-related.. --[[Joey]]

bug report
diff --git a/doc/todo/spin_failure_HEAD.mdwn b/doc/todo/spin_failure_HEAD.mdwn
new file mode 100644
index 0000000..c131591
--- /dev/null
+++ b/doc/todo/spin_failure_HEAD.mdwn
@@ -0,0 +1,10 @@
+Seen recently on 2 hosts:
+
+	Sending privdata (73139 bytes) to kite.kitenet.net ... done
+	fatal: Couldn't find remote ref HEAD
+	propellor: <stdout>: hPutStr: illegal operation (handle is closed)
+	fatal: The remote end hung up unexpectedly
+	Sending git update to kite.kitenet.net ... failed
+
+Despite the error, HEAD seems to be updated to the commit that is being spun,
+but the rest of the propellor runs doesn't happen. --[[Joey]]

response
diff --git a/doc/forum/Using_propellor_for_continers_only/comment_4_9dc985b26c29b9ce21e6c75ec03f6262._comment b/doc/forum/Using_propellor_for_continers_only/comment_4_9dc985b26c29b9ce21e6c75ec03f6262._comment
new file mode 100644
index 0000000..72d7ca8
--- /dev/null
+++ b/doc/forum/Using_propellor_for_continers_only/comment_4_9dc985b26c29b9ce21e6c75ec03f6262._comment
@@ -0,0 +1,21 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 4"""
+ date="2017-05-13T17:42:41Z"
+ content="""
+The way propellor handles running in a chroot or container is it exports
+its binary and support files into the container. This way the
+haskell code can run in a container, rather than being limited to
+only running shell commands in the container, and without needing ghc in
+the container.
+
+It does use the hardcoded `localdir` for that.
+It would certianly be possible to make it use propellor in a different
+location, perhaps using `getExecutablePath`.
+
+Since the git-annex outside the container passes command-line options to
+the one running inside the container to tell it what to do, using
+`mainProperties` would also not work since that does not look at
+command-line options. It would need to use `defaultMain` or
+`processCmdLine` and dispatch itself, or something..
+"""]]

Added a comment
diff --git a/doc/forum/Using_propellor_for_continers_only/comment_3_cd4b9b9e160469e9f0b105f6c40a4ef8._comment b/doc/forum/Using_propellor_for_continers_only/comment_3_cd4b9b9e160469e9f0b105f6c40a4ef8._comment
new file mode 100644
index 0000000..fceeedc
--- /dev/null
+++ b/doc/forum/Using_propellor_for_continers_only/comment_3_cd4b9b9e160469e9f0b105f6c40a4ef8._comment
@@ -0,0 +1,54 @@
+[[!comment format=mdwn
+ username="bardur.arantsson"
+ avatar="http://cdn.libravatar.org/avatar/a0be0039b44d33262b7ae650a0803ad5"
+ subject="comment 3"
+ date="2017-05-12T06:50:49Z"
+ content="""
+Ok, so I've tried to use this to build a Chroot (a reasonable starting point for building containers), using the following program:
+
+    module Main
+      ( main
+      ) where
+
+    import Propellor
+    import Propellor.Engine
+    import Propellor.Property.DiskImage
+    import qualified Propellor.Property.Apt as Apt
+    import qualified Propellor.Property.User as User
+    import Propellor.Property.Chroot
+
+    main :: IO ()
+    main = mainProperties $ host \"whatever\" $ props
+            & provisioned (mychroot \"out\")
+      where
+        mychroot d = debootstrapped mempty d $ props
+            & osDebian Unstable X86_64
+            & Apt.installed [\"linux-image-amd64\"]
+            & User.hasPassword (User \"root\")
+            & User.accountFor (User \"demo\")
+            & User.hasPassword (User \"demo\")
+
+It seems that \"debootstrap\" finishes:
+
+    I: Configuring apt-transport-https...
+    I: Configuring tasksel...
+    I: Configuring tasksel-data...
+    I: Configuring libc-bin...
+    I: Configuring systemd...
+    I: Configuring ca-certificates...
+    I: Base system installed successfully.
+
+But fails immediately afterwards:
+
+    ldd: /usr/local/propellor/propellor: No such file or directory
+    ** warning: user error (ldd [\"/usr/local/propellor/propellor\"] exited 1)
+    whatever chroot out exists ... failed
+    whatever overall ... failed
+
+(I should probably have used a different hostname than \"whatever\", but... whatever :).)
+
+So it seems that the chroot support still expects propellor to be installed on the host system?
+
+I should mention that I've done an extremely small patch to Propellor locally, just to the ChrootBootstrapper instance for ArchLinux to allow it to call debootstrap on Arch Linux -- it seems to exist as a package these days, not sure if it did when that Propellor code was written. Anyway...
+
+"""]]

add news item for propellor 4.0.3
diff --git a/doc/news/version_4.0.2.mdwn b/doc/news/version_4.0.2.mdwn
deleted file mode 100644
index b955c57..0000000
--- a/doc/news/version_4.0.2.mdwn
+++ /dev/null
@@ -1,12 +0,0 @@
-propellor 4.0.2 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
-   * Apt.mirror can be used to set the preferred apt mirror of a host,
-     overriding the default CDN. This info is used by
-     Apt.stdSourcesList and Sbuild.builtFor.
-     Thanks, Sean Whitton.
-   * Property.Partition: Update kpartx output parser, as its output format
-     changed around version 0.6. Both output formats are supported now.
-   * Fix bug when using setContainerProps with a chroot that prevented
-     properties added to a chroot that way from being seen when propellor
-     was running inside the chroot. This affected disk image creation, and
-     possibly other things that use chroots."""]]
\ No newline at end of file
diff --git a/doc/news/version_4.0.3.mdwn b/doc/news/version_4.0.3.mdwn
new file mode 100644
index 0000000..eb46728
--- /dev/null
+++ b/doc/news/version_4.0.3.mdwn
@@ -0,0 +1,6 @@
+propellor 4.0.3 released with [[!toggle text="these changes"]]
+[[!toggleable text="""
+   * Added Fstab.listed, Fstab.swap, and Mount.swapOn properties.
+     Thanks, Daniel Brooks.
+   * Added Propellor.Property.Bootstrap, which can be used to make
+     disk images contain their own installation of propellor."""]]
\ No newline at end of file

Added a comment
diff --git a/doc/forum/Work_on_OS_X/comment_4_74b579d4d590432b6bd236ccb929cc11._comment b/doc/forum/Work_on_OS_X/comment_4_74b579d4d590432b6bd236ccb929cc11._comment
new file mode 100644
index 0000000..d386c1b
--- /dev/null
+++ b/doc/forum/Work_on_OS_X/comment_4_74b579d4d590432b6bd236ccb929cc11._comment
@@ -0,0 +1,16 @@
+[[!comment format=mdwn
+ username="joelmccracken"
+ avatar="http://cdn.libravatar.org/avatar/45175015b9eb3dd3f6c740b3fe920fed"
+ subject="comment 4"
+ date="2017-04-20T02:23:06Z"
+ content="""
+So, it turns out that yes, root is a thing on os x... but it is complicated. I'm going to put what I learned here because I think it will be useful, at least for telling folks how to use propellor on os x.
+
+1. Enable the root account. Steps are here: https://support.apple.com/en-us/HT204012
+2. password-authentication as root is disabled -- if you try to `ssh root@localhost`, it wont work. you need a key pair. 
+3. use su/sudo to install a public key (probably at `.ssh/id_rsa.pub`) to roots authorized_keys. adapted from: https://discussions.apple.com/thread/4078360?start=0&tstart=0
+4. copy the the pub file to authorized keys: `sudo cp /Users/joel/.ssh/id_rsa.pub /var/root/.ssh/authorized_keys`
+5. you should now be able to `ssh root@localhost` without a password.
+
+I'm not super sure that this is even the best way forward, but lets get this working first, then we'll see.
+"""]]

response
diff --git a/doc/forum/Work_on_OS_X/comment_3_294f4783522a8e4887793aac921ee546._comment b/doc/forum/Work_on_OS_X/comment_3_294f4783522a8e4887793aac921ee546._comment
new file mode 100644
index 0000000..ed654d3
--- /dev/null
+++ b/doc/forum/Work_on_OS_X/comment_3_294f4783522a8e4887793aac921ee546._comment
@@ -0,0 +1,14 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 3"""
+ date="2017-04-18T00:08:13Z"
+ content="""
+Yes, this is the place. Or you can email me directly, but I prefer to keep
+discussions public.
+
+`propellor --spin` needs a way to run commands as root on the remote host.
+If ssh as root on OSX is not allowed, it would need a way to get to a user
+who can get root, and it would be very annoying if a password needed to be
+entered since each `propellor --spin` actually makes several ssh connections to
+the remote host. Anything that works within these constraints would be ok.
+"""]]

Added a comment
diff --git a/doc/forum/Work_on_OS_X/comment_2_00b20c240fc13bed6dc54e5b985b41e2._comment b/doc/forum/Work_on_OS_X/comment_2_00b20c240fc13bed6dc54e5b985b41e2._comment
new file mode 100644
index 0000000..aa33c85
--- /dev/null
+++ b/doc/forum/Work_on_OS_X/comment_2_00b20c240fc13bed6dc54e5b985b41e2._comment
@@ -0,0 +1,17 @@
+[[!comment format=mdwn
+ username="joelmccracken"
+ avatar="http://cdn.libravatar.org/avatar/45175015b9eb3dd3f6c740b3fe920fed"
+ subject="comment 2"
+ date="2017-04-17T17:47:30Z"
+ content="""
+Sounds good. I contacted the person you linked to, have not heard back yet. 
+
+
+
+The first issue I ran into is that propellor wants to connect to \"root@<hostname>\", and it doesn't look like this is configurable.
+Would you accept a patch to make this configurable?
+
+Additionally, is this the best place to ask questions about what you would/would not accept?
+
+Thank you!!!
+"""]]

response
diff --git a/doc/forum/Work_on_OS_X/comment_1_6d7d5b89f1de9604718f7973e4b3eeb1._comment b/doc/forum/Work_on_OS_X/comment_1_6d7d5b89f1de9604718f7973e4b3eeb1._comment
new file mode 100644
index 0000000..4eac206
--- /dev/null
+++ b/doc/forum/Work_on_OS_X/comment_1_6d7d5b89f1de9604718f7973e4b3eeb1._comment
@@ -0,0 +1,20 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2017-04-13T21:36:20Z"
+ content="""
+I got a patch some years back to make propellor compile on OSX.
+I merged it. You might want to get in touch with its author, as
+he may be doing something with propellor on OSX.
+<https://github.com/tittoassini/propellor>
+
+Anyway, I'd probably merge OSX patches, if they were not super
+intrusive. And I don't see why it would be, as propellor already supports
+FreeBSD.
+
+Since `Property` is parameterized by the operating systems it
+supports, it should be easy to start by only porting the core parts
+of propellor, and then port over individual Properties one by one as
+needed. See the commits for the recent FreeBSD port for a nice walkthough
+of the changes you'll want to make.
+"""]]

diff --git a/doc/forum/Work_on_OS_X.mdwn b/doc/forum/Work_on_OS_X.mdwn
new file mode 100644
index 0000000..e3c5fd6
--- /dev/null
+++ b/doc/forum/Work_on_OS_X.mdwn
@@ -0,0 +1,5 @@
+I'm interested in using Propellor on OS X. I understand that it is not supported though.
+
+Is there anyone doing this? If it was developed, would support for OS X be merged upstream?
+
+Thanks!

done
diff --git a/debian/changelog b/debian/changelog
index dcbe0a3..70aa139 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -2,6 +2,8 @@ propellor (4.0.3) UNRELEASED; urgency=medium
 
   * Added Fstab.listed, Fstab.swap, and Mount.swapOn properties.
     Thanks, Daniel Brooks.
+  * Added Propellor.Property.Bootstrap, which can be used to make
+    disk images contain their own installation of propellor.
 
  -- Joey Hess <id@joeyh.name>  Thu, 06 Apr 2017 19:40:12 -0400
 
diff --git a/doc/todo/property_to_install_propellor.mdwn b/doc/todo/property_to_install_propellor.mdwn
index 25db819..184977f 100644
--- a/doc/todo/property_to_install_propellor.mdwn
+++ b/doc/todo/property_to_install_propellor.mdwn
@@ -12,3 +12,5 @@ Should support:
 Much of this is already implemented, in non-Property form, in
 Propellor.Bootstrap, but will need adjustments for this new context.
 --[[Joey]]
+
+> [[done]]

well, that didnt work :(
diff --git a/doc/todo/property_to_install_propellor/comment_2_9fea601af57777e1cb49952483f4da63._comment b/doc/todo/property_to_install_propellor/comment_2_9fea601af57777e1cb49952483f4da63._comment
new file mode 100644
index 0000000..f862f79
--- /dev/null
+++ b/doc/todo/property_to_install_propellor/comment_2_9fea601af57777e1cb49952483f4da63._comment
@@ -0,0 +1,7 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 2"""
+ date="2017-04-09T20:49:04Z"
+ content="""
+Well, seems that `unshare` does not work in a chroot. Hmm.
+"""]]
diff --git a/src/Propellor/Property/Bootstrap.hs b/src/Propellor/Property/Bootstrap.hs
index 6158d96..4a60276 100644
--- a/src/Propellor/Property/Bootstrap.hs
+++ b/src/Propellor/Property/Bootstrap.hs
@@ -77,6 +77,9 @@ clonedFrom reposource = property ("Propellor repo cloned from " ++ originloc) $
 
 -- | Runs the shell command with the true localdir exposed,
 -- not the one bind-mounted into a chroot.
+--
+-- FIXME: unshare -m does not work in a chroot!
+-- "unshare: cannot change root filesystem propagation: Invalid argument"
 exposeTrueLocaldir :: String -> Propellor Bool
 exposeTrueLocaldir s = do
 	s' <- ifM inChroot

added Propellor.Property.Bootstrap (untested)
This commit was sponsored by Jake Vosloo on Patreon.
diff --git a/doc/todo/property_to_install_propellor/comment_1_b05e9a44e5c7130d9cc928223cd82d78._comment b/doc/todo/property_to_install_propellor/comment_1_b05e9a44e5c7130d9cc928223cd82d78._comment
new file mode 100644
index 0000000..5a826fe
--- /dev/null
+++ b/doc/todo/property_to_install_propellor/comment_1_b05e9a44e5c7130d9cc928223cd82d78._comment
@@ -0,0 +1,16 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2017-04-09T17:42:10Z"
+ content="""
+Making this work when propellor is setting up a chroot is difficult,
+because the localdir is bind mounted into the chroot.
+
+Hmm, `unshare` could be helpful. Run shell commands to clone the localdir
+inside `unshare -m`, prefixed with a `umount localdir`. This way, the bind
+mount is avoided, and it writes "under" it. Limits the commands that can be
+run to set up the localdir to shell commands, but bootstrap already
+operates on terms of shell commands so that seems ok.
+
+`unshare` is linux-specific; comes in util-linux on modern linuxes.
+"""]]
diff --git a/joeyconfig.hs b/joeyconfig.hs
index e73897b..036c2c9 100644
--- a/joeyconfig.hs
+++ b/joeyconfig.hs
@@ -38,6 +38,7 @@ import qualified Propellor.Property.SiteSpecific.GitAnnexBuilder as GitAnnexBuil
 import qualified Propellor.Property.SiteSpecific.Branchable as Branchable
 import qualified Propellor.Property.SiteSpecific.JoeySites as JoeySites
 import Propellor.Property.DiskImage
+import Propellor.Property.Bootstrap
 
 main :: IO ()           --     _         ______`|                       ,-.__
 main = defaultMain hosts --  /   \___-=O`/|O`/__|                      (____.'
@@ -93,7 +94,7 @@ darkstar = host "darkstar.kitenet.net" $ props
 		[ (SshRsa, "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC1YoyHxZwG5Eg0yiMTJLSWJ/+dMM6zZkZiR4JJ0iUfP+tT2bm/lxYompbSqBeiCq+PYcSC67mALxp1vfmdOV//LWlbXfotpxtyxbdTcQbHhdz4num9rJQz1tjsOsxTEheX5jKirFNC5OiKhqwIuNydKWDS9qHGqsKcZQ8p+n1g9Lr3nJVGY7eRRXzw/HopTpwmGmAmb9IXY6DC2k91KReRZAlOrk0287LaK3eCe1z0bu7LYzqqS+w99iXZ/Qs0m9OqAPnHZjWQQ0fN4xn5JQpZSJ7sqO38TBAimM+IHPmy2FTNVVn9zGM+vN1O2xr3l796QmaUG1+XLL0shfR/OZbb joey@darkstar")
 		]
 
-	! imageBuilt "/tmp/img" c MSDOS (grubBooted PC)
+	& imageBuilt "/tmp/img" c MSDOS (grubBooted PC)
 		[ partition EXT2 `mountedAt` "/boot"
 			`setFlag` BootFlag
 		, partition EXT4 `mountedAt` "/"
@@ -106,6 +107,7 @@ darkstar = host "darkstar.kitenet.net" $ props
 		& Hostname.setTo "demo"
 		& Apt.installed ["linux-image-amd64"]
 		& User "root" `User.hasInsecurePassword` "root"
+		& bootstrappedFrom GitRepoOutsideChroot
 
 gnu :: Host
 gnu = host "gnu.kitenet.net" $ props
diff --git a/propellor.cabal b/propellor.cabal
index a04089b..f4a1f23 100644
--- a/propellor.cabal
+++ b/propellor.cabal
@@ -87,6 +87,7 @@ Library
     Propellor.Property.Apt
     Propellor.Property.Apt.PPA
     Propellor.Property.Attic
+    Propellor.Property.Bootstrap
     Propellor.Property.Borg
     Propellor.Property.Ccache
     Propellor.Property.Cmd
diff --git a/src/Propellor/Property/Bootstrap.hs b/src/Propellor/Property/Bootstrap.hs
new file mode 100644
index 0000000..6158d96
--- /dev/null
+++ b/src/Propellor/Property/Bootstrap.hs
@@ -0,0 +1,95 @@
+module Propellor.Property.Bootstrap (RepoSource(..), bootstrappedFrom, clonedFrom) where
+
+import Propellor.Base
+import Propellor.Bootstrap
+import Propellor.Property.Chroot
+
+import Data.List
+
+-- | Where a propellor repository should be bootstrapped from.
+data RepoSource
+	= GitRepoUrl String
+	| GitRepoOutsideChroot
+
+-- | Bootstraps a propellor installation into
+-- /usr/local/propellor/
+--
+-- Normally, propellor is already bootstrapped when it runs, so this
+-- property is not useful. However, this can be useful inside a
+-- chroot used to build a disk image, to make the disk image
+-- have propellor installed.
+--
+-- The git repository is cloned (or pulled to update if it already exists).
+--
+-- All build dependencies are installed, using distribution packages
+-- or falling back to using cabal.
+bootstrappedFrom :: RepoSource -> Property Linux
+bootstrappedFrom reposource = go `requires` clonedFrom reposource
+  where
+	go :: Property Linux
+	go = property "Propellor bootstrapped" $ do
+		system <- getOS
+		assumeChange $ exposeTrueLocaldir $ buildShellCommand
+			[ "cd " ++ localdir
+			, bootstrapPropellorCommand system
+			]
+
+-- | Clones the propellor repeository into /usr/local/propellor/
+--
+-- GitRepoOutsideChroot can be used when this is used in a chroot.
+-- In that case, it clones the /usr/local/propellor/ from outside the
+-- chroot into the same path inside the chroot.
+--
+-- If the propellor repo has already been cloned, pulls to get it
+-- up-to-date.
+clonedFrom :: RepoSource -> Property Linux
+clonedFrom reposource = property ("Propellor repo cloned from " ++ originloc) $ do
+	ifM needclone
+		( do
+			let tmpclone = localdir ++ ".tmpclone"
+			system <- getOS
+			assumeChange $ exposeTrueLocaldir $ buildShellCommand
+				[ installGitCommand system
+				, "rm -rf " ++ tmpclone
+				, "git clone " ++ shellEscape originloc ++ " " ++ tmpclone
+				, "mkdir -p " ++ localdir
+				-- This is done rather than deleting
+				-- the old localdir, because if it is bound
+				-- mounted from outside the chroot, deleting
+				-- it after unmounting in unshare will remove
+				-- the bind mount outside the unshare.
+				, "(cd " ++ tmpclone ++ " && tar c) | (cd " ++ localdir ++ " && tar x)"
+				, "rm -rf " ++ tmpclone
+				]
+		, assumeChange $ exposeTrueLocaldir $ buildShellCommand
+			[ "cd " ++ localdir
+			, "git pull"
+			]
+		)
+  where
+	needclone = (inChroot <&&> truelocaldirisempty)
+		<||> (liftIO (not <$> doesDirectoryExist localdir))
+	truelocaldirisempty = exposeTrueLocaldir $
+		"test ! -d " ++ localdir ++ "/.git"
+	originloc = case reposource of
+		GitRepoUrl s -> s
+		GitRepoOutsideChroot -> localdir
+
+-- | Runs the shell command with the true localdir exposed,
+-- not the one bind-mounted into a chroot.
+exposeTrueLocaldir :: String -> Propellor Bool
+exposeTrueLocaldir s = do
+	s' <- ifM inChroot
+		( return $ "unshare -m sh -c " ++ shellEscape
+			("umount " ++ localdir ++ " && ( " ++ s ++ ")")
+		, return s
+		)
+	liftIO $ boolSystem "sh" [ Param "-c", Param s']
+
+assumeChange :: Propellor Bool -> Propellor Result
+assumeChange a = do
+	ok <- a
+	return (cmdResult ok <> MadeChange)
+
+buildShellCommand :: [String] -> String
+buildShellCommand = intercalate "&&" . map (\c -> "(" ++ c ++ ")")
diff --git a/src/Propellor/Property/Cmd.hs b/src/Propellor/Property/Cmd.hs
index 6b84acb..f2de1a2 100644
--- a/src/Propellor/Property/Cmd.hs
+++ b/src/Propellor/Property/Cmd.hs
@@ -33,6 +33,7 @@ module Propellor.Property.Cmd (
 	Script,
 	scriptProperty,
 	userScriptProperty,
+	cmdResult,
 	-- * Lower-level interface for running commands
 	CommandParam(..),
 	boolSystem,

tweaks to db45x's patch
Removed mountNow as a top-level property, as I don't think it makes
sense for anything except for mounted to use it.
db45x's patch turns out to have introduced a bug in mounted's use of
"mountNow src". That made mountNow check if the device was a mount
point, which it isn't. The fix would have been to use "mountNow mnt",
but my inlining of mountnow just basically reverted the part of the
patch that introduced the bug.
swapOn does not involve the fstab so moved to the Mount module.
(Also noticed that Mount.mounted is a kind of weird property, given that
it fails the next time ran. It's only used internally by some chroot
properties, so I left it as-is, but added a comment. It might make sense
to make Mount.mounted check like mountNow does if the thing is already
mounted.)
diff --git a/debian/changelog b/debian/changelog
index c3ae190..dcbe0a3 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+propellor (4.0.3) UNRELEASED; urgency=medium
+
+  * Added Fstab.listed, Fstab.swap, and Mount.swapOn properties.
+    Thanks, Daniel Brooks.
+
+ -- Joey Hess <id@joeyh.name>  Thu, 06 Apr 2017 19:40:12 -0400
+
 propellor (4.0.2) unstable; urgency=medium
 
   * Apt.mirror can be used to set the preferred apt mirror of a host,
diff --git a/doc/forum/Fstab.mounted_could_call_swapon_when_activating_swap/comment_5_6dc24952c8efa31a401191a8cf2d0b39._comment b/doc/forum/Fstab.mounted_could_call_swapon_when_activating_swap/comment_5_6dc24952c8efa31a401191a8cf2d0b39._comment
new file mode 100644
index 0000000..f87500b
--- /dev/null
+++ b/doc/forum/Fstab.mounted_could_call_swapon_when_activating_swap/comment_5_6dc24952c8efa31a401191a8cf2d0b39._comment
@@ -0,0 +1,14 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 5"""
+ date="2017-04-06T23:51:08Z"
+ content="""
+Merged. Have not tested it either.
+
+On my Debian system, the swapon command does not support the
+`--no-headings` that you used. It's `--noheadings` here. Is that a typo in
+your patch?
+
+I've simply removed that option for now, since it probably won't
+hurt if it treats the heading like another device that's swapped on.
+"""]]
diff --git a/src/Propellor/Property/Fstab.hs b/src/Propellor/Property/Fstab.hs
index 8196377..29b8542 100644
--- a/src/Propellor/Property/Fstab.hs
+++ b/src/Propellor/Property/Fstab.hs
@@ -15,16 +15,6 @@ import Data.List
 import Utility.Table
 
 -- | Ensures that </etc/fstab> contains a line mounting the specified
--- `Source` on the specified `MountPoint`.
-listed :: FsType -> Source -> MountPoint -> MountOpts -> Property UnixLike
-listed fs src mnt opts = "/etc/fstab" `File.containsLine` l
-	`describe` (mnt ++ " mounted by fstab")
-  where
-	l = intercalate "\t" [src, mnt, fs, formatMountOpts opts, dump, passno]
-	dump = "0"
-	passno = "2"
-
--- | Ensures that </etc/fstab> contains a line mounting the specified
 -- `Source` on the specified `MountPoint`, and that it's currently mounted.
 --
 -- For example:
@@ -34,29 +24,31 @@ listed fs src mnt opts = "/etc/fstab" `File.containsLine` l
 -- Note that if anything else is already mounted at the `MountPoint`, it
 -- will be left as-is by this property.
 mounted :: FsType -> Source -> MountPoint -> MountOpts -> Property Linux
-mounted fs src mnt opts = (listed fs src mnt opts) `onChange` (mountNow src)
+mounted fs src mnt opts = tightenTargets $ 
+	listed fs src mnt opts
+		`onChange` mountnow
+  where
+	-- This use of mountPoints, which is linux-only, is why this
+	-- property currently only supports linux.
+	mountnow = check (notElem mnt <$> mountPoints) $
+		cmdProperty "mount" [mnt]
+
+-- | Ensures that </etc/fstab> contains a line mounting the specified
+-- `Source` on the specified `MountPoint`. Does not ensure that it's
+-- currently `mounted`.
+listed :: FsType -> Source -> MountPoint -> MountOpts -> Property UnixLike
+listed fs src mnt opts = "/etc/fstab" `File.containsLine` l
+	`describe` (mnt ++ " mounted by fstab")
+  where
+	l = intercalate "\t" [src, mnt, fs, formatMountOpts opts, dump, passno]
+	dump = "0"
+	passno = "2"
 
 -- | Ensures that </etc/fstab> contains a line enabling the specified
--- `Source` to be used as swap space, and that it's enabled
+-- `Source` to be used as swap space, and that it's enabled.
 swap :: Source -> Property Linux
-swap src = (listed "swap" src "none" mempty) `onChange` (swapOn src)
-
--- This use of mountPoints, which is linux-only, is why this
--- property currently only supports linux.
-mountNow :: Source -> RevertableProperty Linux Linux
-mountNow mnt = tightenTargets domount <!> tightenTargets doumount
-  where domount = check (notElem mnt <$> mountPoints) $
-		cmdProperty "mount" [mnt]
-        doumount = check (elem mnt <$> mountPoints) $
-		cmdProperty "umount" [mnt]
-
-swapOn :: Source -> RevertableProperty Linux Linux
-swapOn mnt = tightenTargets doswapon <!> tightenTargets doswapoff
-  where swaps = lines <$> readProcess "swapon" ["--no-headings", "--show=NAME"]
-        doswapon = check (notElem mnt <$> swaps) $
-		cmdProperty "swapon" [mnt]
-        doswapoff = check (elem mnt <$> swaps) $
-		cmdProperty "swapoff" [mnt]
+swap src = listed "swap" src "none" mempty
+	`onChange` swapOn src
 
 newtype SwapPartition = SwapPartition FilePath
 
diff --git a/src/Propellor/Property/Mount.hs b/src/Propellor/Property/Mount.hs
index 026509a..5dcc5fe 100644
--- a/src/Propellor/Property/Mount.hs
+++ b/src/Propellor/Property/Mount.hs
@@ -40,6 +40,9 @@ formatMountOpts (MountOpts []) = "defaults"
 formatMountOpts (MountOpts l) = intercalate "," l
 
 -- | Mounts a device, without listing it in </etc/fstab>.
+--
+-- Note that this property will fail if the device is already mounted
+-- at the MountPoint.
 mounted :: FsType -> Source -> MountPoint -> MountOpts -> Property UnixLike
 mounted fs src mnt opts = property (mnt ++ " mounted") $ 
 	toResult <$> liftIO (mount fs src mnt opts)
@@ -52,6 +55,17 @@ bindMount src dest = tightenTargets $
 		`assume` MadeChange
 		`describe` ("bind mounted " ++ src ++ " to " ++ dest)
 
+-- | Enables swapping to a device, which must be formatted already as a swap
+-- partition.
+swapOn :: Source -> RevertableProperty Linux Linux
+swapOn mnt = tightenTargets doswapon <!> tightenTargets doswapoff
+  where
+	swaps = lines <$> readProcess "swapon" ["--show=NAME"]
+	doswapon = check (notElem mnt <$> swaps) $
+		cmdProperty "swapon" [mnt]
+	doswapoff = check (elem mnt <$> swaps) $
+		cmdProperty "swapoff" [mnt]
+
 mount :: FsType -> Source -> MountPoint -> MountOpts -> IO Bool
 mount fs src mnt opts = boolSystem "mount" $
 	[ Param "-t", Param fs

Added a comment
diff --git a/doc/forum/Using_propellor_for_continers_only/comment_2_42b45a126cfdf1dfc370b166c8042690._comment b/doc/forum/Using_propellor_for_continers_only/comment_2_42b45a126cfdf1dfc370b166c8042690._comment
new file mode 100644
index 0000000..45cd3e0
--- /dev/null
+++ b/doc/forum/Using_propellor_for_continers_only/comment_2_42b45a126cfdf1dfc370b166c8042690._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="bardur.arantsson"
+ avatar="http://cdn.libravatar.org/avatar/a0be0039b44d33262b7ae650a0803ad5"
+ subject="comment 2"
+ date="2017-04-06T02:14:58Z"
+ content="""
+I'll try that this weekend, thanks!
+"""]]

Added a comment
diff --git a/doc/forum/Fstab.mounted_could_call_swapon_when_activating_swap/comment_4_b1769231a633ad2b978ee4c9fa90591c._comment b/doc/forum/Fstab.mounted_could_call_swapon_when_activating_swap/comment_4_b1769231a633ad2b978ee4c9fa90591c._comment
new file mode 100644
index 0000000..ca04f94
--- /dev/null
+++ b/doc/forum/Fstab.mounted_could_call_swapon_when_activating_swap/comment_4_b1769231a633ad2b978ee4c9fa90591c._comment
@@ -0,0 +1,9 @@
+[[!comment format=mdwn
+ username="db48x@80bd751a72d5a80737e2f875342cf845629c7202"
+ nickname="db48x"
+ avatar="http://cdn.libravatar.org/avatar/ad2688127feb555a92154b16d8eeb5d3"
+ subject="comment 4"
+ date="2017-04-05T06:39:49Z"
+ content="""
+I took a stab at implementing this. It compiles, but I've not tested it yet as I need to get some sleep; consider it a work in progress. Looks right to me though.
+"""]]

comment
diff --git a/doc/forum/Fstab.mounted_could_call_swapon_when_activating_swap/comment_3_f48a6191c56bed41eda55436f0aa3e9c._comment b/doc/forum/Fstab.mounted_could_call_swapon_when_activating_swap/comment_3_f48a6191c56bed41eda55436f0aa3e9c._comment
new file mode 100644
index 0000000..95c6955
--- /dev/null
+++ b/doc/forum/Fstab.mounted_could_call_swapon_when_activating_swap/comment_3_f48a6191c56bed41eda55436f0aa3e9c._comment
@@ -0,0 +1,15 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 3"""
+ date="2017-04-05T03:08:30Z"
+ content="""
+I like the idea of composing smaller properties to build the current
+property, and add flexability.
+
+Renaming the existing `fstabbed` would probably be too much bother.
+(Also, I think I picked that name because it kind of hints that the
+existing fstab does not come out alive.)
+
+(The swapspace package is great if you can eat the now tiny overhead of a
+swap file compared to a swap partition.)
+"""]]

Added a comment
diff --git a/doc/forum/Fstab.mounted_could_call_swapon_when_activating_swap/comment_2_773fc1441dd06e9dd41508bd800298eb._comment b/doc/forum/Fstab.mounted_could_call_swapon_when_activating_swap/comment_2_773fc1441dd06e9dd41508bd800298eb._comment
new file mode 100644
index 0000000..62cabc0
--- /dev/null
+++ b/doc/forum/Fstab.mounted_could_call_swapon_when_activating_swap/comment_2_773fc1441dd06e9dd41508bd800298eb._comment
@@ -0,0 +1,13 @@
+[[!comment format=mdwn
+ username="db48x@80bd751a72d5a80737e2f875342cf845629c7202"
+ nickname="db48x"
+ avatar="http://cdn.libravatar.org/avatar/ad2688127feb555a92154b16d8eeb5d3"
+ subject="comment 2"
+ date="2017-04-05T02:48:08Z"
+ content="""
+Yes, perhaps if it took an Option FilePath (am I saying this correctly in Haskellese?) it would be nicer.
+
+I don't mind much how it's structured; this was just the smallest obvious change, since it was failing to mount it. Perhaps breaking it up into smaller, more primitive, pieces would help. Fstab.mounted could = Fstab.fstabbed `onChange` Fstab.mounted, for instance, and then I could write Fstab.fstabbed `onChange` Swap.swapEnabled (oh, but Fstab.fstabbed already exists; I'm not using it because it replaces the whole file, which seems like an odd thing to do. Maybe call it Fstab.listed instead?).
+
+Also, for maximum irony I was just perusing your most recent dozen commits or so, and saw you enable Apt.serviceInstalledRunning \"swapspace\" on one of your machines. That's amazing; I had no idea it existed! I am re-evaluating all of my life choices now.
+"""]]

Added a comment
diff --git a/doc/forum/Git.cloned_deletes_harmless_empty_directory/comment_2_289f157f129511242d93beae76fd03a3._comment b/doc/forum/Git.cloned_deletes_harmless_empty_directory/comment_2_289f157f129511242d93beae76fd03a3._comment
new file mode 100644
index 0000000..1a8c144
--- /dev/null
+++ b/doc/forum/Git.cloned_deletes_harmless_empty_directory/comment_2_289f157f129511242d93beae76fd03a3._comment
@@ -0,0 +1,11 @@
+[[!comment format=mdwn
+ username="db48x@80bd751a72d5a80737e2f875342cf845629c7202"
+ nickname="db48x"
+ avatar="http://cdn.libravatar.org/avatar/ad2688127feb555a92154b16d8eeb5d3"
+ subject="comment 2"
+ date="2017-04-05T02:37:44Z"
+ content="""
+Yea, I guess that's a fair point about the other cases.
+
+It just seems inelegant to go back over all the files and fix up their permissions, when it could just have been set right to begin with.
+"""]]

comments
diff --git a/doc/forum/Fstab.mounted_could_call_swapon_when_activating_swap/comment_1_8ab6b313c80486f8f87a5e13e830bfa9._comment b/doc/forum/Fstab.mounted_could_call_swapon_when_activating_swap/comment_1_8ab6b313c80486f8f87a5e13e830bfa9._comment
new file mode 100644
index 0000000..4a144df
--- /dev/null
+++ b/doc/forum/Fstab.mounted_could_call_swapon_when_activating_swap/comment_1_8ab6b313c80486f8f87a5e13e830bfa9._comment
@@ -0,0 +1,20 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2017-04-05T02:17:00Z"
+ content="""
+This idea kind of makes sense, because swap partitions in /etc/fstab
+get swaponed at boot.
+
+But, the implementation doesn't take the types into account. The `mounted`
+property takes a FilePath for the mountpoint, but for swap that
+needs to be "none", which is not really a file-path. Also, the `fstabbed`
+property has a separate `SwapPartition` type, so making `mount` support
+swap partitions without using that type feels wrong.
+
+It might be simpler all round to treat swap partitions being able to
+be specified in /etc/fstab as a historical accident, which it kind of
+is (increasingly so, since eg systemd has other ways to accomplish
+that), and instead of shoehorning this into the `mounted` property,
+add a new `swaponed` property.
+"""]]
diff --git a/doc/forum/Git.cloned_deletes_harmless_empty_directory/comment_1_7cd0521c6d071b25852f8355f4f61f94._comment b/doc/forum/Git.cloned_deletes_harmless_empty_directory/comment_1_7cd0521c6d071b25852f8355f4f61f94._comment
new file mode 100644
index 0000000..91b403b
--- /dev/null
+++ b/doc/forum/Git.cloned_deletes_harmless_empty_directory/comment_1_7cd0521c6d071b25852f8355f4f61f94._comment
@@ -0,0 +1,20 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2017-04-05T02:22:54Z"
+ content="""
+I am not entirely happy with this patch, because it seems that if
+Git.cloned took care to preserve permissions in this case, it could be
+argued that it should also preserve permissions when the directory already
+exists but has the wrong content. Or equally well argued that it should not
+preserve permissions, which might be a leftover from some past unwanted
+state.
+
+Is that really the best way to do it? You could instead say:
+
+	Git.cloned user repo dir Nothing
+		`onChange` recursiveSetGID user dir
+
+And then you just have to write a recursiveSetGID which would be a
+generally useful property.
+"""]]

diff --git a/doc/forum/Fstab.mounted_could_call_swapon_when_activating_swap.mdwn b/doc/forum/Fstab.mounted_could_call_swapon_when_activating_swap.mdwn
new file mode 100644
index 0000000..61cd10c
--- /dev/null
+++ b/doc/forum/Fstab.mounted_could_call_swapon_when_activating_swap.mdwn
@@ -0,0 +1,3 @@
+The mount command won't work when activating a swap partition/file, so we should call swapon instead.
+
+https://github.com/ArchiveTeam/glowing-computing-machine/tree/fstab-swap

diff --git a/doc/forum/Git.cloned_deletes_harmless_empty_directory.mdwn b/doc/forum/Git.cloned_deletes_harmless_empty_directory.mdwn
new file mode 100644
index 0000000..ce3c192
--- /dev/null
+++ b/doc/forum/Git.cloned_deletes_harmless_empty_directory.mdwn
@@ -0,0 +1,3 @@
+In my case I have carefully set up the directory that I'm going to clone into with the correct group ownership and setgid permission, so that the cloned files will also have the correct ownership. This change just checks to see if the directory actually has anything in it before it deletes it.
+
+https://github.com/ArchiveTeam/glowing-computing-machine/tree/git-in-emtpy-directory

bug report
diff --git a/doc/todo/initial_spin_compile_failure_recovery.mdwn b/doc/todo/initial_spin_compile_failure_recovery.mdwn
new file mode 100644
index 0000000..423b279
--- /dev/null
+++ b/doc/todo/initial_spin_compile_failure_recovery.mdwn
@@ -0,0 +1,5 @@
+When initial propellor --spin host fails to compile propellor
+perhaps due to a ghc compatability bug, spinning again doesn't fix the
+problem. IIRC /usr/local/propellor has a git repo set up, but no remote
+set, and so the subsequent spin doesn't update it, since propellor is not
+running there to receive a git push into the repo. --[[Joey]]

response
diff --git a/doc/forum/Using_propellor_for_continers_only/comment_1_95e8b7103f248d93570fecb6b8999996._comment b/doc/forum/Using_propellor_for_continers_only/comment_1_95e8b7103f248d93570fecb6b8999996._comment
new file mode 100644
index 0000000..dc6cc61
--- /dev/null
+++ b/doc/forum/Using_propellor_for_continers_only/comment_1_95e8b7103f248d93570fecb6b8999996._comment
@@ -0,0 +1,20 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2017-03-29T19:09:37Z"
+ content="""
+Sounds like you may want to write a program that uses propellor as a
+library. `Propellor.Engine.mainProperties` is a reasonable
+entry point, just pass it a Host that has the properties you want
+to run.
+
+For example:
+
+	import Propellor
+	import Propellor.Engine
+	import Propellor.Property.DiskImage
+
+	main :: IO ()
+	main = mainProperties $ host "whatever" $ props
+		& imageBuilt "/some/disk.img" ...
+"""]]

diff --git a/doc/forum/Using_propellor_for_continers_only.mdwn b/doc/forum/Using_propellor_for_continers_only.mdwn
index c2b8673..faf0795 100644
--- a/doc/forum/Using_propellor_for_continers_only.mdwn
+++ b/doc/forum/Using_propellor_for_continers_only.mdwn
@@ -2,4 +2,4 @@ Hi,
 
 I was wondering: Is it possible to use propellor to generate images only without actually managing any hosts per-se? I couldn't find any documentation on that.
 
-Ideally, I'd also be able to use it directly from a sandbox so that I wouldn't have to even "pollute" the GHC/Cabal "global" (user home dir) database on the development machine. I see that there's support for having the config.hs stored in a different directory than ~/.propellor, but I haven't managed to get it working when I use a sandbox in e.g. ~/foo with the config.hs stored in the same directory. Perhaps that's just a bug? If it's supposed to work I can provide detailed error messages, etc.
+Ideally, I'd also be able to use it directly from a sandbox so that I wouldn't have to even "pollute" the GHC/Cabal "global" (user home dir) database on the development machine. I see that there's support for having the config.hs stored in a different directory than ~/.propellor, but I haven't managed to get it working when I use a sandbox in e.g. ~/foo with the config.hs stored in the same directory. Perhaps that's just a bug? If it's supposed to work I can provide detailed error messages, etc. **EDIT:** I'd also like to manage the git repository myself -- is that possible?

diff --git a/doc/forum/Using_propellor_for_continers_only.mdwn b/doc/forum/Using_propellor_for_continers_only.mdwn
new file mode 100644
index 0000000..c2b8673
--- /dev/null
+++ b/doc/forum/Using_propellor_for_continers_only.mdwn
@@ -0,0 +1,5 @@
+Hi,
+
+I was wondering: Is it possible to use propellor to generate images only without actually managing any hosts per-se? I couldn't find any documentation on that.
+
+Ideally, I'd also be able to use it directly from a sandbox so that I wouldn't have to even "pollute" the GHC/Cabal "global" (user home dir) database on the development machine. I see that there's support for having the config.hs stored in a different directory than ~/.propellor, but I haven't managed to get it working when I use a sandbox in e.g. ~/foo with the config.hs stored in the same directory. Perhaps that's just a bug? If it's supposed to work I can provide detailed error messages, etc.

todo
diff --git a/doc/todo/property_to_install_propellor.mdwn b/doc/todo/property_to_install_propellor.mdwn
new file mode 100644
index 0000000..25db819
--- /dev/null
+++ b/doc/todo/property_to_install_propellor.mdwn
@@ -0,0 +1,14 @@
+This seems redundant, since propellor must be running to ensure such a
+Property, but a Property to install propellor is useful when eg, creating a
+disk image that itself will need to run propellor. --[[Joey]]
+
+Should support:
+
+* Cloning the git repo propellor is running in. (Using eg `hostChroot`)
+* Cloning some other git repo.
+* Installing the precompiled propellor binary.
+* Installing the propellor haskell library using cabal/stack/apt.
+
+Much of this is already implemented, in non-Property form, in
+Propellor.Bootstrap, but will need adjustments for this new context.
+--[[Joey]]

response
diff --git a/doc/forum/Manage_multiple_different_projects_with_propellor/comment_1_dbad48163b2efd6434ea7c37a72dfd30._comment b/doc/forum/Manage_multiple_different_projects_with_propellor/comment_1_dbad48163b2efd6434ea7c37a72dfd30._comment
new file mode 100644
index 0000000..7513cc0
--- /dev/null
+++ b/doc/forum/Manage_multiple_different_projects_with_propellor/comment_1_dbad48163b2efd6434ea7c37a72dfd30._comment
@@ -0,0 +1,14 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2017-03-24T18:14:14Z"
+ content="""
+There did not used to be a good way to do that, but since propellor 3.2.3,
+when you run eg "propellor --spin host", it first checks to see if there is
+a `./config.hs` file, and if so, uses it instead of the user-global
+`~/.propellor/config.hs`.
+
+So, just make different git repos for the different projects with propellor
+`config.hs` files in them, and `cd` into the one you want to run before running
+propellor.
+"""]]

add news item for propellor 4.0.2
diff --git a/doc/news/version_4.0.1.mdwn b/doc/news/version_4.0.1.mdwn
deleted file mode 100644
index e870a51..0000000
--- a/doc/news/version_4.0.1.mdwn
+++ /dev/null
@@ -1,6 +0,0 @@
-propellor 4.0.1 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
-   * Fix build with pre-AMP ghc.
-   * Tor: Restart daemon after installing private key.
-   * Tor.named, Tor.torPrivKey: Include the new ed25519 public/private key
-     pair in addition to the old secret\_id\_key."""]]
\ No newline at end of file
diff --git a/doc/news/version_4.0.2.mdwn b/doc/news/version_4.0.2.mdwn
new file mode 100644
index 0000000..b955c57
--- /dev/null
+++ b/doc/news/version_4.0.2.mdwn
@@ -0,0 +1,12 @@
+propellor 4.0.2 released with [[!toggle text="these changes"]]
+[[!toggleable text="""
+   * Apt.mirror can be used to set the preferred apt mirror of a host,
+     overriding the default CDN. This info is used by
+     Apt.stdSourcesList and Sbuild.builtFor.
+     Thanks, Sean Whitton.
+   * Property.Partition: Update kpartx output parser, as its output format
+     changed around version 0.6. Both output formats are supported now.
+   * Fix bug when using setContainerProps with a chroot that prevented
+     properties added to a chroot that way from being seen when propellor
+     was running inside the chroot. This affected disk image creation, and
+     possibly other things that use chroots."""]]
\ No newline at end of file

fix diskimage finalization bug
* Fix bug when using setContainerProps with a chroot that prevented
properties added to a chroot that way from being seen when propellor
was running inside the chroot. This affected disk image creation, and
possibly other things that use chroots.
The problem was, propagateChrootInfo was being passed the initial
version of the Chroot, but then the Chroot got more properties
added, and so those were not recorded in the _chroot info.
Fix was simply to make InfoPropagator be passed the Chroot as an
additional parameter, so Chroot.provisioned' can pass in the final
Chroot to it.
diff --git a/debian/changelog b/debian/changelog
index 9e6ffb6..11c9922 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -6,6 +6,10 @@ propellor (4.0.2) UNRELEASED; urgency=medium
     Thanks, Sean Whitton.
   * Property.Partition: Update kpartx output parser, as its output format
     changed around version 0.6. Both output formats are supported now.
+  * Fix bug when using setContainerProps with a chroot that prevented
+    properties added to a chroot that way from being seen when propellor
+    was running inside the chroot. This affected disk image creation, and
+    possibly other things that use chroots.
    
  -- Joey Hess <id@joeyh.name>  Sun, 19 Mar 2017 16:37:27 -0400
 
diff --git a/doc/todo/bug_in_diskimage_finalization.mdwn b/doc/todo/bug_in_diskimage_finalization.mdwn
index 288e96f..3dc9c43 100644
--- a/doc/todo/bug_in_diskimage_finalization.mdwn
+++ b/doc/todo/bug_in_diskimage_finalization.mdwn
@@ -9,3 +9,5 @@ ensure.
 
 I have not yet been able to determine what broke it -- I'm sure it used to
 work. --[[Joey]]
+
+> Figured it out, fixed [[done]] --[[Joey]]
diff --git a/src/Propellor/Property/Chroot.hs b/src/Propellor/Property/Chroot.hs
index 920a46d..7738d97 100644
--- a/src/Propellor/Property/Chroot.hs
+++ b/src/Propellor/Property/Chroot.hs
@@ -46,7 +46,9 @@ data Chroot where
 instance IsContainer Chroot where
 	containerProperties (Chroot _ _ _ h) = containerProperties h
 	containerInfo (Chroot _ _ _ h) = containerInfo h
-	setContainerProperties (Chroot loc b p h) ps = Chroot loc b p (setContainerProperties h ps)
+	setContainerProperties (Chroot loc b p h) ps =
+		let h' = setContainerProperties h ps
+		in Chroot loc b p h'
 
 chrootSystem :: Chroot -> Maybe System
 chrootSystem = fromInfoVal . fromInfo . containerInfo
@@ -118,7 +120,7 @@ debootstrapped conf = bootstrapped (Debootstrapped conf)
 bootstrapped :: ChrootBootstrapper b => b -> FilePath -> Props metatypes -> Chroot
 bootstrapped bootstrapper location ps = c
   where
-	c = Chroot location bootstrapper (propagateChrootInfo c) (host location ps)
+	c = Chroot location bootstrapper propagateChrootInfo (host location ps)
 
 -- | Ensures that the chroot exists and is provisioned according to its
 -- properties.
@@ -134,7 +136,7 @@ provisioned'
 	-> Bool
 	-> RevertableProperty (HasInfo + Linux) Linux
 provisioned' c@(Chroot loc bootstrapper infopropigator _) systemdonly =
-	(infopropigator normalContainerInfo $ setup `describe` chrootDesc c "exists")
+	(infopropigator c normalContainerInfo $ setup `describe` chrootDesc c "exists")
 		<!>
 	(teardown `describe` chrootDesc c "removed")
   where
@@ -153,9 +155,9 @@ provisioned' c@(Chroot loc bootstrapper infopropigator _) systemdonly =
 		property ("removed " ++ loc) $
 			makeChange (removeChroot loc)
 
-type InfoPropagator = (PropagateInfo -> Bool) -> Property Linux -> Property (HasInfo + Linux)
+type InfoPropagator = Chroot -> (PropagateInfo -> Bool) -> Property Linux -> Property (HasInfo + Linux)
 
-propagateChrootInfo :: Chroot -> InfoPropagator
+propagateChrootInfo :: InfoPropagator
 propagateChrootInfo c@(Chroot location _ _ _) pinfo p =
 	propagateContainer location c pinfo $
 		p `setInfoProperty` chrootInfo c
@@ -302,12 +304,12 @@ hostChroot :: ChrootBootstrapper bootstrapper => Host -> bootstrapper -> FilePat
 hostChroot h bootstrapper d = chroot
   where
 	chroot = Chroot d bootstrapper pinfo h
-	pinfo = propagateHostChrootInfo h chroot
+	pinfo = propagateHostChrootInfo h
 
 -- This is different than propagateChrootInfo in that Info using
 -- HostContext is not made to use the name of the chroot as its context,
 -- but instead uses the hostname of the Host.
-propagateHostChrootInfo :: Host -> Chroot -> InfoPropagator
+propagateHostChrootInfo :: Host -> InfoPropagator
 propagateHostChrootInfo h c pinfo p =
 	propagateContainer (hostName h) c pinfo $
 		p `setInfoProperty` chrootInfo c
diff --git a/src/Propellor/Property/DiskImage.hs b/src/Propellor/Property/DiskImage.hs
index 06d0694..90b7010 100644
--- a/src/Propellor/Property/DiskImage.hs
+++ b/src/Propellor/Property/DiskImage.hs
@@ -140,7 +140,7 @@ imageBuilt' rebuild img mkchroot tabletype final partspec =
 			& cachesCleaned
 	-- Only propagate privdata Info from this chroot, nothing else.
 	propprivdataonly (Chroot.Chroot d b ip h) =
-		Chroot.Chroot d b (const $ ip onlyPrivData) h
+		Chroot.Chroot d b (\c _ -> ip c onlyPrivData) h
 
 -- | This property is automatically added to the chroot when building a
 -- disk image. It cleans any caches of information that can be omitted;
diff --git a/src/Propellor/Property/Systemd.hs b/src/Propellor/Property/Systemd.hs
index 8f9c3be..7c40bd1 100644
--- a/src/Propellor/Property/Systemd.hs
+++ b/src/Propellor/Property/Systemd.hs
@@ -281,7 +281,7 @@ nspawned c@(Container name (Chroot.Chroot loc builder _ _) h) =
 			<!>
 		doNothing
 
-	chroot = Chroot.Chroot loc builder (Chroot.propagateChrootInfo chroot) h
+	chroot = Chroot.Chroot loc builder Chroot.propagateChrootInfo h
 
 -- | Sets up the service file for the container, and then starts
 -- it running.

diff --git a/doc/forum/Manage_multiple_different_projects_with_propellor.mdwn b/doc/forum/Manage_multiple_different_projects_with_propellor.mdwn
new file mode 100644
index 0000000..bcba383
--- /dev/null
+++ b/doc/forum/Manage_multiple_different_projects_with_propellor.mdwn
@@ -0,0 +1,7 @@
+Hi there,
+
+I've been tasked with investigating propellor as an alternative to Ansible. I'm a little bit confused about how one might go about managing a *single* project's hosts with propellor, without infecting the global propellor config. It seems that everything is concerned with the ~/.propellor repository. However, I don't want project A's hosts to know about project B's and vice versa. I'm sure I'm overlooking something obvious!
+
+Thanks very much!
+
+Mitchell

bug report
diff --git a/doc/todo/bug_in_diskimage_finalization.mdwn b/doc/todo/bug_in_diskimage_finalization.mdwn
new file mode 100644
index 0000000..288e96f
--- /dev/null
+++ b/doc/todo/bug_in_diskimage_finalization.mdwn
@@ -0,0 +1,11 @@
+DiskImage.imageBuilt has broken and no longer runs the finalization
+properties that get added to the chroot. This includes installing grub, and
+Chroot.noServices etc.
+
+Seems that the `_chroot` info that gets propigated from imageBuilt is
+for the chroot before those properties are added to it. Then when chaining
+into the chroot, `_chroot` info is examined to find the properties to
+ensure.
+
+I have not yet been able to determine what broke it -- I'm sure it used to
+work. --[[Joey]]

Apt.mirror can be used to set the preferred apt mirror of a host, overriding the default CDN.
This info is used by Apt.stdSourcesList and Sbuild.builtFor.
Thanks, Sean Whitton.
diff --git a/debian/changelog b/debian/changelog
index dd950ec..ed0e242 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,12 @@
+propellor (4.0.2) UNRELEASED; urgency=medium
+
+  * Apt.mirror can be used to set the preferred apt mirror of a host,
+    overriding the default CDN. This info is used by 
+    Apt.stdSourcesList and Sbuild.builtFor.
+    Thanks, Sean Whitton.
+
+ -- Joey Hess <id@joeyh.name>  Sun, 19 Mar 2017 16:37:27 -0400
+
 propellor (4.0.1) unstable; urgency=medium
 
   * Fix build with pre-AMP ghc.
diff --git a/doc/todo/Info_property_to_select_host__39__s_preferred_Apt_mirror.mdwn b/doc/todo/Info_property_to_select_host__39__s_preferred_Apt_mirror.mdwn
index e3702ab..4cd7638 100644
--- a/doc/todo/Info_property_to_select_host__39__s_preferred_Apt_mirror.mdwn
+++ b/doc/todo/Info_property_to_select_host__39__s_preferred_Apt_mirror.mdwn
@@ -1,3 +1,5 @@
 It would be good to have an info property, say `Apt.mirror`, which sets a host's preferred apt mirror.  Then all properties in `Propellor.Property.Apt` would use this mirror when generating sources lists, falling back to the `deb.debian.org` default.  The value of `Apt.mirror` could be an apt cache on the LAN, or a mirror that is known to be better than the Debian CDN from where the host is located. --[[spwhitton|user/spwhitton]]
 
 [[!tag user/spwhitton]]
+
+> [[merged|done]] thank you! --[[Joey]]
diff --git a/src/Propellor/Property/Apt.hs b/src/Propellor/Property/Apt.hs
index 8f4678d..686ddb6 100644
--- a/src/Propellor/Property/Apt.hs
+++ b/src/Propellor/Property/Apt.hs
@@ -105,8 +105,9 @@ securityUpdates suite
 		in [l, srcLine l]
 	| otherwise = []
 
--- | Makes sources.list have a standard content using the Debian mirror CDN,
--- with the Debian suite configured by the os.
+-- | Makes sources.list have a standard content using the Debian mirror CDN
+-- (or other host specified using the `mirror` property), with the
+-- Debian suite configured by the os.
 stdSourcesList :: Property Debian
 stdSourcesList = withOS "standard sources.list" $ \w o -> case o of
 	(Just (System (Debian _ suite) _)) ->

add news item for propellor 4.0.1
diff --git a/doc/news/version_4.0.0.mdwn b/doc/news/version_4.0.0.mdwn
deleted file mode 100644
index bb62b4a..0000000
--- a/doc/news/version_4.0.0.mdwn
+++ /dev/null
@@ -1,30 +0,0 @@
-propellor 4.0.0 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
-   * Added Monoid instances for Property and RevertableProperty.
-   * Removed applyToList. Instead, use mconcat. (API change)
-     If you had:  applyToList accountFor [User "joey", User "root"]
-     use instead: mconcat (map accountFor [User "joey", User "root"])
-   * Makefile: Removed "run" target which was default target.
-     "make" now only builds propellor, does not run it.
-     Note that propellor 1.0.0 and earlier relied on this target for
-     the Cron.runPropellor property's cronjob to work, so upgrading
-     directly from 1.0.0 to 4.0.0 would break that cron job.
-   * Remove make from propellor's dependency list; it's not used by
-     propellor any longer.
-   * Implemented hostChroot, as originally seen in my slides at
-     Linux.Conf.Au 2017 in January. Now that it's not vaporware, it allows
-     one Host to build a disk image that has all the properties of another
-     Host.
-   * DiskImage building properties used to propagate DNS info out from
-     the chroot used to build the disk image to the Host. That is no longer
-     done, since that chroot only exists as a side effect of the disk image
-     creation and servers will not be running in it.
-   * The IsInfo types class's propagateInfo function changed to use a
-     PropagateInfo data type. (API change)
-   * The action used to satisfy a property changed to Maybe (Propellor Result).
-     When it is Nothing, propellor knows it can skip displaying the
-     description of that property. This is mostly useful in the
-     implementation of mempty. (API change)
-   * The doNothing property is now simply mempty. The name was retained
-     because it can be clearer than mempty in some contexts.
-   * Added Apache.confEnabled."""]]
\ No newline at end of file
diff --git a/doc/news/version_4.0.1.mdwn b/doc/news/version_4.0.1.mdwn
new file mode 100644
index 0000000..e870a51
--- /dev/null
+++ b/doc/news/version_4.0.1.mdwn
@@ -0,0 +1,6 @@
+propellor 4.0.1 released with [[!toggle text="these changes"]]
+[[!toggleable text="""
+   * Fix build with pre-AMP ghc.
+   * Tor: Restart daemon after installing private key.
+   * Tor.named, Tor.torPrivKey: Include the new ed25519 public/private key
+     pair in addition to the old secret\_id\_key."""]]
\ No newline at end of file

Added a comment: merge request
diff --git a/doc/todo/Info_property_to_select_host__39__s_preferred_Apt_mirror/comment_2_2c2c4817a4259acbc1a63bac2e3fb2e3._comment b/doc/todo/Info_property_to_select_host__39__s_preferred_Apt_mirror/comment_2_2c2c4817a4259acbc1a63bac2e3fb2e3._comment
new file mode 100644
index 0000000..b79ba1c
--- /dev/null
+++ b/doc/todo/Info_property_to_select_host__39__s_preferred_Apt_mirror/comment_2_2c2c4817a4259acbc1a63bac2e3fb2e3._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="spwhitton"
+ avatar="http://cdn.libravatar.org/avatar/9c3f08f80e67733fd506c353239569eb"
+ subject="merge request"
+ date="2017-03-19T18:42:20Z"
+ content="""
+Please see branch `apt-mirror` of repo `https://git.spwhitton.name/propellor` for an implementation of this.
+"""]]

take ownership of todo item
diff --git a/doc/todo/Info_property_to_select_host__39__s_preferred_Apt_mirror.mdwn b/doc/todo/Info_property_to_select_host__39__s_preferred_Apt_mirror.mdwn
index 355d53b..e3702ab 100644
--- a/doc/todo/Info_property_to_select_host__39__s_preferred_Apt_mirror.mdwn
+++ b/doc/todo/Info_property_to_select_host__39__s_preferred_Apt_mirror.mdwn
@@ -1 +1,3 @@
 It would be good to have an info property, say `Apt.mirror`, which sets a host's preferred apt mirror.  Then all properties in `Propellor.Property.Apt` would use this mirror when generating sources lists, falling back to the `deb.debian.org` default.  The value of `Apt.mirror` could be an apt cache on the LAN, or a mirror that is known to be better than the Debian CDN from where the host is located. --[[spwhitton|user/spwhitton]]
+
+[[!tag user/spwhitton]]

add news item for propellor 4.0.0
diff --git a/doc/news/version_4.0.0.mdwn b/doc/news/version_4.0.0.mdwn
new file mode 100644
index 0000000..bb62b4a
--- /dev/null
+++ b/doc/news/version_4.0.0.mdwn
@@ -0,0 +1,30 @@
+propellor 4.0.0 released with [[!toggle text="these changes"]]
+[[!toggleable text="""
+   * Added Monoid instances for Property and RevertableProperty.
+   * Removed applyToList. Instead, use mconcat. (API change)
+     If you had:  applyToList accountFor [User "joey", User "root"]
+     use instead: mconcat (map accountFor [User "joey", User "root"])
+   * Makefile: Removed "run" target which was default target.
+     "make" now only builds propellor, does not run it.
+     Note that propellor 1.0.0 and earlier relied on this target for
+     the Cron.runPropellor property's cronjob to work, so upgrading
+     directly from 1.0.0 to 4.0.0 would break that cron job.
+   * Remove make from propellor's dependency list; it's not used by
+     propellor any longer.
+   * Implemented hostChroot, as originally seen in my slides at
+     Linux.Conf.Au 2017 in January. Now that it's not vaporware, it allows
+     one Host to build a disk image that has all the properties of another
+     Host.
+   * DiskImage building properties used to propagate DNS info out from
+     the chroot used to build the disk image to the Host. That is no longer
+     done, since that chroot only exists as a side effect of the disk image
+     creation and servers will not be running in it.
+   * The IsInfo types class's propagateInfo function changed to use a
+     PropagateInfo data type. (API change)
+   * The action used to satisfy a property changed to Maybe (Propellor Result).
+     When it is Nothing, propellor knows it can skip displaying the
+     description of that property. This is mostly useful in the
+     implementation of mempty. (API change)
+   * The doNothing property is now simply mempty. The name was retained
+     because it can be clearer than mempty in some contexts.
+   * Added Apache.confEnabled."""]]
\ No newline at end of file

done
diff --git a/doc/todo/hostChroot.mdwn b/doc/todo/hostChroot.mdwn
index eccfd64..6a4df9c 100644
--- a/doc/todo/hostChroot.mdwn
+++ b/doc/todo/hostChroot.mdwn
@@ -5,3 +5,5 @@ properties to it. --[[Joey]]
 
 Would need to make privdata use the context of the input Host. And would
 need to propigate privdata info, but not other info. --[[Joey]]
+
+> [[done]] --[[Joey]]

update
diff --git a/doc/README.mdwn b/doc/README.mdwn
index 31d222c..ea622b3 100644
--- a/doc/README.mdwn
+++ b/doc/README.mdwn
@@ -12,8 +12,8 @@ repository to each host it manages, in a
 [components](http://propellor.branchable.com/components/)
 for details.
 
-Properties are defined using Haskell. Edit `~/.propellor/config.hs`
-to get started. There is fairly complete 
+Properties are defined using Haskell in the file `~/.propellor/config.hs`.
+There is fairly complete 
 [API documentation](http://hackage.haskell.org/package/propellor/),
 which includes many built-in Properties for dealing with
 [Apt](http://hackage.haskell.org/package/propellor/docs/Propellor-Property-Apt.html)
diff --git a/doc/download.mdwn b/doc/download.mdwn
new file mode 100644
index 0000000..6fe1ca3
--- /dev/null
+++ b/doc/download.mdwn
@@ -0,0 +1,5 @@
+Propellor's source code and some example configs are in a git repository:
+
+`git clone git://propellor.branchable.com/propellor`
+
+See the [[README]] for details on installing and configuring propellor.
diff --git a/doc/index.mdwn b/doc/index.mdwn
index 52c2302..1e3af9d 100644
--- a/doc/index.mdwn
+++ b/doc/index.mdwn
@@ -1,7 +1,7 @@
 [[!meta title="propellor: deploying properties to hosts with haskell"]]
 
 [[!sidebar content="""
-[[Install]]  
+[[Download]]  
 [API documentation](http://hackage.haskell.org/package/propellor)  
 [[Other Documentation|documentation]]
 [Sample config file](http://git.joeyh.name/?p=propellor.git;a=blob;f=joeyconfig.hs)  
diff --git a/doc/install.mdwn b/doc/install.mdwn
deleted file mode 100644
index 8db966f..0000000
--- a/doc/install.mdwn
+++ /dev/null
@@ -1,3 +0,0 @@
-`git clone git://propellor.branchable.com/propellor`  
-
-Propellor is also available in Debian.

add news item for propellor 3.4.1
diff --git a/doc/news/version_3.2.2.mdwn b/doc/news/version_3.2.2.mdwn
deleted file mode 100644
index 19acc9f..0000000
--- a/doc/news/version_3.2.2.mdwn
+++ /dev/null
@@ -1,5 +0,0 @@
-propellor 3.2.2 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
-   * Added Linode.serialGrub property.
-   * Clean up build warnings about redundant constraints when built with ghc 8.0.
-   * Added Group.hasUser property. Thanks, Daniel Brooks"""]]
\ No newline at end of file
diff --git a/doc/news/version_3.4.1.mdwn b/doc/news/version_3.4.1.mdwn
new file mode 100644
index 0000000..51d9c2a
--- /dev/null
+++ b/doc/news/version_3.4.1.mdwn
@@ -0,0 +1,3 @@
+propellor 3.4.1 released with [[!toggle text="these changes"]]
+[[!toggleable text="""
+   * Fixed https url to propellor git repository."""]]
\ No newline at end of file

add news item for propellor 3.4.0
diff --git a/doc/news/version_3.2.1.mdwn b/doc/news/version_3.2.1.mdwn
deleted file mode 100644
index 214ef42..0000000
--- a/doc/news/version_3.2.1.mdwn
+++ /dev/null
@@ -1,5 +0,0 @@
-propellor 3.2.1 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
-   * Simplify Debootstrap.sourceInstall since #770217 was fixed.
-   * Debootstap.installed: Fix inverted logic that made this never install
-     debootstrap. Thanks, mithrandi."""]]
\ No newline at end of file
diff --git a/doc/news/version_3.4.0.mdwn b/doc/news/version_3.4.0.mdwn
new file mode 100644
index 0000000..d38716e
--- /dev/null
+++ b/doc/news/version_3.4.0.mdwn
@@ -0,0 +1,14 @@
+propellor 3.4.0 released with [[!toggle text="these changes"]]
+[[!toggleable text="""
+   * Added ConfigurableValue type class, for values that can be used in a
+     config file, or to otherwise configure a program.
+   * The val function converts such values to String.
+   * Removed fromPort and fromIPAddr (use val instead). (API change)
+   * Removed several Show instances that were only used for generating
+     configuration, replacing with ConfigurableValue instances. (API change)
+   * The github mirror of propellor's git repository has been removed,
+     since github's terms of service has started imposing unwanted licensing
+     requirements.
+   * propellor --init: The option to clone propellor's git repository
+     used to use the github mirror, and has been changed to use a different
+     mirror."""]]
\ No newline at end of file

github mirror of propellor is gone
* The github mirror of propellor's git repository has been removed,
since github's terms of service has started imposing unwanted licensing
requirements.
* propellor --init: The option to clone propellor's git repository
used to use the github mirror, and has been changed to use a different
mirror.
diff --git a/debian/changelog b/debian/changelog
index f965a58..4763281 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -6,6 +6,12 @@ propellor (3.4.0) UNRELEASED; urgency=medium
   * Removed fromPort and fromIPAddr (use val instead). (API change)
   * Removed several Show instances that were only used for generating
     configuration, replacing with ConfigurableValue instances. (API change)
+  * The github mirror of propellor's git repository has been removed,
+    since github's terms of service has started imposing unwanted licensing
+    requirements.
+  * propellor --init: The option to clone propellor's git repository
+    used to use the github mirror, and has been changed to use a different
+    mirror.
 
  -- Joey Hess <id@joeyh.name>  Sun, 26 Feb 2017 15:15:33 -0400
 
diff --git a/doc/install.mdwn b/doc/install.mdwn
index f64519a..8db966f 100644
--- a/doc/install.mdwn
+++ b/doc/install.mdwn
@@ -1,4 +1,3 @@
 `git clone git://propellor.branchable.com/propellor`  
-Or get it [from github](https://github.com/joeyh/propellor).
 
-Propellor is recently available in Debian.
+Propellor is also available in Debian.
diff --git a/src/Propellor/DotDir.hs b/src/Propellor/DotDir.hs
index 417abcf..f5c20d3 100644
--- a/src/Propellor/DotDir.hs
+++ b/src/Propellor/DotDir.hs
@@ -47,10 +47,10 @@ disthead = distdir </> "head"
 upstreambranch :: String
 upstreambranch = "upstream/master"
 
--- Using the github mirror of the main propellor repo because
+-- Using the joeyh.name mirror of the main propellor repo because
 -- it is accessible over https for better security.
 netrepo :: String
-netrepo = "https://github.com/joeyh/propellor.git"
+netrepo = "https://git.joeyh.name/propellor.git"
 
 dotPropellor :: IO FilePath
 dotPropellor = do

gogogo
diff --git a/doc/todo/Info_property_to_select_host__39__s_preferred_Apt_mirror/comment_1_ac66a33d71092261a745378c82959e69._comment b/doc/todo/Info_property_to_select_host__39__s_preferred_Apt_mirror/comment_1_ac66a33d71092261a745378c82959e69._comment
new file mode 100644
index 0000000..3734d98
--- /dev/null
+++ b/doc/todo/Info_property_to_select_host__39__s_preferred_Apt_mirror/comment_1_ac66a33d71092261a745378c82959e69._comment
@@ -0,0 +1,7 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2017-02-21T03:07:28Z"
+ content="""
+Very good idea. Happy to merge such a patch.
+"""]]

clarify todo
diff --git a/doc/todo/Info_property_to_select_host__39__s_preferred_Apt_mirror.mdwn b/doc/todo/Info_property_to_select_host__39__s_preferred_Apt_mirror.mdwn
index 42aa850..355d53b 100644
--- a/doc/todo/Info_property_to_select_host__39__s_preferred_Apt_mirror.mdwn
+++ b/doc/todo/Info_property_to_select_host__39__s_preferred_Apt_mirror.mdwn
@@ -1 +1 @@
-It would be good to have an info property, say `Apt.mirror`, which sets a host's preferred apt mirror.  Then all properties in `Propellor.Property.Apt` would use this mirror when generating sources lists.  The value of `Apt.mirror` could be an apt cache on the LAN, or a mirror that is known to be better than the Debian CDN, due to where the host is located. --[[spwhitton|user/spwhitton]]
+It would be good to have an info property, say `Apt.mirror`, which sets a host's preferred apt mirror.  Then all properties in `Propellor.Property.Apt` would use this mirror when generating sources lists, falling back to the `deb.debian.org` default.  The value of `Apt.mirror` could be an apt cache on the LAN, or a mirror that is known to be better than the Debian CDN from where the host is located. --[[spwhitton|user/spwhitton]]

create user page in correct place
diff --git a/doc/user/spwhitton.mdwn b/doc/user/spwhitton.mdwn
new file mode 100644
index 0000000..f5f92fa
--- /dev/null
+++ b/doc/user/spwhitton.mdwn
@@ -0,0 +1 @@
+Maintainer of propellor's Debian package, and several modules.

removed
diff --git a/doc/todo/user/spwhitton.mdwn b/doc/todo/user/spwhitton.mdwn
deleted file mode 100644
index 74810d5..0000000
--- a/doc/todo/user/spwhitton.mdwn
+++ /dev/null
@@ -1 +0,0 @@
-Maintainer of the Debian package of propellor.  Also maintainer of some of propellor's modules.

post todo: apt mirror info property
diff --git a/doc/todo/Info_property_to_select_host__39__s_preferred_Apt_mirror.mdwn b/doc/todo/Info_property_to_select_host__39__s_preferred_Apt_mirror.mdwn
new file mode 100644
index 0000000..42aa850
--- /dev/null
+++ b/doc/todo/Info_property_to_select_host__39__s_preferred_Apt_mirror.mdwn
@@ -0,0 +1 @@
+It would be good to have an info property, say `Apt.mirror`, which sets a host's preferred apt mirror.  Then all properties in `Propellor.Property.Apt` would use this mirror when generating sources lists.  The value of `Apt.mirror` could be an apt cache on the LAN, or a mirror that is known to be better than the Debian CDN, due to where the host is located. --[[spwhitton|user/spwhitton]]

create user page
diff --git a/doc/todo/user/spwhitton.mdwn b/doc/todo/user/spwhitton.mdwn
new file mode 100644
index 0000000..74810d5
--- /dev/null
+++ b/doc/todo/user/spwhitton.mdwn
@@ -0,0 +1 @@
+Maintainer of the Debian package of propellor.  Also maintainer of some of propellor's modules.

add news item for propellor 3.3.1
diff --git a/doc/news/version_3.2.0.mdwn b/doc/news/version_3.2.0.mdwn
deleted file mode 100644
index bef06b1..0000000
--- a/doc/news/version_3.2.0.mdwn
+++ /dev/null
@@ -1,17 +0,0 @@
-propellor 3.2.0 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
- * [ Sean Whitton ]
-   * Using ccache with Sbuild.built &amp; Sbuild.builtFor is now toggleable: these
-     properties now take a parameter of type Sbuild.UseCcache.  (API Change)
-   * Sbuild.piupartsConf: no longer takes an Apt.Url. (API Change)
-   * Sbuild.piupartsConf &amp; Sbuild.piupartsConfFor: does nothing if corresponding
-     schroot not built.
-     Previously, these properties built the schroot if it was missing.
-   * Sbuild.built &amp; Sbuild.piupartsConf: add an additional alias to sid chroots.
-     This is for compatibility with `dgit sbuild`.
-   * Further improvements to Sbuild.hs haddock.
- * [ Joey Hess ]
-   * Tor.hiddenService: Converted port parameter from Int to Port. (API change)
-   * Tor.hiddenServiceAvailable: The hidden service hostname file may not
-     be available immedaitely after configuring tor; avoid ugly error in
-     this case."""]]
\ No newline at end of file
diff --git a/doc/news/version_3.3.1.mdwn b/doc/news/version_3.3.1.mdwn
new file mode 100644
index 0000000..84ab72f
--- /dev/null
+++ b/doc/news/version_3.3.1.mdwn
@@ -0,0 +1,8 @@
+propellor 3.3.1 released with [[!toggle text="these changes"]]
+[[!toggleable text="""
+   * Apt: Removed the mirrors.kernel.org line from stdSourcesList etc.
+     The mirror CDN has a new implementation that should avoid the problems
+     with httpredir that made an extra mirror sometimes be needed.
+   * Switch Debian CDN address to deb.debian.org.
+   * Tor.hiddenService: Fix bug in torrc's HiddenServicePort configuration.
+     Thanks, Félix Sipma"""]]
\ No newline at end of file

Revert "removed"
This reverts commit da400e7af20bf418c13de4456822303d91af83a3.
No idea why this comment was removed. Going to assume it was an accident
unless it happens again..
diff --git a/doc/todo/Arch_Linux_Port/comment_3_d917de766dfe7fded7317d7614d1467f._comment b/doc/todo/Arch_Linux_Port/comment_3_d917de766dfe7fded7317d7614d1467f._comment
new file mode 100644
index 0000000..27ef807
--- /dev/null
+++ b/doc/todo/Arch_Linux_Port/comment_3_d917de766dfe7fded7317d7614d1467f._comment
@@ -0,0 +1,25 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 3"""
+ date="2017-02-04T20:55:02Z"
+ content="""
+> Instead,  I changed some properties in DiskImage from Linux to
+> DebianLike.  Is it the correct way to do it?
+
+Looking at it, kpartx is DebianLike-specific, so imageBuiltFrom which uses it
+should be too. The only reason it wasn't marked as DebianLike already and
+was type Linux is because Linux used to be the same as DebianLike and so
+the type checker didn't see a difference. No longer, thanks to your patch.
+
+So, it makes complete sense that you have to change this. You're paying
+the price of blazing the trail of the first non-DebianLike Linux distro in
+Propellor..
+
+---
+
+Looks like your [[!commit 25f6871e1dda3de252fbc6c8ac6962eb0cd9311a]]
+dealt with all my review suggestions. And so, I've merged it.
+
+Unless you have anything else that needs to be done, I'll release
+propellor soon with the added Arch Linux support. Thank you very much!
+"""]]

removed
diff --git a/doc/todo/Arch_Linux_Port/comment_3_d917de766dfe7fded7317d7614d1467f._comment b/doc/todo/Arch_Linux_Port/comment_3_d917de766dfe7fded7317d7614d1467f._comment
deleted file mode 100644
index 27ef807..0000000
--- a/doc/todo/Arch_Linux_Port/comment_3_d917de766dfe7fded7317d7614d1467f._comment
+++ /dev/null
@@ -1,25 +0,0 @@
-[[!comment format=mdwn
- username="joey"
- subject="""comment 3"""
- date="2017-02-04T20:55:02Z"
- content="""
-> Instead,  I changed some properties in DiskImage from Linux to
-> DebianLike.  Is it the correct way to do it?
-
-Looking at it, kpartx is DebianLike-specific, so imageBuiltFrom which uses it
-should be too. The only reason it wasn't marked as DebianLike already and
-was type Linux is because Linux used to be the same as DebianLike and so
-the type checker didn't see a difference. No longer, thanks to your patch.
-
-So, it makes complete sense that you have to change this. You're paying
-the price of blazing the trail of the first non-DebianLike Linux distro in
-Propellor..
-
----
-
-Looks like your [[!commit 25f6871e1dda3de252fbc6c8ac6962eb0cd9311a]]
-dealt with all my review suggestions. And so, I've merged it.
-
-Unless you have anything else that needs to be done, I'll release
-propellor soon with the added Arch Linux support. Thank you very much!
-"""]]

Remove additional space in url. Was not cloneable by copy&paste.
diff --git a/doc/install.mdwn b/doc/install.mdwn
index ad87ced..f64519a 100644
--- a/doc/install.mdwn
+++ b/doc/install.mdwn
@@ -1,4 +1,4 @@
-`git clone git://propellor.branchable.com/ propellor`  
+`git clone git://propellor.branchable.com/propellor`  
 Or get it [from github](https://github.com/joeyh/propellor).
 
 Propellor is recently available in Debian.

add news item for propellor 3.3.0
diff --git a/doc/news/version_3.1.2.mdwn b/doc/news/version_3.1.2.mdwn
deleted file mode 100644
index b54b396..0000000
--- a/doc/news/version_3.1.2.mdwn
+++ /dev/null
@@ -1,22 +0,0 @@
-propellor 3.1.2 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
- * [ Joey Hess ]
-   * Ssh.knownHost: Bug fix: Only fix up the owner of the known\_hosts
-     file after it exists.
- * [ Sean Whitton ]
-   * Sbuild.keypairInsecurelyGenerated: Improved to be more robust.
-   * Pass --allow-unrelated-histories to git merge when run with git 2.9 or
-     newer. This fixes the /usr/bin/propellor wrapper with this version of git.
-   * Sbuild.built &amp; Sbuild.builtFor no longer require Sbuild.keypairGenerated.
-     Transition guide: If you are using sbuild 0.70.0 or newer, you should
-     `rm -r /var/lib/sbuild/apt-keys`.  Otherwise, you should add either
-     Sbuild.keypairGenerated or Sbuild.keypairInsecurelyGenerated to your host.
-   * Sbuild haddock improvements:
-     - State that we don't support squeeze and Buntish older than trusty.
-       This is due to our enhancements, such as eatmydata.
-     - State that you need sbuild 0.70.0 or newer to build for stretch.
-       This is due to gpg2 hitting Debian stretch.
-     - Explain when a keygen is required.
-     - Update sample ~/.sbuildrc for sbuild 0.71.0.
-     - Add hint for customising chroots with propellor.
-     - Update example usage of System type."""]]
\ No newline at end of file
diff --git a/doc/news/version_3.3.0.mdwn b/doc/news/version_3.3.0.mdwn
new file mode 100644
index 0000000..19bd566
--- /dev/null
+++ b/doc/news/version_3.3.0.mdwn
@@ -0,0 +1,26 @@
+propellor 3.3.0 released with [[!toggle text="these changes"]]
+[[!toggleable text="""
+   * Arch Linux is now supported by Propellor!
+     Thanks to Zihao Wang for this port.
+   * Added Propellor.Property.Pacman for Arch's package manager.
+     Maintained by Zihao Wang.
+   * The types of some properties changed; eg from Property DebianLike
+     to Property (DebianLike + ArchLinux). Also, DebianLike and Linux
+     are no longer type synonyms; propellor now knows that Linux includes
+     ArchLinux. This could require updates to code, so is a minor API change.
+   * GHC's fileSystemEncoding is used for all String IO, to avoid
+     encoding-related crashes in eg, Propellor.Property.File.
+   * Add --build option to simply build config.hs.
+   * More informative usage message. Thanks, Daniel Brooks
+   * Tor.hiddenService' added to support multiple ports.
+     Thanks, Félix Sipma.
+   * Apt.noPDiffs added.
+     Thanks, Sean Whitton.
+   * stack.yaml: Compile with GHC 8.0.1 against lts-7.16.
+     Thanks, Andrew Cowie.
+   * Added Propellor.Property.File.configFileName and related functions
+     to generate good filenames for config directories.
+   * Added Apt.suiteAvailablePinned, Apt.pinnedTo.
+     Thanks, Sean Whitton.
+   * Added File.containsBlock
+     Thanks, Sean Whitton."""]]
\ No newline at end of file

link to lwn article
diff --git a/doc/news/Linux.Conf.Au.presentation.mdwn b/doc/news/Linux.Conf.Au.presentation.mdwn
index 0041955..5418097 100644
--- a/doc/news/Linux.Conf.Au.presentation.mdwn
+++ b/doc/news/Linux.Conf.Au.presentation.mdwn
@@ -2,3 +2,4 @@
 
 [video](http://mirror.linux.org.au/pub/linux.conf.au/2017/Type_driven_configuration_management_with_Propellor.webm)
 
+Also see this writeup in [Linux Weekly News](https://lwn.net/Articles/713653/)

Added a comment
diff --git a/doc/todo/Arch_Linux_Port/comment_4_924c73c0ab6fb39c9b25ae51facf6bb6._comment b/doc/todo/Arch_Linux_Port/comment_4_924c73c0ab6fb39c9b25ae51facf6bb6._comment
new file mode 100644
index 0000000..f69e2c8
--- /dev/null
+++ b/doc/todo/Arch_Linux_Port/comment_4_924c73c0ab6fb39c9b25ae51facf6bb6._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="wzhd"
+ avatar="http://cdn.libravatar.org/avatar/d5a499b7c476ca9960cc8dccdf455bae"
+ subject="comment 4"
+ date="2017-02-05T00:59:18Z"
+ content="""
+That's great! Thank you so much!
+"""]]

close
diff --git a/doc/todo/modify_Apt.pinnedTo_to_pin_a_package_to_multiple_suites_with_different_priorities.mdwn b/doc/todo/modify_Apt.pinnedTo_to_pin_a_package_to_multiple_suites_with_different_priorities.mdwn
index 047324c..02be4ad 100644
--- a/doc/todo/modify_Apt.pinnedTo_to_pin_a_package_to_multiple_suites_with_different_priorities.mdwn
+++ b/doc/todo/modify_Apt.pinnedTo_to_pin_a_package_to_multiple_suites_with_different_priorities.mdwn
@@ -3,3 +3,5 @@ Please consider merging the `pin` branch of `https://git.spwhitton.name/propello
 I've modified `Apt.pinnedTo` so that it can pin an `AptPrefPackage` to multiple suites with different pin priorities.  I've included a sample use-case in the function's haddock.
 
 --spwhitton
+
+> merged, [[done]] --[[Joey]]

mention Arch
diff --git a/doc/Linux.mdwn b/doc/Linux.mdwn
index 00276f6..ca0cfd6 100644
--- a/doc/Linux.mdwn
+++ b/doc/Linux.mdwn
@@ -1,5 +1,6 @@
 Propellor was written to manage Linux systems.
-It supports Debian and Debian-derived distributions.
+It supports Debian and Debian-derived distributions,
+as well as Arch Linux.
 
 Support for other distributions should not be too hard to add.
 Indeed, Propellor has been ported to [[FreeBSD]] now!

arch
diff --git a/doc/forum/Supported_OS/comment_3_f2924708a819b962ba7ed690019601ed._comment b/doc/forum/Supported_OS/comment_3_f2924708a819b962ba7ed690019601ed._comment
new file mode 100644
index 0000000..c03f6cd
--- /dev/null
+++ b/doc/forum/Supported_OS/comment_3_f2924708a819b962ba7ed690019601ed._comment
@@ -0,0 +1,7 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""Arch too!"""
+ date="2017-02-04T21:30:26Z"
+ content="""
+Propellor just got support for Arch Linux!
+"""]]

Arch Linux is now supported by Propellor! Thanks to Zihao Wang for this port.
* Arch Linux is now supported by Propellor!
Thanks to Zihao Wang for this port.
* Added Propellor.Property.Pacman for Arch's package manager.
Maintained by Zihao Wang.
* The types of some properties changed; eg from Property DebianLike
to Property (DebianLike + ArchLinux). This could require updates
to code using those properties, so is a minor API change.
diff --git a/debian/changelog b/debian/changelog
index 8136040..3a12ca7 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,5 +1,12 @@
-propellor (3.2.4) UNRELEASED; urgency=medium
-
+propellor (3.3.0) UNRELEASED; urgency=medium
+
+  * Arch Linux is now supported by Propellor!
+    Thanks to Zihao Wang for this port.
+  * Added Propellor.Property.Pacman for Arch's package manager.
+    Maintained by Zihao Wang.
+  * The types of some properties changed; eg from Property DebianLike
+    to Property (DebianLike + ArchLinux). This could require updates
+    to code using those properties, so is a minor API change.
   * GHC's fileSystemEncoding is used for all String IO, to avoid
     encoding-related crashes in eg, Propellor.Property.File.
   * Add --build option to simply build config.hs.
diff --git a/doc/todo/Arch_Linux_Port.mdwn b/doc/todo/Arch_Linux_Port.mdwn
index a899dbb..ac3ee4d 100644
--- a/doc/todo/Arch_Linux_Port.mdwn
+++ b/doc/todo/Arch_Linux_Port.mdwn
@@ -12,3 +12,5 @@ I've made some addtional minor changes to make propellor compile without errors:
 - Rsync.installed and Docker.installed now supports Pacman as well
 
 Hope you enjoy it!
+
+> [[merged|done]]; it was indeed enjoyable. thank you! --[[Joey]]
diff --git a/doc/todo/Arch_Linux_Port/comment_3_d917de766dfe7fded7317d7614d1467f._comment b/doc/todo/Arch_Linux_Port/comment_3_d917de766dfe7fded7317d7614d1467f._comment
new file mode 100644
index 0000000..27ef807
--- /dev/null
+++ b/doc/todo/Arch_Linux_Port/comment_3_d917de766dfe7fded7317d7614d1467f._comment
@@ -0,0 +1,25 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 3"""
+ date="2017-02-04T20:55:02Z"
+ content="""
+> Instead,  I changed some properties in DiskImage from Linux to
+> DebianLike.  Is it the correct way to do it?
+
+Looking at it, kpartx is DebianLike-specific, so imageBuiltFrom which uses it
+should be too. The only reason it wasn't marked as DebianLike already and
+was type Linux is because Linux used to be the same as DebianLike and so
+the type checker didn't see a difference. No longer, thanks to your patch.
+
+So, it makes complete sense that you have to change this. You're paying
+the price of blazing the trail of the first non-DebianLike Linux distro in
+Propellor..
+
+---
+
+Looks like your [[!commit 25f6871e1dda3de252fbc6c8ac6962eb0cd9311a]]
+dealt with all my review suggestions. And so, I've merged it.
+
+Unless you have anything else that needs to be done, I'll release
+propellor soon with the added Arch Linux support. Thank you very much!
+"""]]
diff --git a/propellor.cabal b/propellor.cabal
index 1b5c46d..a33b982 100644
--- a/propellor.cabal
+++ b/propellor.cabal
@@ -1,5 +1,5 @@
 Name: propellor
-Version: 3.2.3
+Version: 3.3.0
 Cabal-Version: >= 1.8
 License: BSD2
 Maintainer: Joey Hess <id@joeyh.name>
@@ -128,6 +128,7 @@ Library
     Propellor.Property.Obnam
     Propellor.Property.OpenId
     Propellor.Property.OS
+    Propellor.Property.Pacman
     Propellor.Property.Parted
     Propellor.Property.Partition
     Propellor.Property.Postfix