Contents

OCI Distribution API

JWT Auth Required
GET /v2/ API version check (Docker registry ping)

Response

HTTP/1.1 200 OK {}

Returns empty JSON object. If unauthenticated, returns 401 with Www-Authenticate header containing the token endpoint URL.

GET /v2/{tenant}/{project}/{name}/manifests/{reference} Pull a manifest by tag or digest

Path Parameters

ParamDescription
tenantTenant namespace (use _ for default)
projectProject name
nameRepository name
referenceTag name or sha256:... digest

Example

curl -H "Authorization: Bearer $TOKEN" \ https://registry.example.com/v2/_/myproject/myapp/manifests/latest
PUT /v2/{tenant}/{project}/{name}/manifests/{reference} Push a manifest (requires Push permission)

Headers

HeaderValue
Content-Typeapplication/vnd.oci.image.manifest.v1+json
POST /v2/{tenant}/{project}/{name}/blobs/uploads/ Initiate a blob upload session

Response

HTTP/1.1 202 Accepted Location: /v2/{t}/{p}/{n}/blobs/uploads/{uuid} Docker-Upload-UUID: {uuid}
GET /v2/{tenant}/{project}/{name}/tags/list List tags for a repository

Response

{"name": "_/myproject/myapp", "tags": ["latest", "v1.0", "v2.0"]}
GET /v2/_catalog List all repositories

Response

{"repositories": ["_/myproject/myapp", "_/myproject/api", "demo/default/nginx"]}

Authentication & Tokens

Public
GET /auth/token?service=registry10x-registry&scope=repository:t/p/n:pull Docker-compatible token exchange (Basic auth)

Authentication

Send credentials via Authorization: Basic base64(user:pass) header. Docker clients send this automatically after receiving a 401 challenge.

Query Parameters

ParamDescription
serviceRegistry service name (from Www-Authenticate)
scopeDocker scope: repository:tenant/project/repo:pull,push

Response

{"token": "eyJhbG...", "access_token": "eyJhbG...", "expires_in": 300, "issued_at": "2026-04-12T..."}
POST /auth/token/refresh Refresh an access token using a refresh token

Request Body

{"refresh_token": "rt_..."}

Response

{"token": "eyJhbG...", "expires_in": 300, "refresh_token": "rt_new..."}
POST /auth/token/revoke Revoke a token immediately

Request Body

{"jti": "token-id-to-revoke"}
POST /auth/introspect Token introspection (RFC 7662)

Request Body

{"token": "eyJhbG..."}

Response

{"active": true, "sub": "admin", "tenant_id": "uuid", "exp": 1712930400, "scope": "pull,push"}
GET /auth/.well-known/jwks.json JWKS public key endpoint for token verification

Response

{"keys": [{"kty": "RSA", "use": "sig", "kid": "...", "alg": "RS256", "n": "...", "e": "AQAB"}]}

OIDC / SSO Login

Browser Flow
GET /auth/oidc/login?provider={name}&redirect_uri={uri} Initiate OIDC Authorization Code flow (redirects to IdP)

Query Parameters

ParamDescription
providerOIDC provider name from config (e.g., azure-ad)
redirect_uriWhere to redirect after login (receives ?token=...)

Flow

1. Generates PKCE code verifier + challenge
2. Stores session state
3. Redirects user to IdP authorization endpoint
4. IdP authenticates user and redirects to /auth/oidc/callback

GET /auth/oidc/callback?code={code}&state={state} OIDC callback (exchanges code for tokens)

Flow

1. Validates state against stored session
2. Exchanges authorization code for ID token
3. Extracts sub, email, groups from ID token
4. Auto-provisions user if new
5. Applies group-to-role mappings
6. Issues Registry10x JWT
7. Redirects to redirect_uri?token=eyJ...

CI/CD OIDC Federation

OIDC Token
POST /auth/ci/token Exchange a CI OIDC token for a Registry10x token

Request Body

{ "token": "eyJhbG...", // CI provider's OIDC JWT "provider": "github", // github | gitlab | k8s | custom "scope": { "tenant": "_", "project": "myproject", "actions": ["pull", "push"] } }

Supported Providers

ProviderIssuerSubject Format
GitHub Actionshttps://token.actions.githubusercontent.comgithub:org/repo
GitLab CIhttps://gitlab.comgitlab:group/project
Kubernetes SAK8s API server OIDC endpointk8s:namespace:sa-name

GitHub Actions Example

# In .github/workflows/build.yml jobs: push: permissions: id-token: write steps: - uses: actions/checkout@v4 - run: | TOKEN=$(curl -sH "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \ "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=registry10x-registry" | jq -r .value) REGISTRY_TOKEN=$(curl -sX POST https://registry.example.com/auth/ci/token \ -H "Content-Type: application/json" \ -d "{\"token\":\"$TOKEN\",\"provider\":\"github\",\"scope\":{\"tenant\":\"_\",\"project\":\"myproj\",\"actions\":[\"pull\",\"push\"]}}" \ | jq -r .token) docker login registry.example.com -u ci -p "$REGISTRY_TOKEN" docker push registry.example.com/_/myproj/myapp:latest
POST /auth/github-actions/token GitHub Actions OIDC (legacy endpoint, use /auth/ci/token instead)

Request Body

{"token": "eyJhbG...", "scope": {"tenant": "_", "project": "myproj", "actions": ["pull", "push"]}}

SCIM 2.0 Provisioning (RFC 7644)

Bearer Token

SCIM endpoints enable Azure AD, Okta, and OneLogin to automatically provision and deprovision registry users. All requests require Authorization: Bearer {scim_token}.

GET /scim/v2/Users List all provisioned users

Response

{ "schemas": ["urn:ietf:params:scim:api:messages:2.0:ListResponse"], "totalResults": 2, "Resources": [ {"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"], "id": "john.doe", "userName": "john.doe", "active": true, "emails": [{"value": "john@example.com", "primary": true}], "groups": [{"value": "registry10x-devs", "display": "registry10x-devs"}]} ] }
POST /scim/v2/Users Provision a new user (pre-create before first login)

Request Body

{ "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"], "userName": "john.doe", "displayName": "John Doe", "active": true, "emails": [{"value": "john@example.com", "type": "work", "primary": true}], "groups": [{"value": "registry10x-devs", "display": "registry10x-devs"}] }
PATCH /scim/v2/Users/{id} Partial update (Azure AD uses this for deactivation)

Deactivate User (Instant Offboarding)

{ "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"], "Operations": [{"op": "replace", "path": "active", "value": false}] }

Setting active=false immediately removes the user and all their access policies. They cannot docker pull from this moment onward.

DELETE /scim/v2/Users/{id} Deprovision a user (removes all access)

Response

HTTP/1.1 204 No Content
POST /scim/v2/Groups Create a SCIM group

Request Body

{ "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"], "displayName": "registry10x-devs", "members": [{"value": "john.doe", "display": "John Doe"}] }
PATCH /scim/v2/Groups/{id} Update group membership (add/remove members)

Add Members

{ "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"], "Operations": [{"op": "add", "path": "members", "value": [{"value": "jane.doe", "display": "Jane Doe"}]}] }

Remove Members

{ "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"], "Operations": [{"op": "remove", "path": "members[value eq \"john.doe\"]"}] }

Robot Accounts

Admin Auth
POST /api/v1/robot-accounts Create a robot account for machine identity

Request Body

{ "name": "ci-pipeline", "description": "GitHub Actions CI", "tenant": "_", "project": "myproject", "role": "maintainer", "expires_in_days": 90 }

Response

{ "id": "uuid", "name": "ci-pipeline", "secret": "nrc_...", // Only shown once! "docker_login": "docker login registry.example.com -u robot:ci-pipeline -p nrc_..." }
GET /api/v1/robot-accounts List all robot accounts
DELETE /api/v1/robot-accounts/{id} Delete a robot account

Dashboard & Monitoring

Basic Auth
GET/dashboardWeb dashboard (HTML)
GET/api/images?q={search}&limit=200Image browser with wildcard search
GET/api/statsRegistry statistics (uptime, push/pull counts)
GET/api/systemSystem metrics (CPU, RAM, disk)
GET/api/ha-statusHA multi-region health status
GET/api/usersList provisioned users (proxied from auth service)
GET/api/groupsList groups and role mappings
GET/healthHealth check (no auth required)
GET/metricsPrometheus metrics (no auth required)

Internal Replication

Internal Only

These endpoints are used for cross-region replication between Registry10x instances. They are served on both the main port and the internal port (5002).

POST/internal/replicate/manifestReceive a replicated manifest from another region
POST/internal/replicate/blobReceive a replicated blob from another region
POST/internal/replicate/deleteReceive a delete event from another region
GET/internal/replication/statusReplication health status for all regions

Integration Guides

Azure AD / Entra ID Setup

Connect Registry10x to Azure AD for SSO and SCIM provisioning.

  1. Register an application in Azure AD → App Registrations → New Registration
  2. Set redirect URI to https://registry.example.com/auth/oidc/callback
  3. Note the Application (client) ID and create a client secret
  4. Configure OIDC provider in Registry10x config:
    oidc_providers: [{issuer_url: "https://login.microsoftonline.com/{tenant}/v2.0", client_id: "...", client_secret: "...", groups_claim: "groups"}]
  5. Enable SCIM: In Azure AD → Enterprise Applications → your app → Provisioning → Set Tenant URL to https://registry.example.com/scim/v2 and enter the SCIM bearer token
  6. Map AD groups to roles: group_role_mappings: [{group: "registry10x-admins", tenant: "_", role: "admin"}]
  7. Test: Click "Login with Azure AD" on the dashboard or run docker login

Okta SCIM Setup

Configure Okta to auto-provision and deprovision Registry10x users.

  1. In Okta Admin → Applications → Create App Integration → SCIM 2.0
  2. Set SCIM connector base URL to https://registry.example.com/scim/v2
  3. Authentication: Bearer token (use enterprise.scim.bearer_token from config)
  4. Enable provisioning actions: Create Users, Update User Attributes, Deactivate Users
  5. Map Okta groups to push groups → these sync to Registry10x group-role mappings
  6. Assign users/groups to the application → Okta pushes SCIM events immediately

GitHub Actions OIDC (Zero Secrets)

Push images from GitHub Actions without storing registry credentials.

  1. Configure GitHub OIDC in Registry10x: github_oidc: {allowed_orgs: ["your-org"], default_role: "maintainer"}
  2. Add id-token: write permission to your workflow
  3. Request OIDC token: curl "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=registry10x-registry"
  4. Exchange for registry token: POST /auth/ci/token with the OIDC JWT
  5. Use the token with docker login → docker push

Kubernetes ImagePullSecrets

Configure Kubernetes pods to pull images from Registry10x.

  1. Create a robot account: POST /api/v1/robot-accounts with desired scope
  2. Create the k8s secret: kubectl create secret docker-registry registry10x-secret --docker-server=registry.example.com --docker-username=robot:ci-pipeline --docker-password=nrc_...
  3. Reference in your deployment: imagePullSecrets: [{name: registry10x-secret}]
  4. For auto-rotation, use External Secrets Operator to call /auth/credential-exchange