← Back
AI job boardAnti-fabrication AIPrivacy-firstLive

CareerLM

A job board that reasons about fit — and an AI resume writer that won't invent a number about you.

Most job tools optimize for volume: more listings, more applications, more polish. CareerLM optimizes for the two things that actually decide an outcome — whether a role is worth your time, and whether the resume you send is one you can defend in the interview.

9

Job sources

scraped + AI-classified daily

3

Deployed services

Vercel · Railway · Supabase

On-device

Resume matching

never uploaded

2-phase

Resume writing

diagnose → verify → write

Not a bigger board, a sharper one

Most job boards are built around two numbers: listings indexed and applications sent. CareerLM is built around a third that neither the board nor the applicant usually gets to see — whether a given role is actually worth your time, and whether the resume you'd send is one you can defend in the room.

The public side scrapes and AI-classifies remote and AI-focused jobs from nine sources every day. The logged-in side turns that raw board into a decision surface: a personalized ranked feed, deep company dossiers, an application tracker, weekly email alerts, and a resume writer with a conscience. It's a full product, deployed and live, built end to end — web app, scraper pipeline, AI worker, payments, and infrastructure.

Not a bigger board, a sharper one 1
Not a bigger board, a sharper one 2

A live career-intelligence platform on top of a scraped, AI-classified job board: a personalized ranked feed, in-browser resume matching that never uploads your resume, 8-layer company dossiers, an application tracker, and a two-phase resume writer that refuses to fabricate facts.

The resume writer that asks before it assumes

Most AI resume tools will polish anything you hand them, including numbers you can't back up. Ask one to make your experience sound impressive and it will happily write "boosted revenue 40%" over a job where you never saw a revenue figure. CareerLM's writer is built in two phases specifically to make that failure impossible.

Phase one is a free diagnosis. A fast model reads the job description against your verified skills, finds the gaps, flags metrics that look rounded or invented, and asks up to five targeted questions — the exact bullets it can't responsibly write without your input. Then it stops and waits for you.

Phase two only runs after you answer. Your answers are used verbatim: an exact figure stays exact, and any fact you didn't supply becomes a visible [add metric] placeholder in the draft rather than a number the model made up. It also downgrades inflated verbs ("spearheaded" back to "led") and runs a 21-pattern lint against the tells that make writing read as machine-generated. The count of unfilled placeholders is shown to you, so the honesty is on the page, not buried in it.

Ranking with no oracle in the loop

The personalized feed doesn't ask a language model what to show you. It's a pure, deterministic engine: a role-family taxonomy, hard-gate filters for the things you've ruled out, compensation banding, and a Tier 1/2/3 assignment — all driven by a saved "lens" (your target roles, comp floor, work modes, and exclusions). Because there's no model in the ranking path, the feed is fast, reproducible, free to run, and explainable line by line.

The AI sits on top as an option, not a dependency. Turn on explanations and the engine attaches a cached "why this fits" note per role; leave it off and nothing about the ranking changes. The whole engine is unit-tested as a pure function, which is what lets it stay trustworthy as the rules grow.

Your resume never leaves your device

The in-browser matcher embeds your resume with a sentence-transformer model that runs entirely client-side — the text is never uploaded, which is the whole point for anyone searching quietly while still employed.

Making that instant for a first-time visitor is the interesting part. Rather than have every browser download a model and score it against thousands of jobs, the server pre-computes an embedding for every listing, quantizes it to about 512 bytes, and ships that compact corpus down. Your device only has to embed one thing: you. Results are cached per-device, so the second visit is instant too.

Your resume never leaves your device 1

A pipeline that maintains itself

Every day a pipeline pulls from nine job boards and ATS providers, filters by title, deduplicates three separate ways (canonical URL, source id, and a content hash), enriches each posting with seniority, work mode, salary, and normalized location, then upserts. Postings that vanish from their source get marked inactive, so the board never fills up with ghost jobs.

Running that daily on a small budget forced real optimization. The ATS scrapers send conditional-fetch headers and skip any board that answers "not modified," which cut the daily pull from over 100 MB to mostly tiny 304s — while a periodic forced refresh keeps stale-job detection honest. A separate AI pass classifies each job's role, required skills, and employment type, kept out of the scrape path so a re-scrape never wipes it. Homepage stats and filters resolve in a single database round-trip instead of pulling every active row over the wire.

A pipeline that maintains itself 1

Getting the money right when things fail

Paid actions run on either credits (Stripe Checkout) or a user's own model API key. The hard part isn't charging; it's charging correctly in an asynchronous system where the AI work happens on a separate worker minutes later. CareerLM enqueues each job into a durable task queue that leases, executes, retries, and dead-letters on its own while the web app polls for status — which is how a 30-second generation survives a 10-second serverless limit and the occasional restart.

Billing is refund-safe end to end. A charge rides along with its task, and the worker refunds it exactly once if the job ultimately fails; duplicate submissions and enqueue failures refund inline. Refund idempotency reuses the same database uniqueness guard as purchases, so no retry or race can double-refund. Bring your own key and it charges nothing at all.

Security as the default posture

Every per-user table is protected by row-level security, and the service-role key that can bypass it is confined to server-only code. Calls from the web app to the worker are HMAC-signed with a timestamp to block replays; cron endpoints are secret-gated. Bring-your-own-key credentials are encrypted at rest with AES-256-GCM and capped by a daily spend limit.

A dedicated hardening pass closed an access-control bug where a user could reference another user's private dossier, locked the credit-granting database functions to the service role, made credit grants idempotent, and made the resume writer treat everything the browser sends — answers included — as untrusted input to be validated and delimited against prompt injection before a model ever sees it.

Highlights

Under the hood

Next.js 16 / React 19FastAPI workerSupabase + RLSDurable task queueDeterministic ranking engineOn-device embeddingsStripe + BYOK billingHMAC service auth

Current status

Live and in active development at careerlm.app — built end to end as a solo project spanning the Next.js web app, a Python scraper and AI-worker service, the Postgres schema, payments, and deployment.

View live ↗