Public: anyone can read this; the crew can change it.
GET /api/v1/boards, /boards/pins and /boards/:slug carry an ETag and answer a repeat with 304 until a board or its pins change #9254
Check · Review · Land · Live done — Landed and live in production.
`GET /api/v1/boards`, `/boards/pins` and `/boards/:slug` now carry a weak ETag and answer a repeat with 304 until a board or its pins change. `tsc` is clean, the new test passes, and 15 neighbouring board and pin tests pass. I skipped `scripts/gate.ts` because it isn't in this clone. I did not run `scripts/design-lint.ts`, since nothing under `src/web/` changed.
shipped production is serving it deploy eb43263
GET /api/v1/boards, /boards/pins and /boards/:slug now carry a weak ETag and answer a repeat with 304 until a board or its pins change. tsc is clean, the new test passes, and 15 neighbouring board and pin tests pass. I skipped scripts/gate.ts because it isn't in this clone. I did not run scripts/design-lint.ts, since nothing under src/web/ changed.
- Change: the three routes in
src/http/api/boards.tsgo through a localtaggedhelper, the same shape as the one inissues.tsandorgs.ts. It takes theweakEtagof the rendered JSON. The response carriescache-control: private, no-cacheandvary: authorization, cookie. - 304 path: a repeat holding the tag gets a 304 with the same headers and no body.
securityFlooradds its headers as usual. The helper runs only after the admin check and the board lookup, so 401, 403 and 404 answers are unchanged and carry no ETag, even withIf-None-Match: *. - Etag movement: the tag is a hash of the body, so it moves whenever a board is created, renamed, deleted or has a panel written, or a pin is added or removed. Each slug has its own tag. Viewers with different pins get different tags.
- Untouched:
/boards/sources,/boards/:slug/panels/:idandsrc/http/api.ts. Bodies are byte-for-byte what they were. - Tests:
test/boards-etag.test.tshas 9 tests covering each of these. With the source change stashed, 7 of the 9 fail; the other two (no session, unknown board, sources and panel data without a tag) check behaviour that stays as it was. - Not done: no shared
taggedhelper across the API files. The repository already keeps one copy per file, and I kept that pattern.
To try it, sign in as an admin and curl -i /api/v1/boards. Repeat it with -H 'if-none-match: <etag>' to get the 304.
GET /api/v1/boards, /boards/pins and /boards/:slug now carry a weak ETag. A repeat that sends it in If-None-Match gets a 304 until a board or its pins change.
The check failed because no commit message had a Try: line. I pushed an empty commit with one, so no history was rewritten. I haven't seen the re-run's result yet. Nothing else changed since the last push, and tsc and the new tests were clean on that push.
- What changed: the three routes in
src/http/api/boards.tsnow build their response through a localtaggedhelper that sets the weak etag,cache-control: private, no-cacheandvary: authorization, cookie. A matching repeat gets a 304 with the same headers and no body.securityFloorstill adds its headers. - Access first: the 304 comes only after the admin check and the board lookup. 401, 403 and 404 answers are unchanged and carry no ETag, even with
If-None-Match: *. - When the etag moves: the tag is a hash of the body. It moves when a board is created, renamed or deleted, when a panel on it is written, and when a pin is added or removed. Each slug has its own tag, and viewers whose pins differ get different tags.
- Left alone:
/boards/sources,/boards/:slug/panels/:idandsrc/http/api.ts. Bodies are byte-for-byte as before. - Tests:
test/boards-etag.test.tshas 9 tests covering each of these. With the source change stashed, 7 of the 9 fail; the other two check behaviour that is meant to stay as it was. - Not done: I added no shared
taggedhelper; the repository keeps one copy per file and I followed that.
Try: curl -i -H 'authorization: Bearer <admin token>' <preview>/api/v1/boards, then repeat it with -H 'if-none-match: <etag>' to get the 304.
🤖 Generated with Claude Code
Clean. The three GET routes tag the rendered body with weakEtag, send cache-control private, no-cache and vary authorization, cookie, and answer a repeat with 304 and no body. The tagging runs only after the admin and board-lookup checks, so 401, 403 and 404 stay untagged, including with If-None-Match: *. sources and panel data are untouched. The tests cover each case: creation, rename and deletion, pin and unpin, panel writes, per-slug tags, and separate viewers. The local helper follows the per-file pattern in issues.ts. The preview answers 401 without a session and no ETag, which fits; I could not sign in there, so the 304 path rests on the tests.
GET /api/v1/boards,/boards/pinsand/boards/:slugnow carry a weak ETag and answer a repeat with 304 until a board or its pins change.tscis clean, the new test passes, and 15 neighbouring board and pin tests pass. I skippedscripts/gate.tsbecause it isn't in this clone. I did not runscripts/design-lint.ts, since nothing undersrc/web/changed.src/http/api/boards.tsgo through a localtaggedhelper, the same shape as the one inissues.tsandorgs.ts. It takes theweakEtagof the rendered JSON. The response carriescache-control: private, no-cacheandvary: authorization, cookie.securityFlooradds its headers as usual. The helper runs only after the admin check and the board lookup, so 401, 403 and 404 answers are unchanged and carry no ETag, even withIf-None-Match: *./boards/sources,/boards/:slug/panels/:idandsrc/http/api.ts. Bodies are byte-for-byte what they were.test/boards-etag.test.tshas 9 tests covering each of these. With the source change stashed, 7 of the 9 fail; the other two (no session, unknown board, sources and panel data without a tag) check behaviour that stays as it was.taggedhelper across the API files. The repository already keeps one copy per file, and I kept that pattern.To try it, sign in as an admin and
curl -i/api/v1/boards. Repeat it with-H 'if-none-match: <etag>'to get the 304.Closes #9242