diff --git a/flake.nix b/flake.nix
index 97a1f97..ed6a3e5 100644
--- a/flake.nix
+++ b/flake.nix
@@ -45,6 +45,21 @@
           dbus-broker = pkgs.callPackage ./pkgs/dbus-broker { };
 
           qemu-uefi-tpm = pkgs.callPackage ./utils/qemu-uefi-tpm.nix { };
+
+          debug-tools-sysext = pkgs.callPackage ./lib/make-sysext.nix {
+            name = "debug-tools";
+            version = "0.0.1";
+            packages = [
+                { drv = pkgs.curl; path = "bin/curl"; }
+                { drv = patosPkgs.tpm2-tools; path = "bin/tpm2"; }
+                { drv = pkgs.cryptsetup; path = "bin/cryptsetup"; }
+                { drv = pkgs.cryptsetup; path = "bin/veritysetup"; }
+                # shared lib required for cryptsetup
+                { drv = pkgs.popt; path = "lib/libpopt.so.0.0.2"; }
+                { drv = pkgs.popt; path = "lib/libpopt.so.0"; }
+                { drv = pkgs.popt; path = "lib/libpopt.so"; }
+            ];
+          };
         };
 
         checks = {
diff --git a/lib/make-sysext.nix b/lib/make-sysext.nix
new file mode 100644
index 0000000..fb1a8f0
--- /dev/null
+++ b/lib/make-sysext.nix
@@ -0,0 +1,87 @@
+{
+  lib,
+  runCommand,
+  pkgs,
+
+  name,
+  packages,
+  osId ? "patos",
+  version ? null,
+}:
+
+
+let
+  metadata = {
+    ID = osId;
+    VERSION_ID = osId;
+    IMAGE_ID = name;
+    IMAGE_VERSION = version;
+  };
+
+  metadataFile = lib.concatStringsSep "\n" (
+    lib.mapAttrsToList (k: v: "${k}=${v}") (lib.filterAttrs (_: v: v != null) metadata)
+  );
+
+  doCopy =
+    {
+      drv,
+      prefix ? "usr",
+      path,
+      destpath ? null,
+    }:
+    "do_copy ${prefix} ${drv} ${path}" + lib.optionalString (destpath != null) " ${destpath}";
+
+in
+
+runCommand name
+  {
+    passthru.name = name;
+    inherit metadataFile;
+    passAsFile = [ "metadataFile" ];
+
+    buildInputs = [
+      pkgs.erofs-utils
+      pkgs.cryptsetup
+    ];
+
+  }
+  ''
+    do_copy () {
+      local prefix="$1"
+      local drv="$2"
+      local path="$3"
+      local destpath="''${4:-$path}"
+
+      local srcfile
+      local destdir
+      local destfile
+      srcfile="$drv/$path"
+      destfile="$out/tree/$prefix/$destpath"
+      destdir="$(dirname -- "$destfile")"
+
+      mkdir -pv "$destdir"
+      cp -Pv "$srcfile" "$destfile"
+
+      chmod 755 "$destfile"
+      patchelf --set-rpath /lib:/usr/lib:/ $destfile
+      patchelf --set-interpreter /lib/ld-linux-x86-64.so.2 $destfile || true
+    }
+
+    mkdir -p $out/tree
+
+    ${lib.concatStringsSep "\n" (map doCopy packages)}
+
+    # bake metadata into the structure
+    if ! [ -f $out/tree/usr/lib/extension-release.d/extension-release."${name}" ]; then
+      mkdir -p $out/tree/usr/lib/extension-release.d
+      cat "$metadataFilePath" > $out/tree/usr/lib/extension-release.d/extension-release."${name}"
+    fi
+
+    pushd $out
+    find tree -type d -exec chmod 0755 {} \;
+    mkfs.erofs --all-root $name.raw tree/
+    veritysetup format --root-hash-file $name.roothash $name.raw $name.verity
+    #TODO: pcks7 signature?
+    rm -rf tree
+    popd
+  ''