Regc
View SourceRegc 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"}
]
endCheck 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}
endFor 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
| Operation | Direct image manifest | Image index |
|---|---|---|
check_image/2 without a platform | Selects the manifest; platform remains unknown | Validates the root and reports image.platforms |
check_image/2 with a platform | Selects the manifest; platform remains unknown until inspection | Resolves the matching child |
inspect_image/2 without a platform | Inspects the config and infers the platform | Returns :platform_required |
inspect_image/2 with a platform | Inspects and validates the config platform | Resolves, 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?
}
endReferences 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:7References 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
| Option | Default | Purpose |
|---|---|---|
:platform | omitted | Select os/architecture[/variant], a qualifier map, or :local |
:max_manifest_bytes | 4 MiB | Limit each downloaded manifest |
:max_config_bytes | 4 MiB | Limit the downloaded image configuration |
:max_index_depth | 8 | Limit nested index traversal during platform resolution |
:max_index_manifests | 64 | Limit child manifests fetched while resolving nested indexes |
:max_redirects | 5 | Limit HTTP redirects followed for one metadata request |
:connect_timeout | 5 seconds | Limit connection establishment time |
:timeout | 15 seconds | Limit total HTTP response time, including redirects |
:allow_insecure | false | Permit explicit plain HTTP for a non-local registry |
:resolver | built-in DNS | Use an arity-2 DNS resolver function |
:transport | built-in HTTP | Use 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:
- requested, registry-provided, and parent-descriptor manifest digests;
- parent-declared manifest sizes;
- image-config digest and size;
- media types and required manifest/config structure;
- exact platform selection and, during inspection, config-platform agreement when requested; and
- configured response-size and nested-index limits.
Current scope
Supported:
- anonymous HTTPS registry access, including OCI bearer-token challenge flows used by Docker Hub and GHCR;
- OCI image indexes and image manifests;
- Docker manifest lists and schema-2 image manifests;
- nested image indexes;
- SHA-256 content verification; and
- verified image-config retrieval, including inline config data.
Not currently supported:
- registry pulls requiring user-supplied credentials;
- image-layer downloads; or
- digest algorithms other than SHA-256.
Development
The project targets Elixir ~> 1.20 and has no runtime package dependencies.
mix test
mix format --check-formatted
mix compile --warnings-as-errors