    -- | Specific configuration for Joey Hess's sites. Probably not useful to
    -- others except as an example.

    {-# LANGUAGE FlexibleContexts, TypeFamilies #-}

    module Propellor.Property.SiteSpecific.JoeySites where

    import Propellor.Base
    import qualified Propellor.Property.Apt as Apt
    import qualified Propellor.Property.File as File
    import qualified Propellor.Property.ConfFile as ConfFile
    import qualified Propellor.Property.Gpg as Gpg
    import qualified Propellor.Property.Ssh as Ssh
    import qualified Propellor.Property.Git as Git
    import qualified Propellor.Property.Cron as Cron
    import qualified Propellor.Property.Service as Service
    import qualified Propellor.Property.User as User
    import qualified Propellor.Property.Group as Group
    import qualified Propellor.Property.Sudo as Sudo
    import qualified Propellor.Property.Borg as Borg
    import qualified Propellor.Property.Apache as Apache
    import qualified Propellor.Property.Postfix as Postfix
    import qualified Propellor.Property.Systemd as Systemd
    import qualified Propellor.Property.Network as Network
    import qualified Propellor.Property.Fail2Ban as Fail2Ban
    import qualified Propellor.Property.LetsEncrypt as LetsEncrypt
    import qualified Propellor.Property.Mount as Mount
    import Utility.Split

    import Data.List
    import System.Posix.Files

    scrollBox :: Property (HasInfo + DebianLike)
    scrollBox :: Property (HasInfo + DebianLike)
    scrollBox = Desc
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    propertyList "scroll server" (Props
       (MetaTypes
          '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
     -> Property (HasInfo + DebianLike))
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property (HasInfo + DebianLike)
    forall a b. (a -> b) -> a -> b
    $ Props UnixLike
    props
    	Props UnixLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & User -> Property DebianLike
    User.accountFor (Desc -> User
    User "scroll")
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & User -> Desc -> Desc -> Maybe Desc -> Property DebianLike
    Git.cloned (Desc -> User
    User "scroll") "git://git.kitenet.net/scroll" (Desc
    d Desc -> Desc -> Desc
    </> "scroll") Maybe Desc
    forall a. Maybe a
    Nothing
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & [Desc] -> Property DebianLike
    Apt.installed ["ghc", "make", "cabal-install", "libghc-vector-dev",
    		"libghc-bytestring-dev", "libghc-mtl-dev", "libghc-ncurses-dev",
    		"libghc-random-dev", "libghc-monad-loops-dev", "libghc-text-dev",
    		"libghc-ifelse-dev", "libghc-case-insensitive-dev",
    		"libghc-data-default-dev", "libghc-optparse-applicative-dev"]
    	Props DebianLike
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & User -> [Desc] -> UncheckedProperty UnixLike
    userScriptProperty (Desc -> User
    User "scroll")
    		[ "cd " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    d Desc -> Desc -> Desc
    </> "scroll"
    		, "git pull"
    		, "cabal configure"
    		, "make"
    		]
    		UncheckedProperty UnixLike -> Result -> Property UnixLike
    forall (p :: * -> *) i.
    Checkable p i =>
    p i -> Result -> Property i
    `assume` Result
    MadeChange
    	Props DebianLike
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc
    s Desc -> [Desc] -> Property UnixLike
    `File.hasContent`
    		[ "#!/bin/sh"
    		, "set -e"
    		, "echo Preparing to run scroll!"
    		, "cd " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    d
    		, "mkdir -p tmp"
    		, "TMPDIR= t=$(tempfile -d tmp)"
    		, "export t"
    		, "rm -f \"$t\""
    		, "mkdir \"$t\""
    		, "cd \"$t\""
    		, "echo"
    		, "echo Note that games on this server are time-limited to 2 hours"
    		, "echo 'Need more time? Run scroll locally instead!'"
    		, "echo"
    		, "echo Press Enter to start the game."
    		, "read me"
    		, "SHELL=/bin/sh script --timing=timing -c " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    g
    		] Property UnixLike
    -> Property UnixLike
    -> CombinedType (Property UnixLike) (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` (Desc
    s Desc -> FileMode -> Property UnixLike
    `File.mode` ([FileMode] -> FileMode
    combineModes (FileMode
    ownerWriteModeFileMode -> [FileMode] -> [FileMode]
    forall a. a -> [a] -> [a]
    :[FileMode]
    readModes [FileMode] -> [FileMode] -> [FileMode]
    forall a. [a] -> [a] -> [a]
    ++ [FileMode]
    executeModes)))
    	Props DebianLike
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc
    g Desc -> [Desc] -> Property UnixLike
    `File.hasContent`
    		[ "#!/bin/sh"
    		, "if ! timeout --kill-after 1m --foreground 2h ../../scroll/scroll; then"
    		, "echo Scroll seems to have ended unexpectedly. Possibly a bug.."
    		, "else"
    		, "echo Thanks for playing scroll! https://joeyh.name/code/scroll/"
    		, "fi"
    		, "echo Your game was recorded, as ID:$(basename \"$t\")"
    		, "echo if you would like to talk about how it went, email scroll@joeyh.name"
    		, "read line"
    		] Property UnixLike
    -> Property UnixLike
    -> CombinedType (Property UnixLike) (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` (Desc
    g Desc -> FileMode -> Property UnixLike
    `File.mode` ([FileMode] -> FileMode
    combineModes (FileMode
    ownerWriteModeFileMode -> [FileMode] -> [FileMode]
    forall a. a -> [a] -> [a]
    :[FileMode]
    readModes [FileMode] -> [FileMode] -> [FileMode]
    forall a. [a] -> [a] -> [a]
    ++ [FileMode]
    executeModes)))
    	-- prevent port forwarding etc by not letting scroll log in via ssh
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc
    Ssh.sshdConfig Desc -> Desc -> Property UnixLike
    `File.containsLine` ("DenyUsers scroll")
    		Property UnixLike
    -> Property DebianLike
    -> CombinedType (Property UnixLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Property DebianLike
    Ssh.restarted
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & User -> Desc -> Property DebianLike
    User.shellSetTo (Desc -> User
    User "scroll") Desc
    s
    	Props DebianLike
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & User -> Property (HasInfo + DebianLike)
    User.hasPassword (Desc -> User
    User "scroll")
    	-- telnetd attracted password crackers, so disabled
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & [Desc] -> Property DebianLike
    Apt.removed ["telnetd"]
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & [Desc] -> Property DebianLike
    Apt.installed ["shellinabox"]
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> [Desc] -> Property UnixLike
    File.hasContent "/etc/default/shellinabox"
    		[ "# Deployed by propellor"
    		, "SHELLINABOX_DAEMON_START=1"
    		, "SHELLINABOX_PORT=4242"
    		, "SHELLINABOX_ARGS=\"--disable-ssl --no-beep --service=:scroll:scroll:" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    d Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ ":" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    s Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ "\""
    		]
    		Property UnixLike
    -> Property DebianLike
    -> CombinedType (Property UnixLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Desc -> Property DebianLike
    Service.restarted "shellinabox"
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property DebianLike
    Service.running "shellinabox"
      where
    	d :: Desc
    d = "/home/scroll"
    	s :: Desc
    s = Desc
    d Desc -> Desc -> Desc
    </> "login.sh"
    	g :: Desc
    g = Desc
    d Desc -> Desc -> Desc
    </> "game.sh"

    kgbServer :: Property (HasInfo + DebianLike)
    kgbServer :: Property (HasInfo + DebianLike)
    kgbServer = Desc
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    propertyList Desc
    desc (Props
       (MetaTypes
          '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
     -> Property (HasInfo + DebianLike))
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property (HasInfo + DebianLike)
    forall a b. (a -> b) -> a -> b
    $ Props UnixLike
    props
    	Props UnixLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property DebianLike
    Apt.serviceInstalledRunning "kgb-bot"
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & "/etc/default/kgb-bot" Desc -> Desc -> Property UnixLike
    `File.containsLine` "BOT_ENABLED=1"
    		Property UnixLike -> Desc -> Property UnixLike
    forall p. IsProp p => p -> Desc -> p
    `describe` "kgb bot enabled"
    		Property UnixLike
    -> Property DebianLike
    -> CombinedType (Property UnixLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Desc -> Property DebianLike
    Service.running "kgb-bot"
    	Props DebianLike
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Context -> Property (HasInfo + UnixLike)
    forall c. IsContext c => Desc -> c -> Property (HasInfo + UnixLike)
    File.hasPrivContent "/etc/kgb-bot/kgb.conf" Context
    anyContext
    		Property
      (Sing
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Property DebianLike
    -> CombinedType
         (Property
            (Sing
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
         (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Desc -> Property DebianLike
    Service.restarted "kgb-bot"
      where
    	desc :: Desc
    desc = "kgb.kitenet.net setup"

    -- git.kitenet.net and git.joeyh.name
    gitServer :: [Host] -> Property (HasInfo + DebianLike)
    gitServer :: [Host] -> Property (HasInfo + DebianLike)
    gitServer hosts :: [Host]
    hosts = Desc
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    propertyList "git.kitenet.net setup" (Props
       (MetaTypes
          '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
     -> Property (HasInfo + DebianLike))
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property (HasInfo + DebianLike)
    forall a b. (a -> b) -> a -> b
    $ Props UnixLike
    props
    	Props UnixLike
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc
    -> BorgRepo
    -> Times
    -> [Desc]
    -> [KeepPolicy]
    -> Property DebianLike
    Borg.backup "/srv/git" BorgRepo
    borgrepo
    		(Desc -> Times
    Cron.Times "33 3 * * *")
    		[]
    		[Int -> KeepPolicy
    Borg.KeepDays 30]
    		Property DebianLike
    -> Property
         (Sing
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> CombinedType
         (Property DebianLike)
         (Property
            (Sing
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Maybe Desc
    -> User
    -> Context
    -> (SshKeyType, Desc)
    -> Property (HasInfo + UnixLike)
    forall c.
    IsContext c =>
    Maybe Desc
    -> User -> c -> (SshKeyType, Desc) -> Property (HasInfo + UnixLike)
    Ssh.userKeyAt (Desc -> Maybe Desc
    forall a. a -> Maybe a
    Just Desc
    sshkey)
    			(Desc -> User
    User "root")
    			(Desc -> Context
    Context "git.kitenet.net")
    			(SshKeyType
    SshEd25519, "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOvgBVYP6srImGbJ+kg1K68HeUQqxHEBQswMWSqu9WOu root@kite")
    		Property
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property UnixLike
    -> CombinedType
         (Property
            (MetaTypes
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
         (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` [Host] -> Desc -> User -> Property UnixLike
    Ssh.knownHost [Host]
    hosts "usw-s002.rsync.net" (Desc -> User
    User "root")
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (Sing
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & User -> Context -> Property (HasInfo + UnixLike)
    forall c. IsContext c => User -> c -> Property (HasInfo + UnixLike)
    Ssh.authorizedKeys (Desc -> User
    User "family") (Desc -> Context
    Context "git.kitenet.net")
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & User -> Property DebianLike
    User.accountFor (Desc -> User
    User "family")
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & [Desc] -> Property DebianLike
    Apt.installed ["git", "rsync", "cgit"]
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & [Desc] -> Property DebianLike
    Apt.installed ["git-annex"]
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & [Desc] -> Property DebianLike
    Apt.installed ["kgb-client"]
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (Sing
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Context -> Property (HasInfo + UnixLike)
    forall c. IsContext c => Desc -> c -> Property (HasInfo + UnixLike)
    File.hasPrivContentExposed "/etc/kgb-bot/kgb-client.conf" Context
    anyContext
    		Property
      (Sing
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Property UnixLike
    -> CombinedType
         (Property
            (Sing
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
         (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Desc -> Property UnixLike
    File.dirExists "/etc/kgb-bot/"
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> RevertableProperty DebianLike DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> RevertableProperty DebianLike DebianLike
    Git.daemonRunning "/srv/git"
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & "/etc/cgitrc" Desc -> [Desc] -> Property UnixLike
    `File.hasContent`
    		[ "clone-url=https://git.joeyh.name/git/$CGIT_REPO_URL git://git.joeyh.name/$CGIT_REPO_URL"
    		, "css=/cgit-css/cgit.css"
    		, "logo=/cgit-css/cgit.png"
    		, "enable-http-clone=1"
    		, "root-title=Joey's git repositories"
    		, "root-desc="
    		, "enable-index-owner=0"
    		, "snapshots=tar.gz"
    		, "enable-git-config=1"
    		, "scan-path=/srv/git"
    		]
    		Property UnixLike -> Desc -> Property UnixLike
    forall p. IsProp p => p -> Desc -> p
    `describe` "cgit configured"
    	-- I keep the website used for git.kitenet.net/git.joeyh.name checked into git..
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & User -> Desc -> Desc -> Maybe Desc -> Property DebianLike
    Git.cloned (Desc -> User
    User "joey") "/srv/git/joey/git.kitenet.net.git" "/srv/web/git.kitenet.net" Maybe Desc
    forall a. Maybe a
    Nothing
    	-- Don't need global apache configuration for cgit.
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> RevertableProperty DebianLike DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a k (x :: [a]) (z :: [a]) (y :: k).
    CheckCombinableNote x z (NoteFor ('Text "!")) =>
    Props (MetaTypes x)
    -> RevertableProperty (MetaTypes y) (MetaTypes z)
    -> Props (MetaTypes (Combine x z))
    ! Desc -> RevertableProperty DebianLike DebianLike
    Apache.confEnabled "cgit"
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> RevertableProperty DebianLike DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> RevertableProperty DebianLike DebianLike
    website "git.kitenet.net"
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> RevertableProperty DebianLike DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> RevertableProperty DebianLike DebianLike
    website "git.joeyh.name"
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> RevertableProperty DebianLike DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> RevertableProperty DebianLike DebianLike
    Apache.modEnabled "cgi"
      where
    	sshkey :: Desc
    sshkey = "/root/.ssh/git.kitenet.net.key"
    	borgrepo :: BorgRepo
    borgrepo = Desc -> [BorgRepoOpt] -> BorgRepo
    rsyncNetBorgRepo "git.kitenet.net.borg" [Desc -> BorgRepoOpt
    Borg.UseSshKey Desc
    sshkey]
    	website :: Desc -> RevertableProperty DebianLike DebianLike
    website hn :: Desc
    hn = Desc
    -> Desc
    -> AgreeTOS
    -> [Desc]
    -> RevertableProperty DebianLike DebianLike
    Apache.httpsVirtualHost' Desc
    hn "/srv/web/git.kitenet.net/" AgreeTOS
    letos
    		[ Desc
    Apache.iconDir
    		, "  <Directory /srv/web/git.kitenet.net/>"
    		, "    Options Indexes ExecCGI FollowSymlinks"
    		, "    AllowOverride None"
    		, "    AddHandler cgi-script .cgi"
    		, "    DirectoryIndex index.cgi"
    		,      Desc
    Apache.allowAll
    		, "  </Directory>"
    		, ""
    		, "  ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/"
    		, "  <Directory /usr/lib/cgi-bin>"
    		, "    SetHandler cgi-script"
    		, "    Options ExecCGI"
    		, "  </Directory>"
    		]

    type AnnexUUID = String

    -- | A website, with files coming from a git-annex repository.
    annexWebSite :: Git.RepoUrl -> HostName -> AnnexUUID -> [(String, Git.RepoUrl)] -> Property (HasInfo + DebianLike)
    annexWebSite :: Desc
    -> Desc
    -> Desc
    -> [(Desc, Desc)]
    -> Property (HasInfo + DebianLike)
    annexWebSite origin :: Desc
    origin hn :: Desc
    hn uuid :: Desc
    uuid remotes :: [(Desc, Desc)]
    remotes = Desc
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    propertyList (Desc
    hn Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++" website using git-annex") (Props
       (MetaTypes
          '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
     -> Property (HasInfo + DebianLike))
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property (HasInfo + DebianLike)
    forall a b. (a -> b) -> a -> b
    $ Props UnixLike
    props
    	Props UnixLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & User -> Desc -> Desc -> Maybe Desc -> Property DebianLike
    Git.cloned (Desc -> User
    User "joey") Desc
    origin Desc
    dir Maybe Desc
    forall a. Maybe a
    Nothing
    		Property DebianLike
    -> Property UnixLike
    -> CombinedType (Property DebianLike) (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Property UnixLike
    setup
    	Props DebianLike
    -> Property
         (Sing
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property (HasInfo + UnixLike)
    alias Desc
    hn
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc
    postupdatehook Desc -> [Desc] -> Property UnixLike
    `File.hasContent`
    		[ "#!/bin/sh"
    		, "exec git update-server-info"
    		] Property UnixLike
    -> Property UnixLike
    -> CombinedType (Property UnixLike) (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange`
    			(Desc
    postupdatehook Desc -> FileMode -> Property UnixLike
    `File.mode` ([FileMode] -> FileMode
    combineModes (FileMode
    ownerWriteModeFileMode -> [FileMode] -> [FileMode]
    forall a. a -> [a] -> [a]
    :[FileMode]
    readModes [FileMode] -> [FileMode] -> [FileMode]
    forall a. [a] -> [a] -> [a]
    ++ [FileMode]
    executeModes)))
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> RevertableProperty DebianLike DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & RevertableProperty DebianLike DebianLike
    setupapache
      where
    	dir :: Desc
    dir = "/srv/web/" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    hn
    	postupdatehook :: Desc
    postupdatehook = Desc
    dir Desc -> Desc -> Desc
    </> ".git/hooks/post-update"
    	setup :: Property UnixLike
    setup = User -> [Desc] -> UncheckedProperty UnixLike
    userScriptProperty (Desc -> User
    User "joey") [Desc]
    setupscript
    		UncheckedProperty UnixLike -> Result -> Property UnixLike
    forall (p :: * -> *) i.
    Checkable p i =>
    p i -> Result -> Property i
    `assume` Result
    MadeChange
    	setupscript :: [Desc]
    setupscript =
    		[ "cd " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc -> Desc
    shellEscape Desc
    dir
    		, "git annex reinit " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc -> Desc
    shellEscape Desc
    uuid
    		] [Desc] -> [Desc] -> [Desc]
    forall a. [a] -> [a] -> [a]
    ++ ((Desc, Desc) -> Desc) -> [(Desc, Desc)] -> [Desc]
    forall a b. (a -> b) -> [a] -> [b]
    map (Desc, Desc) -> Desc
    addremote [(Desc, Desc)]
    remotes [Desc] -> [Desc] -> [Desc]
    forall a. [a] -> [a] -> [a]
    ++
    		[ "git annex get"
    		, "git update-server-info"
    		]
    	addremote :: (Desc, Desc) -> Desc
    addremote (name :: Desc
    name, url :: Desc
    url) = "git remote add " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc -> Desc
    shellEscape Desc
    name Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ " " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc -> Desc
    shellEscape Desc
    url
    	setupapache :: RevertableProperty DebianLike DebianLike
    setupapache = Desc
    -> Desc
    -> AgreeTOS
    -> [Desc]
    -> RevertableProperty DebianLike DebianLike
    Apache.httpsVirtualHost' Desc
    hn Desc
    dir AgreeTOS
    letos
    		[ "  ServerAlias www."Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++Desc
    hn
    		,    Desc
    Apache.iconDir
    		, "  <Directory "Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++Desc
    dirDesc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++">"
    		, "    Options Indexes FollowSymLinks ExecCGI"
    		, "    AllowOverride None"
    		, "    AddHandler cgi-script .cgi"
    		, "    DirectoryIndex index.html index.cgi"
    		,      Desc
    Apache.allowAll
    		, "  </Directory>"
    		]

    letos :: LetsEncrypt.AgreeTOS
    letos :: AgreeTOS
    letos = Maybe Desc -> AgreeTOS
    LetsEncrypt.AgreeTOS (Desc -> Maybe Desc
    forall a. a -> Maybe a
    Just "id@joeyh.name")

    apacheSite :: HostName -> Apache.ConfigFile -> RevertableProperty DebianLike DebianLike
    apacheSite :: Desc -> [Desc] -> RevertableProperty DebianLike DebianLike
    apacheSite hn :: Desc
    hn middle :: [Desc]
    middle = Desc -> [Desc] -> RevertableProperty DebianLike DebianLike
    Apache.siteEnabled Desc
    hn ([Desc] -> RevertableProperty DebianLike DebianLike)
    -> [Desc] -> RevertableProperty DebianLike DebianLike
    forall a b. (a -> b) -> a -> b
    $ Desc -> [Desc] -> [Desc]
    apachecfg Desc
    hn [Desc]
    middle

    apachecfg :: HostName -> Apache.ConfigFile -> Apache.ConfigFile
    apachecfg :: Desc -> [Desc] -> [Desc]
    apachecfg hn :: Desc
    hn middle :: [Desc]
    middle =
    	[ "<VirtualHost *:" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Port -> Desc
    forall t. ConfigurableValue t => t -> Desc
    val Port
    port Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ ">"
    	, "  ServerAdmin grue@joeyh.name"
    	, "  ServerName "Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++Desc
    hnDesc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++":" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Port -> Desc
    forall t. ConfigurableValue t => t -> Desc
    val Port
    port
    	]
    	[Desc] -> [Desc] -> [Desc]
    forall a. [a] -> [a] -> [a]
    ++ [Desc]
    middle [Desc] -> [Desc] -> [Desc]
    forall a. [a] -> [a] -> [a]
    ++
    	[ ""
    	, "  ErrorLog /var/log/apache2/error.log"
    	, "  LogLevel warn"
    	, "  CustomLog /var/log/apache2/access.log combined"
    	, "  ServerSignature On"
    	, "  "
    	, Desc
    Apache.iconDir
    	, "</VirtualHost>"
    	]
    	  where
    		port :: Port
    port = Int -> Port
    Port 80

    gitAnnexDistributor :: Property (HasInfo + DebianLike)
    gitAnnexDistributor :: Property (HasInfo + DebianLike)
    gitAnnexDistributor = Desc
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    combineProperties "git-annex distributor, including rsync server and signer" (Props
       (MetaTypes
          '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
     -> Property (HasInfo + DebianLike))
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property (HasInfo + DebianLike)
    forall a b. (a -> b) -> a -> b
    $ Props UnixLike
    props
    	Props UnixLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & [Desc] -> Property DebianLike
    Apt.installed ["rsync"]
    	Props DebianLike
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Context -> Property (HasInfo + UnixLike)
    forall c. IsContext c => Desc -> c -> Property (HasInfo + UnixLike)
    File.hasPrivContent "/etc/rsyncd.conf" (Desc -> Context
    Context "git-annex distributor")
    		Property
      (Sing
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Property DebianLike
    -> CombinedType
         (Property
            (Sing
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
         (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Desc -> Property DebianLike
    Service.restarted "rsync"
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Context -> Property (HasInfo + UnixLike)
    forall c. IsContext c => Desc -> c -> Property (HasInfo + UnixLike)
    File.hasPrivContent "/etc/rsyncd.secrets" (Desc -> Context
    Context "git-annex distributor")
    		Property
      (Sing
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Property DebianLike
    -> CombinedType
         (Property
            (Sing
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
         (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Desc -> Property DebianLike
    Service.restarted "rsync"
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & "/etc/default/rsync" Desc -> Desc -> Property UnixLike
    `File.containsLine` "RSYNC_ENABLE=true"
    		Property UnixLike
    -> Property DebianLike
    -> CombinedType (Property UnixLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Desc -> Property DebianLike
    Service.running "rsync"
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property Linux
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property Linux
    Systemd.enabled "rsync"
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property UnixLike
    endpoint "/srv/web/downloads.kitenet.net/git-annex/autobuild"
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property UnixLike
    endpoint "/srv/web/downloads.kitenet.net/git-annex/autobuild/x86_64-apple-yosemite"
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property UnixLike
    endpoint "/srv/web/downloads.kitenet.net/git-annex/autobuild/windows"
    	-- git-annex distribution signing key
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & GpgKeyId -> User -> Property (HasInfo + DebianLike)
    Gpg.keyImported (Desc -> GpgKeyId
    Gpg.GpgKeyId "89C809CB") (Desc -> User
    User "joey")
    	-- used for building rpms
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & [Desc] -> Property DebianLike
    Apt.installed ["rpm", "createrepo-c"]
      where
    	endpoint :: Desc -> Property UnixLike
    endpoint d :: Desc
    d = Desc -> Props UnixLike -> Property UnixLike
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    combineProperties ("endpoint " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    d) (Props UnixLike -> Property UnixLike)
    -> Props UnixLike -> Property UnixLike
    forall a b. (a -> b) -> a -> b
    $ Props UnixLike
    props
    		Props UnixLike
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property UnixLike
    File.dirExists Desc
    d
    		Props UnixLike
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> User -> Group -> Property UnixLike
    File.ownerGroup Desc
    d (Desc -> User
    User "joey") (Desc -> Group
    Group "joey")

    downloads :: Property (HasInfo + DebianLike)
    downloads :: Property (HasInfo + DebianLike)
    downloads = Desc
    -> Desc
    -> Desc
    -> [(Desc, Desc)]
    -> Property (HasInfo + DebianLike)
    annexWebSite "/srv/git/downloads.git"
    	"downloads.kitenet.net"
    	"840760dc-08f0-11e2-8c61-576b7e66acfd"
    	[]

    tmp :: Property (HasInfo + DebianLike)
    tmp :: Property (HasInfo + DebianLike)
    tmp = Desc
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    propertyList "tmp.joeyh.name" (Props
       (MetaTypes
          '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
     -> Property (HasInfo + DebianLike))
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property (HasInfo + DebianLike)
    forall a b. (a -> b) -> a -> b
    $ Props UnixLike
    props
    	Props UnixLike
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc
    -> Desc
    -> Desc
    -> [(Desc, Desc)]
    -> Property (HasInfo + DebianLike)
    annexWebSite "/srv/git/joey/tmp.git"
    		"tmp.joeyh.name"
    		"26fd6e38-1226-11e2-a75f-ff007033bdba"
    		[]
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Times -> Property UnixLike
    Cron.jobDropped "pump rss" (Desc -> Times
    Cron.Times "15 * * * *")

    ircBouncer :: Property (HasInfo + DebianLike)
    ircBouncer :: Property (HasInfo + DebianLike)
    ircBouncer = Desc
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    propertyList "IRC bouncer" (Props
       (MetaTypes
          '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
     -> Property (HasInfo + DebianLike))
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property (HasInfo + DebianLike)
    forall a b. (a -> b) -> a -> b
    $ Props UnixLike
    props
    	Props UnixLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & [Desc] -> Property DebianLike
    Apt.installed ["znc"]
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & User -> Property DebianLike
    User.accountFor (Desc -> User
    User "znc")
    	Props DebianLike
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property UnixLike
    File.dirExists (Desc -> Desc
    takeDirectory Desc
    conf)
    	Props DebianLike
    -> Property
         (Sing
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Context -> Property (HasInfo + UnixLike)
    forall c. IsContext c => Desc -> c -> Property (HasInfo + UnixLike)
    File.hasPrivContent Desc
    conf Context
    anyContext
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> User -> Group -> Property UnixLike
    File.ownerGroup Desc
    conf (Desc -> User
    User "znc") (Desc -> Group
    Group "znc")
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Times -> User -> Desc -> Desc -> Property DebianLike
    Cron.job "znconboot" (Desc -> Times
    Cron.Times "@reboot") (Desc -> User
    User "znc") "~" "znc"
    	-- ensure running if it was not already
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & User -> [Desc] -> UncheckedProperty UnixLike
    userScriptProperty (Desc -> User
    User "znc") ["znc || true"]
    		UncheckedProperty UnixLike -> Result -> Property UnixLike
    forall (p :: * -> *) i.
    Checkable p i =>
    p i -> Result -> Property i
    `assume` Result
    NoChange
    		Property UnixLike -> Desc -> Property UnixLike
    forall p. IsProp p => p -> Desc -> p
    `describe` "znc running"
      where
    	conf :: Desc
    conf = "/home/znc/.znc/configs/znc.conf"

    githubBackup :: Property (HasInfo + DebianLike)
    githubBackup :: Property (HasInfo + DebianLike)
    githubBackup = Desc
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    propertyList "github-backup box" (Props
       (MetaTypes
          '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
     -> Property (HasInfo + DebianLike))
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property (HasInfo + DebianLike)
    forall a b. (a -> b) -> a -> b
    $ Props UnixLike
    props
    	Props UnixLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & [Desc] -> Property DebianLike
    Apt.installed ["github-backup", "moreutils"]
    	Props DebianLike
    -> Property
         (Sing
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Property
      (Sing
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    Property (HasInfo + UnixLike)
    githubKeys
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Times -> User -> Desc -> Desc -> Property DebianLike
    Cron.niceJob "github-backup run" (Desc -> Times
    Cron.Times "30 4 * * *") (Desc -> User
    User "joey")
    		"/home/joey/lib/backup" Desc
    backupcmd
      where
    	backupcmd :: Desc
    backupcmd = Desc -> [Desc] -> Desc
    forall a. [a] -> [[a]] -> [a]
    intercalate "&&" ([Desc] -> Desc) -> [Desc] -> Desc
    forall a b. (a -> b) -> a -> b
    $
    		[ "mkdir -p github"
    		, "cd github"
    		, ". $HOME/.github-keys"
    		, "github-backup joeyh"
    		]

    githubKeys :: Property (HasInfo + UnixLike)
    githubKeys :: Property (HasInfo + UnixLike)
    githubKeys =
    	let f :: Desc
    f = "/home/joey/.github-keys"
    	in Desc -> Context -> Property (HasInfo + UnixLike)
    forall c. IsContext c => Desc -> c -> Property (HasInfo + UnixLike)
    File.hasPrivContent Desc
    f Context
    anyContext
    		Property
      (Sing
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Property UnixLike
    -> CombinedType
         (Property
            (Sing
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
         (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Desc -> User -> Group -> Property UnixLike
    File.ownerGroup Desc
    f (Desc -> User
    User "joey") (Desc -> Group
    Group "joey")

    rsyncNetBackup :: [Host] -> Property DebianLike
    rsyncNetBackup :: [Host] -> Property DebianLike
    rsyncNetBackup hosts :: [Host]
    hosts = Desc -> Times -> User -> Desc -> Desc -> Property DebianLike
    Cron.niceJob "rsync.net copied in daily" (Desc -> Times
    Cron.Times "30 5 * * *")
    	(Desc -> User
    User "joey") "/home/joey/lib/backup" "mkdir -p rsync.net && rsync --delete -az 2318@usw-s002.rsync.net: rsync.net"
    	Property DebianLike
    -> Property UnixLike
    -> CombinedType (Property DebianLike) (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` [Host] -> Desc -> User -> Property UnixLike
    Ssh.knownHost [Host]
    hosts "usw-s002.rsync.net" (Desc -> User
    User "joey")

    podcatcher :: Property DebianLike
    podcatcher :: Property DebianLike
    podcatcher = Desc -> Times -> User -> Desc -> Desc -> Property DebianLike
    Cron.niceJob "podcatcher run hourly" (Desc -> Times
    Cron.Times "55 * * * *")
    	(Desc -> User
    User "joey") "/home/joey/lib/sound/podcasts"
    	"xargs git-annex importfeed -c annex.genmetadata=true < feeds; mr --quiet update"
    	Property DebianLike
    -> Property DebianLike
    -> CombinedType (Property DebianLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` [Desc] -> Property DebianLike
    Apt.installed ["git-annex", "myrepos"]

    spamdEnabled :: Property DebianLike
    spamdEnabled :: Property DebianLike
    spamdEnabled = Property UnixLike -> Property DebianLike
    forall (p :: * -> *) (untightened :: [MetaType])
           (tightened :: [MetaType]).
    (TightenTargets p, TightenTargetsAllowed untightened tightened,
     SingI tightened) =>
    p (MetaTypes untightened) -> p (MetaTypes tightened)
    tightenTargets (Property UnixLike -> Property DebianLike)
    -> Property UnixLike -> Property DebianLike
    forall a b. (a -> b) -> a -> b
    $
    	Desc -> [Desc] -> UncheckedProperty UnixLike
    cmdProperty "update-rc.d" ["spamassassin", "enable"]
    		UncheckedProperty UnixLike -> Result -> Property UnixLike
    forall (p :: * -> *) i.
    Checkable p i =>
    p i -> Result -> Property i
    `assume` Result
    MadeChange

    spamassassinConfigured :: Property DebianLike
    spamassassinConfigured :: Property DebianLike
    spamassassinConfigured = Desc -> Props DebianLike -> Property DebianLike
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    propertyList "spamassassin configured" (Props DebianLike -> Property DebianLike)
    -> Props DebianLike -> Property DebianLike
    forall a b. (a -> b) -> a -> b
    $ Props UnixLike
    props
    	Props UnixLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property DebianLike
    Apt.serviceInstalledRunning "spamassassin"
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & "/etc/default/spamassassin" Desc -> [Desc] -> Property UnixLike
    `File.containsLines`
    		[ "# Propellor deployed"
    		, "OPTIONS=\"--create-prefs --max-children 5 --helper-home-dir\""
    		, "CRON=1"
    		, "NICE=\"--nicelevel 15\""
    		]
    		Property UnixLike -> Desc -> Property UnixLike
    forall p. IsProp p => p -> Desc -> p
    `describe` "spamd configured"
    		Property UnixLike
    -> Property DebianLike
    -> CombinedType (Property UnixLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Property DebianLike
    spamdEnabled
    		Property DebianLike
    -> Property DebianLike
    -> CombinedType (Property DebianLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Desc -> Property DebianLike
    Service.restarted "spamassassin"
    		Property DebianLike
    -> Property DebianLike
    -> CombinedType (Property DebianLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Desc -> Property DebianLike
    Apt.serviceInstalledRunning "cron"

    kiteMailServer :: Property (HasInfo + DebianLike)
    kiteMailServer :: Property (HasInfo + DebianLike)
    kiteMailServer = Desc
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    propertyList "kitenet.net mail server" (Props
       (MetaTypes
          '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
     -> Property (HasInfo + DebianLike))
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property (HasInfo + DebianLike)
    forall a b. (a -> b) -> a -> b
    $ Props UnixLike
    props
    	Props UnixLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Property DebianLike
    Postfix.installed
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & [Desc] -> Property DebianLike
    Apt.installed ["postfix-pcre"]
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property DebianLike
    Apt.serviceInstalledRunning "postgrey"
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Property DebianLike
    spamassassinConfigured
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property DebianLike
    Apt.serviceInstalledRunning "spamass-milter"
    	-- Add -m to prevent modifying messages Subject or body.
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & "/etc/default/spamass-milter" Desc -> Desc -> Property UnixLike
    `File.containsLine`
    		"OPTIONS=\"-m -u spamass-milter -i 127.0.0.1\""
    		Property UnixLike
    -> Property DebianLike
    -> CombinedType (Property UnixLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Desc -> Property DebianLike
    Service.restarted "spamass-milter"
    		Property DebianLike -> Desc -> Property DebianLike
    forall p. IsProp p => p -> Desc -> p
    `describe` "spamass-milter configured"

    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property DebianLike
    Apt.serviceInstalledRunning "amavisd-milter"
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & "/etc/default/amavisd-milter" Desc -> [Desc] -> Property UnixLike
    `File.containsLines`
    		[ "# Propellor deployed"
    		, "MILTERSOCKET=/var/spool/postfix/amavis/amavis.sock"
    		, "MILTERSOCKETOWNER=\"postfix:postfix\""
    		, "MILTERSOCKETMODE=\"0660\""
    		]
    		Property UnixLike
    -> Property DebianLike
    -> CombinedType (Property UnixLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Desc -> Property DebianLike
    Service.restarted "amavisd-milter"
    		Property DebianLike -> Desc -> Property DebianLike
    forall p. IsProp p => p -> Desc -> p
    `describe` "amavisd-milter configured for postfix"
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property DebianLike
    Apt.serviceInstalledRunning "clamav-freshclam"
    	-- Workaround https://bugs.debian.org/569150
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Times -> User -> Desc -> Desc -> Property DebianLike
    Cron.niceJob "amavis-expire" Times
    Cron.Daily (Desc -> User
    User "root") "/"
    		"find /var/lib/amavis/virusmails/ -type f -ctime +2 -delete"

    	Props DebianLike
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Property
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    Property (HasInfo + DebianLike)
    dkimInstalled

    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Property DebianLike
    Postfix.saslAuthdInstalled
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Property DebianLike
    Fail2Ban.installed
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property DebianLike
    Fail2Ban.jailEnabled "postfix-sasl"
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & "/etc/default/saslauthd" Desc -> Desc -> Property UnixLike
    `File.containsLine` "MECHANISMS=sasldb"
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (Sing
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> User -> Property (HasInfo + UnixLike)
    Postfix.saslPasswdSet "kitenet.net" (Desc -> User
    User "errol")
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (Sing
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> User -> Property (HasInfo + UnixLike)
    Postfix.saslPasswdSet "kitenet.net" (Desc -> User
    User "joey")

    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & [Desc] -> Property DebianLike
    Apt.installed ["maildrop"]
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & "/etc/maildroprc" Desc -> [Desc] -> Property UnixLike
    `File.hasContent`
    		[ "# Global maildrop filter file (deployed with propellor)"
    		, "DEFAULT=\"$HOME/Maildir\""
    		, "MAILBOX=\"$DEFAULT/.\""
    		, "# Filter spam to a spam folder, unless .keepspam exists"
    		, "if (/^X-Spam-Status: Yes/)"
    		, "{"
    		, "  `test -e \"$HOME/.keepspam\"`"
    		, "  if ( $RETURNCODE != 0 )"
    		, "  to ${MAILBOX}spam"
    		, "}"
    		]
    		Property UnixLike -> Desc -> Property UnixLike
    forall p. IsProp p => p -> Desc -> p
    `describe` "maildrop configured"

    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (Sing
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & "/etc/aliases" Desc -> Context -> Property (HasInfo + UnixLike)
    forall c. IsContext c => Desc -> c -> Property (HasInfo + UnixLike)
    `File.hasPrivContentExposed` Context
    ctx
    		Property
      (Sing
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Property UnixLike
    -> CombinedType
         (Property
            (Sing
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
         (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Property UnixLike
    Postfix.newaliases
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (Sing
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Context -> Property (HasInfo + UnixLike)
    hasPostfixCert Context
    ctx

    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & "/etc/postfix/mydomain" Desc -> [Desc] -> Property UnixLike
    `File.containsLines`
    		[ "/.*\\.kitenet\\.net/\tOK"
    		, "/ikiwiki\\.info/\tOK"
    		, "/joeyh\\.name/\tOK"
    		]
    		Property UnixLike
    -> Property DebianLike
    -> CombinedType (Property UnixLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Property DebianLike
    Postfix.reloaded
    		Property DebianLike -> Desc -> Property DebianLike
    forall p. IsProp p => p -> Desc -> p
    `describe` "postfix mydomain file configured"
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & "/etc/postfix/obscure_client_relay.pcre" Desc -> [Desc] -> Property UnixLike
    `File.hasContent`
    		-- Remove received lines for mails relayed from trusted
    		-- clients. These can be a privacy violation, or trigger
    		-- spam filters.
    		[ "/^Received: from ([^.]+)\\.kitenet\\.net.*using TLS.*by kitenet\\.net \\(([^)]+)\\) with (E?SMTPS?A?) id ([A-F[:digit:]]+)(.*)/ IGNORE"
    		-- Munge local Received line for postfix running on a
    		-- trusted client that relays through. These can trigger
    		-- spam filters.
    		, "/^Received: by ([^.]+)\\.kitenet\\.net.*/ REPLACE X-Question: 42"
    		]
    		Property UnixLike
    -> Property DebianLike
    -> CombinedType (Property UnixLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Property DebianLike
    Postfix.reloaded
    		Property DebianLike -> Desc -> Property DebianLike
    forall p. IsProp p => p -> Desc -> p
    `describe` "postfix obscure_client_relay file configured"
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc
    -> (Desc -> Property UnixLike)
    -> CombinedType (Property UnixLike) (Property UnixLike)
    forall x.
    Combines (Property x) (Property UnixLike) =>
    Desc
    -> (Desc -> Property x)
    -> CombinedType (Property x) (Property UnixLike)
    Postfix.mappedFile "/etc/postfix/virtual"
    		((Desc -> [Desc] -> Property UnixLike)
    -> [Desc] -> Desc -> Property UnixLike
    forall a b c. (a -> b -> c) -> b -> a -> c
    flip Desc -> [Desc] -> Property UnixLike
    File.containsLines
    			[ "# *@joeyh.name to joey"
    			, "@joeyh.name\tjoey"
    			]
    		) Property UnixLike -> Desc -> Property UnixLike
    forall p. IsProp p => p -> Desc -> p
    `describe` "postfix virtual file configured"
    		Property UnixLike
    -> Property DebianLike
    -> CombinedType (Property UnixLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Property DebianLike
    Postfix.reloaded
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (Sing
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc
    -> (Desc
        -> Property
             (Sing
                '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                   'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    -> CombinedType
         (Property
            (Sing
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
         (Property UnixLike)
    forall x.
    Combines (Property x) (Property UnixLike) =>
    Desc
    -> (Desc -> Property x)
    -> CombinedType (Property x) (Property UnixLike)
    Postfix.mappedFile "/etc/postfix/relay_clientcerts"
    		((Desc
     -> Context
     -> Property
          (Sing
             '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    -> Context
    -> Desc
    -> Property
         (Sing
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall a b c. (a -> b -> c) -> b -> a -> c
    flip Desc
    -> Context
    -> Property
         (Sing
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall c. IsContext c => Desc -> c -> Property (HasInfo + UnixLike)
    File.hasPrivContentExposed Context
    ctx)
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc
    Postfix.mainCfFile Desc -> [Desc] -> Property UnixLike
    `File.containsLines`
    		[ "myhostname = kitenet.net"
    		, "mydomain = $myhostname"
    		, "append_dot_mydomain = no"
    		, "myorigin = kitenet.net"
    		, "mydestination = $myhostname, localhost.$mydomain, $mydomain, kite.$mydomain., localhost, regexp:$config_directory/mydomain"
    		, "mailbox_command = maildrop"
    		, "virtual_alias_maps = hash:/etc/postfix/virtual"

    		, "# Allow clients with trusted certs to relay mail through."
    		, "relay_clientcerts = hash:/etc/postfix/relay_clientcerts"
    		, "smtpd_relay_restrictions = permit_mynetworks,permit_tls_clientcerts,permit_sasl_authenticated,reject_unauth_destination"

    		, "# Filter out client relay lines from headers."
    		, "header_checks = pcre:$config_directory/obscure_client_relay.pcre"

    		, "# Password auth for relaying"
    		, "smtpd_sasl_auth_enable = yes"
    		, "smtpd_sasl_security_options = noanonymous"
    		, "smtpd_sasl_local_domain = kitenet.net"

    		, "# Enable postgrey and sasl auth and client certs."
    		, "smtpd_recipient_restrictions = permit_tls_clientcerts,permit_sasl_authenticated,,permit_mynetworks,reject_unauth_destination,check_policy_service inet:127.0.0.1:10023"

    		, "# Enable spamass-milter, amavis-milter (opendkim is not enabled because it causes mails forwarded from eg gmail to be rejected)"
    		, "smtpd_milters = unix:/spamass/spamass.sock unix:amavis/amavis.sock"
    		, "# opendkim is used for outgoing mail"
    		, "non_smtpd_milters = inet:localhost:8891"
    		, "milter_connect_macros = j {daemon_name} v {if_name} _"
    		, "# If a milter is broken, fall back to just accepting mail."
    		, "milter_default_action = accept"

    		, "# TLS setup -- server"
    		, "smtpd_tls_CAfile = /etc/ssl/certs/joeyca.pem"
    		, "smtpd_tls_cert_file = /etc/ssl/certs/postfix.pem"
    		, "smtpd_tls_key_file = /etc/ssl/private/postfix.pem"
    		, "smtpd_tls_loglevel = 1"
    		, "smtpd_tls_received_header = yes"
    		, "smtpd_use_tls = yes"
    		, "smtpd_tls_ask_ccert = yes"
    		, "smtpd_tls_session_cache_database = sdbm:/etc/postfix/smtpd_scache"

    		, "# TLS setup -- client"
    		, "smtp_tls_CAfile = /etc/ssl/certs/joeyca.pem"
    		, "smtp_tls_cert_file = /etc/ssl/certs/postfix.pem"
    		, "smtp_tls_key_file = /etc/ssl/private/postfix.pem"
    		, "smtp_tls_loglevel = 1"
    		, "smtp_use_tls = yes"
    		, "smtp_tls_session_cache_database = sdbm:/etc/postfix/smtp_scache"

    		, "# Allow larger attachments, up to 200 mb."
    		, "# (Avoid setting too high; the postfix queue must have"
    		, "# 1.5 times this much space free, or postfix will reject"
    		, "# ALL mail!)"
    		, "message_size_limit = 204800000"
    		, "virtual_mailbox_limit = 20480000"
    		]
    		Property UnixLike
    -> Property UnixLike
    -> CombinedType (Property UnixLike) (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Property UnixLike
    Postfix.dedupMainCf
    		Property UnixLike
    -> Property DebianLike
    -> CombinedType (Property UnixLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Property DebianLike
    Postfix.reloaded
    		Property DebianLike -> Desc -> Property DebianLike
    forall p. IsProp p => p -> Desc -> p
    `describe` "postfix configured"

    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property DebianLike
    Apt.serviceInstalledRunning "dovecot-imapd"
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property DebianLike
    Apt.serviceInstalledRunning "dovecot-pop3d"
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & "/etc/dovecot/conf.d/10-mail.conf" Desc -> Desc -> Property UnixLike
    `File.containsLine`
    		"mail_location = maildir:~/Maildir"
    		Property UnixLike
    -> Property DebianLike
    -> CombinedType (Property UnixLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Desc -> Property DebianLike
    Service.reloaded "dovecot"
    		Property DebianLike -> Desc -> Property DebianLike
    forall p. IsProp p => p -> Desc -> p
    `describe` "dovecot mail.conf"
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & "/etc/dovecot/conf.d/10-auth.conf" Desc -> Desc -> Property UnixLike
    `File.containsLine`
    		"!include auth-passwdfile.conf.ext"
    		Property UnixLike
    -> Property DebianLike
    -> CombinedType (Property UnixLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Desc -> Property DebianLike
    Service.restarted "dovecot"
    		Property DebianLike -> Desc -> Property DebianLike
    forall p. IsProp p => p -> Desc -> p
    `describe` "dovecot auth.conf"
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (Sing
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Context -> Property (HasInfo + UnixLike)
    forall c. IsContext c => Desc -> c -> Property (HasInfo + UnixLike)
    File.hasPrivContent Desc
    dovecotusers Context
    ctx
    		Property
      (Sing
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Property UnixLike
    -> CombinedType
         (Property
            (Sing
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
         (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` (Desc
    dovecotusers Desc -> FileMode -> Property UnixLike
    `File.mode`
    			[FileMode] -> FileMode
    combineModes [FileMode
    ownerReadMode, FileMode
    groupReadMode])
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> User -> Group -> Property UnixLike
    File.ownerGroup Desc
    dovecotusers (Desc -> User
    User "root") (Desc -> Group
    Group "dovecot")

    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & [Desc] -> Property DebianLike
    Apt.installed ["mutt", "bsd-mailx", "alpine"]

    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc
    pinescript Desc -> [Desc] -> Property UnixLike
    `File.hasContent`
    		[ "#!/bin/sh"
    		, "# deployed with propellor"
    		, "set -e"
    		, "exec alpine \"$@\""
    		]
    		Property UnixLike
    -> Property UnixLike
    -> CombinedType (Property UnixLike) (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` (Desc
    pinescript Desc -> FileMode -> Property UnixLike
    `File.mode`
    			[FileMode] -> FileMode
    combineModes ([FileMode]
    readModes [FileMode] -> [FileMode] -> [FileMode]
    forall a. [a] -> [a] -> [a]
    ++ [FileMode]
    executeModes))
    		Property UnixLike -> Desc -> Property UnixLike
    forall p. IsProp p => p -> Desc -> p
    `describe` "pine wrapper script"
    	-- Make pine use dovecot pipe to read maildir.
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & "/etc/pine.conf" Desc -> [Desc] -> Property UnixLike
    `File.hasContent`
    		[ "# deployed with propellor"
    		, "inbox-path={localhost}inbox"
    		, "rsh-command=" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    imapalpinescript
    		]
    		Property UnixLike -> Desc -> Property UnixLike
    forall p. IsProp p => p -> Desc -> p
    `describe` "pine configured to use local imap server"
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc
    imapalpinescript Desc -> [Desc] -> Property UnixLike
    `File.hasContent`
    		[ "#!/bin/sh"
    		, "# deployed with propellor"
    		, "set -e"
    		, "exec /usr/lib/dovecot/imap 2>/dev/null"
    		]
    		Property UnixLike
    -> Property UnixLike
    -> CombinedType (Property UnixLike) (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` (Desc
    imapalpinescript Desc -> FileMode -> Property UnixLike
    `File.mode`
    			[FileMode] -> FileMode
    combineModes ([FileMode]
    readModes [FileMode] -> [FileMode] -> [FileMode]
    forall a. [a] -> [a] -> [a]
    ++ [FileMode]
    executeModes))
    		Property UnixLike -> Desc -> Property UnixLike
    forall p. IsProp p => p -> Desc -> p
    `describe` "imap script for pine"
    	-- XXX temporarily disabled installing as it's not available in
    	-- debian unstable any longer. Need to upgrade to mailman3
    	-- at some point. (nontrivial)
    	-- & Apt.serviceInstalledRunning "mailman"
    	-- Override the default http url. (Only affects new lists.)
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & "/etc/mailman/mm_cfg.py" Desc -> Desc -> Property UnixLike
    `File.containsLine`
    		"DEFAULT_URL_PATTERN = 'https://%s/cgi-bin/mailman/'"

    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> RevertableProperty DebianLike DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Service -> RevertableProperty DebianLike DebianLike
    Postfix.service Service
    ssmtp

    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & [Desc] -> Property DebianLike
    Apt.installed ["fetchmail"]
      where
    	ctx :: Context
    ctx = Desc -> Context
    Context "kitenet.net"
    	pinescript :: Desc
    pinescript = "/usr/local/bin/pine"
    	imapalpinescript :: Desc
    imapalpinescript = "/usr/local/bin/imap-for-alpine"
    	dovecotusers :: Desc
    dovecotusers = "/etc/dovecot/users"

    	ssmtp :: Service
    ssmtp = ServiceType -> Desc -> ServiceOpts -> Service
    Postfix.Service
    		(Maybe Desc -> Desc -> ServiceType
    Postfix.InetService Maybe Desc
    forall a. Maybe a
    Nothing "ssmtp")
    		"smtpd" ServiceOpts
    Postfix.defServiceOpts

    -- Configures postfix to have the dkim milter, and no other milters.
    dkimMilter :: Property (HasInfo + DebianLike)
    dkimMilter :: Property (HasInfo + DebianLike)
    dkimMilter = Desc
    Postfix.mainCfFile Desc -> [Desc] -> Property UnixLike
    `File.containsLines`
    	[ "smtpd_milters = inet:localhost:8891"
    	, "non_smtpd_milters = inet:localhost:8891"
    	, "milter_default_action = accept"
    	]
    	Property UnixLike -> Desc -> Property UnixLike
    forall p. IsProp p => p -> Desc -> p
    `describe` "postfix dkim milter"
    	Property UnixLike
    -> Property UnixLike
    -> CombinedType (Property UnixLike) (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Property UnixLike
    Postfix.dedupMainCf
    	Property UnixLike
    -> Property DebianLike
    -> CombinedType (Property UnixLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Property DebianLike
    Postfix.reloaded
    	Property DebianLike
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> CombinedType
         (Property DebianLike)
         (Property
            (MetaTypes
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Property
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    Property (HasInfo + DebianLike)
    dkimInstalled
    	Property
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property DebianLike
    -> CombinedType
         (Property
            (MetaTypes
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
         (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Property DebianLike
    Postfix.installed

    -- This does not configure postfix to use the dkim milter,
    -- nor does it set up domainkey DNS.
    dkimInstalled :: Property (HasInfo + DebianLike)
    dkimInstalled :: Property (HasInfo + DebianLike)
    dkimInstalled = Property
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    go Property
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property DebianLike
    -> CombinedType
         (Property
            (MetaTypes
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
         (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Desc -> Property DebianLike
    Service.restarted "opendkim"
      where
    	go :: Property
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    go = Desc
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    propertyList "opendkim installed" (Props
       (MetaTypes
          '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
     -> Property
          (MetaTypes
             '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    forall a b. (a -> b) -> a -> b
    $ Props UnixLike
    props
    		Props UnixLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property DebianLike
    Apt.serviceInstalledRunning "opendkim"
    		Props DebianLike
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property UnixLike
    File.dirExists "/etc/mail"
    		Props DebianLike
    -> Property
         (Sing
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Context -> Property (HasInfo + UnixLike)
    forall c. IsContext c => Desc -> c -> Property (HasInfo + UnixLike)
    File.hasPrivContent "/etc/mail/dkim.key" (Desc -> Context
    Context "kitenet.net")
    		Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> User -> Group -> Property UnixLike
    File.ownerGroup "/etc/mail/dkim.key" (Desc -> User
    User "root") (Desc -> Group
    Group "root")
    		Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & "/etc/default/opendkim" Desc -> Desc -> Property UnixLike
    `File.containsLine`
    			"SOCKET=\"inet:8891@localhost\""
    			Property UnixLike
    -> Property UnixLike
    -> CombinedType (Property UnixLike) (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange`
    				(Desc -> [Desc] -> UncheckedProperty UnixLike
    cmdProperty "/lib/opendkim/opendkim.service.generate" []
    				UncheckedProperty UnixLike -> Result -> Property UnixLike
    forall (p :: * -> *) i.
    Checkable p i =>
    p i -> Result -> Property i
    `assume` Result
    MadeChange)
    			Property UnixLike
    -> Property DebianLike
    -> CombinedType (Property UnixLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Desc -> Property DebianLike
    Service.restarted "opendkim"
    		Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & "/etc/opendkim.conf" Desc -> [Desc] -> Property UnixLike
    `File.containsLines`
    			[ "KeyFile /etc/mail/dkim.key"
    			, "SubDomains yes"
    			, "Domain *"
    			, "Selector mail"
    			]

    -- This is the dkim public key, corresponding with /etc/mail/dkim.key
    -- This value can be included in a domain's additional records to make
    -- it use this domainkey.
    domainKey :: (BindDomain, Record)
    domainKey :: (BindDomain, Record)
    domainKey = (Desc -> BindDomain
    RelDomain "mail._domainkey", Desc -> Record
    TXT "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCc+/rfzNdt5DseBBmfB3C6sVM7FgVvf4h1FeCfyfwPpVcmPdW6M2I+NtJsbRkNbEICxiP6QY2UM0uoo9TmPqLgiCCG2vtuiG6XMsS0Y/gGwqKM7ntg/7vT1Go9vcquOFFuLa5PnzpVf8hB9+PMFdS4NPTvWL2c5xxshl/RJzICnQIDAQAB")

    postfixSaslPasswordClient :: Property (HasInfo + DebianLike)
    postfixSaslPasswordClient :: Property (HasInfo + DebianLike)
    postfixSaslPasswordClient = Desc
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    combineProperties "postfix uses SASL password to authenticate with smarthost" (Props
       (MetaTypes
          '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
     -> Property (HasInfo + DebianLike))
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property (HasInfo + DebianLike)
    forall a b. (a -> b) -> a -> b
    $ Props UnixLike
    props
    	Props UnixLike
    -> Property
         (Sing
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc
    -> (Desc
        -> Property
             (Sing
                '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                   'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    -> CombinedType
         (Property
            (Sing
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
         (Property UnixLike)
    forall x.
    Combines (Property x) (Property UnixLike) =>
    Desc
    -> (Desc -> Property x)
    -> CombinedType (Property x) (Property UnixLike)
    Postfix.mappedFile "/etc/postfix/sasl_passwd"
    		(Desc -> Context -> Property (HasInfo + UnixLike)
    forall c. IsContext c => Desc -> c -> Property (HasInfo + UnixLike)
    `File.hasPrivContent` (Desc -> Context
    Context "kitenet.net"))
    	Props
      (Sing
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc
    Postfix.mainCfFile Desc -> [Desc] -> Property UnixLike
    `File.containsLines`
    		[ "# TLS setup for SASL auth to kite"
    		, "smtp_sasl_auth_enable = yes"
    		, "smtp_tls_security_level = encrypt"
    		, "smtp_sasl_tls_security_options = noanonymous"
    		, "relayhost = [kitenet.net]"
    		, "smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd"
    		]
    		Property UnixLike
    -> Property DebianLike
    -> CombinedType (Property UnixLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Property DebianLike
    Postfix.reloaded
    	-- Comes after so it does not set relayhost but uses the setting
    	-- above.
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Property DebianLike
    Postfix.satellite

    hasPostfixCert :: Context -> Property (HasInfo + UnixLike)
    hasPostfixCert :: Context -> Property (HasInfo + UnixLike)
    hasPostfixCert ctx :: Context
    ctx = Desc
    -> Props
         (Sing
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Property
         (Sing
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    combineProperties "postfix tls cert installed" (Props
       (Sing
          '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
             'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
     -> Property (HasInfo + UnixLike))
    -> Props
         (Sing
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Property (HasInfo + UnixLike)
    forall a b. (a -> b) -> a -> b
    $ Props UnixLike
    props
    	Props UnixLike
    -> Property
         (Sing
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & "/etc/ssl/certs/postfix.pem" Desc -> Context -> Property (HasInfo + UnixLike)
    forall c. IsContext c => Desc -> c -> Property (HasInfo + UnixLike)
    `File.hasPrivContentExposed` Context
    ctx
    	Props
      (Sing
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Property
         (Sing
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & "/etc/ssl/private/postfix.pem" Desc -> Context -> Property (HasInfo + UnixLike)
    forall c. IsContext c => Desc -> c -> Property (HasInfo + UnixLike)
    `File.hasPrivContent` Context
    ctx

    -- Legacy static web sites and redirections from kitenet.net to newer
    -- sites.
    legacyWebSites :: Property (HasInfo + DebianLike)
    legacyWebSites :: Property (HasInfo + DebianLike)
    legacyWebSites = Desc
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    propertyList "legacy web sites" (Props
       (MetaTypes
          '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
     -> Property (HasInfo + DebianLike))
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property (HasInfo + DebianLike)
    forall a b. (a -> b) -> a -> b
    $ Props UnixLike
    props
    	Props UnixLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property DebianLike
    Apt.serviceInstalledRunning "apache2"
    	Props DebianLike
    -> RevertableProperty DebianLike DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> RevertableProperty DebianLike DebianLike
    Apache.modEnabled "rewrite"
    	Props DebianLike
    -> RevertableProperty DebianLike DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> RevertableProperty DebianLike DebianLike
    Apache.modEnabled "cgi"
    	Props DebianLike
    -> RevertableProperty DebianLike DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> RevertableProperty DebianLike DebianLike
    Apache.modEnabled "speling"
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Property DebianLike
    userDirHtml
    	Props DebianLike
    -> RevertableProperty DebianLike DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc
    -> Desc
    -> AgreeTOS
    -> [Desc]
    -> RevertableProperty DebianLike DebianLike
    Apache.httpsVirtualHost' "kitenet.net" "/var/www" AgreeTOS
    letos [Desc]
    kitenetcfg
    	Props DebianLike
    -> Property
         (Sing
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property (HasInfo + UnixLike)
    alias "anna.kitenet.net"
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> RevertableProperty DebianLike DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> [Desc] -> RevertableProperty DebianLike DebianLike
    apacheSite "anna.kitenet.net"
    		[ "DocumentRoot /home/anna/html"
    		, "<Directory /home/anna/html/>"
    		, "  Options Indexes ExecCGI"
    		, "  AllowOverride None"
    		, Desc
    Apache.allowAll
    		, "</Directory>"
    		]
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (Sing
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property (HasInfo + UnixLike)
    alias "sows-ear.kitenet.net"
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (Sing
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property (HasInfo + UnixLike)
    alias "www.sows-ear.kitenet.net"
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> RevertableProperty DebianLike DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> [Desc] -> RevertableProperty DebianLike DebianLike
    apacheSite "sows-ear.kitenet.net"
    		[ "ServerAlias www.sows-ear.kitenet.net"
    		, "DocumentRoot /srv/web/sows-ear.kitenet.net"
    		, "<Directory /srv/web/sows-ear.kitenet.net>"
    		, "  Options FollowSymLinks"
    		, "  AllowOverride None"
    		, Desc
    Apache.allowAll
    		, "</Directory>"
    		, "RewriteEngine On"
    		, "RewriteRule .* http://www.sowsearpoetry.org/ [L]"
    		]
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (Sing
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property (HasInfo + UnixLike)
    alias "wortroot.kitenet.net"
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (Sing
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property (HasInfo + UnixLike)
    alias "www.wortroot.kitenet.net"
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> RevertableProperty DebianLike DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> [Desc] -> RevertableProperty DebianLike DebianLike
    apacheSite "wortroot.kitenet.net"
    		[ "ServerAlias www.wortroot.kitenet.net"
    		, "DocumentRoot /srv/web/wortroot.kitenet.net"
    		, "<Directory /srv/web/wortroot.kitenet.net>"
    		, "  Options FollowSymLinks"
    		, "  AllowOverride None"
    		, Desc
    Apache.allowAll
    		, "</Directory>"
    		]
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (Sing
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property (HasInfo + UnixLike)
    alias "creeksidepress.com"
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> RevertableProperty DebianLike DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> [Desc] -> RevertableProperty DebianLike DebianLike
    apacheSite "creeksidepress.com"
    		[ "ServerAlias www.creeksidepress.com"
    		, "DocumentRoot /srv/web/www.creeksidepress.com"
    		, "<Directory /srv/web/www.creeksidepress.com>"
    		, "  Options FollowSymLinks"
    		, "  AllowOverride None"
    		, Desc
    Apache.allowAll
    		, "</Directory>"
    		]
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (Sing
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property (HasInfo + UnixLike)
    alias "joey.kitenet.net"
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> RevertableProperty DebianLike DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> [Desc] -> RevertableProperty DebianLike DebianLike
    apacheSite "joey.kitenet.net"
    		[ "DocumentRoot /var/www"
    		, "<Directory /var/www/>"
    		, "  Options Indexes ExecCGI"
    		, "  AllowOverride None"
    		, Desc
    Apache.allowAll
    		, "</Directory>"

    		, "RewriteEngine On"

    		, "# Old ikiwiki filenames for joey's wiki."
    		, "rewritecond $1 !.*/index$"
    		, "rewriterule (.+).html$ http://joeyh.name/$1/ [l]"

    		, "rewritecond $1 !.*/index$"
    		, "rewriterule (.+).rss$ http://joeyh.name/$1/index.rss [l]"

    		, "# Redirect all to joeyh.name."
    		, "rewriterule (.*) http://joeyh.name$1 [r]"
    		]
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (Sing
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property (HasInfo + UnixLike)
    alias "house.joeyh.name"
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> RevertableProperty DebianLike DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> [Desc] -> RevertableProperty DebianLike DebianLike
    apacheSite "house.joeyh.name"
    		[ "DocumentRoot /srv/web/house.joeyh.name"
    		, "<Directory /srv/web/house.joeyh.name>"
    		, "  Options Indexes ExecCGI"
    		, "  AllowOverride None"
    		, Desc
    Apache.allowAll
    		, "</Directory>"
    		]
      where
    	kitenetcfg :: [Desc]
    kitenetcfg =
    		-- /var/www is empty
    		[ "DocumentRoot /var/www"
    		, "<Directory /var/www>"
    		, "  Options Indexes FollowSymLinks MultiViews ExecCGI Includes"
    		, "  AllowOverride None"
    		, Desc
    Apache.allowAll
    		, "</Directory>"
    		, "ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/"

    		-- for mailman cgi scripts
    		, "<Directory /usr/lib/cgi-bin>"
    		, "  AllowOverride None"
    		, "  Options ExecCGI"
    		, Desc
    Apache.allowAll
    		, "</Directory>"
    		, "Alias /pipermail/ /var/lib/mailman/archives/public/"
    		, "<Directory /var/lib/mailman/archives/public/>"
    		, "  Options Indexes MultiViews FollowSymlinks"
    		, "  AllowOverride None"
    		, Desc
    Apache.allowAll
    		, "</Directory>"
    		, "Alias /images/ /usr/share/images/"
    		, "<Directory /usr/share/images/>"
    		, "  Options Indexes MultiViews"
    		, "  AllowOverride None"
    		, Desc
    Apache.allowAll
    		, "</Directory>"

    		, "RewriteEngine On"
    		, "# Force hostname to kitenet.net"
    		, "RewriteCond %{HTTP_HOST} !^kitenet\\.net [NC]"
    		, "RewriteCond %{HTTP_HOST} !^$"
    		, "RewriteRule ^/(.*) http://kitenet\\.net/$1 [L,R]"

    		, "# Moved pages"
    		, "RewriteRule /programs/debhelper http://joeyh.name/code/debhelper/ [L]"
    		, "RewriteRule /programs/satutils http://joeyh.name/code/satutils/ [L]"
    		, "RewriteRule /programs/filters http://joeyh.name/code/filters/ [L]"
    		, "RewriteRule /programs/ticker http://joeyh.name/code/ticker/ [L]"
    		, "RewriteRule /programs/pdmenu http://joeyh.name/code/pdmenu/ [L]"
    		, "RewriteRule /programs/sleepd http://joeyh.name/code/sleepd/ [L]"
    		, "RewriteRule /programs/Lingua::EN::Words2Nums http://joeyh.name/code/Words2Nums/ [L]"
    		, "RewriteRule /programs/wmbattery http://joeyh.name/code/wmbattery/ [L]"
    		, "RewriteRule /programs/dpkg-repack http://joeyh.name/code/dpkg-repack/ [L]"
    		, "RewriteRule /programs/debconf http://joeyh.name/code/debconf/ [L]"
    		, "RewriteRule /programs/perlmoo http://joeyh.name/code/perlmoo/ [L]"
    		, "RewriteRule /programs/alien http://joeyh.name/code/alien/ [L]"
    		, "RewriteRule /~joey/blog/entry/(.+)-[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9].html http://joeyh.name/blog/entry/$1/ [L]"
    		, "RewriteRule /~anna/.* http://waldeneffect\\.org/ [R]"
    		, "RewriteRule /~anna/.* http://waldeneffect\\.org/ [R]"
    		, "RewriteRule /~anna http://waldeneffect\\.org/ [R]"
    		, "RewriteRule /simpleid/ http://openid.kitenet.net:8086/simpleid/"
    		, "# Even the kite home page is not here any more!"
    		, "RewriteRule ^/$ http://www.kitenet.net/ [R]"
    		, "RewriteRule ^/index.html http://www.kitenet.net/ [R]"
    		, "RewriteRule ^/joey http://www.kitenet.net/joey/ [R]"
    		, "RewriteRule ^/joey/index.html http://www.kitenet.net/joey/ [R]"
    		, "RewriteRule ^/wifi http://www.kitenet.net/wifi/ [R]"
    		, "RewriteRule ^/wifi/index.html http://www.kitenet.net/wifi/ [R]"

    		, "# Old ikiwiki filenames for kitenet.net wiki."
    		, "rewritecond $1 !^/~"
    		, "rewritecond $1 !^/doc/"
    		, "rewritecond $1 !^/pipermail/"
    		, "rewritecond $1 !^/cgi-bin/"
    		, "rewritecond $1 !.*/index$"
    		, "rewriterule (.+).html$ $1/ [r]"

    		, "# Old ikiwiki filenames for joey's wiki."
    		, "rewritecond $1 ^/~joey/"
    		, "rewritecond $1 !.*/index$"
    		, "rewriterule (.+).html$ http://kitenet.net/$1/ [L,R]"

    		, "# ~joey to joeyh.name"
    		, "rewriterule /~joey/(.*) http://joeyh.name/$1 [L]"

    		, "# Old familywiki location."
    		, "rewriterule /~family/(.*).html http://family.kitenet.net/$1 [L]"
    		, "rewriterule /~family/(.*).rss http://family.kitenet.net/$1/index.rss [L]"
    		, "rewriterule /~family(.*) http://family.kitenet.net$1 [L]"

    		, "rewriterule /~kyle/bywayofscience(.*) http://bywayofscience.branchable.com$1 [L]"
    		, "rewriterule /~kyle/family/wiki/(.*).html http://macleawiki.branchable.com/$1 [L]"
    		, "rewriterule /~kyle/family/wiki/(.*).rss http://macleawiki.branchable.com/$1/index.rss [L]"
    		, "rewriterule /~kyle/family/wiki(.*) http://macleawiki.branchable.com$1 [L]"
    		]

    userDirHtml :: Property DebianLike
    userDirHtml :: Property DebianLike
    userDirHtml = Desc -> ([Desc] -> [Desc]) -> Desc -> Property UnixLike
    forall c.
    (FileContent c, Eq c) =>
    Desc -> (c -> c) -> Desc -> Property UnixLike
    File.fileProperty "apache userdir is html" ((Desc -> Desc) -> [Desc] -> [Desc]
    forall a b. (a -> b) -> [a] -> [b]
    map Desc -> Desc
    munge) Desc
    conf
    	Property UnixLike
    -> Property DebianLike
    -> CombinedType (Property UnixLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Property DebianLike
    Apache.reloaded
    	Property DebianLike
    -> RevertableProperty DebianLike DebianLike
    -> CombinedType
         (Property DebianLike) (RevertableProperty DebianLike DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Desc -> RevertableProperty DebianLike DebianLike
    Apache.modEnabled "userdir"
      where
    	munge :: Desc -> Desc
    munge = Desc -> Desc -> Desc -> Desc
    forall a. Eq a => [a] -> [a] -> [a] -> [a]
    replace "public_html" "html"
    	conf :: Desc
    conf = "/etc/apache2/mods-available/userdir.conf"

    -- Alarm clock: see
    -- <http://joeyh.name/blog/entry/a_programmable_alarm_clock_using_systemd/>
    --
    -- oncalendar example value: "*-*-* 7:30"
    alarmClock :: String -> User -> String -> Property Linux
    alarmClock :: Desc -> User -> Desc -> Property Linux
    alarmClock oncalendar :: Desc
    oncalendar (User user :: Desc
    user) command :: Desc
    command = Desc -> Props Linux -> Property Linux
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    combineProperties "goodmorning timer installed" (Props Linux -> Property Linux) -> Props Linux -> Property Linux
    forall a b. (a -> b) -> a -> b
    $ Props UnixLike
    props
    	Props UnixLike
    -> Property Linux
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & "/etc/systemd/system/goodmorning.timer" Desc -> [Desc] -> Property UnixLike
    `File.hasContent`
    		[ "[Unit]"
    		, "Description=good morning"
    		, ""
    		, "[Timer]"
    		, "Unit=goodmorning.service"
    		, "OnCalendar=" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    oncalendar
    		, "WakeSystem=true"
    		, "Persistent=false"
    		, ""
    		, "[Install]"
    		, "WantedBy=multi-user.target"
    		]
    		Property UnixLike
    -> Property Linux
    -> CombinedType (Property UnixLike) (Property Linux)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` (Property Linux
    Systemd.daemonReloaded
    			Property Linux
    -> Property Linux -> CombinedType (Property Linux) (Property Linux)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `before` Desc -> Property Linux
    Systemd.restarted "goodmorning.timer")
    	Props Linux
    -> Property Linux
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & "/etc/systemd/system/goodmorning.service" Desc -> [Desc] -> Property UnixLike
    `File.hasContent`
    		[ "[Unit]"
    		, "Description=good morning"
    		, "RefuseManualStart=true"
    		, "RefuseManualStop=true"
    		, "ConditionACPower=true"
    		, "StopWhenUnneeded=yes"
    		, ""
    		, "[Service]"
    		, "Type=oneshot"
    		, "ExecStart=/bin/systemd-inhibit --what=handle-lid-switch --why=goodmorning /bin/su " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    user Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ " -c \"" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    command Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ "\""
    		]
    		Property UnixLike
    -> Property Linux
    -> CombinedType (Property UnixLike) (Property Linux)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Property Linux
    Systemd.daemonReloaded
    	Props Linux
    -> Property Linux
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property Linux
    Systemd.enabled "goodmorning.timer"
    	Props Linux
    -> Property Linux
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property Linux
    Systemd.started "goodmorning.timer"
    	Props Linux
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & "/etc/systemd/logind.conf" Desc -> (Desc, Desc, Desc) -> Property UnixLike
    `ConfFile.containsIniSetting`
    		("Login", "LidSwitchIgnoreInhibited", "no")

    house :: IsContext c => User -> [Host] -> c -> (SshKeyType, Ssh.PubKeyText) -> Property (HasInfo + DebianLike)
    house :: User
    -> [Host]
    -> c
    -> (SshKeyType, Desc)
    -> Property (HasInfo + DebianLike)
    house user :: User
    user hosts :: [Host]
    hosts ctx :: c
    ctx sshkey :: (SshKeyType, Desc)
    sshkey = Desc
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    propertyList "home automation" (Props
       (MetaTypes
          '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
     -> Property (HasInfo + DebianLike))
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property (HasInfo + DebianLike)
    forall a b. (a -> b) -> a -> b
    $ Props UnixLike
    props
    	Props UnixLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Property DebianLike
    Apache.installed
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & [Desc] -> Property DebianLike
    Apt.installed ["libmodbus-dev", "rrdtool", "rsync"]
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & User -> Desc -> Desc -> Maybe Desc -> Property DebianLike
    Git.cloned User
    user "https://git.joeyh.name/git/joey/house.git" Desc
    d Maybe Desc
    forall a. Maybe a
    Nothing
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & User -> Desc -> Desc -> Maybe Desc -> Property DebianLike
    Git.cloned User
    user "https://git.joeyh.name/git/reactive-banana-automation.git" (Desc
    d Desc -> Desc -> Desc
    </> "reactive-banana-automation") Maybe Desc
    forall a. Maybe a
    Nothing
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & User -> Desc -> Desc -> Maybe Desc -> Property DebianLike
    Git.cloned User
    user "https://git.joeyh.name/git/haskell-libmodbus.git" (Desc
    d Desc -> Desc -> Desc
    </> "haskell-libmodbus") Maybe Desc
    forall a. Maybe a
    Nothing
    	Props DebianLike
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Property UnixLike
    websitesymlink
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Property DebianLike
    build
    	Props DebianLike
    -> Property Linux
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property Linux
    Systemd.enabled Desc
    setupservicename
    		Property Linux
    -> Property UnixLike
    -> CombinedType (Property Linux) (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Property UnixLike
    setupserviceinstalled
    		Property Linux
    -> Property Linux -> CombinedType (Property Linux) (Property Linux)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Desc -> Property Linux
    Systemd.started Desc
    setupservicename
    	Props DebianLike
    -> Property Linux
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property Linux
    Systemd.enabled Desc
    pollerservicename
    		Property Linux
    -> Property UnixLike
    -> CombinedType (Property Linux) (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Property UnixLike
    pollerserviceinstalled
    		Property Linux
    -> Property Linux -> CombinedType (Property Linux) (Property Linux)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Desc -> Property Linux
    Systemd.started Desc
    pollerservicename
    	Props DebianLike
    -> Property Linux
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property Linux
    Systemd.enabled Desc
    controllerservicename
    		Property Linux
    -> Property UnixLike
    -> CombinedType (Property Linux) (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Property UnixLike
    controllerserviceinstalled
    		Property Linux
    -> Property Linux -> CombinedType (Property Linux) (Property Linux)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Desc -> Property Linux
    Systemd.started Desc
    controllerservicename
    	Props DebianLike
    -> Property Linux
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property Linux
    Systemd.enabled Desc
    watchdogservicename
    		Property Linux
    -> Property UnixLike
    -> CombinedType (Property Linux) (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Property UnixLike
    watchdogserviceinstalled
    		Property Linux
    -> Property Linux -> CombinedType (Property Linux) (Property Linux)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Desc -> Property Linux
    Systemd.started Desc
    watchdogservicename
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property DebianLike
    Apt.serviceInstalledRunning "watchdog"
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & User -> Group -> Property DebianLike
    User.hasGroup User
    user (Desc -> Group
    Group "dialout")
    	Props DebianLike
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Group -> Maybe Int -> Property UnixLike
    Group.exists (Desc -> Group
    Group "gpio") Maybe Int
    forall a. Maybe a
    Nothing
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & User -> Group -> Property DebianLike
    User.hasGroup User
    user (Desc -> Group
    Group "gpio")
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & [Desc] -> Property DebianLike
    Apt.installed ["i2c-tools"]
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & User -> Group -> Property DebianLike
    User.hasGroup User
    user (Desc -> Group
    Group "i2c")
    	Props DebianLike
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & "/etc/modules-load.d/house.conf" Desc -> [Desc] -> Property UnixLike
    `File.hasContent` ["i2c-dev"]
    	Props DebianLike
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Times -> User -> Desc -> Desc -> Property DebianLike
    Cron.niceJob "house upload"
    		(Desc -> Times
    Cron.Times "1 * * * *") User
    user Desc
    d Desc
    rsynccommand
    		Property DebianLike
    -> Property
         (Sing
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> CombinedType
         (Property DebianLike)
         (Property
            (Sing
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Maybe Desc
    -> User -> c -> (SshKeyType, Desc) -> Property (HasInfo + UnixLike)
    forall c.
    IsContext c =>
    Maybe Desc
    -> User -> c -> (SshKeyType, Desc) -> Property (HasInfo + UnixLike)
    Ssh.userKeyAt (Desc -> Maybe Desc
    forall a. a -> Maybe a
    Just Desc
    sshkeyfile) User
    user c
    ctx (SshKeyType, Desc)
    sshkey
    		Property
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property UnixLike
    -> CombinedType
         (Property
            (MetaTypes
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
         (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Desc -> User -> Group -> Property UnixLike
    File.ownerGroup (Desc -> Desc
    takeDirectory Desc
    sshkeyfile)
    			User
    user (User -> Group
    userGroup User
    user)
    		Property
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property UnixLike
    -> CombinedType
         (Property
            (MetaTypes
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
         (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Desc -> Property UnixLike
    File.dirExists (Desc -> Desc
    takeDirectory Desc
    sshkeyfile)
    		Property
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property UnixLike
    -> CombinedType
         (Property
            (MetaTypes
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
         (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` [Host] -> Desc -> User -> Property UnixLike
    Ssh.knownHost [Host]
    hosts "kitenet.net" User
    user
    	Props
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (Sing
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Props
         (MetaTypes
            (Combine
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Context -> Property (HasInfo + UnixLike)
    forall c. IsContext c => Desc -> c -> Property (HasInfo + UnixLike)
    File.hasPrivContentExposed "/etc/darksky-forecast-url" Context
    anyContext
      where
    	d :: Desc
    d = "/home/joey/house"
    	sshkeyfile :: Desc
    sshkeyfile = Desc
    d Desc -> Desc -> Desc
    </> ".ssh/key"
    	build :: Property DebianLike
    build = IO Bool -> Property DebianLike -> Property DebianLike
    forall (p :: * -> *) i (m :: * -> *).
    (Checkable p i, LiftPropellor m) =>
    m Bool -> p i -> Property i
    check (Bool -> Bool
    not (Bool -> Bool) -> IO Bool -> IO Bool
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Desc -> IO Bool
    doesFileExist (Desc
    d Desc -> Desc -> Desc
    </> "controller")) (Property DebianLike -> Property DebianLike)
    -> Property DebianLike -> Property DebianLike
    forall a b. (a -> b) -> a -> b
    $
    		User -> [Desc] -> UncheckedProperty UnixLike
    userScriptProperty (Desc -> User
    User "joey")
    			[ "cd " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    d Desc -> Desc -> Desc
    </> "reactive-banana-automation"
    			, "cabal install"
    			, "cd " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    d Desc -> Desc -> Desc
    </> "haskell-libmodbus"
    			, "cabal install"
    			, "cd " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    d
    			, "make"
    			]
    		UncheckedProperty UnixLike -> Result -> Property UnixLike
    forall (p :: * -> *) i.
    Checkable p i =>
    p i -> Result -> Property i
    `assume` Result
    MadeChange
    		Property UnixLike
    -> Property DebianLike
    -> CombinedType (Property UnixLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` [Desc] -> Property DebianLike
    Apt.installed
    			[ "ghc", "cabal-install", "make"
    			, "libghc-http-types-dev"
    			, "libghc-aeson-dev"
    			, "libghc-wai-dev"
    			, "libghc-warp-dev"
    			, "libghc-http-client-dev"
    			, "libghc-http-client-tls-dev"
    			, "libghc-reactive-banana-dev"
    			, "libghc-hinotify-dev"
    			]
    	pollerservicename :: Desc
    pollerservicename = "house-poller"
    	pollerservicefile :: Desc
    pollerservicefile = "/etc/systemd/system/" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    pollerservicename Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ ".service"
    	pollerserviceinstalled :: Property UnixLike
    pollerserviceinstalled = Desc
    pollerservicefile Desc -> [Desc] -> Property UnixLike
    `File.hasContent`
    		[ "[Unit]"
    		, "Description=house poller"
    		, ""
    		, "[Service]"
    		, "ExecStart=" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    d Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ "/poller"
    		, "WorkingDirectory=" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    d
    		, "User=joey"
    		, "Group=joey"
    		, "Restart=always"
    		, ""
    		, "[Install]"
    		, "WantedBy=multi-user.target"
    		, "WantedBy=house-controller.target"
    		]
    	controllerservicename :: Desc
    controllerservicename = "house-controller"
    	controllerservicefile :: Desc
    controllerservicefile = "/etc/systemd/system/" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    controllerservicename Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ ".service"
    	controllerserviceinstalled :: Property UnixLike
    controllerserviceinstalled = Desc
    controllerservicefile Desc -> [Desc] -> Property UnixLike
    `File.hasContent`
    		[ "[Unit]"
    		, "Description=house controller"
    		, ""
    		, "[Service]"
    		, "ExecStart=" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    d Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ "/controller"
    		, "WorkingDirectory=" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    d
    		, "User=joey"
    		, "Group=joey"
    		, "Restart=always"
    		, ""
    		, "[Install]"
    		, "WantedBy=multi-user.target"
    		]
    	watchdogservicename :: Desc
    watchdogservicename = "house-watchdog"
    	watchdogservicefile :: Desc
    watchdogservicefile = "/etc/systemd/system/" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    watchdogservicename Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ ".service"
    	watchdogserviceinstalled :: Property UnixLike
    watchdogserviceinstalled = Desc
    watchdogservicefile Desc -> [Desc] -> Property UnixLike
    `File.hasContent`
    		[ "[Unit]"
    		, "Description=house watchdog"
    		, ""
    		, "[Service]"
    		, "ExecStart=" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    d Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ "/watchdog"
    		, "WorkingDirectory=" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    d
    		, "User=root"
    		, "Group=root"
    		, "Restart=always"
    		, ""
    		, "[Install]"
    		, "WantedBy=multi-user.target"
    		]
    	setupservicename :: Desc
    setupservicename = "house-setup"
    	setupservicefile :: Desc
    setupservicefile = "/etc/systemd/system/" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    setupservicename Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ ".service"
    	setupserviceinstalled :: Property UnixLike
    setupserviceinstalled = Desc
    setupservicefile Desc -> [Desc] -> Property UnixLike
    `File.hasContent`
    		[ "[Unit]"
    		, "Description=house setup"
    		, ""
    		, "[Service]"
    		, "ExecStart=" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    d Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ "/setup"
    		, "WorkingDirectory=" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    d
    		, "User=root"
    		, "Group=root"
    		, "Type=oneshot"
    		, ""
    		, "[Install]"
    		, "WantedBy=multi-user.target"
    		, "WantedBy=house-poller.target"
    		, "WantedBy=house-controller.target"
    		, "WantedBy=house-watchdog.target"
    		]
    	-- Any changes to the rsync command will need my .authorized_keys
    	-- rsync server command to be updated too.
    	rsynccommand :: Desc
    rsynccommand = "rsync -e 'ssh -i" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    sshkeyfile Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ "' -avz rrds/ joey@kitenet.net:/srv/web/house.joeyh.name/rrds/ >/dev/null 2>&1"

    	websitesymlink :: Property UnixLike
    	websitesymlink :: Property UnixLike
    websitesymlink = IO Bool -> Property UnixLike -> Property UnixLike
    forall (p :: * -> *) i (m :: * -> *).
    (Checkable p i, LiftPropellor m) =>
    m Bool -> p i -> Property i
    check (Bool -> Bool
    not (Bool -> Bool) -> (FileStatus -> Bool) -> FileStatus -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . FileStatus -> Bool
    isSymbolicLink (FileStatus -> Bool) -> IO FileStatus -> IO Bool
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Desc -> IO FileStatus
    getSymbolicLinkStatus "/var/www/html")
    		(Desc -> Propellor Result -> Property UnixLike
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc -> Propellor Result -> Property (MetaTypes metatypes)
    property "website symlink" (Propellor Result -> Property UnixLike)
    -> Propellor Result -> Property UnixLike
    forall a b. (a -> b) -> a -> b
    $ IO () -> Propellor Result
    makeChange (IO () -> Propellor Result) -> IO () -> Propellor Result
    forall a b. (a -> b) -> a -> b
    $ do
    			Desc -> IO ()
    removeDirectoryRecursive "/var/www/html"
    			Desc -> Desc -> IO ()
    createSymbolicLink Desc
    d "/var/www/html"
    		)

    homerouterWifiInterface :: String
    homerouterWifiInterface :: Desc
    homerouterWifiInterface = "wlx9cefd5fcd6f3"

    homerouterWifiInterfaceOld :: String
    homerouterWifiInterfaceOld :: Desc
    homerouterWifiInterfaceOld = "wlx7cdd90753b9f"

    -- My home router, running hostapd and dnsmasq,
    -- with eth0 connected to a satellite modem, and a fallback ppp connection.
    homeRouter :: Property (HasInfo + DebianLike)
    homeRouter :: Property (HasInfo + DebianLike)
    homeRouter = Desc
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    propertyList "home router" (Props
       (MetaTypes
          '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
     -> Property (HasInfo + DebianLike))
    -> Props
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property (HasInfo + DebianLike)
    forall a b. (a -> b) -> a -> b
    $ Props UnixLike
    props
    	Props UnixLike
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property UnixLike
    File.notPresent (Desc -> Desc
    Network.interfaceDFile Desc
    homerouterWifiInterfaceOld)
    	Props UnixLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> IPAddr -> Maybe Gateway -> Property DebianLike
    Network.static Desc
    homerouterWifiInterface (Desc -> IPAddr
    IPv4 "10.1.1.1") Maybe Gateway
    forall a. Maybe a
    Nothing
    		Property DebianLike
    -> Property DebianLike
    -> CombinedType (Property DebianLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Property DebianLike
    Network.cleanInterfacesFile
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & [Desc] -> Property DebianLike
    Apt.installed ["hostapd"]
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> [Desc] -> Property UnixLike
    File.hasContent "/etc/hostapd/hostapd.conf"
    			[ "interface=" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    homerouterWifiInterface
    			, "ssid=house"
    			, "hw_mode=g"
    			, "channel=8"
    			]
    		Property UnixLike
    -> Property UnixLike
    -> CombinedType (Property UnixLike) (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Desc -> Property UnixLike
    File.dirExists "/etc/hostapd"
    		Property UnixLike
    -> Property UnixLike
    -> CombinedType (Property UnixLike) (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Desc -> [Desc] -> Property UnixLike
    File.hasContent "/etc/default/hostapd"
    			[ "DAEMON_CONF=/etc/hostapd/hostapd.conf" ]
    		Property UnixLike
    -> Property DebianLike
    -> CombinedType (Property UnixLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Desc -> Property DebianLike
    Service.running "hostapd"
    	Props DebianLike
    -> Property Linux
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property Linux
    Systemd.enabled "hostapd"
    	Props DebianLike
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> [Desc] -> Property UnixLike
    File.hasContent "/etc/resolv.conf"
    		[ "domain kitenet.net"
    		, "search kitenet.net"
    		, "nameserver 8.8.8.8"
    		, "nameserver 8.8.4.4"
    		]
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & [Desc] -> Property DebianLike
    Apt.installed ["dnsmasq"]
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> [Desc] -> Property UnixLike
    File.hasContent "/etc/dnsmasq.conf"
    		[ "domain-needed"
    		, "bogus-priv"
    		, "interface=" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    homerouterWifiInterface
    		, "interface=eth0"
    		, "domain=kitenet.net"
    		-- lease time is short because the house
    		-- controller wants to know when clients disconnect
    		, "dhcp-range=10.1.1.100,10.1.1.150,10m"
    		, "no-hosts"
    		, "address=/honeybee.kitenet.net/10.1.1.1"
    		, "address=/house.kitenet.net/10.1.1.1"
    		, "dhcp-host=0c:98:38:80:6a:f9,10.1.1.134,android-kodama"
    		]
    		Property UnixLike
    -> Property DebianLike
    -> CombinedType (Property UnixLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Desc -> Property DebianLike
    Service.restarted "dnsmasq"
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property DebianLike
    ipmasq Desc
    homerouterWifiInterface
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc
    -> IPAddr -> Maybe Gateway -> [(Desc, Desc)] -> Property DebianLike
    Network.static' "eth0" (Desc -> IPAddr
    IPv4 "192.168.1.100")
    		(Gateway -> Maybe Gateway
    forall a. a -> Maybe a
    Just (IPAddr -> Gateway
    Network.Gateway (Desc -> IPAddr
    IPv4 "192.168.1.1")))
    		-- When satellite is down, fall back to dialup
    		[ ("pre-up", "poff -a || true")
    		, ("post-down", "pon")
    		-- ethernet autonegotiation with satellite receiver
    		-- sometimes fails
    		, ("ethernet-autoneg", "off")
    		, ("link-speed", "100")
    		, ("link-duplex", "full")
    		]
    		Property DebianLike
    -> Property DebianLike
    -> CombinedType (Property DebianLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Property DebianLike
    Network.cleanInterfacesFile
    		Property DebianLike
    -> Property DebianLike
    -> CombinedType (Property DebianLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` [Desc] -> Property DebianLike
    Apt.installed ["ethtool"]
    	Props DebianLike
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & [Desc] -> Property DebianLike
    Apt.installed ["ppp"]
    		Property DebianLike
    -> Property UnixLike
    -> CombinedType (Property DebianLike) (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `before` Desc -> [Desc] -> Property UnixLike
    File.hasContent "/etc/ppp/peers/provider"
    			[ "user \"joeyh@arczip.com\""
    			, "connect \"/usr/sbin/chat -v -f /etc/chatscripts/pap -T 3825441\""
    			, "/dev/ttyACM0"
    			, "115200"
    			, "noipdefault"
    			, "defaultroute"
    			, "persist"
    			, "noauth"
    			]
    		Property DebianLike
    -> Property
         (Sing
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> CombinedType
         (Property DebianLike)
         (Property
            (Sing
               '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall x y. Combines x y => x -> y -> CombinedType x y
    `before` Desc -> Context -> Property (HasInfo + UnixLike)
    forall c. IsContext c => Desc -> c -> Property (HasInfo + UnixLike)
    File.hasPrivContent "/etc/ppp/pap-secrets" (Desc -> Context
    Context "joeyh@arczip.com")

    -- | Enable IP masqerading, on whatever other interfaces come up, besides the
    -- provided intif.
    ipmasq :: String -> Property DebianLike
    ipmasq :: Desc -> Property DebianLike
    ipmasq intif :: Desc
    intif = Desc -> [Desc] -> Property UnixLike
    File.hasContent Desc
    ifupscript
    	[ "#!/bin/sh"
    	, "INTIF=" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    intif
    	, "if [ \"$IFACE\" = $INTIF ] || [ \"$IFACE\" = lo ]; then"
    	, "exit 0"
    	, "fi"
    	, "iptables -F"
    	, "iptables -A FORWARD -i $IFACE -o $INTIF -m state --state ESTABLISHED,RELATED -j ACCEPT"
    	, "iptables -A FORWARD -i $INTIF -o $IFACE -j ACCEPT"
    	, "iptables -t nat -A POSTROUTING -o $IFACE -j MASQUERADE"
    	, "echo 1 > /proc/sys/net/ipv4/ip_forward"
    	]
    	Property UnixLike
    -> Property UnixLike
    -> CombinedType (Property UnixLike) (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `before` Desc -> Property UnixLike
    scriptmode Desc
    ifupscript
    	Property UnixLike
    -> Property UnixLike
    -> CombinedType (Property UnixLike) (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `before` Desc -> Property UnixLike
    File.dirExists (Desc -> Desc
    takeDirectory Desc
    pppupscript)
    	Property UnixLike
    -> Property UnixLike
    -> CombinedType (Property UnixLike) (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `before` Desc -> [Desc] -> Property UnixLike
    File.hasContent Desc
    pppupscript
    		[ "#!/bin/sh"
    		, "IFACE=$PPP_IFACE " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    ifupscript
    		]
    	Property UnixLike
    -> Property UnixLike
    -> CombinedType (Property UnixLike) (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `before` Desc -> Property UnixLike
    scriptmode Desc
    pppupscript
    	Property UnixLike
    -> Property DebianLike
    -> CombinedType (Property UnixLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` [Desc] -> Property DebianLike
    Apt.installed ["iptables"]
      where
    	ifupscript :: Desc
    ifupscript = "/etc/network/if-up.d/ipmasq"
    	pppupscript :: Desc
    pppupscript = "/etc/ppp/ip-up.d/ipmasq"
    	scriptmode :: Desc -> Property UnixLike
    scriptmode f :: Desc
    f = Desc
    f Desc -> FileMode -> Property UnixLike
    `File.mode` [FileMode] -> FileMode
    combineModes ([FileMode]
    readModes [FileMode] -> [FileMode] -> [FileMode]
    forall a. [a] -> [a] -> [a]
    ++ [FileMode]
    executeModes)

    laptopSoftware :: Property DebianLike
    laptopSoftware :: Property DebianLike
    laptopSoftware = [Desc] -> Property DebianLike
    Apt.installed
    	[ "intel-microcode", "acpi"
    	, "procmeter3", "xfce4", "procmeter3", "unclutter-xfixes"
    	, "mplayer", "fbreader", "firefox", "chromium"
    	, "libdatetime-event-sunrise-perl", "libtime-duration-perl"
    	, "network-manager", "network-manager-openvpn-gnome", "openvpn"
    	, "gtk-redshift", "powertop"
    	, "gimp", "gthumb", "inkscape", "sozi", "xzgv", "hugin"
    	, "mpc", "mpd", "ncmpc", "sonata", "mpdtoys"
    	, "bsdgames", "nethack-console"
    	, "xmonad", "libghc-xmonad-dev", "libghc-xmonad-contrib-dev"
    	, "ttf-bitstream-vera", "fonts-symbola", "fonts-noto-color-emoji"
    	, "mairix", "offlineimap", "mutt", "slrn"
    	, "mtr", "nmap", "whois", "wireshark", "tcpdump", "iftop"
    	, "pmount", "tree", "pv"
    	, "arbtt", "hledger", "bc"
    	, "apache2", "ikiwiki", "libhighlight-perl"
    	, "avahi-daemon", "avahi-discover"
    	, "pal"
    	, "yeahconsole", "xkbset", "xinput"
    	, "assword", "pumpa"
    	, "vorbis-tools", "audacity"
    	, "ekiga"
    	, "bluez-firmware", "blueman", "pulseaudio-module-bluetooth"
    	, "fwupd"
    	, "xul-ext-ublock-origin", "xul-ext-pdf.js", "xul-ext-status4evar"
    	, "vim-syntastic", "vim-fugitive"
    	, "adb", "gthumb"
    	, "w3m", "sm", "weechat"
    	, "borgbackup", "wipe", "smartmontools", "libgfshare-bin"
    	, "units"
    	, "virtualbox", "virtualbox-guest-additions-iso", "qemu-kvm"
    	]
    	Property DebianLike
    -> Property DebianLike
    -> CombinedType (Property DebianLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Property DebianLike
    baseSoftware
    	Property DebianLike
    -> Property DebianLike
    -> CombinedType (Property DebianLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Property DebianLike
    devSoftware

    baseSoftware :: Property DebianLike
    baseSoftware :: Property DebianLike
    baseSoftware = [Desc] -> Property DebianLike
    Apt.installed
    	[ "bash", "bash-completion", "vim", "screen", "less", "moreutils"
    	, "git", "mr", "etckeeper", "git-annex", "ssh", "vim-vimoutliner"
    	]

    devSoftware :: Property DebianLike
    devSoftware :: Property DebianLike
    devSoftware = [Desc] -> Property DebianLike
    Apt.installed
    	[ "build-essential", "debhelper", "devscripts"
    	, "ghc", "cabal-install", "haskell-stack"
    	, "hothasktags", "hdevtools", "hlint"
    	, "gdb", "time"
    	, "dpkg-repack", "lintian"
    	, "pristine-tar", "github-backup"
    	]

    cubieTruckOneWire :: Property DebianLike
    cubieTruckOneWire :: Property DebianLike
    cubieTruckOneWire = Property UnixLike
    utilitysetup
    	Property UnixLike
    -> Property UnixLike
    -> CombinedType (Property UnixLike) (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Property UnixLike
    dtsinstalled
    	Property UnixLike
    -> Property DebianLike
    -> CombinedType (Property UnixLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Property DebianLike
    utilityinstalled
      where
    	dtsinstalled :: CombinedType (Property UnixLike) (Property UnixLike)
    dtsinstalled = Desc -> [Desc] -> Property UnixLike
    File.hasContent "/etc/easy-peasy-devicetree-squeezy/my.dts" [Desc]
    mydts
    		Property UnixLike
    -> Property UnixLike
    -> CombinedType (Property UnixLike) (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Desc -> Property UnixLike
    File.dirExists "/etc/easy-peasy-devicetree-squeezy"
    	utilityinstalled :: CombinedType (Property DebianLike) (Property DebianLike)
    utilityinstalled = User -> Desc -> Desc -> Maybe Desc -> Property DebianLike
    Git.cloned (Desc -> User
    User "root") "https://git.joeyh.name/git/easy-peasy-devicetree-squeezy.git" "/usr/local/easy-peasy-devicetree-squeezy" Maybe Desc
    forall a. Maybe a
    Nothing
    		Property DebianLike
    -> RevertableProperty UnixLike UnixLike
    -> CombinedType
         (Property DebianLike) (RevertableProperty UnixLike UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Desc -> LinkTarget -> RevertableProperty UnixLike UnixLike
    File.isSymlinkedTo "/usr/local/bin/easy-peasy-devicetree-squeezy" (Desc -> LinkTarget
    File.LinkTarget "/usr/local/easy-peasy-devicetree-squeezy/easy-peasy-devicetree-squeezy")
    		Property DebianLike
    -> Property DebianLike
    -> CombinedType (Property DebianLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` [Desc] -> Property DebianLike
    Apt.installed ["pv", "device-tree-compiler", "cpp", "linux-source"]
    	utilitysetup :: Property UnixLike
    utilitysetup = IO Bool -> Property UnixLike -> Property UnixLike
    forall (p :: * -> *) i (m :: * -> *).
    (Checkable p i, LiftPropellor m) =>
    m Bool -> p i -> Property i
    check (Bool -> Bool
    not (Bool -> Bool) -> IO Bool -> IO Bool
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Desc -> IO Bool
    doesFileExist Desc
    dtb) (Property UnixLike -> Property UnixLike)
    -> Property UnixLike -> Property UnixLike
    forall a b. (a -> b) -> a -> b
    $
    		Desc -> [Desc] -> UncheckedProperty UnixLike
    cmdProperty "easy-peasy-devicetree-squeezy"
    			["--debian", "sun7i-a20-cubietruck"]
    			UncheckedProperty UnixLike -> Result -> Property UnixLike
    forall (p :: * -> *) i.
    Checkable p i =>
    p i -> Result -> Property i
    `assume` Result
    MadeChange
    	dtb :: Desc
    dtb = "/etc/flash-kernel/dtbs/sun7i-a20-cubietruck.dtb"
    	mydts :: [Desc]
    mydts =
    		[ "/* Device tree addition enabling onewire sensors on CubieTruck GPIO pin PC21 */"
    		, "#include <dt-bindings/gpio/gpio.h>"
    		, ""
    		, "/ {"
    		, "\tonewire_device {"
    		, "\t\tcompatible = \"w1-gpio\";"
    		, "\t\tgpios = <&pio 2 21 GPIO_ACTIVE_HIGH>; /* PC21 */"
    		, "\t\tpinctrl-names = \"default\";"
    		, "\t\tpinctrl-0 = <&my_w1_pin>;"
    		, "\t};"
    		, "};"
    		, ""
    		, "&pio {"
    		, "\tmy_w1_pin: my_w1_pin@0 {"
    		, "\t\tallwinner,pins = \"PC21\";"
    		, "\t\tallwinner,function = \"gpio_in\";"
    		, "\t};"
    		, "};"
    		]

    -- My home networked attached storage server.
    homeNAS :: Property DebianLike
    homeNAS :: Property DebianLike
    homeNAS = Desc -> Props DebianLike -> Property DebianLike
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    propertyList "home NAS" (Props DebianLike -> Property DebianLike)
    -> Props DebianLike -> Property DebianLike
    forall a b. (a -> b) -> a -> b
    $ Props UnixLike
    props
    	Props UnixLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & [Desc] -> Property DebianLike
    Apt.installed ["uhubctl"]
    	Props DebianLike
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & "/etc/udev/rules.d/52-startech-hub.rules" Desc -> [Desc] -> Property UnixLike
    `File.hasContent`
    		[ "# let users power control startech hub with uhubctl"
    		, "ATTR{idVendor}==\"" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    hubvendor Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ "\", ATTR{idProduct}==\"005a\", MODE=\"0666\""
    		]
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc
    -> USBHubPort -> USBDriveId -> Maybe Desc -> Property DebianLike
    autoMountDrivePort "archive-10"
    		(Desc -> Int -> USBHubPort
    USBHubPort Desc
    hubvendor 1)
    		(Desc -> Desc -> USBDriveId
    USBDriveId Desc
    wd "1230")
    		(Desc -> Maybe Desc
    forall a. a -> Maybe a
    Just "archive-oldest")
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc
    -> USBHubPort -> USBDriveId -> Maybe Desc -> Property DebianLike
    autoMountDrivePort "archive-11"
    		(Desc -> Int -> USBHubPort
    USBHubPort Desc
    hubvendor 2)
    		(Desc -> Desc -> USBDriveId
    USBDriveId Desc
    wd "25ee")
    		(Desc -> Maybe Desc
    forall a. a -> Maybe a
    Just "archive-older")
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc
    -> USBHubPort -> USBDriveId -> Maybe Desc -> Property DebianLike
    autoMountDrivePort "archive-12"
    		(Desc -> Int -> USBHubPort
    USBHubPort Desc
    hubvendor 3)
    		(Desc -> Desc -> USBDriveId
    USBDriveId Desc
    seagate "3322")
    		(Desc -> Maybe Desc
    forall a. a -> Maybe a
    Just "archive-old")
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc
    -> USBHubPort -> USBDriveId -> Maybe Desc -> Property DebianLike
    autoMountDrivePort "archive-13"
    		(Desc -> Int -> USBHubPort
    USBHubPort Desc
    hubvendor 4)
    		(Desc -> Desc -> USBDriveId
    USBDriveId Desc
    wd "25a3")
    		(Desc -> Maybe Desc
    forall a. a -> Maybe a
    Just "archive")
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc
    -> USBHubPort -> USBDriveId -> Maybe Desc -> Property DebianLike
    autoMountDrivePort "archive-14"
    		(Desc -> Int -> USBHubPort
    USBHubPort Desc
    hubvendor 2)
    		(Desc -> Desc -> USBDriveId
    USBDriveId Desc
    wd "25a3")
    		Maybe Desc
    forall a. Maybe a
    Nothing
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Maybe Desc -> Property DebianLike
    autoMountDrive "passport" Maybe Desc
    forall a. Maybe a
    Nothing
    	Props DebianLike
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & [Desc] -> Property DebianLike
    Apt.installed ["git-annex", "borgbackup"]
      where
    	hubvendor :: Desc
    hubvendor = "0409"
    	wd :: Desc
    wd = "1058"
    	seagate :: Desc
    seagate = "0bc2"

    data USBHubPort = USBHubPort
    	{ USBHubPort -> Desc
    hubVendor :: String
    	, USBHubPort -> Int
    hubPort :: Int
    	}

    data USBDriveId = USBDriveId
    	{ USBDriveId -> Desc
    driveVendorId :: String
    	, USBDriveId -> Desc
    driveProductId :: String
    	}

    -- Makes a USB drive with the given label automount, and unmount after idle
    -- for a while.
    --
    -- The hub port is turned on and off automatically as needed, using
    -- uhubctl.
    autoMountDrivePort :: Mount.Label -> USBHubPort -> USBDriveId -> Maybe FilePath -> Property DebianLike
    autoMountDrivePort :: Desc
    -> USBHubPort -> USBDriveId -> Maybe Desc -> Property DebianLike
    autoMountDrivePort label :: Desc
    label hp :: USBHubPort
    hp drive :: USBDriveId
    drive malias :: Maybe Desc
    malias = Desc -> Props DebianLike -> Property DebianLike
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    propertyList Desc
    desc (Props DebianLike -> Property DebianLike)
    -> Props DebianLike -> Property DebianLike
    forall a b. (a -> b) -> a -> b
    $ Props UnixLike
    props
    	Props UnixLike
    -> Property Linux
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> [Desc] -> Property UnixLike
    File.hasContent ("/etc/systemd/system/" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    hub)
    		[ "[Unit]"
    		, "Description=Startech usb hub port " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Int -> Desc
    forall a. Show a => a -> Desc
    show (USBHubPort -> Int
    hubPort USBHubPort
    hp) Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ " vendor " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ USBDriveId -> Desc
    driveVendorId USBDriveId
    drive Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ " driveid " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ USBDriveId -> Desc
    driveProductId USBDriveId
    drive
    		, "PartOf=" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    mount
    		, "[Service]"
    		, "Type=oneshot"
    		, "RemainAfterExit=true"
    		, "ExecStart=/bin/sh -c 'uhubctl -a on " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    selecthubport Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ "'"
    		, "ExecStop=/bin/sh -c 'uhubctl -a off " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    selecthubport
    			-- Powering off the port does not remove device
    			-- files, so ask udev to remove the devfile; it will
    			-- be added back after the drive next spins up
    			-- and so avoid mount happening before the drive is
    			-- spun up.
    			-- (This only works when the devfile is in
    			-- by-label.)
    			Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ "; udevadm trigger --action=remove " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    devfile Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ " || true'"
    		, "[Install]"
    		, "WantedBy="
    		]
    		Property UnixLike
    -> Property Linux
    -> CombinedType (Property UnixLike) (Property Linux)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Property Linux
    Systemd.daemonReloaded
    	Props Linux
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & [Desc] -> Desc -> Maybe Desc -> Property DebianLike
    autoMountDrive'
    		[ "Requires=" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    hub
    		, "After=" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    hub
    		] Desc
    label Maybe Desc
    malias
      where
    	devfile :: Desc
    devfile = "/dev/disk/by-label/" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    label
    	mountpoint :: Desc
    mountpoint = "/media/joey/" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    label
    	desc :: Desc
    desc = "auto mount with hub port power control " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    mountpoint
    	hub :: Desc
    hub = "startech-hub-port-" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Int -> Desc
    forall a. Show a => a -> Desc
    show (USBHubPort -> Int
    hubPort USBHubPort
    hp) Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ "-vendor-" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ USBDriveId -> Desc
    driveVendorId USBDriveId
    drive Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ "-drivedid-" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ USBDriveId -> Desc
    driveProductId USBDriveId
    drive Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ ".service"
    	mount :: Desc
    mount = Desc
    svcbase Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ ".mount"
    	svcbase :: Desc
    svcbase = Desc -> Desc
    Systemd.escapePath Desc
    mountpoint
    	selecthubport :: Desc
    selecthubport = [Desc] -> Desc
    unwords
    		[ "-p", Int -> Desc
    forall a. Show a => a -> Desc
    show (USBHubPort -> Int
    hubPort USBHubPort
    hp)
    		, "-n", USBHubPort -> Desc
    hubVendor USBHubPort
    hp
    		, "-l", [Desc] -> Desc
    forall (t :: * -> *) a. Foldable t => t [a] -> [a]
    concat
    			-- The hub's location id, eg "1-1.4", does not seem
    			-- as stable as uhubctl claims it will be,
    			-- and the vendor is not sufficient since I have 2
    			-- hubs from the same vendor. So search for the
    			-- drive lsusb to find that. This works even if the
    			-- port is powered off, as long as it's been on at
    			-- some point before.
    			[ "$(lsusb -tvv | perl -lne \"if (\\\\$h && m!/sys/bus/usb/devices/(.*?) !) {\\\\$v=\\\\$1}; if (m/Hub/) { \\\\$h=1 } else { \\\\$h=0 }; if (/"
    			, USBDriveId -> Desc
    driveVendorId USBDriveId
    drive Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ ":" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ USBDriveId -> Desc
    driveProductId USBDriveId
    drive
    			Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ "/) { print \\\\$v; last}\")"
    			]
    		]

    -- Makes a USB drive with the given label automount, and unmount after idle
    -- for a while.
    autoMountDrive :: Mount.Label -> Maybe FilePath -> Property DebianLike
    autoMountDrive :: Desc -> Maybe Desc -> Property DebianLike
    autoMountDrive = [Desc] -> Desc -> Maybe Desc -> Property DebianLike
    autoMountDrive' []

    autoMountDrive' :: [String] -> Mount.Label -> Maybe FilePath -> Property DebianLike
    autoMountDrive' :: [Desc] -> Desc -> Maybe Desc -> Property DebianLike
    autoMountDrive' mountunitadd :: [Desc]
    mountunitadd label :: Desc
    label malias :: Maybe Desc
    malias = Desc -> Props DebianLike -> Property DebianLike
    forall k (metatypes :: k).
    SingI metatypes =>
    Desc
    -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    propertyList Desc
    desc (Props DebianLike -> Property DebianLike)
    -> Props DebianLike -> Property DebianLike
    forall a b. (a -> b) -> a -> b
    $ Props UnixLike
    props
    	Props UnixLike
    -> Property UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> User -> Group -> Property UnixLike
    File.ownerGroup Desc
    mountpoint (Desc -> User
    User "joey") (Desc -> Group
    Group "joey")
    		Property UnixLike
    -> Property UnixLike
    -> CombinedType (Property UnixLike) (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Desc -> Property UnixLike
    File.dirExists Desc
    mountpoint
    	Props UnixLike
    -> RevertableProperty UnixLike UnixLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & case Maybe Desc
    malias of
    		Just t :: Desc
    t -> ("/media/joey/" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    t) Desc -> LinkTarget -> RevertableProperty UnixLike UnixLike
    `File.isSymlinkedTo`
    			Desc -> LinkTarget
    File.LinkTarget Desc
    mountpoint
    		Nothing -> Property UnixLike
    forall k (t :: k). SingI t => Property (MetaTypes t)
    doNothing Property UnixLike
    -> Property UnixLike -> RevertableProperty UnixLike UnixLike
    forall setupmetatypes undometatypes.
    Property setupmetatypes
    -> Property undometatypes
    -> RevertableProperty setupmetatypes undometatypes
    <!> Property UnixLike
    forall k (t :: k). SingI t => Property (MetaTypes t)
    doNothing
    	Props UnixLike
    -> Property Linux
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> [Desc] -> Property UnixLike
    File.hasContent ("/etc/systemd/system/" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    mount)
    		([ "[Unit]"
    		, "Description=" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    label
    		] [Desc] -> [Desc] -> [Desc]
    forall a. [a] -> [a] -> [a]
    ++ [Desc]
    mountunitadd [Desc] -> [Desc] -> [Desc]
    forall a. [a] -> [a] -> [a]
    ++
    		[ "[Mount]"
    		-- avoid mounting whenever the block device is available,
    		-- only want to automount on demand
    		, "Options=noauto"
    		, "What=" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    devfile
    		, "Where=" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    mountpoint
    		, "[Install]"
    		, "WantedBy="
    		])
    		Property UnixLike
    -> Property Linux
    -> CombinedType (Property UnixLike) (Property Linux)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Property Linux
    Systemd.daemonReloaded
    	Props Linux
    -> Property Linux
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> [Desc] -> Property UnixLike
    File.hasContent ("/etc/systemd/system/" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    automount)
    		[ "[Unit]"
    		, "Description=Automount " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    label
    		, "[Automount]"
    		, "Where=" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    mountpoint
    		, "TimeoutIdleSec=300"
    		, "[Install]"
    		, "WantedBy=multi-user.target"
    		]
    		Property UnixLike
    -> Property Linux
    -> CombinedType (Property UnixLike) (Property Linux)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Property Linux
    Systemd.daemonReloaded
    	Props Linux
    -> Property Linux
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property Linux
    Systemd.enabled Desc
    automount
    	Props Linux
    -> Property Linux
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> Property Linux
    Systemd.started Desc
    automount
    	Props Linux
    -> RevertableProperty DebianLike Linux
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & Desc -> [Desc] -> RevertableProperty DebianLike Linux
    Sudo.sudoersDFile ("automount-" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    label)
    		[ "joey ALL= NOPASSWD: " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    sudocommands
    		]
      where
    	devfile :: Desc
    devfile = "/dev/disk/by-label/" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    label
    	mountpoint :: Desc
    mountpoint = "/media/joey/" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    label
    	desc :: Desc
    desc = "auto mount " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    mountpoint
    	automount :: Desc
    automount = Desc
    svcbase Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ ".automount"
    	mount :: Desc
    mount = Desc
    svcbase Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ ".mount"
    	svcbase :: Desc
    svcbase = Desc -> Desc
    Systemd.escapePath Desc
    mountpoint
    	sudocommands :: Desc
    sudocommands = Desc -> [Desc] -> Desc
    forall a. [a] -> [[a]] -> [a]
    intercalate " , " ([Desc] -> Desc) -> [Desc] -> Desc
    forall a b. (a -> b) -> a -> b
    $ (Desc -> Desc) -> [Desc] -> [Desc]
    forall a b. (a -> b) -> [a] -> [b]
    map (\c :: Desc
    c -> "/bin/systemctl " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    c)
    		[ "stop " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    mountpoint
    		, "start " Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    mountpoint
    		]

    rsyncNetBorgRepo :: String -> [Borg.BorgRepoOpt] -> Borg.BorgRepo
    rsyncNetBorgRepo :: Desc -> [BorgRepoOpt] -> BorgRepo
    rsyncNetBorgRepo d :: Desc
    d os :: [BorgRepoOpt]
    os = [BorgRepoOpt] -> Desc -> BorgRepo
    Borg.BorgRepoUsing [BorgRepoOpt]
    os' ("2318@usw-s002.rsync.net:" Desc -> Desc -> Desc
    forall a. [a] -> [a] -> [a]
    ++ Desc
    d)
      where
    	-- rsync.net has a newer borg here
    	os' :: [BorgRepoOpt]
    os' = (Desc, Desc) -> BorgRepoOpt
    Borg.UsesEnvVar ("BORG_REMOTE_PATH", "borg1") BorgRepoOpt -> [BorgRepoOpt] -> [BorgRepoOpt]
    forall a. a -> [a] -> [a]
    : [BorgRepoOpt]
    os

    noExim :: Property DebianLike
    noExim :: Property DebianLike
    noExim = [Desc] -> Property DebianLike
    Apt.removed ["exim4", "exim4-base", "exim4-daemon-light"]
    	Property DebianLike
    -> Property DebianLike
    -> CombinedType (Property DebianLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Property DebianLike
    Apt.autoRemove