What spamex stores, and why hashes are enough
A spam filter needs memory to work. It does not need your visitors' messages. How the storage is designed so the default is privacy.
A filter with no memory judges every message from scratch. A filter with memory notices that the message it is looking at was sent to sixty other sites yesterday. Memory is where most of the value is, and also where most of the privacy risk is. spamex is designed so the memory is useful without holding the text.
What is kept by default
For every classification:
- a SHA-256 hash of the normalised text, which lets us recognise the exact same message again without storing it;
- a 64-bit SimHash fingerprint, which lets us recognise a near-identical message (a template with the site name swapped in) within a few bits of difference;
- the verdict: label, score, stage, reason codes, latency;
- hashed sender identifiers: SHA-256 of the IP and of the email address, plus a masked display form like
203.0.113.x, and decayed spam/ham counters attached to each; - a usage count for your key and month.
Not kept: the name, the email address in clear, the subject, the message, the user agent.
What a hash cannot do
A hash cannot be reversed into the message. It cannot be searched for a phrase. It cannot be joined with another dataset to identify a person, beyond “the same string appeared”. The SimHash is 64 bits derived from word statistics; it can tell you two messages are similar and nothing about what they say.
Reputation counters are numbers attached to hashes. They can say “this hashed address has been confidently spam eleven times in the last two weeks across three sites”. They cannot say who the address belongs to.
When raw text is kept
Some customers want more: to review verdicts with the actual message in front of them, and to have spamex learn from their corrected examples. That requires storing the text, so it is a per-key setting (retain_raw_text), off by default, and it applies only to that key’s traffic. A customer who turns it on can delete any single stored submission with one API call, and the deletion also removes the sender keys and fingerprints for that record.
The one external call
Gray-zone messages, about one in twenty, are sent to a language-model provider for a verdict. We send the fields needed to classify (name, email, subject, message, and the customer’s site description) and nothing else: no IP, no user agent, no customer identity. The provider’s API terms govern that transfer; where the provider offers a control against training on API data, it is used.
Why this design
Because the default should be the safe one. A customer who does nothing but paste a key into their form handler should end up with a filter that remembers patterns and forgets people. Everything more is a choice they make, per key, with a deletion path.
The details, including retention periods and sub-processors, are in the privacy policy.