Announcing ORAS Go v3.0.0 - unified registry configuration, mirrors, policy, and more
The ORAS community is excited to announce ORAS Go v3.0.0, the next major release of the Go library for working with OCI artifacts and registries. v3 makes the library understand the same configuration files that container tools such as Podman, Buildah, and Skopeo already use, so your Go application can share registry settings, credentials, mirrors, and trust policy with the rest of your toolchain.
This post walks through what is new in v3, the breaking changes to expect when you upgrade from v2, and what happens to v2 from here.
Getting startedโ
v3 is published under a new module path, github.com/oras-project/oras-go/v3, replacing oras.land/oras-go/v2:
go get github.com/oras-project/oras-go/v3
v3 requires Go 1.26 or later.
The v2 and v3 module paths are distinct, so both can be imported by the same application while you migrate package by package. Values from the two major versions are distinct Go types, so don't pass them between versions without converting them explicitly.
What's new in v3โ
Unified configurationโ
The new registry/remote/config package loads the configuration files the container ecosystem already uses, in one call:
- Docker credentials (
~/.docker/config.json) and containersauth.json registries.conffor registry aliases, mirrors, and blocked or insecure registriespolicy.jsonfor allow and deny trust policyregistries.dfor signature lookaside storage- Certificate directories (
certs.d)
The default search paths follow containers/image, so a host that is already set up for Podman works with ORAS Go without any further configuration.
Configured clients with ClientBuilderโ
remote.ClientBuilder turns that configuration into ready-to-use clients, registries, and repositories, applying TLS, retries, credentials, token flow, logging, policy, and mirror settings in one place:
cfg, err := config.LoadConfigs()
if err != nil {
return err
}
props, err := cfg.RegistryProperties("registry.example.com/team/app")
if err != nil {
return err
}
store, err := cfg.CredentialStore(credentials.StoreOptions{})
if err != nil {
return err
}
builder := remote.NewClientBuilder()
builder.CredentialStore = store
repo, err := builder.NewRepository(props)
if err != nil {
return err
}
desc, err := oras.Copy(ctx, repo, "v1", memory.New(), "v1", oras.DefaultCopyOptions)
Registry mirrorsโ
Repositories created from registry properties try any applicable mirrors from registries.conf for read operations, and fall back to the primary registry. Writes always go to the primary.
Policy and signature verificationโ
The registry/remote/policy package enforces policy.json allow and deny rules. The registry/remote/signature package verifies OpenPGP simple signatures stored in registries.d lookaside storage.
Content verificationโ
content.Verify, oras.Verify, oras.VerifyGraph, and oras.ExtendedVerify check content integrity without writing it anywhere, and collect per-node results in a report. oci.Verify sweeps an OCI image layout and reports missing, unreferenced, and leftover blobs, as well as invalid oci-layout or index.json files.
New ways to pushโ
- Unsafe push: the
content.UnsafePusherinterface pushes content whose digest or size isn't known up front, such as a stream or a tar built on the fly, and computes both while writing. It is bounded byMaxUnsafePushBytes, 4 GiB by default. - Chunked and single-request uploads:
MaxChunkSizeenables OCI chunked uploads, andSingleRequestPushThresholdpushes small blobs in a singlePOST. Both are opt-in. - Configurable digest algorithms:
PackManifest,PushBytes,TagBytesN, and the file store can produce SHA-384 or SHA-512 digests.
And moreโ
- Faster copies: the default copy concurrency rises from 3 to 8, and the opt-in
SkipExistsCheckBelowSizeskips the destination existence check for small blobs. - Content caching:
content/cache.CacheReadOnlyTargetwraps a read-only target with a local content store, andcontent/cache.NewFromEnvenables it from theORAS_CACHEenvironment variable. - Hierarchical credential matching: credentials can be matched by the longest namespace prefix, as with
auth.jsonin Podman and Buildah. - HTTP diagnostics:
remote.NewLoggingTransportaddsslog-based debug logging that redacts sensitive headers. - Tag deletion:
remote.Repository.Untagdeletes a tag without deleting the manifest it points to. - Better copy errors:
oras.CopyError.Descriptoridentifies the content that failed to copy. - Smarter retries: the default retry policy now honors the
Retry-Afterheader on retryable responses such as 429 and 503, and retries connection resets.
Breaking changes and upgrading from v2โ
v3 is a major release, and existing v2 code needs changes to compile. The most common ones are:
- Registry owns shared settings. A
remote.Repositorynow points to its parentremote.Registry, which holds the HTTP client, the plain-HTTP setting, the warning handler, and the metadata limit. For example,repo.Clientbecomesrepo.Registry.Clientandrepo.PlainHTTPbecomesrepo.Registry.PlainHTTP.repo.Referencebecomes the read-onlyrepo.Reference(). - Credentials have their own package. Credential types and helpers moved from
registry/remote/authtoregistry/remote/credentials. For example,auth.Credentialbecomescredentials.Credential, andauth.Client.Credentialbecomesauth.Client.CredentialFunc. - Typed scopes. Authentication scopes are
auth.Scopevalues instead of strings, and scope hints are keyed by resource rather than by host. - Token flow.
ForceAttemptOAuth2was replaced by a pluggableauth.Client.TokenFetcher. With a username and password, v3 tries the OAuth2 password grant first and falls back to the distribution-spec token flow when the token server doesn't support it. - References. New code should use
properties.Reference.registry.Referencenow keeps the tag and the digest separately, and is deprecated. - File store.
file.NewWithFallbackLimitandfile.NewWithFallbackStorageare replaced byfile.NewWithOptions.
Some behavior changes even when your code compiles unchanged:
- Retries wait longer. The maximum backoff rises from 3 to 10 seconds, and
Retry-Afteris honored for up to 60 seconds, so without a context deadline the worst-case wait rises from about 7 seconds to about 5 minutes. To fail fast, setClientBuilder.RetryPolicyor the transport'sPolicy. remote.NewRepositoryrejects a reference that includes a tag or digest, such aslocalhost:5000/example:v1.- The file store writes directory annotations under the project-owned
land.oras.contentkeys as well as the legacyio.deis.oras.contentkeys. v2 and the ORAS CLI can still unpack directories pushed by v3, but manifest digests for artifacts containing a directory differ between v2 and v3. - Native credential stores are detected by default.
oras.ExtendedCopylooks up predecessors concurrently.oci.Store.GCkeeps unreachable blobs that were modified within the last hour.
The migration guide covers every change, with before-and-after code and the steps to restore v2 behavior where that is possible. Read it before you upgrade.
What about v2?โ
ORAS Go v2 remains supported for one year after the v3.0.0 release. During that time, the v2 branch continues to receive security fixes and important bug fixes, and follows Go's security policy, supporting the two latest Go releases. New features land in v3 only. We encourage new projects to start on v3 and existing projects to plan their migration.
With the v3.0.0 release, ORAS Go v1 has reached end of life and no longer receives updates, including security fixes. If you still depend on v1, plan to move to v3.
Learn moreโ
Thank youโ
Thank you to everyone who contributed code, reviews, bug reports, and feedback through the v3 release candidates. Questions and feedback are welcome on the CNCF Slack #oras channel and in GitHub Discussions.
