No description
  • Go 99.7%
  • CUE 0.3%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Jeff a1f620b3cd
Merge pull request #14 from docker/ociselector
Add oci.Interface implementation that allows for configuring multiple oci.Interface implementations behind a selector function.
2026-07-16 15:35:08 -04:00
.github/workflows chore: pin GitHub Actions to commit SHA 2026-04-20 14:48:49 +02:00
cmd/ocisrv Rename for docker/oci 2026-04-03 11:33:20 -04:00
internal Add Docker Auth Wrapper to check for auth entries for all Docker Hub domains 2026-04-15 14:00:30 -04:00
ociauth update ociclient to stop relying on internal/ocirequest, instead build requests in a way that is easier to extend 2026-05-27 14:04:04 -04:00
ociclient Implement an experimental "copy image" function which calls an extension on Docker Hub to mount a manifest from one repo to another 2026-06-17 15:13:22 -04:00
ocidebug Rename for docker/oci 2026-04-03 11:33:20 -04:00
ocidigest Add package for calculating OCI digests. This unblocks copying in OCI model types. 2026-05-21 16:29:11 -04:00
ocifilter Add oci.Interface implementation that allows for configuring multiple oci.Interface implementations behind a selector function. 2026-07-14 17:23:18 -04:00
ocilarge Add package for calculating OCI digests. This unblocks copying in OCI model types. 2026-05-21 16:29:11 -04:00
ocilayout implement ocilayout implementation for oci.Interface 2026-06-01 13:43:59 -04:00
ocimem Update ocimem to be compatible with non-canonical algorithms 2026-06-26 17:14:40 -04:00
ociref Update Parse implementation to match the comments - particularly that Parse acts differently than ParseRelative 2026-06-25 15:08:11 -04:00
ociserver Add package for calculating OCI digests. This unblocks copying in OCI model types. 2026-05-21 16:29:11 -04:00
ocitest Add package for calculating OCI digests. This unblocks copying in OCI model types. 2026-05-21 16:29:11 -04:00
ociunify Add package for calculating OCI digests. This unblocks copying in OCI model types. 2026-05-21 16:29:11 -04:00
.gitignore initial pruning and renaming 2026-02-19 20:24:18 -05:00
.golangci.yml Flatten code so the main package isn't nested 2026-03-12 18:16:19 -04:00
error.go Flatten code so the main package isn't nested 2026-03-12 18:16:19 -04:00
error_test.go Flatten code so the main package isn't nested 2026-03-12 18:16:19 -04:00
func.go Flatten code so the main package isn't nested 2026-03-12 18:16:19 -04:00
go.mod Add package for calculating OCI digests. This unblocks copying in OCI model types. 2026-05-21 16:29:11 -04:00
go.sum Add package for calculating OCI digests. This unblocks copying in OCI model types. 2026-05-21 16:29:11 -04:00
interface.go Add package for calculating OCI digests. This unblocks copying in OCI model types. 2026-05-21 16:29:11 -04:00
iter.go Flatten code so the main package isn't nested 2026-03-12 18:16:19 -04:00
iter_test.go Flatten code so the main package isn't nested 2026-03-12 18:16:19 -04:00
LICENSE licensing: fix up licensing and copyright 2023-09-28 20:44:47 +00:00
models.go Add package for calculating OCI digests. This unblocks copying in OCI model types. 2026-05-21 16:29:11 -04:00
models_test.go Add package for calculating OCI digests. This unblocks copying in OCI model types. 2026-05-21 16:29:11 -04:00
README.md implement ocilayout implementation for oci.Interface 2026-06-01 13:43:59 -04:00
Taskfile.yml Flatten code so the main package isn't nested 2026-03-12 18:16:19 -04:00
valid.go Rename for docker/oci 2026-04-03 11:33:20 -04:00

OCI Go modules

This repository holds functionality related to OCI (Open Container Initiative).

The top-level package (oci) defines a Go interface that encapsulates the operations provided by an OCI registry — reading blobs and manifests, pushing content, listing tags, and more.

Full reference documentation can be found at pkg.go.dev/github.com/docker/oci.

The aim is to provide an ergonomic interface for defining and layering OCI registry implementations.

Although the API is fairly stable, it's still in v0 currently, so incompatible changes can't be ruled out.

The code was originally derived from cue-labs/oci which was originally derived from the go-containerregistry package, but has considerably diverged since then.

Purpose

There are other libraries that exist for working with OCI registries, and they are great! However, as we look to invest in Docker Hub and add new features ahead of them getting standardized into the OCI spec, we need a library that can be used to interact with those features.

Packages

Package Description
oci Core interface (oci.Interface) and types shared across all packages.
ociclient HTTP client that implements oci.Interface against a remote OCI registry.
ociserver HTTP server that serves the OCI distribution protocol on top of any oci.Interface.
ocimem Lightweight in-memory oci.Interface implementation, useful for testing and caching.
ocilayout Filesystem-backed oci.Interface implementation for OCI Image Layout directories, including shared and per-repository layouts.
ociauth Authentication transport implementing the Docker/OCI token flow, plus helpers for loading credentials from Docker config files.
ocifilter Wrappers that expose restricted or transformed views of a registry (read-only, immutable, namespace prefix, custom access control).
ociunify Combines two registries into a single unified oci.Interface, with configurable read policy.
ocilarge Parallel multi-range download (and upload) for large blobs, automatically tuning chunk size to available bandwidth.
ocidebug Registry wrapper that logs every operation — useful for tracing and debugging.
ociref Reference and digest parsing/validation utilities.

The server currently passes the OCI distribution conformance tests.

Usage

List tags on Docker Hub

package main

import (
	"context"
	"fmt"

	"github.com/docker/oci/ociauth"
	"github.com/docker/oci/ociclient"
)

func main() {
	cf, err := ociauth.Load(nil)
	if err != nil {
		panic(err)
	}

	ocl, err := ociclient.New("index.docker.io", &ociclient.Options{
		Transport: ociauth.NewStdTransport(ociauth.StdTransportParams{
			Config: cf,
		}),
	})
	if err != nil {
		panic(err)
	}

	tags := ocl.Tags(context.Background(), "library/alpine", nil)
	for tag, err := range tags {
		if err != nil {
			panic(err)
		}
		fmt.Printf("%s\n", tag)
	}
}

Fetch a manifest by tag

package main

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/docker/oci"
	"github.com/docker/oci/ociauth"
	"github.com/docker/oci/ociclient"
)

func main() {
	cf, err := ociauth.Load(nil)
	if err != nil {
		panic(err)
	}

	ocl, err := ociclient.New("index.docker.io", &ociclient.Options{
		Transport: ociauth.NewStdTransport(ociauth.StdTransportParams{
			Config: cf,
		}),
	})
	if err != nil {
		panic(err)
	}

	// Fetch the manifest for alpine:latest.
	r, err := ocl.GetTag(context.Background(), "library/alpine", "latest")
	if err != nil {
		panic(err)
	}
	defer r.Close()

	fmt.Printf("media type: %s\n", r.Descriptor().MediaType)
	fmt.Printf("digest:     %s\n", r.Descriptor().Digest)

	var manifest oci.IndexOrManifest
	if err := json.NewDecoder(r).Decode(&manifest); err != nil {
		panic(err)
	}
	fmt.Printf("config digest: %s\n", manifest.Config.Digest)
	for i, layer := range manifest.Layers {
		fmt.Printf("layer[%d]: %s (%d bytes)\n", i, layer.Digest, layer.Size)
	}
}

Pull a blob (image layer)

package main

import (
	"context"
	"io"
	"os"

	"github.com/docker/oci/ociauth"
	"github.com/docker/oci/ociclient"
)

func main() {
	cf, err := ociauth.Load(nil)
	if err != nil {
		panic(err)
	}

	ocl, err := ociclient.New("index.docker.io", &ociclient.Options{
		Transport: ociauth.NewStdTransport(ociauth.StdTransportParams{
			Config: cf,
		}),
	})
	if err != nil {
		panic(err)
	}

	// Pull a specific blob by digest.
	const repo = "library/alpine"
	const layerDigest = "sha256:bca4290a96390d7a6fc6f2f9929370d06f8dfcacba591c76e3d5c5044e7f420c"

	blob, err := ocl.GetBlob(context.Background(), repo, layerDigest)
	if err != nil {
		panic(err)
	}
	defer blob.Close()

	f, err := os.Create("layer.tar.gz")
	if err != nil {
		panic(err)
	}
	defer f.Close()

	if _, err := io.Copy(f, blob); err != nil {
		panic(err)
	}
}

Serve a local in-memory registry over HTTP

package main

import (
	"net/http"

	"github.com/docker/oci/ocimem"
	"github.com/docker/oci/ociserver"
)

func main() {
	backend := ocimem.New()
	handler := ociserver.New(backend, nil)
	http.ListenAndServe(":5000", handler)
}