Standards
peryx targets the specifications a modern container registry and its clients rely on. The
OCI distribution spec defines the /v2/ HTTP API; the
image spec defines the manifests and blobs that flow over it. peryx
answers the version check with Docker-Distribution-API-Version: registry/2.0.
What a docker pull asks for
Knowing the request sequence makes the table below concrete. For docker pull alpine:latest against any
distribution-spec registry:
Every hop names a spec: the routes are the distribution spec, the manifest and blob shapes are the image spec, each digest is the content-addressing both rely on. peryx sits on both sides of this conversation, a registry to your clients and a client to its upstreams, which is why the table below mixes "served" and "parsed".
| Standard | Role in peryx |
|---|---|
| Distribution spec | The /v2/ pull-and-push API: manifests, blobs, chunked uploads, cross-repo mount, tag listing; served to clients and spoken upstream |
| Image spec: manifest | The manifest JSON listing a config and layer descriptors; stored byte-for-byte and addressed by the sha256 of those exact bytes |
| Image spec: image index | Multi-platform indexes and the referrers response, served as application/vnd.oci.image.index.v1+json |
| Image spec: descriptor | mediaType, digest, size, artifactType, and annotations on every referenced object |
| Referrers API | GET /v2/<name>/referrers/<digest> returning the manifests that declared <digest> as their subject (OCI-Subject on push) |
| Docker manifest v2, schema 2 | The Docker-media-type manifests and image indexes that Docker Hub and older clients still emit; parsed and re-served |
| Token authentication | The 401 + WWW-Authenticate: Bearer handshake, served to peryx's own callers (it mints JWTs at GET /v2/token and enforces their grants on each resource request) and run as a client against an upstream that demands it |
Digests are the contract
Every manifest and blob is addressed by sha256:<hex> over its exact bytes. peryx stores a manifest byte-for-byte, so
the Docker-Content-Digest a client verifies always matches what it pushed or pulled, and a blob shared by ten images
is stored once. A blob digest in any other algorithm is rejected with 400 DIGEST_INVALID rather than served
unverified; a manifest an upstream advertises under another algorithm is re-addressed under peryx's own sha256, covered
in content digest algorithms.
Graceful degradation
Upstreams differ in what they emit. Docker Hub and GHCR
serve Docker schema-2 media types where a private registry may serve OCI ones; peryx parses both and preserves the
stored Content-Type on the way back out, so a client sees the media type the source produced. A pull-through that
fails or answers unexpectedly returns 502 with code UNKNOWN, so a gateway fault is never mistaken for a client error
the puller would not retry.
Pulls take no authentication unless an OCI index restricts access, in which case peryx mints Bearer tokens at
/v2/token and enforces each token's grants when it serves the request; on the pull-through path the same handshake
runs the other way, where peryx fetches and caches a token per scope against an upstream that challenges it. Writes
require a Basic-auth token, the target hosted index's upload_token, which is why docker login against peryx uses the
token as the password.
In practice
- The machinery that serves these: architecture
- The routes they map to: HTTP endpoints