Public: anyone can read this; the crew can change it.
GET /api/v1/orgs/:org/teams and …/teams/:team/members carry an ETag and answer a repeat with 304 until a team or its members change #9171
Check · Review · Land · Live done — Landed and live in production.
`GET /api/v1/orgs/:org/teams` and `…/teams/:team/members` now carry a weak ETag. A repeat that sends it back gets a 304 instead of the whole list.
shipped production is serving it deploy d5155e2
GET /api/v1/orgs/:org/teams and …/teams/:team/members now carry a weak ETag. A repeat that sends it back gets a 304 instead of the whole list.
- Headers: Both routes in
src/http/api/orgs.tsbuild the tag from the exact JSON body withweakEtag. The 200 carries thatetag,cache-control: private, no-cacheandvary: authorization, cookie. A repeat holding the tag gets a 304 with the same headers and no body, and thesecurityFloorheaders are still there. This uses a smalltaggedhelper local toorgs.ts, the same shape as the one inissues.ts. Response bodies are unchanged and I didn't touchsrc/http/api.ts. - Access: The 304 is only reached after the org and team checks. A 404 (unknown org, unknown team, or a team the viewer cannot see) and a refused write carry no ETag. A stranger holding another viewer's tag gets a 404 for a team they can't see, or a 200 with a different tag for the teams list.
- Contract: I added
alsoStatus: [304]and a sentence on the ETag to the two route summaries insrc/contract/openapi/, as the earlier ETag commits did. That is beyond the two files the issue listed. - Tests:
test/org-teams-etag.test.tscovers the 200 and 304 headers, creating and removing a team, adding and removing a member, and the 404, 403 and stranger cases. Without the change it fails: there is no ETag to hold. - Checks: The new test, the existing org, MCP org, contract and OpenAPI coverage tests,
bun x tsc --noEmitandbun scripts/gate.tsall pass. I did not runscripts/fast.tsor the full suite.
To try it: curl -i -H "authorization: Bearer $T" $URL/api/v1/orgs/acme/teams, then repeat with -H 'if-none-match: <etag>'.
I chose not to tag GET /api/v1/orgs/:org, which also lists teams, because the issue names only these two routes.
The check failed on a gate rule, not on code: the pull changes src/http/api/orgs.ts, but no commit message had a Try: line. I added one in a new empty commit (d340b62e) and pushed it, so history isn't rewritten. bun scripts/gate.ts is clean locally. I haven't seen the forge's check run on the new push yet.
The Try: line says to call GET /api/v1/orgs/<org>/teams on the preview, then repeat it with the returned etag in If-None-Match and expect a 304.
The code and tests from the first push are unchanged.
Clean. The tag is computed after the org and team access checks, so 404s carry no ETag and a stranger cannot get a 304 for a team they cannot see. A 304 carries the same headers, and the securityFloor headers are asserted in the test. The test fails without the change. Note: a third local tagged helper now exists beside issues.ts and releases.ts; worth consolidating later, not blocking.
GET /api/v1/orgs/:org/teamsand…/teams/:team/membersnow carry a weak ETag. A repeat that sends it back gets a 304 instead of the whole list.src/http/api/orgs.tsbuild the tag from the exact JSON body withweakEtag. The 200 carries thatetag,cache-control: private, no-cacheandvary: authorization, cookie. A repeat holding the tag gets a 304 with the same headers and no body, and thesecurityFloorheaders are still there. This uses a smalltaggedhelper local toorgs.ts, the same shape as the one inissues.ts. Response bodies are unchanged and I didn't touchsrc/http/api.ts.alsoStatus: [304]and a sentence on the ETag to the two route summaries insrc/contract/openapi/, as the earlier ETag commits did. That is beyond the two files the issue listed.test/org-teams-etag.test.tscovers the 200 and 304 headers, creating and removing a team, adding and removing a member, and the 404, 403 and stranger cases. Without the change it fails: there is no ETag to hold.bun x tsc --noEmitandbun scripts/gate.tsall pass. I did not runscripts/fast.tsor the full suite.To try it:
curl -i -H "authorization: Bearer $T" $URL/api/v1/orgs/acme/teams, then repeat with-H 'if-none-match: <etag>'.I chose not to tag
GET /api/v1/orgs/:org, which also lists teams, because the issue names only these two routes.Closes #9165