@encthenet i really appreciated @astraleureka helping me to realize how difficult the block-level encryption problem is even if you allow yourself to have extra data per block somehow. i had especially been hoping to have distinct keys/key schedules used across the fs somehow, but i was quickly convinced that there's really no way to incorporate an additional trust root when the filesystem is the source of truth.
my entire purpose (which i believe merits both a filesystem and a kernel) is to make filesystem visibility into an analogue of virtual memory, i.e. processes live in their own chroot by default (all i/o is scoped to a named synchronization domain) and extending POSIX to incorporate structured mechanisms for explicit data flow (mostly in the form of "i/o transactions"). after write(), an i/o transaction call can progress the state machine (blocking or nonblocking) to synchronize the written data with another domain and/or to persist it to disk.
in particular i think separating the act of persistence from normal writes and shared file visibility offers an opportunity to employ cryptographic keys, as a way to serialize access to some sort of runtime capability. of course, if a cryptographic key to access secret data is written to disk in any form, then it is visible to an attacker--so that key can be encrypted with another key, making use of the structured supervisor hierarchy.
to answer your question:
What do you do when the key to your data leaks? How do you rotate it? How do you make sure that data encrypted with the old key is deleted?
i don't at all have the answers to this yet, but i think separating writes and visibility from persistence is a really important step. consider my (less difficult) case above, where a cryptographic key is used to encode access to some sort of runtime capability in a way that persists across shutdown. in that particular case, every time the process starts up, it can exchange its key for the capability, and then it can atomically regenerate a new key and ensure that one is persisted.
i'm obviously cheating and handwaving to an immense degree here, but i think that a graph of recursive supervisor processes (down from init) which can locate their persisted state and transform it into runtime state upon startup can define a tree of trust relationships which avoids deep structural dependence upon a particular cipher or master key