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
- dockerd sends
GET /v2/library/nginx/manifests/latest. frontendparses the path withociapi, 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.frontendselects the route configured for that registry alias.- The dispatcher chain runs: logging, then blob resumption, then the route.
- The route takes a snapshot of enabled upstreams from its pool, then calls the strategy.
- 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. - The first acceptable (2xx) response is returned. Its headers are copied through an allowlist, and its body is streamed.
Failure handling
| Event | Handling |
|---|---|
| Connection refused or silent upstream | Counted as a failure. The next upstream is tried. Bounded by the first-byte timeout. |
| 5xx or 429 | Counted as a failure. |
| 401 with a Bearer challenge | Handled once: a token is fetched and the request is retried. A second 401 is returned and counts as an alive upstream with no content. |
| 404 | Not a health problem. The next upstream is tried. If every attempted upstream answered 404, the proxy answers 404 and dockerd falls back. |
| Breaker open | The attempt is skipped without touching the network. |
| Mid-stream failure of a blob | The 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 disconnect | The request context is cancelled, the strategy stops, and nothing more is written. |
| Every upstream failed, not with 404 | 502 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
Authorizationis dropped at the frontend. Redirects that cross hosts drop theAuthorizationheader, because that is standard Gonet/httpbehavior. - 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.