NanoApply
AI job-fit assistant running Gemini Nano fully on-device — no server, no API keys, nothing leaves the browser.
Solo — design, product, engineering
View on GitHubThe problem
Job seekers spend hours tailoring resumes to postings, mostly guessing at what the recruiter's ATS is scoring for. Existing tools (Teal, Rezi) send resume and posting text to a server, which is a privacy problem and a running cost problem for anyone applying to 20+ roles a week. I wanted the assistant to live on the same tab as the job posting, run on-device, and cost me zero to keep in front of me.
The approach
Chrome MV3 side-panel extension so the assistant stays open while the user browses. All AI inference runs through Chrome's built-in Gemini Nano via the Prompt API — no OpenAI key, no backend, no per-token cost. Storage is chrome.storage local-only. The one network call the extension makes is anonymous GA4 event pings for install / scan / build counts, opt-out in settings.
Selected details
- Side panel architecture chosen over popup so the extension survives tab switches — critical when you're moving between LinkedIn, company careers pages, and Notion tracking.
- Resume input starts as plain text — deliberate MVP scope. PDF/DOCX parsing is Phase 2 to avoid shipping a broken parser.
- Scoring prompt is a strict JSON schema with
fit_score,missing_keywords, andtailored_bullets— no free-form output to avoid Gemini Nano hallucinating structure. - CV builder opens a full tab with the tailored resume, click-to-edit sections, and Save as PDF via the print dialog — no PDF library, no bundle bloat.
- GA4 pings never include resume or job posting text, only event names and counts. Documented in
PRIVACY_POLICY.mdso the Chrome Web Store review passes cleanly.
Outcome
MVP shipped and running against real job postings on LinkedIn, Naukri, Instahyre, and company career pages. Score-first-pass matches recruiter intuition ~70% of the time on my own applications. Phase 2 (PDF parsing, foreign-role cost-of-living compare) is planned; Phase 3 is auto-fill of application forms.
What I'd do differently: Ship the CV builder as a separate flow from day 1 rather than bolting it onto the analyzer — the two have different user intent and got tangled in the sidepanel.js single-file architecture.
Stack