About Veridex

Built by locals. Run by evidence.

How the 0–100 Trust Score is built, what each of the five analyzers looks for, and the small crew that runs the rules.

The short version

Why this exists.

Make 'veridex it' the default reflex before tapping a link, replying to a message, or scanning a code — so locals don't pay the price for a global pattern of inbox scams.

Who builds Veridex

A small crew, a single principle.

Veridex is built by a small crew — founders, engineers, and a handful of advisors who have watched an inbox scam cost a family member real money. We are local-first and we keep the team small enough that every shipped rule has a real person's name on it.

Want to reach the team? Email us — every message lands in the same inbox.

Locals-first / privacy-respecting

We build for the family member who pastes an SMS into the wrong field. Their input is the payload, never the product — we keep the smallest set of rows we need to surface a permalink, and nothing else.

Evidence over narrative

Every chip on a result is a named rule with a fixed weight. If we miss a scam, you can see which rule didn't fire. If we call a real thing a scam, the matched signals are sitting in plain text next to the verdict.

Open about the limits

No inbox pipe, no crawl layer, no opaque model in the loop — though a model-driven version of the screenshot explanation is on the roadmap. The pipeline is the same five deterministic rules on every input, and we say so on every result page.

What the score actually is

A second opinion, not a gatekeeper.

Veridex never auto-blocks a message. It runs a fixed set of deterministic rules — every signal is a named rule a reviewer can argue with — and returns a Trust Score plus the red flags that drove it. If we miss a scam, you can see exactly which rule didn't fire. If we call a real thing a scam, you can override the call because the matched signals are sitting in plain text next to the verdict.

Heuristic, not a guarantee

Every signal is a named rule. There is no opaque model in the loop, and no "trust" verdict — only the absence of matched red flags.

Same scale on every input

URL, email, text, QR, and screenshot all share the same three-band scale and the same per-rule weight philosophy, so the habit carries across the five analyzers.

A second opinion you can act on

The output is the same shape every time, so the habit of reading the matched signals first — then deciding — carries across inputs.

The 0–100 scale

Three bands, fixed cutoffs.

A higher score means fewer red flags were matched — not "verified safe". We never claim a thing is safe; we claim we did not see the usual scam signals. The scale is walked rule-by-rule on the methodology page.

ScoreVerdictNext step
0–39badPotential warning signs detected. Do not enter credentials, payment details, or one-time codes — navigate to the brand yourself in a fresh tab.
40–74warnPotential warning signs detected — verify the sender through a separate channel before clicking or signing in.
75–100okNo major warning signs detected — standard caution still advised; sign in only on sites you reached yourself.

The five analyzers

What each input actually gets run through.

All five analyzers share the same banded scale, the same deterministic-rule philosophy, and (where it fits) the same URL pipeline. The signals below come straight from the live src/lib/business/* modules — no claims about signals the code doesn't actually run.

Websites

Pipeline
Open checker

The URL pipeline — host, scheme, path, query, no network calls.

The URL pipeline looks at the host, scheme, path, and query string and folds a weighted set of rules against them. There is no DNS lookup, no fetch, no redirect resolution — exactly so the checker cannot be weaponised into a phishing payload by a malicious link.

  • HTTPS scheme tag (encrypted transport) and plain-HTTP penalty
  • Punycode (xn--) decode and IDN look-alike check
  • Brand-impersonation lookup against an offline token list
  • Brand token with a digit substituted (paypa1, g00gle)
  • Brand name used as a subdomain of an unrelated registry
  • Known URL shortener recognition — never resolving the redirect
  • Credential-path heuristics (login / verify / reset / wallet / confirm)
  • Query string carries password / token / secret fields
  • Uncommon TLD penalty weights (.top, .xyz, .click, …)
  • Raw IP host and non-default port
  • Numeric label parked next to a brand token in the host
  • Excessive length or deeply nested path

Emails

Pipeline
Open checker

Header parsing + body lexicon + per-link fold.

Pasting an email parses From: / Reply-To: / Subject from the headers, scans the body for scam phrasing, and folds each in-message link back through the URL pipeline — so a single terrible link in an otherwise fine email still collapses the score.

  • From: vs. Reply-To: domain dichotomy
  • Sender domain vs. known brand token (offline list)
  • Display name mentions a brand the sender domain is not
  • Authentication headers (SPF / DKIM) present
  • Uncommon sender TLD penalty
  • Urgency lexicon hits shared with the SMS pipeline
  • Generic greeting ("Dear Customer") detection
  • In-message link carries credential-like query params
  • In-message link folded through scoreUrl() — worst per-URL penalty wins
  • Shortlink in body
  • Link to brand-spoofing host
  • Suspicious attachment extension advertised (exe / hta / iso / …)
  • HTML-only body with no plain-text alternative

Text messages

Pipeline
Open checker

Sender shape + body lexicon + per-link fold.

The SMS pipeline reads the sender shape (shortcode / ten-digit mobile / alpha-tag / unknown), keyword slices, and any link present. As with the email checker, a single bad link in the message collapses the score.

  • Sender shape heuristic — shortcode / mobile / alpha / unknown
  • Urgency lexicon hits shared with the email pipeline
  • Legal-threat or law-enforcement mentions
  • Asks you to reply with a verification code or PIN
  • Asks for payment to a new recipient (wire, gift card, crypto, Zelle, Venmo)
  • Mentions an attachment or tap-to-view media
  • Short-link in body
  • In-message link folded through scoreUrl() — worst per-URL penalty wins
  • Uncommon TLD on an in-message link
  • In-message link points at a brand-spoofing host
  • In-message link contains a digit-substituted brand
  • Credential-shaped path on an in-message link
  • Query string carries password / token / secret fields
  • No-links / no-urgency affirmative chip when the picture is clean

QR codes

Pipeline
Open checker

URL payload → URL pipeline. Text payload → keyword scoring.

After pngjs + jsqr have decoded the QR on the server, the payload is routed two ways and a transparent chip tells you which — so the QR target lands on the same URL pipeline the /check page already uses, and a non-URL payload gets the SMS-style keyword slices.

  • If the decoded payload looks like a URL — delegate to scoreUrl() so the QR lands on the same URL pipeline the existing /check page uses
  • A qr-decoded-url chip is added so the result makes clear the URL came out of a QR, not a paste
  • If the decoded payload is text / vCard / Wi-Fi creds — score text-shape using SMS urgency / threat / code-request / payment-recipient keyword slices
  • A chrome-bar label is generated (vCard · N fields, Wi-Fi credentials · N chars, Text QR · preview) so the renderer never shows an empty URL field

Screenshots

Pipeline
Open checker

OCR text → URL fold + keyword fold + contact-info chip.

The screenshot pipeline is the only one that produces an aiExplanation. It first OCRs the image via the Polsia proxy, then runs the same URL / keyword / contact-info rules against the extracted text. The output includes a paragraph, a confidence number, and three action recommendations.

  • OCR text via the Polsia proxy (textLength / text / urls returned to the route handler)
  • Find URLs in the OCR text — fold worst per-URL scoreUrl() as a single in-screenshot-link-score chip
  • Reuse SMS keyword slices (urgency / threat / code-request / payment) against the OCR text
  • Emit a mentions-contact-info chip when the OCR text contains an email- or phone-shaped token

The AI explanation

A deterministic paragraph, not a model.

The screenshot analyzer is the only one that produces an aiExplanation field. The paragraph is a deterministic template keyed off the score band and the worst fired signal id — so every screenshot result that lands in the same band references the same wording and the same worst-fired signal, not a paraphrase.

Shape on the wire

The trust-score route on a screenshot returns { paragraph, confidence, actions } — a short paragraph, a confidence number, and three action recommendations keyed off the verdict band.

Sparkles — AI explanation86% confidence

Confidence formula

The confidence number is computed from the distance of the score from 100, shaved for every warn or bad signal fired so a noisy result gets a proportionally lower confidence rating.

100 − |100 − score| − Σ( warn × 3 + bad × 6 ), clamped to [0, 100]

Honestly: it's a template today.

The current explanation is a deterministic template, not a model call. We say so on the result itself. A model-driven version is on the roadmap; it will be swapped in once the templates are stable enough to A/B against.

What we deliberately don't do

Honest about what the score does not tell you.

  • No header inspection for arbitrary inbound mail. The email checker reads only the headers you paste; there is no inbox pipe hooked into your mailbox.
  • No live site reputation yet. Scoring is offline — DNS, heuristics, and brand lists. A crawl layer ships later.
  • Weighted rule set, not machine learning. Every signal is a named rule with a fixed weight a reviewer can read and argue with.
  • No auto-block. We never decide for you. We surface the score, the matched signals, and a concrete next step — the click stays yours.

Try a verifier

The score shifts when the signals fire.

Run a check on a real input and watch the score update as each rule matches.