<?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: TEJAS _</title>
    <description>The latest articles on DEV Community by TEJAS _ (@jx777).</description>
    <link>https://dev.to/jx777</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%2F3796482%2F67728b15-bdc0-41e2-b649-6d332a1839e5.jpg</url>
      <title>DEV Community: TEJAS _</title>
      <link>https://dev.to/jx777</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jx777"/>
    <language>en</language>
    <item>
      <title>OfferProof: an offline job-scam checker for my fresher friends</title>
      <dc:creator>TEJAS _</dc:creator>
      <pubDate>Fri, 02 Oct 2026 13:15:45 +0000</pubDate>
      <link>https://dev.to/jx777/offerproof-an-offline-job-scam-checker-for-my-fresher-friends-1eh7</link>
      <guid>https://dev.to/jx777/offerproof-an-offline-job-scam-checker-for-my-fresher-friends-1eh7</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-weekend-2026-10-01"&gt;Hacktoberfest Weekend Challenge: Build for a Friend&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;When I read "build for a friend", my first thought was that none of my friends had a problem I could fix in a weekend. Then I thought about what most of my batch is doing right now. We're all applying for our first jobs. And if you're a fresher looking for a job, you're exactly who fake job offers are aimed at.&lt;/p&gt;

&lt;p&gt;They look something like this. I wrote this one from public fraud advisories, so it isn't a real message, but every line in it is something those advisories warn about:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;From:&lt;/strong&gt; &lt;a href="mailto:infosys.recruitment.cell@gmail.com"&gt;infosys.recruitment.cell@gmail.com&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Subject:&lt;/strong&gt; Offer Letter - Infosys Ltd&lt;/p&gt;

&lt;p&gt;Dear Candidate, congratulations! You are selected for System Engineer at Infosys, Pune.&lt;br&gt;
Your interview will be on Telegram, message our HR @infosys_hr_desk.&lt;br&gt;
Pay a refundable registration fee of Rs 1,999 within 2 hours to confirm your seat.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you've seen a few of these, you'd laugh at it. But if it's the first offer you've ever got, it says Infosys, it says congratulations, and it wants an answer in two hours. ₹1,999 to lock in a job at Infosys doesn't sound like much.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;OfferProof&lt;/strong&gt;. You paste the message in and it tells you which parts look like a scam, quotes the exact words it's worried about, and gives you a question to send back. Something like "can you email me from your official Infosys address?" That's an easy question for a real recruiter and a hard one for a scammer.&lt;/p&gt;

&lt;p&gt;It runs on your own laptop with Gemma 3 4B through Ollama. Offer letters have your phone number, address and PAN on them, and I didn't want anyone pasting that into some website.&lt;/p&gt;

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

&lt;p&gt;The first message is the one above: a Gmail address pretending to be Infosys, an interview on Telegram, and a "refundable" fee due in two hours. It flags all three, and the two-hour deadline too.&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%2Fcyaznge9e4ahpbe21ppx.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%2Fcyaznge9e4ahpbe21ppx.gif" alt="OfferProof flagging a fake Infosys offer, with the quoted words behind each sign" width="760" height="585"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The second is a normal Infosys email that happens to say "Infosys never charges a registration fee". A plain keyword search would flag it for the word "fee". OfferProof doesn't. It also says &lt;em&gt;Not verified&lt;/em&gt;, not &lt;em&gt;Genuine&lt;/em&gt;. No message can prove an offer is real, so it never claims that.&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%2Fzm22nx76jtztl7lip4dn.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%2Fzm22nx76jtztl7lip4dn.gif" alt="OfferProof leaving a genuine Infosys email unflagged" width="760" height="585"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Gemma takes 15 to 30 seconds per message on my GTX 1650. I cut that wait out of the GIFs.)&lt;/em&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/Surge77" rel="noopener noreferrer"&gt;
        Surge77
      &lt;/a&gt; / &lt;a href="https://github.com/Surge77/offerproof" rel="noopener noreferrer"&gt;
        offerproof
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Check a job offer for scam signs, locally, with an open-weight model.
    &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;OfferProof&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Check a job offer for scam signs before you reply, pay, or share documents. It runs on your own computer
with an open-weight model, so your offer letter, phone number and PAN never leave it.&lt;/p&gt;
&lt;p&gt;Fake job offers mostly target people with little or no work experience. Most of them repeat a few tells
a fee to "confirm your seat", an interview held over Telegram, a Gmail address claiming to be a large
company. OfferProof looks for those tells and shows you the exact words that triggered each one.&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;
&lt;pre class="notranslate"&gt;&lt;code&gt;$ offerproof check offer.txt
LIKELY SCAM: This message has clear signs of a job scam.

[severe] Personal email claiming to be Infosys
  "infosys.recruitment.cell@gmail.com"
  Large employers recruit from their own company domain, never from Gmail, Yahoo or Outlook.
  Ask them: Can you write to me from your official Infosys email address?

[severe] Asks you to pay money
  "Pay a refundable registration&lt;/code&gt;&lt;/pre&gt;…&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Surge77/offerproof" 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;I didn't want the model deciding "scam or not". A 4B model will give you a confident answer either way, and you can't tell why it said it. So Gemma only reads the message. Plain code makes the call.&lt;/p&gt;

&lt;p&gt;There are four parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pattern rules in &lt;code&gt;signals.yaml&lt;/code&gt; for known scam wording. They skip negated sentences, which is why the "never charges a fee" email comes through clean.&lt;/li&gt;
&lt;li&gt;Domain checks in &lt;code&gt;companies.yaml&lt;/code&gt;. If a message claims to be from TCS but comes from a Gmail address, or from something like &lt;code&gt;tcs-careers.in&lt;/code&gt;, that gets flagged.&lt;/li&gt;
&lt;li&gt;Gemma answers a few narrow questions. Does it ask for money? An OTP? Documents? How is the interview being done? Every answer has to come with a quote from the message, and if that quote isn't actually in the message, the answer gets thrown away.&lt;/li&gt;
&lt;li&gt;A few lines of Python decide. Any severe sign means &lt;em&gt;likely scam&lt;/em&gt;. Anything else is &lt;em&gt;not verified&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Where it went wrong
&lt;/h3&gt;

&lt;p&gt;I wrote 20 test messages, 12 scams and 8 genuine ones, and ran everything locally.&lt;/p&gt;

&lt;p&gt;The first run caught all 12 scams. Looked great, until I noticed it had also flagged 3 of the 8 genuine messages.&lt;/p&gt;

&lt;p&gt;That confused me, because I was already checking that Gemma's quotes existed in the message. They did. The problem was what it used them for. It called a "quick call" a chat-only interview. It called a rejection email a chat-only interview too. And it decided a HackerRank assessment was a task scam. Every quote was real. None of them had anything to do with the claim.&lt;/p&gt;

&lt;p&gt;So a quote being in the message isn't enough, it has to be about the thing being claimed. Each signal now has a list of words its quote must contain. If Gemma says "interview only over chat", the quote has to mention Telegram, WhatsApp or chat:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;quote_supports&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signal_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;The quote must be about the claim, not just appear somewhere in the message.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;evidence&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_signals&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="n"&gt;signal_id&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;evidence&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;evidence&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;evidence&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also spelled out in the prompt the things it kept getting wrong. Phone and video calls aren't chat-only. Coding tests aren't task scams. If a message doesn't say how the interview happens, the answer is "not mentioned".&lt;/p&gt;

&lt;p&gt;After that:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Scams caught (of 12)&lt;/th&gt;
&lt;th&gt;Genuine wrongly flagged (of 8)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Rules only&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Model only&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Combined&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The one scam it still misses says the "training material costs 999". I could add a rule for it, but it's one message I wrote myself, and tuning rules until my own test set passes wouldn't prove much.&lt;/p&gt;

&lt;p&gt;That's the real limitation right now: all 20 messages are mine. Next I want my friends to run it on messages they've actually received, and add those to the test set with names and numbers removed.&lt;/p&gt;

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

&lt;p&gt;For this project it's pretty practical.&lt;/p&gt;

&lt;p&gt;I'd only trust a tool with my offer letter if the letter never leaves my laptop. That works because Gemma is open and small enough to run on my GTX 1650.&lt;/p&gt;

&lt;p&gt;Scam scripts also change faster than one person can keep up with. The patterns are plain YAML, so if you've seen a scam OfferProof misses, adding it is a couple of lines and a test. &lt;code&gt;CONTRIBUTING.md&lt;/code&gt; has the steps.&lt;/p&gt;

&lt;p&gt;And you don't have to take my word for the numbers. The test messages and the script are in the repo. Run them yourself, or point &lt;code&gt;OFFERPROOF_MODEL&lt;/code&gt; at a different model and see how it does.&lt;/p&gt;

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

&lt;p&gt;Best Use of Gemma. Gemma 3 4B does all the reading, locally through Ollama.&lt;/p&gt;




&lt;p&gt;If someone has sent you one of these and you're in India: don't pay, don't share OTPs or documents, and report it at &lt;a href="https://cybercrime.gov.in" rel="noopener noreferrer"&gt;cybercrime.gov.in&lt;/a&gt; or call 1930.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>hf26challenge</category>
    </item>
  </channel>
</rss>
