← ALL POSTS · /BLOG

Cover art: a faint wall of 251 small tiles above 14 larger square-matrix tiles; a yellow route runs from the top into one yellow tile and down to a framed node
CORPUS HUB V2 · 3,663 CONTRIBUTORS · ROUTED · SIGNED · AS OF 2026-10-19

CORPUS HUB V2: ASK THE QUESTION,
GET THE ONE PACK THAT ANSWERS IT

As of 2026-10-19, 3,663 developers have said yes. Each of them agreed to let AI agents learn from a piece of their own work: a CUDA kernel fix in llama.cpp, a Turkish translation for cc-switch, a compiler fix in rust-lang/rust, a streaming fix in Fabric. Their repositories now wait in licence review as 3,659 corpora, next to the 513 corpora already live on hub.xerj.org. Hub v2 is how that shared memory reaches an agent. You type xerj "how does lz4 decompress a block", the CLI picks the right corpus from a signed catalogue, pulls one pre-indexed 4.4 MB pack and answers on your own machine: 1.7 to 2.3 seconds the first time, 37 to 57 ms after that. Every number below was fetched or measured on 2026-10-19, and each one comes with a URL or a command you can run yourself.

01 · COMMUNITY

The community so far

A corpus is only as good as the people who wrote the code in it. So before the CLI, here is who showed up. These figures come from hub.xerj.org/contributors/, the hub's own APIs and the GitHub API, all read on the evening of 2026-10-19 (Pacific time).

3,663
developers agreed
3,663 repositories, each included with its author's permission, under its own licence, with attribution
3,659
corpora from their work
all in licence review; 70 people appear in more than one
3,861,407
lines of selected commits
summed over the 3,653 contributor cards that list a line count
513
corpora live today
11 domains, 78.6 GB of pinned text, per the hub.xerj.org header

The contributor wall is sorted by the GitHub stars of the repository each person's work landed in, so the top of the list doubles as a map of the projects developers care about most. Here are the first ten, as listed tonight. The stars belong to the repository, not to the person, and every one of these corpora is still in licence review:

#contributorselected work incorporalinesGitHub ★ on repohub ★
1@NianJiuZstopenclaw/openclaw1308392,4430
2@abhay-2108morluto/rea1882330,2450
3@wellkiloaffaan-m/ECC12,822283,6350
4@alkankfarion1231/cc-switch1174146,1990
5@edenfunfggml-org/llama.cpp1399131,6850
6@SrijanSrivGraphify-Labs/graphify1236128,7460
7@fufesourustdesk/rustdesk11,357126,3030
8@paradoxicalguyrust-lang/rust1291120,5780
9@bilashcseaxios/axios1342109,2980
10@MeAkibangular/angular1265101,1100

Hub stars are the ones visitors give on the hub itself, and that count is brand new. Across the whole hub there are 3 as of 2026-10-20: one on @ksylvan's profile, one on @hosseinkarami-dev's, and the first one on a corpus, vuln-fix-commits. If someone's fix saved you an afternoon, their page has a star button. The project repository, xerj-org/xerj, stood at 10,671 GitHub stars and 637 forks on 2026-10-20, with 60 accounts credited as code contributors.

Three different numbers describe "how many corpora", and each one answers a different question:

what lives where, checked 2026-10-19 (Releases re-counted 2026-10-20)countsource
corpora listed as live in the registry513hub.xerj.org header (11 domains, 550 registry rows, 78.6 GB pinned text)
corpora with a signed v2 pack on GitHub Releases251distinct slugs among published *.hubpack.tar.zst assets; builds are still landing, so this keeps climbing
corpora in the signed catalogue xerj ask routes over14catalogue generated 2026-10-18 02:13 UTC, 1.3 MB
corpora built from contributors' repositories3,659hub.xerj.org/contributors/, all in licence review

The catalogue row is the one that limits xerj ask today. Pack builds have run ahead of it, so a corpus can have a signed pack on Releases and stay invisible to the router until the catalogue is regenerated. That gap, 14 against 251, is the one to close.

02 · ASK

Ask without naming a corpus

The launch post ended on a promise: "the pre-indexed half — a corpus you mount instead of indexing". This is it. CI builds each pack, indexes it, signs it and attaches it to a GitHub Release. Your machine restores those indices as they are. No clone, no indexer. Here is a fresh install with an empty cache and no node running:

$ xerj "how does lz4 decompress a block"
xerj hub: no node answers at http://localhost:9200; starting a local node on port 9300 (data: ~/.xerj/hub/node, log: ~/.xerj/hub/node.log)
xerj hub: node ready on port 9300 (pid 1626207)
xerj hub: pulling 'lz4' (4.4 MB) — one-time download, later questions answer locally
xerj hub: pulled lz4 0774d055 4623814 → 4 indices, 1702 definitions
lz4  pin 2026-10-02 (18 days old)  mixed: BSD-2 (lib) / GPL-2.0+ (rest)  lz4/doc/lz4_Block_format.md:0
...
lz4  pin 2026-10-02 (18 days old)  mixed: BSD-2 (lib) / GPL-2.0+ (rest)  lz4/lib/lz4frame.h:572
LZ4F_decompress_usingDict(LZ4F_dctx* dctxPtr,
...
answered from: local pack
warning: approach-only licence (mixed: BSD-2 (lib) / GPL-2.0+ (rest)): read, do not copy

Every hit names its corpus, pin date, licence and file path. lz4 is GPL-2.0+ outside lib/, so the CLI marks it approach-only and says "read, do not copy". For an exact symbol, --def skips ranking and reads the definition table built with the pack:

$ xerj ask --def LZ4_compress_fast
lz4  pin 2026-10-02 (18 days old)  mixed: BSD-2 (lib) / GPL-2.0+ (rest)  lz4/lib/lz4.c:1462
int LZ4_compress_fast(const char* src, char* dest, int srcSize, int dstCapacity, int acceleration)
lz4  pin 2026-10-02 (18 days old)  mixed: BSD-2 (lib) / GPL-2.0+ (rest)  lz4/lib/lz4.h:236
LZ4LIB_API int LZ4_compress_fast (const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
answered from: local pack

One flaw is right there in the first transcript: the top hit is cited at line 0. Prose files in rc.97 lose their line numbers. The fix for the same bug in xerj code (#1892, fixed by #1908) reached main on 2026-10-19, after rc.97 shipped, and we have not re-run these transcripts against it.

03 · EXAMPLES

Four more questions, four more corpora

Same rc.97 binary, same fresh scratch HOME, run on 2026-10-19. Each question named no corpus; the router picked one and the CLI pulled it on first use. Output is real and trimmed with ....

Alertmanager. A how-question about grouping lands on the dispatcher function that does the grouping:

$ xerj "how does alertmanager group alerts before sending notifications"
xerj hub: pulling 'alertmanager' (13.3 MB) — one-time download, later questions answer locally
xerj hub: pulled alertmanager 55b27cfb 13937896 → 3 indices, 4725 definitions
alertmanager  pin 2026-10-13 (7 days old)  Apache-2.0  alertmanager/docs/alertmanager.md:1
...
It takes care of deduplicating, grouping, and routing
them to the correct receiver integration such as email, PagerDuty, or OpsGenie.
...
alertmanager  pin 2026-10-13 (7 days old)  Apache-2.0  alertmanager/dispatch/dispatch.go:440
func (d *Dispatcher) groupAlert(ctx context.Context, alrt *alert.Alert, route *Route) {
...
	groupLabels := getGroupLabels(alrt, route)

	fp := groupLabels.Fingerprint()
...
answered from: local pack

Avro. Schema evolution, answered from two language bindings at once:

$ xerj "how does avro resolve schema evolution between writer and reader"
xerj hub: pulling 'avro' (21.8 MB) — one-time download, later questions answer locally
xerj hub: pulled avro 28cb08c1 22902146 → 21 indices, 22064 definitions
avro  pin 2026-10-13 (7 days old)  Apache-2.0  avro/lang/c/src/avro/resolver.h:64
avro_resolved_writer_new(avro_schema_t writer_schema,
			 avro_schema_t reader_schema);
...
avro  pin 2026-10-13 (7 days old)  Apache-2.0  avro/lang/java/avro/src/main/java/org/apache/avro/Resolver.java:36
public class Resolver {
  /**
   * Returns a {@link Resolver.Action} tree for resolving the writer schema
   * <tt>writer</tt> and the reader schema <tt>reader</tt>.
...
answered from: local pack

Snappy. "How does it find matches" goes straight to the compression loop and the unit test that pins it down:

$ xerj "how does snappy find matches"
xerj hub: pulling 'snappy' (10.8 MB) — one-time download, later questions answer locally
xerj hub: pulled snappy 9c28114a 11338487 → 4 indices, 382 definitions
snappy  pin 2026-10-02 (18 days old)  BSD-3-Clause  snappy/snappy.cc:812
char* CompressFragment(const char* input, size_t input_size, char* op,
                       uint16_t* table, const int table_size) {
...
snappy  pin 2026-10-02 (18 days old)  BSD-3-Clause  snappy/snappy_unittest.cc:1025
TEST(Snappy, FindMatchLength) {
  // Exercise all different code paths through the function.
...
answered from: local pack

zstd, then a symbol. The question finds the level API; a --def lookup then pins the definition:

$ xerj "how does zstd choose a compression level"
xerj hub: pulling 'zstd' (20.1 MB) — one-time download, later questions answer locally
xerj hub: pulled zstd 01b7154f 21038204 → 8 indices, 7874 definitions
zstd  pin 2026-10-02 (18 days old)  mixed: BSD-3 (lib) / GPL-2.0 (cli)  zstd/lib/zstd.h:264
ZSTDLIB_API int          ZSTD_defaultCLevel(void);         /*!< default compression level, specified by ZSTD_CLEVEL_DEFAULT, requires v1.5.0+ */
...
$ xerj ask --def ZSTD_defaultCLevel
zstd  pin 2026-10-02 (18 days old)  mixed: BSD-3 (lib) / GPL-2.0 (cli)  zstd/lib/compress/zstd_compress.c:8207
int ZSTD_defaultCLevel(void)
...
answered from: local pack
warning: approach-only licence (mixed: BSD-3 (lib) / GPL-2.0 (cli)): read, do not copy
step (xerj v1.0.0-rc.97, Linux, isolated HOME, 2026-10-19)wall clockruns
lz4, first question, cold: node start, catalogue, 4.4 MB pull, answer1.72–2.27 s4
lz4, the same question again, pack cached37–57 ms11
--def LZ4_compress_fast → lz4.c:146221–25 ms4
alertmanager question, pack cached54–122 ms3
avro question, pack cached62–65 ms3
--def ZSTD_defaultCLevel and --def groupAlert27–39 ms6

Not every answer is this sharp. "How does astro render islands" pulled the astro pack and came back with an error definition, the config types and three test files cited at line 0, not the island renderer itself. Ranking a prose question against framework code still needs work.

04 · ROUTING

Ambiguous questions come back as a choice

The router does not guess between two plausible corpora. Ask about two compressors at once and it hands the decision back:

$ xerj "lz4 or snappy decompression speed"
more than one corpus matches; re-run with --corpus <slug>
lz4 — an agent needing fast block compression queries this for the block format and decoder loop
snappy — an agent needing cheap compression queries this for the format and the reference encoder decisions
$ echo $?
4

Exit 4 already means "needs a decision" in xerj autoindex, so a wrapper can turn it into a question for the user. When nothing clears the bar, the CLI exits 1 and names the nearest corpora:

$ xerj "how do I run a github actions workflow locally"
no hub corpus matches; see xerj.org/llms.txt
closest corpora: act, astro, agentops; re-run with --corpus <slug> to ask one of them

That one is a miss, and we left it in on purpose. act exists for exactly this question; the router ranked it first but not confidently enough to answer. The threshold is ours to fix.

Exit 1 used to hide a third case: hub unreachable, an HTML page where the signed catalogue should be, or --offline with nothing cached. An agent reading exit codes took that as "the hub has no answer" and fell back to memory. A fresh-user run hit it on 2026-10-19 (#1904), and PR #1905, merged to main the same day, gives it its own code. rc.97 still exits 1.

exitmeaning
0answered, at least one hit
1no match, or nothing to answer from (closest corpora are named)
2usage error or unknown --corpus
3a pulled pack failed SHA-256, signature or format verification
4ambiguous: candidates printed, re-run with --corpus <slug>
5this build has no hub public key, so nothing is downloaded
6the hub catalogue cannot be loaded (on main since #1905; rc.97 still exits 1)
05 · CONTRIBUTORS

A page for every contributor

Every corpus is somebody's work, so every contributor gets a page that shows exactly what was selected and where it goes. Here is the text of @edenfunf's page as it rendered on 2026-10-19, trimmed:

@edenfunf
Corpus Hub contributor since 2026-10-10
1 corpus · 1 selected commit · 3 files · 399 lines
★ 131,685 GitHub stars on repo · ★ 0 hub stars

Selected work
ggml-org/llama.cpp  fda1866613
CUDA: fix norm family kernels when ne[2]/ne[3] exceed grid dim limits…
3 files · 399 lines · CUDA, C++ · ggml, tests
selected · in licence review · corpus ggml-org--llama-cpp · MIT · agreed 2026-10-10
Not installable yet ... It becomes installable when this card says live in the hub.

The page is upfront about what you cannot do yet: until licence review passes, xerj corpus add and xerj ask cannot fetch that corpus. One commit can feed several corpora. @ksylvan's single Fabric commit (aa3e1727b7, 29 files, 1,055 lines of Go) appears in 2, and the totals count it once. Each profile carries a README badge at /contributors/<login>/badge.svg and prints how to leave: reply to the permission email or open an issue.

06 · SIGNALS

Human stars and agent scores, kept apart

A corpus page carries two signals and never blends them. Hub stars are people clicking a star. The agent score comes from structured reviews agents send after using a corpus, through xerj hub pull, MCP queries or the xerj_corpus_feedback tool. A review rates usefulness from 1 to 5, says whether the answer was found (yes, partial or no) and how fresh the corpus felt, and can name a coverage gap or flag a problem: error, slow, stale or wrong. The rules are printed on every corpus page:

Here is the score for zstd tonight, and the hub-wide list:

$ curl -s "https://hub.xerj.org/api/corpus-feedback?corpus=zstd"
{"ok":true,"enabled":true,"corpus":"zstd","reviews":0,"score":null,"raw_mean":null,
 "confidence":"none","prior":{"mean":3,"weight":5},"found":{"yes":0,"partial":0,"no":0,"rate":null},
 "freshness_median":null,"families":[],"coverage_gaps":[],
 "problems":{"error":0,"slow":0,"stale":0,"wrong":0},"agent_reviews":0,"pull_reports":0,
 "dropped_outliers":0,"k_min":3,"window_weeks":26}
$ curl -s "https://hub.xerj.org/api/corpus-feedback"
{"ok":true,"enabled":true,"prior":{"mean":3,"weight":5},"scores":{}}

No corpus has an agent score yet. The page shows that as empty instead of inventing a starting rating, and the first agent review will be the first real data point.

07 · TRAFFIC

Traffic and usage on every corpus page

Each corpus page has a traffic panel modelled on GitHub's, covering the last 14 days in UTC: views and unique views (crawlers excluded, uniques by a hash salted with the day), pulls, hosted-hub searches, requests per agent family, and referring sites once a site has sent 3 views. Pulls are the growth in the GitHub Release download count for that corpus's pack, our own verification downloads included. It is one JSON call:

$ curl -s "https://hub.xerj.org/api/hub-traffic?corpus=act"
{..."totals":{"views":9,"unique_views":8,"pulls":1,"pulls_unique":null,"searches":0},
 "pulls_since":"2026-10-20","recording_since":"2026-10-19",
 "agents":[{"family":"curl","count":9}],"referrers":[]}

Nine views, all from curl, and most of them were us writing this post. Recording began on 2026-10-19, so every corpus starts near zero.

Below that sits agent usage: requests over 7 and 30 days, agent families, top topics and the most common query templates. A template swaps identifiers, versions, paths, numbers and quoted text for placeholders, appears only after 3 sightings in 30 days, and raw queries are never published. Only requests to the hosted hub API (hub.xerj.org/api/v1/code and /def) count. A question answered on your own node never leaves your machine, so xerj ask with a cached pack never shows up here.

08 · SIGN IN

Sign in with GitHub or Hugging Face

Everything above is public. Signing in through /auth/github/login or /auth/hf/login (standard OAuth, PKCE on both) opens the detail behind it: a corpus page adds its pack builds, pin history, source pins, review records and signing; a contributor page adds every selected commit in full, with commit date, folders, review state and pin. Usage detail needs a session too; without one the usage API answers "detail_requires_signin":true.

09 · FIELD REPORTS

Field reports that turn into fixes

After an agent installs or uses XERJ, the CLI sends a short field report: what the agent pointed XERJ at, what it used it for, and its verdict. It arrives as a GitHub pull request adding one Markdown file under user-feedback/16-agent-field-reports/, forking first when the account cannot push. Only when GitHub is unavailable does it fall back to POST https://xerj.org/api/field-report, which files an issue. --disable-feedback or XERJ_DISABLE_FEEDBACK=true turns it off. Whether an agent should send one without asking its user first is open in #1896; the opt-out works either way.

As of 2026-10-19, main holds 40 reports in that folder, and 12 more came in through the API fallback as issues titled "Agent field report (API)". One full loop: several reports flagged a stray backslash in the MCP registration text that xerj init prints. Issue #1838 was opened from them on 2026-10-19, and PR #1848 fixed the three format strings and closed it at 23:34 UTC the same day. A small bug, reported by agents who had no other way to tell us, and fixed before the day was out.

10 · HOW IT WORKS

How it works

The catalogue. hub.xerj.org/api/v1/catalogue.json is one JSON file with a detached ed25519 signature beside it in catalogue.json.sig. The CLI fetches it with If-None-Match and verifies the signature before new bytes may replace its cache; on any network or verification failure it keeps the last good copy. Each entry holds the slug, domain, topics, a one-sentence ask_for, dependency_names, the licence (SPDX plus class), the pin SHA and date, the pack URL, size, SHA-256 and signature URL, and a term_sketch.

Routing. The router reads only that metadata plus two things on your disk: the dependency names in your Cargo.lock, package.json or go.mod, and the packs you already have cached. The term sketch is a Bloom filter over the corpus's terms, built with 7 hashes. A Bloom filter never gives a false negative, so when the sketch rules out a word in your question, that corpus drops out without a download. A declared dependency is the strongest evidence a corpus can earn. Corpora whose licence class is excluded are removed before routing starts: never picked, never offered, never named as closest.

The pack. A confident pick downloads that one pack from GitHub Releases, checks its SHA-256 and signature (a failure is exit 3) and restores its pre-built indices into a node. If nothing answers on localhost:9200, the CLI starts its own on port 9300, and xerj hub stop stops that process and no other. The hub serves the catalogue, the web pages and a few small JSON APIs; the search runs on your node. A hosted switch exists (xerj hub config hosted on|off|auto), but no hosted client ships in the CLI yet (checked in rc.97 and on main, 2026-10-19), so every xerj ask answer today is computed on your machine.

11 · GETTING STARTED

Getting started

Install the CLI, then ask. The first question starts the node and pulls the pack; later ones answer from the cache:

$ curl -fsSL https://xerj.org/get | sh
$ export PATH="$HOME/.local/bin:$PATH"
$ xerj "how does lz4 decompress a block"
$ xerj "how does alertmanager group alerts before sending notifications"
$ xerj ask --def LZ4_compress_fast
$ xerj ask --corpus snappy "how does snappy find matches"
$ xerj hub status
$ xerj hub stop

xerj hub status lists cached packs with age and size, plus the node, the hosted setting and the catalogue age. Add --offline to stay on cached packs, --no-fetch to forbid downloads and --json when an agent parses the answer.

12 · NEXT STEPS

Next steps