New
Apps 1,895Makers 1,890This week 1,685
Promote your app−30%
Open Vacancy Radar screenshot 1
4views in
the last 30 days

Open Vacancy Radar

Local-first vacancy radar with direct ATS discovery and job-search workflows

Built with Claude Code

by jortega0033 · from NLNetherlands · added Oct 29, 2026
no ratings0 comments

An open-source, local-first Electron desktop app for discovering frontend-developer vacancies, tracking applications, and preparing CVs and cover letters. It uses no external database, no cloud account, and no API key held by this project. Application data lives in an embedded SQLite workspace on the user's own machine; AI features run through the reusable AgentDock runtime, which drives AI agent CLIs already installed and authenticated on the user's computer (starting with Claude Code and Codex), without this project ever receiving a password, token, or API key.

Open Vacancy Radar search workspace

Reference interface shown with sample data; production behavior comes from the Electron source in this repository, not the bundled prototype used to prepare the image.

30-second overview

If the player above doesn't render, watch/download the video directly. Illustrative UI recreation with sample data, not screen-captured footage of the live app.

What this is

A personal job-search workspace built around one worldwide/remote discovery pipeline: frontend-only roles from roughly 40 researched public sources (ATS APIs, RSS feeds, keyed job-board APIs), filtered for genuinely remote, non-US-only eligibility, with no default country, role, or salary bias baked in.

For any vacancy that normalizes to a Netherlands location, a best-effort check additionally looks up the employer by name against the IND's official recognised-sponsor register. This is a name-keyed, unverified lookup, not a curated evidence chain, so a match is always reported as a "possible sponsor match" rather than a confirmed one -- confirm the legal entity yourself before relying on sponsorship. Like every discovery source, it runs on a bounded budget: one scan resolves a capped number of distinct employers against Wikidata rather than one lookup per listing, and any employer it did not reach is reported as unchecked, never as unmatched.

The pipeline is deterministic-first: official ATS APIs and structured feeds before plain HTTP, before JSON-LD, before HTML parsing, with a narrowly-scoped headless-browser fallback only where nothing else works. See packages/vacancy-engine for the engine itself, vendored from the standalone ind-job-radar CLI project and ported from PostgreSQL to an embedded better-sqlite3 database so the desktop app needs no external services at all.

On top of that deterministic pipeline, an opt-in "Include AI web search" checkbox on the Search page can add one budget-capped session of your configured Claude CLI (its WebSearch/WebFetch tools only, no filesystem access) to look for further candidate vacancies. It only runs once a search profile is configured, runs alongside the job-site sources rather than before them (their results appear while it is still searching, and its own results are merged in before the scan is scored and saved), never blocks the rest of a scan if it fails or times out, and every candidate it returns is schema-validated and checked against the same blocked/prohibited source list as the deterministic sources before being accepted.

The AI step searches for what you submitted for that scan, not for your saved profile. When you run a search, the role, country, employment type and minimum salary you entered are captured once as the scan's intent and given to both the AI step and the job-site sources, so editing your profile while a scan runs cannot change it. A role, country or employment type is passed to the AI as wording for its web searches; a minimum salary cannot be searched on the web, so the AI is told that and the same local filter that checks every other source applies it afterwards (salaries the AI reads from a page are unverified, so with "hide unknown salaries" on they do not pass). Your saved profile only adds background (skills, years of experience, and your home country when you did not pick one) and its target roles are never searched. "Browse all" stays browse-all: no role is searched. "Unspecified location" is not a place: the AI is told you want vacancies that name no country, is not asked to search for it, and the local filter keeps only such rows. The scan panel shows what the run is searching for, and "Copy diagnostics" lists how each criterion reached the AI step, using search terms and counts only, never CV text or the prompt. The local filter still decides what is kept.

Search plan. Under "What you are looking for" you can approve a search plan: a primary function, an ordered list of equivalent titles (for example React developer, Frontend engineer, UI engineer), the working language each title is written in, and the markets you care about. It describes the work you want, not facts about you, and it works by hand with no AI. Titles suggested by the CV tools ("Best-fit roles") or an AI answer wait in a separate list and are searched only after you add them. When a plan has titles, the Search page offers "Also search my N approved titles"; it starts off, shows the titles it adds, and remembers your choice. The title you type is searched first, exactly as before; then each approved title is asked only of the job sources that can filter by title (the others return one feed whatever the title, so they are not asked again and the title is matched locally).

The whole plan -- the typed query's discovery pass, including fixed feeds and the company-roster batch, and every approved title -- runs inside ONE declared allowance: a number of network requests counted at the HTTP client before each request is made (every retry and redirect counts), one deadline that also cuts a request still in flight, and one limit on matching rows. The allowance covers the search stage only, and the limit applies to the rows that stage returns. These run the same with or without a plan and are outside it: the Workable and MPSV bulk snapshots (their attempts are reported as usage.outsideEnvelopeAttempts), official employer checks (counted in the run statistics) and sponsor lookups (reported as sponsor counts). Rows from those snapshots, and from the AI web search, that match an approved title are kept but are not counted against the result limit, and the run's wall time is the search stage plus those fixed stages, not the allowance alone. Titles the allowance did not reach are listed as deferred, and each source row says whether that title really reached it, was only partly asked, or was not asked, with the reason, in the results notice and in "Copy diagnostics". A call that reached a source and was then stopped (a retry the allowance refused, or a request cut by the deadline) is counted as a request and said to have been "stopped after asking"; only a call refused before any network contact is "not asked" and counts as zero. A row is kept when the typed query matches it as before, or when an approved title appears in its title (a title only mentioned in a description does not count); each job says which query found it and whether the typed query matched only its description. Most of what extra titles add comes from that local matching: a source with no title filter returns one feed whatever it is asked, so the feed the typed query already fetched is matched against every approved title for free, and an extra title request helps only at sources that filter by title and only when the allowance reaches it. In the repository's synthetic, author-labelled measurement the extra jobs at the tight and moderate allowances all came from that local matching and no extra title request fetched one; title requests added jobs only at a generous allowance. Markets and the primary function are kept with the plan but do not filter searches (use Country for that); the report and diagnostics say so per source. The language you pick for a title is a label kept for provenance and display only: no source is filtered or queried by language, and it never makes a job count as requiring that language, which only an explicit statement in the advert does. A job whose title matches an approved title is treated as in scope by the engine's pre-classification, so the usual language, location and salary checks apply to it -- but only when its title carries none of the role families the product always excludes (full-stack, back-end, manager, director, mobile, QA and the like): "Frontend Engineering Manager" stays a role mismatch. A run takes a frozen copy of the plan when it starts, so editing or removing a title changes the next search only, and finished reports keep their own copy. If the plan was empty or could not be read when a run started, the results say so. A damaged plan file, or one written by a newer version, is never overwritten by an ordinary save: the editor says which it is and offers "Start a new plan", which first keeps the file's exact bytes as a timestamped backup next to it. The unattended background scan uses the same plan and allowance in one run instead of scanning each saved role (falling back to the first saved role, and saying so, when the plan cannot be used); the background company-board scout checks only the first approved title, with no plan allowance of its own. Without approved titles, or with the switch off, a search is the single typed query it always was. Same-requisition duplicates are merged by the existing identity rules; different requisitions with the same company and title stay separate.

Vacancy coverage is a product requirement: a lawful public source is integrated by default in the broadest mode its evidence supports. Sources that take longer to implement remain planned. Unclear republication rights use a factual linked index; explicit prohibitions, authentication or partner boundaries, and technical access controls stop ingestion. See the job source policy.

On top of that, the desktop app is a full personal tracker:

  • Search: run the pipeline on demand, optionally add the AI web search pass above, save leads, or open the CV assistant on any result.
  • Saved Jobs / Applications: track status end to end, with confirm-before-delete and a short undo window on every delete. An application in recruiter-screen or interview stage can also generate a review-only interview-prep pack (likely questions, claims to defend, gaps, STAR candidates) built only from data already in the workspace, never invented and never auto-saved.
  • CV Library: upload or hand-enter multiple CVs, one marked default, each usable for AI gap-analysis against a specific vacancy. A scanned or image-only PDF with no selectable text can fall back to AI transcription with your explicit consent; you review and correct the text before it's saved, and the CV record keeps track of which source produced it.
  • Letters: generate motivation letters, cover letters, recruiter messages, or short application-form blurbs from a CV + vacancy pair, in a chosen tone and length, with a library of saved drafts.
  • Settings: theme (light/dark/system), density, default market, application-data reset, documented manual backup, and (under Workspace) saved answers to recurring application-form questions that are reused only when you explicitly click "Use this answer," all persisted locally.
  • AI Runtime: the AgentDock provider panel: see each installed CLI's authentication, version, and capabilities, set one as the default AI features run through, and verify it without spending a model call.

This desktop app relies on CLIs the user already has installed and authenticated, not its own subscription or API key: if a user already has claude or codex installed and logged in, Open Vacancy Radar can use that existing session for local AI workflows (gap analysis, letter drafting). The installed CLI stays the sole authentication and provider boundary; this project never receives a password, token, or API key for those AI CLIs. (A separate MCP job-source layer exists in the daemon; it ships with one reviewed, no-auth provider registered — InfoSec Job Board, a public vacancy-search MCP server that needs no credential — while generic credential-bearing/OAuth MCP providers remain a future, separately reviewed integration. There is currently no screen in the app that surfaces this provider; see SECURITY.md.)

Renderer (React) ──IPC──▶ Electron main ──@agent-dock/client──▶ Local Daemon (Fastify, protocol v1)
                              │                                          │
                    embedded SQLite (workspace +               Unified Agent Runtime
                    vendored vacancy-engine)              ├── Claude Code adapter ──▶ claude CLI
                                                           └── Codex adapter ────────▶ codex CLI

The renderer never calls the daemon directly. Only Electron's main process does, through the typed @agent-dock/client SDK. See SECURITY.md for why, and Client SDK below for what that package looks like from the outside.

See docs/architecture.md for the full breakdown, and SECURITY.md for exactly what protects the daemon and why.

Where do I start?

What this is not

  • Not an unofficial authentication bypass. Every CLI call goes through the real claude/codex binary using its own login state. Nothing here reads, copies, or reverse-engineers credential storage.
  • Not a token extractor or an API proxy. This project never makes a direct Anthropic/OpenAI API call and never asks a user for an API key in CLI mode.
  • Not a hosted job portal or cloud account service. Application data stays in the local desktop workspace, and the AI runtime remains provider-neutral infrastructure.
  • Not a replacement for Claude Code or Codex. It's a thin, provider-neutral shell around them.

Repository layout

apps/
  desktop/          Open Vacancy Radar Electron + React application
                       src/components/{search,saved,applications,cv,cv-library,letters,runtime,settings}/
                       electron/workspace/  : the personal-data SQLite schema, IPC, repository
  daemon/           Standalone local Node.js service (Fastify), runnable without Electron
packages/
  vacancy-engine/   The worldwide/remote discovery engine (plus a best-effort IND sponsor check),
                     vendored from the standalone `ind-job-radar` CLI and ported to embedded SQLite
  agent-runtime/    Provider-neutral runtime: process management, adapters, normalized events
  client/           @agent-dock/client: typed daemon SDK (HTTP+SSE, auth, protocol version check)
  shared/           Types, Zod schemas, and the protocol v1 AgentEvent contract everything else uses
  application-executor/  CDP-based application-form field mapping, readiness, and submission-outcome
                     primitives behind the desktop app's auto-apply review pipeline, currently
                     hidden from the UI pending further scoping

Design tokens and shared UI primitives live in apps/desktop/src/styles/tokens.css (the single source of theme/color/spacing truth, see DESIGN-TOKENS.md) and apps/desktop/src/components/shell/ (ConfirmDialog, UndoToast, EmptyState, and the app shell), reused across every page rather than redefined per feature.

Requirements

  • OS: Windows 10 or later, 64-bit (x64). The packaged installer is Windows-only for now; see docs/packaging.md#platform-matrix.
  • Node.js: >=22 if you're running from source (see engines in package.json). Not needed to just run the packaged installer.
  • An AI provider CLI, for AI features only (search, saved jobs, applications, and letters work without one): install Claude Code and/or Codex first, then authenticate the CLIs you want to use, independently of this project:
# Claude Code
claude auth login
claude auth status

# Codex
codex login
codex login status

This project never automates account signup or handles credentials on your behalf, and never installs a CLI for you. Do all of that directly with each CLI first; see docs/troubleshooting.md#claudecodex-not-detected-installed-false if the app doesn't detect a CLI you've already installed.

Getting started

pnpm install

# Run the daemon on its own (no Electron required)
pnpm daemon

# In another terminal, verify it's alive
curl http://127.0.0.1:<port>/health

The daemon prints its listening URL and where it wrote its discovery file (port + auth token) on startup. See SECURITY.md for what that file is and why the token exists, and docs/daemon.md for everything else about running it standalone.

To run the full desktop app (spawns the daemon automatically):

pnpm dev:desktop

No database server or setup step is required: both the vacancy-engine's own database and the personal workspace database are embedded SQLite files under Electron's per-user app-data directory, created and migrated automatically on first launch. pnpm install's postinstall step also rebuilds better-sqlite3's native binding against Electron's ABI (electron-rebuild). A plain node_modules install alone is not sufficient for the desktop app to run.

Everyday commands

pnpm build       # build every package
pnpm typecheck   # strict TypeScript across the whole workspace
pnpm test        # unit + integration tests (no real CLI calls; see docs/providers.md)
pnpm lint        # ESLint

To run vacancy-engine's discovery pipeline standalone, without the desktop app or daemon: copy packages/vacancy-engine/config/.env.example to .env and fill in the keys you need, then

pnpm scan            # global-remote:scan -- the full worldwi

Description taken from the repository README.

Ratings

–
0 ratings
5 0
4 0
3 0
2 0
1 0

Rate this app

Sign in to rate

Comments 0

Sign in to comment

No comments yet.

More from jortega0033

All apps by this maker

Related apps

#jobs · #ats · #local-first