diff --git a/flake.lock b/flake.lock
index 44359c4fc5bedb6005d29135ff047aa528df80d6..39426be19ab74b7155fa635344a851aa541f24e7 100644
--- a/flake.lock
+++ b/flake.lock
@@ -44,30 +44,14 @@
         "type": "github"
       }
     },
-    "horizon-gen-nix": {
-      "flake": false,
-      "locked": {
-        "lastModified": 1670944091,
-        "narHash": "sha256-WFj0uQqaEIURuZpfsb8yy/iqyzN8Lrc9BpWloL5a4R0=",
-        "rev": "8eb5ffc81cd8331f340546d746a786c7b2f021a6",
-        "revCount": 109,
-        "type": "git",
-        "url": "https://gitlab.homotopic.tech/horizon/horizon-gen-nix"
-      },
-      "original": {
-        "rev": "8eb5ffc81cd8331f340546d746a786c7b2f021a6",
-        "type": "git",
-        "url": "https://gitlab.homotopic.tech/horizon/horizon-gen-nix"
-      }
-    },
     "horizon-platform": {
       "flake": false,
       "locked": {
-        "lastModified": 1672214025,
-        "narHash": "sha256-nGVh2FVHzFT2AFzrwKiiMiIm/0moyk7XBJWS5AWPyTE=",
+        "lastModified": 1672428555,
+        "narHash": "sha256-+Aui0RtWnkWjSSbOrcg1zcnETuyD44YBj0WJ9fZsogI=",
         "ref": "refs/heads/master",
-        "rev": "4b8c268525ba482e4bfd3c181aa8b222e1221d15",
-        "revCount": 786,
+        "rev": "e187fb1aff7b3aad3cd81f11c5df6963d65b67ed",
+        "revCount": 804,
         "type": "git",
         "url": "https://gitlab.homotopic.tech/horizon/horizon-platform"
       },
@@ -113,11 +97,11 @@
     },
     "nixpkgs_2": {
       "locked": {
-        "lastModified": 1665830552,
-        "narHash": "sha256-qel2bZ9TqfW8WzWCWdjuCy4bVFhhGsEeqFv/bj1ka2s=",
+        "lastModified": 1672397839,
+        "narHash": "sha256-o+TBFFNSdOu5L1CUetXGMSinghqqlWcI2Sj9GVoJmUY=",
         "owner": "nixos",
         "repo": "nixpkgs",
-        "rev": "26b7e6ab6a864c3e7e077bcb27a49f0480b9894a",
+        "rev": "a3f612564c15e8471dd17c15efaf7ede83c57763",
         "type": "github"
       },
       "original": {
@@ -131,7 +115,6 @@
       "inputs": {
         "flake-utils": "flake-utils",
         "get-flake": "get-flake",
-        "horizon-gen-nix": "horizon-gen-nix",
         "horizon-platform": "horizon-platform",
         "lint-utils": "lint-utils",
         "nixpkgs": "nixpkgs_2"
diff --git a/flake.nix b/flake.nix
index 1eee053a8a11e32e43adebbc370e48962fe25282..aa2b982b70eda09bf2c37aea78e441eb95413cf7 100644
--- a/flake.nix
+++ b/flake.nix
@@ -2,10 +2,6 @@
   inputs = {
     get-flake.url = "github:ursi/get-flake";
     lint-utils.url = "git+https://gitlab.homotopic.tech/nix/lint-utils";
-    horizon-gen-nix = {
-      url = "git+https://gitlab.homotopic.tech/horizon/horizon-gen-nix?rev=8eb5ffc81cd8331f340546d746a786c7b2f021a6";
-      flake = false;
-    };
     horizon-platform = {
       url = "git+https://gitlab.homotopic.tech/horizon/horizon-platform";
       flake = false;
@@ -18,7 +14,6 @@
     { self
     , get-flake
     , flake-utils
-    , horizon-gen-nix
     , horizon-platform
     , lint-utils
     , nixpkgs
@@ -34,10 +29,10 @@
       with lint-utils.writers.${system};
       let
 
-        horizon-gen-nix-app = get-flake horizon-gen-nix;
-
         horizon-platform-prev = get-flake horizon-platform;
 
+        horizon-gen-nix = horizon-platform-prev.legacyPackages.${system}.horizon-gen-nix;
+
         haskellLib = pkgs.haskell.lib.compose;
 
         legacyPackages = pkgs.callPackage (nixpkgs + /pkgs/development/haskell-modules) {
@@ -84,7 +79,10 @@
 
         apps = {
 
-          horizon-gen-nix = horizon-gen-nix-app.outputs.apps.${system}.horizon-gen-nix;
+          horizon-gen-nix = {
+            type = "app";
+            program = "${horizon-gen-nix}/bin/horizon-gen-nix";
+          };
 
           horizon-gen-gitlab-ci = {
             type = "app";
diff --git a/horizon.dhall b/horizon.dhall
index 2424fa748c0c917f0dd6081512e931c5a235bac8..5e01779291958fafe6c42abeb75dd15da05a85bc 100644
--- a/horizon.dhall
+++ b/horizon.dhall
@@ -100,6 +100,7 @@ let packages =
       , autodocodec-servant-multipart =
           H.callHackage "autodocodec-servant-multipart" "0.0.0.0"
       , autodocodec-yaml = H.callHackage "autodocodec-yaml" "0.2.0.2"
+      , autoexporter = H.callHackage "autoexporter" "2.0.0.2"
       , barbies = H.callHackage "barbies" "2.0.3.1"
       , base-orphans = H.callHackage "base-orphans" "0.8.7"
       , basement = H.callHackage "basement" "0.0.15"
@@ -188,6 +189,7 @@ let packages =
       , cursor-dirforest = callGitCursorDirforest "cursor-dirforest"
       , cursor-dirforest-brick = callGitCursorDirforest "cursor-dirforest-brick"
       , cursor-dirforest-gen = callGitCursorDirforest "cursor-dirforest-gen"
+      , cursor-fuzzy-time = H.callHackage "cursor-fuzzy-time" "0.0.0.0"
       , cursor-gen = H.callHackage "cursor-gen" "0.4.0.0"
       , cursor = H.callHackage "cursor" "0.3.2.0"
       , cryptonite = H.callHackage "cryptonite" "0.30"
@@ -281,6 +283,7 @@ let packages =
       , feedback = H.callHackage "feedback" "0.1.0.1"
       , file-embed-lzma = H.callHackage "file-embed-lzma" "0.0.1"
       , file-embed = H.callHackage "file-embed" "0.0.15.0"
+      , filelock = H.callHackage "filelock" "0.1.1.5"
       , filemanip = H.callHackage "filemanip" "0.3.6.3"
       , filepattern = H.callHackage "filepattern" "0.1.3"
       , filtrable = H.callHackage "filtrable" "0.1.6.0"
@@ -303,6 +306,7 @@ let packages =
       , freer-simple = H.callHackage "freer-simple" "1.2.1.2"
       , friendly-time = H.callHackage "friendly-time" "0.4.1"
       , fsnotify = H.callHackage "fsnotify" "0.4.1.0"
+      , fuzzy-time-gen = H.callHackage "fuzzy-time-gen" "0.2.0.1"
       , fuzzy-time = H.callHackage "fuzzy-time" "0.2.0.3"
       , generically = H.callHackage "generically" "0.1"
       , generic-deriving = H.callHackage "generic-deriving" "1.14.2"
@@ -622,6 +626,7 @@ let packages =
           H.callHackage "postgresql-simple-migration" "0.1.15.0"
       , postgres-options = H.callHackage "postgres-options" "0.2.0.0"
       , pretty-hex = H.callHackage "pretty-hex" "1.1"
+      , pretty-relative-time = H.callHackage "pretty-relative-time" "0.3.0.0"
       , pretty-show = H.callHackage "pretty-show" "1.10"
       , pretty-simple = H.callHackage "pretty-simple" "4.1.1.0"
       , prettyprinter-ansi-terminal =
@@ -694,6 +699,7 @@ let packages =
       , semigroups = H.callHackage "semigroups" "0.20"
       , semigroupoids = H.callHackage "semigroupoids" "5.3.7"
       , semirings = H.callHackage "semirings" "0.6"
+      , semver = H.callHackage "semver" "0.4.0.1"
       , serialise = H.callHackage "serialise" "0.2.6.0"
       , servant = callGitServant "servant"
       , servant-blaze = H.callHackage "servant-blaze" "0.9.1"
diff --git a/initial-packages.nix b/initial-packages.nix
index 34a607ede042a77bee5729152c72ca66c7b13c70..6d3762e12684859b980f8ff586de5027a37479c6 100644
--- a/initial-packages.nix
+++ b/initial-packages.nix
@@ -111,6 +111,8 @@ self: with pkgs.haskell.lib; {
 
   autodocodec-yaml = self.callPackage (./pkgs/autodocodec-yaml.nix) { };
 
+  autoexporter = self.callPackage (./pkgs/autoexporter.nix) { };
+
   barbies = self.callPackage (./pkgs/barbies.nix) { };
 
   base-compat = self.callPackage (./pkgs/base-compat.nix) { };
@@ -285,6 +287,8 @@ self: with pkgs.haskell.lib; {
 
   cursor-dirforest-gen = self.callPackage (./pkgs/cursor-dirforest-gen.nix) { };
 
+  cursor-fuzzy-time = self.callPackage (./pkgs/cursor-fuzzy-time.nix) { };
+
   cursor-gen = self.callPackage (./pkgs/cursor-gen.nix) { };
 
   daemons = self.callPackage (./pkgs/daemons.nix) { };
@@ -409,6 +413,8 @@ self: with pkgs.haskell.lib; {
 
   file-embed-lzma = self.callPackage (./pkgs/file-embed-lzma.nix) { };
 
+  filelock = self.callPackage (./pkgs/filelock.nix) { };
+
   filemanip = self.callPackage (./pkgs/filemanip.nix) { };
 
   filepattern = self.callPackage (./pkgs/filepattern.nix) { };
@@ -447,6 +453,8 @@ self: with pkgs.haskell.lib; {
 
   fuzzy-time = self.callPackage (./pkgs/fuzzy-time.nix) { };
 
+  fuzzy-time-gen = self.callPackage (./pkgs/fuzzy-time-gen.nix) { };
+
   generic-deriving = self.callPackage (./pkgs/generic-deriving.nix) { };
 
   generic-lens-core = self.callPackage (./pkgs/generic-lens-core.nix) { };
@@ -985,6 +993,8 @@ self: with pkgs.haskell.lib; {
 
   pretty-hex = self.callPackage (./pkgs/pretty-hex.nix) { };
 
+  pretty-relative-time = self.callPackage (./pkgs/pretty-relative-time.nix) { };
+
   pretty-show = self.callPackage (./pkgs/pretty-show.nix) { };
 
   pretty-simple = self.callPackage (./pkgs/pretty-simple.nix) { };
@@ -1121,6 +1131,8 @@ self: with pkgs.haskell.lib; {
 
   semirings = self.callPackage (./pkgs/semirings.nix) { };
 
+  semver = self.callPackage (./pkgs/semver.nix) { };
+
   serialise = self.callPackage (./pkgs/serialise.nix) { };
 
   servant = self.callPackage (./pkgs/servant.nix) { };
diff --git a/pkgs/autoexporter.nix b/pkgs/autoexporter.nix
new file mode 100644
index 0000000000000000000000000000000000000000..72a6486bc90295ca7079d92adc333ee94b0314eb
--- /dev/null
+++ b/pkgs/autoexporter.nix
@@ -0,0 +1,21 @@
+{ mkDerivation, Cabal, base, directory, filepath, lib }:
+mkDerivation {
+  pname = "autoexporter";
+  version = "2.0.0.2";
+  sha256 = "f1de55da144f0bfaf1e1d9194684d61cd0a7ff825b3a7aae687631daa5a3a880";
+  isLibrary = true;
+  isExecutable = true;
+  enableSeparateDataOutput = false;
+  libraryHaskellDepends = [ base Cabal directory filepath ];
+  executableHaskellDepends = [ base Cabal directory filepath ];
+  enableLibraryProfiling = true;
+  enableExecutableProfiling = true;
+  doHaddock = false;
+  jailbreak = true;
+  doCheck = false;
+  doBenchmark = false;
+  hyperlinkSource = false;
+  description = "Automatically re-export modules";
+  license = lib.licenses.mit;
+  broken = false;
+}
diff --git a/pkgs/cursor-fuzzy-time.nix b/pkgs/cursor-fuzzy-time.nix
new file mode 100644
index 0000000000000000000000000000000000000000..19b23a9bdfaa9c83f5d6643189bdcae8d53b89ae
--- /dev/null
+++ b/pkgs/cursor-fuzzy-time.nix
@@ -0,0 +1,45 @@
+{ mkDerivation
+, base
+, containers
+, cursor
+, deepseq
+, fuzzy-time
+, lib
+, megaparsec
+, microlens
+, text
+, time
+, validity
+, validity-time
+}:
+mkDerivation {
+  pname = "cursor-fuzzy-time";
+  version = "0.0.0.0";
+  sha256 = "fa959494f95f7c54a1da1766351e8559e3ec51fc9c6b3d8f23a76429f7b5a0f7";
+  isLibrary = true;
+  isExecutable = false;
+  enableSeparateDataOutput = false;
+  libraryHaskellDepends = [
+    base
+    containers
+    cursor
+    deepseq
+    fuzzy-time
+    megaparsec
+    microlens
+    text
+    time
+    validity
+    validity-time
+  ];
+  enableLibraryProfiling = true;
+  enableExecutableProfiling = true;
+  doHaddock = false;
+  jailbreak = true;
+  doCheck = false;
+  doBenchmark = false;
+  hyperlinkSource = false;
+  homepage = "https://github.com/NorfairKing/fuzzy-time";
+  license = lib.licenses.mit;
+  broken = false;
+}
diff --git a/pkgs/filelock.nix b/pkgs/filelock.nix
new file mode 100644
index 0000000000000000000000000000000000000000..a41492ddc7032c46fbecea3201193719f8fad41c
--- /dev/null
+++ b/pkgs/filelock.nix
@@ -0,0 +1,22 @@
+{ mkDerivation, async, base, lib, process, unix }:
+mkDerivation {
+  pname = "filelock";
+  version = "0.1.1.5";
+  sha256 = "50ebea81e8443356af26f32221d4594709d94102445931673fcd94a44e244419";
+  isLibrary = true;
+  isExecutable = false;
+  enableSeparateDataOutput = false;
+  libraryHaskellDepends = [ base unix ];
+  testHaskellDepends = [ async base process ];
+  enableLibraryProfiling = true;
+  enableExecutableProfiling = true;
+  doHaddock = false;
+  jailbreak = true;
+  doCheck = false;
+  doBenchmark = false;
+  hyperlinkSource = false;
+  homepage = "http://github.com/takano-akio/filelock";
+  description = "Portable interface to file locking (flock / LockFileEx)";
+  license = lib.licenses.publicDomain;
+  broken = false;
+}
diff --git a/pkgs/fuzzy-time-gen.nix b/pkgs/fuzzy-time-gen.nix
new file mode 100644
index 0000000000000000000000000000000000000000..a98a706fb045485119a8808e9bdcbb5d51ebae04
--- /dev/null
+++ b/pkgs/fuzzy-time-gen.nix
@@ -0,0 +1,64 @@
+{ mkDerivation
+, QuickCheck
+, base
+, containers
+, criterion
+, fuzzy-time
+, genvalidity
+, genvalidity-criterion
+, genvalidity-hspec
+, genvalidity-text
+, genvalidity-time
+, hspec
+, lib
+, megaparsec
+, text
+, time
+}:
+mkDerivation {
+  pname = "fuzzy-time-gen";
+  version = "0.2.0.1";
+  sha256 = "24819f2e7ea8c2d890ac58bf0760f4f5db2aa77da10bb162a8ed4131284a1280";
+  isLibrary = true;
+  isExecutable = false;
+  enableSeparateDataOutput = false;
+  libraryHaskellDepends = [
+    base
+    containers
+    fuzzy-time
+    genvalidity
+    genvalidity-time
+    megaparsec
+    QuickCheck
+    time
+  ];
+  testHaskellDepends = [
+    base
+    fuzzy-time
+    genvalidity
+    genvalidity-hspec
+    genvalidity-text
+    genvalidity-time
+    hspec
+    megaparsec
+    QuickCheck
+    text
+    time
+  ];
+  benchmarkHaskellDepends = [
+    base
+    criterion
+    fuzzy-time
+    genvalidity-criterion
+  ];
+  enableLibraryProfiling = true;
+  enableExecutableProfiling = true;
+  doHaddock = false;
+  jailbreak = true;
+  doCheck = false;
+  doBenchmark = false;
+  hyperlinkSource = false;
+  homepage = "https://github.com/NorfairKing/fuzzy-time";
+  license = lib.licenses.mit;
+  broken = false;
+}
diff --git a/pkgs/pretty-relative-time.nix b/pkgs/pretty-relative-time.nix
new file mode 100644
index 0000000000000000000000000000000000000000..cf33fc2f71022c2d8a74c187bc0ed4012726bfc8
--- /dev/null
+++ b/pkgs/pretty-relative-time.nix
@@ -0,0 +1,43 @@
+{ mkDerivation
+, QuickCheck
+, base
+, genvalidity
+, genvalidity-hspec
+, genvalidity-time
+, hspec
+, lib
+, time
+, validity
+, validity-time
+}:
+mkDerivation {
+  pname = "pretty-relative-time";
+  version = "0.3.0.0";
+  sha256 = "709d1fd8cbb92a5af3ee1de1801e7412b322f45a419cc55103caa704d7f90deb";
+  isLibrary = true;
+  isExecutable = false;
+  enableSeparateDataOutput = false;
+  libraryHaskellDepends = [ base time validity validity-time ];
+  testHaskellDepends = [
+    base
+    genvalidity
+    genvalidity-hspec
+    genvalidity-time
+    hspec
+    QuickCheck
+    time
+    validity
+    validity-time
+  ];
+  enableLibraryProfiling = true;
+  enableExecutableProfiling = true;
+  doHaddock = false;
+  jailbreak = true;
+  doCheck = false;
+  doBenchmark = false;
+  hyperlinkSource = false;
+  homepage = "https://github.com/NorfairKing/pretty-relative-time#readme";
+  description = "Pretty relative time";
+  license = lib.licenses.mit;
+  broken = false;
+}
diff --git a/pkgs/semver.nix b/pkgs/semver.nix
new file mode 100644
index 0000000000000000000000000000000000000000..a5ef5c5b7b151d37b7f7aa6a378a303e774625c7
--- /dev/null
+++ b/pkgs/semver.nix
@@ -0,0 +1,35 @@
+{ mkDerivation
+, attoparsec
+, base
+, criterion
+, deepseq
+, hashable
+, lib
+, tasty
+, tasty-hunit
+, text
+}:
+mkDerivation {
+  pname = "semver";
+  version = "0.4.0.1";
+  sha256 = "7c47e326684efe407b2dc77924aa71c57c712465a9ed39c4097e6c1e1a1ff641";
+  revision = "1";
+  editedCabalFile = "13c692s2fbn6xygw70aglj84a8hq549gcj1p40g11j77w68p9xx4";
+  isLibrary = true;
+  isExecutable = false;
+  enableSeparateDataOutput = false;
+  libraryHaskellDepends = [ attoparsec base deepseq hashable text ];
+  testHaskellDepends = [ base tasty tasty-hunit text ];
+  benchmarkHaskellDepends = [ base criterion text ];
+  enableLibraryProfiling = true;
+  enableExecutableProfiling = true;
+  doHaddock = false;
+  jailbreak = true;
+  doCheck = false;
+  doBenchmark = false;
+  hyperlinkSource = false;
+  homepage = "https://github.com/brendanhay/semver";
+  description = "Representation, manipulation, and de/serialisation of Semantic Versions";
+  license = lib.licenses.mpl20;
+  broken = false;
+}
diff --git a/shell/ShellRC.hs b/shell/ShellRC.hs
index 1af79fa98c6dbd29dfd4a6b3f013092419e27ea6..71a04898fab72eb9aebd675e31dd819c048ce97f 100644
--- a/shell/ShellRC.hs
+++ b/shell/ShellRC.hs
@@ -18,6 +18,7 @@ import qualified Data.Text                 as T
 import qualified Data.Text.Encoding        as T
 import qualified Data.Yaml                 as Y
 import qualified Data.Yaml.Pretty          as Y
+import           Horizon.Spec
 import           Network.HTTP.Simple
 import           Path
 import           Procex.Prelude
diff --git a/shell/default.nix b/shell/default.nix
index 56ae06ea9bc5ccb6aa9ad661dd9161f494865c2d..82d9f15784076ee0c2f56b0d1af9ada82ec7d46e 100644
--- a/shell/default.nix
+++ b/shell/default.nix
@@ -13,6 +13,8 @@ let
     containers
     dhall
     http-conduit
+    horizon-gen-nix
+    horizon-spec
     lens
     lens-aeson
     path