diff --git a/cabal.project b/cabal.project
deleted file mode 100644
index b216db5dffaa26749cae2f6bce4530a0d9e41498..0000000000000000000000000000000000000000
--- a/cabal.project
+++ /dev/null
@@ -1,10 +0,0 @@
-packages: .
-
-tests: True
-
-package zlib
-        -- Turn on discovery of the underlying zlib using pkg-config.  This
-        -- fixes build failures when the underlying zlib is not in the
-        -- traditional location but is discoverable with pkg-config.  It might
-        -- break non-pkg-config platforms.
-        flags: +pkg-config
diff --git a/flake.lock b/flake.lock
index 16274dd6ca881162b90299c04d227defe60d2e14..7b6553f8889506b75c0820f2f318e7379327d637 100644
--- a/flake.lock
+++ b/flake.lock
@@ -16,6 +16,70 @@
         "type": "github"
       }
     },
+    "flake-compat_2": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1673956053,
+        "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
+        "owner": "edolstra",
+        "repo": "flake-compat",
+        "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
+        "type": "github"
+      },
+      "original": {
+        "owner": "edolstra",
+        "repo": "flake-compat",
+        "type": "github"
+      }
+    },
+    "flake-compat_3": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1673956053,
+        "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
+        "owner": "edolstra",
+        "repo": "flake-compat",
+        "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
+        "type": "github"
+      },
+      "original": {
+        "owner": "edolstra",
+        "repo": "flake-compat",
+        "type": "github"
+      }
+    },
+    "flake-compat_4": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1673956053,
+        "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
+        "owner": "edolstra",
+        "repo": "flake-compat",
+        "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
+        "type": "github"
+      },
+      "original": {
+        "owner": "edolstra",
+        "repo": "flake-compat",
+        "type": "github"
+      }
+    },
+    "flake-compat_5": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1673956053,
+        "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
+        "owner": "edolstra",
+        "repo": "flake-compat",
+        "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
+        "type": "github"
+      },
+      "original": {
+        "owner": "edolstra",
+        "repo": "flake-compat",
+        "type": "github"
+      }
+    },
     "flake-utils": {
       "inputs": {
         "systems": "systems"
@@ -29,70 +93,498 @@
         "type": "github"
       },
       "original": {
-        "owner": "numtide",
-        "repo": "flake-utils",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
+    },
+    "flake-utils_10": {
+      "locked": {
+        "lastModified": 1676283394,
+        "narHash": "sha256-XX2f9c3iySLCw54rJ/CZs+ZK6IQy7GXNY4nSOyu2QG4=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "3db36a8b464d0c4532ba1c7dda728f4576d6d073",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
+    },
+    "flake-utils_2": {
+      "locked": {
+        "lastModified": 1676283394,
+        "narHash": "sha256-XX2f9c3iySLCw54rJ/CZs+ZK6IQy7GXNY4nSOyu2QG4=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "3db36a8b464d0c4532ba1c7dda728f4576d6d073",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
+    },
+    "flake-utils_3": {
+      "locked": {
+        "lastModified": 1667395993,
+        "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
+    },
+    "flake-utils_4": {
+      "locked": {
+        "lastModified": 1676283394,
+        "narHash": "sha256-XX2f9c3iySLCw54rJ/CZs+ZK6IQy7GXNY4nSOyu2QG4=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "3db36a8b464d0c4532ba1c7dda728f4576d6d073",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
+    },
+    "flake-utils_5": {
+      "locked": {
+        "lastModified": 1667395993,
+        "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
+    },
+    "flake-utils_6": {
+      "locked": {
+        "lastModified": 1676283394,
+        "narHash": "sha256-XX2f9c3iySLCw54rJ/CZs+ZK6IQy7GXNY4nSOyu2QG4=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "3db36a8b464d0c4532ba1c7dda728f4576d6d073",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
+    },
+    "flake-utils_7": {
+      "inputs": {
+        "systems": "systems_2"
+      },
+      "locked": {
+        "lastModified": 1681202837,
+        "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "cfacdce06f30d2b68473a46042957675eebb3401",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
+    },
+    "flake-utils_8": {
+      "locked": {
+        "lastModified": 1676283394,
+        "narHash": "sha256-XX2f9c3iySLCw54rJ/CZs+ZK6IQy7GXNY4nSOyu2QG4=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "3db36a8b464d0c4532ba1c7dda728f4576d6d073",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
+    },
+    "flake-utils_9": {
+      "locked": {
+        "lastModified": 1667395993,
+        "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
+    },
+    "gitignore": {
+      "inputs": {
+        "nixpkgs": [
+          "hs-flake-utils",
+          "pre-commit-hooks",
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1660459072,
+        "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=",
+        "owner": "hercules-ci",
+        "repo": "gitignore.nix",
+        "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73",
+        "type": "github"
+      },
+      "original": {
+        "owner": "hercules-ci",
+        "repo": "gitignore.nix",
+        "type": "github"
+      }
+    },
+    "gitignore_2": {
+      "inputs": {
+        "nixpkgs": [
+          "tahoe-capabilities",
+          "hs-flake-utils",
+          "pre-commit-hooks",
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1660459072,
+        "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=",
+        "owner": "hercules-ci",
+        "repo": "gitignore.nix",
+        "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73",
+        "type": "github"
+      },
+      "original": {
+        "owner": "hercules-ci",
+        "repo": "gitignore.nix",
+        "type": "github"
+      }
+    },
+    "gitignore_3": {
+      "inputs": {
+        "nixpkgs": [
+          "tahoe-chk",
+          "hs-flake-utils",
+          "pre-commit-hooks",
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1660459072,
+        "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=",
+        "owner": "hercules-ci",
+        "repo": "gitignore.nix",
+        "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73",
+        "type": "github"
+      },
+      "original": {
+        "owner": "hercules-ci",
+        "repo": "gitignore.nix",
+        "type": "github"
+      }
+    },
+    "gitignore_4": {
+      "inputs": {
+        "nixpkgs": [
+          "tahoe-ssk",
+          "hs-flake-utils",
+          "pre-commit-hooks",
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1660459072,
+        "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=",
+        "owner": "hercules-ci",
+        "repo": "gitignore.nix",
+        "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73",
+        "type": "github"
+      },
+      "original": {
+        "owner": "hercules-ci",
+        "repo": "gitignore.nix",
+        "type": "github"
+      }
+    },
+    "gitignore_5": {
+      "inputs": {
+        "nixpkgs": [
+          "tahoe-ssk",
+          "tahoe-capabilities",
+          "hs-flake-utils",
+          "pre-commit-hooks",
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1660459072,
+        "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=",
+        "owner": "hercules-ci",
+        "repo": "gitignore.nix",
+        "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73",
+        "type": "github"
+      },
+      "original": {
+        "owner": "hercules-ci",
+        "repo": "gitignore.nix",
+        "type": "github"
+      }
+    },
+    "hs-flake-utils": {
+      "inputs": {
+        "flake-utils": "flake-utils_2",
+        "nixpkgs": "nixpkgs",
+        "pre-commit-hooks": "pre-commit-hooks"
+      },
+      "locked": {
+        "lastModified": 1686149618,
+        "narHash": "sha256-/IHWacNutk3tklouWOgClp7FnOH+Yk6If13kKJMSEi8=",
+        "ref": "main",
+        "rev": "49696f704eb965f57d729871c6d450ac0aff2b68",
+        "revCount": 12,
+        "type": "git",
+        "url": "https://whetstone.private.storage/jcalderone/hs-flake-utils.git"
+      },
+      "original": {
+        "ref": "main",
+        "type": "git",
+        "url": "https://whetstone.private.storage/jcalderone/hs-flake-utils.git"
+      }
+    },
+    "hs-flake-utils_2": {
+      "inputs": {
+        "flake-utils": "flake-utils_4",
+        "nixpkgs": "nixpkgs_2",
+        "pre-commit-hooks": "pre-commit-hooks_2"
+      },
+      "locked": {
+        "lastModified": 1681762240,
+        "narHash": "sha256-+PLx9xHBvV70dA7Gy/+YTH1w3PcSOrGV0z0rGxts8jU=",
+        "ref": "main",
+        "rev": "a51e591b7fdf8881ac0237452691df7b1aceecd3",
+        "revCount": 10,
+        "type": "git",
+        "url": "https://whetstone.private.storage/jcalderone/hs-flake-utils.git"
+      },
+      "original": {
+        "ref": "main",
+        "type": "git",
+        "url": "https://whetstone.private.storage/jcalderone/hs-flake-utils.git"
+      }
+    },
+    "hs-flake-utils_3": {
+      "inputs": {
+        "flake-utils": "flake-utils_6",
+        "nixpkgs": "nixpkgs_3",
+        "pre-commit-hooks": "pre-commit-hooks_3"
+      },
+      "locked": {
+        "lastModified": 1677773826,
+        "narHash": "sha256-xJmOtHugr4k2zNhP/AF6JdIUnIEyM+TEspLn2n5kloc=",
+        "ref": "main",
+        "rev": "d3a83fdd9563546ca41771186427638e685a2e2b",
+        "revCount": 9,
+        "type": "git",
+        "url": "https://whetstone.private.storage/jcalderone/hs-flake-utils.git"
+      },
+      "original": {
+        "ref": "main",
+        "type": "git",
+        "url": "https://whetstone.private.storage/jcalderone/hs-flake-utils.git"
+      }
+    },
+    "hs-flake-utils_4": {
+      "inputs": {
+        "flake-utils": "flake-utils_8",
+        "nixpkgs": "nixpkgs_4",
+        "pre-commit-hooks": "pre-commit-hooks_4"
+      },
+      "locked": {
+        "lastModified": 1681762240,
+        "narHash": "sha256-+PLx9xHBvV70dA7Gy/+YTH1w3PcSOrGV0z0rGxts8jU=",
+        "ref": "main",
+        "rev": "a51e591b7fdf8881ac0237452691df7b1aceecd3",
+        "revCount": 10,
+        "type": "git",
+        "url": "https://whetstone.private.storage/jcalderone/hs-flake-utils.git"
+      },
+      "original": {
+        "ref": "main",
+        "type": "git",
+        "url": "https://whetstone.private.storage/jcalderone/hs-flake-utils.git"
+      }
+    },
+    "hs-flake-utils_5": {
+      "inputs": {
+        "flake-utils": "flake-utils_10",
+        "nixpkgs": "nixpkgs_5",
+        "pre-commit-hooks": "pre-commit-hooks_5"
+      },
+      "locked": {
+        "lastModified": 1681762240,
+        "narHash": "sha256-+PLx9xHBvV70dA7Gy/+YTH1w3PcSOrGV0z0rGxts8jU=",
+        "ref": "main",
+        "rev": "a51e591b7fdf8881ac0237452691df7b1aceecd3",
+        "revCount": 10,
+        "type": "git",
+        "url": "https://whetstone.private.storage/jcalderone/hs-flake-utils.git"
+      },
+      "original": {
+        "ref": "main",
+        "type": "git",
+        "url": "https://whetstone.private.storage/jcalderone/hs-flake-utils.git"
+      }
+    },
+    "nixpkgs": {
+      "locked": {
+        "lastModified": 1677624842,
+        "narHash": "sha256-4DF9DbDuK4/+KYx0L6XcPBeDHUFVCtzok2fWtwXtb5w=",
+        "owner": "nixos",
+        "repo": "nixpkgs",
+        "rev": "d70f5cd5c3bef45f7f52698f39e7cc7a89daa7f0",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nixos",
+        "ref": "nixos-22.11",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "nixpkgs-stable": {
+      "locked": {
+        "lastModified": 1673800717,
+        "narHash": "sha256-SFHraUqLSu5cC6IxTprex/nTsI81ZQAtDvlBvGDWfnA=",
+        "owner": "NixOS",
+        "repo": "nixpkgs",
+        "rev": "2f9fd351ec37f5d479556cd48be4ca340da59b8f",
+        "type": "github"
+      },
+      "original": {
+        "owner": "NixOS",
+        "ref": "nixos-22.11",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "nixpkgs-stable_2": {
+      "locked": {
+        "lastModified": 1673800717,
+        "narHash": "sha256-SFHraUqLSu5cC6IxTprex/nTsI81ZQAtDvlBvGDWfnA=",
+        "owner": "NixOS",
+        "repo": "nixpkgs",
+        "rev": "2f9fd351ec37f5d479556cd48be4ca340da59b8f",
+        "type": "github"
+      },
+      "original": {
+        "owner": "NixOS",
+        "ref": "nixos-22.11",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "nixpkgs-stable_3": {
+      "locked": {
+        "lastModified": 1673800717,
+        "narHash": "sha256-SFHraUqLSu5cC6IxTprex/nTsI81ZQAtDvlBvGDWfnA=",
+        "owner": "NixOS",
+        "repo": "nixpkgs",
+        "rev": "2f9fd351ec37f5d479556cd48be4ca340da59b8f",
+        "type": "github"
+      },
+      "original": {
+        "owner": "NixOS",
+        "ref": "nixos-22.11",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "nixpkgs-stable_4": {
+      "locked": {
+        "lastModified": 1673800717,
+        "narHash": "sha256-SFHraUqLSu5cC6IxTprex/nTsI81ZQAtDvlBvGDWfnA=",
+        "owner": "NixOS",
+        "repo": "nixpkgs",
+        "rev": "2f9fd351ec37f5d479556cd48be4ca340da59b8f",
+        "type": "github"
+      },
+      "original": {
+        "owner": "NixOS",
+        "ref": "nixos-22.11",
+        "repo": "nixpkgs",
         "type": "github"
       }
     },
-    "flake-utils_2": {
+    "nixpkgs-stable_5": {
       "locked": {
-        "lastModified": 1676283394,
-        "narHash": "sha256-XX2f9c3iySLCw54rJ/CZs+ZK6IQy7GXNY4nSOyu2QG4=",
-        "owner": "numtide",
-        "repo": "flake-utils",
-        "rev": "3db36a8b464d0c4532ba1c7dda728f4576d6d073",
+        "lastModified": 1673800717,
+        "narHash": "sha256-SFHraUqLSu5cC6IxTprex/nTsI81ZQAtDvlBvGDWfnA=",
+        "owner": "NixOS",
+        "repo": "nixpkgs",
+        "rev": "2f9fd351ec37f5d479556cd48be4ca340da59b8f",
         "type": "github"
       },
       "original": {
-        "owner": "numtide",
-        "repo": "flake-utils",
+        "owner": "NixOS",
+        "ref": "nixos-22.11",
+        "repo": "nixpkgs",
         "type": "github"
       }
     },
-    "gitignore": {
-      "inputs": {
-        "nixpkgs": [
-          "hs-flake-utils",
-          "pre-commit-hooks",
-          "nixpkgs"
-        ]
-      },
+    "nixpkgs_2": {
       "locked": {
-        "lastModified": 1660459072,
-        "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=",
-        "owner": "hercules-ci",
-        "repo": "gitignore.nix",
-        "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73",
+        "lastModified": 1677624842,
+        "narHash": "sha256-4DF9DbDuK4/+KYx0L6XcPBeDHUFVCtzok2fWtwXtb5w=",
+        "owner": "nixos",
+        "repo": "nixpkgs",
+        "rev": "d70f5cd5c3bef45f7f52698f39e7cc7a89daa7f0",
         "type": "github"
       },
       "original": {
-        "owner": "hercules-ci",
-        "repo": "gitignore.nix",
+        "owner": "nixos",
+        "ref": "nixos-22.11",
+        "repo": "nixpkgs",
         "type": "github"
       }
     },
-    "hs-flake-utils": {
-      "inputs": {
-        "flake-utils": "flake-utils_2",
-        "nixpkgs": "nixpkgs",
-        "pre-commit-hooks": "pre-commit-hooks"
-      },
+    "nixpkgs_3": {
       "locked": {
-        "lastModified": 1681762240,
-        "narHash": "sha256-+PLx9xHBvV70dA7Gy/+YTH1w3PcSOrGV0z0rGxts8jU=",
-        "ref": "main",
-        "rev": "a51e591b7fdf8881ac0237452691df7b1aceecd3",
-        "revCount": 10,
-        "type": "git",
-        "url": "https://whetstone.private.storage/jcalderone/hs-flake-utils.git"
+        "lastModified": 1677624842,
+        "narHash": "sha256-4DF9DbDuK4/+KYx0L6XcPBeDHUFVCtzok2fWtwXtb5w=",
+        "owner": "nixos",
+        "repo": "nixpkgs",
+        "rev": "d70f5cd5c3bef45f7f52698f39e7cc7a89daa7f0",
+        "type": "github"
       },
       "original": {
-        "ref": "main",
-        "type": "git",
-        "url": "https://whetstone.private.storage/jcalderone/hs-flake-utils.git"
+        "owner": "nixos",
+        "ref": "nixos-22.11",
+        "repo": "nixpkgs",
+        "type": "github"
       }
     },
-    "nixpkgs": {
+    "nixpkgs_4": {
       "locked": {
         "lastModified": 1677624842,
         "narHash": "sha256-4DF9DbDuK4/+KYx0L6XcPBeDHUFVCtzok2fWtwXtb5w=",
@@ -108,17 +600,17 @@
         "type": "github"
       }
     },
-    "nixpkgs-stable": {
+    "nixpkgs_5": {
       "locked": {
-        "lastModified": 1673800717,
-        "narHash": "sha256-SFHraUqLSu5cC6IxTprex/nTsI81ZQAtDvlBvGDWfnA=",
-        "owner": "NixOS",
+        "lastModified": 1677624842,
+        "narHash": "sha256-4DF9DbDuK4/+KYx0L6XcPBeDHUFVCtzok2fWtwXtb5w=",
+        "owner": "nixos",
         "repo": "nixpkgs",
-        "rev": "2f9fd351ec37f5d479556cd48be4ca340da59b8f",
+        "rev": "d70f5cd5c3bef45f7f52698f39e7cc7a89daa7f0",
         "type": "github"
       },
       "original": {
-        "owner": "NixOS",
+        "owner": "nixos",
         "ref": "nixos-22.11",
         "repo": "nixpkgs",
         "type": "github"
@@ -152,6 +644,128 @@
         "type": "github"
       }
     },
+    "pre-commit-hooks_2": {
+      "inputs": {
+        "flake-compat": "flake-compat_2",
+        "flake-utils": [
+          "tahoe-capabilities",
+          "hs-flake-utils",
+          "flake-utils"
+        ],
+        "gitignore": "gitignore_2",
+        "nixpkgs": [
+          "tahoe-capabilities",
+          "hs-flake-utils",
+          "nixpkgs"
+        ],
+        "nixpkgs-stable": "nixpkgs-stable_2"
+      },
+      "locked": {
+        "lastModified": 1677722096,
+        "narHash": "sha256-7mjVMvCs9InnrRybBfr5ohqcOz+pyEX8m22C1XsDilg=",
+        "owner": "cachix",
+        "repo": "pre-commit-hooks.nix",
+        "rev": "61a3511668891c68ebd19d40122150b98dc2fe3b",
+        "type": "github"
+      },
+      "original": {
+        "owner": "cachix",
+        "repo": "pre-commit-hooks.nix",
+        "type": "github"
+      }
+    },
+    "pre-commit-hooks_3": {
+      "inputs": {
+        "flake-compat": "flake-compat_3",
+        "flake-utils": [
+          "tahoe-chk",
+          "hs-flake-utils",
+          "flake-utils"
+        ],
+        "gitignore": "gitignore_3",
+        "nixpkgs": [
+          "tahoe-chk",
+          "hs-flake-utils",
+          "nixpkgs"
+        ],
+        "nixpkgs-stable": "nixpkgs-stable_3"
+      },
+      "locked": {
+        "lastModified": 1677722096,
+        "narHash": "sha256-7mjVMvCs9InnrRybBfr5ohqcOz+pyEX8m22C1XsDilg=",
+        "owner": "cachix",
+        "repo": "pre-commit-hooks.nix",
+        "rev": "61a3511668891c68ebd19d40122150b98dc2fe3b",
+        "type": "github"
+      },
+      "original": {
+        "owner": "cachix",
+        "repo": "pre-commit-hooks.nix",
+        "type": "github"
+      }
+    },
+    "pre-commit-hooks_4": {
+      "inputs": {
+        "flake-compat": "flake-compat_4",
+        "flake-utils": [
+          "tahoe-ssk",
+          "hs-flake-utils",
+          "flake-utils"
+        ],
+        "gitignore": "gitignore_4",
+        "nixpkgs": [
+          "tahoe-ssk",
+          "hs-flake-utils",
+          "nixpkgs"
+        ],
+        "nixpkgs-stable": "nixpkgs-stable_4"
+      },
+      "locked": {
+        "lastModified": 1677722096,
+        "narHash": "sha256-7mjVMvCs9InnrRybBfr5ohqcOz+pyEX8m22C1XsDilg=",
+        "owner": "cachix",
+        "repo": "pre-commit-hooks.nix",
+        "rev": "61a3511668891c68ebd19d40122150b98dc2fe3b",
+        "type": "github"
+      },
+      "original": {
+        "owner": "cachix",
+        "repo": "pre-commit-hooks.nix",
+        "type": "github"
+      }
+    },
+    "pre-commit-hooks_5": {
+      "inputs": {
+        "flake-compat": "flake-compat_5",
+        "flake-utils": [
+          "tahoe-ssk",
+          "tahoe-capabilities",
+          "hs-flake-utils",
+          "flake-utils"
+        ],
+        "gitignore": "gitignore_5",
+        "nixpkgs": [
+          "tahoe-ssk",
+          "tahoe-capabilities",
+          "hs-flake-utils",
+          "nixpkgs"
+        ],
+        "nixpkgs-stable": "nixpkgs-stable_5"
+      },
+      "locked": {
+        "lastModified": 1677722096,
+        "narHash": "sha256-7mjVMvCs9InnrRybBfr5ohqcOz+pyEX8m22C1XsDilg=",
+        "owner": "cachix",
+        "repo": "pre-commit-hooks.nix",
+        "rev": "61a3511668891c68ebd19d40122150b98dc2fe3b",
+        "type": "github"
+      },
+      "original": {
+        "owner": "cachix",
+        "repo": "pre-commit-hooks.nix",
+        "type": "github"
+      }
+    },
     "root": {
       "inputs": {
         "flake-utils": "flake-utils",
@@ -159,7 +773,10 @@
         "nixpkgs": [
           "hs-flake-utils",
           "nixpkgs"
-        ]
+        ],
+        "tahoe-capabilities": "tahoe-capabilities",
+        "tahoe-chk": "tahoe-chk",
+        "tahoe-ssk": "tahoe-ssk"
       }
     },
     "systems": {
@@ -176,6 +793,119 @@
         "repo": "default",
         "type": "github"
       }
+    },
+    "systems_2": {
+      "locked": {
+        "lastModified": 1681028828,
+        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+        "owner": "nix-systems",
+        "repo": "default",
+        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nix-systems",
+        "repo": "default",
+        "type": "github"
+      }
+    },
+    "tahoe-capabilities": {
+      "inputs": {
+        "flake-utils": "flake-utils_3",
+        "hs-flake-utils": "hs-flake-utils_2",
+        "nixpkgs": [
+          "hs-flake-utils",
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1686164259,
+        "narHash": "sha256-T0U5B/DH+XDEXfBG7cHlTcMec4aUTDd+HH8Bsv0C71Y=",
+        "ref": "refs/heads/main",
+        "rev": "f0a11218007f6c2fe0f6b5e09e0993fe253f6e23",
+        "revCount": 9,
+        "type": "git",
+        "url": "https://whetstone.private.storage/PrivateStorage/tahoe-capabilities"
+      },
+      "original": {
+        "type": "git",
+        "url": "https://whetstone.private.storage/PrivateStorage/tahoe-capabilities"
+      }
+    },
+    "tahoe-capabilities_2": {
+      "inputs": {
+        "flake-utils": "flake-utils_9",
+        "hs-flake-utils": "hs-flake-utils_5",
+        "nixpkgs": [
+          "tahoe-ssk",
+          "hs-flake-utils",
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1686164259,
+        "narHash": "sha256-T0U5B/DH+XDEXfBG7cHlTcMec4aUTDd+HH8Bsv0C71Y=",
+        "ref": "refs/heads/main",
+        "rev": "f0a11218007f6c2fe0f6b5e09e0993fe253f6e23",
+        "revCount": 9,
+        "type": "git",
+        "url": "https://whetstone.private.storage/PrivateStorage/tahoe-capabilities"
+      },
+      "original": {
+        "type": "git",
+        "url": "https://whetstone.private.storage/PrivateStorage/tahoe-capabilities"
+      }
+    },
+    "tahoe-chk": {
+      "inputs": {
+        "flake-utils": "flake-utils_5",
+        "hs-flake-utils": "hs-flake-utils_3",
+        "nixpkgs": [
+          "hs-flake-utils",
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1683552888,
+        "narHash": "sha256-h9pgP/LYPtUr5CeCAhqt1XJyAqKTnkQxuIygiTulU/U=",
+        "ref": "refs/tags/0.1.0.1",
+        "rev": "05aeb5a433b85406ca3c0c313c46299a1026ade0",
+        "revCount": 344,
+        "type": "git",
+        "url": "https://whetstone.private.storage/PrivateStorage/tahoe-chk"
+      },
+      "original": {
+        "ref": "refs/tags/0.1.0.1",
+        "type": "git",
+        "url": "https://whetstone.private.storage/PrivateStorage/tahoe-chk"
+      }
+    },
+    "tahoe-ssk": {
+      "inputs": {
+        "flake-utils": "flake-utils_7",
+        "hs-flake-utils": "hs-flake-utils_4",
+        "nixpkgs": [
+          "hs-flake-utils",
+          "nixpkgs"
+        ],
+        "tahoe-capabilities": "tahoe-capabilities_2",
+        "tahoe-chk": [
+          "tahoe-chk"
+        ]
+      },
+      "locked": {
+        "lastModified": 1686166709,
+        "narHash": "sha256-z+QzmnfmLuqqNFWvlYAVZoS7jBHEj54OZest/sKgDqk=",
+        "ref": "refs/heads/main",
+        "rev": "8f73a87054546c875c3b0284ee4d2f3e0483d963",
+        "revCount": 93,
+        "type": "git",
+        "url": "https://whetstone.private.storage/PrivateStorage/tahoe-ssk.git"
+      },
+      "original": {
+        "type": "git",
+        "url": "https://whetstone.private.storage/PrivateStorage/tahoe-ssk.git"
+      }
     }
   },
   "root": "root",
diff --git a/flake.nix b/flake.nix
index a1cdf8f85673f6a2d7ffc687cf827f53fec5c4d0..e5071379920df5838d9abbfcf76d9d9106cd4439 100644
--- a/flake.nix
+++ b/flake.nix
@@ -6,6 +6,19 @@
     flake-utils.url = github:numtide/flake-utils;
     hs-flake-utils.url = "git+https://whetstone.private.storage/jcalderone/hs-flake-utils.git?ref=main";
     nixpkgs.follows = "hs-flake-utils/nixpkgs";
+    tahoe-chk = {
+      url = "git+https://whetstone.private.storage/PrivateStorage/tahoe-chk?ref=refs/tags/0.1.0.1";
+      inputs.nixpkgs.follows = "hs-flake-utils/nixpkgs";
+    };
+    tahoe-ssk = {
+      url = "git+https://whetstone.private.storage/PrivateStorage/tahoe-ssk.git";
+      inputs.nixpkgs.follows = "hs-flake-utils/nixpkgs";
+      inputs.tahoe-chk.follows = "tahoe-chk";
+    };
+    tahoe-capabilities = {
+      url = "git+https://whetstone.private.storage/PrivateStorage/tahoe-capabilities";
+      inputs.nixpkgs.follows = "hs-flake-utils/nixpkgs";
+    };
   };
 
   outputs = {
@@ -13,6 +26,9 @@
     nixpkgs,
     flake-utils,
     hs-flake-utils,
+    tahoe-chk,
+    tahoe-ssk,
+    tahoe-capabilities,
   }: let
     ulib = flake-utils.lib;
     ghcVersion = "ghc8107";
@@ -27,10 +43,19 @@
         src = ./.;
         compilerVersion = ghcVersion;
         packageName = "tahoe-directory";
+        hsPkgsOverrides = hfinal: hprev: {
+          tahoe-chk = tahoe-chk.outputs.packages.${system}.default;
+          tahoe-ssk = tahoe-ssk.outputs.packages.${system}.default;
+          tahoe-capabilities = tahoe-capabilities.outputs.packages.${system}.default;
+          haskellLib = pkgs.haskell.lib;
+        };
       };
     in {
       checks = hslib.checks {};
       devShells = hslib.devShells {
+        shellHook = ''
+          nix run .#generate-cabal-project
+        '';
         extraBuildInputs = pkgs:
           with pkgs; [
             zlib
@@ -39,6 +64,31 @@
       packages = hslib.packages {};
       apps.hlint = hslib.apps.hlint {};
 
+      apps.generate-cabal-project = {
+        type = "app";
+        program = "${
+          pkgs.writeShellApplication {
+            name = "generate-cabal-project";
+            text = ''
+              cat >cabal.project.local <<EOF
+              -- This file is auto-generated by the flake devShell's shellHook.  Do
+              -- not edit this file.  Make changes in flake.nix.
+              tests: true
+              packages:
+                -- These aren't released on hackage yet so we have to get them
+                -- another way.  Here, we get them from the Nix store.
+                -- tahoe-chk
+                ${tahoe-chk}
+                -- tahoe-ssk
+                ${tahoe-ssk}
+                -- tahoe-capabilities
+                ${tahoe-capabilities}
+              EOF
+            '';
+          }
+        }/bin/generate-cabal-project";
+      };
+
       # Using the working directory of `nix run`, do a build with cabal and
       # then run the test suite.
       apps.cabal-test = {
@@ -49,6 +99,7 @@
             runtimeInputs = with pkgs; [pkg-config haskell.compiler.${ghcVersion} cabal-install];
 
             text = ''
+              nix run .#generate-cabal-project
               cabal update hackage.haskell.org
               cabal build
               cabal run tests
diff --git a/src/Tahoe/Directory.hs b/src/Tahoe/Directory.hs
index 611a9b08e837e93b337f8997fcd8559fc16df65c..dbde17304dcf9404f3fc9aeef528ee4522279816 100644
--- a/src/Tahoe/Directory.hs
+++ b/src/Tahoe/Directory.hs
@@ -1,7 +1,16 @@
 module Tahoe.Directory (
     module Tahoe.Directory.Internal.Parsing,
     module Tahoe.Directory.Internal.Types,
+    module Tahoe.Directory.Internal.Capability,
 ) where
 
+import Tahoe.Directory.Internal.Capability (
+    DirectoryCapability (..),
+    pReadCHK,
+    pReadSDMF,
+    pVerifyCHK,
+    pVerifySDMF,
+    pWriteSDMF,
+ )
 import Tahoe.Directory.Internal.Parsing (parse, serialize)
 import Tahoe.Directory.Internal.Types (Directory (..), Entry (..))
diff --git a/src/Tahoe/Directory/Internal/Capability.hs b/src/Tahoe/Directory/Internal/Capability.hs
new file mode 100644
index 0000000000000000000000000000000000000000..09f69bb130dfb1f3bf628371693e7ade0674b5b2
--- /dev/null
+++ b/src/Tahoe/Directory/Internal/Capability.hs
@@ -0,0 +1,87 @@
+{-# LANGUAGE FlexibleInstances #-}
+
+module Tahoe.Directory.Internal.Capability where
+
+import qualified Data.Text as T
+import Data.Void (Void)
+import qualified Tahoe.CHK.Capability as CHK
+import Tahoe.Capability (ConfidentialShowable (..))
+import qualified Tahoe.SDMF as SDMF
+
+import Text.Megaparsec (Parsec, getInput, setInput)
+
+{- | A wrapper around some other capability type which signals that the
+ plaintext is an encoded list of files.
+-}
+newtype DirectoryCapability a = DirectoryCapability a deriving (Eq, Ord, Show)
+
+instance ConfidentialShowable (DirectoryCapability CHK.Verifier) where
+    confidentiallyShow (DirectoryCapability a) =
+        T.replace "URI:CHK-Verifier:" "URI:DIR2-CHK-Verifier:" (CHK.dangerRealShow (CHK.CHKVerifier a))
+
+instance ConfidentialShowable (DirectoryCapability CHK.Reader) where
+    confidentiallyShow (DirectoryCapability a) =
+        T.replace "URI:CHK:" "URI:DIR2-CHK:" (CHK.dangerRealShow (CHK.CHKReader a))
+
+instance ConfidentialShowable (DirectoryCapability SDMF.Verifier) where
+    confidentiallyShow (DirectoryCapability a) =
+        T.replace "URI:SSK-Verifier:" "URI:DIR2-Verifier:" (confidentiallyShow a)
+
+instance ConfidentialShowable (DirectoryCapability SDMF.Reader) where
+    confidentiallyShow (DirectoryCapability a) =
+        T.replace "URI:SSK-RO:" "URI:DIR2-RO:" (confidentiallyShow a)
+
+instance ConfidentialShowable (DirectoryCapability SDMF.Writer) where
+    confidentiallyShow (DirectoryCapability a) =
+        T.replace "URI:SSK:" "URI:DIR2:" (confidentiallyShow a)
+
+type Parser = Parsec Void T.Text
+
+{- | Parse a CHK directory verifier capability.
+
+ The implementation is a cheesy hack that does string substitution on the
+ input before applying the original CHK verifier parser.
+-}
+pVerifyCHK :: Parser (DirectoryCapability CHK.Verifier)
+pVerifyCHK = do
+    s <- getInput
+    setInput $ T.replace "URI:DIR2-CHK-Verifier:" "URI:CHK-Verifier:" s
+    DirectoryCapability <$> CHK.pVerifier
+
+{- | Parse a CHK directory reader capability.
+
+ The implementation is a cheesy hack that does string substitution on the
+ input before applying the original CHK reader parser.
+-}
+pReadCHK :: Parser (DirectoryCapability CHK.Reader)
+pReadCHK = do
+    s <- getInput
+    setInput $ T.replace "URI:DIR2-CHK:" "URI:CHK:" s
+    DirectoryCapability <$> CHK.pReader
+
+{- | Parse an SDMF directory verifier capability.  As is the case for the other
+ directory capability parsers, the implementation is cheesy.
+-}
+pVerifySDMF :: Parser (DirectoryCapability SDMF.Verifier)
+pVerifySDMF = do
+    s <- getInput
+    setInput $ T.replace "URI:DIR2-Verifier:" "URI:SSK-Verifier:" s
+    DirectoryCapability <$> SDMF.pVerifier
+
+{- | Parse an SDMF directory reader capability.  As is the case for the other
+ directory capability parsers, the implementation is cheesy.
+-}
+pReadSDMF :: Parser (DirectoryCapability SDMF.Reader)
+pReadSDMF = do
+    s <- getInput
+    setInput $ T.replace "URI:DIR2-RO:" "URI:SSK-RO:" s
+    DirectoryCapability <$> SDMF.pReader
+
+{- | Parse an SDMF directory writer capability.  As is the case for the other
+ directory capability parsers, the implementation is cheesy.
+-}
+pWriteSDMF :: Parser (DirectoryCapability SDMF.Writer)
+pWriteSDMF = do
+    s <- getInput
+    setInput $ T.replace "URI:DIR2:" "URI:SSK:" s
+    DirectoryCapability <$> SDMF.pWriter
diff --git a/tahoe-directory.cabal b/tahoe-directory.cabal
index fad8cfd975977dd00e4c5ef0a71bec7e1a352f60..de8c35b9a3f23518d1f79f860b24580883efa8aa 100644
--- a/tahoe-directory.cabal
+++ b/tahoe-directory.cabal
@@ -90,13 +90,17 @@ library
   hs-source-dirs:  src
   exposed-modules:
     Tahoe.Directory
+    Tahoe.Directory.Internal.Capability
     Tahoe.Directory.Internal.Parsing
     Tahoe.Directory.Internal.Types
 
   build-depends:
-    , base        >=4 && <5.0
+    , base                >=4 && <5.0
     , bytestring
     , megaparsec
+    , tahoe-capabilities
+    , tahoe-chk
+    , tahoe-ssk
     , text
 
 test-suite tahoe-directory-test
@@ -131,6 +135,8 @@ test-suite tahoe-directory-test
     , base
     , bytestring
     , hedgehog
+    , megaparsec
+    , tahoe-capabilities
     , tahoe-directory
     , tasty
     , tasty-hedgehog
diff --git a/test/Spec.hs b/test/Spec.hs
index ca23fb82884a3afcc25418e037a9cb329d898d74..5caab95bce103f862b8ea87c78b2deeea010fb9f 100644
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -4,10 +4,12 @@ import qualified Data.ByteString as B
 import Generators (directories)
 import Hedgehog (forAll, property, tripping)
 import System.IO (hSetEncoding, stderr, stdout, utf8)
+import Tahoe.Capability (confidentiallyShow)
 import qualified Tahoe.Directory as Directory
 import Test.Tasty (TestTree, defaultMain, testGroup)
 import Test.Tasty.HUnit (assertEqual, testCase)
 import Test.Tasty.Hedgehog (testProperty)
+import Text.Megaparsec (parse)
 
 tests :: TestTree
 tests =
@@ -23,6 +25,36 @@ tests =
             property $ do
                 directory <- forAll directories
                 tripping directory Directory.serialize Directory.parse
+        , testCase "well-known directory CHK read capability round-trips through parseCapability . confidentiallyShow" $ do
+            -- ❯ curl -XPOST http://localhost:3456/uri?t=mkdir-immutable --data '{"foo": ["filenode", {}], "bar": ["filenode", {}], "baz": ["filenode", {}]}'
+            let original = "URI:DIR2-CHK:46y5edbbxojwg5ez4lelafu5jy:fbgn7ijfxarstdxmr363et4de522n7eslnqxavymfqkoax4lc65q:3:10:63"
+            let parsed = parse Directory.pReadCHK "" original
+            let serialized = confidentiallyShow <$> parsed
+            assertEqual "original /= serialized" (Right original) serialized
+        , testCase "well-known directory CHK verify capability round-trips through parseCapability . confidentiallyShow" $ do
+            -- `tahoe ls --json <read cap>` to produce verifier
+            let original = "URI:DIR2-CHK-Verifier:ni4pjcqflws33ikhifsxqwvmya:fbgn7ijfxarstdxmr363et4de522n7eslnqxavymfqkoax4lc65q:3:10:63"
+            let parsed = parse Directory.pVerifyCHK "" original
+            let serialized = confidentiallyShow <$> parsed
+            assertEqual "original /= serialized" (Right original) serialized
+        , testCase "well-known directory SDMF write capability round-trips through parseCapability . confidentiallyShow" $ do
+            -- `tahoe mkdir` to produce directory writer
+            let original = "URI:DIR2:ez2k3glrx46svivnnrmh77uieq:c43fbv5274wmphdykpmpweq4moat5co53fvf42lg2z2xekkghm6a"
+                parsed = parse Directory.pWriteSDMF "" original
+                serialized = confidentiallyShow <$> parsed
+            assertEqual "original /= serialized" (Right original) serialized
+        , testCase "well-known directory SDMF read capability round-trips through parseCapability . confidentiallyShow" $ do
+            -- `tahoe ls --json <write cap>` to produce reader
+            let original = "URI:DIR2-RO:g3vdy2tlmpejr2tts7n6pyxbnu:c43fbv5274wmphdykpmpweq4moat5co53fvf42lg2z2xekkghm6a"
+                parsed = parse Directory.pReadSDMF "" original
+                serialized = confidentiallyShow <$> parsed
+            assertEqual "original /= serialized" (Right original) serialized
+        , testCase "well-known directory SDMF verify capability round-trips through parseCapability . confidentiallyShow" $ do
+            -- `tahoe ls --json <write cap>` to produce verifier
+            let original = "URI:DIR2-Verifier:aofaktmdrgegdvnq3vsxnccc7q:c43fbv5274wmphdykpmpweq4moat5co53fvf42lg2z2xekkghm6a"
+                parsed = parse Directory.pVerifySDMF "" original
+                serialized = confidentiallyShow <$> parsed
+            assertEqual "original /= serialized" (Right original) serialized
         ]
 
 main :: IO ()