From 41829963879798df8e3aeec0607cbdf873da53b0 Mon Sep 17 00:00:00 2001 From: ariesy Date: Sat, 8 Aug 2026 14:27:00 +0000 Subject: [PATCH 1/2] fix: fallback token scope for registries that return placeholder scope (e.g. ghcr.io) Two issues prevented pull-through fallback from working with ghcr.io: 1. The scope returned by the upstream /v2/ endpoint (e.g. ghcr.io returns "repository:user/image:pull") was used as-is and then double-prefixed with "repository:", producing garbage scopes like "repository:repository:user/image:pull:pull,push" which the token endpoint rejects. Always use the actual request namespace instead. 2. Requested scope included push permission (":pull,push") even though fallback only performs read operations. ghcr.io rejects combined pull,push scopes with 403 DENIED; requesting only ":pull" works. Verified: ghcr.io (actions/actions-runner, home-assistant, otel-collector) now returns 200 through fallback; docker.io, registry.k8s.io, quay.io and gcr.io regression tests all pass. --- src/registry/http.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/registry/http.ts b/src/registry/http.ts index 2b3dda8..e31911f 100644 --- a/src/registry/http.ts +++ b/src/registry/http.ts @@ -293,7 +293,11 @@ export class RegistryHTTPClient implements Registry { } const authCtx = authHeaderIntoAuthContext(this.url, authenticateHeader); - if (!authCtx.scope) authCtx.scope = namespace; + // The scope returned by the upstream /v2/ endpoint is only a placeholder + // (e.g. ghcr.io returns "repository:user/image:pull"). Always use the + // namespace from the actual request, otherwise the scope ends up + // double-prefixed (repository:repository:...) and the token request fails. + authCtx.scope = namespace; switch (authCtx.authType) { case "bearer": return await this.authenticateBearer(authCtx); @@ -331,7 +335,7 @@ export class RegistryHTTPClient implements Registry { const params = new URLSearchParams({ service: ctx.service, // explicitely include that we don't want an offline_token. - scope: `repository:${ctx.scope}:pull,push`, + scope: `repository:${ctx.scope}:pull`, client_id: "r2registry", grant_type: this.configuration.username === undefined ? "none" : "password", password: this.configuration.username === undefined ? "" : this.password(), From e56c34324bd32e4536abaf24225b42917b449a9f Mon Sep 17 00:00:00 2001 From: ariesy Date: Sat, 8 Aug 2026 14:27:12 +0000 Subject: [PATCH 2/2] chore: ignore local credentials and build artifacts --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 7a26622..4cf64b8 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,7 @@ node_modules wrangler.toml wrangler.jsonc *.tsbuildinfo + +# local credentials & build artifacts +registry-credentials.txt +dist/