DEV Community

asosa933
asosa933

Posted on AI-assisted

Gig Scam Shield: a private scam checker for side-gig hunters, powered by EmbeddingGemma in the browser

Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle Submission 💸

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle

What I Built

Gig Scam Shield is a small web app where you paste a job or side-gig post and, a couple of seconds later, get a verdict: ✅ Looks OK, ⚠️ Be careful or ⛔ Likely scam. It lists every red flag it found and quotes the exact words that triggered it.

I built it because I'm living the problem. I'm a beginner looking for online side income from Italy, and my "opportunities" list is full of traps: a "starter kit" fee, an agency that wants a photo of my ID before any interview, a recruiter who moves straight to Telegram, a remote job that asks me to buy a laptop with gift cards "to be reimbursed later". For someone without experience, each one takes real time to evaluate, and getting it wrong costs money.

Who would pay for it? Probably not the job seeker. People who screen posts for other people would:

  • admins of Facebook/Telegram job groups, who spend hours removing scam posts,
  • small niche job boards and community newsletters that list gigs,
  • career counsellors and student-placement offices.

For them, a cheap tool that pre-screens posts without sending applicants' data to a third-party API is worth a small monthly fee (think €5-10 per month for a group admin, more for a job board with a moderation queue). Because the model runs on the user's device, my running cost per check is zero, and that's the only reason a price that low is possible.

Demo

👉 Live demo: https://asosa933.github.io/gig-scam-shield/ (the first AI check downloads the model once, about 200 MB; then it's cached)

Gig Scam Shield demo

The "sneaky one": the rules only see "gift cards" (⚠️), but EmbeddingGemma recognises the pattern and raises it to ⛔:

Sneaky scam raised by the local AI

A normal freelance gig, correctly left alone (with the good signs it found):

Safe gig

Code

GitHub logo asosa933 / gig-scam-shield

Private, in-browser scam checker for job & side-gig posts: rules + open-weight EmbeddingGemma via transformers.js

🛡️ Gig Scam Shield

Paste a job or side-gig post and get a scam check in seconds. Nothing leaves your browser.

Live demo: https://asosa933.github.io/gig-scam-shield/

Demo

Gig Scam Shield is a tiny, private scam checker for people hunting online side income, and for anyone who screens job posts for others: Facebook/Telegram job-group admins, career counsellors, small job boards. It combines:

  1. A transparent rules engine (English + Italian) that flags classic traps: fees before you earn, card or bank details, ID/selfie before any interview, gift cards/crypto/check tricks, moving the chat to Telegram/WhatsApp, unrealistic pay, urgency, reshipping, free-email "companies".
  2. An open-weight model running locally: EmbeddingGemma (Google's 300M-parameter Gemma embedding model) via transformers.js, on WebGPU when available and WASM/CPU otherwise. It embeds the post and runs a 5-nearest-neighbour vote against a small labelled set of scam, caution and safe posts (src/examples.js), so it can catch reworded scams the regexes miss.

How the…

How I Built It

The app is plain HTML/CSS/JavaScript with no build step and no backend. It has two layers:

1. A transparent rules engine (src/rules.js). About ten regex-based checks, in English and Italian, for the classic traps: fees before you earn, card or bank details, ID/selfie before any interview, gift cards/crypto/"deposit this check" tricks, moving the chat to Telegram/WhatsApp, unrealistic pay, urgency, reshipping and "payment processing agent" jobs, and free-email "companies". Each rule has a weight; the score maps to a level. It's fast, explainable, and fully unit-tested with node:test.

2. An open-weight model running locally: EmbeddingGemma. In a Web Worker I load onnx-community/embeddinggemma-300m-ONNX with transformers.js, on WebGPU when the browser has it and on WASM/CPU otherwise. I embed a small, hand-written reference set of 30 labelled posts (scam / caution / safe) once, then for every new post:

const [vec] = await embed([post]);       // EmbeddingGemma, "task: classification | query: " prompt
const r = knnVote(vec, refs, 5);         // cosine similarity, weighted top-5 vote
// → { verdict: 'scam', confidence: 0.6, nearest: { text: '…', sim: 0.76 } }
Enter fullscreen mode Exit fullscreen mode

The app also shows the most similar reference post, so you can see why it thinks what it thinks.

Combining them: the rules set the floor, and the AI can only raise the verdict by one step. A post that asks for a fee stays "Likely scam" even if the model hesitates; a reworded scam that slips past the regexes still gets bumped up. I think that's the right trust model for a small local model.

What didn't work (and what I learned). My first plan was a generative Gemma model as the classifier: Gemma 3 270M, then 1B, in the browser, prompted to answer scam/caution/safe. At those sizes and with the quantizations the WASM backend can actually run, the answers were unreliable: the 270M model called an obvious "$49 registration fee + send your card" post "safe". I also hit a practical snag: the 4-bit exports use a GatherBlockQuantized operator that the WASM backend doesn't implement, so on CPU you need fp16/int8 files or the special "no_gather" export. Switching to embeddings + nearest neighbours made everything better at once: about 200 MB instead of about 1 GB, about 2 s per check on CPU, deterministic, and explainable.

Testing.

  • npm test: 10 unit tests (rules in both languages, kNN vote, combine logic, CSV quoting).
  • eval/eval-model.mjs runs the real model in Node on 11 held-out posts: rules 7/11, EmbeddingGemma kNN 9/11, combined 9/11. It's a tiny set, so it's a sanity check, not a benchmark, but it showed that the model catches the reworded scams the rules miss (for example a "financial agent" money-mule post with no trigger words).
  • An end-to-end Puppeteer script loads the app in headless Chrome, downloads the model on WASM, runs the four sample posts and checks history persistence and the CSV export.

AI disclosure: I'm a beginner programmer and I built this with substantial help from an AI coding assistant, for planning, code, tests and debugging the model loading. The problem, the product decisions (rules as the floor, privacy first, who pays) and the testing choices are mine, and I made sure I can explain every file.

Why Does Open Innovation Matter?

For this particular product, open weights aren't a nice-to-have; they are the business model:

  • Privacy by construction. Job posts often come with personal context ("I got this DM…"), and job-group admins handle other people's messages. With EmbeddingGemma running in the tab, nothing is uploaded anywhere. I don't have to write a privacy policy promising to be careful with data, because I never receive it.
  • Zero marginal cost. A closed API would charge per check. For a tool that might sell for a few euros a month to a group admin checking hundreds of posts, per-call pricing kills the margin. Here the user's own CPU/GPU does the work, and the static site is hosted for free on GitHub Pages.
  • Works offline, no keys. After the first load it runs without an account or an API key, which also means there's no key to leak in a client-side app.
  • I could swap models when the first choice failed. Because everything is open (weights, ONNX exports, runtime), I could try Gemma 3 270M, then 1B, read the actual error from ONNX Runtime, pick a different export, and finally switch to EmbeddingGemma in an afternoon. With a closed API I'd have been stuck with whatever the endpoint does.
  • Tunable for a niche. The "knowledge" is a plain, editable list of examples in src/examples.js. A job-board owner can add the scam patterns they actually see, in their language, and the tool improves immediately, with no fine-tuning bill.

Prize Categories

  • Best Use of Gemma: the project's AI is EmbeddingGemma (Google's open-weight Gemma embedding model), running locally in the browser with transformers.js (WebGPU or WASM).

Thanks for reading! If you've seen a scam pattern that Gig Scam Shield misses, open an issue with an example. That's exactly the data that makes it better.

Top comments (0)