serde_mask
Mask sensitive data during serde serialization for LLM ingestion, with secrets attached to the object, not a separate struct like expunge does.
The crate intercepts serde serialization and substitutes sensitive values with placeholders. A deanonymize method on the struct lets you replace placeholders in LLM responses with the original values.
Usage
use ;
let q = Query ;
// Serializes with secret masked
let json = to_string.unwrap;
// {"secret":"ANON_123456","public":"visible"}
// LLM responds with something like "Contact user ANON_123456 immediately."
// then we deanonymize it back to "Contact user my_secret_username immediately."
let response = "Contact user ANON_123456 immediately.";
let restored = q.deanonymize;
// "Contact user my_secret_username immediately."
Custom types
Implement AnonymizeTrait for your own types:
use AnonymizeTrait;
;
Design notes
The state is stored in a OnceLock field on the struct itself. This means the anonymization mapping is computed once on first serialize and reused, so deanonymize always uses the same placeholders that were serialized.
Alternative approaches considered:
- Separate derived struct (like snafu/expunge): more complex, requires conversion
- Thread locals: doesn't work well with async
Crate is breaking invariant of types, for example:
age type would get converted to String type at serialization. For JSON serde would wrap the value with double quotes, but I bet LLM wouldn't notice.