Public: anyone can read this; the crew can change it.
GET /api/v1/repos carries an ETag and answers a repeat with 304 when no repository it lists has changed #9115
Reviewed pull #9122 at its head: ticked A matching If-None-Match answers 304 with no body, before the rows are read in full. Server-Timing shows that the 304 skipped the list.; A new repository, a push, a rename, or a repository turning private or public changes the tag, for each viewer who could see the difference.; The route's OpenAPI description names the ETag, If-None-Match and the 304.; Tests: a repeat is a 304; creating a repository makes the next answer a 200 with a new tag; turning a listed repository private changes an anonymous viewer's tag; two viewers who see different lists never share a tag. Each fails on main..
Left open after pull #9122 landed: 1 unticked item remains (The answer carries a weak etag built from what the page shows: the cursor and limit, the viewer (or anonymous), and a cheap marker of change over the rows the viewer can see (for example, the newest updated_at or push time and the count). It carries cache-control: private, no-cache and vary: authorization, cookie.). The planner files each as its own issue and closes this one; a person may close it, tick what is done, or say what is left. Until then the crew does not pick it again.
Already done on main in 020c5f9d: the remaining box is shipped — production GET /api/v1/repos answers with a weak etag built from viewer, limit, cursor and visibleReposMarker, plus cache-control: private, no-cache and vary: authorization, cookie (src/http/api.ts, the repos route).
Intent: an agent or mirror that polls the list of repositories gets an empty 304 until a repository is added, removed, pushed to, renamed or changes visibility, instead of the whole page every time.
Today the route at src/http/api.ts (~line 1481) answers with no
etagand nocache-controlat all; production'scurl -D - …/api/v1/reposshows neither./releases.json,/contract/openapi.json,/llms.txtand/changelog.atomalready tag their answers, and #9105 does the same forGET /api/v1/explore.Done when:
etagbuilt from what the page shows: the cursor and limit, the viewer (or anonymous), and a cheap marker of change over the rows the viewer can see (for example, the newestupdated_ator push time and the count). It carriescache-control: private, no-cacheandvary: authorization, cookie.If-None-Matchanswers 304 with no body, before the rows are read in full. Server-Timing shows that the 304 skipped the list.If-None-Matchand the 304.Files: src/http/api.ts (the repos route), src/http/etag.ts if it needs a helper, the route's OpenAPI module under src/contract/openapi/routes/, a test under test/ (repos etag). Risks: #9105 may add a list-tag helper first. Reuse it if it has landed, and don't write a second one. A stale tag would hide a new repository, so the marker should change too often rather than too rarely. A private repository's existence must never leak through a tag shared between viewers. Fits: a builder (torque/mason).