ORAS Go

repository·main·Indexed 42 days ago

https://github.com/oras-project/oras-go

A Go library for managing OCI artifacts, providing unified APIs for interacting with OCI-compliant registries, local file systems, and in-memory stores. It is compliant with the OCI Image Format and Distribution Specifications, supporting operations such as pushing, pulling, and copying artifacts modeled as Directed Acyclic Graphs (DAGs). Available versions include v2 (stable), v1 (maintenance), and v3 (development).

Tokens
16.7K
Snippets
44
Records
77
Agent score
55%

What's inside ORAS Go

  1. Major additions and features in v3

    main

    ORAS Go v3 introduces several significant architectural improvements and new capabilities:

    • Unified configuration: registry/remote/config.LoadConfigs can load Docker credentials, containers auth.json, registries.conf, policy.json, registries.d, and certificate directories. It follows the containers/image default path strategy, with an experimental UAPI strategy also available.
    • Configured clients: remote.ClientBuilder is used to construct clients, registries, and repositories with support for TLS, retries, credentials, token flow, warnings, logging, policy, and mirror settings.
    • Registry mirrors: Repositories created from registry properties will attempt to use applicable mirrors for read operations, falling back to the primary registry if necessary. Writes always target the primary registry.
    • Policy and signatures: The registry/remote/policy package enforces allow/deny policies, while registry/remote/signature verifies OpenPGP simple signatures using registries.d lookaside storage.
    • Content caching: content/cache.CacheReadOnlyTarget wraps a read-only target with a content store. content/cache.NewFromEnv allows configuration via the ORAS_CACHE environment variable.
    • Hierarchical credential matching: Setting credentials.StoreOptions.Hierarchical enables longest-prefix namespace matching when reading credentials from plaintext config files (compatible with containers auth.json used by Podman/Buildah).
    • HTTP diagnostics: remote.NewLoggingTransport provides slog-based debug logging featuring sensitive-header redaction and bounded response bodies.
    • Tag deletion: remote.Repository.Untag allows deleting a tag without deleting the referenced manifest.
    • Copy diagnostics and policy: oras.CopyError.Descriptor identifies failing content during copy operations, and oras.CopyOptions.PolicyCheck can reject a copy before content transfer begins.
  2. Overview of Content Stores in oras-go v2

    main

    In oras-go v2, a content store is an implementation of the GraphTarget interface. Content stores are responsible for managing the storage and retrieval of artifacts, manifests, and blobs. The library provides four built-in implementations depending on your use case:

    • Memory Store: Stores everything in memory. Best for caching or testing.
    • OCI Store: Stores content in the OCI-Image layout on the file system. Best for local caches or maintaining a local copy of a remote repository.
    • File Store: Stores location-addressable content (files/directories) on the file system. Best for packaging arbitrary files.
    • Repository Store: Communicates with remote artifact repositories (e.g., ghcr.io, docker.io) via OCI distribution APIs.
  3. Referrers API and Referrers Tag Schema fallback

    main

    When interacting with artifact registries, predecessor finding may be limited. Many registries support discovery via the Referrers API, but if that is unavailable, oras-go falls back to the Referrers Tag Schema to simulate the behavior.

    In these specific registry-based systems:

    • The Predecessors function effectively operates as Referrers.
    • ExtendedCopy is restricted by these relationships. For example, if you call ExtendedCopy(m0) and the only predecessor relationship is a subject link from m2, it will find m2 and copy its graph, but it will not find unrelated nodes like an Index i0 that might also point to m0 via a manifests relationship.
  4. How artifacts are modeled in oras-go

    main

    In oras-go v2, artifacts are modeled as Directed Acyclic Graphs (DAGs) stored in Content-Addressable Storages (CASs).

    Key concepts of this model:

    • Root Node: An artifact is represented as a rooted DAG where the root node is an OCI Manifest.
    • Immutability: Because the system uses a Merkle DAG, every object is a node uniquely identified by its digest. Since digests are computed from content, every node is immutable.
    • Grouping: Multiple manifests can be grouped together using an OCI Index, which acts as a rooted DAG itself.
    • Graph vs Tree: Artifacts are modeled as graphs rather than trees because different manifests can reference the same blobs (e.g., a shared config blob), allowing the CAS to store the blob only once.
  5. Importing v2 and v3 in the same application

    main
    You can import both v2 and v3 in a single application because they use different module paths. This facilitates incremental migrations. However, be aware that values from the two major versions have distinct Go types and cannot be mixed without explicit conversion.
  6. How signature manifests and referrers work

    main

    When an artifact is signed (e.g., using notation), a signature manifest is created. This signature manifest acts as a Referrer to the original artifact.

    • Subject Field: The signature manifest contains a subject field that points to the digest of the target artifact manifest.
    • Graph Relationship: In the resulting DAG, the signature manifest becomes the root for the combined graph, while the original artifact manifest remains the root of its own subgraph. They may share nodes (like config blobs) if they are identical.
    {
      "schemaVersion": 2,
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "config": {
        "mediaType": "application/vnd.cncf.notary.signature",
        "digest": "sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
        "size": 2
      },
      "layers": [
        {
          "mediaType": "application/jose+json",
          "digest": "sha256:37f88486592fd90ace303ee38f8d1ff698193e76c76d3c1fef8627a39e677696",
          "size": 2090
        }
      ],
      "subject": {
        "mediaType": "application/vnd.oci.image.manifest.v1+json",
        "digest": "sha256:314c7f20dd44ee1cca06af399a67f7c463a9f586830d630802d9e365933da9fb",
        "size": 762
      },
      "annotations": {
        "io.cncf.notary.x509chain.thumbprint#S256": "[\"a9c85558943f197f41fe7cf3caf691f7df8d0088be426a33d895560717893962\"]",
        "org.opencontainers.image.created": "2025-02-01T09:50:52Z"
      }
    }
  7. Understand Graph Concepts: Successors, Predecessors, and Referrers

    main

    ORAS models artifacts (manifests, indexes, and blobs) as nodes in a Directed Acyclic Graph (DAG). To navigate this graph, three key relationships are used:

    • Successor: Any node pointed to by a given node (e.g., a manifest's layers or config blobs).
    • Predecessor: Any non-leaf node that directly points to a given node.
    • Referrer: A specific type of relationship where a manifest with a subject field points to another manifest. In this case, the manifest with the subject field is the referrer, and the target is the subject.

    Note that for the subject relationship, the referrer is both a predecessor and a referrer of the subject node.

  8. Understand the core modeling concepts: Descriptors, Storage, and Targets

    main

    In oras-go v2, artifacts are modeled as Directed Acyclic Graphs (DAGs) stored in Content-Addressable Storages (CASs).

    Descriptors

    Every node in the graph is a descriptor which must contain:

    • mediaType: The media type of the referenced content.
    • digest: The digest of the targeted content.
    • size: The size in bytes of the raw content.

    Core Interfaces

    oras-go v2 uses four primary interfaces to manage these graphs and content:

    1. Storage: A basic CAS for accessing content via descriptors.
    2. GraphStorage: An extension of Storage that allows finding predecessors (nodes pointing to a specific node).
    3. Target: A CAS that supports tagging (associating a tag string with a descriptor) and resolving tags to descriptors.
    4. GraphTarget: The most comprehensive interface, combining GraphStorage and Target capabilities.
  9. Getting started with `oras-go` v2

    main

    To begin using oras-go v2, you should follow a structured learning path to understand how artifacts are modeled and how content is stored.

    Recommended learning order:

    1. Modeling Artifacts: Understand how to define and structure the artifacts you want to manage.
    2. Targets and Content Stores: Learn how oras-go v2 handles storage targets and content management.
    3. Quickstart Tutorial: Follow the hands-on guide to manage OCI artifacts using the library.
  10. Migrate Registry and Repository configuration from v2 to v3

    main

    In v3, configuration is reorganized: a remote.Registry owns shared settings (like HTTP client, plain-HTTP, and metadata limits), while remote.Repository points to its parent registry and handles per-repository overrides (like media types and page sizes).

    Key Changes:

    • Shared settings like Client, PlainHTTP, MaxMetadataBytes, and HandleWarning move from remote.Repository to remote.Registry.
    • repo.Reference is now a read-only method repo.Reference() that returns only the registry and repository name. It no longer holds tags or digests.
    • To handle references with tags or digests, use properties.NewReference.
    • Use remote.NewRegistryWithProperties or remote.NewRepositoryWithProperties when configuration comes from external files (e.g., registries.conf or Docker config).

    Example Migration:

    // v2 pattern
    repo, err := remote.NewRepository("localhost:5000/example")
    if err != nil {
    	return err
    }
    repo.Client = client
    repo.PlainHTTP = true
    ref := repo.Reference
    // v3 pattern
    repo, err := remote.NewRepository("localhost:5000/example")
    if err != nil {
    	return err
    }
    repo.Registry.Client = client
    repo.Registry.PlainHTTP = true
    ref := repo.Reference()
    // v3
    repo, err := remote.NewRepository("localhost:5000/example")
    if err != nil {
    	return err
    }
    repo.Registry.Client = client
    repo.Registry.PlainHTTP = true
    ref := repo.Reference()
  11. Migrate Authentication and Credentials from v2 to v3

    main

    Credential management has moved from registry/remote/auth to registry/remote/credentials.

    Key Changes:

    • auth.Credential $\rightarrow$ credentials.Credential.
    • auth.StaticCredential $\rightarrow$ credentials.StaticCredentialFunc.
    • auth.Client.Credential $\rightarrow$ auth.Client.CredentialFunc.
    • Scope functions (e.g., WithScopes, AppendScopes, GetScopes) are now host-specific. You must pass the registry host (e.g., from properties.Reference.Host()) to these functions.
    • credentials.Store methods now use credentials.Credential types.

    Example Migration (Static Credentials):

    // v2
    client := &auth.Client{
    	Credential: auth.StaticCredential("registry.example.com", auth.Credential{
    		Username: "user",
    		Password: "password",
    	}),
    }
    // v3
    client := &auth.Client{
    	CredentialFunc: credentials.StaticCredentialFunc(
    		"registry.example.com",
    		credentials.Credential{
    			Username: "user",
    			Password: "password",
    		},
    	),
    }
    // v3
    client := &auth.Client{
    	CredentialFunc: credentials.StaticCredentialFunc(
    		"registry.example.com",
    		credentials.Credential{
    			Username: "user",
    			Password: "password",
    		},
    	),
    }