diff --git a/horizon.dhall b/horizon.dhall
index 8241f9a323b49c0aff09753925cffb34c16e7e7b..3db42d96f816780b4110f2e3be7c573fe64f18c9 100644
--- a/horizon.dhall
+++ b/horizon.dhall
@@ -1,5 +1,5 @@
 let H =
-      https://gitlab.homotopic.tech/horizon/horizon-spec/-/raw/0.6/dhall/package.dhall
+      https://gitlab.homotopic.tech/horizon/dhall/horizon-spec/-/raw/0.6/dhall/package.dhall
         sha256:9a80164572526dc5350f105c8db0790fdf36634629b4cf03402ba14fd173d121
 
 let packages =
@@ -339,6 +339,7 @@ let packages =
             (None H.Subdir)
       , ekg-statsd = H.callHackage "ekg-statsd" "0.2.5.0"
       , ekg-wai = H.callHackage "ekg-wai" "0.1.1.0"
+      , ema = H.callHackage "ema" "0.10.0.0"
       , email-validate = H.callHackage "email-validate" "2.3.2.16"
       , emojis = H.callHackage "emojis" "0.1.2"
       , enclosed-exceptions = H.callHackage "enclosed-exceptions" "1.0.3"
@@ -719,6 +720,7 @@ let packages =
       , lucid-aria = H.callHackage "lucid-aria" "0.1.0.1"
       , lucid-svg = H.callHackage "lucid-svg" "0.7.1"
       , lukko = H.callHackage "lukko" "0.1.1.3"
+      , lvar = H.callHackage "lvar" "0.1.0.0"
       , lzma = H.callHackage "lzma" "0.0.0.4"
       , managed = H.callHackage "managed" "1.0.9"
       , markdown = H.callHackage "markdown" "0.1.17.5"
@@ -753,6 +755,7 @@ let packages =
       , monadlist = H.callHackage "monadlist" "0.0.2"
       , monad-control = H.callHackage "monad-control" "1.0.3.1"
       , monad-logger = H.callHackage "monad-logger" "0.3.37"
+      , monad-logger-extras = H.callHackage "monad-logger-extras" "0.1.1.1"
       , monad-loops = H.callHackage "monad-loops" "0.4.3"
       , monad-par = H.callHackage "monad-par" "0.3.5"
       , monad-par-extras = H.callHackage "monad-par-extras" "0.3.3"
@@ -1204,6 +1207,7 @@ let packages =
       , unordered-containers = H.callHackage "unordered-containers" "0.2.19.1"
       , uri-bytestring = H.callHackage "uri-bytestring" "0.3.3.1"
       , uri-encode = H.callHackage "uri-encode" "1.5.0.7"
+      , url-slug = H.callHackage "url-slug" "0.1.0.0"
       , utf8-string = H.callHackage "utf8-string" "1.0.2"
       , utility-ht = H.callHackage "utility-ht" "0.0.16"
       , uuid = H.callHackage "uuid" "1.3.15"
diff --git a/initial-packages.nix b/initial-packages.nix
index 011576db90eed1fd96451201a9177c86a9b3f79e..ffd3a68ac507ac5953aa7fb5badfaafe08ef2796 100644
--- a/initial-packages.nix
+++ b/initial-packages.nix
@@ -519,6 +519,8 @@ self: with pkgs.haskell.lib; {
 
   ekg-wai = self.callPackage (./pkgs/ekg-wai.nix) { };
 
+  ema = self.callPackage (./pkgs/ema.nix) { };
+
   email-validate = self.callPackage (./pkgs/email-validate.nix) { };
 
   emojis = self.callPackage (./pkgs/emojis.nix) { };
@@ -1129,6 +1131,8 @@ self: with pkgs.haskell.lib; {
 
   lukko = self.callPackage (./pkgs/lukko.nix) { };
 
+  lvar = self.callPackage (./pkgs/lvar.nix) { };
+
   lzma = self.callPackage (./pkgs/lzma.nix) { };
 
   managed = self.callPackage (./pkgs/managed.nix) { };
@@ -1191,6 +1195,8 @@ self: with pkgs.haskell.lib; {
 
   monad-logger = self.callPackage (./pkgs/monad-logger.nix) { };
 
+  monad-logger-extras = self.callPackage (./pkgs/monad-logger-extras.nix) { };
+
   monad-loops = self.callPackage (./pkgs/monad-loops.nix) { };
 
   monad-par = self.callPackage (./pkgs/monad-par.nix) { };
@@ -1969,6 +1975,8 @@ self: with pkgs.haskell.lib; {
 
   uri-encode = self.callPackage (./pkgs/uri-encode.nix) { };
 
+  url-slug = self.callPackage (./pkgs/url-slug.nix) { };
+
   utf8-string = self.callPackage (./pkgs/utf8-string.nix) { };
 
   utility-ht = self.callPackage (./pkgs/utility-ht.nix) { };
diff --git a/pkgs/ema.nix b/pkgs/ema.nix
new file mode 100644
index 0000000000000000000000000000000000000000..355e87f196183342caaf763a65b04b947796f2e9
--- /dev/null
+++ b/pkgs/ema.nix
@@ -0,0 +1,82 @@
+{ mkDerivation
+, aeson
+, async
+, base
+, constraints-extras
+, data-default
+, dependent-sum
+, dependent-sum-template
+, directory
+, file-embed
+, filepath
+, filepattern
+, http-types
+, lib
+, lvar
+, monad-logger
+, monad-logger-extras
+, mtl
+, neat-interpolation
+, optics-core
+, optparse-applicative
+, relude
+, sop-core
+, text
+, unliftio
+, url-slug
+, wai
+, wai-middleware-static
+, wai-websockets
+, warp
+, websockets
+}:
+mkDerivation {
+  pname = "ema";
+  version = "0.10.0.0";
+  sha256 = "69332303174f82e3317142c2456a334bf49efad992ab094dbeca7289a820d92b";
+  isLibrary = true;
+  isExecutable = false;
+  enableSeparateDataOutput = true;
+  libraryHaskellDepends = [
+    aeson
+    async
+    base
+    constraints-extras
+    data-default
+    dependent-sum
+    dependent-sum-template
+    directory
+    file-embed
+    filepath
+    filepattern
+    http-types
+    lvar
+    monad-logger
+    monad-logger-extras
+    mtl
+    neat-interpolation
+    optics-core
+    optparse-applicative
+    relude
+    sop-core
+    text
+    unliftio
+    url-slug
+    wai
+    wai-middleware-static
+    wai-websockets
+    warp
+    websockets
+  ];
+  enableLibraryProfiling = true;
+  enableExecutableProfiling = true;
+  doHaddock = false;
+  jailbreak = true;
+  doCheck = false;
+  doBenchmark = false;
+  hyperlinkSource = false;
+  homepage = "https://ema.srid.ca/";
+  description = "Static site generator library with hot reload";
+  license = lib.licenses.agpl3Only;
+  broken = false;
+}
diff --git a/pkgs/lvar.nix b/pkgs/lvar.nix
new file mode 100644
index 0000000000000000000000000000000000000000..c199338035c8aa65ed8434b208465a794e84e59a
--- /dev/null
+++ b/pkgs/lvar.nix
@@ -0,0 +1,21 @@
+{ mkDerivation, base, containers, lib, relude, stm }:
+mkDerivation {
+  pname = "lvar";
+  version = "0.1.0.0";
+  sha256 = "6437b76048cb367f063b78071038e5bbdd947901777955fa60634b6d49de94c2";
+  isLibrary = true;
+  isExecutable = false;
+  enableSeparateDataOutput = false;
+  libraryHaskellDepends = [ base containers relude stm ];
+  enableLibraryProfiling = true;
+  enableExecutableProfiling = true;
+  doHaddock = false;
+  jailbreak = true;
+  doCheck = false;
+  doBenchmark = false;
+  hyperlinkSource = false;
+  homepage = "https://github.com/srid/lvar";
+  description = "TMVar that can be listened to";
+  license = lib.licenses.bsd3;
+  broken = false;
+}
diff --git a/pkgs/monad-logger-extras.nix b/pkgs/monad-logger-extras.nix
new file mode 100644
index 0000000000000000000000000000000000000000..084f3eb0bc94a414198dbfd2277565fc554c64f4
--- /dev/null
+++ b/pkgs/monad-logger-extras.nix
@@ -0,0 +1,37 @@
+{ mkDerivation
+, ansi-terminal
+, base
+, bytestring
+, hsyslog
+, lib
+, monad-logger
+, mtl
+}:
+mkDerivation {
+  pname = "monad-logger-extras";
+  version = "0.1.1.1";
+  sha256 = "ab0c043973c5744bc88b37621301800be66bccd49e6458487a1046c3b1dabcff";
+  isLibrary = true;
+  isExecutable = true;
+  enableSeparateDataOutput = false;
+  libraryHaskellDepends = [
+    ansi-terminal
+    base
+    bytestring
+    hsyslog
+    monad-logger
+    mtl
+  ];
+  executableHaskellDepends = [ base monad-logger ];
+  enableLibraryProfiling = true;
+  enableExecutableProfiling = true;
+  doHaddock = false;
+  jailbreak = true;
+  doCheck = false;
+  doBenchmark = false;
+  hyperlinkSource = false;
+  homepage = "https://github.com/obsidiansystems/monad-logger-extras";
+  description = "Utilities for composing loggers, coloring output, plus a few orphan instances";
+  license = lib.licenses.bsd3;
+  broken = false;
+}
diff --git a/pkgs/url-slug.nix b/pkgs/url-slug.nix
new file mode 100644
index 0000000000000000000000000000000000000000..89f4da3b68227c03ece2349a6085ea37d820bd74
--- /dev/null
+++ b/pkgs/url-slug.nix
@@ -0,0 +1,35 @@
+{ mkDerivation
+, aeson
+, base
+, lib
+, relude
+, text
+, unicode-transforms
+, uri-encode
+}:
+mkDerivation {
+  pname = "url-slug";
+  version = "0.1.0.0";
+  sha256 = "f6bb5635a3d2ddf3ac511eeb25c31982cf08e41b75fb5a19b62cd0fabb9bbd74";
+  isLibrary = true;
+  isExecutable = false;
+  enableSeparateDataOutput = false;
+  libraryHaskellDepends = [
+    aeson
+    base
+    relude
+    text
+    unicode-transforms
+    uri-encode
+  ];
+  enableLibraryProfiling = true;
+  enableExecutableProfiling = true;
+  doHaddock = false;
+  jailbreak = true;
+  doCheck = false;
+  doBenchmark = false;
+  hyperlinkSource = false;
+  description = "Encoding and decoding of URL slugs";
+  license = lib.licenses.mit;
+  broken = false;
+}