diff --git a/src/app/api/aggregate/route.ts b/src/app/api/aggregate/route.ts index 6500c8b28..24c873439 100644 --- a/src/app/api/aggregate/route.ts +++ b/src/app/api/aggregate/route.ts @@ -10,6 +10,26 @@ // aggregate-blob.ts can point AGGREGATE_BLOB_URL at this route instead // of kv.openchainbench.com to get near-zero latency from any Vercel // function in the same region. +// +// UNFILTERED BY DESIGN. This route serves the worker's blob verbatim, so on +// production it carries every dev-only bench (the DEV_ONLY_BENCH_SLUGS set in +// src/lib/removed-benches.ts) with its full editorial fields, including a +// seo_title that names a leader for a bench whose page 404s. An SEO audit on +// 2026-09-30 flagged that as a leak. Do not "fix" it by filtering on +// VERCEL_ENV here. +// +// The reason is aggregate-blob.ts:66: on ANY Vercel deployment, staging and +// previews included, the site fetches this route on the production domain. +// Staging is the only place the dev-only benches render, so filtering them out +// here would empty the staging pipeline of all nine of them. The gate belongs +// where it is, at the loader (src/lib/materialize/load.ts), which drops them +// per deployment; this route is transport underneath that gate, not a public +// surface. +// +// What bounds the exposure instead: the route is noindex (the /api/:path* +// X-Robots-Tag in next.config.ts), it is linked from no page and listed in +// neither llms.txt nor the OpenAPI document, and it carries no secret. Checked +// 2026-09-30: no keyed URL shape appears anywhere in the payload. export const runtime = "nodejs"; // force-dynamic + explicit Cache-Control: CDN caches via s-maxage without // generating ISR write entries. revalidateTag("bench-aggregate") from the diff --git a/src/app/api/sitemap-data/route.ts b/src/app/api/sitemap-data/route.ts index 04a6a6553..45964eb3f 100644 --- a/src/app/api/sitemap-data/route.ts +++ b/src/app/api/sitemap-data/route.ts @@ -1,3 +1,14 @@ +// Vercel-edge proxy for the worker's slim sitemap blob, the same shape of +// transport as /api/aggregate. +// +// UNFILTERED BY DESIGN, for the same reason and with the same warning: it +// serves the blob verbatim, so on production it lists every dev-only bench. +// sitemap-blob.ts:4 points every Vercel deployment at this route on the +// production domain, staging included, and staging is where those benches +// render. Filtering on VERCEL_ENV here would strip them from the staging +// sitemap. The gate that matters is in src/lib/sitemap-builder.ts, which drops +// them per deployment; sitemap.xml on production is correct and carries none +// of them. export const runtime = "nodejs"; export const revalidate = 3600; diff --git a/src/lib/removed-benches.ts b/src/lib/removed-benches.ts index feaad9193..ae6b70d44 100644 --- a/src/lib/removed-benches.ts +++ b/src/lib/removed-benches.ts @@ -1,19 +1,45 @@ /** - * Bench slugs that must not exist on production. They render normally - * on dev / staging / preview (VERCEL_ENV !== "production") and are the - * staging pipeline: benches still being validated, or held back for a - * partnership announcement. + * Which bench slugs production does not serve, and what a hit on one gets. * - * Enforced in three places, all driven by this single set: - * 1. src/middleware.ts returns 410 Gone for direct URL hits on prod - * (SEO-correct signal for previously indexed URLs). - * 2. src/lib/materialize/load.ts drops the specs at loader level on - * prod, so the catalog index, category pages, /rpc hub, compare - * pairs, /api/citable, llms.txt and RSS never link or cite them. - * 3. src/app/sitemap.ts excludes the routes on prod. + * This file holds three sets, and they are NOT interchangeable. Each has its + * own docblock below; this is the map, because an earlier version of this + * comment described all of them as one and got the status code wrong. * - * Moving a bench to production = remove its slug here, bump the - * bench-set cache keys in src/lib/spec.ts, ship dev to main. + * REMOVED_BENCH_SLUGS retired for good, plus a few held for review. + * src/middleware.ts answers 410 Gone on prod, which is + * the SEO-correct signal for a URL that was indexed. + * RENAMED_BENCH_SLUGS split or renamed; a 308 to the successor, issued from + * next.config.ts redirects() so it costs no invocation. + * Checked before the 410 so a rename always wins. + * DEV_ONLY_BENCH_SLUGS the staging pipeline: still being validated, or held + * back on purpose. Dropped at the loader, so the page + * 404s rather than 410s. Nothing was ever published at + * those URLs, so there is no signal to preserve. + * + * The loader gate (src/lib/materialize/load.ts) is what keeps every rendered + * surface consistent: catalog index, category pages, /rpc hub, compare pairs, + * /api/citable, /api/stat, llms.txt, RSS, MCP and the Markdown views never + * link or cite a slug this deployment does not serve. The sitemap is dropped + * separately in src/lib/sitemap-builder.ts, through the isDevOnlyBench and + * isDevOnlyRoute helpers below rather than the sets directly, and is served by + * src/app/sitemap.xml/route.ts (there is no src/app/sitemap.ts; it became a + * Route Handler so it could set its own Cache-Control). + * + * Moving a bench to production = remove its slug from DEV_ONLY_BENCH_SLUGS, + * bump the bench-set cache keys in src/lib/spec.ts, ship dev to main. + * + * TWO PRODUCTION ENDPOINTS DELIBERATELY DO NOT ENFORCE ANY OF THIS, and an + * audit will find them again: /api/aggregate and /api/sitemap-data serve the + * worker's blobs verbatim, so on production both carry every dev-only slug + * with its editorial fields, including an seo_title naming a leader for a + * bench whose page 404s. Flagged 2026-09-30. They are transport, not a public + * surface: every Vercel deployment including staging fetches those two routes + * on the production domain (src/lib/aggregate-blob.ts, src/lib/sitemap-blob.ts), + * and staging is the only place these benches render, so filtering them there + * would empty the staging pipeline of all of them. Both routes carry that + * warning at the top. What bounds the exposure: noindex via the /api/:path* + * X-Robots-Tag, linked from no page, absent from llms.txt and the OpenAPI + * document, and no secret in the payload (no keyed URL shape, checked). */ /** * Bench slugs renamed / split, mapped to their canonical successor.