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

GET /api/v1/repos/:owner/:name/egress-grants carries an ETag and answers a repeat with 304 until a grant is added, used or revoked #9184

Merged opened this

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

Merged

`GET /api/v1/repos/:owner/:name/egress-grants` now sends a weak ETag, so an admin's agent that asks again with the same tag gets a 304 with no body until a grant changes.

shipped production is serving it deploy e209d31

opened this

GET /api/v1/repos/:owner/:name/egress-grants now sends a weak ETag, so an admin's agent that asks again with the same tag gets a 304 with no body until a grant changes.

What changed

  • In src/http/api/egress_grants.ts, the read builds the same JSON body as before and tags it with weakEtag. The 200 also sends cache-control: private, no-cache and vary: authorization, cookie.
  • A request whose If-None-Match matches the tag gets a 304 with the same headers and no body. The securityFloor headers are still on it because the server adds them.
  • The tag is added only after the scope, repository and admin checks pass. So a token short of admin, a non-admin and a missing repository answer as they do today, with no ETag, even with If-None-Match: *.
  • This uses the same tagged helper as the hooks route (#9173). src/http/api.ts, the POST and DELETE routes and the egress proxy are untouched.

What to look at

  • Refused requests don't move the tag. The issue asks that a refused request the view lists moves it. This view lists only the destinations and the grant and revoke history, not refused attempts. A refused grant writes nothing, so the tag holds, and the test checks that it does. If refused proxy requests should count, the view has to show them first, which belongs in another issue.
  • Changes to forge.json move the tag but aren't tested. Adding or removing a destination there changes the body, so the tag moves too.

Tests

  • test/egress-grants-etag.test.ts covers the 200 headers, the 304 and its security headers, granting and revoking moving the tag, a refused grant leaving it, and the three refusals with *. With the route change removed, 4 of its 6 tests fail.
  • That file, test/api-egress-grants-timing.test.ts and test/app-egress-grants.test.ts pass, as do bun scripts/gate.ts and bun x tsc --noEmit.

To try it: call GET /api/v1/repos/<owner>/<name>/egress-grants with an admin token, then again with If-None-Match: <etag> to get the 304. A POST of a destination that forge.json asks for gives you a 200 with a new tag.

🤖 Generated with Claude Code

Closes #9179

commented

GET /api/v1/repos/:owner/:name/egress-grants now sends a weak ETag, so an admin's agent that asks again with the same tag gets a 304 with no body until a grant changes.

What changed

  • In src/http/api/egress_grants.ts, the read builds the same JSON body as before and tags it with weakEtag. The 200 also sends cache-control: private, no-cache and vary: authorization, cookie.
  • A request whose If-None-Match matches the tag gets a 304 with the same headers and no body. The securityFloor headers are still on it because the server adds them.
  • The tag is added only after the scope, repository and admin checks pass. So a token short of admin, a non-admin and a missing repository answer as they do today, with no ETag, even with If-None-Match: *.
  • This uses the same tagged helper as the hooks route (#9173). src/http/api.ts, the POST and DELETE routes and the egress proxy are untouched.

What to look at

  • Refused requests don't move the tag. The issue asks that a refused request the view lists moves it. This view lists only the destinations and the grant and revoke history, not refused attempts. A refused grant writes nothing, so the tag holds, and the test checks that it does. If refused proxy requests should count, the view has to show them first, which belongs in another issue.
  • Changes to forge.json move the tag but aren't tested. Adding or removing a destination there changes the body, so the tag moves too.

Tests

  • test/egress-grants-etag.test.ts covers the 200 headers, the 304 and its security headers, granting and revoking moving the tag, a refused grant leaving it, and the three refusals with *. With the route change removed, 4 of its 6 tests fail.
  • That file, test/api-egress-grants-timing.test.ts and test/app-egress-grants.test.ts pass, as do bun scripts/gate.ts and bun x tsc --noEmit.

To try it: call GET /api/v1/repos/<owner>/<name>/egress-grants with an admin token, then again with If-None-Match: <etag> to get the 304. A POST of a destination that forge.json asks for gives you a 200 with a new tag.

🤖 Generated with Claude Code

commented

GET /api/v1/repos/:owner/:name/egress-grants now sends a weak ETag, so an admin's agent that asks again with the same tag gets a 304 with no body until a grant changes.

What changed

  • In src/http/api/egress_grants.ts, the read builds the same JSON body as before and tags it with weakEtag. The 200 also sends cache-control: private, no-cache and vary: authorization, cookie.
  • A request whose If-None-Match matches the tag gets a 304 with the same headers and no body. The securityFloor headers are still on it because the server adds them.
  • The tag is added only after the scope, repository and admin checks pass. So a token short of admin, a non-admin and a missing repository answer as they do today, with no ETag, even with If-None-Match: *.
  • This uses the same tagged helper as the hooks route (#9173). src/http/api.ts, the POST and DELETE routes and the egress proxy are untouched.
  • The last commit adds no code, only the Try: line the gate requires. It is a separate commit because history can't be force-pushed.

What to look at

  • Refused requests don't move the tag. The issue asks that a refused request the view lists moves it. This view lists only the destinations and the grant and revoke history, not refused attempts. A refused grant writes nothing, so the tag holds, and the test checks that it does. If refused proxy requests should count, the view has to show them first, which belongs in another issue.
  • Changes to forge.json move the tag but aren't tested. Adding or removing a destination there changes the body, so the tag moves too.

Tests

  • test/egress-grants-etag.test.ts covers the 200 headers, the 304 and its security headers, granting and revoking moving the tag, a refused grant leaving it, and the three refusals with *. With the route change removed, 4 of its 6 tests fail.
  • bun scripts/gate.ts passes with the new Try: line, and the new test file passes (6 of 6).
  • Before this push: test/api-egress-grants-timing.test.ts, test/app-egress-grants.test.ts and bun x tsc --noEmit passed. The only commit since then adds no code, so I didn't rerun them.

To try it on the preview: call GET /api/v1/repos/<owner>/<name>/egress-grants with an admin token, then again with If-None-Match: <etag> to get the 304.

🤖 Generated with Claude Code

Approved

Clean: tag is added only after scope/repo/admin checks, weak etag over the rendered body, private no-cache and vary set, 304 is bodiless and the server adds the floor. Tests cover each case. Refused grants write nothing the view lists, so the tag holding is correct. Notes: the local tagged helper repeats a pattern already in several modules (non-blocking).

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs