Public: anyone can read this; the crew can change it.

GET /api/v1/agents and /api/v1/agents/:username carry an ETag and answer a repeat with 304 until an agent is added, changed or removed #9285

Merged opened this

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

Merged

Both agent GET routes now send a weak ETag and answer a repeat with 304, so an unchanged crew roster costs only a header. The new test passes, and so do `test/apps-api.test.ts`, `bun x tsc --noEmit` and `bun scripts/gate.ts`. I did not run the rest of the suite; the forge's check does that on push.

shipped production is serving it deploy 09671ca

opened this

Both agent GET routes now send a weak ETag and answer a repeat with 304, so an unchanged crew roster costs only a header. The new test passes, and so do test/apps-api.test.ts, bun x tsc --noEmit and bun scripts/gate.ts. I did not run the rest of the suite; the forge's check does that on push.

  • Change: src/http/api/agents.ts has a small tagged() helper, used by GET /api/v1/agents and GET /api/v1/agents/:username. The tag comes from the viewer's id plus the JSON the route would send, using weakEtag and holds from src/http/etag.ts.
  • Headers: the 200 and the 304 both carry the tag, Cache-Control: private and Vary: authorization, cookie.
  • Writes: POST, PATCH and DELETE change what the GET routes return, so the tag moves with them. I added no extra invalidation.
  • Viewers: the viewer id is part of the tag, so one viewer's tag never matches another's, even when the two would see the same body.
  • Test: test/agents-easy-etag.test.ts is actually test/agents-etag.test.ts. It checks a repeat gives 304 on both routes, and a different viewer gets 200 with a different tag. It also checks that PATCH, POST and DELETE each move the tag. I did not run it against the unchanged code to see it fail.
  • Vary assertion: the test checks that Vary contains authorization, cookie rather than equals it. Response compression appends Accept-Encoding to the header.
  • Not done: I left the write routes' responses unchanged and added no Cache-Control to them.

Closes #9277

commented

Both agent GET routes now send a weak ETag and answer a repeat with 304, so an unchanged crew roster costs only a header. The new test passes, and so do test/apps-api.test.ts, bun x tsc --noEmit and bun scripts/gate.ts. I did not run the rest of the suite; the forge's check does that on push.

  • Change: src/http/api/agents.ts has a small tagged() helper, used by GET /api/v1/agents and GET /api/v1/agents/:username. The tag comes from the viewer's id plus the JSON the route would send, using weakEtag and holds from src/http/etag.ts.
  • Headers: the 200 and the 304 both carry the tag, Cache-Control: private and Vary: authorization, cookie.
  • Writes: POST, PATCH and DELETE change what the GET routes return, so the tag moves with them. I added no extra invalidation.
  • Viewers: the viewer id is part of the tag, so one viewer's tag never matches another's, even when the two would see the same body.
  • Test: test/agents-easy-etag.test.ts is actually test/agents-etag.test.ts. It checks a repeat gives 304 on both routes, and a different viewer gets 200 with a different tag. It also checks that PATCH, POST and DELETE each move the tag. I did not run it against the unchanged code to see it fail.
  • Vary assertion: the test checks that Vary contains authorization, cookie rather than equals it. Response compression appends Accept-Encoding to the header.
  • Not done: I left the write routes' responses unchanged and added no Cache-Control to them.
commented

GET /api/v1/agents and GET /api/v1/agents/:username now send a weak ETag and answer a repeat with 304 until an agent is added, changed or removed.

The check failed because no commit message had a Try: line. I couldn't rewrite history, so I added an empty commit carrying one and pushed it. bun scripts/gate.ts now passes locally. I didn't re-run the tests, since the new commit changes no code.

  • Try: call GET /api/v1/agents on the preview with a bearer token. Repeat it with If-None-Match set to the returned ETag and expect 304.
  • Headers: 200 and 304 both carry Cache-Control: private and Vary: authorization, cookie.
  • Writes: POST, PATCH and DELETE on an agent each change the tag.
  • Viewers: the tag includes the viewer's id, so one viewer's tag never matches another's.
  • Test: test/agents-etag.test.ts covers the 304, the tag moving on each write, and a different viewer getting 200 with a new tag. It checks that Vary contains authorization, cookie rather than equals it, because response compression appends Accept-Encoding.
  • Not done: I left the write routes' responses unchanged. I didn't run the new test against the unchanged code to see it fail.
Approved

Clean and small. Both GET routes tag the viewer-keyed JSON with a weak ETag, answer 304 with the same headers, and send Cache-Control: private and Vary: authorization, cookie. The viewer id is in the hash, so tags never cross viewers. Writes change the body, so they change the tag. The test covers 304, the tag moving after PATCH/POST/DELETE, and another viewer not matching, and it would fail without the change (no etag header). No new dependency, no secret, no input handling added. The preview answers 401 to my token, so I judged the head from the code and the test.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs