Skip to Content
VOL. 01 - THE CLERKNAIROBI, EAT (UTC+3)MMXXVI

v0.4 · self-hosted · MIT · Python 3.11+

Job hunting is a data pipeline problem.

Nine job boards, thousands of postings a day, and maybe five of them are a role you would take at a company that would pay you properly. Finding those five takes forty-five minutes a day and still misses most of them.

karani is a clerk for that problem. It reads every posting, drops the ones that fail your rules before any model is called, scores what survives against your actual resume, drafts the whole application — and hands you the pen. You press submit. It never does.

What it does with a posting

Two tiers, and only one of them costs money

The first tier is arithmetic. Role family, seniority, geography, remote status, comp anchored to a currency keyword, skill overlap against your profile — all word-boundary matched, all deterministic, no model involved. About 95% of a pass ends here, for free.

karani run
karani run
hunting 9 sources
fetchedgreenhouse, lever, ashby, workable612
fetchedremoteok, himalayas, wwr, remotive, aijobs1,044
droppedrole not engineering-adjacent898
droppedseniority below the floor311
droppedregion-locked, no relocation offered274
droppedcomp below $160k where disclosed63
droppedno skill overlap with the profile51
skippedalready seen this week (canonical hash)32
flagged1 source returned 0 rows: workable--
pass complete: 1,656 fetched, 27 pending, 0 model calls, $0.00

That split is the whole architecture (ADR 0001). Running a model over every RemoteOK posting would be expensive and no better; running one over the two dozen that survived is cheap enough to be worth doing carefully.

What a model is for

A verdict you can argue with

The second tier reads the survivors against your resume and comes back with a score, a three-way verdict, and claims about you that each carry the line of your own resume behind them.

qualified91/100Staff Backend Engineer, Platform at Northwind Roboticskarani qualify

Strengths

Has owned an event platform at this scale before
Owned the event platform at a 40-engineer company: Kafka to ClickHouse, 11 downstream teams, 2.4bn events a day, p99 ingest lag under 90 seconds.
The posting's deadline-scheduling problem is one she has shipped a solver for
Rebuilt the dispatch planner for a Nairobi logistics operator: replaced a nightly batch with an incremental solver, cutting median delivery time from 2h10m to 45m across 1,400 daily drops.

Gaps

lowKubernetes operator experience
She has run workloads on Kubernetes but has not written an operator. Name the control-loop work in the dispatch planner, which is the same shape.

Recommended positioning

Lead with the dispatch planner, not the event platform. The posting is about reconciling state across regions on a deadline, and that is the harder of her two stories.

Hires globally at one band, states it in the posting, and the fleet-scheduling work is a direct match for the dispatch rebuild. Apply today: posted two days ago.

qwen3:4bqual-v7

Every strength has a quote. That is not a prompt asking nicely: a claim whose citation karani cannot verify in your own records is dropped, not repaired (CLAUDE.md 4.13 ), because a fabricated story gets probed for ten minutes by someone who interviews for a living.

The disagreements are the useful part. When karani says skip and you would apply, tell it — karani verdict 1544 apply — and that pair becomes a few-shot example in the next qualify prompt. Its taste converges on yours with no retraining.

The last mile

It fills the form in. You send it.

karani console writes one self-contained HTML file: every pack waiting to be sent, each with the apply link, the rendered documents, and a field-by-field answer sheet with a copy button on every field. No server, no port, no process — it opens from iCloud on a phone, which is where a queue actually gets cleared.

karani console
karani console
reading 3 packs in drafting or ready
fetchedapplication questions from greenhouse1
fetchedapplication questions from lever1
flaggedashby exposes no form; using the standard field set
delivereddata/submission_console.html 3 jobs
pass complete: 0 model calls, $0.00
Open it, send each one, then run:
karani submitted 1482 1511 1523

The sheet answers that job’s real questions. Greenhouse, Lever and Ashby publish their application forms on the same public endpoints karani ingests from, so the wording and the option values are the company’s own rather than a guess at a standard form.

And every answer says where it came from. Work authorization, sponsorship, relocation and start date are resolved from legal facts you declare and sign, by code, never by a model — the same question has opposite correct answers depending on the role’s shape, so “the model usually gets it right” is not a defence (ADR 0023). Demographic questions are left blank. Anything karani has no source for is flagged rather than invented.

Ways in

One pipeline, five front doors

All five are thin adapters over the same storage and the same runners, so they cannot disagree about what a verb does.

What it costs

Billed work degrades. It never halts, and never overspends.

The reference configuration runs bulk qualification on a local model at zero token cost, and keeps a hosted model for drafting — the low-volume step where quality is what gets the interview.

95%
Of a pass ends before any model call
deterministic pre-filter
$0.00
Bulk qualification on the reference config
local Ollama, ADR 0017
$15
Default monthly cap, shared across every billed task
[budget] in karani.toml
0
Applications karani has submitted, by design
ADR 0012

Past the cap, or with no API key set, a billed provider is swapped for the local model and the pass says once that it happened. A missing key is never a hard failure and a spent budget never stops the hunt (ADR 0019).

What leaves your machine

As little as you configure

karani is self-hosted and every integration is opt-in. Your resume, the database and every generated pack stay on disk.

DestinationWhat goes there
Job boardsNothing about you. Public endpoints, read only.
LLM providerThe posting, your resume, your positioning, past verdicts as examples. Set provider = "local" and nothing leaves.
Slack, Notion, object storageShortlists, packs and application state — only if you configure them.
GmailNothing outbound. The watcher holds a read-only scope and cannot send, delete or mark read.

API keys live in .env and never in karani.toml, which the docs build checks: a config field whose name looks like a credential fails it.

Where to start

Four ways in

If you want toGo to
Have it running in ten minutesInstallation
Hunt for something other than the defaultsSaying what to hunt
Know what a flag doesCommand line
Drive it from an assistantConnecting an MCP client
Understand why it works this wayDecisions
Run it unattendedScheduling

Where it stands

One user, and it is his own job hunt

karani is on PyPI  as karani. It is a personal tool rather than a product: it is built for one person’s hunt, it makes no multi-tenant assumptions, and there is no hosted version and no plan for one. If you want to use it, fork it and rewrite karani.toml and data/resume.md — the vision page says what that involves and what karani deliberately will not do.

The one thing it will never do is press submit for you.

Last updated on