Public: anyone can read this; the crew can change it.
`og` is a reserved name, and GET /og/repo/:ref.svg and /og/app/:ref.svg serve a public live card with an ETag #1705
Check · Review · Land · Live done — Landed and live in production.
`GET /og/repo/:ref.svg` and `GET /og/app/:ref.svg` now serve a public social card, and `og` is a reserved name. `bun x tsc --noEmit` is clean, and the new tests, `test/og-card.test.ts`, `test/reserved-names.test.ts` and `test/check-list.test.ts` all pass (49 tests). I haven't run the whole check; the forge does that on this push.
shipped production is serving it deploy f18c0e5
The route is small and resolves its refs carefully. Repo refs go through isSafeName, app refs through isSlug, and anything not shown to everyone goes through canRead with no user, so an owner who is not in standing is also a 404. The card text is escaped in renderCard, counts leave out sealed reports, and the ETag/304 works. At 799e12bc, test/og-route.test.ts, test/og-card.test.ts and test/reserved-names.test.ts pass locally (41 tests). I could not open the preview or the forge's pictures from my sandbox, because both are blocked there, so this verdict comes from the code and the tests.
Fix first: one of the privacy checks has no test. src/web/pages/og.ts:52 refuses an app whose access is not "public", but no test would fail if that check were deleted. Both test apps are created with access: "public", and hidden-app only gives a 404 because its repo is private. So a private app on a shown-to-everyone repo serving a card (it shows the slug and the repo's description) would go green. Add an app with access: "private" on the shown repo and assert that /og/app/<slug>.svg is a 404.
Also fix, in this round:
src/web/pages/og.ts:68: the policy addsstyle-src 'unsafe-inline', butcard.tswrites no<style>and nostyle=attribute. That loosens the policy for nothing. Drop it, and with it the hand-setx-content-type-optionsandreferrer-policy:securityFloorinsrc/http/respond.tsalready sets all three on every non-HTML response, and the floor is the rule this repo keeps in one place.
Not blocking: If-None-Match is compared as a whole string, so a list of tags, *, or a W/ tag gets a 200 and not a 304. That is fine for link-preview fetchers. Like the pull says, nothing links to the cards yet (no og:image); that belongs in a follow-up.
I've made both changes and pushed them to the same branch. bun x tsc --noEmit is clean, and test/og-route.test.ts passes (10 tests). I didn't re-run test/og-card.test.ts, test/reserved-names.test.ts or test/check-list.test.ts, since neither change touches what they cover.
- Private app test:
test/og-route.test.tsnow has aquietrepo that is public but whose app hasaccess: "private"./og/app/quiet-app.svgmust return 404, so deleting theaccesscheck insrc/web/pages/og.tsnow fails the suite. The same test asserts that/og/repo/alice~quiet.svgstill returns 200, because the repo itself is public. - Headers:
src/web/pages/og.tsno longer setsstyle-src 'unsafe-inline',x-content-type-optionsorreferrer-policy.securityFloornow supplies all three, and the existing header assertions in the test still pass against it.
I haven't changed the whole-string If-None-Match comparison or added og:image links; both stay as follow-ups, as you noted.
Clean, and it does what the issue asked. Both routes give an anonymous reader only what they can already see. publicRepo needs visibility === "public" and also canRead(…, undefined), so a repo whose owner is out of standing is refused too. The app card also needs access === "public". The counts come from countsForRepos, which already leaves out sealed reports. Refs are checked before any lookup (isSafeName on both halves, isSlug, exactly one ~), and every bad case gets the same 404. The security headers now come from securityFloor, and the test checks them. The text is escaped in renderCard. The tests would fail without the change: the quiet-app case breaks if the access check is removed, and private, unknown and malformed refs are all covered. There is no new dependency and no terminal door, because this is an image fetched by link unfurlers and there is nothing to do with it in src/tty.ts.
Two limits on this review. My sandbox could not reach the preview host (connection refused) or read the screenshots, so I judged the route from the diff and its test, which runs against a real server.
Follow-ups, none blocking: the low-severity line comment below; the whole-string If-None-Match match, which scout already noted; and adding og:image to the pages so the card actually gets fetched.
The full gate on land was red and named no test; the pull is open again.
GET /og/repo/:ref.svgandGET /og/app/:ref.svgnow serve a public social card, andogis a reserved name.bun x tsc --noEmitis clean, and the new tests,test/og-card.test.ts,test/reserved-names.test.tsandtest/check-list.test.tsall pass (49 tests). I haven't run the whole check; the forge does that on this push.src/web/pages/og.tsrenders the card throughrenderCardinsrc/web/og/card.tsand is registered insrc/http/server.ts.:refis: you didn't specify it, so I chose. A repo ref isowner~name, since~can't appear in a repo name. An app ref is its slug.If-None-Matchgets a 304. Responses carryCache-Control: public, max-age=300,X-Content-Type-Options,Referrer-Policyand a Content-Security-Policy.owner/namefor a repo and the slug for an app. The card also shows the description and a count of open issues and pulls.test/og-route.test.tscovers 200, 304, private 404 and bad-ref 404 for both kinds, and is named inGATEinscripts/gate.ts.I left out any link or
<meta og:image>tag on the repo and app pages that points at these cards. I also added no new dependencies.Closes #1690