Skip to content
machinemail

Classifier

How the two-stage injection and phishing classifier scores inbound mail, rules first and a hosted model second, and what each probability, the category and the backend field mean.

The classifier fills trust.classification: five probabilities, a category and a language. It runs in two stages. Rules score every message, then a hosted decision model judges it, and the two are merged so that hard evidence from the rules can’t be talked away.

json
"classification": {
  "is_spam": 0.02,
  "is_phishing": 0.02,
  "contains_instructions_to_agent": 0.98,
  "requests_credentials_or_payment": 0.02,
  "urgency": 0.047,
  "category": "other",
  "language": "en",
  "backend": "heuristic"
}

What the numbers mean

Each probability answers one question about the message:

FieldQuestion
contains_instructions_to_agentDoes it contain prompt injection: text aimed at an AI assistant or agent reading the mailbox, trying to make it act, change its behaviour, reveal data or ignore its rules? Hidden text counts. Ordinary requests to the human recipient don’t, and neither does text that only discusses prompt injection.
is_phishingDoes it pretend to be a trusted person, brand or service in order to steal credentials, money, codes or access?
requests_credentials_or_paymentDoes it ask for a password, one-time code, API key or other secret, a sign-in through a link, money, gift cards, an invoice payment, or a change of bank details? Delivering a code to you, or a receipt for a completed payment, does not count.
is_spamIs it unsolicited bulk or commercial mail? Subscribed newsletters, receipts and notifications are not spam.
urgencyHow much time pressure does it put on the reader?

Values run from 0.02 to 0.98: the classifier never claims certainty. Read them as confidence, not as calibrated frequencies. They matter most at the thresholds the trust layer acts on:

ThresholdEffect
is_phishing or contains_instructions_to_agent at 0.8 or abovelevel is quarantined
any of is_spam, is_phishing, contains_instructions_to_agent, requests_credentials_or_payment at 0.5 or abovelevel is at least suspicious
is_spam at 0.9 or aboveSPAM label instead of INBOX

urgency never changes the level. It is there so an agent can notice pressure tactics, and it is reduced for one-time codes, which expire by design.

The probabilities also make up 35% of the score: the content component is 1 minus the largest of contains_instructions_to_agent, is_phishing, 0.8 × is_spam and 0.6 × requests_credentials_or_payment.

Stage one: rules

The rules stage runs on every message, in-process, with no network calls. It is deterministic: the same message always gets the same scores. Each probability is a logistic function of weighted signals, and every signal that fires is written to trust.reasons as Classifier <target>: <description>, or Classifier <target> (reduced): ... when it lowered the score.

It looks at the agent-safe text and subject, the hidden text, text extracted from attachments, headers, the link and attachment reports, authentication results and the sender relationship.

Injection goes up for the agent-directed patterns described in Content scrubbing: instructions found in visible text, much more for the same in hidden text, instructions in attachments, words split with zero-width characters, Unicode tag characters carrying hidden text, and hidden text in a message that also addresses an AI. Quoted phrases count for less. Visible instructions from an authenticated sender you correspond with count for slightly less.

Credentials or payment goes up for requests to enter or send a password, PIN, card details or one-time code, to verify an account, and to pay, wire money or change bank details. Phrases preceded by a negation (“we will never ask for your password”) don’t count, and receipts for completed payments count against it.

Phishing goes up for credential requests, threats of account suspension, claims of unusual activity, “click to verify” wording, mailbox-quota and password-expiry lures, business-email-compromise phrasing, mismatched and suspicious links, a display name that claims a brand the sending domain doesn’t belong to, failed authentication, a Reply-To on a different domain (especially a free mail provider), a sender claiming to be your own domain without authentication, and HTML or other high-risk attachments. It goes down for an aligned DMARC pass, prior correspondence and allow-listed senders.

Spam goes up for bulk-mail headers from senders you haven’t written to, marketing and spam vocabulary, excessive capitals and exclamation marks, many links, tracking pixels and failed authentication. It goes down for authenticated transactional, notification and one-time-code mail, prior correspondence, and very strongly for allow-listed senders.

Urgency goes up for deadlines, expiry wording and urgent subject lines.

Stage two: a hosted model

A hosted decision model then reads a rendering of the message: subject, sender, authentication results, link and attachment findings, hidden text, the body and text extracted from attachments, cut to a fixed length if the message is very long. It answers the same five questions, plus category and language.

The two stages are merged:

  • The model’s answers replace the rules’ answers.
  • Except where the rules found hard evidence: any probability the rules put at 0.9 or higher stays, if the model scored it lower. Hidden injection text and chat-template tokens are examples. The reasons then include kept heuristic <field> <value> over <model> <value> (hard evidence).
  • The model’s answers are added to reasons on one line: <model>: injection 0.97, phishing 0.04, spam 0.03, credentials/payment 0.05, urgency 0.10.

So the model can catch what the rules miss, such as a polite, novel injection, and can clear what the rules over-flag, such as a newsletter about prompt injection. It can’t override a hidden instruction.

The backend field

backend says which stage produced the final numbers:

ValueMeaning
heuristicThe rules alone.
a model nameThe hosted model judged the message, merged with the rules as above.
heuristic (fallback: <reason>)The model was called but failed or timed out, so the rules’ scores stand. Mail is never held up waiting for a model.

Treat a fallback report like any other: the rules are conservative about hard evidence, so hidden injections are still quarantined.

Category

category is one of:

CategoryTypical mail
otpA short message carrying a one-time code.
transactionalReceipts, invoices, order, shipping, booking and refund confirmations.
notificationAutomated notices: GitHub, CI, alerts, “no-reply” senders, Auto-Submitted mail.
newsletterBulk mail with List-Unsubscribe, Precedence: bulk or list headers.
conversationPerson-to-person mail, replies, people you’ve written to.
bounceDelivery failure reports.
otherNothing matched strongly.

Category is descriptive. It does not change the level, except that authenticated otp, transactional and notification mail is less likely to be scored as spam.

Language

language is the ISO 639-1 code of the body’s main language when it can be told with confidence: en, es, fr, de, pt, it or nl. For other languages, or very short messages, it is left out.

What the classifier is not

  • It is not a guarantee. Novel attacks get through any classifier; that is why the level also depends on authentication and sender history, and why agents should gate actions, not just reading. See Using trust in agents.
  • It does not remove content. Visible instructions stay in content.text; the classifier only tells you they are there.
  • It does not see your agent’s prompt. It judges the message on its own, so “ignore previous instructions” is flagged whether or not your agent has any.