TrustTrail

Lays out a secondhand deal’s evidence and shows the exact words behind each warning sign.

Web app (opens in a new tab)

Role
Sole developer: product decisions from two research reports, design, TypeScript rule engine, on-device screenshot reading, React app, AI layer, testing and release
Dates
Nov 2026 – present
Status
Web app live · beta
Stack
TypeScript, React, on-device OCR · 1,768 tests

TrustTrail, screen by screen

  • Summary. The summary leads with counts, then the words behind each sign.

  • Findings. Each finding points back to the exact words in the chat.

  • Evidence. One screenshot, five warning signs, each numbered on the picture.

  • Plan. A plan with a message to send for every step, no AI needed.

  • Connections. Trace any finding back through the exact words to its screenshot.

  • Reviews. Review screenshots are read too: bursts and “just transfer” reviews.

  • Checks. The official checks only you can run, and where to record them.

  • Nothing found. Nothing found is never called safe.

  • Phone, dark. Its own dark mode, built for phones.

  • Landing. Free, no sign-up, runs in your browser.

In short

  • No finding without the exact words

    Every warning sign must quote a verbatim span of the evidence or it is dropped, so a buyer can always see why something was flagged and judge it for themselves.

  • Private by design, nothing to pay for

    Screenshots are read and checked in the browser; there is no server, account or analytics. The only outside call is optional Gemini, after an explicit yes, with numbers blocked out.

  • AI that explains but never decides

    The rules decide the result. The AI can only reword it and suggest steps; each AI note must quote its line word for word, and verdict-like wording is withheld.

The problem

Most secondhand-purchase scams follow a few scripts, but a buyer sees them one message at a time, under time pressure, with nobody to ask.

  • Warning signs are spread across three places: the listing, the chat and the payment request. A changed price or a different account name only shows when they sit side by side.
  • Scam checkers that answer “safe” or “scam” are wrong in both directions, and a wrong “safe” pushes someone to pay.
  • The evidence is a private chat. People will not upload it to a stranger’s server, and they should not have to.
  • The official checks (bank-account and phone lookups) exist, but most people do not know which one applies or what to do with the answer.

How it works

  1. Add the evidence

    Drop in screenshots of the listing, the chat, the payment details and any reviews. Each is read in the background and filed by what it says.

  2. Answer a few questions

    One question at a time about the payment, the seller and how the deal went. Answers become findings marked “From your answers”.

  3. Read the warning signs

    A count first, then each sign with the exact words behind it, how the trick usually works and what to do. What looks normal and what is not confirmed are kept apart.

  4. Act on a plan

    Numbered next steps, each with a message to copy, and the official checks for the phone number and bank account, with a place to record what they showed.

  5. Keep a diary

    Updates such as “I paid” or “now he wants a shipping fee” are read and answered with first steps for your country.

From three screenshots to a plan, all on the device.

Engineering challenges

  1. Telling who proposed what

    Warning signs, what looks normal and what is simply not confirmed are now three separate lists everywhere.

  2. A reader that said nothing

    The same screenshot now yields the account age, the review dates and two warning signs.

  3. An AI that cannot overrule the rules

    Prompt-injection text changed the result 0 of 130 times, and the plan works without any AI at all.

Technical decisions

DecisionWhat I choseInstead of
Local-first instead of the research’s stackReact, TypeScript and Vite in the browser, IndexedDB storage, OCR in a Web WorkerNext.js, FastAPI and PostgreSQL with row-level security
No “is this photo AI” verdictRead only the AI label a picture file carriesA pixel-based AI-image detector API
Counts and words, never a scoreSix named outcomes and a count of warning signs

Results

tests passing
1,768
of findings quote their source word for word
100%
versioned warning-sign rules
26
languages read on the device
25

Measured 11–12 November 2026 on fictional examples. Real-world accuracy has not been measured.

Reflection

What worked

  • Making “no quote, no finding” a validator, not a guideline, kept every screen explainable and made the AI layer safe to add later.
  • Walking the live site with scripts after every deploy caught layout and reading bugs that unit tests could not.

What I underestimated

  • How much of the value sits in reading the screenshot well: a perfect rule engine is useless if the reader returns nothing.
  • How quickly a product feels “useless” when any panel is empty: every screen now always offers a next step.

What I would do next

  • A screen-reader pass with VoiceOver and NVDA, and 200% text zoom.
  • A reading-accuracy benchmark on degraded real-looking screenshots.
  • Native-speaker review of the translations and rules, and testing with real buyers.

Full engineering notes

8 sections, about 6 minutes to read

Summary

Marketplace scams rarely look like scams in the moment: a seller asks to move to WhatsApp, wants a deposit first, and the bank account carries someone else’s name. TrustTrail is built for that moment, for ordinary buyers rather than investigators. You add screenshots of the listing, the chat and the payment request; it reads them on the device, files each one, and a pure TypeScript engine of versioned rules finds the warning signs, each quoting the exact words it rests on. It then gives a plan with messages you can send, and the official checks only you can run. It never calls a person a scammer, never calls a deal safe and never shows a risk score. I designed and built it alone from two research reports, and chose to run everything in the browser so private chats never leave the phone.

My role

What I owned

  • The product decisions from two research reports, including where the build departs from them (local-first instead of Next.js, FastAPI and PostgreSQL).
  • The rule engine in TypeScript: 26 versioned warning-sign rules, contradiction finding across listing, chat and payment, copied-review clustering, a link-spelling inspector and six ordered outcomes.
  • On-device screenshot reading with Tesseract.js: per-message lines, speaker guessing from bubble margins, automatic filing, and an AI-label reader for picture files.
  • The React app and its design: summary, evidence, findings with three views, checks, report, interview, updates diary, landing page, light and dark, right-to-left.
  • The guarded AI layer (Chrome built-in AI and Gemini free tier), the tests, the browser walk and accessibility scripts, and the release on Firebase Hosting.

What I did not own

  • React, Vite, Tesseract.js and its language data, Chrome's built-in model and the Gemini API it is built on.
  • The two research reports were supplied as input; the build decisions and departures are mine.
  • The 24 non-English interface translations and 23 languages of rules are machine-drafted and have not been reviewed by native speakers.
  • No design collaborator, user research, outside code review or legal review yet.

Constraints

  • No lawful labelled data

    There is no set of real, labelled scam chats a student can lawfully train on, so any probability would be invented. The engine is deterministic, versioned rules instead, and the result is one of six named outcomes, never a score.

  • Private chats, zero budget

    The research ranked “buyers will not upload private chats” as the biggest risk. Running everything in the browser removes it and costs nothing to host; the Google Cloud project has no billing account attached.

  • People who are not technical

    The person using it may be anxious and in a hurry. Every screen leads with numbers and plain words, one step at a time, with the reasoning folded away.

How each challenge was solved

Telling who proposed what

Why it was hard

In the owner’s own test, a buyer typing “Can I make payment first and you ship it?” was reported as the seller demanding prepayment. A rule that matches words without knowing the speaker accuses the wrong person.

What I built

The screenshot reader now guesses the speaker from each bubble’s left and right margins, and a buyer’s offer to pay first became a separate, lower-level finding quoting the buyer’s own line. If the owner answers “I suggested it”, a seller-attributed line is downgraded.

Options considered
  • Ignore buyer lines entirely, and miss deals where the buyer is talked into it
  • Keep one rule and soften its wording for everyone
  • Attribute each line to a speaker and give buyer-proposed prepayment its own, neutral finding
Trade-off

The speaker guess can be wrong on unusual chat layouts, so the speaker is shown and can be switched by hand.

Validation

tests/interview.test.ts and the third built-in example (tests/case-ps3.test.ts), the owner’s acceptance case.

A reader that said nothing

Why it was hard

A clean seller-profile screenshot came back as “No text was found”, so the product felt useless at exactly the moment it should help. The reader dropped a whole paragraph when one line was faint.

What I built

Lines are now kept individually and grouped into messages by position; small pictures are enlarged two to two and a half times before reading; star rows and symbol soup are removed. Profile and review pages are recognised by what they say, with new detectors for days-old accounts, review bursts and reviews that tell you to pay first.

Options considered
  • Send screenshots to a cloud OCR service
  • Show the raw text and ask the person to correct it
  • Keep lines one by one, enlarge small pictures in memory, and drop only noise
Trade-off

Doubtful lines are hidden instead of shown for correction, so a misread line can still produce a finding nobody checked; the quote is always shown so it can be seen.

Validation

scripts/ocr-probe.mjs on a real-looking profile screenshot; tests/detect.test.ts.

An AI that cannot overrule the rules

Why it was hard

The owner wanted AI suggestions, but a language model asked about a chat can be steered by text inside that chat, and can sound certain about a verdict it has no basis for.

What I built

The AI only rewords the result and suggests steps. Every note it writes must quote its line word for word or it is dropped, verdict-like wording is withheld, and evidence text is passed as data. On-device Chrome AI is preferred; Gemini runs only after consent, with phone, account and ID numbers blocked out.

Trade-off

The AI is less “clever” than it could be, and the free Gemini allowance is small; when it runs out the card says so and the built-in plan remains.

Validation

130 prompt-injection attempts in evidence; scripts/ai-check.mjs on the live site.

Why I made each decision

Local-first instead of the research’s stack

Because

The research proposed Next.js, FastAPI and PostgreSQL, but its own top risk was buyers refusing to upload private chats. With no server there is nothing to leak, operate or pay for, and the engine stays pure TypeScript that could move behind an API unchanged.

Trade-off

No shared cases or sync between devices, and the screenshot-reading data (56 MB for 25 languages) loads on the device, one language at a time.

No “is this photo AI” verdict

Because

No free detector is reliable on re-compressed marketplace photos, and a wrong “looks real” would push someone to pay. The file’s own label (C2PA, IPTC, Stable Diffusion settings) is a fact; a missing label is shown as proving nothing.

Counts and words, never a score

Because

A percentage invites the reading “72% safe”. A count with the exact words lets the buyer weigh it, and every result says a warning sign is not proof.

Testing

Vitest covers the rule engine, the screenshot-reading helpers, the interview, updates, the AI guards and 261 cross-language samples. Puppeteer scripts walk the live site in real headless Chrome at four device sizes, and axe-core audits eleven screens in light and dark.

tests passing
1,768
accessibility issues found and fixed across 11 screens
118
fictional rule fixtures in English and Malay
840

Results in detail

  • Live, free and private

    The web app is public on Firebase Hosting’s free plan, installable and usable offline. A full session sends nothing to any other server unless the person asks for Gemini.

  • Shaped by a live test

    Eight rounds of changes in two days came from the owner’s own test runs and an outside review: speaker attribution, one-question-at-a-time interview, an updates diary, automatic suggestions, and warning-sign rules for all 25 languages.

Stack and release record

  • Platforms

    Web (live): installable, works offline after the first visit, Phone, tablet and desktop layouts, light and dark, right-to-left languages

  • Built with

    TypeScript, React 19, Vite, IndexedDB, Tesseract.js (on-device OCR, self-hosted), Chrome built-in AI (Prompt API), Google Gemini API (free tier, opt-in), Vitest, Puppeteer, axe-core, Firebase Hosting

  • Testing and delivery

    Vitest runs 1,768 tests across the engine, reader helpers, AI guards and 25-language samples. After each deploy, Puppeteer walks the live site at four device sizes and counts outside requests, and axe-core audits eleven screens. Deployed to Firebase Hosting on the free plan.

  • Status in full

    Live on Firebase Hosting (free plan) since 11 November 2026, in beta. The rules are tested on fictional examples only; no real users or native speakers have reviewed it yet, and official check routes are verified for Malaysia and Singapore only. The source is private; a public showcase repository explains the build.

Next case study LetMeKnow Turns messages, bills, promises and tasks into open loops, with a plan to close each one.

All work and archive

Hiring interns for January 2027?

I’m available full-time from 25 January to 23 April 2027, and for graduate roles in Malaysia or overseas from August 2027. Email is the quickest way to reach me.

  • Subang Jaya, Malaysia
  • local time