diff --git a/Makefile b/Makefile
index e98eb68..b33df40 100644
--- a/Makefile
+++ b/Makefile
@@ -31,7 +31,7 @@ VERAR := $(foreach l,TZPFMS,-D$(l)_VERSION='$($(l)_VERSION)')
 BINARY_SOURCES := $(sort $(wildcard $(SRCDIR)bin/*.cpp $(SRCDIR)bin/**/*.cpp))
 COMMON_SOURCES := $(filter-out $(BINARY_SOURCES),$(sort $(wildcard $(SRCDIR)*.cpp $(SRCDIR)**/*.cpp $(SRCDIR)**/**/*.cpp $(SRCDIR)**/**/**/*.cpp)))
 # TEST_SOURCES := $(sort $(wildcard $(TSTDIR)*.cpp $(TSTDIR)**/*.cpp $(TSTDIR)**/**/*.cpp $(TSTDIR)**/**/**/*.cpp))
-MANPAGE_SOURCES := $(sort $(wildcard $(MANDIR)*.md $(MANDIR)**/*.md))
+MANPAGE_SOURCES := $(sort $(wildcard $(MANDIR)*.md.pp))
 
 
 .PHONY : all clean build build-test man
@@ -48,17 +48,17 @@ clean :
 
 build : $(subst $(SRCDIR)bin/,$(OUTDIR),$(subst .cpp,$(EXE),$(BINARY_SOURCES)))
 #build-test : $(OUTDIR)tzpfms-test$(EXE)
-man : $(subst $(MANDIR),$(OUTDIR)man/,$(MANPAGE_SOURCES))
+man : $(OUTDIR)man/index.txt
 
 
 #$(OUTDIR)tzpfms-test$(EXE) : $(subst $(TSTDIR),$(BLDDIR)test/,$(subst .cpp,$(OBJ),$(TEST_SOURCES))) $(subst $(SRCDIR),$(OBJDIR),$(subst .cpp,$(OBJ),$(filter-out $(SRCDIR)main.cpp,$(SOURCES)))) $(patsubst ext/fmt/src/%.cc,$(BLDDIR)fmt/obj/%$(OBJ),$(wildcard ext/fmt/src/*.cc))
 #	$(CXX) $(CXXAR) -o$@ $^ $(PIC) $(LDAR)
 
-$(subst $(MANDIR),$(OUTDIR)man/,$(MANPAGE_SOURCES)) : $(MANDIR)index.txt $(MANPAGE_SOURCES)
-	@rm -rf $(dir $@) && mkdir -p $(dir $@)
-	cp $^ $(dir $@)
-	$(RONN) $@
-	$(RONN) -f $@
+$(OUTDIR)man/index.txt : $(MANDIR)index.txt $(patsubst $(MANDIR)%.pp,$(OUTDIR)man/%,$(MANPAGE_SOURCES))
+	@mkdir -p $(dir $@)
+	cp $< $(dir $@)
+	$(RONN) --organization="tzpfms developers"    $(filter-out $<,$^)
+	$(RONN) --organization="tzpfms developers" -f $(filter-out $<,$^)
 
 
 $(OUTDIR)%$(EXE) : $(subst $(SRCDIR),$(OBJDIR),$(subst .cpp,$(OBJ),$(SRCDIR)bin/%.cpp $(COMMON_SOURCES)))
@@ -73,3 +73,7 @@ $(OBJDIR)%$(OBJ) : $(SRCDIR)%.cpp
 $(BLDDIR)test/%$(OBJ) : $(TSTDIR)%.cpp
 	@mkdir -p $(dir $@)
 	$(CXX) $(CXXAR) $(INCAR) -I$(SRCDIR) $(VERAR) -c -o$@ $^
+
+$(OUTDIR)man/%.md : $(MANDIR)%.md.pp $(sort $(wildcard $(MANDIR)*.h))
+	@mkdir -p $(dir $@)
+	$(AWK) '/^#include/ {gsub("\"", "", $$2); while((getline inc < ("$(dir $<)" $$2)) == 1) print inc; next}  {print}' $< > $@
diff --git a/configMakefile b/configMakefile
index 5aaeda9..831ded7 100644
--- a/configMakefile
+++ b/configMakefile
@@ -52,7 +52,6 @@ TZPFMS_VERSION := "0.0.0-$(shell git rev-list HEAD --count)"
 INCCMAKEAR := CXXFLAGS="$(INCCXXAR)"
 LNCMAKEAR := LDFLAGS="$(LNCXXAR)"
 
-LDD ?= ldd
 AWK ?= awk
 RONN ?= ronn
 OBJ := .o
diff --git a/man/backend-tpm2.h b/man/backend-tpm2.h
new file mode 100644
index 0000000..4f83b01
--- /dev/null
+++ b/man/backend-tpm2.h
@@ -0,0 +1,17 @@
+## TPM2 back-end configuration
+
+### Environment variables
+
+  * `TSS2_LOG`=:
+    Any of: *NONE*, *ERROR*, *WARNING*, *INFO*, *DEBUG*, *TRACE*. Default: *WARNING*.
+
+### TPM selection
+
+The library `libtss2-tcti-default.so` can be linked to any of the `libtss2-tcti-*.so` libraries to select the default,
+otherwise `/dev/tpmrm0`, then `/dev/tpm0`, then `localhost:2321` will be tried, in order (see ESYS_CONTEXT(3)).
+
+### See also
+
+The tpm2-tss git repository at <https://github.com/tpm2-software/tpm2-tss> and the documentation at <https://tpm2-tss.readthedocs.io>.
+
+The TPM 2.0 specifications, mainly at &lt;<https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-1-Architecture-01.38.pdf>&gt; and related pages.
diff --git a/man/common.h b/man/common.h
new file mode 100644
index 0000000..f7bd4c8
--- /dev/null
+++ b/man/common.h
@@ -0,0 +1,16 @@
+## AUTHOR
+
+Written by наб &lt;<nabijaczleweli@nabijaczleweli.xyz>&gt;
+
+## SPECIAL THANKS
+
+To all who support further development, in particular:
+
+  * ThePhD
+  * Embark Studios
+
+## REPORTING BUGS
+
+&lt;<https://todo.sr.ht/~nabijaczleweli/tzpfms>&gt;
+
+&lt;<mailto:~nabijaczleweli/tzpfms@lists.sr.ht>&gt;, archived at &lt;<https://lists.sr.ht/~nabijaczleweli/tzpfms>&gt;
diff --git a/man/index.txt b/man/index.txt
new file mode 100644
index 0000000..f83ce9f
--- /dev/null
+++ b/man/index.txt
@@ -0,0 +1,8 @@
+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(8)                  https://manpages.debian.org/bullseye/zfsutils-linux/zfs.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
diff --git a/man/zfs-tpm2-change-key.md.pp b/man/zfs-tpm2-change-key.md.pp
new file mode 100644
index 0000000..06a7f15
--- /dev/null
+++ b/man/zfs-tpm2-change-key.md.pp
@@ -0,0 +1,56 @@
+zfs-tpm2-change-key(8) -- change ZFS dataset key to one stored on the TPM
+=========================================================================
+
+## SYNOPSIS
+
+`zfs-tpm2-change-key` [-b file] <dataset>
+
+## DESCRIPTION
+
+To normalise `dataset`, zfs-tpm2-change-key(8) will open its encryption root in its stead.
+zfs-tpm2-change-key(8) will *never* create or destroy encryption roots; use **zfs(8) change-key** for that.
+
+First, a connection is made to the TPM, which *must* be TPM-2.0-compatible.
+
+If `dataset` was previously encrypted with tzpfms and the *TPM2* back-end was used, the previous key will be freed from the TPM.
+Otherwise, or in case of an error, data required for manual intervention will be printed to the standard error stream.
+
+Next, a new wrapping key is be generated on the TPM, optionally backed up (see [OPTIONS][]),
+and sealed to a persistent object on the TPM under the owner hierarchy.
+
+The following properties are set on `dataset`:
+
+  * `xyz.nabijaczleweli:tzpfms.backend`=`TPM2`
+  * `xyz.nabijaczleweli:tzpfms.key`=*(ID of persistent object)*
+
+`tzpfms.backend` identifies this dataset for work with *TPM2*-back-ended tzpfms tools
+(namely zfs-tpm2-change-key(8), zfs-tpm2-load-key(8), and zfs-tpm2-clear-key(8)).
+
+`tzpfms.key` is an integer representing the sealed object;
+if needed, it can be passed to **tpm2_unseal(1) -c ${tzpfms.key}** or equivalent for back-up (see [OPTIONS][]).
+If you have a sealed key you can access with that or equivalent tool and set both of these properties, it will funxion seamlessly.
+
+Finally, the equivalent of **zfs(8) change-key -o keylocation=prompt -o keyformat=raw dataset** is performed with the new key.
+If an error occurred, best effort is made to clean up the persistent object and properties,
+or to issue a note for manual intervention into the standard error stream.
+
+A final verification should be made by running **zfs-tpm2-load-key(8) -n dataset**.
+If that command succeeds, all is well,
+but otherwise the dataset can be manually rolled back to a password with **zfs-tpm2-clear-key(8) dataset** (or, if that fails to work, **zfs(8) change-key -o keyformat=passphrase dataset**), and you are hereby asked to report a bug, please.
+
+**zfs-tpm2-clear-key(8) dataset** can be used to free the TPM persistent object and go back to using a password.
+
+## OPTIONS
+
+  * `-b` *file*:
+    Save a back-up of the key to *file*, which must not exist beforehand.
+    This back-up **must** be stored securely, off-site.
+    In case of a catastrophic event, the key can be loaded by running **zfs(8) load-key dataset < backup-file**.
+
+#include "backend-tpm2.h"
+
+#include "common.h"
+
+## SEE ALSO
+
+&lt;<https://git.sr.ht/~nabijaczleweli/tzpfms>&gt;
diff --git a/man/zfs-tpm2-clear-key.md.pp b/man/zfs-tpm2-clear-key.md.pp
new file mode 100644
index 0000000..3fb864d
--- /dev/null
+++ b/man/zfs-tpm2-clear-key.md.pp
@@ -0,0 +1,24 @@
+zfs-tpm2-clear-key(8) -- rewrap ZFS dataset key in passsword and clear tzpfms TPM2 metadata
+===========================================================================================
+
+## SYNOPSIS
+
+`zfs-tpm2-clear-key` <dataset>
+
+## DESCRIPTION
+
+zfs-tpm2-clear-key(8), after verifying that `dataset` was encrypted with tzpfms backend *TPM2* will:
+
+  1. perform the equivalent of **zfs(8) change-key -o keylocation=prompt -o keyformat=passphrase dataset**,
+  2. free the sealed key previously used to encrypt `dataset`,
+  3. remove the `xyz.nabijaczleweli:tzpfms.{backend,key}` properties from `dataset`.
+
+See zfs-tpm2-change-key(8) for a detailed description.
+
+#include "backend-tpm2.h"
+
+#include "common.h"
+
+## SEE ALSO
+
+&lt;<https://git.sr.ht/~nabijaczleweli/tzpfms>&gt;
diff --git a/man/zfs-tpm2-load-key.md.pp b/man/zfs-tpm2-load-key.md.pp
new file mode 100644
index 0000000..93f92ae
--- /dev/null
+++ b/man/zfs-tpm2-load-key.md.pp
@@ -0,0 +1,25 @@
+zfs-tpm2-load-key(8) -- load tzpfms TPM2-encrypted ZFS dataset key
+==================================================================
+
+## SYNOPSIS
+
+`zfs-tpm2-load-key` [-n] <dataset>
+
+## DESCRIPTION
+
+zfs-tpm2-load-key(8), after verifying that `dataset` was encrypted with tzpfms backend *TPM2* will unseal the key and load it into `dataset`.
+
+See zfs-tpm2-change-key(8) for a detailed description.
+
+## OPTIONS
+
+  * `-n`:
+    Do a no-op/dry run, can be used even if the key is already loaded. Equivalent to **zfs(8) load-key**'s `-n` option.
+
+#include "backend-tpm2.h"
+
+#include "common.h"
+
+## SEE ALSO
+
+&lt;<https://git.sr.ht/~nabijaczleweli/tzpfms>&gt;
diff --git a/src/bin/zfs-tpm2-change-key.cpp b/src/bin/zfs-tpm2-change-key.cpp
index e69fa4f..cbd0003 100644
--- a/src/bin/zfs-tpm2-change-key.cpp
+++ b/src/bin/zfs-tpm2-change-key.cpp
@@ -62,8 +62,8 @@ int main(int argc, char ** argv) {
 
 		    TRY_MAIN(with_tpm2_session([&](auto tpm2_ctx, auto tpm2_session) {
 			    char *previous_backend{}, *previous_handle_s{};
-			    TRY_MAIN(lookup_userprop(zfs_get_user_props(dataset), PROPNAME_BACKEND, previous_backend));
-			    TRY_MAIN(lookup_userprop(zfs_get_user_props(dataset), PROPNAME_KEY, previous_handle_s));
+			    TRY_MAIN(lookup_userprop(dataset, PROPNAME_BACKEND, previous_backend));
+			    TRY_MAIN(lookup_userprop(dataset, PROPNAME_KEY, previous_handle_s));
 			    if(!!previous_backend ^ !!previous_handle_s)
 				    fprintf(stderr, "Inconsistent tzpfms metadata for %s: back-end is %s, but handle is %s?\n", zfs_get_name(dataset), previous_backend,
 				            previous_handle_s);
@@ -98,6 +98,9 @@ int main(int argc, char ** argv) {
 			    quickscope_wrapper persistent_clearer{[&] {
 				    if(!ok && tpm2_free_persistent(tpm2_ctx, tpm2_session, persistent_handle))
 					    fprintf(stderr, "Couldn't free persistent handle. You might need to run \"tpm2_evictcontrol -c 0x%X\" or equivalent!\n", persistent_handle);
+				    if(!ok && clear_key_props(dataset))  // Sync with zfs-tpm2-clear-key
+					    fprintf(stderr, "You might need to run \"zfs inherit %s %s\" and \"zfs inherit %s %s\"!\n", PROPNAME_BACKEND, zfs_get_name(dataset), PROPNAME_KEY,
+					            zfs_get_name(dataset));
 			    }};
 
 			    TRY_MAIN(set_key_props(dataset, THIS_BACKEND, persistent_handle));
diff --git a/src/bin/zfs-tpm-clear-key.cpp b/src/bin/zfs-tpm2-clear-key.cpp
similarity index 61%
rename from src/bin/zfs-tpm-clear-key.cpp
rename to src/bin/zfs-tpm2-clear-key.cpp
index 9b23197..a85a9d3 100644
--- a/src/bin/zfs-tpm-clear-key.cpp
+++ b/src/bin/zfs-tpm2-clear-key.cpp
@@ -6,19 +6,32 @@
 #include <stdio.h>
 
 #include "../main.hpp"
+#include "../tpm2.hpp"
 #include "../zfs.hpp"
 
 
+#define THIS_BACKEND "TPM2"
+
+
 int main(int argc, char ** argv) {
 	return do_main(
 	    argc, argv, "", [&](auto) {},
 	    [&](auto dataset) {
 		    REQUIRE_KEY_LOADED(dataset);
 
+		    TPMI_DH_PERSISTENT persistent_handle{};
+		    TRY_MAIN(parse_key_props(dataset, THIS_BACKEND, persistent_handle));
+
 		    if(zfs_crypto_rewrap(dataset, TRY_PTR("get clear rewrap args", clear_rewrap_args()), B_FALSE))
 			    return __LINE__;  // Error printed by libzfs
 
-		    if(clear_key_props(dataset)) {
+
+		    TRY_MAIN(with_tpm2_session([&](auto tpm2_ctx, auto tpm2_session) {
+			    TRY_MAIN(tpm2_free_persistent(tpm2_ctx, tpm2_session, persistent_handle));
+			    return 0;
+		    }));
+
+		    if(clear_key_props(dataset)) {  // Sync with zfs-tpm2-change-key
 			    fprintf(stderr, "You might need to run \"zfs inherit %s %s\" and \"zfs inherit %s %s\"!\n", PROPNAME_BACKEND, zfs_get_name(dataset), PROPNAME_KEY,
 			            zfs_get_name(dataset));
 			    return __LINE__;
diff --git a/src/bin/zfs-tpm2-load-key.cpp b/src/bin/zfs-tpm2-load-key.cpp
index dfcd547..415fb80 100644
--- a/src/bin/zfs-tpm2-load-key.cpp
+++ b/src/bin/zfs-tpm2-load-key.cpp
@@ -9,7 +9,6 @@
 
 #include "../fd.hpp"
 #include "../main.hpp"
-#include "../parse.hpp"
 #include "../tpm2.hpp"
 #include "../zfs.hpp"
 
@@ -22,29 +21,8 @@ int main(int argc, char ** argv) {
 	return do_main(
 	    argc, argv, "n", [&](auto) { noop = B_TRUE; },
 	    [&](auto dataset) {
-		    char *backend{}, *handle_s{};
-		    TRY_MAIN(lookup_userprop(zfs_get_user_props(dataset), PROPNAME_BACKEND, backend));
-
-		    if(!backend) {
-			    fprintf(stderr, "Dataset %s not encrypted with tzpfms!\n", zfs_get_name(dataset));
-			    return __LINE__;
-		    }
-		    if(strcmp(backend, THIS_BACKEND)) {
-			    fprintf(stderr, "Dataset %s encrypted with tzpfms back-end %s, but we are %s.\n", zfs_get_name(dataset), backend, THIS_BACKEND);
-			    return __LINE__;
-		    }
-
-		    TRY_MAIN(lookup_userprop(zfs_get_user_props(dataset), PROPNAME_KEY, handle_s));
-		    if(!handle_s) {
-			    fprintf(stderr, "Dataset %s missing key data.\n", zfs_get_name(dataset));
-			    return __LINE__;
-		    }
-
 		    TPMI_DH_PERSISTENT handle{};
-		    if(parse_int(handle_s, handle)) {
-			    fprintf(stderr, "Dataset %s's handle %s not valid.\n", zfs_get_name(dataset), handle_s);
-			    return __LINE__;
-		    }
+		    TRY_MAIN(parse_key_props(dataset, THIS_BACKEND, handle));
 
 
 		    uint8_t wrap_key[WRAPPING_KEY_LEN];
diff --git a/src/parse.hpp b/src/parse.hpp
index fb5e675..7eee9e1 100644
--- a/src/parse.hpp
+++ b/src/parse.hpp
@@ -6,6 +6,7 @@
 
 #include <charconv>
 #include <stdio.h>
+#include <string.h>
 
 
 template <class T>
diff --git a/src/tpm2.hpp b/src/tpm2.hpp
index 9fa7b47..6f97f35 100644
--- a/src/tpm2.hpp
+++ b/src/tpm2.hpp
@@ -6,6 +6,8 @@
 
 #include "common.hpp"
 
+#include <libzfs.h>
+
 #include <tss2/tss2_common.h>
 #include <tss2/tss2_esys.h>
 #include <tss2/tss2_rc.h>
diff --git a/src/zfs.cpp b/src/zfs.cpp
index 4fa9858..58e6ab5 100644
--- a/src/zfs.cpp
+++ b/src/zfs.cpp
@@ -3,9 +3,13 @@
 
 #include "zfs.hpp"
 #include "common.hpp"
+#include "main.hpp"
+#include "parse.hpp"
 
 #include <libzfs.h>
 
+#include <string.h>
+
 
 // Funxion statics pull in libc++'s __cxa_guard_acquire()
 static nvlist_t * rrargs{};
@@ -59,14 +63,19 @@ nvlist_t * clear_rewrap_args() {
 		TRY_NVL(what, _try_retl);             \
 	})
 
-// TODO: how does this interact with nested datasets?
-int lookup_userprop(nvlist_t * from, const char * name, char *& out) {
+int lookup_userprop(zfs_handle_t * in, const char * name, char *& out) {
 	// xyz.nabijaczleweli:tzpfms.key:
 	//   value: '76B0286BEB3FAF57536C47D9A2BAD38157FD522A75A59E72867BBFD6AF167395'
 	//   source: 'owo/enc'
 
 	nvlist_t * vs{};
-	TRY_LOOKUP("look up user property", nvlist_lookup_nvlist(from, name, &vs));
+	TRY_LOOKUP("look up user property", nvlist_lookup_nvlist(zfs_get_user_props(in), name, &vs));
+
+	char * source{};
+	TRY_LOOKUP("look up user property source", nvlist_lookup_string(vs, "source", &source));
+	if(!source || strcmp(source, zfs_get_name(in)))
+		return 0;
+
 	TRY_LOOKUP("look up user property value", nvlist_lookup_string(vs, "value", &out));
 	return 0;
 }
@@ -98,3 +107,31 @@ int clear_key_props(zfs_handle_t * from) {
 	TRY("delete tzpfms.key", zfs_prop_inherit(from, PROPNAME_KEY, B_FALSE));
 	return 0;
 }
+
+
+int parse_key_props(zfs_handle_t * in, const char * our_backend, uint32_t & handle) {
+	char *backend{}, *handle_s{};
+	TRY_MAIN(lookup_userprop(in, PROPNAME_BACKEND, backend));
+
+	if(!backend) {
+		fprintf(stderr, "Dataset %s not encrypted with tzpfms!\n", zfs_get_name(in));
+		return __LINE__;
+	}
+	if(strcmp(backend, our_backend)) {
+		fprintf(stderr, "Dataset %s encrypted with tzpfms back-end %s, but we are %s.\n", zfs_get_name(in), backend, our_backend);
+		return __LINE__;
+	}
+
+	TRY_MAIN(lookup_userprop(in, PROPNAME_KEY, handle_s));
+	if(!handle_s) {
+		fprintf(stderr, "Dataset %s missing key data.\n", zfs_get_name(in));
+		return __LINE__;
+	}
+
+	if(parse_int(handle_s, handle)) {
+		fprintf(stderr, "Dataset %s's handle %s not valid.\n", zfs_get_name(in), handle_s);
+		return __LINE__;
+	}
+
+	return 0;
+}
diff --git a/src/zfs.hpp b/src/zfs.hpp
index 8324569..74f7d4b 100644
--- a/src/zfs.hpp
+++ b/src/zfs.hpp
@@ -6,9 +6,6 @@
 
 #include <libzfs.h>
 #include <sys/nvpair.h>
-// #include <sys/fs/zfs.h>
-//// #include <sys/zio_crypt.h>
-// #define WRAPPING_KEY_LEN 32
 
 
 #define TRY_NVL(what, ...) TRY_GENERIC(what, , , _try_ret, _try_ret, strerror, __VA_ARGS__)
@@ -36,10 +33,13 @@ extern nvlist_t * clear_rewrap_args();
 /// Extract user property name from ZFS property list from to out.
 ///
 /// Returns success but does not touch out on not found.
-extern int lookup_userprop(nvlist_t * from, const char * name, char *& out);
+extern int lookup_userprop(zfs_handle_t * from, const char * name, char *& out);
 
 /// Set required decoding props on the dataset
 extern int set_key_props(zfs_handle_t * on, const char * backend, uint32_t handle);
 
 /// Remove decoding props from the dataset
 extern int clear_key_props(zfs_handle_t * from);
+
+/// Read in decoding props from the dataset
+extern int parse_key_props(zfs_handle_t * in, const char * our_backend, uint32_t & handle);