# Files changed · PR \#21632 · pingdotgg/t3code

[View on GitCafe](https://git.cafe/pingdotgg/t3code/pull/21632/diffs)

Repository: [pingdotgg/t3code](https://git.cafe/pingdotgg/t3code)

Visibility: public

[fix\(server\): load PR uploads from SSO organizations](https://git.cafe/pingdotgg/t3code/pull/21632.md)

Version: 1

Head commit: 5ebb9aa198f9d91824f6cc723c649421520ede6b

Base commit: 2503bd508df4fa5f827f6c23cef252fc65596134

Comparison base: 2503bd508df4fa5f827f6c23cef252fc65596134

Showing up to 20 entries from this revision. If the pull request changes, reopen the first page before continuing.

## apps/server/src/assets/AssetAccess.test.ts

Status: modified; +253; −15

```diff
@@ -36,8 +36,10 @@
 import * as NativeAppIconResolver from "./NativeAppIconResolver.ts";
 import { openMediaFile } from "./MediaFile.ts";
 import { symlinksSupported } from "@t3tools/shared/testing/symlinks";
+import * as GitHubRest from "@t3tools/source-control-github/server/GitHubRest";
+import * as RepositoryIdentityResolver from "../project/RepositoryIdentityResolver.ts";
 import * as GitHubCredentials from "@t3tools/source-control-github/server/GitHubCredentials";
+import * as GitHubMediaFetch from "./GitHubMediaFetch.ts";
-import { githubMediaResponse } from "./GitHubMediaFetch.ts";
 import * as HostProcess from "@t3tools/shared/HostProcess";
 
 vi.mock("node:fs/promises", async (importOriginal) => {
@@ -123,6 +125,123 @@
   ServerSecretStore.layer.pipe(Layer.provide(layerConfig)),
 ).pipe(Layer.provideMerge(NodeServices.layer));
 
+const makeSsoMediaFixture = (
+  asset: { url: string; cwd: string; repository?: string; expiresAt: number },
+  checkoutRepository = "personal/unrelated",
+) => {
+  const bytes = new Uint8Array([0, 255, 2, 128, 4, 127, 6, 7, 8, 9]);
+  const state = { account: "account-a", expired: new Set<string>() };
+  const rendered: Array<GitHubRest.GitHubRestInput> = [];
+  const signedUrls: Array<string> = [];
+  const requests: Array<{
+    url: string;
+    authorization: string | undefined;
+    range: string | undefined;
+  }> = [];
+  const resolve = vi.fn(() =>
+    Effect.succeed({
+      canonicalKey: `github.com/${checkoutRepository}`,
+      locator: { source: "git-remote" as const, remoteName: "origin", remoteUrl: "" },
+    }),
+  );
+  const credential = (host: string) =>
+    Effect.sync(() => ({
+      host,
+      token: Redacted.make(state.account),
+      fingerprint: state.account,
+      source: "gh" as const,
+    }));
+  const externals = Layer.mergeAll(
+    Layer.mock(GitHubCredentials.GitHubCredentials)({ get: credential }),
+    Layer.mock(RepositoryIdentityResolver.RepositoryIdentityResolver)({ resolve }),
+    Layer.mock(GitHubRest.GitHubRest)({
+      credential,
+      rest: (input) =>
+        Effect.sync(() => {
+          rendered.push(input);
+          expect(input).toMatchObject({
+            host: "github.com",
+            method: "POST",
+            path: "markdown",
+            accept: "text/html",
+          });
+          const signed = `https://private-user-images.githubusercontent.com/1/upload.mp4?jwt=${state.account}-${rendered.length}&x=1`;
+          const correctContext =
+            typeof input.body === "object" &&
+            input.body !== null &&
+            "context" in input.body &&
+            input.body.context === "enterprise/private";
+          if (correctContext) signedUrls.push(signed);
+          return {
+            status: 200,
+            headers: {},
+            body: `<p><img src="${correctContext ? signed.replaceAll("&", "&amp;") : asset.url}" alt=""></p>`,
+            truncated: false,
+            invalidUtf8: false,
+          };
+        }),
+    }),
+    Layer.succeed(
+      HttpClient.HttpClient,
+      HttpClient.make((request) =>
+        Effect.sync(() => {
+          requests.push({
+            url: request.url,
+            authorization: request.headers.authorization,
+            range: request.headers.range,
+          });
+          if (request.url === asset.url) {
+            return HttpClientResponse.fromWeb(
+              request,
+              new Response("<html>Sign in with SSO</html>", {
+                headers: { "content-type": "text/html; charset=utf-8" },
+              }),
+            );
+          }
+          expect(signedUrls).toContain(request.url);
+          expect(request.headers.authorization).toBeUndefined();
+          if (state.expired.has(request.url)) {
+            return HttpClientResponse.fromWeb(request, new Response(null, { status: 403 }));
+          }
+          const range = request.headers.range;
+          if (range) {
+            expect(range).toBe("bytes=2-5");
+            expect(request.headers["if-range"]).toBe('"upload"');
+          }
+          return HttpClientResponse.fromWeb(
+            request,
+            new Response(range ? bytes.slice(2, 6) : bytes, {
+              status: range ? 206 : 200,
+              headers: {
+                "content-type": "video/mp4",
+                "content-length": range ? "4" : "10",
+                "accept-ranges": "bytes",
+                etag: '"upload"',
+                ...(range ? { "content-range": "bytes 2-5/10" } : {}),
+              },
+            }),
+          );
+        }),
+      ),
+    ),
+  );
+  const layer = GitHubMediaFetch.layer.pipe(Layer.provide(externals));
+  const read = Effect.fnUntraced(function* (headers: Record<string, string> = {}) {
+    const response = HttpServerResponse.toWeb(
+      yield* GitHubMediaFetch.githubMediaResponse(asset, headers),
+    );
+    expect(response.status).toBe(headers.range ? 206 : 200);
+    expect(response.headers.get("content-type")).toBe("video/mp4");
+    expect(response.headers.get("content-length")).toBe(headers.range ? "4" : "10");
+    expect(response.headers.get("accept-ranges")).toBe("bytes");
+    expect(response.headers.get("etag")).toBe('"upload"');
+    expect(response.headers.get("content-range")).toBe(headers.range ? "bytes 2-5/10" : null);
+    const body = yield* Effect.promise(() => response.arrayBuffer());
+    expect(new Uint8Array(body)).toEqual(headers.range ? bytes.slice(2, 6) : bytes);
+  });
+  return { layer, read, state, rendered, signedUrls, requests, resolve };
+};
+
 describe("AssetAccess", () => {
   describe("readMcpAppHtml", () => {
     it.effect("accepts snapshots at the stored HTML byte limit", () =>
@@ -191,24 +310,32 @@
         cwd: "/repo",
         expiresAt: Number.MAX_SAFE_INTEGER,
       };
-      expect((yield* githubMediaResponse(asset, {})).status).toBe(404);
-      expect((yield* githubMediaResponse(asset, {})).status).toBe(200);
-      expect((yield* githubMediaResponse(asset, {})).status).toBe(200);
+      expect((yield* GitHubMediaFetch.githubMediaResponse(asset, {})).status).toBe(404);
+      expect((yield* GitHubMediaFetch.githubMediaResponse(asset, {})).status).toBe(200);
+      expect((yield* GitHubMediaFetch.githubMediaResponse(asset, {})).status).toBe(200);
       expect(lookups).toBe(3);
       expect(authorizations).toEqual([undefined, "Bearer signed-in", "Bearer signed-in"]);
     }).pipe(
       Effect.provide(
-        Layer.mock(GitHubCredentials.GitHubCredentials)({
-          get: (host) =>
-            ++lookups === 1
-              ? Effect.fail(new GitHubCredentials.GitHubNotSignedInError({ host }))
-              : Effect.succeed({
-                  host,
-                  token: Redacted.make("signed-in"),
+        GitHubMediaFetch.layer.pipe(
+          Layer.provide(
+            Layer.mergeAll(
+              Layer.mock(GitHubRest.GitHubRest)({}),
+              Layer.mock(RepositoryIdentityResolver.RepositoryIdentityResolver)({}),
+              Layer.mock(GitHubCredentials.GitHubCredentials)({
+                get: (host) =>
+                  ++lookups === 1
+                    ? Effect.fail(new GitHubCredentials.GitHubNotSignedInError({ host }))
-                  source: "gh" as const,
-                  fingerprint: "fingerprint",
-                }),
-        }),
+                    : Effect.succeed({
+                        host,
+                        token: Redacted.make("signed-in"),
+                        source: "gh" as const,
+                        fingerprint: "fingerprint",
+                      }),
+              }),
+            ),
+          ),
+        ),
       ),
       Effect.provideService(
         HttpClient.HttpClient,
@@ -229,6 +356,117 @@
     );
   });
 
+  it.effect(
+    "loads SSO uploads in the PR repository and reuses the signed URL for a range request",
+    () => {
+      const asset = {
+        url: "https://github.com/user-attachments/assets/explicit-pr-context",
+        cwd: "/personal-checkout",
+        repository: "enterprise/private",
+        expiresAt: Number.MAX_SAFE_INTEGER,
+      };
+      const fixture = makeSsoMediaFixture(asset);
+      return Effect.gen(function* () {
+        yield* fixture.read();
+        yield* fixture.read({ range: "bytes=2-5", "if-range": '"upload"' });
+        expect(fixture.resolve).not.toHaveBeenCalled();
+        expect(fixture.rendered).toHaveLength(1);
+        expect(fixture.rendered[0]?.body).toEqual({
+          text: `![](${asset.url})`,
+          mode: "gfm",
+          context: asset.repository,
+        });
+        expect(fixture.requests).toEqual([
+          { url: asset.url, authorization: "Bearer account-a", range: undefined },
+          { url: fixture.signedUrls[0], authorization: undefined, range: undefined },
+          { url: fixture.signedUrls[0], authorization: undefined, range: "bytes=2-5" },
+        ]);
+      }).pipe(Effect.provide(fixture.layer), Effect.scoped);
+    },
+  );
+
+  it.effect.each([
+    {
+      name: "checkout identity",
+      url: "https://github.com/user-attachments/assets/old-client",
+      checkout: "enterprise/private",
+      resolves: true,
+    },
+    {
+      name: "legacy upload repository",
+      url: "https://github.com/enterprise/private/assets/123/old-client",
+      repository: "personal/unrelated",
+      checkout: "personal/unrelated",
+      resolves: false,
+    },
+  ])("loads uploads using $name", ({ url, repository, checkout, resolves }) => {
+    const fixture = makeSsoMediaFixture(
+      {
+        url,
+        cwd: "/old-client",
+        ...(repository ? { repository } : {}),
+        expiresAt: Number.MAX_SAFE_INTEGER,
+      },
+      checkout,
+    );
+    return Effect.gen(function* () {
+      yield* fixture.read();
+      expect(fixture.rendered).toHaveLength(1);
+      expect(fixture.rendered[0]?.body).toEqual({
+        text: `![](${url})`,
+        mode: "gfm",
+        context: "enterprise/private",
+      });
+      if (resolves) expect(fixture.resolve).toHaveBeenCalledWith("/old-client");
+      else expect(fixture.resolve).not.toHaveBeenCalled();
+    }).pipe(Effect.provide(fixture.layer), Effect.scoped);
+  });
+
+  it.effect(
+    "refreshes signed media across account changes, early expiry, TTL and service lifetimes",
+    () => {
+      const fixture = makeSsoMediaFixture({
+        url: "https://github.com/user-attachments/assets/cache-lifecycle",
+        cwd: "/personal-checkout",
+        repository: "enterprise/private",
+        expiresAt: Number.MAX_SAFE_INTEGER,
+      });
+      const session = Effect.gen(function* () {
+        yield* fixture.read();
+        const firstAccountUrl = fixture.signedUrls[0];
+        fixture.state.account = "account-b";
+        const beforeSwitch = fixture.requests.length;
+        yield* fixture.read();
+        expect(fixture.rendered).toHaveLength(2);
+        expect(fixture.requests.slice(beforeSwitch).map(({ url }) => url)).not.toContain(
+          firstAccountUrl,
+        );
+        const stale = fixture.signedUrls.at(-1)!;
+        fixture.state.expired.add(stale);
+        const beforeExpiry = fixture.requests.length;
+        yield* fixture.read({ range: "bytes=2-5", "if-range": '"upload"' });
+        expect(fixture.requests[beforeExpiry]?.url).toBe(stale);
+        expect(fixture.rendered).toHaveLength(3);
+        yield* TestClock.adjust("6 minutes");
+        const beforeTtl = fixture.requests.length;
+        const beforeTtlUrl = fixture.signedUrls.at(-1);
+        yield* fixture.read();
+        expect(fixture.rendered).toHaveLength(4);
+        expect(fixture.requests.slice(beforeTtl).map(({ url }) => url)).not.toContain(beforeTtlUrl);
+      }).pipe(Effect.provide(fixture.layer), Effect.scoped);
+      return Effect.gen(function* () {
+        yield* session;
+        const previousUrl = fixture.signedUrls.at(-1);
+        const beforeRestart = fixture.requests.length;
+        yield* fixture.read().pipe(Effect.provide(fixture.layer), Effect.scoped);
+        expect(fixture.rendered).toHaveLength(5);
+        expect(fixture.requests.slice(beforeRestart).map(({ url }) => url)).not.toContain(
+          previousUrl,
+        );
+      });
+    },
+  );
+
   it.effect("serves an image a tool returned inline from the stored item", () =>
     Effect.gen(function* () {
       const resource = {
```

## apps/server/src/assets/GitHubMediaFetch.ts

Status: modified; +208; −49

```diff
@@ -1,19 +1,24 @@
 import * as Mime from "effect/http/Mime";
 import { githubMediaFileName } from "@t3tools/shared/githubMedia";
 import * as Clock from "effect/Clock";
+import * as Context from "effect/Context";
+import * as Layer from "effect/Layer";
+import * as Schema from "effect/Schema";
+import type * as Scope from "effect/Scope";
 import * as Effect from "effect/Effect";
 import * as Option from "effect/Option";
 import * as Redacted from "effect/Redacted";
+import * as FetchHttpClient from "effect/http/FetchHttpClient";
+import * as HttpClient from "effect/http/HttpClient";
+import * as HttpClientRequest from "effect/http/HttpClientRequest";
+import * as HttpServerResponse from "effect/http/HttpServerResponse";
+import type * as HttpClientResponse from "effect/http/HttpClientResponse";
-import {
-  FetchHttpClient,
-  HttpClient,
-  HttpClientRequest,
-  HttpServerResponse,
-  type HttpClientResponse,
-} from "effect/http";
 
 import * as GitHubCredentials from "@t3tools/source-control-github/server/GitHubCredentials";
+import * as GitHubRest from "@t3tools/source-control-github/server/GitHubRest";
 
+import * as RepositoryIdentityResolver from "../project/RepositoryIdentityResolver.ts";
+
 /**
  * Everything a redirect leads to — the presigned object stores GitHub hands assets off to —
  * authorizes with its own signature, and some of them reject a request that also carries a
@@ -27,7 +32,12 @@
 ]);
 const isCredentialedHost = (url: string) => {
   try {
-    return CREDENTIALED_HOSTS.has(new URL(url).hostname.toLowerCase());
+    const target = new URL(url);
+    return (
+      target.protocol === "https:" &&
+      target.port === "" &&
+      CREDENTIALED_HOSTS.has(target.hostname.toLowerCase())
+    );
   } catch {
     return false;
   }
@@ -50,55 +60,204 @@
 // body cannot run script against this origin.
 const SVG_CONTENT_SECURITY_POLICY = "default-src 'none'; style-src 'unsafe-inline'; sandbox";
 
-const githubToken = GitHubCredentials.GitHubCredentials.pipe(
-  Effect.flatMap((credentials) => credentials.get("github.com")),
+type GitHubMediaLocation = {
+  readonly url: string;
+  readonly cwd: string;
+  readonly repository?: string;
+};
-  Effect.map((credential) => credential.token),
-  Effect.orElseSucceed(() => null),
-);
 
-const fetchFollowingRedirects = Effect.fn("GitHubMediaFetch.fetchFollowingRedirects")(function* (
-  url: string,
-  headers: Record<string, string>,
-  token: Redacted.Redacted | null,
+class GitHubMediaFetchError extends Schema.TaggedError<GitHubMediaFetchError>()(
+  "GitHubMediaFetchError",
+  { cause: Schema.Defect() },
 ) {
+  override get message(): string {
+    return "Failed to fetch GitHub media.";
+  }
+}
+
+export class GitHubMediaFetch extends Context.Service<
+  GitHubMediaFetch,
+  {
+    readonly fetch: (
+      asset: GitHubMediaLocation,
+      requestHeaders: Readonly<Record<string, string | undefined>>,
+    ) => Effect.Effect<
+      HttpClientResponse.HttpClientResponse | null,
+      GitHubMediaFetchError,
+      Scope.Scope
+    >;
+  }
+>()("t3/assets/GitHubMediaFetch") {}
+
+const SIGNED_URL_CACHE_TTL_MS = 4 * 60_000;
+const SIGNED_URL_CACHE_MAX_ENTRIES = 64;
+
+const make = Effect.gen(function* () {
+  const credentials = yield* GitHubCredentials.GitHubCredentials;
+  const rest = yield* GitHubRest.GitHubRest;
+  const identities = yield* RepositoryIdentityResolver.RepositoryIdentityResolver;
   const httpClient = HttpClient.withScope(yield* HttpClient.HttpClient);
+  const signedUrls = new Map<string, { readonly at: number; readonly url: string }>();
+
+  const fetchFollowingRedirects = Effect.fnUntraced(function* (
+    url: string,
+    headers: Record<string, string>,
+    token: Redacted.Redacted<string> | null,
+  ) {
+    let target = url;
+    for (let hop = 0; ; hop += 1) {
+      const parsed = URL.parse(target);
+      if (parsed === null || parsed.protocol !== "https:" || parsed.username || parsed.password) {
+        return null;
+      }
+      const authorization =
+        token !== null && isCredentialedHost(target) ? `Bearer ${Redacted.value(token)}` : null;
+      const response: HttpClientResponse.HttpClientResponse = yield* httpClient
+        .execute(
+          HttpClientRequest.get(target).pipe(
+            HttpClientRequest.setHeaders({
+              ...headers,
+              // The bytes are streamed straight through, so never let an encoding layer in.
+              "accept-encoding": "identity",
+              ...(authorization === null ? {} : { authorization }),
+            }),
+          ),
+        )
+        .pipe(
+          Effect.provideService(FetchHttpClient.RequestInit, MANUAL_REDIRECT),
+          // Signed query strings must never be added to HTTP tracing spans.
+          Effect.provideService(HttpClient.TracerDisabledWhen, () => true),
+          Effect.mapError((cause) => new GitHubMediaFetchError({ cause })),
+        );
+      const location = response.headers.location;
+      if (response.status < 300 || response.status >= 400) return response;
+      if (!location || hop >= MAX_REDIRECTS) return null;
+      const next = URL.parse(location, target);
+      if (next === null || next.protocol !== "https:") return null;
+      target = next.toString();
+    }
+  });
+
+  const renderSignedUrl = Effect.fnUntraced(function* (url: string, repository: string) {
+    const rendered = yield* rest
+      .rest({
+        host: "github.com",
+        operation: "renderMarkdown",
+        method: "POST",
+        path: "markdown",
+        accept: "text/html",
+        body: { text: `![](${url})`, mode: "gfm", context: repository },
+      })
+      .pipe(Effect.orElseSucceed(() => null));
+    if (
+      rendered === null ||
+      rendered.status !== 200 ||
+      rendered.truncated ||
+      rendered.invalidUtf8
+    ) {
+      return null;
+    }
-  let target = url;
-  for (let hop = 0; ; hop += 1) {
-    const authorization =
-      token !== null && isCredentialedHost(target) ? `Bearer ${Redacted.value(token)}` : null;
-    const response: HttpClientResponse.HttpClientResponse = yield* httpClient
-      .execute(
-        HttpClientRequest.get(target).pipe(
-          HttpClientRequest.setHeaders({
-            ...headers,
-            // The bytes are streamed straight through, so never let an encoding layer in.
-            "accept-encoding": "identity",
-            ...(authorization === null ? {} : { authorization }),
-          }),
-        ),
-      )
-      .pipe(Effect.provideService(FetchHttpClient.RequestInit, MANUAL_REDIRECT));
-    const location = response.headers.location;
-    if (response.status < 300 || response.status >= 400) return response;
-    if (!location || hop >= MAX_REDIRECTS) return null;
-    const next = new URL(location, target);
-    if (next.protocol !== "https:") return null;
-    target = next.toString();
-  }
+    for (const match of rendered.body.matchAll(/<img\b[^>]*\ssrc\s*=\s*(["'])(.*?)\1/giu)) {
+      const signed = URL.parse(match[2]?.replaceAll("&amp;", "&") ?? "");
+      if (
+        signed?.protocol === "https:" &&
+        signed.hostname === "private-user-images.githubusercontent.com" &&
+        signed.port === "" &&
+        signed.username === "" &&
+        signed.password === "" &&
+        signed.searchParams.get("jwt")
+      ) {
+        return signed.toString();
+      }
+    }
+    return null;
+  });
+
+  const fetch: GitHubMediaFetch["Service"]["fetch"] = Effect.fn("GitHubMediaFetch.fetch")(
+    function* (asset, requestHeaders) {
+      // Read on every request so a login or account change takes effect immediately.
+      const credential = yield* credentials
+        .get("github.com")
+        .pipe(Effect.orElseSucceed(() => null));
+      const forwarded: Record<string, string> = {};
+      for (const name of FORWARDED_REQUEST_HEADERS) {
+        const value = requestHeaders[name];
+        if (value !== undefined) forwarded[name] = value;
+      }
+      const target = URL.parse(asset.url);
+      const attachment =
+        target?.protocol === "https:" &&
+        target.hostname === "github.com" &&
+        target.port === "" &&
+        target.username === "" &&
+        target.password === "" &&
+        /^\/(?:user-attachments\/assets\/[\w-]+|[^/]+\/[^/]+\/assets\/\d+\/[\w-]+)$/u.test(
+          target.pathname,
+        );
+      let repository: string | undefined;
+      if (attachment && credential !== null) {
+        repository = /^\/([^/]+\/[^/]+)\/assets\//u.exec(target.pathname)?.[1] ?? asset.repository;
+        if (repository === undefined) {
+          const identity = yield* identities.resolve(asset.cwd);
+          repository = /^github\.com\/([^/]+\/[^/]+)$/u.exec(identity?.canonicalKey ?? "")?.[1];
+        }
+      }
+      // An unusable explicit context must not fall back to an unrelated checkout.
+      const cacheKey =
+        credential !== null && repository !== undefined && /^[\w.-]+\/[\w.-]+$/u.test(repository)
+          ? [credential.fingerprint, repository, asset.url].map(encodeURIComponent).join("|")
+          : null;
+      const now = yield* Clock.currentTimeMillis;
+      const cached = cacheKey === null ? undefined : signedUrls.get(cacheKey);
+      let response: HttpClientResponse.HttpClientResponse | null;
+      if (cacheKey !== null && cached !== undefined && now - cached.at < SIGNED_URL_CACHE_TTL_MS) {
+        response = yield* fetchFollowingRedirects(cached.url, forwarded, null);
+        if (response === null || response.status >= 400) signedUrls.delete(cacheKey);
+        if (response?.status !== 403) return response;
+      } else {
+        if (cacheKey !== null) signedUrls.delete(cacheKey);
+        response = yield* fetchFollowingRedirects(asset.url, forwarded, credential?.token ?? null);
+        if (
+          response?.headers["content-type"]?.split(";", 1)[0]?.trim().toLowerCase() !== "text/html"
+        ) {
+          return response;
+        }
+      }
+      if (cacheKey === null || repository === undefined || credential === null) return response;
+      const signed = yield* renderSignedUrl(asset.url, repository);
+      if (signed === null) return response;
+      const media = yield* fetchFollowingRedirects(signed, forwarded, null);
+      if (
+        (media?.status === 200 || media?.status === 206) &&
+        MEDIA_CONTENT_TYPE_PATTERN.test(
+          media.headers["content-type"]?.split(";", 1)[0]?.trim() ?? "",
+        )
+      ) {
+        // The REST policy reads credentials too; do not retain a fill across an account switch.
+        const current = yield* credentials.get("github.com").pipe(Effect.orElseSucceed(() => null));
+        if (current?.fingerprint === credential.fingerprint) {
+          if (signedUrls.size >= SIGNED_URL_CACHE_MAX_ENTRIES) {
+            const oldest = signedUrls.keys().next().value;
+            if (oldest !== undefined) signedUrls.delete(oldest);
+          }
+          signedUrls.set(cacheKey, { at: now, url: signed });
+        }
+      }
+      return media;
+    },
+  );
+
+  return GitHubMediaFetch.of({ fetch });
 });
+
+export const layer = Layer.effect(GitHubMediaFetch, make);
 
 export const githubMediaResponse = Effect.fn("GitHubMediaFetch.githubMediaResponse")(function* (
-  asset: { readonly url: string; readonly cwd: string; readonly expiresAt: number },
+  asset: GitHubMediaLocation & { readonly expiresAt: number },
   requestHeaders: Record<string, string | undefined>,
 ) {
-  // Both media hosts are served by github.com's account, which is the host `gh` stores it under.
-  const token = yield* githubToken;
-  const forwarded: Record<string, string> = {};
-  for (const name of FORWARDED_REQUEST_HEADERS) {
-    const value = requestHeaders[name];
-    if (value !== undefined) forwarded[name] = value;
-  }
-  const response = yield* fetchFollowingRedirects(asset.url, forwarded, token);
+  const service = yield* GitHubMediaFetch;
+  const response = yield* service.fetch(asset, requestHeaders);
   const remainingSeconds = Math.floor((asset.expiresAt - (yield* Clock.currentTimeMillis)) / 1000);
   const headers: Record<string, string> = {
     "cache-control":
```

## apps/server/src/http.ts

Status: modified; +6; −3

```diff
@@ -30,7 +30,7 @@
 
 import * as ServerConfig from "./config.ts";
 import { ASSET_ROUTE_PREFIX, readMcpAppHtml, resolveAsset } from "./assets/AssetAccess.ts";
-import { githubMediaResponse } from "./assets/GitHubMediaFetch.ts";
+import * as GitHubMediaFetch from "./assets/GitHubMediaFetch.ts";
 import { statMediaFile, streamMediaFile, type OpenMediaFile } from "./assets/MediaFile.ts";
 import {
   ATTACHMENT_UPLOAD_ROUTE_PREFIX,
@@ -431,9 +431,12 @@
       });
     }
     if (asset.kind === "github-media") {
+      return yield* GitHubMediaFetch.githubMediaResponse(asset, request.headers).pipe(
-      return yield* githubMediaResponse(asset, request.headers).pipe(
         Effect.tapError((cause) =>
+          Effect.logWarning("Failed to fetch GitHub media.", {
-          Effect.logWarning("Failed to fetch GitHub media.", { url: asset.url, cause }),
+            upstream_host: new URL(asset.url).hostname,
+            domain_error_tag: cause._tag,
+          }),
         ),
         Effect.orElseSucceed(() =>
           HttpServerResponse.empty({
```

## apps/server/src/server.ts

Status: modified; +2; −0

```diff
@@ -95,6 +95,7 @@
 import * as ProjectFaviconResolver from "./project/ProjectFaviconResolver.ts";
 import * as T3ProjectFileLoader from "./project/T3ProjectFileLoader.ts";
 import * as RepositoryIdentityResolver from "./project/RepositoryIdentityResolver.ts";
+import * as GitHubMediaFetch from "./assets/GitHubMediaFetch.ts";
 import * as WorkspaceEntries from "./workspace/WorkspaceEntries.ts";
 import * as KnownWorkspaces from "./workspace/KnownWorkspaces.ts";
 import * as WorkspaceFileSystem from "./workspace/WorkspaceFileSystem.ts";
@@ -704,6 +705,7 @@
   // Both transports consume the same service instance, so caches single-flight across clients
   // and mutations observed on WebSocket invalidate patches subsequently read over HTTP.
   Layer.provide(layerPullRequestService),
+  HttpRouter.provideRequest(GitHubMediaFetch.layer),
   Layer.provide(ServerBrowser.layer.pipe(Layer.provide(DesktopBrowserChannel.layer))),
   Layer.provide(PreviewBrowser.layer),
   Layer.provide(PreviewAutomationBroker.layer),
```
