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
Check · Review · Land · Live done — Landed and live in production.
`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
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 notessessionfirst, so a refused read (404/403) still names it. The read sits insidetimePhase, andjson(…)inside arenderphase.loadRepoand the scope checks stay outside the read phase. On/app, the access checkappViewsits inside theappphase, because the read is the same call. - Test:
test/app-read-timing.test.tshas two tests per route. One asserts the phase order on a 200. The other asserts a 404 for a missing repo names onlysession. Followingtest/api-tags-timing.test.ts, it drops the server's own leadingapp/dbentries before comparing. All eight fail without theapi.tschange. - Checks:
bun testpasses for the new file,test/apps-api.test.tsandtest/api-tags-timing.test.ts.bun x tsc --noEmitfailed in this sandbox with "Cannot find type definition file for 'bun'", so I did not get a type check. I did not runbun installfirst. - Name clash: The
/appread phase is namedapp, which is also the name of the server's own total entry (app;dur=…). The header is valid. A reader that looks upappby name finds the server's entry first. - Left out: The mutating routes (POST, PUT, PATCH, DELETE) are untimed.
🤖 Generated with Claude Code
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
/appread phase repeats the nameappthat the server uses for its total. The issue asked for that name, andPOST /api/v1/reposalready emits anappphase (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,appViewis 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
rotoken on/domainsor/env) would also namesessionby construction, so this is fine as it stands.
GET …/app,…/app/members,…/app/domainsand…/app/envnow answer aServer-Timingheader namingsession, then the read (app,members,domains,env), thenrender. Bodies, status codes and access rules are unchanged, and an env read never puts a value in a phase name.src/http/api.ts, each handler notessessionfirst, so a refused read (404/403) still names it. The read sits insidetimePhase, andjson(…)inside arenderphase.loadRepoand the scope checks stay outside the read phase. On/app, the access checkappViewsits inside theappphase, because the read is the same call.test/app-read-timing.test.tshas two tests per route. One asserts the phase order on a 200. The other asserts a 404 for a missing repo names onlysession. Followingtest/api-tags-timing.test.ts, it drops the server's own leadingapp/dbentries before comparing. All eight fail without theapi.tschange.bun testpasses for the new file,test/apps-api.test.tsandtest/api-tags-timing.test.ts.bun x tsc --noEmitfailed in this sandbox with "Cannot find type definition file for 'bun'", so I did not get a type check. I did not runbun installfirst./appread phase is namedapp, which is also the name of the server's own total entry (app;dur=…). The header is valid. A reader that looks upappby name finds the server's entry first.🤖 Generated with Claude Code
Closes #4981