← Back

Personal project

Kestrel

I built Kestrel while looking for my next role. It finds job offers, tells me which ones are worth my time, prepares the CV and the letter, and keeps track of each application.

My role · Everything, for one user: product, design system and code, in TypeScript and Next.js with AI coding tools. No team, no client, no deadline but my own job search.

01 · The map

How the pieces fit

I wanted everything to hang off a single record per offer. It comes in through a board’s API, from LinkedIn’s public pages or from a page I add myself, and from then on it is one row that gets scored, gets its documents and collects whatever happens until the company answers. Three rules from day one: it is for one person, my data stays on my computer, and nothing is sent without me pressing the button.

Kestrel architecture: UK job boards feed the Supabase offers database through APIs and scraping; the Kestrel app in the centre; Jev resolves duplicates and emails; Gmail, the Chrome extension and the portfolio connect back to the app
Sources and the extension on the left, Kestrel and its database in the middle, the model, Gmail and the portfolio on the right.

The pipeline

  1. 01FetchEvery source, in parallel
  2. 02DiscoverNew boards from links
  3. 03EnrichFull text before scoring
  4. 04EvaluateScore and deal-breakers
  5. 05DedupeOne record per job
  6. 06SaveJobs and their events
Every offer gets its full text before it is scored. Each saved offer also keeps an event log, which is what the history tab shows.
Kestrel dashboard after a run: three stage cards and the Ready to apply list with scores, salaries and locations
The list after a run. The companies and offers in all the screenshots are made up.

02 · What you tell it

It only knows what I wrote down

Seven tabs I filled in once. Each field says on screen where it is used, because I kept forgetting which one fed the forms and which one fed the search. Most of the deal-breaker words got there after an offer slipped through that should not have.

01

Profile

The facts forms ask for

Right to work, expected salary, notice period, standard answers.
02

Search

Which terms, at which priority

Salary floor, office cities, remote regions, max age.
03

Deal-breakers

Word lists

Titles, sectors, companies and tools that dismiss.
04

Documents

CV and letter parts

Written once; every job gets its copy.
The seven tabs. Each field says on screen where it is used.
Settings, Search tab: search terms in three priorities, high, medium and low, and the salary floor
Search terms by priority. The high ones are searched more often and read deeper.

03 · The score

What counts, and what I decided should not

The first version scored everything, including how much of my CV the ad covered. It felt thorough and it was useless: nearly every job landed in the same band. I cut it down to two parts, title and topics in the description. Location, salary and deal-breakers still decide whether an offer is kept, with the reason written next to it, but they no longer move it up the list.

80

Title

Patterns with points, scaled to the highest

Perfect 80 · good 60 · acceptable 40. The title decides the order.
20

Skills

Topics in the description, by level

Very important 6 · important 3 · nice to have 2. What Settings shows is what counts.
The score only orders the list. Only the deal-breakers remove something from it.
Job detail for a fictional Senior Design Systems Designer role with a score of 100 and the reasons behind it
A job page: the score and what it is made of.

04 · CV and cover letter

One CV, one copy per job

I write the CV once. For each job Kestrel makes a copy: it picks the title from my list closest to the advert and moves the most relevant bullets up. It never writes a line I did not write. The letter is assembled from the three achievements on my list that fit the description best. Both come out as PDFs with a portfolio link unique to that application.

Tailored CV tab showing a rendered CV for Carlos Borghes adapted to a fictional Northwind Studio role, with 100% coverage
My CV, reordered for a made-up job, with the keyword coverage at the top.
Cover letter tab for the fictional Northwind Studio role: the assembled letter text with copy and PDF buttons
The letter for the same job, assembled from three of them.

05 · The way out

It fills the form. I press send.

On an application page I click the extension. It fills in what it knows, attaches the CV and the letter and marks what it could not answer. I read it through and submit it myself; it never clicks the button. Afterwards, portfolio visits and the Gmail emails I file land on that application.

Process view for a fictional Kite Travel application: a recruiter call marked done, a portfolio review scheduled, and the match score
A process: a recruiter call done, a portfolio review coming up, and the questions I prepared.

06 · Design engineering

The part I care most about

If Kestrel decides something, I can see why in the interface: the score with its points, the dismissal with the rule it tripped. Nothing hides behind a number.

Three decisions I would make again. The CV is one module that renders in Settings, on the job page and in the PDF, so what I preview is what the recruiter receives. The Score page explains itself with three invented jobs, because a rule I cannot read in the app is a rule I will not trust. And the extension stops before Submit, not because it cannot press the button, but because sending an application is my promise, not the tool's.

Where it stops is deliberate too: when it is not sure, it flags the item instead of deciding.