Skip to content

[bug][v2] Client metadata document's logo_uri on executor.sh returns HTML, not an image #2541

Closed

Description

@AugusDogus

Note

🤖 Claude Opus 5.5 (reasoning effort: high) writing on behalf of Augie (@AugusDogus)

https://executor.sh/oauth/client-metadata.json says "logo_uri": "https://executor.sh/favicon.png", but that URL is v1's HTML app page, not an image. Authorization servers that show the client's logo on their consent page show a broken image for Executor.

$ curl -s -o /dev/null -w '%{http_code} %{content_type}\n' https://executor.sh/favicon.png
200 text/html; charset=utf-8
$ curl -s -o /dev/null -w '%{http_code} %{content_type}\n' https://v2.executor.sh/favicon.png
200 image/png

Cause

The logo worked until the document moved to executor.sh on 2026-11-09 (#2528 on v1, and the v2 export 141787b9a, which builds the document's URL from the edge origin). logo_uri is built on the document's origin (apps/hosted/server/src/implementation/oauth-client-metadata.ts:111), so it moved too. v1's edge forwards /oauth/client-metadata.json to v2 but not /favicon.png, and v1 has no file at that path, so its SPA fallback answers. Before the move the document was on v2.executor.sh, which serves the real PNG.

e2e/tests/cloud-client-metadata.spec.ts checks the logo_uri string but never fetches it. The self-host scenario does fetch it, and self-host is fine.

Fix

Forward /favicon.png from executor.sh to v2, the same way as the document. Pointing logo_uri at /favicon-192.png instead would break self-host, which only serves /favicon.png.

Patches, with the same two halves as #2528. Pull requests here are limited to collaborators, so they are linked and inlined instead of opened:

  • v2: AugusDogus@55bf3a1 (branch AugusDogus:fix/cimd-logo-on-edge). It adds clientMetadataLogoPath to the hosted server's contract, used by both the document and siteFiles, so the edge serves /favicon.png and cannot drift from the document again. It also regenerates the edge contract (one forwarded GET /favicon.png example) and makes cloud-client-metadata.spec.ts fetch each document's logo and expect a PNG. Typecheck, lint, format, edge-contract.ts --check and the e2e boundary check pass; I could not run the cloud scenario, which needs a deployed test stage.
  • v1 (main): forward the path and copy v2's new contract. vitest run src/edge/marketing.test.ts src/edge/production-config.test.ts passes (352 tests); without the marketing.ts change, the two contract cases for GET /favicon.png fail.
v1 patch
diff --git a/apps/cloud/src/edge/marketing.test.ts b/apps/cloud/src/edge/marketing.test.ts
index 1f8e31d70..003b362c7 100644
--- a/apps/cloud/src/edge/marketing.test.ts
+++ b/apps/cloud/src/edge/marketing.test.ts
@@ -648,6 +648,8 @@ describe("isV2MarketingPath", () => {
     "/authors/author.png",
     "/og-image.png",
     "/pattern-graph-paper.svg",
+    // The logo v2's OAuth client metadata document names; v1 has no such file.
+    "/favicon.png",
     "/docs",
     "/docs/",
     "/docs/quickstart",
diff --git a/apps/cloud/src/edge/marketing.ts b/apps/cloud/src/edge/marketing.ts
index 7ef91d2c1..36e044e43 100644
--- a/apps/cloud/src/edge/marketing.ts
+++ b/apps/cloud/src/edge/marketing.ts
@@ -105,7 +105,9 @@ export const isV2Path = (pathname: string): boolean =>
  *  `/apps/*` and `/experiments/*` are v2's; v2 publishes nothing at a bare
  *  `/experiments`, which stays with v1. `/demo` is a valid, unreserved v1
  *  organization slug, so it and everything below it stay with v1. The
- *  favicons stay with v1 too: v1's dashboard serves identical files. */
+ *  favicons stay with v1 too: v1's dashboard serves identical files. v1 has
+ *  no `/favicon.png`, which v2's OAuth client metadata document names as its
+ *  logo, so that one is v2's. */
 const V2_MARKETING_PATHS: ReadonlyArray<string> = [
   "/home",
   "/pricing",
@@ -126,6 +128,7 @@ const V2_MARKETING_PATHS: ReadonlyArray<string> = [
   "/authors/*",
   "/og-image.png",
   "/pattern-graph-paper.svg",
+  "/favicon.png",
 ];
 
 /** Whether a pathname belongs to v2's marketing site on `executor.sh`. `/`
diff --git a/apps/cloud/src/edge/v2-edge-contract.json b/apps/cloud/src/edge/v2-edge-contract.json
index 5af17e08a..6031aed94 100644
--- a/apps/cloud/src/edge/v2-edge-contract.json
+++ b/apps/cloud/src/edge/v2-edge-contract.json
@@ -32,6 +32,7 @@
     "/authors/*",
     "/og-image.png",
     "/pattern-graph-paper.svg",
+    "/favicon.png",
     "/.well-known/agent-skills/*",
     "/api/00e2e1f082a6ef17/*",
     "/api/fd6fab1fbb4883e1/submit"
@@ -62,6 +63,11 @@
       "target": "/oauth/client-metadata.json",
       "forwards": true
     },
+    {
+      "method": "GET",
+      "target": "/favicon.png",
+      "forwards": true
+    },
     {
       "method": "GET",
       "target": "/git/acme/site.git/info/refs?service=git-upload-pack",

Merge either first: until both deploy, the logo stays broken as it is today.

Activity

  1. added a commit that references this issue on Nov 12, 2026
    28731c8
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions