Regc

Regc validates OCI and Docker image metadata without pulling image layers. It can check whether a tag points to valid manifest content, list the platforms advertised by a multi-platform image, resolve one exact platform, and retrieve its verified image configuration.

Installation

Add regc to the dependencies in mix.exs:

def deps do
[
{:regc, "~> 0.0.1"}
]
end

Check a tag

Platform selection is optional. This makes the default safe for checking a list of tags whose platforms are not known in advance:

case Regc.check_image("registry.example.com/team/app:1.2.3") do
{:ok, image} ->
Enum.map(image.platforms, fn platform ->
{platform.os, platform.architecture, platform.variant}
end)
{:error, error} ->
{error.stage, error.code, error.message}
end

For an image index, this request fetches and validates only the root manifest. It does not choose a child based on the machine running Regc:

image.platform # => nil
image.selected_ref # => nil
image.manifest # => nil
image.config_descriptor # => nil
image.layer_descriptors # => []

The root metadata remains available through image.root_ref and image.root_manifest.

If the tag points directly to a single-image manifest, that manifest is selected automatically. Its platform is not known until its configuration is inspected.

Select a platform

Pass an OCI platform string to resolve an index to an exact image manifest:

{:ok, image} =
Regc.check_image("registry.example.com/team/app:1.2.3",
platform: "linux/amd64"
)
image.root_ref.digest
image.selected_ref.digest
image.config_descriptor.digest
Enum.map(image.layer_descriptors, & &1.digest)

Platforms have the form os/architecture or os/architecture/variant, such as linux/amd64 or linux/arm/v7.

When an index distinguishes images by additional OCI platform qualifiers, pass a map so selection remains exact:

Regc.check_image(reference,
platform: %{
os: "windows",
architecture: "amd64",
os_version: "10.0.20348.2402",
os_features: ["win32k"]
}
)

The map also accepts :variant and :features. CPU :features can be verified only when selecting a descriptor from an image index; requesting them for a direct image manifest returns :platform_features_unverifiable. If multiple manifests match the base OS and architecture but differ by qualifiers, Regc returns :ambiguous_platform instead of choosing one arbitrarily.

Use platform: :local only when selecting the OS and architecture of the runtime machine is intentional:

Regc.check_image(reference, platform: :local)

Inspect the image configuration

inspect_image/2 fetches, verifies, and decodes the selected image configuration:

{:ok, image} =
Regc.inspect_image("registry.example.com/team/app:1.2.3",
platform: "linux/amd64"
)
image.platform
image.config.created
image.config.os
image.config.architecture
image.config.raw["config"]["Cmd"]

An explicit platform is required when the tag points to an image index. For a direct single-image manifest, the platform can be omitted; Regc reads it from the verified configuration.

If only the creation timestamp is needed:

{:ok, created} =
Regc.image_created("registry.example.com/team/app:1.2.3",
platform: "linux/amd64"
)

Platform behavior

OperationDirect image manifestImage index
check_image/2 without a platformSelects the manifest; platform remains unknownValidates the root and reports image.platforms
check_image/2 with a platformSelects the manifest; platform remains unknown until inspectionResolves the matching child
inspect_image/2 without a platformInspects the config and infers the platformReturns :platform_required
inspect_image/2 with a platformInspects and validates the config platformResolves, inspects, and validates the matching child

Errors

All public functions return {:ok, result} or {:error, %Regc.Oci.Error{}}. Errors include the failing stage, a stable code, a human-readable message, and whether retrying may succeed:

case Regc.check_image(reference) do
{:ok, image} ->
image
{:error, error} ->
%{
stage: error.stage,
code: error.code,
message: error.message,
retryable?: error.retryable?
}
end

References and transport

Docker-style shorthand references are expanded using Docker Hub defaults:

redis:latest -> docker.io/library/redis:latest
bitnami/redis:7 -> docker.io/bitnami/redis:7

References must include a tag or SHA-256 digest. Registry-qualified references remain supported:

registry.example.com/team/app:1.2.3
registry.example.com/team/app@sha256:0123456789abcdef...

HTTPS is the default. Plain HTTP is accepted automatically for local development registries:

Regc.check_image("http://localhost:5000/team/app:dev")

For any other plaintext registry, both the http:// scheme and allow_insecure: true are required.

Redirects are followed within a bounded request chain. HTTPS targets are accepted; plaintext targets still require allow_insecure: true, except when both endpoints are local development hosts. Authorization, proxy authorization, and cookie headers are removed when the redirect changes origin. Redirects from public hosts to targets that resolve to non-global addresses, including private, loopback, link-local, and special-use ranges, are rejected, and the validated target addresses are pinned to the redirected connection.

Options

OptionDefaultPurpose
:platformomittedSelect os/architecture[/variant], a qualifier map, or :local
:max_manifest_bytes4 MiBLimit each downloaded manifest
:max_config_bytes4 MiBLimit the downloaded image configuration
:max_index_depth8Limit nested index traversal during platform resolution
:max_index_manifests64Limit child manifests fetched while resolving nested indexes
:max_redirects5Limit HTTP redirects followed for one metadata request
:connect_timeout5 secondsLimit connection establishment time
:timeout15 secondsLimit total HTTP response time, including redirects
:allow_insecurefalsePermit explicit plain HTTP for a non-local registry
:resolverbuilt-in DNSUse an arity-2 DNS resolver function
:transportbuilt-in HTTPUse an arity-1 request function or a module implementing Regc.Oci.Transport
:transport_options[]Pass a keyword list to a transport module through Regc.Oci.Options

Custom transports

An arity-1 transport function receives a %Regc.Oci.Transport.Request{} and returns {:ok, response} or {:error, reason}. A successful response can be a %Regc.Oci.Transport.Response{} or a map with :status, :headers, and :body fields.

For a reusable transport with configuration, implement the Regc.Oci.Transport behaviour:

defmodule MyTransport do
@behaviour Regc.Oci.Transport
alias Regc.Oci.{Options, Transport}
alias Regc.Oci.Transport.{Request, Response}
@impl Transport
def request(%Request{} = request, %Options{} = options) do
client = Keyword.fetch!(options.transport_options, :client)
# Perform one GET request with client, respecting request.max_body_bytes.
{:ok, %Response{status: 200, headers: %{}, body: "..."}}
end
end
Regc.check_image(reference,
transport: MyTransport,
transport_options: [client: client]
)

The resolver function receives (host, family), where family is :inet or :inet6, and returns {:ok, addresses} or {:error, reason}. Custom transports that follow redirects or resolve hosts independently are responsible for equivalent timeout, redirect, TLS, and network-target protections.

Verification

Regc verifies:

Current scope

Supported:

Not currently supported:

Development

The project targets Elixir ~> 1.20 and has no runtime package dependencies.

mix test
mix format --check-formatted
mix compile --warnings-as-errors