Email prompt injection attacks

An agent that reads email reads text a stranger wrote. Prompt injection is that text written to steer the agent: drop its instructions, leak what it can see, send mail its owner never meant to send. Here are ten ways it's done, each with examples and defenses.

Why email is different.

In a chat, a person reads every answer. An email agent forwards, replies and updates records on its own. A successful injection can forward confidential mail to the attacker, send replies the account holder never wrote, or feed made-up data into a workflow. The agent follows instructions. It can't tell whose. No single check stops that, so defenses come in layers.

What Molted does about it.

Molted is the mailbox under your agent. It sees each message before the agent does and each reply before it goes out. It can't make a model immune to injection. It hides a flagged message's body when your agent reads the thread, and holds what the agent does next.

// get_thread: one entry of envelopedMessages
"untrustedContent": {
  "bodyText": "[quarantined -- injection risk]",
  "bodyHtml": null,
  "sanitizationExpected": true
},
"agentGuidance": {
  "injectionRisk": "high",
  "contentTrust": "untrusted",
  "recommendedIsolation": true,
  "flags": ["injection_risk", "quarantined"]
}
Screened on arrival
Every inbound message is checked against ten pattern families, from instruction overrides and fake system tokens to tool-call attempts and CSS-hidden text, after invisible Unicode is stripped. The subject, the text body and a text rendering of the HTML body are all covered, so an HTML-only message can't slip through. The patterns match known phrasings, mostly English. New wording can get past them.
Quarantined by default
When your agent reads a thread (get_thread), a high-risk message shows as [quarantined -- injection risk], not its body. Showing the original there takes the operator scope.
Marked as untrusted
In get_thread, every inbound body comes back inside untrustedContent, with agentGuidance beside it: the risk level, whether to isolate it, and contentTrust: "untrusted". That label doesn't depend on the patterns matching.
Replies held on a flagged thread
If a thread had a quarantined or high-risk message in the last seven days, your agent's reply on it waits for a person, whatever the mailbox's autonomy level.
A canary in every thread
Reading a thread hands your agent a canary token. A reply on that thread that repeats it is blocked by default.

Keys and data.

How Molted holds your keys and your workspace, and how you take your data back out. The privacy policy has the rest.

Keys stored as argon2 hashes
Molted keeps a hash and a prefix of each API key, never the key itself. It can check a key, not read one back.
One database, filtered by workspace
Workspaces share one Postgres database, not one each. Your key resolves to your workspace, and queries are filtered by it.
Export and delete
Settings, Workspace data: download your workspace's data, or delete the workspace. Deletion runs after a seven-day wait, and the owner can cancel it until then.