I built an opportunity tracker for a friend using a local 4B model
My friend is building a career in cybersecurity and AI. Finding opportunities was never her problem. Hackathons, internships, free courses, and certifications are everywhere. Keeping track of them was. She saved links across tabs, bookmarks, and chats, then missed deadlines and felt behind before she started. So I built Beacon Hub.
Beacon Hub
What it does
- Browse Cybersecurity, AI, and Cloud opportunities, filtered by events, hackathons (virtual or in-person), courses, internships, and certifications, ten at a time.
- Add items to a To-Do list, a Calendar, or both. Ticking something off moves it to Completed instead of deleting it.
- Event dates and application deadlines are shown separately. If the app can't confirm a date, it says "dates missing, check the link" instead of guessing.
How it works
It runs on Gemma 3 (4B) through Ollama. That machine made the model's job small by necessity. The rule I followed: code finds the facts, Gemma handles ambiguity, code verifies the result. Code pulls candidate dates out of each page, and Gemma only picks between dates that code already found, so it can't invent one. Search comes from SerpApi (cached, with a hard cap), reminders run as Temporal workflows with times computed in code, and the app is FastAPI, vanilla JavaScript, and SQLite.
It runs on Gemma 3 (4B) through Ollama. That machine made the model's job small by necessity. The rule I followed: code finds the facts, Gemma handles ambiguity, code verifies the result. Code pulls candidate dates out of each page, and Gemma only picks between dates that code already found, so it can't invent one. Search comes from SerpApi (cached, with a hard cap), reminders run as Temporal workflows with times computed in code, and the app is FastAPI, vanilla JavaScript, and SQLite.
What broke
- Ollama was streaming its reply and breaking my JSON parser, and I blamed the model until I checked the raw response.
- Gemma answered with "November 9" instead of a date index, so I moved date selection into code.
- A path change made the app read an empty database. The tests still passed, and I caught it before deploying.
Limits
The live demo is a snapshot of 101 opportunities from 4 Oct. It doesn't refresh itself, because that needs a SerpApi key and a local model. The database is shared between visitors, and many pages have no date, so some cards still need a click-through.
The live demo is a snapshot of 101 opportunities from 4 Oct. It doesn't refresh itself, because that needs a SerpApi key and a local model. The database is shared between visitors, and many pages have no date, so some cards still need a click-through.
What's next (not built)
A scheduled refresh is the obvious step. One idea is a timed job on AWS, such as EventBridge triggering a function that reruns the search and updates the data.
A scheduled refresh is the obvious step. One idea is a timed job on AWS, such as EventBridge triggering a function that reruns the search and updates the data.
Try it
Live demo: https://beacon-hub-e3ag.onrender.com/ (the first load can take about a minute)
Code: https://github.com/hanfaimran/beacon-hub
Live demo: https://beacon-hub-e3ag.onrender.com/ (the first load can take about a minute)
Code: https://github.com/hanfaimran/beacon-hub
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article