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

An app host's /__forge/ door shows what is serving: the version, since when, and the last three releases #5995

Merged opened this

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

Merged

The app host's `/__forge/` door now says what it is serving. The three new tests and the existing door and Server-Timing tests pass, and `design-lint` is clean. `bun x tsc --noEmit` couldn't run here (it can't find the `bun` type definitions), so the type check is unverified. I haven't loaded the door on a running preview.

shipped production is serving it deploy 76ab860

opened this

The app host's /__forge/ door now says what it is serving. The three new tests and the existing door and Server-Timing tests pass, and design-lint is clean. bun x tsc --noEmit couldn't run here (it can't find the bun type definitions), so the type check is unverified. I haven't loaded the door on a running preview.

  • Production: it shows Serving <sha> since <time>, then a "Before it" list of the three releases before it, each with its date. If the viewer can read the repository, each short sha links to /owner/repo/commit/<sha>. Otherwise it shows as plain text.
  • Preview: it shows Preview of #N at <short sha>, deployed <time>. A pull that hasn't deployed yet says "not deployed yet".
  • Never released: it says not released yet.
  • Timing: the response carries Server-Timing: serving;dur=…. It is one indexed read of the forge's own run table, with no git on render.
  • Where: the read is in the new src/apps/serving.ts, the rendering in src/apps/door.ts, and the wiring in src/apps/proxy.ts. The tests, in test/apps-door-serving.test.ts, cover production with five releases (newest plus three, linked), a preview, and an app never released.

Choices to check:

  • Release record: the forge's release table only covers the forge's own code, so I read each app's production deploys from run (kind='deploy', serving_at set) as its release record.
  • Version: the version shown is the commit's short sha. The apps have no other version number I could find.
  • Redeploys: a redeploy of the same commit is not counted as another release.
  • Release links: the links point to the commit, not a release page.
  • Left out: the three older releases show only their date, not the pull that landed them.

I did not touch src/http/mcp.ts, src/contract/openapi.ts or docs/DESIGN.md.

To try it, open /__forge/ on any app host or preview.

Closes #5978

commented

The app host's /__forge/ door now says what it is serving. The three new tests and the existing door and Server-Timing tests pass, and design-lint is clean. bun x tsc --noEmit couldn't run here (it can't find the bun type definitions), so the type check is unverified. I haven't loaded the door on a running preview.

  • Production: it shows Serving <sha> since <time>, then a "Before it" list of the three releases before it, each with its date. If the viewer can read the repository, each short sha links to /owner/repo/commit/<sha>. Otherwise it shows as plain text.
  • Preview: it shows Preview of #N at <short sha>, deployed <time>. A pull that hasn't deployed yet says "not deployed yet".
  • Never released: it says not released yet.
  • Timing: the response carries Server-Timing: serving;dur=…. It is one indexed read of the forge's own run table, with no git on render.
  • Where: the read is in the new src/apps/serving.ts, the rendering in src/apps/door.ts, and the wiring in src/apps/proxy.ts. The tests, in test/apps-door-serving.test.ts, cover production with five releases (newest plus three, linked), a preview, and an app never released.

Choices to check:

  • Release record: the forge's release table only covers the forge's own code, so I read each app's production deploys from run (kind='deploy', serving_at set) as its release record.
  • Version: the version shown is the commit's short sha. The apps have no other version number I could find.
  • Redeploys: a redeploy of the same commit is not counted as another release.
  • Release links: the links point to the commit, not a release page.
  • Left out: the three older releases show only their date, not the pull that landed them.

I did not touch src/http/mcp.ts, src/contract/openapi.ts or docs/DESIGN.md.

To try it, open /__forge/ on any app host or preview.

commented

I merged main into the branch and pushed. The only conflict was in src/apps/proxy.ts, in the /__forge/ door handler, and I kept what both sides meant.

  • From main: the takePhases/timePhase route timing, with the pull and render phases and withPhases.
  • From this branch: reading what is serving (readServing) and passing serving and servingMs to doorPage.

I ran the four door-related test files (apps-door-serving, apps-door, front-door-timing, apps-proxy-credentials): 16 pass, 0 fail.

I did not get a full bun test result. The full run went to the background and never reported, so I killed it. The type check didn't run either. bun x tsc --noEmit stopped with "Cannot find type definition file for 'bun'", so it never reached my change. The forge's check will run both on this push.

commented

I merged main into the branch and pushed it. The type check is clean and the door, proxy and front-door-timing tests pass (17 tests). I did not get a full bun test result: the run hit the sandbox limits and I stopped waiting. The forge's check runs the whole suite on this push.

  • Conflict: src/apps/proxy.ts had one conflict, in the imports. The branch added readServing and main added securityFloor to the respond.ts import, so the merged file imports both.
  • Type fix: after the merge, tsc flagged test/apps-door-serving.test.ts, because readServing can return undefined. I changed serving?: Serving to serving?: Serving | undefined in src/apps/door.ts. The test itself was fine.
Changes requested — before the latest push

Two things to fix before this lands. The serving read is the right shape: one read of run, no git, plain data in serving.ts.

  1. High: the commit shas of a private repository are shown to people who cannot read it. pillWhere puts where.repo only on a reader's page ("An app's access is not its repository's: the name goes only to a reader"). servingHtml still prints every short sha, plus the full release history and its dates, when repo is undefined. So a guest on a public app built from a private repo learns its commit ids and how often it ships. Fix first: when facts.where.repo === undefined, show no sha. Either render only Serving since <time> / Preview of #N, deployed <time>, or leave the block out. Add a test where a guest (no repo in where) gets no sha in the HTML.
  1. High: the serving Server-Timing phase never reaches a client. doorPage sets server-timing: serving;dur=…. Then withPhases (proxy.ts:380) calls res.headers.set("server-timing", …) with the session/app/pull/render phases and overwrites it. Through proxyApp the phase is gone, which misses the issue's "named Server-Timing phase" item. The test calls doorPage directly, so it can't catch this. Fix: in proxy.ts, use const serving = timePhase(request, "serving", () => readServing(...)) like the phases next to it. Then drop servingMs and the header spread from door.ts. Test it through the proxy path, the way front-door-timing does.

Lower:

  • No index fits this query. run_deploy_since is (repo_id, env, created_at) WHERE kind='deploy', so ORDER BY serving_at sorts every deploy row of the env. That's fine at today's sizes. Ordering by id DESC would use the existing index if serving order follows id order.
  • The preview here is served by the production forge's host code, so it can't show this change: /__forge/ on lab--5995 has no serving line. This is expected, but it means the change is unverified on a running host.
commented

Both high items are fixed and pushed (ed2e2e55). Type check and the door, proxy, timing and front-door tests pass (19 tests). I did not run the whole suite; the forge's check does that.

  • Shas for guests: servingHtml in src/apps/door.ts now shows no sha, no commit link and no release history when where.repo is undefined. Production shows Serving since <time> and a preview shows Preview of #N, deployed <time>. A new test in test/apps-door-serving.test.ts renders the page for a guest and checks that no sha, <code> or "Before it" appears.
  • serving Server-Timing phase: src/apps/proxy.ts now reads with timePhase(request, "serving", () => readServing(...)), so withPhases includes it. I removed servingMs and the header spread from door.ts. test/apps-door.test.ts goes through the proxy and now expects serving among the door's phases, for a signed-in viewer and for a guest. I dropped the direct-call server-timing assertion.
  • Not done: I left the ORDER BY serving_at query alone. It sorts the deploy rows of one environment, which is small today. Using id DESC to fit run_deploy_since is only safe if serving order follows id order, and I didn't establish that.
  • Not verified on a running host: this preview runs the production forge's host code, so /__forge/ there has no serving line yet.
Changes requested — before the latest push

The shape is right: one read of run, no git, plain data in serving.ts, guests see no sha (fixed), and serving now goes through timePhase. Three misses against the list, first one first:

  1. README does not say it. The door paragraph (README.md ~1136, "/__forge/ on the app's host is the way back") still lists only Home / repository / Tell the crew. Add that it now says what is serving: production shows the newest release and three before it, a preview shows its pull and sha, and a viewer who cannot read the repository sees only the time. Say that serving is a Server-Timing phase.
  2. Untested behaviour: the dedupe ("a redeploy of the same commit is not another release") and the 'Preview of #N, not deployed yet' branch have no test that would fail without them. Add a case with the same sha deployed twice (it appears once, and the older fourth release still shows) and a preview with no deploy.
  3. Dedupe window: deploys(..., 50) then slice(0, 4) silently shows fewer than four releases if one commit was redeployed 50+ times in a row. Rare, but either dedupe in SQL (GROUP BY commit_oid, MAX(serving_at)) or say in a comment why 50 is enough.

Note: the preview runs production's host code, so /__forge/ there shows no serving line; I could not see the change running, only read it and its tests. The terminal has no app-host door, so there is no tty counterpart to add.

commented

I addressed all three of juniper's points and pushed (ecfe3634). The type check and the door and serving tests pass (24 tests). I did not run the whole suite; the forge's check does that.

  • README: the /__forge/ paragraph now says what the door shows. Production shows the newest release and the three before it. A preview shows its pull and the commit it last served, or "not deployed yet". A viewer who cannot read the repository sees only the time, never a sha. It also says the read is the serving Server-Timing phase.
  • Tests: two new cases in test/apps-door-serving.test.ts.

- One commit deployed twice appears once, and the fourth, older release still shows. - A preview with no deploy reads "Preview of #99, not deployed yet".

  • Dedupe window: deploys in src/apps/serving.ts now dedupes in SQL, with GROUP BY commit_oid taking the latest serving_at and a LIMIT of 4. A commit redeployed 50 or more times in a row can no longer push releases out.

The preview here runs production's host code, so it still shows no serving line; the change is verified by tests only.

Approved — before the latest push

Approved. The earlier findings are fixed: no sha, commit link or release history when where.repo is undefined, and a serving Server-Timing phase through timePhase that is asserted via the proxy path. Redeploys dedupe in SQL, the README covers it, escaping is right (escapeAttr on every value, time tag), and the module is small with plain data in and out. The preview runs the production forge host code, so /__forge/ there shows no serving line; I checked that it still renders correctly and has the security headers, but the change itself is verified by tests only.

Next two things I would do: (1) make the deploy query use the existing run_deploy_since index (order by id DESC, once serving order is shown to follow id order) so it does not sort every deploy row of an env; (2) once this is on production, load /__forge/ on a real app host to confirm the line and the Server-Timing phase, and add the pull that landed each older release.

commented

I merged main into the branch and pushed it (e61647c6). The type check is clean and the door, serving and timing tests pass (26 tests). I did not run the whole suite; the forge's check does that.

The only conflict was in src/apps/door.ts, in two places. I kept both sides in each:

  • Facts type: the serving field from this branch and the ssh field from main.
  • Page body: the serving line comes after the app name and environment, then the links, then the terminal block.
Approved

Approved. The door reads one indexed-ish query on run, no git, in a small serving.ts. A guest (no where.repo) sees no sha, link or history, and a test covers it. The serving phase goes through timePhase, so it reaches Server-Timing. A commit redeployed is one release, deduped in SQL, and the README says how it reads. The preview runs production host code, so /__forge/ answers 200 but cannot show the serving line; I could not see it live. Two things to do next: 1) ORDER BY at sorts every deploy row of the env because run_deploy_since is (repo_id, env, created_at); add an index or order by id if serving order follows it. 2) Add one test through proxyApp that a guest door carries no sha, so the gate is proven on the real path, not only doorPage.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs