You are probably here because I applied to one of your postings. It did not reach me through a job board or a recruiter — it came out of an index of the job market that I built and run myself.
mike@forn.al · Marlborough, Massachusetts · forward deployed / applied AI engineer
The index rebuilt 2026-08-13
| collected | 4,542,649 | |
| distinct | 2,193,724 |
Slightly more than half of what I collect is the same job posted to several boards at once. Collapsing those into one record — and giving it a key that survives the next rebuild — is most of the work. It is string comparison, not similarity: the key is parsed out of the apply URL, so the same posting keeps the same identity every time.
| Greenhouse | 8,441 | |
| Personio | 6,352 | |
| Welcome to the Jungle | 4,647 | |
| Y Combinator | 4,260 | |
| Ashby | 3,659 | |
| Lever | 2,847 | |
| Rippling | 2,308 | |
| Workday | 1,570 | |
| Notion | 970 | |
| Getro | 838 | |
| ADP | 825 | |
| iCIMS | 509 |
13 more vendors · 1,815 boards
Six of these vendors publish no documentation and no list of the companies using them. There is no directory to download, so the boards have to be found: enumerated out of archived URLs, mined from the apply links inside postings already collected, and probed against endpoints their own interfaces never expose in bulk.
Reading a board is the easy half. Knowing which boards are still alive is the hard half.
GET https://acme.jobs.personio.de/search.json 307 → https://personio.com 429 Too Many Requests read as: upstream is throttling us, back off and retry actually: this tenant was retired. the 429 is the marketing site, not the vendor. we backed off forever.
A retired tenant does not answer 404. Three of my crawlers read that redirect as polite backpressure and throttled themselves into the ground — nothing errored, nothing alerted, the counts just quietly stopped growing. Teaching them to tell a dead tenant from a busy one took one pipeline from 68 hours to 14 minutes.
A number on my résumé is wrong found 2026-08-14
It says 227 Supabase edge functions. It is 225 — the other two are _shared
and a node_modules directory, and neither deploys. I had published the
method next to the number, which is the only reason it was findable.
The arithmetic was fine; the method was wrong, and a wrong method reports a clean result forever. Every number on this page is produced by a command, and the command is printed with it.
What else I build
An AI-native commerce platform, built and operated alone: a customer types a prompt, a multi-model pipeline produces a print-ready asset, Stripe takes the payment, Printful prints and ships, a Stripe Connect payout goes to the creator. Two storefronts on one backend, launched 34 days apart — gp-tee.com and gpett.com, both answering 200 when I checked them on 2026-08-14.
Two of the four bugs I am proudest of finding are on that payment path. One was six
weeks of successful payments writing zero orders, behind a 200 on every delivery. The
other was row-level security that was switched on and inert, because
auth.uid() returns null under a third-party token.
The honest limit: the constraint turned out to be demand, not engineering. I proved the build and under-invested in distribution.
Experience
First US commercial hire at a UK-founded fund finance marketplace, between a UK engineering team and US institutional buyers.
Technical due diligence and solution design on tokenized private markets for global banks and asset managers.