<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: asosa933</title>
    <description>The latest articles on DEV Community by asosa933 (@asosa933).</description>
    <link>https://dev.to/asosa933</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4190261%2F4d946029-d3f7-42ed-ab21-f4a280f4540c.png</url>
      <title>DEV Community: asosa933</title>
      <link>https://dev.to/asosa933</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/asosa933"/>
    <language>en</language>
    <item>
      <title>Gig Scam Shield: a private scam checker for side-gig hunters, powered by EmbeddingGemma in the browser</title>
      <dc:creator>asosa933</dc:creator>
      <pubDate>Sat, 17 Oct 2026 12:17:53 +0000</pubDate>
      <link>https://dev.to/asosa933/gig-scam-shield-a-private-scam-checker-for-side-gig-hunters-powered-by-embeddinggemma-in-the-54a3</link>
      <guid>https://dev.to/asosa933/gig-scam-shield-a-private-scam-checker-for-side-gig-hunters-powered-by-embeddinggemma-in-the-54a3</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week2-2026-10-12"&gt;Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

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

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who would pay for it?&lt;/strong&gt; Probably not the job seeker. People who screen posts &lt;em&gt;for other people&lt;/em&gt; would:&lt;/p&gt;

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

&lt;p&gt;For them, a cheap tool that pre-screens posts &lt;em&gt;without sending applicants' data to a third-party API&lt;/em&gt; 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 &lt;strong&gt;zero&lt;/strong&gt;, and that's the only reason a price that low is possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;👉 &lt;strong&gt;Live demo:&lt;/strong&gt; &lt;a href="https://asosa933.github.io/gig-scam-shield/" rel="noopener noreferrer"&gt;https://asosa933.github.io/gig-scam-shield/&lt;/a&gt; (the first AI check downloads the model once, about 200 MB; then it's cached)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fitj2fft7lr6vworscfrv.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fitj2fft7lr6vworscfrv.gif" alt="Gig Scam Shield demo" width="760" height="622"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F67i6a8isw42t95png06g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F67i6a8isw42t95png06g.png" alt="Sneaky scam raised by the local AI" width="800" height="1001"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A normal freelance gig, correctly left alone (with the good signs it found):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6o2a00f6rvogxxh2meln.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6o2a00f6rvogxxh2meln.png" alt="Safe gig" width="800" height="1055"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/asosa933" rel="noopener noreferrer"&gt;
        asosa933
      &lt;/a&gt; / &lt;a href="https://github.com/asosa933/gig-scam-shield" rel="noopener noreferrer"&gt;
        gig-scam-shield
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Private, in-browser scam checker for job &amp;amp; side-gig posts: rules + open-weight EmbeddingGemma via transformers.js
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🛡️ Gig Scam Shield&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Paste a job or side-gig post and get a scam check in seconds. Nothing leaves your browser.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Live demo: &lt;strong&gt;&lt;a href="https://asosa933.github.io/gig-scam-shield/" rel="nofollow noopener noreferrer"&gt;https://asosa933.github.io/gig-scam-shield/&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/asosa933/gig-scam-shield/docs/demo.gif"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fasosa933%2Fgig-scam-shield%2FHEAD%2Fdocs%2Fdemo.gif" alt="Demo"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;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:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A transparent rules engine&lt;/strong&gt; (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".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An open-weight model running locally:&lt;/strong&gt; &lt;a href="https://ai.google.dev/gemma/docs/embeddinggemma" rel="nofollow noopener noreferrer"&gt;EmbeddingGemma&lt;/a&gt; (Google's 300M-parameter Gemma embedding model) via &lt;a href="https://github.com/huggingface/transformers.js" rel="noopener noreferrer"&gt;transformers.js&lt;/a&gt;, 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 (&lt;code&gt;src/examples.js&lt;/code&gt;), so it can catch reworded scams the regexes miss.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;How the&lt;/strong&gt;…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/asosa933/gig-scam-shield" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

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

&lt;p&gt;&lt;strong&gt;1. A transparent rules engine (&lt;code&gt;src/rules.js&lt;/code&gt;).&lt;/strong&gt; 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 &lt;code&gt;node:test&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. An open-weight model running locally: EmbeddingGemma.&lt;/strong&gt; In a Web Worker I load &lt;a href="https://huggingface.co/onnx-community/embeddinggemma-300m-ONNX" rel="noopener noreferrer"&gt;&lt;code&gt;onnx-community/embeddinggemma-300m-ONNX&lt;/code&gt;&lt;/a&gt; with &lt;a href="https://github.com/huggingface/transformers.js" rel="noopener noreferrer"&gt;transformers.js&lt;/a&gt;, 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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;embed&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;       &lt;span class="c1"&gt;// EmbeddingGemma, "task: classification | query: " prompt&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;knnVote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;refs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;         &lt;span class="c1"&gt;// cosine similarity, weighted top-5 vote&lt;/span&gt;
&lt;span class="c1"&gt;// → { verdict: 'scam', confidence: 0.6, nearest: { text: '…', sim: 0.76 } }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The app also shows the most similar reference post, so you can see &lt;em&gt;why&lt;/em&gt; it thinks what it thinks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Combining them:&lt;/strong&gt; the rules set the floor, and the AI can only &lt;em&gt;raise&lt;/em&gt; 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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What didn't work (and what I learned).&lt;/strong&gt; My first plan was a &lt;em&gt;generative&lt;/em&gt; Gemma model as the classifier: Gemma 3 270M, then 1B, in the browser, prompted to answer &lt;code&gt;scam&lt;/code&gt;/&lt;code&gt;caution&lt;/code&gt;/&lt;code&gt;safe&lt;/code&gt;. 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 &lt;code&gt;GatherBlockQuantized&lt;/code&gt; operator that the WASM backend doesn't implement, so on CPU you need fp16/int8 files or the special "no_gather" export. Switching to &lt;strong&gt;embeddings + nearest neighbours&lt;/strong&gt; made everything better at once: about 200 MB instead of about 1 GB, about 2 s per check on CPU, deterministic, and explainable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Testing.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;npm test&lt;/code&gt;: 10 unit tests (rules in both languages, kNN vote, combine logic, CSV quoting).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;eval/eval-model.mjs&lt;/code&gt; runs the real model in Node on 11 held-out posts: &lt;strong&gt;rules 7/11, EmbeddingGemma kNN 9/11, combined 9/11&lt;/strong&gt;. 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).&lt;/li&gt;
&lt;li&gt;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.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;AI disclosure:&lt;/strong&gt; 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;For this particular product, open weights aren't a nice-to-have; they &lt;em&gt;are&lt;/em&gt; the business model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Privacy by construction.&lt;/strong&gt; 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.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero marginal cost.&lt;/strong&gt; 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.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Works offline, no keys.&lt;/strong&gt; 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.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I could swap models when the first choice failed.&lt;/strong&gt; 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.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tunable for a niche.&lt;/strong&gt; The "knowledge" is a plain, editable list of examples in &lt;code&gt;src/examples.js&lt;/code&gt;. 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.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma:&lt;/strong&gt; the project's AI is EmbeddingGemma (Google's open-weight Gemma embedding model), running locally in the browser with transformers.js (WebGPU or WASM).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;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.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
  </channel>
</rss>
