changelog
Notable changes to vibe dex, newest first. Semantic versioning; each release is dated and git-stamped. Generated from the repo's CHANGELOG.md.
[Unreleased]
Removed
gen-seed(the seed generator) is retired.seed.jsonis edited directly now, fully hand-curated. The generator's monorepo-derived regenerate had drifted too far from the curated set to reconcile (86 vs 55 entries at retirement, and growing), so it was deleted rather than kept running-but-untrusted. No product-visible change;seed.jsonitself is unchanged.
Added
- Builder-profile enrichment. A top-repos showcase (the highest-starred non-fork repos each builder has authored, with stars/language/description), a profile-README bio fallback for an unclaimed builder with no self-declared bio (from their
github.com/<u>/<u>special repo, always attributed), and a bounded, TTL-aware cron pass that populates per-entry stars/language/last-commit for builders outside the existing profile-refresh rotation. - Public roadmap page.
/roadmapshows what's queued, in progress, and shipped for vibe dex, in a kanban-shaped board. Sourced from a small hand-curated list (not Han's private cross-repo ops backlog, which would leak unrelated personal tasks); an empty column reads "nothing here right now" instead of a gap. - Builder-profile data-viz polish. The contribution heatmap now carries a less/more legend, each year's calendar shows a self-scaled monthly sparkline (blank for months that haven't happened yet in the current year), the hero is a two-column layout (identity next to the stat scoreboard), the GitHub panel is grouped into shipped / contributions / stack, the language bars show an honest "other" bucket instead of silently reporting %-of-top-5 as %-of-all, and the recent-activity feed shows a per-event glyph plus relative time instead of a bare event/repo line.
Fixed
- A returning contributor can edit their own entries again. On approve, an entry submitted by an ALREADY-existing builder was left with a null
builder_id, so the owner-scopedPATCH /api/entries/:id(ownership =builder_idmatches the session) always 403'd them out of editing/pinning their own repeat entries.approveSubmissionnow setsbuilder_idon every approved entry.
Security
- Session/PII responses are never cached.
GET /api/settings/export(contains the owner's email),/auth/me(CSRF token), and/settings(CSRF token + privacy flags) now setCache-Control: private, no-store, so a shared proxy / browser disk cache / over-broad Cache Rule can't serve one builder's PII to another. - The legacy submit path no longer double-lists a repo.
repo.submitnow canonicalizesrepo_urland dedups against pending submissions (not just published entries), matching the flat path , sogithub.com/o/rvsGitHub.com/O/R/(or two identical pending submissions) can't create duplicate listings for one repository. - Takedown/archive works for every submitted handle. The legacy submit path stored
builder_githubverbatim (mixed-case), but archive / takedown / self-delete match the normalized handle, so an entry submitted asNewPersonbecame a permanent, un-removable orphan in the public feed.repo.submitnow normalizes the handle at the boundary (like the flat path), closing the consent bypass. - Submitted URLs, tags, and handles are length-bounded.
HttpUrlSchemacaps at 2048 chars,tagsget a per-tag length cap + an array-count cap, andbuilder_githubis capped at 64 on every submit path, so a submission can't store a multi-MB URL / tag / handle that would be re-served on every public read (normalizeGithubstrips bad characters but not length). - Free-text fields are length-bounded on every write path. The owner-edit routes (
PATCH /api/builders/:handle,PATCH /api/entries/:id) AND the submit paths (SubmissionInputSchema, the legacyNewEntrySchema) now capheadline/ai_blurb/bio/what_it_does/problem_solved, so neither an authenticated owner nor a submission-then-approve can store an unbounded blob that would be served in full on every public/api/entriesread. - Account export no longer leaks a third party's email.
GET /api/settings/exportstripped the submitter-suppliedcontact_emailfrom the exportedsubmissionsrows: because anyone can file a pending entry for another builder's handle with their own email, that field was not necessarily the exporting builder's own PII. The builder's own contact remains in theiridentity.email. - URL scheme allowlist on submissions.
repo_url/site_url/image_urlnow rejectjavascript:/data:/vbscript:schemes at the write boundary (http(s) only), closing a stored-XSS vector where a submittedsite_urlwould execute when rendered into a profile link. - Pinning is operator-only on every path. The legacy
POST /api/entries//submitpath no longer accepts apinnedflag from anonymous submitters (it was silently honored into the pending row and onto the entry at approve); it is dropped at the schema and hard-coalesced to false, matching the flat/api/v1/submissionspath. Operators still pin via the authenticated edit path.
Fixed
- Federation listing fetch enforces its size cap before buffering.
fetchListingnow checksContent-Lengthand stream-reads with a running byte count that aborts past the cap, instead of downloading the whole body and then checking its length, so a malicious builder's oversizedvibedex.jsoncan't be materialized into memory during the operator sync. - Migration completeness (restore-path drift). Seven columns that predate the additive-migration runner (
entries.builder_id/pinned,builders.headline/ai_blurb,submissions.pinned/headline/ai_blurb) were added toLIFECYCLE_COLUMNS, so a live D1 restored to a pre-2026-06-15 point (Time Travel) and brought forward withmigrate:remotegets them too, instead of 500ing the approve-new -builder path on a missingbuilder_id. A reverse-direction contract test now fails if any future fresh-schema column is added without its migration entry. /healthnow canaries every table, not justbuilders. The liveness probe selects fromidentitiesandlisting_cachetoo, so a live D1 missing a migrated column/table trips 503 instead of staying green and masking the outage (the failure mode that hid this cycle's schema drift). CI now also runsmigrate:remotebeforewrangler deploy, so the live schema can no longer lag the deployed code.- Live schema drift that 500'd the site. CI deploys code but never migrated the live D1, so columns and a whole table (
listing_cache) that shipped in code were missing on production, and every data + profile read 500'd (while/healthstayed green and hid it). Addedbun run migrate:remote, a one-command, idempotent, non-destructive runner that brings the live D1 up to the deployed schema (creates missing tables, adds missing columns) and applied it. Abin/smokescript now walks the full production surface so this class of drift is caught immediately.
Added
- First-login onboarding + designed empty states. After claiming a profile, the owner sees a one-time checklist (add a headline, say how you use AI, add a first entry) that tracks completion from their own public fields; a "skip for now" dismisses it permanently (owner-only + CSRF). The empty directory, empty profile, and no-search-results views now carry designed copy with a submit call to action instead of the old dev seed hints.
- Account settings (
/settings). A signed-in builder owns their account: edit profile fields, control visibility (hide from the directory/feed/profile/leaderboard, or opt out of the leaderboard only, flags respected by every read surface), sign out everywhere (stateless-session revoke), export all their data as JSON, and delete their account self-serve (a typed confirm, then the lifecycle purge, real deletion, not a soft flag). Anonymous and non-owner visitors get none of it. - Launch pack: legal, SEO, analytics. Plain-language
/terms+/privacy(what data is held, how deletion works, no cookies/email/ads), a/sitemap.xml+/robots.txt(private paths excluded), complete baseline meta/OG + canonical on every page, and cookie-free Cloudflare Web Analytics (no consent banner). Adocs/launch-checklist.mdenumerates the launch-day truths. - Admin moderation queue (
/admin). A browser moderation surface gated by Cloudflare Access at the edge AND re-verified in the Worker (RS256 over the team JWKS: audience + issuer + expiry checks, fail closed). It approves / rejects submissions, processes takedowns, and archives / unarchives builders through the SAME core paths as the operator CLI (one moderation brain), never exposingcontact_email. No app-level admin auth: zero admin passwords or sessions in the app. - Production hardening (abuse + failure hygiene). Every mutating endpoint is rate-limited (
429with aRetry-After); the anonymous submission path sits behind Cloudflare Turnstile (server-verified, with a dev bypass when no key is set); every response carries security headers (CSP, HSTS,nosniff,X-Frame-Options: DENY); a global error boundary returns a clean page/JSON with no stack or internals (and the legacy submit route no longer leaks raw validation issues); a/healthendpoint backs a synthetic uptime probe; and D1 is exported to R2 daily with a matched, rehearsed restore path. - Builder-owned listings (GitHub as your data node). A builder can own a
vibedex.jsonon their GitHub profile repo; the dex resolves, validates, and caches it (via the operator-runbun run sync-listings), then renders their profile from it with builder-owned precedence (falling back to the dex's own data when absent). An invalid or deleted listing never clobbers the last good one. atproto-shape: the data lives on your node, the dex is the AppView , no account to run, no server to host. (Ingest is operator-invoked for now; auto-scheduling it on the cron is deferred pending an egress review of fetching every builder's external URL each tick.) - Build-signal leaderboard. A transparent, outcome-first BUILD SIGNAL ranks builders on the web (
/leaderboard), the API (GET /api/leaderboard), and MCP. It scores shipped / used / sustained / reach from public data (token burn is excluded), shown as a band + a per-signal breakdown with the formula visible, never a bare number and never a personal ranking. The criteria are public indocs/ranking-standard.md; the weights are config-driven (a re-weight is a config edit, not a rewrite). - Sign in with GitHub to claim + edit your page. A builder can "Sign in with GitHub" (
read:userscope only), which claims their profile, and a signed-in builder edits their own headline, blurb, and entries directly, no operator approval for their own content. The anonymous submission + operator- approve path still works, and an anonymous visitor sees no change. Sessions are httpOnly + Secure + SameSite cookies; CSRF is enforced on the OAuth state and every edit; all secrets are op://-managed. (Sign-in is inert until the OAuth app secrets are provisioned; the read + submit paths are unaffected.) - Exact contribution calendar + streak with a GitHub token. When the optional
GITHUB_BOT_TOKENis set, a builder's contribution calendar, yearly totals, and streak come from GitHub's exact GraphQL API, and every GitHub read is authenticated (5000/hr instead of 60/hr). Without a token (or with an invalid one) the dex silently falls back to the public HTML scrape, exactly as before. The token is fine-grained, read-only, minimal-scope, public-data only, and never committed (op:// reference). - Share cards now unfurl as a PNG. A profile's
og:imagepoints at a real 1200x630 PNG rendered at the edge (/b/:handle/og.png), so a shared/b/:handlelink shows a designed card on X / Slack / Discord / iMessage (those platforms do not unfurl an SVGog:image). The PNG is rasterized deterministically from the same mono card SVG viaresvg-wasmwith an embedded JetBrains Mono (SIL OFL) face; the/og.svgroute stays as the fallback and the on-page card. - Builder-data lifecycle (onboard / refresh / archive) + a public submission API. The directory is now live data, not a static seed. A scheduled hourly cron re-fetches each builder's public GitHub signal (deep profile + per-repo stars/language/last-commit) into D1, keyed by the builder's immutable GitHub id, with two freshness clocks, a last-good-wins flap guard, and a dead-handle retirement (a 404'd account is dropped from the rotation after 3 strikes). Anyone can propose a builder + project via
POST /api/v1/submissions(flat, validated, consent-required, idempotent onIdempotency-Key, opaquesub_<ulid>id) or the MCPsubmit_entrytool;contact_emailis stored server-side only and never read back. Removal is one unauthenticated call:POST /api/v1/builders/:handle/takedown, which an operator approves to archive the builder (hidden from every read) and hard-purge their PII. Operators run approve / reject / archive / takedown as a CLI over the live D1 (no public admin surface). Repo stars/languages now span all of a builder's repos (paginated), and the repo count is the true GitHub count, not a 100-cap./openapi.jsondocuments the submission path and carries a real version. Migrated non-destructively over the existing D1 (no drop-and-reseed). - Multi-builder seed. The directory seeds three builders now, not one:
tamnd(Tam Nguyen Duc) andmonotykamary(Tom) jointieubao, each with a few of their top public repos as entries (ascii-gif,ccrawl-cli,brain;localterm,inline-yaml,folio). Curated external builders live in a newpackages/core/seed.external.json, merged into the seed bygen-seedand kept separate from the auto-generated Han set for privacy review. Their data is static seed for now; a live refresh flow is backlogged. - Builder profile v2. A hierarchy + data pass on
/b/:handle. Orgs render as a linked greyscale logo grid (using the already-cached org avatar, full-color on hover, with a generated placeholder fallback for avatar-less orgs). The stat band becomes a boxed "scoreboard" (~2rem numbers, hairline cell rules). The profile entry list splits into a Featured (pinned) block + a secondary "All work (N)" list with aN entries · P products · E experimentscount header, each row carrying its own repo/site links + tags + a detail link. NewsplitFeatured+entryKindCountsspine helpers; the render stays a dumb projector. CSS + inline SVG + already-cached data only (edge-safe). The home/feed keeps its flat list unchanged.
Changed
- Contribution-number label. The stat-band total is now labelled
contributions · last 12moso it no longer silently conflicts with the calendar's per-year totals (N contributions in YYYY). Two numbers, two honest labels.
Fixed
- Streak-0. When the current streak is 0, the
0 days currentchip (sitting next to a stronglongest) is dropped; onlylongestrenders. The page no longer shows a self-sabotaging zero.
0.4.0 · 2026-06-15 · acbb351
Added
- Public
/changelogpage. A nav-linked changelog page rendered edge-safe from a committed, generated TS const (parsed from this file at build time, no markdown parser on the edge). - Release automation: changelog drift gate + auto-deploy CI.
bun run changelog:draftreads the conventional commits since the last released section and drafts/updates a top## [Unreleased]block (grouped Added / Changed / Fixed) for a human to polish;bun run changelog:checkFAILS when a diff touchespackages/**code without aCHANGELOG.mdnote, so a code change can no longer ship without a changelog entry. A repo-root GitHub Actions workflow runs the full gate (test, typecheck, biome,changelog:check,build:worker) on push tomainand deploys the Cloudflare Worker, tolerating the benign zone-routes auth error. The workflow is inert until the two Cloudflare repo secrets are set.
Fixed
- Builder handle corrected to
tieubao. - Three flagship entries pinned: tide, bot-fleet, vps-mon.
- The builder name in the entry list now links to the builder's profile.
0.3.0 · 2026-06-15 · 9fdba71
Added
- Stable opaque builder id under the GitHub handle. Each builder gets a stable opaque id that is the durable key, with the GitHub handle as the human-facing alias; the model survives a handle rename without a re-key.
- Profile hero + headline stat band. A profile-top hero with a headline stat band (GitHub signal summarized as numbers), GitHub social / followers, a markdown bio (edge-safe inline render), and a claim badge. The legacy token-burn stat was removed.
- Activity-depth showcase. A merged-PR reel, a multi-year contribution calendar with a year toggle, a current streak, and language % bars, all from public GitHub signal, cached.
- Pinned / featured projects + self-declared headline & AI blurb. A builder can pin/feature projects and self-declare a headline and an AI blurb, supplied via submit and applied on operator approve (no login).
- Builder directory + sort/search + vanity
/@handle. A mono builder directory at/builderswith sort (activity / recency / tech) and a handle/tech search, plus a vanity/@handleURL that 302-redirects to the canonical profile. - Per-profile OG share-card + embeddable badge. A deterministic, edge-safe SVG OG share-card per profile (wired into
og:image) and an embeddable "Featured on vibe dex" badge with a copyable snippet.
0.2.0 · 2026-06-13 · d972bc6
Added
- Identity model. A GitHub-canonical
identitiestable (one per builder), the auth anchor distinct from the public profile. Carriesemail(server-side only) +claim_status(unclaimeddefault)./api/builders/:githubnow resolvesclaim_statusthrough identity. OAuth/claiming is deferred; the model leaves room for it without a re-model.normalizeGithubcanonicalizes handles (lowercase).gen-sqlemits identity rows into the D1 seed. - Repo-sourced rich detail. The detail page assembles itself from the entry repo's README into themed sections. A cached
entry_docstable holds sanitized section HTML (the markdown parser runs withhtml:false, so raw HTML /javascript:links are inert). Extraction is a cachedextract-docsstep (token-aware split, immune to#in code fences), kept out of the Workers bundle. Degrades gracefully when a repo has no README or is private.bun run extract-docspopulates the cache. - Deep GitHub profile. Builder profiles show public GitHub signal: a contribution heatmap (mono grey ramp), repo stats, top languages, orgs, and recent activity. All public + unauthenticated (no token), cached in
profile_cache; the HTML scrape is isolated to one non-barrel adapter and kept out of the Workers bundle. Each source degrades independently.bun run fetch-profilespopulates the cache. - Generative placeholders. Imageless entries and avatar-less builders render a deterministic near-monochrome SVG pattern (
generatePattern, a pure id-seeded function, no deps) instead of a blank box: wired into the entry detail slot, list thumbnails, and builder avatars, each falling back to a real image when present. - Info / onboarding pages. About, How it works, Access the data, and a Submit guide, each with an in-style ASCII diagram, linked from a nav. The access page documents the real HTTP API / MCP tools / CLI commands, with a route test that fails if the docs drift from the code.
0.1.0 · 2026-06-13 · de09400
Added
- v1 read slice. An agent-native catalog readable over web, HTTP API, MCP, and CLI from one data core.
- Entry model v2 + real-data seed. The richer entry model (builder, what-it-does, problem, links, kind, tags) and a seed of real entries.
- Builder profiles. Per-builder profile pages.
- Design guideline applied to the web surface. The mono / light visual language.
- Discovery filters + agent export protocols. Filter by kind / tag / query;
/openapi.jsonand/schema.jsonderived from the one Zod source. - GitHub repo enrichment. Cached, graceful repo metadata enrichment.
- Submit + operator curation. A submit flow (web form, API, MCP, CLI) where an entry lands pending and an operator approves it before it goes live.
- Deploy. Live on Cloudflare Workers + D1 at dex.han.ws, recreatable from the repo + 1Password alone.