Intent: when an app feels slow, its Server-Timing header says whether the forge or the app spent the time.
Seen 2026-11-02: https://lab--forge-trevato-apps.open-platform.sh/healthz takes 6–11 s to first byte, but its Server-Timing is only the app's own db;dur=0.1, app;dur=1.1. For a proxied answer, the proxy adds no phase of its own: withPhases is used only on the forge's own /__forge/* pages.
Done looks like:
[ ] Every response the proxy relays from an app keeps the app's Server-Timing entries and appends the forge's phases: at least route (finding the target and its grant), wake (waiting for a sleeping or starting env, 0 when none), upstream (until the app's headers) and rewrite (pill injection, when it ran).
[ ] Phase names never collide with the app's: prefix them forge-.
[ ] A test with a slow fake upstream (300 ms) sees forge-upstream ≥ 300 and the app's own entries intact. A test of a sleeping env sees forge-wake > 0.
[ ] The pull description has a curl of a preview of this pull showing the header, and says where the 6–11 s goes.
Files: src/apps/proxy.ts, a test under test/apps/.
Risk: the header must not leak anything beyond durations: no hosts, ids or user names.
Intent: when an app feels slow, its Server-Timing header says whether the forge or the app spent the time.
Seen 2026-11-02: https://lab--forge-trevato-apps.open-platform.sh/healthz takes 6–11 s to first byte, but its Server-Timing is only the app's own
db;dur=0.1, app;dur=1.1. For a proxied answer, the proxy adds no phase of its own:withPhasesis used only on the forge's own/__forge/*pages.Done looks like:
route(finding the target and its grant),wake(waiting for a sleeping or starting env, 0 when none),upstream(until the app's headers) andrewrite(pill injection, when it ran).forge-.forge-upstream≥ 300 and the app's own entries intact. A test of a sleeping env seesforge-wake> 0.Files: src/apps/proxy.ts, a test under test/apps/. Risk: the header must not leak anything beyond durations: no hosts, ids or user names.