ZyVOP Logo
Content That Connects
SeriesAI NewsLeaderboardWrite for Us
ZyVOP Logo
Content That Connects

Empowering developers and creators with cutting-edge insights, comprehensive tutorials, and innovative solutions for the digital future.

Content

  • Categories
  • Tags
  • Badges
  • Leaderboard
  • Write Article
  • Newsletter

Company

  • About Us
  • API Documentation
  • Write for Us
  • Contact

Connect

  • Privacy Policy
  • Terms of Service
  • Cookie Policy
  • DMCA Policy
  • Code of Conduct

© 2026 ZyVOP. Crafted with care for the developer community.

Made with ❤️ by the ZyVOP team
All systems operational
HomeProduct ShowcaseWhy I built my own local-first job-search tool (and how it works)
Product Showcase

Why I built my own local-first job-search tool (and how it works)

A free, open-source, privacy-first alternative to Jobscan — 78 job sources, AI scoring against your CV, zero cloud.

Сергей Емельянов
Сергей ЕмельяновSenior Developer
August 11, 2026
4 min read
Why I built my own local-first job-search tool (and how it works)
#AI#Ashby#job-search#job#open-source#free#Claude#Greenhouse#self-hosted#Jobscan
👍2

I'm a backend engineer, and a while ago I was job-hunting while working full time. The painful part wasn't emotional — it was engineering-inefficient. hh.ru mixes senior roles with junior noise, the same job gets reposted under new URLs, salaries are hidden or in random currencies, and half the interesting roles live in Western ATS (Greenhouse, Ashby, Lever, Workday) that hh.ru never sees. Decomposed honestly, it's a classic ETL problem over messy sources — so I built a tool for it.

career-ops-ui is a free, open-source, local-first job-search command center. It aggregates 78 sources into one de-duplicated feed, AI-scores every posting against your CV on a six-dimension 0 to 5 rubric, tracks each application through a CRM-style funnel, and generates ATS-optimized PDF resumes. It runs entirely on 127.0.0.1 — no cloud, no telemetry, no auto-submit, no account. Your CV and applications never leave your machine.

Under the hood, the frontend is vanilla ES-module JavaScript with no build step and no virtual DOM — chosen for auditability (it handles PII), a tiny supply chain, and a strict script-src 'self' CSP with no unsafe-inline. It's a companion over the terminal engine santifer/career-ops and reads the same flat-file source of truth (cv.md, config, data) read-only. The scanner is a two-phase SSE stream with deterministic termination, and all egress goes through an SSRF envelope with DNS-pinning: resolve once, pin the IP, re-validate on every redirect — so a job URL you paste can't reach your cloud metadata endpoint.

It ships in 17 languages including right-to-left Arabic, is fully keyboard-accessible, and is locked down by around 2,300 tests plus an agentic end-to-end pass that walks every route in every locale. You bring your own LLM key (Anthropic, Gemini, OpenAI, Qwen or OpenRouter) and pay your provider directly. It's MIT-licensed, and every application still stays a manual, deliberate decision — the tool gives you precision, not spam.

Let me go a level deeper on the parts I'm most proud of. This is a companion app, not a fork: it sits on top of the terminal engine santifer/career-ops (around sixty thousand GitHub stars) and treats it as a read-only dependency. Both processes share one flat-file state — your resume is cv.md, your target roles and deal-breakers live in config/profile.yml, and the application tracker is data/applications.md. There is no database as the source of truth, and that is deliberate: the state is git-diffable, portable by a plain copy, and readable by a human without a special client. Any index is just a derived cache you can always rebuild from the files.

The scanner is where most of the messy-source pain gets solved. Every source runs through one pipeline — normalize, filter by your title keywords, de-duplicate against three files at once (scan history, pipeline, and applications), then append. De-duplicating against all three is what kills the reposted-job problem: a role you already applied to a month ago never resurfaces as new, no matter which fresh URL a company re-publishes it under. Sources live in two registries — an auto-discovery one that drives the dropdown, and a hand-maintained ALL_ADAPTERS list the fetcher actually walks — and a test locks the two together, so registering a board in one but forgetting the other turns CI red instead of silently returning zero results.

Scanning streams its progress over Server-Sent Events, in two phases (ATS boards, then regional portals) on a single connection, with a final flag on the terminal event so the browser never closes early and drops the second half. A failed source degrades softly: one board returning HTML or a 429 doesn't abort the sweep over the other seventy-plus. It's the kind of determinism that's boring when it works and infuriating when it doesn't — so it is pinned by tests rather than left to luck.

Security got more attention than a personal tool probably deserves, because the server fetches arbitrary job URLs that you paste — a textbook SSRF surface. Every outbound request goes through one guarded path: a single DNS lookup, reject anything resolving to a private, loopback, or cloud-metadata address, pin the exact IP so there is no rebind between the check and the connect, re-validate on every redirect with a hop cap, and truncate the response body with a streaming byte cap. Without pinning the IP, a domain could resolve to a public address during validation and to 169.254.169.254 during the actual connect — DNS-pinning closes that window.

Quality is enforced by process, not vibes. Under roughly 2,300 node:test cases sits the interesting part: an agentic end-to-end pass, where a spec-regression document is handed to an AI agent that drives a real browser — walking every route across all 17 locales, exercising every filter, and probing the SSRF gate with real requests to loopback and the metadata IP — then writing a GO or NO-GO verdict. It's the second pair of eyes that solo development usually lacks. The method behind it is Spec-Driven Development: the spec, not the code, is the source of truth; each release fixes observable invariants, every invariant gets a test-lock, and one fix ships per release so the diff stays reviewable. Across twenty-plus releases, no regression has reached a user.

A couple of things I'd genuinely like other engineers' opinions on. First: a build-less, virtual-DOM-less frontend at this size — reasonable trade for auditability and a tiny attack surface, or tech debt I'll regret? Second: does Spec-Driven Development plus one-fix-per-release actually scale for a solo project, or is it over-engineering dressed up as discipline? The project started as pure utility — I needed a job and the existing tools solved the problem badly — but it turned into a nice sandbox for local-first architecture, and I'm still not sure which of those two framings will age better.

Repo: https://github.com/Fighter90/career-ops-ui — Site: https://cvstart.org . Feedback and contributions are very welcome. Connect with me on LinkedIn: https://www.linkedin.com/in/sergei-emelianov-in-job/ — thanks for reading.

Сергей Емельянов

Сергей Емельянов

Passionate developer sharing knowledge about modern web technologies and best practices.

Comments (0)

Login to post a comment.

Related Posts

Why I score contractor websites for after-hours leak risk before I sell anything

I score HVAC and plumbing public sites for after-hours leak risk before any paid offer. Free radar first; fixed 149 USD audit only if they want the write-up.

Read article

OpenRouter: One API Key to Rule Them All 🔑

Overview Hey everyone 👋 Managing AI models in 2026 has gotten ridiculous. Anthropic key here, OpenAI key there, a separate billing account for Google, another ...

Read article

I got tired of paying $50/mo for AI video tools so I built and open-sourced my own

Honestly? I just got frustrated. I was paying for like 3 different AI video subscriptions, burning through credits, and still not getting what I wanted. So I did what any reasonable developer would do - I spent way more time building my own thing than I would've ever spent on subscriptions.

Read article

How I Cut AI Video Generation Costs by 8x

A few months ago, I was working on a pet project. YumCut is an end-to-end service for creating short vertical videos, from writing the text and generating images to editing and adding subtitles.

Read article

Developers now install the author, not the skill: 'matt pocock skills' searches are up 160%

The fastest-rising Claude Code skill search is an author's name. Setup skills with 540k installs, lockstep family installs, and 97 of 100 trending slots held by six authors: the adoption unit is now the author.

Read article