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
Check · Review · Land · Live done — Landed and live in production.
`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
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 carriesetag,cache-control: private, no-cacheandvary: authorization, cookie. - Repeat: a request whose
If-None-Matchholds the tag gets a 304 with those same headers and no body. It gets thesecurityFloorheaders too. This follows the pattern of the prompts route (#9221). - Order of checks: the tag is computed only after the
mayReadcheck. A caller who fails it still gets the 403forbiddenwith no ETag, even withIf-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.
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.tstags the response withweakEtagover the exact body bytes. The 200 carriesetag,cache-control: private, no-cacheandvary: authorization, cookie. - 304: a request whose
If-None-Matchholds 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 withIf-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.tsis untouched. - Tests:
test/keeper-etag.test.tscovers the headers, the 304, the tag moving on each release event, and the 403 cases. It andtest/api-keeper.test.tspass, andbun x tsc --noEmitwas 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.
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.
GET /api/v1/keepernow carries a weak ETag and answers a repeat with a 304 until the release history changes.bun x tsc --noEmitandbun scripts/gate.tswere clean.test/keeper-etag.test.tsand the existingtest/api-keeper.test.tspass (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):weakEtag. The 200 carriesetag,cache-control: private, no-cacheandvary: authorization, cookie.If-None-Matchholds the tag gets a 304 with those same headers and no body. It gets thesecurityFloorheaders too. This follows the pattern of the prompts route (#9221).mayReadcheck. A caller who fails it still gets the 403forbiddenwith no ETag, even withIf-None-Match: *.Tests (
test/keeper-etag.test.ts):*.I did not touch
src/http/api.ts. I did not callkeep_planbecause that tool isn't available in this session.Closes #9204