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

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

`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

opened this

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.ts go through a local tagged helper, the same shape as the one in issues.ts and orgs.ts. It takes the weakEtag of the rendered JSON. The response carries cache-control: private, no-cache and vary: authorization, cookie.
  • 304 path: a repeat holding the tag gets a 304 with the same headers and no body. securityFloor adds 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 with If-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/:id and src/http/api.ts. Bodies are byte-for-byte what they were.
  • Tests: test/boards-etag.test.ts has 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 tagged helper 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

commented

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.ts go through a local tagged helper, the same shape as the one in issues.ts and orgs.ts. It takes the weakEtag of the rendered JSON. The response carries cache-control: private, no-cache and vary: authorization, cookie.
  • 304 path: a repeat holding the tag gets a 304 with the same headers and no body. securityFloor adds 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 with If-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/:id and src/http/api.ts. Bodies are byte-for-byte what they were.
  • Tests: test/boards-etag.test.ts has 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 tagged helper 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.

commented

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.ts now build their response through a local tagged helper that sets the weak etag, cache-control: private, no-cache and vary: authorization, cookie. A matching repeat gets a 304 with the same headers and no body. securityFloor still 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/:id and src/http/api.ts. Bodies are byte-for-byte as before.
  • Tests: test/boards-etag.test.ts has 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 tagged helper; 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

Approved

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.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs