I wanted to solve what sounded like a fairly simple problem:
Open an encrypted .docx in Microsoft Word without ever writing the decrypted document to disk.
The ideal flow looked like this:
encrypted file
↓
decrypt requested bytes
↓
Windows virtual file
↓
Microsoft Word
Plaintext would exist only in memory.
No temporary .docx.
No cleanup problem.
No plaintext sitting somewhere on disk waiting to be copied.
Windows Cloud Files API seemed almost perfect for this.
It lets you create placeholder files whose actual content is provided on demand. When an application asks for some bytes, your provider gets a callback and returns exactly that range.
Conceptually:
fn fetch_data(offset: usize, length: usize) {
let end = (offset + length).min(document.len());
provide_to_windows(
offset,
&document[offset..end],
);
}
So I built a small test.
The placeholder contained no document data. The provider kept the real bytes in memory and served them when Windows requested them.
Then I opened the file in real Microsoft Word.
First attempt: full hydration
In the normal hydration mode, everything worked.
Word requested the file:
offset: 0
length: 13400
process: WINWORD.EXE
The provider returned the bytes.
Word opened the document successfully.
Great.
Except Windows had now written the hydrated contents to disk.
After stopping the provider entirely, the file was still readable as a normal .docx.
So the architecture had effectively become:
encrypted file
↓
decrypt
↓
plaintext file on disk
↓
Word
Which was exactly what I was trying to avoid.
Second attempt: streaming
Cloud Files also has a streaming mode.
In this mode, the application can request data without permanently hydrating the placeholder.
That sounded much better.
A normal sequential reader worked:
ReadAllBytes()
→ 13400 bytes
→ success
The plaintext never became a normal file on disk.
Then I tried Word.
It failed.
Word experienced an error trying to open the file
The interesting part was that Word did request the data, and the provider successfully returned it.
So the problem wasn't that Word couldn't read the bytes.
The problem was how Word wanted to access them.
Word doesn't just read the file
A simple application might behave roughly like this:
open()
read()
read()
read()
That works perfectly with streaming placeholders.
Word does something more complicated.
It needs a representation of the file that can be backed by a Windows section object / memory mapping.
And Windows cannot create that mapping over file contents that only exist as temporary streamed responses.
The difference is roughly:
Sequential read:
ReadFile()
↓
provider
↓
bytes
↓
works
versus:
Word:
open file
↓
create mapped section
↓
requires locally backed data
↓
streaming placeholder
↓
fails
I reproduced the same behavior without Word.
Sequential reading worked.
Trying to create a mapped representation of the same file failed.
At that point it was clear this wasn't just a Word quirk.
The storage model itself was incompatible with what the application expected.
The practical solution
The compromise was:
1. Hydrate the plaintext file
2. Open it in Word
3. Keep it hydrated only for the active session
4. Dehydrate it when the session ends
Windows provides CfDehydratePlaceholder, which can return the file to its placeholder state.
So the final flow became:
encrypted document
↓
temporary hydration
↓
Microsoft Word
↓
document closes
↓
dehydrate
↓
plaintext removed
This worked.
But it changed the security model.
The guarantee was no longer:
plaintext never touches disk
It became:
plaintext exists on disk only inside a controlled working session and is removed afterward.
And that creates another problem.
Crashes matter
This is not enough:
on_document_close(|| {
dehydrate(path)?;
});
Because sometimes there is no clean close.
The machine can lose power.
The provider can crash.
The process can be killed.
So recovery has to be part of the design:
fn recover() {
for file in leftover_hydrated_files() {
dehydrate(file);
}
}
That was probably the most useful realization from the experiment:
cleanup isn't just cleanup when plaintext is involved.
It becomes part of the security model.
The larger lesson
The original idea was much prettier:
plaintext exists only in RAM
Unfortunately, software you don't control may make assumptions your architecture cannot satisfy.
And sometimes the right response isn't another workaround.
It's changing the assumption.
For applications that perform ordinary streaming reads, an in-memory delivery path can still work.
For applications like Word, the safer practical model is a temporary protected working session with explicit cleanup and crash recovery.
The experiment turned this:
protect the file
into this:
protect the lifetime of the plaintext
Not as elegant.
But much more realistic.
Top comments (0)