Case study · Side project · 2026
A job-search pipeline with a fabrication guardrail
A private dashboard I built and run on a small home server. It finds relevant roles, rates them against my background, tailors a resume for each one, and tracks every recruiter conversation. It is also where I test ideas about AI, trust and automation on a system whose failures cost me directly.
- Role
- Sole designer, developer and tester
- Stack
- Python, FastAPI, SQLite, React, Vite, Docker Compose, Claude API, Cloudflare
- Tests
- 1,005 backend, 159 frontend, mypy strict
What it does
- Scans job boards. Adapters for six applicant tracking systems read public job APIs, one request per second per host, and filter to fully remote US testing roles before anything is stored.
- Rates each role. Claude scores a posting against my background, with a short reason and a list of the requirements I do not meet.
- Tailors a resume. For a chosen job it produces a resume in Word and PDF, drawn only from a verified list of facts.
- Finds the real posting. A job alert links to a job board. The pipeline finds the same job on the employer's own site, verifies the title and company against it, and saves where to apply.
- Tracks outreach. It reads recruiter mail through a read-only grant and keeps each thread's status, rate and next step.
The guardrails are the design
Nothing invented reaches a resume
The model is allowed to select, reorder and reword bullets from my own resume, and nothing else. The backend rejects any bullet that is not in the source outright, and flags any rewording that introduces a number, a company or a technology the original did not have. That validator has its own dedicated tests, because it is the one part of the system that must never regress.
Automate the preparation, never the submission
It never submits an application and never sends an email. The mail grant is read-only, and a test asserts that no send or modify scope ever creeps in. Mass-applying is why response rates collapse, so the pipeline's job is to make each application fast and specific, then hand it to me.
Defense in depth on a home server
The only way in is an outbound Cloudflare Tunnel, with no port published on the host; the build fails if one appears. Cloudflare Access sits in front, and the app still verifies the Access token itself on every request, because an edge check in front of an open origin is a bypass waiting to happen.
A measurement that changed the plan
Across 25 company boards and about 1,870 remote postings, only 6 were genuinely relevant US remote QA roles. Large product companies fold testing into general engineering titles. Measuring that early redirected the effort from a bigger company list, which would not have helped, to reading job-alert mail and finding each alert's posting on the employer's own site.
How it is tested and shipped
- Pytest across unit, integration and API layers; Vitest and Testing Library for the React screens.
- A pre-commit hook runs what CI runs: ruff, format checks, mypy strict, the compose guard and the frontend tests.
- CI runs on a self-hosted GitHub Actions runner on the same server, and every deploy starts with a verified database backup.
The repository is private. I am happy to walk through the code in an interview.