API Endpoints Reference
Angos implements the OCI Distribution Specification v1.1 plus extension endpoints.
OCI Distribution API
Base path: /v2/
API Version Check
GET /v2/
Returns 200 OK if the registry is available. Used for authentication challenges.
Blobs
HEAD /v2/{namespace}/blobs/{digest}
GET /v2/{namespace}/blobs/{digest}
Check existence or download a blob by digest.
DELETE /v2/{namespace}/blobs/{digest}
Delete a blob.
Blob Upload
POST /v2/{namespace}/blobs/uploads/
Start a new blob upload. Returns 202 Accepted with Location header.
Query parameters:
digest- Complete upload in single request (monolithic)mount- Mount blob from another repository
GET /v2/{namespace}/blobs/uploads/{uuid}
Get upload status.
PATCH /v2/{namespace}/blobs/uploads/{uuid}
Upload a chunk. Use Content-Range header for chunked uploads.
PUT /v2/{namespace}/blobs/uploads/{uuid}?digest={digest}
Complete the upload with final digest.
DELETE /v2/{namespace}/blobs/uploads/{uuid}
Cancel an upload.
Manifests
HEAD /v2/{namespace}/manifests/{reference}
GET /v2/{namespace}/manifests/{reference}
Check existence or download a manifest. {reference} can be a tag or digest.
PUT /v2/{namespace}/manifests/{reference}
Push a manifest.
DELETE /v2/{namespace}/manifests/{reference}
Delete a manifest by tag or digest.
Tags
GET /v2/{namespace}/tags/list
List tags for a namespace.
Query parameters:
n- Maximum number of resultslast- Pagination marker
Catalog
GET /v2/_catalog
List repositories.
Query parameters:
n- Maximum number of resultslast- Pagination marker
Referrers
GET /v2/{namespace}/referrers/{digest}
List manifests that reference a subject digest.
Query parameters:
artifactType- Filter by artifact type
Extension API (not part of the OCI specification)
Base path: /v2/_ext/
List Repositories
GET /v2/_ext/_repositories
List all configured repositories with namespace counts.
Response:
{
"repositories": [
{
"name": "library",
"namespaces": 15,
"is_pull_through": true,
"immutable_tags": true
}
]
}
List Namespaces
GET /v2/_ext/{repository}/_namespaces
List namespaces within a repository.
Response:
{
"namespaces": [
{
"name": "nginx",
"manifests": 25,
"uploads": 0
}
]
}
List Revisions
GET /v2/_ext/{namespace}/_revisions
List all manifest revisions with tags and parent relationships.
Response:
{
"revisions": [
{
"digest": "sha256:abc123...",
"media_type": "application/vnd.oci.image.index.v1+json",
"tags": ["latest", "1.25.0"],
"parent": null,
"pushed_at": 1703123456,
"last_pulled_at": 1703200000
}
]
}
List Uploads
GET /v2/_ext/{namespace}/_uploads
List blob uploads in progress.
Response:
{
"uploads": [
{
"uuid": "123e4567-e89b-12d3-a456-426614174000",
"size": 1048576,
"started_at": 1703123456
}
]
}
Health and Metrics
Health Check (Liveness)
GET /healthz
Returns 200 OK if the service is running. Use this for Kubernetes liveness probes to detect hung processes.
Readiness Check
GET /readyz
Returns 200 OK if the storage backend is healthy and ready to handle requests. Checks accessibility of the blob store, metadata store, and lock backend.
Use this for Kubernetes readiness probes to detect when a replica is unable to serve traffic.
Success Response:
{"status":"ready"}
Add ?verbose=true to the query string to include conditional operation capabilities (S3 metadata store only):
GET /readyz?verbose=true
Verbose Success Response (S3 metadata store):
{
"status": "ready",
"conditional": {
"put_if_none_match": true,
"put_if_match": true,
"delete_if_match": true
}
}
Verbose Success Response (filesystem metadata store):
{"status":"ready"}
Service Unavailable Response (503):
{"status":"not_ready","error":"storage backend not ready: ..."}
Prometheus Metrics
GET /metrics
Returns metrics in Prometheus exposition format.
Web UI
When the UI is enabled, non-API paths serve the web interface.
UI Routes
| Route | Description |
|---|---|
/ | Repository list |
/{repository} | Namespace list |
/{repository}/{namespace} | Manifest list |
/{repository}/{namespace}:{tag} | Manifest details by tag |
/{repository}/{namespace}@{digest} | Manifest details by digest |
UI Configuration
GET /_ui/config
Returns UI configuration.
Response:
{
"name": "My Container Registry"
}
Authentication
All endpoints (except /healthz and /readyz) require authentication when access policies are configured.
Methods
Basic Authentication:
Authorization: Basic base64(username:password)
Bearer Token (OIDC):
Authorization: Bearer <jwt-token>
OIDC via Basic Auth (Docker compatibility):
Authorization: Basic base64(provider-name:jwt-token)
When the username matches an OIDC provider name, the password is validated as a JWT token. This enables Docker clients to authenticate with OIDC tokens:
echo "$OIDC_TOKEN" | docker login registry.example.com \
--username github-actions --password-stdin
mTLS:
Present a client certificate during TLS handshake.
Authentication Flow
- Client makes unauthenticated request
- Server returns
401 UnauthorizedwithWWW-Authenticateheader - Client retries with credentials
- Server validates and processes request
Error Responses
Errors follow OCI Distribution error format:
{
"errors": [
{
"code": "MANIFEST_UNKNOWN",
"message": "manifest unknown",
"detail": "sha256:abc123..."
}
]
}
Error Codes
| Code | HTTP Status | Description |
|---|---|---|
BLOB_UNKNOWN | 404 | Blob does not exist |
BLOB_UPLOAD_INVALID | 400 | Invalid upload |
BLOB_UPLOAD_UNKNOWN | 404 | Upload session not found |
DIGEST_INVALID | 400 | Invalid digest format |
MANIFEST_INVALID | 400 | Invalid manifest content |
MANIFEST_UNKNOWN | 404 | Manifest does not exist |
NAME_INVALID | 400 | Invalid repository name |
NAME_UNKNOWN | 404 | Repository not found |
SIZE_INVALID | 400 | Size mismatch |
TAG_INVALID | 400 | Invalid tag |
TAG_IMMUTABLE | 409 | Tag cannot be overwritten |
UNAUTHORIZED | 401 | Authentication required |
DENIED | 403 | Access denied by policy |
UNSUPPORTED | 415 | Unsupported operation |