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

GET /api/v1/keeper carries an ETag and answers a repeat with 304 until a release is kept, promoted or rolled back #9231

Merged opened this

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

Merged

`GET /api/v1/keeper` now carries a weak ETag and answers a repeat with a 304 until the release history changes. `bun x tsc --noEmit` and `bun scripts/gate.ts` were clean. `test/keeper-etag.test.ts` and the existing `test/api-keeper.test.ts` pass (6 tests). I did not run the full suite, so the forge's check on the push is the first full run.

shipped production is serving it deploy 5a6a4f7

opened this

GET /api/v1/keeper now carries a weak ETag and answers a repeat with a 304 until the release history changes. bun x tsc --noEmit and bun scripts/gate.ts were clean. test/keeper-etag.test.ts and the existing test/api-keeper.test.ts pass (6 tests). I did not run the full suite, so the forge's check on the push is the first full run.

What changed (src/http/api/keeper.ts):

  • Same body: the route builds the same JSON as before, so the body is byte-for-byte unchanged, and tags it with weakEtag. The 200 carries etag, cache-control: private, no-cache and vary: authorization, cookie.
  • Repeat: a request whose If-None-Match holds the tag gets a 304 with those same headers and no body. It gets the securityFloor headers too. This follows the pattern of the prompts route (#9221).
  • Order of checks: the tag is computed only after the mayRead check. A caller who fails it still gets the 403 forbidden with no ETag, even with If-None-Match: *.
  • Per-viewer tags: the tag is a hash of the exact bytes sent, so two viewers shown different bodies never share one.

Tests (test/keeper-etag.test.ts):

  • Headers and 304: the 200 headers, the tag matching the body's hash, and the bodiless 304 with its headers.
  • Movement: the tag moves when a release is recorded, promoted, held or rolled back, and when a hold is lifted. A stale tag gets a 200, a current one a 304.
  • 403 cases: a non-admin and an anonymous caller get the 403 with no ETag, even with *.

I did not touch src/http/api.ts. I did not call keep_plan because that tool isn't available in this session.

Closes #9204

commented

GET /api/v1/keeper now carries a weak ETag and answers a repeat with a 304 until the release history changes. bun x tsc --noEmit and bun scripts/gate.ts were clean. test/keeper-etag.test.ts and the existing test/api-keeper.test.ts pass (6 tests). I did not run the full suite, so the forge's check on the push is the first full run.

What changed (src/http/api/keeper.ts):

  • Same body: the route builds the same JSON as before, so the body is byte-for-byte unchanged, and tags it with weakEtag. The 200 carries etag, cache-control: private, no-cache and vary: authorization, cookie.
  • Repeat: a request whose If-None-Match holds the tag gets a 304 with those same headers and no body. It gets the securityFloor headers too. This follows the pattern of the prompts route (#9221).
  • Order of checks: the tag is computed only after the mayRead check. A caller who fails it still gets the 403 forbidden with no ETag, even with If-None-Match: *.
  • Per-viewer tags: the tag is a hash of the exact bytes sent, so two viewers shown different bodies never share one.

Tests (test/keeper-etag.test.ts):

  • Headers and 304: the 200 headers, the tag matching the body's hash, and the bodiless 304 with its headers.
  • Movement: the tag moves when a release is recorded, promoted, held or rolled back, and when a hold is lifted. A stale tag gets a 200, a current one a 304.
  • 403 cases: a non-admin and an anonymous caller get the 403 with no ETag, even with *.

I did not touch src/http/api.ts. I did not call keep_plan because that tool isn't available in this session.

commented

GET /api/v1/keeper now carries a weak ETag and answers a repeat with a 304 until a release is recorded, promoted, held or rolled back. The check had failed because no commit message had a Try: line. I added an empty commit carrying one and pushed it. bun scripts/gate.ts now passes locally. The forge's check on this push has not come back yet.

What changed

  • Headers: src/http/api/keeper.ts tags the response with weakEtag over the exact body bytes. The 200 carries etag, cache-control: private, no-cache and vary: authorization, cookie.
  • 304: a request whose If-None-Match holds the tag gets a 304 with the same headers, no body, and the usual security headers.
  • Access: the tag is computed only after mayRead. A viewer who is neither an administrator nor a repository reader still gets the 403 with no ETag, even with If-None-Match: *.
  • Per-viewer tags: the tag hashes the body, so viewers shown different bodies never share one.
  • Body: it is byte-for-byte unchanged, and src/http/api.ts is untouched.
  • Tests: test/keeper-etag.test.ts covers the headers, the 304, the tag moving on each release event, and the 403 cases. It and test/api-keeper.test.ts pass, and bun x tsc --noEmit was clean. I did not run the full suite here.

Not done: there is no separate commit-message rule fix beyond the one empty commit.

Try: curl -i -H 'authorization: Bearer <admin token>' https://lab--9231--forge-trevato-apps.open-platform.sh/api/v1/keeper, then repeat it with -H 'if-none-match: <the etag>' to get a 304. The preview URL assumes the pull's preview follows the same pattern as the others listed.

Approved

Clean. The tag is computed after mayRead over the exact bytes sent, headers and 304 match the prompts route pattern, 403 path is untouched, tests cover each behaviour.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs