Enter one GitHub handle → a dossier on your dev persona, read from public commits. Enter two → are you mergeable? Dating-app voice, strictly about code.
Live: https://gitmatch.nvps742.workers.dev
A Vite frontend + one Cloudflare Worker + KV, served from a single origin. One handle becomes a "case file" persona card; two handles become a compatibility face-off. Every word on a card is generated from the numbers by rule — no LLM, no API keys beyond a free GitHub token, genuinely $0 to run.
Each card is also a shareable image: /api/card.svg?handle=x and
/api/compare.svg?a=x&b=y render self-contained SVGs (embeddable in a README or
a tweet), and a shared link unfurls into that image via server-injected OG tags.
Public activity only, plus one thing more:
- Public commits, repos, profile via the REST API — the last 100 commits, languages, stars, fork ratio, abandoned repos.
- Commit hours in the author's local time. GitHub returns each commit date
with its original UTC offset (
…T02:51:59.000+05:30); we read the hour on the author's own clock, not UTC. "Night owl" means night where they are. - An anonymized count of private work via the GraphQL
contributionsCollection(restrictedContributionsCount). We can see how much someone keeps private — never what it is. For many developers this is the majority of their year, so a quiet public profile is never read as "inactive."
A zero-scope GitHub PAT is enough for all of it: it only ever returns public data plus these anonymized counts. No private repo, commit, or diff is reachable.
Pure core, thin I/O shell. The functions that decide what a card says are pure and deterministic; everything that touches the network or KV is isolated at the edges, so the interesting logic is trivially testable and can never invent data.
src/types.ts— locked data contracts (Facts,Card,MatchCard,PrivateSignal, …).src/facts.ts— pure.extractFacts()turns raw GitHub data intoFacts(local-time commit hours, garbage-date filtering, private share);compatibility()scores twoFacts0–100 from public behavior only (language 0.4 + hours 0.3 + habits 0.3).src/persona.ts— pure. TurnsFactsinto a developer's dating profile: a curated archetype ("The Tinkerer") from a combination of public behaviors, a witty bio, trait chips, and the merge face-off (a dating-match blurb + the click/clash signals). Deterministic; every number it prints is interpolated fromFacts, so nothing can be invented.src/github.ts— I/O. 4 calls per uncached handle: user, repos, commit search (REST) + contributions (GraphQL, best-effort — a failure never blocks a card).src/cache.ts— KV get/put, versioned keys (bumpVERSIONon any shape change).src/ratelimit.ts— per-IP, per-hour counter in KV.src/card.ts— orchestration:getSoloCard()/getMatchCard().src/index.ts— Worker router:/api/card,/api/compare, the.svgcard endpoints, per-handle OG-tag injection on/, CORS, static assets.src/client/— Vite frontend ("The File On You"): case-file solo card, the merge face-off, local-time commit clock, and the private-work redaction bar. Builds todist/, which the Worker serves as static assets.
The Worker serves dist/ and handles /api/* in the same script ([assets] in
wrangler.toml), so the frontend's relative fetch("/api/card…") calls are
same-origin by construction — no routing rule to configure, and no secret ever
reaches the client.
npm install
# create the KV namespace, then paste its id into wrangler.toml
npx wrangler kv namespace create GITMATCH_KV
# the only secret (never committed)
npx wrangler secret put GITHUB_TOKEN # PAT with NO scopes — public data onlynpm test # vitest + Miniflare, one file per module
npm run typecheck # tsc --noEmit, strict mode
npm run build # build the client into dist/
# Full stack, one origin. Two terminals (deliberately not chained — the
# shell operators for backgrounding differ across platforms):
npm run dev # terminal 1: wrangler dev, serves dist/ + /api/*
npm run watch # terminal 2 (optional): rebuild dist/ on client editsnpm run deploy # vite build → dist/, then wrangler deployFirst deploy only: register a free *.workers.dev subdomain in the Cloudflare
dashboard, and set the GITHUB_TOKEN secret (above). Secrets apply on the next
request, no redeploy.
Accounts/auth/swiping, a user database, deeper history via GraphQL beyond the contribution count, anything romantic about a real person.
MIT — see LICENSE.