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

An app's API reads name their Server-Timing phases: app, members, domains and env #4991

Merged opened this

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

Merged

`GET …/app`, `…/app/members`, `…/app/domains` and `…/app/env` now answer a `Server-Timing` header naming `session`, then the read (`app`, `members`, `domains`, `env`), then `render`. Bodies, status codes and access rules are unchanged, and an env read never puts a value in a phase name.

shipped production is serving it deploy eb43263

opened this

GET …/app, …/app/members, …/app/domains and …/app/env now answer a Server-Timing header naming session, then the read (app, members, domains, env), then render. Bodies, status codes and access rules are unchanged, and an env read never puts a value in a phase name.

  • Placement: In src/http/api.ts, each handler notes session first, so a refused read (404/403) still names it. The read sits inside timePhase, and json(…) inside a render phase. loadRepo and the scope checks stay outside the read phase. On /app, the access check appView sits inside the app phase, because the read is the same call.
  • Test: test/app-read-timing.test.ts has two tests per route. One asserts the phase order on a 200. The other asserts a 404 for a missing repo names only session. Following test/api-tags-timing.test.ts, it drops the server's own leading app/db entries before comparing. All eight fail without the api.ts change.
  • Checks: bun test passes for the new file, test/apps-api.test.ts and test/api-tags-timing.test.ts. bun x tsc --noEmit failed in this sandbox with "Cannot find type definition file for 'bun'", so I did not get a type check. I did not run bun install first.
  • Name clash: The /app read phase is named app, which is also the name of the server's own total entry (app;dur=…). The header is valid. A reader that looks up app by name finds the server's entry first.
  • Left out: The mutating routes (POST, PUT, PATCH, DELETE) are untimed.

🤖 Generated with Claude Code

Closes #4981

commented

GET …/app, …/app/members, …/app/domains and …/app/env now answer a Server-Timing header naming session, then the read (app, members, domains, env), then render. Bodies, status codes and access rules are unchanged, and an env read never puts a value in a phase name.

  • Placement: In src/http/api.ts, each handler notes session first, so a refused read (404/403) still names it. The read sits inside timePhase, and json(…) inside a render phase. loadRepo and the scope checks stay outside the read phase. On /app, the access check appView sits inside the app phase, because the read is the same call.
  • Test: test/app-read-timing.test.ts has two tests per route. One asserts the phase order on a 200. The other asserts a 404 for a missing repo names only session. Following test/api-tags-timing.test.ts, it drops the server's own leading app/db entries before comparing. All eight fail without the api.ts change.
  • Checks: bun test passes for the new file, test/apps-api.test.ts and test/api-tags-timing.test.ts. bun x tsc --noEmit failed in this sandbox with "Cannot find type definition file for 'bun'", so I did not get a type check. I did not run bun install first.
  • Name clash: The /app read phase is named app, which is also the name of the server's own total entry (app;dur=…). The header is valid. A reader that looks up app by name finds the server's entry first.
  • Left out: The mutating routes (POST, PUT, PATCH, DELETE) are untimed.

🤖 Generated with Claude Code

Approved

Approved. The change does what #4981 asks and nothing else. All four app reads note session before any check, so a refused read still names it. Each read sits inside its timePhase and json(…) sits inside render. Bodies, status codes and access rules are unchanged. Every phase name is a constant, so an env read never puts a variable name or value into the header. No new input, write path, dependency or terminal surface.

On the preview, GET /api/v1/repos/trevato/forge/app answers 404 with server-timing: app;dur=5.4, session;dur=0.1, so the refused path shows session live. The preview has no repositories and no app, so I could not look at a 200 there; the new test covers it with eight cases that fail without the change.

Notes, none of them blocking:

  • The /app read phase repeats the name app that the server uses for its total. The issue asked for that name, and POST /api/v1/repos already emits an app phase (api.ts:5276), so this follows the existing pattern. A later rename across the board (app-view, say) would make the header unambiguous.
  • On /app, appView is the access check and the read in one call, so the check is timed with the read. That is disclosed and reasonable.
  • Only the 404 path is tested as a refusal. A 403 (a ro token on /domains or /env) would also name session by construction, so this is fine as it stands.
Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs