oras icon indicating copy to clipboard operation
oras copied to clipboard

`oras copy` can not handle separate authentication for same registry

Open MalteHei opened this issue 1 month ago • 25 comments

What happened in your environment?

When copying an artifact, it seems that oras uses only the target authentication for both the source and target artifact if both artifacts are in the same registry:

# copy from docker hub to harbor demo, works as expected
$ oras copy \
    'docker.io/library/alpine:latest' \
    'demo.goharbor.io/oras-src/alpine:latest' \
    --to-username='robot_oras-src+src' --to-password='DX37nuS39P7Hb7AbhaC9Ed1TtuulcSYG'

Copied [registry] docker.io/library/alpine:latest => [registry] demo.goharbor.io/oras-src/alpine:latest


# copy from harbor demo to harbor demo, fails with authorization error
$ oras copy \
    'demo.goharbor.io/oras-src/alpine:latest' \
    --from-username='robot_oras-src+src' --from-password='DX37nuS39P7Hb7AbhaC9Ed1TtuulcSYG' \
    'demo.goharbor.io/oras-dst/alpine:latest' \
    --to-username='robot_oras-dst+dst' --to-password='COhhaKcEy2i1IMwHVjQ00a8GyVgDTrc3'

Error from source registry for "demo.goharbor.io/oras-src/alpine:latest": unauthorized: unauthorized to access repository: oras-src/alpine, action: pull: unauthorized to access repository: oras-src/alpine, action: pull
Debug Logs
$ oras copy --debug \
    'demo.goharbor.io/oras-src/alpine:latest' \
    --from-username='robot_oras-src+src' --from-password='DX37nuS39P7Hb7AbhaC9Ed1TtuulcSYG' \
    'demo.goharbor.io/oras-dst/alpine:latest' \
    --to-username='robot_oras-dst+dst' --to-password='COhhaKcEy2i1IMwHVjQ00a8GyVgDTrc3'

[2025-11-05T14:40:40.115127814+01:00][DEBUG]: --> Request #0
> Request URL: "https://demo.goharbor.io/v2/oras-src/alpine/manifests/latest"
> Request method: "GET"
> Request headers:
   "Accept": "application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, application/vnd.oci.artifact.manifest.v1+json"
   "User-Agent": "oras/1.3.0"


[2025-11-05T14:40:40.198292177+01:00][DEBUG]: <-- Response #0
< Response Status: "401 Unauthorized"
< Response headers:
   "Strict-Transport-Security": "max-age=31536000; includeSubDomains"
   "Date": "Wed, 05 Nov 2025 13:36:17 GMT"
   "Content-Type": "application/json; charset=utf-8"
   "Content-Length": "152"
   "Docker-Distribution-Api-Version": "registry/2.0"
   "Set-Cookie": "*****"
   "Www-Authenticate": "Bearer realm=\"https://demo.goharbor.io/service/token\",service=\"harbor-registry\",scope=\"repository:oras-src/alpine:pull\""
   "X-Request-Id": "26993d607654ddbc23e3ef82b646af75"
< Response body:
{"errors":[{"code":"UNAUTHORIZED","message":"authorize header needed to send HEAD to repository: authorize header needed to send HEAD to repository"}]}



[2025-11-05T14:40:40.198417478+01:00][DEBUG]: --> Request #1
> Request URL: "https://demo.goharbor.io/service/token?scope=repository%3Aoras-dst%2Falpine%3Apull%2Cpush&scope=repository%3Aoras-src%2Falpine%3Apull&service=harbor-registry"
> Request method: "GET"
> Request headers:
   "Authorization": "*****"
   "User-Agent": "oras/1.3.0"


[2025-11-05T14:40:40.231140378+01:00][DEBUG]: <-- Response #1
< Response Status: "200 OK"
< Response headers:
   "Set-Cookie": "*****"
   "X-Request-Id": "31ae690e60b9787d8280dc8f08ae7996"
   "Strict-Transport-Security": "max-age=31536000; includeSubDomains"
   "Date": "Wed, 05 Nov 2025 13:36:17 GMT"
   "Content-Type": "application/json; charset=utf-8"
< Response body:
   Response body redacted due to potential credentials


[2025-11-05T14:40:40.231308662+01:00][DEBUG]: --> Request #2
> Request URL: "https://demo.goharbor.io/v2/oras-src/alpine/manifests/latest"
> Request method: "GET"
> Request headers:
   "Accept": "application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, application/vnd.oci.artifact.manifest.v1+json"
   "Authorization": "*****"
   "User-Agent": "oras/1.3.0"


[2025-11-05T14:40:40.254262499+01:00][DEBUG]: <-- Response #2
< Response Status: "401 Unauthorized"
< Response headers:
   "Content-Length": "180"
   "Docker-Distribution-Api-Version": "registry/2.0"
   "Set-Cookie": "*****"
   "Www-Authenticate": "Basic realm=\"harbor\""
   "X-Request-Id": "ba4e559bc202e7472ec357a640722e3f"
   "Strict-Transport-Security": "max-age=31536000; includeSubDomains"
   "Date": "Wed, 05 Nov 2025 13:36:17 GMT"
   "Content-Type": "application/json; charset=utf-8"
< Response body:
{"errors":[{"code":"UNAUTHORIZED","message":"unauthorized to access repository: oras-src/alpine, action: pull: unauthorized to access repository: oras-src/alpine, action: pull"}]}



Error from source registry for "demo.goharbor.io/oras-src/alpine:latest": unauthorized: unauthorized to access repository: oras-src/alpine, action: pull: unauthorized to access repository: oras-src/alpine, action: pull

What did you expect to happen?

oras copy should use the from-credentials for the source artifact and the to-credentials for the target artifact, even if both artifacts share the same registry (hostname).

How can we reproduce it?

  1. Create two private projects at https://demo.goharbor.io/
  2. Create a robot account with full permissions in each project
  3. Push an artifact into the first project
  4. oras copy the artifact into the second project

What is the version of your ORAS CLI?

$ oras version
Version:        1.3.0
Go version:     go1.25.0
OS/Arch:        linux/amd64
Git commit:     40530fe4c68e5825b868cd874bd46fc0cdd0f432
Git tree state: clean

What is your OS environment?

rhel 8.8

Are you willing to submit PRs to fix it?

  • [ ] Yes, I am willing to fix it.

MalteHei avatar Nov 05 '25 13:11 MalteHei

@wangxiaoxuan273 Could you take a look? I understand that it is a limitation that we cannot use two accounts for the same registry. However, this use of separate --username and --password should work.

shizhMSFT avatar Nov 05 '25 14:11 shizhMSFT

I can reproduce this error, though with a different error message (error from target registry instead of source). Will look into this further.

oras cp demo.goharbor.io/wangxiaoxuan120/alpine:latest demo.goharbor.io/wangxiaoxuan121/alpine:latest --from-username robot_wangxiaoxuan120+wangxiaoxuan120 --from-password qlqZZwK1qGg5VaV97IPhdogwdO15gSCh --to-username robot_wangxiaoxuan121+wangxiaoxuan121 --to-password FmKge3lnIPTJujO2rn3160WmUkANrScW

Error from destination registry for "demo.goharbor.io/wangxiaoxuan121/alpine:latest": unauthorized: unauthorized to access repository: wangxiaoxuan121/alpine, action: push: unauthorized to access repository: wangxiaoxuan121/alpine, action: push

wangxiaoxuan273 avatar Nov 06 '25 12:11 wangxiaoxuan273

The issue is caused by mount failure. The error is given by https://github.com/oras-project/oras-go/blob/6aa7882b2346987b7751fea36cb1b2d0d54e60aa/copy.go#L332 and https://github.com/oras-project/oras-go/blob/6aa7882b2346987b7751fea36cb1b2d0d54e60aa/registry/remote/repository.go#L798. If CopyNode is used instead of Mount the command succeeds.

Need to find out more details about:

  1. Why does mount fail with that error message. It looks like the blobStore mounter uses the credentials of the destination repo, the failure may be expected but the error message does not make sense to me.
  2. Why doesn't mount fail back to copy node after failure. Not sure if this is by design or not.

Thanks @MalteHei for reporting this issue. Will spend more time to look into this.

wangxiaoxuan273 avatar Nov 07 '25 10:11 wangxiaoxuan273

I looked into this further and believe the error originates from a server-side issue. oras is using the correct credentials and making requests that comply with the distribution spec. The server responds with a 401 (Unauthorized), and in this case it's correct for oras to return the response with errors without further attempting to fetch the content in (s *blobStore) Mount.

At this point, I don’t see anything that needs fixing on the oras side. @MalteHei

However, I’d like to suggest a possible enhancement for oras-go's mountOrCopyNode. This method is intended to mount blobs and, if that fails, fall back to copying. Currently, the fallback logic is delegated entirely to mounter.Mount, which depends on the remote server. This makes mountOrCopyNode fragile when server-side issues occur and prevents efficient fallback. A better approach might be to add a copy fallback after line 334 if mounter.Mount fails. This enhancement could resolve the oras cp failure posted in this issue.

@shizhMSFT @Wwwsylvia @FeynmanZhou

wangxiaoxuan273 avatar Nov 12 '25 08:11 wangxiaoxuan273

Interesting! I was not aware of that endpoint in the spec and I haven't looked into the oras code as I'm not familiar with go.

But do I understand correctly: if oras detects the source and target artifact use the same registry, oras does not copy the artifact itself but sends a request to the registry so it handles copying via mounting the source blob in another location?

If so, I wonder how credentials should be included in the request as the registry may require mulitple credentials (one for reading the source and one for pushing the target artifact).

Anyways, thanks for investigating @wangxiaoxuan273! I would love a fallback in oras if the server-side mount is not successful. Although I will also create an issue for Harbor to investigate the endpoint.

MalteHei avatar Nov 12 '25 10:11 MalteHei

Although the debug logs suggest that oras copy does not use the correct Authorization header when requesting a token from the URL in the Www-Authenticate header. Because directly after getting this token, oras fails to GET the source artifact (https://demo.goharbor.io/v2/oras-src/alpine/manifests/latest) with error unauthorized.

The token seems to be correctly provided by the registry because fetching the manifest works as expected:

Debug Logs: oras manifest fetch
$ oras manifest fetch --debug \
             'demo.goharbor.io/oras-src/alpine:latest' \
             --username='robot_oras-src+src' --password='DX37nuS39P7Hb7AbhaC9Ed1TtuulcSYG' \
             >/dev/null

[2025-11-12T12:25:15.241549568+01:00][DEBUG]: --> Request #0
> Request URL: "https://demo.goharbor.io/v2/oras-src/alpine/manifests/latest"
> Request method: "GET"
> Request headers:
   "User-Agent": "oras/1.3.0"
   "Accept": "application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, application/vnd.oci.artifact.manifest.v1+json"


[2025-11-12T12:25:17.327424321+01:00][DEBUG]: <-- Response #0
< Response Status: "401 Unauthorized"
< Response headers:
   "Date": "Wed, 12 Nov 2025 11:24:57 GMT"
   "Content-Type": "application/json; charset=utf-8"
   "Content-Length": "152"
   "Docker-Distribution-Api-Version": "registry/2.0"
   "Set-Cookie": "*****"
   "Www-Authenticate": "Bearer realm=\"https://demo.goharbor.io/service/token\",service=\"harbor-registry\",scope=\"repository:oras-src/alpine:pull\""
   "X-Request-Id": "516163c038a5ef1d6f4e178111a4bfe5"
   "Strict-Transport-Security": "max-age=31536000; includeSubDomains"
< Response body:
{"errors":[{"code":"UNAUTHORIZED","message":"authorize header needed to send HEAD to repository: authorize header needed to send HEAD to repository"}]}



[2025-11-12T12:25:17.327494892+01:00][DEBUG]: --> Request #1
> Request URL: "https://demo.goharbor.io/service/token?scope=repository%3Aoras-src%2Falpine%3Apull&service=harbor-registry"
> Request method: "GET"
> Request headers:
   "Authorization": "*****"
   "User-Agent": "oras/1.3.0"


[2025-11-12T12:25:17.361676379+01:00][DEBUG]: <-- Response #1
< Response Status: "200 OK"
< Response headers:
   "Strict-Transport-Security": "max-age=31536000; includeSubDomains"
   "Date": "Wed, 12 Nov 2025 11:24:57 GMT"
   "Content-Type": "application/json; charset=utf-8"
   "Set-Cookie": "*****"
   "X-Request-Id": "0d233c8cfd2c20a48efb58bc0467b7da"
< Response body:
   Response body redacted due to potential credentials


[2025-11-12T12:25:17.361837215+01:00][DEBUG]: --> Request #2
> Request URL: "https://demo.goharbor.io/v2/oras-src/alpine/manifests/latest"
> Request method: "GET"
> Request headers:
   "Accept": "application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, application/vnd.oci.artifact.manifest.v1+json"
   "Authorization": "*****"
   "User-Agent": "oras/1.3.0"


[2025-11-12T12:25:17.380745649+01:00][DEBUG]: <-- Response #2
< Response Status: "200 OK"
< Response headers:
   "Content-Length": "9218"
   "Etag": "\"sha256:4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412\""
   "Set-Cookie": "*****"
   "Date": "Wed, 12 Nov 2025 11:24:57 GMT"
   "Content-Type": "application/vnd.oci.image.index.v1+json"
   "Docker-Content-Digest": "sha256:4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412"
   "Docker-Distribution-Api-Version": "registry/2.0"
   "X-Request-Id": "6c9a2cd211ccafebf67e6ab7b19ebeb0"
   "Strict-Transport-Security": "max-age=31536000; includeSubDomains"
< Response body:
{<omitted>}

MalteHei avatar Nov 12 '25 11:11 MalteHei

The ORAS CLI uses the config.json format used by Docker and these credentials are scoped to the domain and you are looking for credentials scoped to the domain/namespace. There is pull request this https://github.com/oras-project/oras-go/pull/1006 to add support to oras-go for auth.json which scopes credentials to any level you want, potentially to the repository

TerryHowe avatar Nov 12 '25 12:11 TerryHowe

@wangxiaoxuan273 reminds me that mount operation is only possible with a single account having access to both repositories. In your case, you have one account per repository. oras-go should fallback to copy instead of mount in such a case.

shizhMSFT avatar Nov 12 '25 14:11 shizhMSFT

But do I understand correctly: if oras detects the source and target artifact use the same registry, oras does not copy the artifact itself but sends a request to the registry so it handles copying via mounting the source blob in another location?

If so, I wonder how credentials should be included in the request as the registry may require mulitple credentials (one for reading the source and one for pushing the target artifact).

Yes, you understand it correctly. Here are more details: if oras detects the source and target artifact use the same registry, it first checks if the registry supports the mount endpoint specified by the distribution spec. If the endpoint is supported, oras uses the available endpoint to mount the blob. Otherwise it copies the blob.

How the credentials to be used is not specified in the spec, and I believe it's subject to specific registries. @MalteHei

wangxiaoxuan273 avatar Nov 13 '25 05:11 wangxiaoxuan273

@wangxiaoxuan273 reminds me that mount operation is only possible with a single account having access to both repositories. In your case, you have one account per repository. oras-go should fallback to copy instead of mount in such a case.

@shizhMSFT Should we open an issue on oras-go and propose a fix to this?

wangxiaoxuan273 avatar Nov 13 '25 05:11 wangxiaoxuan273

@wangxiaoxuan273 reminds me that mount operation is only possible with a single account having access to both repositories. In your case, you have one account per repository. oras-go should fallback to copy instead of mount in such a case.

@shizhMSFT Should we open an issue on oras-go and propose a fix to this?

@wangxiaoxuan273 No, we should propose a fix in oras instead as oras-go does not have sufficient context.

shizhMSFT avatar Nov 13 '25 05:11 shizhMSFT

@wangxiaoxuan273 No, we should propose a fix in oras instead as oras-go does not have sufficient context.

@shizhMSFT How can this be fixed in oras? I have to call oras.Copy, oras.CopyGraph or oras.ExtendedCopyGraph, they all eventually go to mountOrCopy in oras-go and proceed to the mount endpoint.

https://github.com/oras-project/oras/blob/6511fe3dc5d0465450475799b0a713c05544b692/cmd/oras/root/cp.go#L167

wangxiaoxuan273 avatar Nov 13 '25 06:11 wangxiaoxuan273

If flags like --from-username are present (unless --from-xxx credentials are exactly the same as --to-xxx credentials), I think you can just skip mounting. Here's the related code:

https://github.com/oras-project/oras/blob/6511fe3dc5d0465450475799b0a713c05544b692/cmd/oras/root/cp.go#L177-L181

shizhMSFT avatar Nov 13 '25 06:11 shizhMSFT

@shizhMSFT @Wwwsylvia Please review PR #1903

wangxiaoxuan273 avatar Nov 13 '25 08:11 wangxiaoxuan273

@MalteHei The fix has been merged. You may try out the main branch, it should work for this scenario now. Thanks for reporting this issue again.

wangxiaoxuan273 avatar Nov 18 '25 01:11 wangxiaoxuan273

Using the latest version of oras:

$ oras version
Version:        1.3.0+unreleased
Go version:     go1.25.4
OS/Arch:        linux/amd64
Git commit:     6c3e3e5a3e087ef2881cebb310f3d5fb6348b2ab
Git tree state: clean

I am still unable to copy artifacts:

$ oras copy \
>     'docker.io/library/alpine:latest' \
>     'demo.goharbor.io/oras-src/alpine:latest' \
>     --to-username='robot_oras-src+src' --to-password='DX37nuS39P7Hb7AbhaC9Ed1TtuulcSYG'
✓ Copied  application/vnd.oci.image.index.v1+json                                                                                                                9/9 KB 100.00%  287ms
  └─ sha256:4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
Copied [registry] docker.io/library/alpine:latest => [registry] demo.goharbor.io/oras-src/alpine:latest
Digest: sha256:4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412

$ oras copy \
>     'demo.goharbor.io/oras-src/alpine:latest' \
>     --from-username='robot_oras-src+src' --from-password='DX37nuS39P7Hb7AbhaC9Ed1TtuulcSYG' \
>     'demo.goharbor.io/oras-dst/alpine:latest' \
>     --to-username='robot_oras-dst+dst' --to-password='51HARHfzxRExB5mbpPtZN7dkePqMvrp7'
Error from destination registry for "demo.goharbor.io/oras-dst/alpine:latest": HEAD "https://demo.goharbor.io/v2/oras-dst/alpine/manifests/sha256:4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412": response status code 401: Unauthorized

Pushing works correctly:

$ oras push \
>     'demo.goharbor.io/oras-dst/alpine:latest' \
>     --username='robot_oras-dst+dst' --password='51HARHfzxRExB5mbpPtZN7dkePqMvrp7' \
>     file.txt
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
✓ Uploaded  file.txt                                                                                                                                             6/6  B 100.00%  913ms
  └─ sha256:5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03
✓ Exists    application/vnd.oci.empty.v1+json                                                                                                                    2/2  B 100.00%     0s
  └─ sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a
✓ Uploaded  application/vnd.oci.image.manifest.v1+json                                                                                                       586/586  B 100.00%  491ms
  └─ sha256:23c5501cfe1fb4106eb4089dd1ba086dcc7060cd0a006f22e38e196542b16368
Pushed [registry] demo.goharbor.io/oras-dst/alpine:latest
ArtifactType: application/vnd.unknown.artifact.v1
Digest: sha256:23c5501cfe1fb4106eb4089dd1ba086dcc7060cd0a006f22e38e196542b16368

MalteHei avatar Dec 01 '25 08:12 MalteHei

I believe this is a server side issue.

Details: I reproduced the issue on the registry. The requests oras sends are all decent. The error is caused by https://github.com/oras-project/oras/blob/6c3e3e5a3e087ef2881cebb310f3d5fb6348b2ab/cmd/oras/root/cp.go#L138

If we manually replace ctx with context.Background the copy succeeds.

It looks like the registry server does not accept the scopes of pull and push being added to the context, and returns 401. But this should not be the correct behavior.

I don't remember the details of the authorization workflows. Looping to @shizhMSFT for confirmation.

wangxiaoxuan273 avatar Dec 02 '25 09:12 wangxiaoxuan273

To confirm it, the best way is to use the --debug flag.

shizhMSFT avatar Dec 02 '25 09:12 shizhMSFT

@MalteHei

This is a server-side issue. I looked into the token returned by the registry server, it looks like this:

Image

Notice that the action fields are empty. This results the 401 error.

wangxiaoxuan273 avatar Dec 03 '25 05:12 wangxiaoxuan273

@wangxiaoxuan273 see my comment https://github.com/oras-project/oras/issues/1892#issuecomment-3521487323. I suspect that oras just send the wrong credentials in the Authorization header when requesting the token.

MalteHei avatar Dec 03 '25 06:12 MalteHei

@wangxiaoxuan273 see my comment #1892 (comment). I suspect that oras just send the wrong credentials in the Authorization header when requesting the token.

@MalteHei I have checked that oras uses the correct credentials and the form of authorization headers are also correct. The registry returns a token with empty action and that causes the 401. See my last comment.

wangxiaoxuan273 avatar Dec 03 '25 09:12 wangxiaoxuan273

its not a issue on the server(harbor) side. it is working as expected from the logs its clear that it allows the authentication.

Here is logs of oras

copy from harbor to harbor

oras copy \
    'localhost:8080/from/alpine:latest' \
    --from-username='rob_from+kumar' --from-password='hZh1LVvP8LhclF8zfzyHRdgFkH0OcQkt' \
    'localhost:8080/to/alpine:latest' \
    --to-username='rob_to+to' --to-password='tQ2FtScoZJzERiLT0ueRJPyqtEXKI2zH' --debug
[2025-12-07T02:35:21.838224324+05:30][DEBUG]: --> Request #0
> Request URL: "http://localhost:8080/v2/from/alpine/manifests/latest"
> Request method: "GET"
> Request headers:
   "Accept": "application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, application/vnd.oci.artifact.manifest.v1+json"
   "User-Agent": "oras/1.3.0"


[2025-12-07T02:35:21.846153106+05:30][DEBUG]: <-- Response #0
< Response Status: "401 Unauthorized"
< Response headers:
   "Www-Authenticate": "Bearer realm=\"http://localhost:8080/service/token\",service=\"harbor-registry\",scope=\"repository:from/alpine:pull\""
   "X-Request-Id": "6be567ce-3b9e-4604-863a-ddc557e8dec8"
   "Date": "Sat, 06 Dec 2025 21:05:21 GMT"
   "Content-Length": "152"
   "Content-Type": "application/json; charset=utf-8"
   "Docker-Distribution-Api-Version": "registry/2.0"
   "Set-Cookie": "*****"
< Response body:
{"errors":[{"code":"UNAUTHORIZED","message":"authorize header needed to send HEAD to repository: authorize header needed to send HEAD to repository"}]}



[2025-12-07T02:35:21.846185416+05:30][DEBUG]: --> Request #1
> Request URL: "http://localhost:8080/service/token?scope=repository%3Afrom%2Falpine%3Apull&scope=repository%3Ato%2Falpine%3Apull%2Cpush&service=harbor-registry"
> Request method: "GET"
> Request headers:
   "User-Agent": "oras/1.3.0"
   "Authorization": "*****"


[2025-12-07T02:35:21.867113389+05:30][DEBUG]: <-- Response #1
< Response Status: "200 OK"
< Response headers:
   "Date": "Sat, 06 Dec 2025 21:05:21 GMT"
   "Content-Length": "1277"
   "Content-Type": "application/json; charset=utf-8"
   "Set-Cookie": "*****"
   "X-Request-Id": "e843d3f2-255a-40cd-803d-4bb0d84f61fb"
< Response body:
   Response body redacted due to potential credentials


[2025-12-07T02:35:21.86716041+05:30][DEBUG]: --> Request #2
> Request URL: "http://localhost:8080/v2/from/alpine/manifests/latest"
> Request method: "GET"
> Request headers:
   "Authorization": "*****"
   "User-Agent": "oras/1.3.0"
   "Accept": "application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, application/vnd.oci.artifact.manifest.v1+json"


[2025-12-07T02:35:21.879045234+05:30][DEBUG]: <-- Response #2
< Response Status: "200 OK"
< Response headers:
   "Content-Length": "9218"
   "Content-Type": "application/vnd.oci.image.index.v1+json"
   "Date": "Sat, 06 Dec 2025 21:05:21 GMT"
   "Docker-Content-Digest": "sha256:51183f2cfa6320055da30872f211093f9ff1d3cf06f39a0bdb212314c5dc7375"
   "Docker-Distribution-Api-Version": "registry/2.0"
   "Etag": "\"sha256:51183f2cfa6320055da30872f211093f9ff1d3cf06f39a0bdb212314c5dc7375\""
   "Set-Cookie": "*****"
   "X-Content-Type-Options": "nosniff"
   "X-Request-Id": "f3515f1d-37b3-4fa0-9cb8-a2e9174416b8"
< Response body:
{"manifests":[{"annotations":{"com.docker.official-images.bashbrew.arch":"amd64","org.opencontainers.image.base.name":"scratch","org.opencontainers.image.created":"2025-12-03T19:29:46Z","org.opencontainers.image.revision":"24735c621e78574b49bb05b10dddac2497e423c2","org.opencontainers.image.source":"https:\/\/github.com\/alpinelinux\/docker-alpine.git#24735c621e78574b49bb05b10dddac2497e423c2:x86_64","org.opencontainers.image.url":"https:\/\/hub.docker.com\/_\/alpine","org.opencontainers.image.version":"3.23.0"},"digest":"sha256:a107a3c031732299dd9dd607bb13787834db2de38cfa13f1993b7105e4814c60","mediaType":"application\/vnd.oci.image.manifest.v1+json","platform":{"architecture":"amd64","os":"linux"},"size":1022},{"annotations":{"com.docker.official-images.bashbrew.arch":"amd64","vnd.docker.reference.digest":"sha256:a107a3c031732299dd9dd607bb13787834db2de38cfa13f1993b7105e4814c60","vnd.docker.reference.type":"attestation-manifest"},"digest":"sha256:135aac389d867b500874d83410faca4b15020bf2a96ed049c61d48723d8644c0","mediaType":"application\/vnd.oci.image.manifest.v1+json","platform":{"architecture":"unknown","os":"unknown"},"size":838},{"annotations":{"com.docker.official-images.bashbrew.arch":"arm32v6","org.opencontainers.image.base.name":"scratch","org.opencontainers.image.created":"2025-12-03T19:29:44Z","org.opencontainers.image.revision":"24735c621e78574b49bb05b10dddac2497e423c2","org.opencontainers.image.source":"https:\/\/github.com\/alpinelinux\/docker-alpine.git#24735c621e78574b49bb05b10dddac2497e423c2:armhf","org.opencontainers.image.url":"https:\/\/hub.docker.com\/_\/alpine","org.opencontainers.image.version":"3.23.0"},"digest":"sha256:d6218bcba4fa7414451627f556efe9ec47f9684336ccccf9a207836096578578","mediaType":"application\/vnd.oci.image.manifest.v1+json","platform":{"architecture":"arm","os":"linux","variant":"v6"},"size":1023},{"annotations":{"com.docker.official-images.bashbrew.arch":"arm32v6","vnd.docker.reference.digest":"sha256:d6218bcba4fa7414451627f556efe9ec47f9684336ccccf9a207836096578578","vnd.docker.reference.type":"attestation-manifest"},"digest":"sha256:5a3386629390bbc37645af9167beb2c97840319978211ca11388873e69b644c2","mediaType":"application\/vnd.oci.image.manifest.v1+json","platform":{"architecture":"unknown","os":"unknown"},"size":566},{"annotations":{"com.docker.official-images.bashbrew.arch":"arm32v7","org.opencontainers.image.base.name":"scratch","org.opencontainers.image.created":"2025-12-03T19:30:38Z","org.opencontainers.image.revision":"24735c621e78574b49bb05b10dddac2497e423c2","org.opencontainers.image.source":"https:\/\/github.com\/alpinelinux\/docker-alpine.git#24735c621e78574b49bb05b10dddac2497e423c2:armv7","org.opencontainers.image.url":"https:\/\/hub.docker.com\/_\/alpine","org.opencontainers.image.version":"3.23.0"},"digest":"sha256:8d133a316546e1e39d1837cb8c87a344b34cff218aca55e1313d9c4069dc0825","mediaType":"application\/vnd.oci.image.manifest.v1+json","platform":{"architecture":"arm","os":"linux","variant":"v7"},"size":1023},{"annotations":{"com.docker.official-images.bashbrew.arch":"arm32v7","vnd.docker.reference.digest":"sha256:8d133a316546e1e39d1837cb8c87a344b34cff218aca55e1313d9c4069dc0825","vnd.docker.reference.type":"attestation-manifest"},"digest":"sha256:8e6e34008e3a1a01366c863888f4e9c9e75f853ed43125001bcaa7713d9cd117","mediaType":"application\/vnd.oci.image.manifest.v1+json","platform":{"architecture":"unknown","os":"unknown"},"size":838},{"annotations":{"com.docker.official-images.bashbrew.arch":"arm64v8","org.opencontainers.image.base.name":"scratch","org.opencontainers.image.created":"2025-12-03T19:29:44Z","org.opencontainers.image.revision":"24735c621e78574b49bb05b10dddac2497e423c2","org.opencontainers.image.source":"https:\/\/github.com\/alpinelinux\/docker-alpine.git#24735c621e78574b49bb05b10dddac2497e423c2:aarch64","org.opencontainers.image.url":"https:\/\/hub.docker.com\/_\/alpine","org.opencontainers.image.version":"3.23.0"},"digest":"sha256:ed4a87b21407bf05ebacdb971243c59903b5b12e06c8e373de2b1b1955f5dd9e","mediaType":"application\/vnd.oci.image.manifest.v1+json","platform":{"architecture":"arm64","os":"linux","variant":"v8"},"size":1025},{"annotations":{"com.docker.official-images.bashbrew.arch":"arm64v8","vnd.docker.reference.digest":"sha256:ed4a87b21407bf05ebacdb971243c59903b5b12e06c8e373de2b1b1955f5dd9e","vnd.docker.reference.type":"attestation-manifest"},"digest":"sha256:fddefdcec3fff10cc8b0b737cb76a2a9a20e54781620b7f181ff7aca2952955d","mediaType":"application\/vnd.oci.image.manifest.v1+json","platform":{"architecture":"unknown","os":"unknown"},"size":838},{"annotations":{"com.docker.official-images.bashbrew.arch":"i386","org.opencontainers.image.base.name":"scratch","org.opencontainers.image.created":"2025-12-03T19:29:49Z","org.opencontainers.image.revision":"24735c621e78574b49bb05b10dddac2497e423c2","org.opencontainers.image.source":"https:\/\/github.com\/alpinelinux\/docker-alpine.git#24735c621e78574b49bb05b10dddac2497e423c2:x86","org.opencontainers.image.url":"https:\/\/hub.docker.com\/_\/alpine","org.opencontainers.image.version":"3.23.0"},"digest":"sha256:c78ded0fee4493809c8ca71d4a6057a46237763d952fae15ea418f6d14137f2d","mediaType":"application\/vnd.oci.image.manifest.v1+json","platform":{"architecture":"386","os":"linux"},"size":1018},{"annotations":{"com.docker.official-images.bashbrew.arch":"i386","vnd.docker.reference.digest":"sha256:c78ded0fee4493809c8ca71d4a6057a46237763d952fae15ea418f6d14137f2d","vnd.docker.reference.type":"attestation-manifest"},"digest":"sha256:b37b830f7e326d2f3af5186a748d9acef7232c6b9d645a4a7a00b959087d04c0","mediaType":"application\/vnd.oci.image.manifest.v1+json","platform":{"architecture":"unknown","os":"unknown"},"size":838},{"annotations":{"com.docker.official-images.bashbrew.arch":"ppc64le","org.opencontainers.image.base.name":"scratch","org.opencontainers.image.created":"2025-12-03T19:28:54Z","org.opencontainers.image.revision":"24735c621e78574b49bb05b10dddac2497e423c2","org.opencontainers.image.source":"https:\/\/github.com\/alpinelinux\/docker-alpine.git#24735c621e78574b49bb05b10dddac2497e423c2:ppc64le","org.opencontainers.image.url":"https:\/\/hub.docker.com\/_\/alpine","org.opencontainers.image.version":"3.23.0"},"digest":"sha256:21fdb77c56a0fb3f1e483a20de513c41a48b34fb52f2e0ea3a95f48fd5deddf6","mediaType":"application\/vnd.oci.image.manifest.v1+json","platform":{"architecture":"ppc64le","os":"linux"},"size":1025},{"annotations":{"com.docker.official-images.bashbrew.arch":"ppc64le","vnd.docker.reference.digest":"sha256:21fdb77c56a0fb3f1e483a20de513c41a48b34fb52f2e0ea3a95f48fd5deddf6","vnd.docker.reference.type":"attestation-manifest"},"digest":"sha256:3e4690e11f84fbddc0ad26fe129ddf2fbab351a6c06b800769a646095f3cae11","mediaType":"application\/vnd.oci.image.manifest.v1+json","platform":{"architecture":"unknown","os":"unknown"},"size":838},{"annotations":{"com.docker.official-images.bashbrew.arch":"riscv64","org.opencontainers.image.base.name":"scratch","org.opencontainers.image.created":"2025-12-03T19:29:17Z","org.opencontainers.image.revision":"24735c621e78574b49bb05b10dddac2497e423c2","org.opencontainers.image.source":"https:\/\/github.com\/alpinelinux\/docker-alpine.git#24735c621e78574b49bb05b10dddac2497e423c2:riscv64","org.opencontainers.image.url":"https:\/\/hub.docker.com\/_\/alpine","org.opencontainers.image.version":"3.23.0"},"digest":"sha256:a9e9814086aafbe9f687cf671a963233882537cf4eb1c239366cd98ebe3414e2","mediaType":"application\/vnd.oci.image.manifest.v1+json","platform":{"architecture":"riscv64","os":"linux"},"size":1025},{"annotations":{"com.docker.official-images.bashbrew.arch":"riscv64","vnd.docker.reference.digest":"sha256:a9e9814086aafbe9f687cf671a963233882537cf4eb1c239366cd98ebe3414e2","vnd.docker.reference.type":"attestation-manifest"},"digest":"sha256:1888a4ac62d41df1cb00d643d5b3217dddb8abb3709b004ab3aa73fa62e93f17","mediaType":"application\/vnd.oci.image.manifest.v1+json","platform":{"architecture":"unknown","os":"unknown"},"size":838},{"annotations":{"com.docker.official-images.bashbrew.arch":"s390x","org.opencontainers.image.base.name":"scratch","org.opencontainers.image.created":"2025-12-03T19:29:09Z","org.opencontainers.image.revision":"24735c621e78574b49bb05b10dddac2497e423c2","org.opencontainers.image.source":"https:\/\/github.com\/alpinelinux\/docker-alpine.git#24735c621e78574b49bb05b10dddac2497e423c2:s390x","org.opencontainers.image.url":"https:\/\/hub.docker.com\/_\/alpine","org.opencontainers.image.version":"3.23.0"},"digest":"sha256:2c4dfe4431612ed70fbbf79888190325eb3adeb9c7105ed3a7f507750300a893","mediaType":"application\/vnd.oci.image.manifest.v1+json","platform":{"architecture":"s390x","os":"linux"},"size":1021},{"annotations":{"com.docker.official-images.bashbrew.arch":"s390x","vnd.docker.reference.digest":"sha256:2c4dfe4431612ed70fbbf79888190325eb3adeb9c7105ed3a7f507750300a893","vnd.docker.reference.type":"attestation-manifest"},"digest":"sha256:ad2445a2c5f06b54237fff3bcce5e5729ff25b427a8e0428044df0f4504f6abd","mediaType":"application\/vnd.oci.image.manifest.v1+json","platform":{"architecture":"unknown","os":"unknown"},"size":838}],"mediaType":"application\/vnd.oci.image.index.v1+json","schemaVersion":2}


[2025-12-07T02:35:21.879408347+05:30][DEBUG]: --> Request #3
> Request URL: "http://localhost:8080/v2/to/alpine/manifests/sha256:51183f2cfa6320055da30872f211093f9ff1d3cf06f39a0bdb212314c5dc7375"
> Request method: "HEAD"
> Request headers:
   "User-Agent": "oras/1.3.0"
   "Accept": "application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, application/vnd.oci.artifact.manifest.v1+json"


[2025-12-07T02:35:21.880782967+05:30][DEBUG]: <-- Response #3
< Response Status: "401 Unauthorized"
< Response headers:
   "Content-Type": "application/json; charset=utf-8"
   "Docker-Distribution-Api-Version": "registry/2.0"
   "Set-Cookie": "*****"
   "Www-Authenticate": "Bearer realm=\"http://localhost:8080/service/token\",service=\"harbor-registry\",scope=\"repository:to/alpine:pull\""
   "X-Request-Id": "228c8b05-60eb-4f5b-a14b-a97c60e51d4a"
   "Date": "Sat, 06 Dec 2025 21:05:21 GMT"
   "Content-Length": "152"
< Response body:
   No response body to print


[2025-12-07T02:35:21.880852558+05:30][DEBUG]: --> Request #4
> Request URL: "http://localhost:8080/service/token?scope=repository%3Ato%2Falpine%3Apull%2Cpush&service=harbor-registry"
> Request method: "GET"
> Request headers:
   "Authorization": "*****"
   "User-Agent": "oras/1.3.0"


[2025-12-07T02:35:21.898373974+05:30][DEBUG]: <-- Response #4
< Response Status: "200 OK"
< Response headers:
   "Content-Length": "1195"
   "Content-Type": "application/json; charset=utf-8"
   "Set-Cookie": "*****"
   "X-Request-Id": "b3be47b3-cc43-4d0b-abcc-8909fa110f47"
   "Date": "Sat, 06 Dec 2025 21:05:21 GMT"
< Response body:
   Response body redacted due to potential credentials


[2025-12-07T02:35:21.898411304+05:30][DEBUG]: --> Request #5
> Request URL: "http://localhost:8080/v2/to/alpine/manifests/sha256:51183f2cfa6320055da30872f211093f9ff1d3cf06f39a0bdb212314c5dc7375"
> Request method: "HEAD"
> Request headers:
   "Accept": "application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, application/vnd.oci.artifact.manifest.v1+json"
   "Authorization": "*****"
   "User-Agent": "oras/1.3.0"


[2025-12-07T02:35:21.908321432+05:30][DEBUG]: <-- Response #5
< Response Status: "404 Not Found"
< Response headers:
   "Content-Type": "application/json; charset=utf-8"
   "Set-Cookie": "*****"
   "X-Request-Id": "f766e053-ab43-4e6c-a3ac-37072a204d37"
   "Date": "Sat, 06 Dec 2025 21:05:21 GMT"
   "Content-Length": "147"
< Response body:
   No response body to print


[2025-12-07T02:35:21.908486233+05:30][DEBUG]: --> Request #6
> Request URL: "http://localhost:8080/v2/to/alpine/manifests/sha256:d6218bcba4fa7414451627f556efe9ec47f9684336ccccf9a207836096578578"
> Request method: "HEAD"
> Request headers:
   "Accept": "application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, application/vnd.oci.artifact.manifest.v1+json"
   "Authorization": "*****"
   "User-Agent": "oras/1.3.0"


[2025-12-07T02:35:21.908490803+05:30][DEBUG]: --> Request #7
> Request URL: "http://localhost:8080/v2/to/alpine/manifests/sha256:a107a3c031732299dd9dd607bb13787834db2de38cfa13f1993b7105e4814c60"
> Request method: "HEAD"
> Request headers:
   "Accept": "application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, application/vnd.oci.artifact.manifest.v1+json"
   "Authorization": "*****"
   "User-Agent": "oras/1.3.0"


[2025-12-07T02:35:21.908513303+05:30][DEBUG]: --> Request #8
> Request URL: "http://localhost:8080/v2/to/alpine/manifests/sha256:135aac389d867b500874d83410faca4b15020bf2a96ed049c61d48723d8644c0"
> Request method: "HEAD"
> Request headers:
   "Accept": "application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, application/vnd.oci.artifact.manifest.v1+json"
   "Authorization": "*****"
   "User-Agent": "oras/1.3.0"


[2025-12-07T02:35:21.917210641+05:30][DEBUG]: <-- Response #8
< Response Status: "404 Not Found"
< Response headers:
   "Content-Type": "application/json; charset=utf-8"
   "Set-Cookie": "*****"
   "X-Request-Id": "96e42267-4d21-423a-a67e-2a17d500bd11"
   "Date": "Sat, 06 Dec 2025 21:05:21 GMT"
   "Content-Length": "147"
< Response body:
   No response body to print


[2025-12-07T02:35:21.917261661+05:30][DEBUG]: --> Request #9
> Request URL: "http://localhost:8080/v2/from/alpine/manifests/sha256:135aac389d867b500874d83410faca4b15020bf2a96ed049c61d48723d8644c0"
> Request method: "GET"
> Request headers:
   "Accept": "application/vnd.oci.image.manifest.v1+json"
   "Authorization": "*****"
   "User-Agent": "oras/1.3.0"


[2025-12-07T02:35:21.919517239+05:30][DEBUG]: <-- Response #6
< Response Status: "404 Not Found"
< Response headers:
   "Content-Type": "application/json; charset=utf-8"
   "Set-Cookie": "*****"
   "X-Request-Id": "720520ce-ee1d-41f6-ba10-7053b68a179e"
   "Date": "Sat, 06 Dec 2025 21:05:21 GMT"
   "Content-Length": "147"
< Response body:
   No response body to print


[2025-12-07T02:35:21.920081793+05:30][DEBUG]: --> Request #10
> Request URL: "http://localhost:8080/v2/from/alpine/manifests/sha256:d6218bcba4fa7414451627f556efe9ec47f9684336ccccf9a207836096578578"
> Request method: "GET"
> Request headers:
   "Accept": "application/vnd.oci.image.manifest.v1+json"
   "Authorization": "*****"
   "User-Agent": "oras/1.3.0"


[2025-12-07T02:35:21.928328897+05:30][DEBUG]: <-- Response #9
< Response Status: "200 OK"
< Response headers:
   "X-Content-Type-Options": "nosniff"
   "Content-Length": "838"
   "Content-Type": "application/vnd.oci.image.manifest.v1+json"
   "Date": "Sat, 06 Dec 2025 21:05:21 GMT"
   "Docker-Content-Digest": "sha256:135aac389d867b500874d83410faca4b15020bf2a96ed049c61d48723d8644c0"
   "Docker-Distribution-Api-Version": "registry/2.0"
   "Set-Cookie": "*****"
   "X-Request-Id": "674999b6-31f3-4029-8bce-5817ee825135"
   "Etag": "\"sha256:135aac389d867b500874d83410faca4b15020bf2a96ed049c61d48723d8644c0\""
< Response body:
{
  "schemaVersion": 2,
  "mediaType": "application/vnd.oci.image.manifest.v1+json",
  "config": {
    "mediaType": "application/vnd.oci.image.config.v1+json",
    "digest": "sha256:f386f0c85682b1a81b7db055fb8371703137f1b7d7ef53d2d3e74bf19124ee13",
    "size": 241
  },
  "layers": [
    {
      "mediaType": "application/vnd.in-toto+json",
      "digest": "sha256:8c2d3640fffccdf484e149526e2e8b1f047d7bacd577a80c835aa42aa10253a0",
      "size": 79649,
      "annotations": {
        "in-toto.io/predicate-type": "https://spdx.dev/Document"
      }
    },
    {
      "mediaType": "application/vnd.in-toto+json",
      "digest": "sha256:fd1c12efde934d78574b7c8836bdbdaa5caa76e723a95bb93f6f068d538f4475",
      "size": 5639,
      "annotations": {
        "in-toto.io/predicate-type": "https://slsa.dev/provenance/v0.2"
      }
    }
  ]
}


[2025-12-07T02:35:21.928487859+05:30][DEBUG]: --> Request #11
> Request URL: "http://localhost:8080/v2/to/alpine/manifests/sha256:5a3386629390bbc37645af9167beb2c97840319978211ca11388873e69b644c2"
> Request method: "HEAD"
> Request headers:
   "Accept": "application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, application/vnd.oci.artifact.manifest.v1+json"
   "Authorization": "*****"
   "User-Agent": "oras/1.3.0"


[2025-12-07T02:35:21.932692961+05:30][DEBUG]: <-- Response #7
< Response Status: "404 Not Found"
< Response headers:
   "Content-Length": "147"
   "Content-Type": "application/json; charset=utf-8"
   "Set-Cookie": "*****"
   "X-Request-Id": "10d60003-ad41-48fc-9f50-aaa5903d31ad"
   "Date": "Sat, 06 Dec 2025 21:05:21 GMT"
< Response body:
   No response body to print


[2025-12-07T02:35:21.932737832+05:30][DEBUG]: --> Request #12
> Request URL: "http://localhost:8080/v2/from/alpine/manifests/sha256:a107a3c031732299dd9dd607bb13787834db2de38cfa13f1993b7105e4814c60"
> Request method: "GET"
> Request headers:
   "Accept": "application/vnd.oci.image.manifest.v1+json"
   "Authorization": "*****"
   "User-Agent": "oras/1.3.0"


[2025-12-07T02:35:21.935681425+05:30][DEBUG]: <-- Response #11
< Response Status: "404 Not Found"
< Response headers:
   "Content-Type": "application/json; charset=utf-8"
   "Set-Cookie": "*****"
   "X-Request-Id": "68b1c460-5a7b-4bfe-b01e-c485fb0c92eb"
   "Date": "Sat, 06 Dec 2025 21:05:21 GMT"
   "Content-Length": "147"
< Response body:
   No response body to print


[2025-12-07T02:35:21.935714625+05:30][DEBUG]: --> Request #13
> Request URL: "http://localhost:8080/v2/from/alpine/manifests/sha256:5a3386629390bbc37645af9167beb2c97840319978211ca11388873e69b644c2"
> Request method: "GET"
> Request headers:
   "Accept": "application/vnd.oci.image.manifest.v1+json"
   "Authorization": "*****"
   "User-Agent": "oras/1.3.0"


[2025-12-07T02:35:21.941736812+05:30][DEBUG]: <-- Response #10
< Response Status: "200 OK"
< Response headers:
   "Content-Length": "1023"
   "Date": "Sat, 06 Dec 2025 21:05:21 GMT"
   "Docker-Content-Digest": "sha256:d6218bcba4fa7414451627f556efe9ec47f9684336ccccf9a207836096578578"
   "Docker-Distribution-Api-Version": "registry/2.0"
   "Etag": "\"sha256:d6218bcba4fa7414451627f556efe9ec47f9684336ccccf9a207836096578578\""
   "Set-Cookie": "*****"
   "X-Request-Id": "6cf25df3-7f60-46b7-83b2-4402cc80422e"
   "Content-Type": "application/vnd.oci.image.manifest.v1+json"
   "X-Content-Type-Options": "nosniff"
< Response body:
{
  "schemaVersion": 2,
  "mediaType": "application/vnd.oci.image.manifest.v1+json",
  "config": {
    "mediaType": "application/vnd.oci.image.config.v1+json",
    "digest": "sha256:2863f9493a815fde4e60dd4a24b4034efeab2534bfdc69b7a1a440f00315e24d",
    "size": 623
  },
  "layers": [
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
      "digest": "sha256:dd0740468c9e19d93d459e4637a652c4fb8e1012c1adeac2e7311f14dcd210f6",
      "size": 3567894
    }
  ],
  "annotations": {
    "com.docker.official-images.bashbrew.arch": "arm32v6",
    "org.opencontainers.image.base.name": "scratch",
    "org.opencontainers.image.created": "2025-12-03T19:29:44Z",
    "org.opencontainers.image.revision": "24735c621e78574b49bb05b10dddac2497e423c2",
    "org.opencontainers.image.source": "https://github.com/alpinelinux/docker-alpine.git#24735c621e78574b49bb05b10dddac2497e423c2:armhf",
    "org.opencontainers.image.url": "https://hub.docker.com/_/alpine",
    "org.opencontainers.image.version": "3.23.0"
  }
}


[2025-12-07T02:35:21.941872423+05:30][DEBUG]: --> Request #14
> Request URL: "http://localhost:8080/v2/to/alpine/blobs/sha256:f386f0c85682b1a81b7db055fb8371703137f1b7d7ef53d2d3e74bf19124ee13"
> Request method: "HEAD"
> Request headers:
   "Authorization": "*****"
   "User-Agent": "oras/1.3.0"


[2025-12-07T02:35:21.942506368+05:30][DEBUG]: <-- Response #12
< Response Status: "200 OK"
< Response headers:
   "Set-Cookie": "*****"
   "X-Content-Type-Options": "nosniff"
   "Content-Length": "1022"
   "Docker-Content-Digest": "sha256:a107a3c031732299dd9dd607bb13787834db2de38cfa13f1993b7105e4814c60"
   "Docker-Distribution-Api-Version": "registry/2.0"
   "X-Request-Id": "2b9ae661-9520-4680-8724-b26f628141a5"
   "Content-Type": "application/vnd.oci.image.manifest.v1+json"
   "Date": "Sat, 06 Dec 2025 21:05:21 GMT"
   "Etag": "\"sha256:a107a3c031732299dd9dd607bb13787834db2de38cfa13f1993b7105e4814c60\""
< Response body:
{
  "schemaVersion": 2,
  "mediaType": "application/vnd.oci.image.manifest.v1+json",
  "config": {
    "mediaType": "application/vnd.oci.image.config.v1+json",
    "digest": "sha256:7acffee03fe864cd6b88219a1028855d6c912e7cf6fac633aa4307529fd0cc08",
    "size": 611
  },
  "layers": [
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
      "digest": "sha256:014e56e613968f73cce0858124ca5fbc601d7888099969a4eea69f31dcd71a53",
      "size": 3859315
    }
  ],
  "annotations": {
    "com.docker.official-images.bashbrew.arch": "amd64",
    "org.opencontainers.image.base.name": "scratch",
    "org.opencontainers.image.created": "2025-12-03T19:29:46Z",
    "org.opencontainers.image.revision": "24735c621e78574b49bb05b10dddac2497e423c2",
    "org.opencontainers.image.source": "https://github.com/alpinelinux/docker-alpine.git#24735c621e78574b49bb05b10dddac2497e423c2:x86_64",
    "org.opencontainers.image.url": "https://hub.docker.com/_/alpine",
    "org.opencontainers.image.version": "3.23.0"
  }
}


[2025-12-07T02:35:21.942621809+05:30][DEBUG]: --> Request #15
> Request URL: "http://localhost:8080/v2/to/alpine/manifests/sha256:8d133a316546e1e39d1837cb8c87a344b34cff218aca55e1313d9c4069dc0825"
> Request method: "HEAD"
> Request headers:
   "User-Agent": "oras/1.3.0"
   "Accept": "application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, application/vnd.oci.artifact.manifest.v1+json"
   "Authorization": "*****"


[2025-12-07T02:35:21.956218324+05:30][DEBUG]: <-- Response #14
< Response Status: "404 Not Found"
< Response headers:
   "X-Content-Type-Options": "nosniff"
   "X-Request-Id": "9edf90c8-b4e7-4268-90eb-59ed756f2ec4"
   "Content-Length": "157"
   "Content-Type": "application/json"
   "Date": "Sat, 06 Dec 2025 21:05:21 GMT"
   "Docker-Distribution-Api-Version": "registry/2.0"
   "Set-Cookie": "*****"
< Response body:
   No response body to print


[2025-12-07T02:35:21.956297865+05:30][DEBUG]: --> Request #16
> Request URL: "http://localhost:8080/v2/to/alpine/blobs/uploads/?mount=sha256:f386f0c85682b1a81b7db055fb8371703137f1b7d7ef53d2d3e74bf19124ee13&from=from/alpine"
> Request method: "POST"
> Request headers:
   "User-Agent": "oras/1.3.0"


[2025-12-07T02:35:21.958847775+05:30][DEBUG]: <-- Response #13
< Response Status: "200 OK"
< Response headers:
   "Content-Type": "application/vnd.oci.image.manifest.v1+json"
   "Date": "Sat, 06 Dec 2025 21:05:21 GMT"
   "Docker-Distribution-Api-Version": "registry/2.0"
   "X-Content-Type-Options": "nosniff"
   "X-Request-Id": "d7635ce0-9cf8-4230-87cb-5912b5d4504f"
   "Docker-Content-Digest": "sha256:5a3386629390bbc37645af9167beb2c97840319978211ca11388873e69b644c2"
   "Etag": "\"sha256:5a3386629390bbc37645af9167beb2c97840319978211ca11388873e69b644c2\""
   "Set-Cookie": "*****"
   "Content-Length": "566"
< Response body:
{
  "schemaVersion": 2,
  "mediaType": "application/vnd.oci.image.manifest.v1+json",
  "config": {
    "mediaType": "application/vnd.oci.image.config.v1+json",
    "digest": "sha256:b7bcdd83889ea21787dcacf4281f3728dec123702bf98009b5827020679c9d56",
    "size": 167
  },
  "layers": [
    {
      "mediaType": "application/vnd.in-toto+json",
      "digest": "sha256:fba5f1d3b130264107912992beb23ed29df1935ebddfc31971265ee5b4b0faa8",
      "size": 5465,
      "annotations": {
        "in-toto.io/predicate-type": "https://slsa.dev/provenance/v0.2"
      }
    }
  ]
}


[2025-12-07T02:35:21.958970176+05:30][DEBUG]: --> Request #17
> Request URL: "http://localhost:8080/v2/to/alpine/blobs/sha256:2863f9493a815fde4e60dd4a24b4034efeab2534bfdc69b7a1a440f00315e24d"
> Request method: "HEAD"
> Request headers:
   "Authorization": "*****"
   "User-Agent": "oras/1.3.0"


[2025-12-07T02:35:21.959651431+05:30][DEBUG]: <-- Response #16
< Response Status: "401 Unauthorized"
< Response headers:
   "Docker-Distribution-Api-Version": "registry/2.0"
   "Set-Cookie": "*****"
   "Www-Authenticate": "Bearer realm=\"http://localhost:8080/service/token\",service=\"harbor-registry\",scope=\"repository:to/alpine:pull,push repository:from/alpine:pull\""
   "X-Request-Id": "85f4172c-4c9f-4d26-afd7-7681525cb324"
   "Date": "Sat, 06 Dec 2025 21:05:21 GMT"
   "Content-Length": "168"
   "Content-Type": "application/json; charset=utf-8"
< Response body:
{"errors":[{"code":"UNAUTHORIZED","message":"unauthorized to access repository: to/alpine, action: push: unauthorized to access repository: to/alpine, action: push"}]}



[2025-12-07T02:35:21.959718082+05:30][DEBUG]: --> Request #18
> Request URL: "http://localhost:8080/service/token?scope=repository%3Afrom%2Falpine%3Apull&scope=repository%3Ato%2Falpine%3Apull%2Cpush&service=harbor-registry"
> Request method: "GET"
> Request headers:
   "Authorization": "*****"
   "User-Agent": "oras/1.3.0"


[2025-12-07T02:35:21.979615287+05:30][DEBUG]: <-- Response #18
< Response Status: "200 OK"
< Response headers:
   "Date": "Sat, 06 Dec 2025 21:05:21 GMT"
   "Content-Length": "1253"
   "Content-Type": "application/json; charset=utf-8"
   "Set-Cookie": "*****"
   "X-Request-Id": "dc174eca-2961-4401-9911-ab245a4931bd"
< Response body:
   Response body redacted due to potential credentials


[2025-12-07T02:35:21.979656788+05:30][DEBUG]: --> Request #19
> Request URL: "http://localhost:8080/v2/to/alpine/blobs/uploads/?mount=sha256:f386f0c85682b1a81b7db055fb8371703137f1b7d7ef53d2d3e74bf19124ee13&from=from/alpine"
> Request method: "POST"
> Request headers:
   "Authorization": "*****"
   "User-Agent": "oras/1.3.0"


[2025-12-07T02:35:21.98117653+05:30][DEBUG]: <-- Response #17
< Response Status: "404 Not Found"
< Response headers:
   "Content-Length": "157"
   "Content-Type": "application/json"
   "Date": "Sat, 06 Dec 2025 21:05:21 GMT"
   "Docker-Distribution-Api-Version": "registry/2.0"
   "Set-Cookie": "*****"
   "X-Content-Type-Options": "nosniff"
   "X-Request-Id": "0317cfdd-fb2d-45d7-a973-67e128df14a7"
< Response body:
   No response body to print


[2025-12-07T02:35:21.98121716+05:30][DEBUG]: --> Request #20
> Request URL: "http://localhost:8080/v2/to/alpine/blobs/uploads/?mount=sha256:2863f9493a815fde4e60dd4a24b4034efeab2534bfdc69b7a1a440f00315e24d&from=from/alpine"
> Request method: "POST"
> Request headers:
   "Authorization": "*****"
   "User-Agent": "oras/1.3.0"


[2025-12-07T02:35:21.984495606+05:30][DEBUG]: <-- Response #19
< Response Status: "401 Unauthorized"
< Response headers:
   "X-Request-Id": "02ba8e33-133a-44ac-b8f0-3c9b1545994d"
   "Date": "Sat, 06 Dec 2025 21:05:21 GMT"
   "Content-Length": "168"
   "Content-Type": "application/json; charset=utf-8"
   "Docker-Distribution-Api-Version": "registry/2.0"
   "Set-Cookie": "*****"
   "Www-Authenticate": "Basic realm=\"harbor\""
< Response body:
{"errors":[{"code":"UNAUTHORIZED","message":"unauthorized to access repository: to/alpine, action: push: unauthorized to access repository: to/alpine, action: push"}]}



[2025-12-07T02:35:21.984622827+05:30][ERROR]: <-- Response #15
Error in getting response: failed to perform "Mount" on destination: POST "http://localhost:8080/v2/to/alpine/blobs/uploads/?mount=sha256:f386f0c85682b1a81b7db055fb8371703137f1b7d7ef53d2d3e74bf19124ee13&from=from/alpine": response status code 401: unauthorized: unauthorized to access repository: to/alpine, action: push: unauthorized to access repository: to/alpine, action: push


[2025-12-07T02:35:21.984624017+05:30][ERROR]: <-- Response #20
Error in getting response: failed to perform "Mount" on destination: POST "http://localhost:8080/v2/to/alpine/blobs/uploads/?mount=sha256:f386f0c85682b1a81b7db055fb8371703137f1b7d7ef53d2d3e74bf19124ee13&from=from/alpine": response status code 401: unauthorized: unauthorized to access repository: to/alpine, action: push: unauthorized to access repository: to/alpine, action: push


Error from destination registry for "localhost:8080/to/alpine:latest": unauthorized: unauthorized to access repository: to/alpine, action: push: unauthorized to access repository: to/alpine, action: push

Here is corresponding harbor logs

[dev:backend:core] 2025-12-07T02:31:08+05:30 [DEBUG] [/server/middleware/log/log.go:36]: attach request id 1b9c461e-22f7-4313-a19d-e9aa0af18d2c to the logger for the request GET /service/token
[dev:backend:core] 2025-12-07T02:31:08+05:30 [DEBUG] [/core/service/token/token.go:37]: URL for token request: /service/token?scope=repository%3Afrom%2Falpine%3Apull%2Cpush&service=harbor-registry
[dev:backend:core] 2025-12-07T02:31:08+05:30 [DEBUG] [/core/service/token/creator.go:201]: scopes: [repository:from/alpine:pull,push]
[dev:backend:core] 2025-12-07T02:31:08+05:30 [DEBUG] [/core/service/token/authutils.go:51]: scopes: [repository:from/alpine:pull,push]
[dev:backend:core] 2025/12/07 02:31:08 Model:
[dev:backend:core] 2025/12/07 02:31:08 r.r: sub, obj, act
[dev:backend:core] 2025/12/07 02:31:08 p.p: sub, obj, act, eft
[dev:backend:core] 2025/12/07 02:31:08 e.e: some(where (p_eft == allow)) && !some(where (p_eft == deny))
[dev:backend:core] 2025/12/07 02:31:08 m.m: g(r_sub, p_sub) && keyMatch2(r_obj, p_obj) && (r_act == p_act || p_act == '*')
[dev:backend:core] 2025/12/07 02:31:08 g.g: _, _
[dev:backend:core] 2025/12/07 02:31:08 Policy:
[dev:backend:core] 2025/12/07 02:31:08 p: sub, obj, act, eft: [[rob_from+kumar /project/2/repository pull allow] [rob_from+kumar /project/2/repository push allow] [rob_from+kumar /project/2/repository pull allow]]
[dev:backend:core] 2025/12/07 02:31:08 g: _, _: []
[dev:backend:core] 2025/12/07 02:31:08 Role links for: g
[dev:backend:core] 2025/12/07 02:31:08
[dev:backend:core] 2025/12/07 02:31:08 Request: rob_from+kumar, /project/2/repository, delete ---> false
[dev:backend:core] 2025/12/07 02:31:08 Request: rob_from+kumar, /project/2/repository, scanner-pull ---> false
[dev:backend:core] 2025/12/07 02:31:08 Request: rob_from+kumar, /project/2/repository, pull ---> true
[dev:backend:core] 2025/12/07 02:31:08 Request: rob_from+kumar, /project/2/repository, push ---> true
[dev:backend:core] 2025-12-07T02:31:08+05:30 [DEBUG] [/core/service/token/authutils.go:102]: user: rob_from+kumar, access: &{repository  from/alpine [pull push]}
[dev:backend:core] 2025-12-07T02:31:08+05:30 [DEBUG] [/server/middleware/artifactinfo/artifact_info.go:55]: In artifact info middleware, url: /v2/from/alpine/manifests/sha256:51183f2cfa6320055da30872f211093f9ff1d3cf06f39a0bdb212314c5dc7375
....
[dev:backend:core] 2025-12-07T02:35:21+05:30 [DEBUG] [/server/middleware/log/log.go:36]: attach request id b3be47b3-cc43-4d0b-abcc-8909fa110f47 to the logger for the request GET /service/token
[dev:backend:core] 2025-12-07T02:35:21+05:30 [DEBUG] [/core/service/token/token.go:37]: URL for token request: /service/token?scope=repository%3Ato%2Falpine%3Apull%2Cpush&service=harbor-registry
[dev:backend:core] 2025-12-07T02:35:21+05:30 [DEBUG] [/core/service/token/creator.go:201]: scopes: [repository:to/alpine:pull,push]
[dev:backend:core] 2025-12-07T02:35:21+05:30 [DEBUG] [/core/service/token/authutils.go:51]: scopes: [repository:to/alpine:pull,push]
[dev:backend:core] 2025/12/07 02:35:21 Model:
[dev:backend:core] 2025/12/07 02:35:21 g.g: _, _
[dev:backend:core] 2025/12/07 02:35:21 r.r: sub, obj, act
[dev:backend:core] 2025/12/07 02:35:21 p.p: sub, obj, act, eft
[dev:backend:core] 2025/12/07 02:35:21 e.e: some(where (p_eft == allow)) && !some(where (p_eft == deny))
[dev:backend:core] 2025/12/07 02:35:21 m.m: g(r_sub, p_sub) && keyMatch2(r_obj, p_obj) && (r_act == p_act || p_act == '*')
[dev:backend:core] 2025/12/07 02:35:21 Policy:
[dev:backend:core] 2025/12/07 02:35:21 p: sub, obj, act, eft: [[rob_to+to /project/3/repository pull allow] [rob_to+to /project/3/repository push allow] [rob_to+to /project/3/repository pull allow]]
[dev:backend:core] 2025/12/07 02:35:21 g: _, _: []
[dev:backend:core] 2025/12/07 02:35:21 Role links for: g
[dev:backend:core] 2025/12/07 02:35:21
[dev:backend:core] 2025/12/07 02:35:21 Request: rob_to+to, /project/3/repository, pull ---> true
[dev:backend:core] 2025/12/07 02:35:21 Request: rob_to+to, /project/3/repository, push ---> true
[dev:backend:core] 2025/12/07 02:35:21 Request: rob_to+to, /project/3/repository, delete ---> false
[dev:backend:core] 2025/12/07 02:35:21 Request: rob_to+to, /project/3/repository, scanner-pull ---> false
[dev:backend:core] 2025-12-07T02:35:21+05:30 [DEBUG] [/core/service/token/authutils.go:102]: user: rob_to+to, access: &{repository  to/alpine [pull push]}
[dev:backend:core] 2025-12-07T02:35:21+05:30 [DEBUG] [/server/middleware/artifactinfo/artifact_info.go:55]: In artifact info middleware, url: /v2/to/alpine/manifests/sha256:51183f2cfa6320055da30872f211093f9ff1d3cf06f39a0bdb212314c5dc7375

It is clear that harbor treats both requests as same and given that push from a different repo works fine

oras logs for copying from different repo

❯ oras copy \
    'docker.io/library/alpine:latest' \
    'localhost:8080/from/alpine:latest' \
    --to-username='rob_from+kumar' --to-password='hZh1LVvP8LhclF8zfzyHRdgFkH0OcQkt' --debug
[2025-12-07T02:31:06.580804847+05:30][DEBUG]: --> Request #0
> Request URL: "https://registry-1.docker.io/v2/library/alpine/manifests/latest"
> Request method: "GET"
> Request headers:
   "Accept": "application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, application/vnd.oci.artifact.manifest.v1+json"
   "User-Agent": "oras/1.3.0"


[2025-12-07T02:31:07.413399444+05:30][DEBUG]: <-- Response #0
< Response Status: "401 Unauthorized"
< Response headers:
   "Content-Type": "application/json"
   "Content-Length": "157"
   "Docker-Distribution-Api-Version": "registry/2.0"
   "Www-Authenticate": "Bearer realm=\"https://auth.docker.io/token\",service=\"registry.docker.io\",scope=\"repository:library/alpine:pull\""
   "Strict-Transport-Security": "max-age=31536000"
   "Docker-Ratelimit-Source": "2406:7400:bb:7903::"
   "Date": "Sat, 06 Dec 2025 21:01:07 GMT"
< Response body:
{"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":[{"Type":"repository","Class":"","Name":"library/alpine","Action":"pull"}]}]}



[2025-12-07T02:31:07.413471395+05:30][DEBUG]: --> Request #1
> Request URL: "https://auth.docker.io/token?scope=repository%3Alibrary%2Falpine%3Apull&service=registry.docker.io"
> Request method: "GET"
> Request headers:
   "Authorization": "*****"
   "User-Agent": "oras/1.3.0"


[2025-12-07T02:31:08.253408381+05:30][DEBUG]: <-- Response #1
< Response Status: "200 OK"
< Response headers:
   "Date": "Sat, 06 Dec 2025 21:01:08 GMT"
   "Content-Type": "application/json"
   "X-Trace-Id": "625d3f0521c031c963fbd6dffa90df41"
   "X-Trace-Sampled": "true"
   "Strict-Transport-Security": "max-age=31536000"
< Response body:
   Response body redacted due to potential credentials


[2025-12-07T02:31:08.253512042+05:30][DEBUG]: --> Request #2
> Request URL: "https://registry-1.docker.io/v2/library/alpine/manifests/latest"
> Request method: "GET"
> Request headers:
   "Accept": "application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, application/vnd.oci.artifact.manifest.v1+json"
   "Authorization": "*****"
   "User-Agent": "oras/1.3.0"


[2025-12-07T02:31:08.557540279+05:30][DEBUG]: <-- Response #2
< Response Status: "200 OK"
< Response headers:
   "Date": "Sat, 06 Dec 2025 21:01:08 GMT"
   "Content-Type": "application/vnd.oci.image.index.v1+json"
   "Docker-Content-Digest": "sha256:51183f2cfa6320055da30872f211093f9ff1d3cf06f39a0bdb212314c5dc7375"
   "Etag": "\"sha256:51183f2cfa6320055da30872f211093f9ff1d3cf06f39a0bdb212314c5dc7375\""
   "Strict-Transport-Security": "max-age=31536000"
   "Ratelimit-Limit": "200;w=21600"
   "Ratelimit-Remaining": "191;w=21600"
   "Docker-Ratelimit-Source": "d7e585f7-7317-4499-9ce6-59cfc16f1d25"
   "Content-Length": "9218"
   "Docker-Distribution-Api-Version": "registry/2.0"
< Response body:
{"manifests":[{"annotations":{"com.docker.official-images.bashbrew.arch":"amd64","org.opencontainers.image.base.name":"scratch","org.opencontainers.image.created":"2025-12-03T19:29:46Z","org.opencontainers.image.revision":"24735c621e78574b49bb05b10dddac2497e423c2","org.opencontainers.image.source":"https:\/\/github.com\/alpinelinux\/docker-alpine.git#24735c621e78574b49bb05b10dddac2497e423c2:x86_64","org.opencontainers.image.url":"https:\/\/hub.docker.com\/_\/alpine","org.opencontainers.image.version":"3.23.0"},"digest":"sha256:a107a3c031732299dd9dd607bb13787834db2de38cfa13f1993b7105e4814c60","mediaType":"application\/vnd.oci.image.manifest.v1+json","platform":{"architecture":"amd64","os":"linux"},"size":1022},{"annotations":{"com.docker.official-images.bashbrew.arch":"amd64","vnd.docker.reference.digest":"sha256:a107a3c031732299dd9dd607bb13787834db2de38cfa13f1993b7105e4814c60","vnd.docker.reference.type":"attestation-manifest"},"digest":"sha256:135aac389d867b500874d83410faca4b15020bf2a96ed049c61d48723d8644c0","mediaType":"application\/vnd.oci.image.manifest.v1+json","platform":{"architecture":"unknown","os":"unknown"},"size":838},{"annotations":{"com.docker.official-images.bashbrew.arch":"arm32v6","org.opencontainers.image.base.name":"scratch","org.opencontainers.image.created":"2025-12-03T19:29:44Z","org.opencontainers.image.revision":"24735c621e78574b49bb05b10dddac2497e423c2","org.opencontainers.image.source":"https:\/\/github.com\/alpinelinux\/docker-alpine.git#24735c621e78574b49bb05b10dddac2497e423c2:armhf","org.opencontainers.image.url":"https:\/\/hub.docker.com\/_\/alpine","org.opencontainers.image.version":"3.23.0"},"digest":"sha256:d6218bcba4fa7414451627f556efe9ec47f9684336ccccf9a207836096578578","mediaType":"application\/vnd.oci.image.manifest.v1+json","platform":{"architecture":"arm","os":"linux","variant":"v6"},"size":1023},{"annotations":{"com.docker.official-images.bashbrew.arch":"arm32v6","vnd.docker.reference.digest":"sha256:d6218bcba4fa7414451627f556efe9ec47f9684336ccccf9a207836096578578","vnd.docker.reference.type":"attestation-manifest"},"digest":"sha256:5a3386629390bbc37645af9167beb2c97840319978211ca11388873e69b644c2","mediaType":"application\/vnd.oci.image.manifest.v1+json","platform":{"architecture":"unknown","os":"unknown"},"size":566},{"annotations":{"com.docker.official-images.bashbrew.arch":"arm32v7","org.opencontainers.image.base.name":"scratch","org.opencontainers.image.created":"2025-12-03T19:30:38Z","org.opencontainers.image.revision":"24735c621e78574b49bb05b10dddac2497e423c2","org.opencontainers.image.source":"https:\/\/github.com\/alpinelinux\/docker-alpine.git#24735c621e78574b49bb05b10dddac2497e423c2:armv7","org.opencontainers.image.url":"https:\/\/hub.docker.com\/_\/alpine","org.opencontainers.image.version":"3.23.0"},"digest":"sha256:8d133a316546e1e39d1837cb8c87a344b34cff218aca55e1313d9c4069dc0825","mediaType":"application\/vnd.oci.image.manifest.v1+json","platform":{"architecture":"arm","os":"linux","variant":"v7"},"size":1023},{"annotations":{"com.docker.official-images.bashbrew.arch":"arm32v7","vnd.docker.reference.digest":"sha256:8d133a316546e1e39d1837cb8c87a344b34cff218aca55e1313d9c4069dc0825","vnd.docker.reference.type":"attestation-manifest"},"digest":"sha256:8e6e34008e3a1a01366c863888f4e9c9e75f853ed43125001bcaa7713d9cd117","mediaType":"application\/vnd.oci.image.manifest.v1+json","platform":{"architecture":"unknown","os":"unknown"},"size":838},{"annotations":{"com.docker.official-images.bashbrew.arch":"arm64v8","org.opencontainers.image.base.name":"scratch","org.opencontainers.image.created":"2025-12-03T19:29:44Z","org.opencontainers.image.revision":"24735c621e78574b49bb05b10dddac2497e423c2","org.opencontainers.image.source":"https:\/\/github.com\/alpinelinux\/docker-alpine.git#24735c621e78574b49bb05b10dddac2497e423c2:aarch64","org.opencontainers.image.url":"https:\/\/hub.docker.com\/_\/alpine","org.opencontainers.image.version":"3.23.0"},"digest":"sha256:ed4a87b21407bf05ebacdb971243c59903b5b12e06c8e373de2b1b1955f5dd9e","mediaType":"application\/vnd.oci.image.manifest.v1+json","platform":{"architecture":"arm64","os":"linux","variant":"v8"},"size":1025},{"annotations":{"com.docker.official-images.bashbrew.arch":"arm64v8","vnd.docker.reference.digest":"sha256:ed4a87b21407bf05ebacdb971243c59903b5b12e06c8e373de2b1b1955f5dd9e","vnd.docker.reference.type":"attestation-manifest"},"digest":"sha256:fddefdcec3fff10cc8b0b737cb76a2a9a20e54781620b7f181ff7aca2952955d","mediaType":"application\/vnd.oci.image.manifest.v1+json","platform":{"architecture":"unknown","os":"unknown"},"size":838},{"annotations":{"com.docker.official-images.bashbrew.arch":"i386","org.opencontainers.image.base.name":"scratch","org.opencontainers.image.created":"2025-12-03T19:29:49Z","org.opencontainers.image.revision":"24735c621e78574b49bb05b10dddac2497e423c2","org.opencontainers.image.source":"https:\/\/github.com\/alpinelinux\/docker-alpine.git#24735c621e78574b49bb05b10dddac2497e423c2:x86","org.opencontainers.image.url":"https:\/\/hub.docker.com\/_\/alpine","org.opencontainers.image.version":"3.23.0"},"digest":"sha256:c78ded0fee4493809c8ca71d4a6057a46237763d952fae15ea418f6d14137f2d","mediaType":"application\/vnd.oci.image.manifest.v1+json","platform":{"architecture":"386","os":"linux"},"size":1018},{"annotations":{"com.docker.official-images.bashbrew.arch":"i386","vnd.docker.reference.digest":"sha256:c78ded0fee4493809c8ca71d4a6057a46237763d952fae15ea418f6d14137f2d","vnd.docker.reference.type":"attestation-manifest"},"digest":"sha256:b37b830f7e326d2f3af5186a748d9acef7232c6b9d645a4a7a00b959087d04c0","mediaType":"application\/vnd.oci.image.manifest.v1+json","platform":{"architecture":"unknown","os":"unknown"},"size":838},{"annotations":{"com.docker.official-images.bashbrew.arch":"ppc64le","org.opencontainers.image.base.name":"scratch","org.opencontainers.image.created":"2025-12-03T19:28:54Z","org.opencontainers.image.revision":"24735c621e78574b49bb05b10dddac2497e423c2","org.opencontainers.image.source":"https:\/\/github.com\/alpinelinux\/docker-alpine.git#24735c621e78574b49bb05b10dddac2497e423c2:ppc64le","org.opencontainers.image.url":"https:\/\/hub.docker.com\/_\/alpine","org.opencontainers.image.version":"3.23.0"},"digest":"sha256:21fdb77c56a0fb3f1e483a20de513c41a48b34fb52f2e0ea3a95f48fd5deddf6","mediaType":"application\/vnd.oci.image.manifest.v1+json","platform":{"architecture":"ppc64le","os":"linux"},"size":1025},{"annotations":{"com.docker.official-images.bashbrew.arch":"ppc64le","vnd.docker.reference.digest":"sha256:21fdb77c56a0fb3f1e483a20de513c41a48b34fb52f2e0ea3a95f48fd5deddf6","vnd.docker.reference.type":"attestation-manifest"},"digest":"sha256:3e4690e11f84fbddc0ad26fe129ddf2fbab351a6c06b800769a646095f3cae11","mediaType":"application\/vnd.oci.image.manifest.v1+json","platform":{"architecture":"unknown","os":"unknown"},"size":838},{"annotations":{"com.docker.official-images.bashbrew.arch":"riscv64","org.opencontainers.image.base.name":"scratch","org.opencontainers.image.created":"2025-12-03T19:29:17Z","org.opencontainers.image.revision":"24735c621e78574b49bb05b10dddac2497e423c2","org.opencontainers.image.source":"https:\/\/github.com\/alpinelinux\/docker-alpine.git#24735c621e78574b49bb05b10dddac2497e423c2:riscv64","org.opencontainers.image.url":"https:\/\/hub.docker.com\/_\/alpine","org.opencontainers.image.version":"3.23.0"},"digest":"sha256:a9e9814086aafbe9f687cf671a963233882537cf4eb1c239366cd98ebe3414e2","mediaType":"application\/vnd.oci.image.manifest.v1+json","platform":{"architecture":"riscv64","os":"linux"},"size":1025},{"annotations":{"com.docker.official-images.bashbrew.arch":"riscv64","vnd.docker.reference.digest":"sha256:a9e9814086aafbe9f687cf671a963233882537cf4eb1c239366cd98ebe3414e2","vnd.docker.reference.type":"attestation-manifest"},"digest":"sha256:1888a4ac62d41df1cb00d643d5b3217dddb8abb3709b004ab3aa73fa62e93f17","mediaType":"application\/vnd.oci.image.manifest.v1+json","platform":{"architecture":"unknown","os":"unknown"},"size":838},{"annotations":{"com.docker.official-images.bashbrew.arch":"s390x","org.opencontainers.image.base.name":"scratch","org.opencontainers.image.created":"2025-12-03T19:29:09Z","org.opencontainers.image.revision":"24735c621e78574b49bb05b10dddac2497e423c2","org.opencontainers.image.source":"https:\/\/github.com\/alpinelinux\/docker-alpine.git#24735c621e78574b49bb05b10dddac2497e423c2:s390x","org.opencontainers.image.url":"https:\/\/hub.docker.com\/_\/alpine","org.opencontainers.image.version":"3.23.0"},"digest":"sha256:2c4dfe4431612ed70fbbf79888190325eb3adeb9c7105ed3a7f507750300a893","mediaType":"application\/vnd.oci.image.manifest.v1+json","platform":{"architecture":"s390x","os":"linux"},"size":1021},{"annotations":{"com.docker.official-images.bashbrew.arch":"s390x","vnd.docker.reference.digest":"sha256:2c4dfe4431612ed70fbbf79888190325eb3adeb9c7105ed3a7f507750300a893","vnd.docker.reference.type":"attestation-manifest"},"digest":"sha256:ad2445a2c5f06b54237fff3bcce5e5729ff25b427a8e0428044df0f4504f6abd","mediaType":"application\/vnd.oci.image.manifest.v1+json","platform":{"architecture":"unknown","os":"unknown"},"size":838}],"mediaType":"application\/vnd.oci.image.index.v1+json","schemaVersion":2}


[2025-12-07T02:31:08.558252565+05:30][DEBUG]: --> Request #3
> Request URL: "http://localhost:8080/v2/from/alpine/manifests/sha256:51183f2cfa6320055da30872f211093f9ff1d3cf06f39a0bdb212314c5dc7375"
> Request method: "HEAD"
> Request headers:
   "Accept": "application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, application/vnd.oci.artifact.manifest.v1+json"
   "User-Agent": "oras/1.3.0"


[2025-12-07T02:31:08.563434918+05:30][DEBUG]: <-- Response #3
< Response Status: "401 Unauthorized"
< Response headers:
   "Date": "Sat, 06 Dec 2025 21:01:08 GMT"
   "Content-Length": "152"
   "Content-Type": "application/json; charset=utf-8"
   "Docker-Distribution-Api-Version": "registry/2.0"
   "Set-Cookie": "*****"
   "Www-Authenticate": "Bearer realm=\"http://localhost:8080/service/token\",service=\"harbor-registry\",scope=\"repository:from/alpine:pull\""
   "X-Request-Id": "aafd8901-ca2b-49c7-aa6a-5457779c0e0c"
< Response body:
   No response body to print


[2025-12-07T02:31:08.563511039+05:30][DEBUG]: --> Request #4
> Request URL: "http://localhost:8080/service/token?scope=repository%3Afrom%2Falpine%3Apull%2Cpush&service=harbor-registry"
> Request method: "GET"
> Request headers:
   "Authorization": "*****"
   "User-Agent": "oras/1.3.0"


[2025-12-07T02:31:08.581073893+05:30][DEBUG]: <-- Response #4
< Response Status: "200 OK"
< Response headers:
   "X-Request-Id": "1b9c461e-22f7-4313-a19d-e9aa0af18d2c"
   "Date": "Sat, 06 Dec 2025 21:01:08 GMT"
   "Content-Length": "1205"
   "Content-Type": "application/json; charset=utf-8"
   "Set-Cookie": "*****"
< Response body:
   Response body redacted due to potential credentials


[2025-12-07T02:31:08.581127613+05:30][DEBUG]: --> Request #5
> Request URL: "http://localhost:8080/v2/from/alpine/manifests/sha256:51183f2cfa6320055da30872f211093f9ff1d3cf06f39a0bdb212314c5dc7375"
> Request method: "HEAD"
> Request headers:
   "Accept": "application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, application/vnd.oci.artifact.manifest.v1+json"
   "Authorization": "*****"
   "User-Agent": "oras/1.3.0"


[2025-12-07T02:31:08.588613245+05:30][DEBUG]: <-- Response #5
< Response Status: "200 OK"
< Response headers:
   "Content-Length": "9218"
   "Content-Type": "application/vnd.oci.image.index.v1+json"
   "Date": "Sat, 06 Dec 2025 21:01:08 GMT"
   "X-Request-Id": "31b70a47-cbab-456c-bfd2-7060a54785b0"
   "Docker-Content-Digest": "sha256:51183f2cfa6320055da30872f211093f9ff1d3cf06f39a0bdb212314c5dc7375"
   "Docker-Distribution-Api-Version": "registry/2.0"
   "Etag": "\"sha256:51183f2cfa6320055da30872f211093f9ff1d3cf06f39a0bdb212314c5dc7375\""
   "Set-Cookie": "*****"
   "X-Content-Type-Options": "nosniff"
< Response body:
   No response body to print


[2025-12-07T02:31:08.588676146+05:30][DEBUG]: --> Request #6
> Request URL: "http://localhost:8080/v2/from/alpine/manifests/latest"
> Request method: "PUT"
> Request headers:
   "User-Agent": "oras/1.3.0"
   "Content-Type": "application/vnd.oci.image.index.v1+json"
   "Authorization": "*****"


[2025-12-07T02:31:08.613173366+05:30][DEBUG]: <-- Response #6
< Response Status: "201 Created"
< Response headers:
   "Docker-Distribution-Api-Version": "registry/2.0"
   "Location": "http://localhost:8080/v2/from/alpine/manifests/sha256:51183f2cfa6320055da30872f211093f9ff1d3cf06f39a0bdb212314c5dc7375"
   "Set-Cookie": "*****"
   "X-Content-Type-Options": "nosniff"
   "X-Request-Id": "32c8b37e-cd65-49e7-8385-f95674b48535"
   "Content-Length": "0"
   "Date": "Sat, 06 Dec 2025 21:01:08 GMT"
   "Docker-Content-Digest": "sha256:51183f2cfa6320055da30872f211093f9ff1d3cf06f39a0bdb212314c5dc7375"
< Response body:
   No response body to print


Copied [registry] docker.io/library/alpine:latest => [registry] localhost:8080/from/alpine:latest
Digest: sha256:51183f2cfa6320055da30872f211093f9ff1d3cf06f39a0bdb212314c5dc7375

bupd avatar Dec 06 '25 21:12 bupd

Okay digging deeper feels like

This failed because ORAS attempted Cross-Repository Blob Mount. because the source and destination are on the same server. The destination user (rob_to+to) tried to mount data residing in the from project, But the problem is the second user doesnt have access to the first project so he cannot access the blobs from the first project. so harbor denies the request for cross blob mounting.

So if cross mounting fails ORAS should fallback to normal push. which in this case it doesn't do - which is a problem to fix.

This works if the --to-user has permission for both the projects. -- which is hardly the case in private container registries like harbor.

TODO

  • [ ] ORAS should have a fallback method for supporting this. (#1924)

bupd avatar Dec 06 '25 21:12 bupd

I have opened PR #1924 fixing this issue and adding a fallback mentioned in above comment

Please leave a review.

Thanks

bupd avatar Dec 07 '25 01:12 bupd

It looks like #1924 is the same fix as #1903 (don't mount when it's the same registry but different credentials), which has been merged already. Currently when I reproduce the error on Harbor, the failure comes from Resolve, not Mount. But the error message I get is different from MalteHei's error (error on source instead of destination).

wangxiaoxuan273 avatar Dec 09 '25 09:12 wangxiaoxuan273