← Work · 05 / Security · email

Inconclusive

Screens recruiter mail for fraud, and says so when it can't tell. The third answer is the point.

7
signals, none decisive alone
0
links opened
three
outcomes, not two
Python · Gmail API · RDAP · VirusTotal · SQLite · Claudeplanned[ year ][ repo ]
A verdict note, one screenshot The companion note the tool drops in the inbox: the score, the signals that fired, and the ones that didn't. Use an inconclusive case, not a clean catch — the hedge is more interesting than the hit.
The evidence it gathered, and what it refused to conclude from it.
01

The problem

Job seekers are worth targeting. Someone actively applying opens recruiter mail from senders they don't recognise, follows links to portals they've never used, and is primed to believe a good offer arrived unprompted. The scams are built for exactly that: a spoofed sender, a company that half exists, and a fee for equipment payable before the first pay cheque.

Filtering it looks like a spam problem, and it isn't. Spam filters are tuned against false negatives — let junk through and someone deletes it. Here the asymmetry runs the other way. A phishing email that slips past costs a bad afternoon. A real offer flagged as fraud during a job hunt costs the job.

So the expensive mistake isn't missing a scam. It's being confidently wrong about a real one.

02

What changed

BeforeAfter
JudgementRead it and guessSeven signals combined into a band
Any one flagEnough to condemnWeighted; none is decisive alone
New companiesYoung domain reads as fraudWeighted low, trusted list overrides
LinksClicked to checkAnalysed, never opened
DeletionPermanentTrash only — the scope excludes delete
OutboundReply to checkDrafted for a human to send

Refusing to let any single signal decide is what made the rest usable. A brand-new company with a three-week-old domain and no LinkedIn footprint is the exact profile of a real early-stage startup and the exact profile of a scam. Any rule sharp enough to catch the second condemns the first. Weighting each signal by how strongly it actually indicates fraud — a sender-domain mismatch heavily, a young domain barely — is what lets both through the same pipeline.

03

How it works

The tool polls Gmail while it's running, and on startup catches up on anything that arrived while the laptop was closed. Each new message is sorted into one of four buckets; only recruiter and interview mail continues. Promotional senders on a list go to Trash, recoverably.

What continues gets a cheap pass first: impersonated company names, display-name and Return-Path mismatch, urgency and financial bait in the wording. Blatant fraud is caught here without spending anything further. What survives is the interesting case — a polished scam reads clean — so those go to deep verification: the company's real domain checked against the sender, registration age, links expanded and inspected without ever being fetched, and a search for public complaints against the name.

Every signal contributes weighted points to a single score. The email gets a label and a note explaining which signals fired and which didn't, and every decision is written to a local history table. When the score genuinely doesn't resolve, the email is surfaced for a human with the evidence attached rather than being pushed into a band it doesn't belong in.

04

Architecture

  1. Poll. Short interval while running; a catch-up scan on launch, so mail that arrived offline is delayed rather than dropped.
  2. Sort. Four buckets. Promotional to Trash, updates labelled, other skipped. Only recruiter mail continues.
  3. Scan. Cheap text and header pass — impersonation, spoofing, urgency, bait. Blatant cases end here.
  4. Verify. Only for mail that survived. Domain against the real company, domain age, link structure without fetching, public scam reports.
  5. Score. Weighted sum into a band. No single flag decisive; trusted senders bypass entirely.
  6. Deliver. Label plus a plain-language note. Inconclusive cases go to a human with the evidence, not into a band.
Architecture diagram Left to right through the six steps, with the branch after Sort drawn going somewhere else entirely. Mark the gap between Scan and Verify — most mail stops at Scan, and the width of the arrows should say so.
Deep verification is for the emails that already look fine.
05

The decision

The expensive error is not the scam you miss. It's the real offer you kill.

The obvious build gives every email a verdict. Genuine or fraudulent, a clean binary, a confident label on every message. It demos beautifully — every input gets an answer, the tool always looks like it knows.

Making inconclusive a first-class outcome costs the demo and buys the thing the tool is actually for. When the signals don't resolve, the email is handed back with its evidence rather than pushed into a band, which means some share of mail still needs a human — the exact work the tool was supposed to remove. It is a strictly less impressive product.

It's also the only version that's safe to trust during a job hunt, because the alternative failure is silent. A missed scam announces itself eventually. A real offer scored as fraudulent gets a label, gets ignored, and nothing ever tells you it happened.

06

What's next

A labelled test set — fifty emails, half fraudulent — so the signal weights are scored rather than chosen. An MX lookup alongside the domain check, since a sending domain with no mail exchanger is a strong signal and cheap to test. Dropping the LinkedIn presence check unless it earns its place, because a signal that degrades silently is worse than no signal. [ Confirm before publishing ] A learned score once the history table holds enough labelled decisions to train on.