Registry Credentials
Store credentials for pulling private task images
/registry-credentials stores encrypted credentials for pulling private task images. It supports
Google Artifact Registry (GAR), GitHub Container Registry (GHCR), and Amazon Elastic Container
Registry (ECR).
The examples below use $BASE and $KEY. Get a key from
Harbor Hub API Key.
export BASE=https://ofhuhcpkvzjlejydnvyd.supabase.co/functions/v1
export KEY=sk-harbor-...Storing a Credential
POST /registry-credentials validates the credential, encrypts it, and returns metadata without ever
returning the secret. Every request takes a registry_host and a display_name of at most 100
characters, which is how jobs later select the credential. The provider is inferred from the host, so
provider is optional; if you do send it, it has to agree with the host.
| Provider | Host | Secret fields |
|---|---|---|
gar | <region>-docker.pkg.dev | service_account_json |
ghcr | ghcr.io | username, token |
ecr | <account-id>.dkr.ecr.<region>.amazonaws.com | access_key_id, secret_access_key |
A GAR credential:
{
"registry_host": "us-east1-docker.pkg.dev",
"display_name": "prod-puller",
"service_account_json": "{...service account JSON...}"
}Use a service account with read access to the repository you need, typically
roles/artifactregistry.reader scoped to that repository.
A GHCR credential:
{
"registry_host": "ghcr.io",
"display_name": "github-packages-puller",
"username": "octocat",
"token": "ghp_..."
}Use a classic GitHub personal access token limited to read:packages.
An ECR credential:
{
"registry_host": "123456789012.dkr.ecr.us-east-1.amazonaws.com",
"display_name": "production-ecr-puller",
"access_key_id": "AKIAIOSFODNN7EXAMPLE",
"secret_access_key": "..."
}Use a long-lived IAM access key limited to the ECR pull permissions your task images need. Temporary
session credentials, the ones beginning ASIA, are rejected because they need a session token that
Harbor does not store.
A successful response looks like:
{
"id": "00000000-0000-0000-0000-000000000003",
"org_id": "00000000-0000-0000-0000-00000000000a",
"provider": "gar",
"registry_host": "us-east1-docker.pkg.dev",
"display_name": "prod-puller",
"fingerprint": "harbor-puller@my-project.iam.gserviceaccount.com",
"status": "active",
"created_at": "2026-08-13T14:30:00Z"
}Reusing an existing display_name returns 409 replacement_required with the existing record's
fingerprint; confirm it with supersede_credential_id exactly as with secrets. Each organization can
hold at most 20 active registry credentials.
For GAR, build the request from the service account file to avoid shell quoting problems:
curl -sS -X POST "$BASE/registry-credentials" \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d "$(jq -n --rawfile sa ./gar-puller.json \
'{registry_host:"us-east1-docker.pkg.dev", display_name:"prod-puller", service_account_json:$sa}')"Listing and Revoking Registry Credentials
GET /registry-credentials lists active credentials and returns metadata only. Use status=revoked
or status=all to include other states.
curl -sS "$BASE/registry-credentials?status=active" \
-H "Authorization: Bearer $KEY"DELETE /registry-credentials revokes a credential by default. Set purge to true only when the
stored record should be permanently deleted.
curl -sS -X DELETE "$BASE/registry-credentials" \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{"credential_id":"00000000-0000-0000-0000-000000000003","purge":false}'Using a Credential In a Job
Pin a stored credential by display name or ID in registry_credentials, as a sibling of config:
{
"config": {
"job_name": "private-image-job",
"agents": [
{
"name": "terminus-2",
"model_name": "openai/gpt-4o",
"secrets": ["OPENAI_API_KEY"]
}
],
"datasets": [
{
"name": "my-org/private-image-dataset",
"ref": "latest"
}
]
},
"registry_credentials": {
"us-east1-docker.pkg.dev": "prod-puller"
},
"job_secrets": {
"OPENAI_API_KEY": "sk-..."
}
}That mapping reads: for images on us-east1-docker.pkg.dev, use the stored credential named
prod-puller. Keys have to be supported registry hosts, and you can send at most 20 entries.
If exactly one active credential is available for a host you can leave registry_credentials out
entirely. When several match, the launch has to pick one by display name, credential ID, or the
owner-qualified display name Harbor shows.