// BEC defense · the hard-stop firewall

Stop BEC by refusing the action, not just reading the mail.

Business email compromise is an irreversible-action problem, not a spam problem. The loss happens the moment someone — or an autonomous agent acting for them — is socially engineered into sending a wire, changing where money is remitted, or replying to a strangerwith sensitive data. RadMail's answer is a deterministic firewall that removes those dangerous action classes from what an autonomous agent can do at all.

RadMail is a tool, not a guarantee. It refuses the exact action classes that cause BEC losses; it does not promise that no fraud can ever occur, and it claims no detection rate. RadMail is pre-release, with its engine live in a test bed on two real businesses; the public MCP server runs the heuristic sandbox engine.

Why a smarter filter doesn't stop it.

BEC mail is often perfectly deliverable — from a real, compromised, or look-alike account, and grammatically clean — so a "is this spam?" score does nothing. The malicious content is the same content the agent is asked to read and reason about, which turns the classic BEC playbook (urgency, authority, a plausible pretext) into a prompt-injection attack against the agent. You cannot make these actions safe by classifying them more accurately. You make them safe by refusing to let an autonomous process perform them.

The action classes RadMail keeps human-only.

These are human-only, forever — no RadMail tool auto-executes them, at any tenant, at any rollout stage. The firewall fails closed: if the risk evaluation was not performed, the answer is hard-stop, not send.

hard-stop :: human-only action classes
  • Moving moneyApproving a wire, paying an invoice, an ACH or remittance instruction — any movement of money.
  • Changing banking detailsThe highest-yield pattern: 'we've updated our bank account, send future payments here.' One accepted change redirects every future payment.
  • Decisions that commit the accountApprove, sign off, authorize, or go-ahead on an order, PO, contract, or payment.
  • Contacting a third partyBeing steered into looping in, or disclosing to, someone outside the known counterparty — including a first-contact reply to a new party.
  • Releasing a deliverableSending out a document, credential, or asset.

The only action ever eligible to auto-send is a self-directed follow_up— a "did you get my last note?" check-in that moves no money and commits nothing — and even that requires a known counterparty at an allowlisted domain, and is blocked outright if the source mail carries a money, new-banking, decision, or injection signal.

What an agent connected to RadMail cannot do.

The MCP server exposes only read, triage, explain, list-commitments, and draft tools. There is no tool that does any of the following — by design, as the BEC defense:

How the hard-stop is enforced.

RadMail's MCP server reports an enforcement model of capability-absent. The distinction matters more than the promise does, so the whole vocabulary is published rather than just our own value:

enforcement model :: how a refusal is actually held
  • capability-absent ← RadMailThe tool that would perform the forbidden action does not exist on the server, so it cannot be called. There is nothing to configure and nothing to bypass.
  • config-restrictedThe capability exists and is narrowed by configuration — an allowlist, a scope, a policy file. Whoever holds the configuration can widen it again.
  • policy-gatedThe capability exists and is restrained by instructions, prompts, or documented policy that a model is asked to follow.

There is no setting, feature flag, plan tier, environment variable, or admin toggle on RadMail's MCP server that enables a forbidden action. The only behaviour-widening environment variables are RADMAIL_API_KEY and RADMAIL_API_URL, which unlock connected mode — and connected mode is read-only: it widens what an agent can READ, never what it can do. Enabling a forbidden action would require shipping a tool that does not exist.

The five classes RadMail refuses to even draft.

Separate from the no-send guarantee, and stronger than it — for these classes the package's draft_reply produces no draft at all and routes the message to a human:

Stronger than the no-send guarantee, and separate from it: for these five classes the radmail-mcp package's draft_reply refuses to produce a draft AT ALL and routes the message to a human, rather than drafting something a human must then catch. `decision` and `injection` are the two that the forbiddenActions list above does not cover, because they are not send-shaped — they are classes where even a proposed reply is withheld. Scope: the radmail-mcp package (the production stdio server), whose tool descriptions are frozen under a fail-closed sha256 manifest since 0.5.0 — so this is verifiable by reading the tool list back, or in the MIT source. The zero-auth hosted sandbox serves a fixed demo inbox and accepts no caller-supplied message, so it cannot be used to exercise these refusals.

Don't trust this page — check it.

Do not take this contract's word for it — call the server and read the tool list back. Every tool is read, triage, search, explain, draft, or read-only diagnostic. If you find one that sends money, changes banking, or makes first contact, this contract is wrong and security@radmail.ai wants to hear about it. The same facts are published as a machine-readable contract at https://radmail.ai/.well-known/agent-safety.json, which carries these commands and the expected result.

verify :: hosted sandbox — no account, no key
curl -s -X POST https://radmail.ai/api/mcp/sandbox -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

Expect: A tool list containing no send-capable tool. draft_reply produces a draft for a human to review and send; it does not send.

verify :: radmail-mcp package — where the refusal classes live
printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"verify","version":"1"}}}' '{"jsonrpc":"2.0","method":"notifications/initialized"}' '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' | npx -y radmail-mcp

The command above checks the hosted sandbox, which serves 6 core triage tools over a fixed demo inbox. The `draftRefusalClasses` guarantee and the frozen tool manifest are scoped to the radmail-mcp PACKAGE — a larger surface the sandbox does not expose — so verify that tier directly rather than inferring it from the sandbox. This is the harder surface to check and it is the one the stronger claims live on.

Tamper-evidence: Since 0.5.0 the package's tool names, descriptions, and input schemas are frozen under a sha256 manifest that the server recomputes at startup and refuses to serve on any mismatch — a fail-closed defense against MCP tool-description poisoning. That means the tool list you read back is the audited text or the server does not answer at all. Source (MIT, public): https://github.com/radmail-ai/radmail-mcp

Common questions.

What is business email compromise (BEC)?

Business email compromise is a fraud where an attacker uses email — often from a real, compromised, or look-alike account — to socially engineer someone into taking an irreversible action: sending a wire, changing where payments are remitted, approving a purchase, or replying to a stranger with sensitive data. The message is only the delivery vehicle; the loss happens at the moment of the action taken in response, which is why a clean, deliverable email can still cause a large loss.

tl;dr BEC is an irreversible-action problem, not a spam problem.

How does RadMail stop BEC and wire fraud?

RadMail's firewall removes the dangerous action classes from what an autonomous agent can do at all. Moving money, changing banking or wire instructions, decisions that commit the account, contacting a new third party, and releasing a deliverable are human-only: the firewall will never return an auto-sendable reply for them, at any tenant, at any rollout stage. It refuses the exact action classes that cause the losses rather than trying to score intent more accurately — so a persuasive email cannot talk an agent into the dangerous action, because the agent was never able to perform it.

tl;dr It refuses the irreversible action classes outright — human-only, forever.

Why can't you just detect BEC with a smarter spam filter?

Because BEC mail is often perfectly deliverable, from a real or look-alike account, and grammatically clean, so a 'is this spam?' score does nothing. The malicious content is the same content the agent is asked to read and reason about, which turns the classic BEC playbook — urgency, authority, a plausible pretext — into a prompt-injection attack against the agent. RadMail's premise is that you cannot make these actions safe by classifying them more accurately; you make them safe by refusing to let an autonomous process perform them.

What about a changed-banking-details email?

A changed-banking-details request is the single highest-yield BEC pattern — one accepted banking change redirects every future payment to the attacker. RadMail treats any new-banking signal as a permanent hard-stop: the agent will never auto-send a reply that acts on it, and a change-of-bank instruction surfaces to a human to review. This holds even for a well-known, long-trusted sender, because a known counterparty is never a key that unlocks money or banking changes.

tl;dr Changed banking is a permanent hard-stop, even from a trusted sender.

Can an agent connected to RadMail be tricked into sending money?

No auto-send tool for money exists to be tricked. The safety decision is deterministic — not a confidence threshold and not delegated to a model — and it fails closed: if the risk evaluation was not performed, the answer is hard-stop, not send. Obfuscation such as zero-width characters or base64-encoded payloads is normalized before scanning, so hiding the instruction cannot buy a bypass. A model drafts language; it cannot talk the firewall into sending.

tl;dr The high-risk send is not exposed as a tool at all, and the system fails closed.

How is RadMail's hard-stop enforced — is it an allowlist, a setting, or something else?

None of those. RadMail's MCP server reports an enforcement model of "capability-absent", which means the tool that would perform the forbidden action does not exist on the server, so it cannot be called — there is nothing to configure and nothing to bypass. That is a different mechanism from a "config-restricted" model, where the capability exists and is narrowed by an allowlist, a scope, or a policy file that whoever holds the configuration can widen again; and different again from a "policy-gated" model, where the capability exists and is restrained by instructions a model is asked to follow. There is no setting, feature flag, plan tier, environment variable, or admin toggle on RadMail's MCP server that enables a forbidden action. The only behaviour-widening environment variables are RADMAIL_API_KEY and RADMAIL_API_URL, which unlock connected mode — and connected mode is read-only: it widens what an agent can READ, never what it can do. Enabling a forbidden action would require shipping a tool that does not exist.

tl;dr The capability is absent, not restrained — there is no setting that turns a forbidden action back on.

Are there messages RadMail refuses to even draft a reply for?

Yes, and this is separate from — and stronger than — the no-send guarantee. For five classes (money, changed-banking, first-contact, decision, injection) the radmail-mcp package's draft_reply refuses to produce a draft at all and routes the message to a human, rather than drafting something a human then has to catch. Two of those five, "decision" and "injection", are not send-shaped, so they sit outside the forbidden-action list: they are classes where even a proposed reply is withheld. Scope matters and is published: this applies to the radmail-mcp package (the production stdio server), whose tool descriptions are frozen under a fail-closed sha256 manifest since 0.5.0. The zero-auth hosted sandbox serves a fixed demo inbox and accepts no caller-supplied message, so it cannot be used to exercise these refusals.

tl;dr Five classes get no draft at all — including two that are not send-shaped.

How can I verify the hard-stop instead of taking RadMail's word for it?

Call the server and read the tool list back. RadMail publishes a machine-readable safety contract at https://radmail.ai/.well-known/agent-safety.json that carries the forbidden actions, the enforcement model, the draft-refusal classes, and the exact commands to check them. Do not take this contract's word for it — call the server and read the tool list back. Every tool is read, triage, search, explain, draft, or read-only diagnostic. If you find one that sends money, changes banking, or makes first contact, this contract is wrong and security@radmail.ai wants to hear about it. The hosted sandbox is checkable with a single unauthenticated HTTP request and needs no account or key; the radmail-mcp package, where the draft-refusal classes and the frozen tool manifest are scoped, is checkable by piping a tools/list request into npx. If either list ever contains a tool that sends money, changes banking, or makes first contact, the contract is wrong.

tl;dr The claim is designed to be disproven by execution, not trusted — the contract publishes the commands.

Is RadMail fraud-proof or a guarantee against BEC?

No. RadMail is a tool, not a guarantee. It narrows what an autonomous agent can be tricked into doing by removing the dangerous action classes from its reach; it does not certify that fraud is impossible or claim any detection rate. It does not replace your own controls — bank-side call-back verification for banking changes, dual authorization for wires, SPF/DKIM/DMARC, and human judgment on the surfaced drafts remain your responsibility. Compliance is a shared responsibility, and RadMail is pre-release with its engine live in a two-business test bed.

tl;dr A tool that shrinks the blast radius — not a guarantee, and not a replacement for your controls.

See the hard-stop refuse a send. The public magic-moment shows RadMail visibly refusing a money / new-banking / first-contact send — no signup, no credentials.

› Start free — no card
radmail@inbox:~$ disposition --money --new-banking --first-contact = hard_stop