Architecture

Components, request flow, failure handling, and security boundaries.

Components

cmd/oci-mirror-proxy        entry point; calls internal/cli
internal/cli                commands, flag parsing, output formatting
internal/app                composition root: owns configuration state, pools, route table
internal/frontend           Registry v2 HTTP surface dockerd talks to
internal/route              Dispatcher, middleware chaining, atomically swapped route table
internal/middleware         logging, blob resumption
internal/scheduler          strategies: sequential and hedged; the Candidate contract
internal/upstream           one upstream (endpoint), its breaker and transport; the reconciling pool
internal/auth               per-upstream authorizers: none, basic, token challenge flow
internal/secret             secret references and redaction
internal/config             configuration model, defaults, validation, file I/O
internal/ociapi             registry path parsing; a leaf package
internal/admin              admin API over a unix socket, and its client
internal/daemonconf         dockerd integration: detection, pure rendering, transactional apply
internal/unixhttp           HTTP over unix sockets, shared by admin and daemonconf
internal/fsutil             atomic file writes
internal/cache              reserved extension point for a blob cache (interface only)

Request flow

  1. dockerd sends GET /v2/library/nginx/manifests/latest.
  2. frontend parses the path with ociapi, which derives the alias (docker.io), the kind (manifest), the repository and the reference. Anything outside the registry grammar is a 404. Only GET and HEAD are accepted.
  3. frontend selects the route configured for that registry alias.
  4. The dispatcher chain runs: logging, then blob resumption, then the route.
  5. The route takes a snapshot of enabled upstreams from its pool, then calls the strategy.
  6. The strategy tries upstreams. Each attempt goes through Endpoint.Attempt, which checks the breaker, sends the request, applies the first-byte and overall timeouts, answers a single authentication challenge if one is issued, and records the outcome.
  7. The first acceptable (2xx) response is returned. Its headers are copied through an allowlist, and its body is streamed.

Failure handling

EventHandling
Connection refused or silent upstreamCounted as a failure. The next upstream is tried. Bounded by the first-byte timeout.
5xx or 429Counted as a failure.
401 with a Bearer challengeHandled once: a token is fetched and the request is retried. A second 401 is returned and counts as an alive upstream with no content.
404Not a health problem. The next upstream is tried. If every attempted upstream answered 404, the proxy answers 404 and dockerd falls back.
Breaker openThe attempt is skipped without touching the network.
Mid-stream failure of a blobThe download continues from the current offset on another upstream, if that upstream returns a 206 whose Content-Range starts at the same offset. Otherwise the original error is surfaced. Up to three resumptions per download.
Client disconnectThe request context is cancelled, the strategy stops, and nothing more is written.
Every upstream failed, not with 404502 UNAVAILABLE. dockerd falls back.

Strategies

Sequential (default): one upstream at a time, in configured order. The next one is contacted only after the previous one has failed.

Hedged (default): the first upstream starts immediately. Whenever race.delay passes with no answer, another upstream is started, up to race.max_parallel in flight. A failed attempt starts the next upstream immediately. The first acceptable response wins, and the other attempts are cancelled.

Configuration reload

Upstream changes take effect without restarting the proxy. Unchanged upstreams keep their breaker state and counters. Invalid configuration is rejected while the previous configuration continues serving requests.

Reload does not interrupt existing HTTP transfers or change their resume candidates. Later HTTP requests, including those in the same docker pull, use the new configuration. See changing the configuration.

Security boundaries

  • The data plane is unauthenticated and listens on loopback by default.
  • The admin API is reachable only through a socket protected by file permissions.
  • Upstream credentials are scoped to their upstream. The client's Authorization is dropped at the frontend. Redirects that cross hosts drop the Authorization header, because that is standard Go net/http behavior.
  • Content integrity is end-to-end: dockerd verifies each digest. A faulty upstream can cause a failed pull, but it cannot cause an image with wrong content to be accepted.

On this page