diff --git a/man/index.txt b/man/index.txt
index befd9ee..8902381 100644
--- a/man/index.txt
+++ b/man/index.txt
@@ -1,9 +1,13 @@
-zfs-tpm2-change-key(8)  zfs-tpm2-change-key.8.ronn
-zfs-tpm2-load-key(8)    zfs-tpm2-load-key.8.ronn
-zfs-tpm2-clear-key(8)   zfs-tpm2-clear-key.8.ronn
+zfs-tpm2-change-key(8)   zfs-tpm2-change-key.8.ronn
+zfs-tpm2-load-key(8)     zfs-tpm2-load-key.8.ronn
+zfs-tpm2-clear-key(8)    zfs-tpm2-clear-key.8.ronn
+zfs-tpm1x-change-key(8)  zfs-tpm1x-change-key.8.ronn
+zfs-tpm1x-load-key(8)    zfs-tpm1x-load-key.8.ronn
+zfs-tpm1x-clear-key(8)   zfs-tpm1x-clear-key.8.ronn
+zfs-tpm-list(8)          zfs-tpm-list.8.ronn
 
-zfs(8)                  https://manpages.debian.org/bullseye/zfsutils-linux/zfs.8.en.html
-tcsd(8)                 https://manpages.debian.org/bullseye/trousers/tcsd.8.en.html
-tpm2_unseal(1)          https://manpages.debian.org/bullseye/tpm2-tools/tpm2_unseal.1.en.html
+zfs(8)                   https://manpages.debian.org/bullseye/zfsutils-linux/zfs.8.en.html
+tcsd(8)                  https://manpages.debian.org/bullseye/trousers/tcsd.8.en.html
+tpm2_unseal(1)           https://manpages.debian.org/bullseye/tpm2-tools/tpm2_unseal.1.en.html
 
-ESYS_CONTEXT(3)         https://www.mankier.com/3/ESYS_CONTEXT
+ESYS_CONTEXT(3)          https://www.mankier.com/3/ESYS_CONTEXT
diff --git a/man/zfs-tpm-list.md.pp b/man/zfs-tpm-list.md.pp
new file mode 100644
index 0000000..3b47e2a
--- /dev/null
+++ b/man/zfs-tpm-list.md.pp
@@ -0,0 +1,67 @@
+zfs-tpm-list(8) -- print dataset tzpfms metadata
+================================================
+
+## SYNOPSIS
+
+`zfs-tpm-list` [-H] [-r\|-d *depth*] [-a\|-b *back-end*] [*filesystem*\|*volume*]…
+
+## DESCRIPTION
+
+zfs-tpm-list(8) lists the following properties on encryption roots:
+
+  * `name`,
+  * `back-end`: the tzpfms back-end (e.g. "TPM2" for zfs-tpm2-change-key(8) or "TPM1.X" for zfs-tpm1x-change-key(8)),
+                or "-" if none is configured,
+  * `keystatus`: "available" or "unavailable",
+  * `coherent`: "yes" if either both `xyz.nabijaczleweli:tzpfms.backend` and `xyz.nabijaczleweli:tzpfms.key` are present or missing, "no" otherwise.
+
+Incoherent datasets require immediate operator attention, with either the appropriate zfs-tpm\*-clear-key program or zfs(8) change-key —
+if the key becomes unloaded, they will require restoration from back-up.
+However, they should never occur, unless something went terribly wrong with the dataset properties.
+
+If no datasets are specified, lists all matching encryption roots.
+The default filter is to list all roots managed by tzpfms.
+The `-a` and `-b` [OPTIONS]() can be used to either list all roots or only ones backed by a particular end, respectively.
+
+## OPTIONS
+
+  * `-H`:
+    Used for scripting mode. Do not print headers and separate fields by a single tab instead of arbitrary white space.
+
+  * `-r`:
+    Recurse into all descendant datasets. Default if no datasets listed on the command-line.
+  * `-d` *depth*:
+    Recurse at most *depth* datasets deep. Defaults to zero if datasets were listed on the command-line.
+
+  * `-a`:
+    List all encryption roots, even ones not managed by tzpfms.
+  * `-b` *back-end*:
+    List only encryption roots with tzpfms back-end *back-end*.
+
+## EXAMPLES
+
+    $ zfs-tpm-list
+    NAME      BACK-END  KEYSTATUS    COHERENT
+    owo/venc  TPM2      unavailable  yes
+    owo/enc   TPM1.X    available    yes
+
+    $ zfs-tpm-list -ad0
+    NAME  BACK-END  KEYSTATUS  COHERENT
+    awa   -         available  yes
+
+    $ zfs-tpm-list -b TPM2
+    NAME      BACK-END  KEYSTATUS    COHERENT
+    owo/venc  TPM2      unavailable  yes
+
+    $ zfs-tpm-list -ra owo
+    NAME      BACK-END  KEYSTATUS    COHERENT
+    owo/venc  TPM2      unavailable  yes
+    owo/vtnc  -         available    yes
+    owo/v nc  -         available    yes
+    owo/enc   TPM1.X    available    yes
+
+#include "common.h"
+
+## SEE ALSO
+
+&lt;<https://git.sr.ht/~nabijaczleweli/tzpfms>&gt;
diff --git a/src/bin/zfs-tpm-list.cpp b/src/bin/zfs-tpm-list.cpp
index fcfcc19..067d0ba 100644
--- a/src/bin/zfs-tpm-list.cpp
+++ b/src/bin/zfs-tpm-list.cpp
@@ -23,7 +23,7 @@ struct output_line {
 	bool coherent : 1;
 
 	bool included(bool print_nontzpfms, const char * backend_restrixion) const {
-		return (print_nontzpfms || this->backend[0] != '\0') && (!backend_restrixion || !strcmp(backend_restrixion, this->backend));
+		return (print_nontzpfms || !this->coherent || this->backend[0] != '\0') && (!backend_restrixion || !strcmp(backend_restrixion, this->backend));
 	}
 
 	const char * backend_display() const { return (this->backend[0] != '\0') ? this->backend : "-"; }
@@ -39,7 +39,7 @@ int main(int argc, char ** argv) {
 	size_t maxdepth                 = MAXDEPTH_UNSET;
 	const char * backend_restrixion = nullptr;
 	return do_bare_main(
-	    argc, argv, "Hrd:ab:", "[-H] [-r|-d max] [-a|-b back-end]",
+	    argc, argv, "Hrd:ab:", "[-H] [-r|-d max] [-a|-b back-end]", "[filesystem|volume]…",
 	    [&](auto arg) {
 		    switch(arg) {
 			    case 'H':
diff --git a/src/main.hpp b/src/main.hpp
index da7d05f..00aa070 100644
--- a/src/main.hpp
+++ b/src/main.hpp
@@ -20,7 +20,7 @@
 
 
 template <class G, class M>
-int do_bare_main(int argc, char ** argv, const char * getoptions, const char * usage, G && getoptfn, M && main) {
+int do_bare_main(int argc, char ** argv, const char * getoptions, const char * usage, const char * dataset_usage, G && getoptfn, M && main) {
 	const auto libz = TRY_PTR("initialise libzfs", libzfs_init());
 	quickscope_wrapper libz_deleter{[=] { libzfs_fini(libz); }};
 
@@ -35,7 +35,7 @@ int do_bare_main(int argc, char ** argv, const char * getoptions, const char * u
 		switch(opt) {
 			case '?':
 			case 'h':
-				fprintf(opt == 'h' ? stdout : stderr, "Usage: %s [-hV] %s%s<dataset>\n", argv[0], usage, strlen(usage) ? " " : "");
+				fprintf(opt == 'h' ? stdout : stderr, "Usage: %s [-hV] %s%s%s\n", argv[0], usage, strlen(usage) ? " " : "", dataset_usage);
 				return opt == 'h' ? 0 : __LINE__;
 			case 'V':
 				printf("tzpfms version %s\n", TZPFMS_VERSION);
@@ -45,7 +45,7 @@ int do_bare_main(int argc, char ** argv, const char * getoptions, const char * u
 					getoptfn(opt);
 				else {
 					if(auto err = getoptfn(opt)) {
-						fprintf(stderr, "Usage: %s [-hV] %s%s<dataset>\n", argv[0], usage, strlen(usage) ? " " : "");
+						fprintf(stderr, "Usage: %s [-hV] %s%s%s\n", argv[0], usage, strlen(usage) ? " " : "", dataset_usage);
 						return err;
 					}
 				}
@@ -56,7 +56,7 @@ int do_bare_main(int argc, char ** argv, const char * getoptions, const char * u
 
 template <class G, class M>
 int do_main(int argc, char ** argv, const char * getoptions, const char * usage, G && getoptfn, M && main) {
-	return do_bare_main(argc, argv, getoptions, usage, getoptfn, [&](auto libz) {
+	return do_bare_main(argc, argv, getoptions, usage, "<dataset>", getoptfn, [&](auto libz) {
 		if(optind >= argc) {
 			fprintf(stderr,
 			        "No dataset to act on?\n"