34. The community package index: a second static site
- Status: Accepted
- Date: 2026-09-05
Context
ADR 0031 makes packages discoverable only by already knowing their git URL. Something has to let people find out a package exists in the first place — "a second site alongside this one indexing shared libraries, fed by a submission process," per the project's own roadmap. The user locked in, ahead of any design work here: no submission-form backend, no Cloudflare Pages Function, no KV/D1 — a GitHub-PR curated list instead. This ADR designs the concrete shape of that, following ADR 0031's own governing constraint: no new hosted infrastructure.
korrin-web/ (the existing docs/landing site) is the direct precedent for
what "static site on Cloudflare Pages" already looks like in this project —
scripts/deploy-site.sh, wrangler.toml (pages_build_output_dir = "korrin-web"), a hand-written index.html plus style.css. editors/vscode/
is the precedent for "a self-contained, non-Rust tool in its own directory,
with its own build and its own review scope" (ADR 0030).
Decision
A new sibling directory, korrin-packages/, deployed the same way korrin-web/ is
korrin-packages/
index.html # hand-written, vanilla JS — mirrors korrin-web/index.html's style
style.css
packages.json # generated: the merged, machine-readable index
data/packages/
requests.json # one file per submission
json.json
wrangler.toml
CONTRIBUTING.md # what a submission PR needs to contain
A second instance of the exact deploy pattern korrin-web/ already uses (a
static directory, its own wrangler.toml, pushed with wrangler pages deploy) — not a new kind of infrastructure. It is not built with
mdBook: mdBook is the right tool for docs/'s prose, the wrong one for a
searchable, tabular listing with no chapters. The only build step is a small,
dependency-free script that globs data/packages/*.json into packages.json;
index.html fetches and renders that client-side — simpler than
korrin-web/'s own mdbook build phase, not more complex.
One JSON file per package, not one shared array
{
"name": "requests",
"git": "https://github.com/alice/korrin-requests",
"description": "HTTP client for Korrin, built on io.",
"tags": ["http", "network"]
}
data/packages/<name>.json, one file per submission. This keeps a
submission PR's diff to exactly one new file, avoids merge conflicts between
two people submitting packages around the same time, and keeps a reviewer's
job trivial — the same reason curated-list projects (Awesome-lists,
deno.land/x) generally adopt this shape over one shared array everyone
edits.
JSON, not TOML, deliberately: this is data a static page's own client-side
JavaScript consumes, not a Rust-ecosystem manifest. Matching korrin.toml's
format for consistency's sake would only add a conversion step for no
benefit — the data is authored directly as the format the page needs.
Submission is a PR, moderation is review
korrin-packages/CONTRIBUTING.md states the requirement: a submission PR
adds exactly one data/packages/<name>.json file with the fields above; the
package it names must have a korrin.toml at its git root and a working
src/lib.kor. A lightweight CI job in this directory validates that the JSON
parses and the required fields are present — schema validation, not
moderation. Whether a package is a good fit for the index is a human PR
review question, same as any other contribution to this project.
Consequences
- No new infrastructure exists anywhere: no server, no database, no form
endpoint. The entire "backend" is Cloudflare Pages serving static files,
identical to how
korrin-web/already works. - Submitting a package is exactly as much friction as any other open-source contribution — a PR — which is a real barrier compared to a self-serve form, and a deliberate one: it puts a human reviewer between "anyone can type a URL" and "it appears on the index," with no code required to enforce that.
- The index can go stale — a listed package can be deleted, renamed, or abandoned upstream with nothing here noticing automatically, since there is no backend to periodically check anything. A "does this still resolve" CI job is realistic future work; not built now.
- Two entirely separate Cloudflare Pages projects (
korrin-langforkorrin-web/, a new one forkorrin-packages/) means two deploy targets to remember, documented inkorrin-packages/'s ownDEPLOY.md, mirroringkorrin-web/DEPLOY.md.
Alternatives considered
- A submission form with a backend. Explicitly ruled out by the user
ahead of this ADR — would need a Cloudflare Pages Function, a loosened
_headersCSP (the current one blocks all form submissions viaform-action 'none'), and a storage backend (KV or D1) that doesn't exist anywhere in this project today. - One shared
packages.jsonarray, edited in place by every submission PR. Simpler to read as a single file, but every concurrent submission becomes a merge conflict against the same line range, and a reviewer has to diff a bigger file to see what one submission actually changed. - A new page on the existing
korrin-web/docs/mdBook site, rather than a second site. Would need no newwrangler.toml/deploy target, but forces a searchable/filterable listing through a prose-book renderer built for chapters, not tables — the wrong tool, and it conflates "the language's own documentation" with "a community-maintained list of third-party code," which are different trust levels worth keeping visibly separate. - Building this on mdBook anyway, generating one page per package from
the same
data/packages/*.jsonfiles. Would give consistent site chrome withkorrin-web/for free, but the JSON-per-package shape doesn't map cleanly to mdBook's chapter/heading model without real per-page templating workkorrin-web/doesn't already have a pattern for — not worth it for a first cut when a plain fetch-and-render page does the whole job in far less code.