Skip to content

Repository files navigation

gitmatch

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

Overview

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. The image endpoints never build a card for a handle nobody has looked up — an <img> is fetched unattended by crawlers and proxies, and inventing handles is free for an attacker, so that path is refused and renders a placeholder pointing at the page instead. A handle a human has looked up is remembered for 90 days and keeps refreshing, so an embedded badge stays alive long after the card behind it expires.

What it reads

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.

How it works

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 into Facts (local-time commit hours, garbage-date filtering, private share); compatibility() scores two Facts 0–100 from public behavior only (language 0.4 + hours 0.3 + habits 0.3).
  • src/persona.ts — pure. Turns Facts into 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 from Facts, 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. Two versions, because the layers cost different amounts to rebuild: bump FACTS_VERSION only when the Facts shape changes (that eviction costs 4 GitHub calls per handle), and COPY_VERSION freely for any wording change (regenerating copy from cached facts is free). The same asymmetry sets the TTLs — facts live 30 days, cards 7 — so a card rebuild reuses cached facts instead of going back to GitHub.
  • src/ratelimit.ts — per-IP, per-hour counter in KV. Approximate by construction and documented as such; it fails open. It guards /api/*; the image paths are bounded structurally instead (see cachedOnly in card.ts).
  • src/config.ts — pure. Parses and validates the numeric vars once per request, with documented defaults, so a bad value can't surface as an opaque 500 from inside a KV write.
  • src/card.ts — orchestration: getSoloCard() / getMatchCard(). Owns handle validation and the cachedOnly switch the image endpoints run under: cached card → serve it; cold but known → rebuild; cold and unknown → placeholder.
  • src/index.ts — Worker router: /api/card, /api/compare, the .svg card endpoints, per-handle OG-tag injection on /, CORS, static assets. Also emits one Analytics Engine data point per request (path, GitHub calls, outcome) — the numbers that say whether "$0 to run" still holds.
  • src/shared/lang.ts — pure. The language palette and contrast guard, used by both renderers so the shareable image can't drift from the web card.
  • 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 to dist/, 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.

Setup

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 only

Develop & test

npm test            # two projects: Worker code on Miniflare, client code on node
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 edits

Deploy

npm run deploy      # vite build → dist/, then wrangler deploy

First 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.

Not in scope (by design)

Accounts/auth/swiping, a user database, deeper history via GraphQL beyond the contribution count, anything romantic about a real person.

License

MIT — see LICENSE.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages