{"id":1191,"date":"2026-09-26T05:27:43","date_gmt":"2026-09-26T05:27:43","guid":{"rendered":"https:\/\/www.devopsschool.com\/tutorials\/?p=1191"},"modified":"2026-09-26T05:27:46","modified_gmt":"2026-09-26T05:27:46","slug":"github-packages-complete-reference-guide-hands-on-tutorial","status":"publish","type":"post","link":"https:\/\/www.devopsschool.com\/tutorials\/github-packages-complete-reference-guide-hands-on-tutorial\/","title":{"rendered":"GitHub Packages \u2014 Complete Reference Guide &#038; Hands-On Tutorial"},"content":{"rendered":"\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong>Last Verified:<\/strong>&nbsp;September 2026<br><strong>Scope:<\/strong>&nbsp;GitHub.com \/ GitHub Enterprise Cloud unless explicitly stated otherwise<br><strong>Audience:<\/strong>&nbsp;Developers, DevOps engineers, platform engineers, administrators, architects, trainers, and engineering teams<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub Packages is GitHub&#8217;s package-hosting platform. It lets teams publish, store, secure, discover, automate, and consume software packages and container images while keeping package ownership, source code, CI\/CD, permissions, security metadata, and billing close to the GitHub development workflow.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This guide is organized as a progressive learning journey:&nbsp;<strong>concept \u2192 why \u2192 how \u2192 example \u2192 practice \u2192 real-world use case \u2192 best practices \u2192 troubleshooting<\/strong>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">1. What GitHub Packages Is<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub Packages is a set of package registries integrated with GitHub. A package is a distributable software artifact such as a container image, an npm library, a Java artifact, a NuGet package, or a Ruby gem.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Why teams use it<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A typical software organization produces more than source code. It also produces reusable libraries, container images, command-line tools, SDKs, internal frameworks, application dependencies, and release artifacts. Those outputs need a trusted place to live.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub Packages solves that problem by giving teams a registry that is integrated with:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>GitHub repositories<\/li>\n\n\n\n<li>GitHub organizations and teams<\/li>\n\n\n\n<li>GitHub Actions<\/li>\n\n\n\n<li>GitHub permissions and tokens<\/li>\n\n\n\n<li>GitHub Releases<\/li>\n\n\n\n<li>Supply-chain security features<\/li>\n\n\n\n<li>Artifact attestations<\/li>\n\n\n\n<li>Linked artifacts<\/li>\n\n\n\n<li>REST APIs and webhooks<\/li>\n\n\n\n<li>Billing and usage controls<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Core lifecycle<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    A&#91;Source Code] --&gt; B&#91;Build]\n    B --&gt; C&#91;Test]\n    C --&gt; D&#91;Package]\n    D --&gt; E&#91;Authenticate]\n    E --&gt; F&#91;Publish]\n    F --&gt; G&#91;Registry]\n    G --&gt; H&#91;Install \/ Pull]\n    H --&gt; I&#91;Application or Deployment]\n    I --&gt; J&#91;Observe \/ Audit]\n    J --&gt; K&#91;Update \/ Retire]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The important idea is that a registry sits between&nbsp;<strong>producers<\/strong>&nbsp;and&nbsp;<strong>consumers<\/strong>. Producers create immutable versions; consumers fetch known versions through an authenticated and governed channel.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">GitHub Packages vs GitHub Releases vs Actions artifacts<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Capability<\/th><th class=\"has-text-align-left\" data-align=\"left\">GitHub Packages<\/th><th class=\"has-text-align-left\" data-align=\"left\">GitHub Releases<\/th><th class=\"has-text-align-left\" data-align=\"left\">GitHub Actions artifacts<\/th><\/tr><\/thead><tbody><tr><td>Primary purpose<\/td><td>Dependency\/package distribution<\/td><td>Product release publishing<\/td><td>Temporary workflow outputs<\/td><\/tr><tr><td>Typical consumers<\/td><td>Applications, builds, deployments<\/td><td>Humans and release automation<\/td><td>Workflow jobs and engineers<\/td><\/tr><tr><td>Versioned package semantics<\/td><td>Yes<\/td><td>Release\/tag semantics<\/td><td>No package-manager semantics<\/td><\/tr><tr><td>Package manager integration<\/td><td>Yes<\/td><td>No<\/td><td>No<\/td><\/tr><tr><td>Container registry<\/td><td>Yes<\/td><td>No<\/td><td>No<\/td><\/tr><tr><td>Long-term reusable dependency<\/td><td>Yes<\/td><td>Sometimes<\/td><td>Usually no<\/td><\/tr><tr><td>CI intermediate file<\/td><td>Possible but not ideal<\/td><td>No<\/td><td>Yes<\/td><\/tr><tr><td>Best example<\/td><td><code>ghcr.io\/acme\/api:1.4.2<\/code><\/td><td><code>v1.4.2<\/code>&nbsp;release + binaries<\/td><td>test reports, build outputs<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">A useful rule:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Use\u00a0<strong>Packages<\/strong>\u00a0when another build, runtime, or developer will consume the artifact as a package.<\/li>\n\n\n\n<li>Use\u00a0<strong>Releases<\/strong>\u00a0when you are publishing a release event, release notes, and downloadable assets.<\/li>\n\n\n\n<li>Use\u00a0<strong>Actions artifacts<\/strong>\u00a0for workflow-produced files that are primarily tied to a workflow run.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">2. Supported Registries and Permission Models<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub Packages supports these principal registries:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Ecosystem<\/th><th class=\"has-text-align-left\" data-align=\"left\">Registry \/ endpoint<\/th><th class=\"has-text-align-left\" data-align=\"left\">Typical client<\/th><th class=\"has-text-align-left\" data-align=\"left\">Permission model<\/th><\/tr><\/thead><tbody><tr><td>Containers<\/td><td><code>ghcr.io<\/code><\/td><td>Docker \/ OCI clients<\/td><td>Granular user\/org scoped<\/td><\/tr><tr><td>npm<\/td><td><code>npm.pkg.github.com<\/code><\/td><td>npm \/ Yarn<\/td><td>Granular user\/org scoped<\/td><\/tr><tr><td>NuGet<\/td><td><code>nuget.pkg.github.com<\/code><\/td><td><code>dotnet<\/code>&nbsp;\/ NuGet CLI<\/td><td>Granular user\/org scoped<\/td><\/tr><tr><td>RubyGems<\/td><td><code>rubygems.pkg.github.com<\/code><\/td><td><code>gem<\/code>&nbsp;\/ Bundler<\/td><td>Granular user\/org scoped<\/td><\/tr><tr><td>Apache Maven<\/td><td><code>maven.pkg.github.com<\/code><\/td><td>Maven<\/td><td>Repository scoped<\/td><\/tr><tr><td>Gradle<\/td><td>Maven-compatible GitHub Packages endpoint<\/td><td>Gradle<\/td><td>Repository scoped<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub&#8217;s legacy Docker registry used&nbsp;<code>docker.pkg.github.com<\/code>. It has been replaced by the Container registry at&nbsp;<code>ghcr.io<\/code>&nbsp;and should be treated primarily as a migration\/backward-compatibility topic.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Granular packages vs repository-scoped packages<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">This is one of the most important architectural distinctions in GitHub Packages.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Granular-permission packages<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Supported by:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Container registry<\/li>\n\n\n\n<li>npm<\/li>\n\n\n\n<li>NuGet<\/li>\n\n\n\n<li>RubyGems<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">These packages can be scoped to a personal account or organization. Their visibility and access can be managed separately from a repository, although they can be connected to one.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Repository-scoped packages<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Supported by:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Apache Maven<\/li>\n\n\n\n<li>Gradle<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">These packages inherit their repository&#8217;s permissions and visibility.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Package scope model<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart TD\n    A&#91;GitHub Package]\n    A --&gt; B{Registry permission model}\n    B --&gt;|Granular| C&#91;User or Organization Scope]\n    C --&gt; D&#91;Optional repository connection]\n    C --&gt; E&#91;Independent Read \/ Write \/ Admin]\n    B --&gt;|Repository scoped| F&#91;Repository]\n    F --&gt; G&#91;Repository visibility]\n    F --&gt; H&#91;Repository permissions]\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">3. Core Concepts and Terminology<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Term<\/th><th class=\"has-text-align-left\" data-align=\"left\">Meaning<\/th><\/tr><\/thead><tbody><tr><td>Package<\/td><td>A distributable software artifact managed by a registry<\/td><\/tr><tr><td>Registry<\/td><td>Service that stores and distributes package versions<\/td><\/tr><tr><td>Package manager<\/td><td>Client that publishes or installs packages<\/td><\/tr><tr><td>Namespace<\/td><td>Owner\/name space used to prevent naming ambiguity<\/td><\/tr><tr><td>Package owner<\/td><td>User, organization, or repository that controls package scope<\/td><\/tr><tr><td>Version<\/td><td>A named package release such as&nbsp;<code>1.4.2<\/code><\/td><\/tr><tr><td>Tag<\/td><td>A movable or human-friendly alias, especially common for containers<\/td><\/tr><tr><td>Digest<\/td><td>Content-addressed immutable identifier, especially for OCI images<\/td><\/tr><tr><td>Metadata<\/td><td>Description, source repository, license, tags, publication data, etc.<\/td><\/tr><tr><td>Visibility<\/td><td>Public, private, or internal where supported<\/td><\/tr><tr><td>Permission<\/td><td>Read, write, or admin access<\/td><\/tr><tr><td>Repository connection<\/td><td>Association between a granular package and source repository<\/td><\/tr><tr><td>Publisher<\/td><td>User or automation that creates a package version<\/td><\/tr><tr><td>Consumer<\/td><td>Build, developer, service, or deployment that downloads the package<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Version vs tag vs digest<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">For containers, these three terms must not be confused:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Version<\/strong>: a package version as represented by GitHub&#8217;s package system.<\/li>\n\n\n\n<li><strong>Tag<\/strong>: a friendly image reference such as\u00a0<code>1.8.0<\/code>,\u00a0<code>main<\/code>, or\u00a0<code>latest<\/code>.<\/li>\n\n\n\n<li><strong>Digest<\/strong>: immutable content identifier such as\u00a0<code>sha256:...<\/code>.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">For production deployment, digest pinning is the strongest way to guarantee that the exact bytes tested are the bytes deployed.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">4. Where Packages Appear in GitHub<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub exposes packages from multiple contexts:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Organization packages<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Navigate to the organization and select&nbsp;<strong>Packages<\/strong>. This is the natural management surface for shared internal libraries and container images owned by an organization.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">User packages<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A user&#8217;s profile can expose packages scoped to that user.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Repository packages<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A repository sidebar can show packages associated with that repository. For repository-scoped registries, the repository is the package&#8217;s permission boundary. For granular registries, the repository is a connection that can provide source metadata and optionally inherited access.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Package search and discovery<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Use organization, user, or repository Packages surfaces to browse packages you can access. From a package landing page, follow source-repository and version links to understand ownership and usage. For private\/internal packages, discovery is permission-aware: a package that exists but is outside your access boundary may not be visible as a normal browsable asset.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Package landing page<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A package page can expose:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Description<\/li>\n\n\n\n<li>Installation and usage instructions<\/li>\n\n\n\n<li>Source repository<\/li>\n\n\n\n<li>Version history<\/li>\n\n\n\n<li>Publication dates<\/li>\n\n\n\n<li>Download activity<\/li>\n\n\n\n<li>Metadata<\/li>\n\n\n\n<li>Package settings<\/li>\n\n\n\n<li>Access configuration<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">5. Access Control, Visibility, and Permissions<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Package roles<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Role<\/th><th class=\"has-text-align-left\" data-align=\"left\">Typical capability<\/th><\/tr><\/thead><tbody><tr><td>Read<\/td><td>View metadata and download\/install<\/td><\/tr><tr><td>Write<\/td><td>Read plus upload\/publish<\/td><\/tr><tr><td>Admin<\/td><td>Publish, delete\/manage package, change access, grant permissions<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">For organization-scoped granular packages, access can be granted to individuals or teams. Organization owners also have administrative control.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Visibility<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Depending on the registry and account context, packages can be:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Public<\/strong>\u00a0\u2014 intended for broad consumption.<\/li>\n\n\n\n<li><strong>Private<\/strong>\u00a0\u2014 restricted to explicitly authorized users\/repositories.<\/li>\n\n\n\n<li><strong>Internal<\/strong>\u00a0\u2014 organization\/enterprise-oriented visibility where supported by granular registries and plan context.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">For most GitHub Packages registries, even public packages require package-client authentication. The major exception is the Container registry: public container images can be pulled anonymously.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Permission inheritance<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">When a granular package is connected to a repository, GitHub can let the package inherit repository permissions. Organizations can also disable automatic inheritance for newly published packages.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use inheritance when repository membership should define package access. Use explicit granular access when one package is shared across many repositories or teams.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Repository transfer behavior<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Repository transfers affect the two package models differently:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Granular packages:<\/strong>\u00a0the package remains owned by its user\/organization scope. A repository connection can be removed during transfer, and workflows\/Codespaces can lose access.<\/li>\n\n\n\n<li><strong>Repository-scoped packages:<\/strong>\u00a0the package follows the repository and its ownership\/permission model.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">This is why migration planning must include package ownership, not only repository ownership.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">6. Authentication and Authorization<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Authentication answers&nbsp;<strong>who are you?<\/strong>&nbsp;Authorization answers&nbsp;<strong>what may you do?<\/strong><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Personal access token (classic)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub Packages package-client authentication uses a&nbsp;<strong>personal access token (classic)<\/strong>&nbsp;with package scopes.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Scope<\/th><th class=\"has-text-align-left\" data-align=\"left\">Purpose<\/th><th class=\"has-text-align-left\" data-align=\"left\">Typical minimum package permission<\/th><\/tr><\/thead><tbody><tr><td><code>read:packages<\/code><\/td><td>Download\/install<\/td><td>Read<\/td><\/tr><tr><td><code>write:packages<\/code><\/td><td>Publish\/upload<\/td><td>Write<\/td><\/tr><tr><td><code>delete:packages<\/code><\/td><td>Delete<\/td><td>Admin<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">For organizations using SAML SSO, the token may also need authorization for that organization.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><code>GITHUB_TOKEN<\/code><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Inside GitHub Actions, prefer the repository&#8217;s automatically generated&nbsp;<code>GITHUB_TOKEN<\/code>&nbsp;where possible.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Example minimum workflow permissions for publishing:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>permissions:\n  contents: read\n  packages: write\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For a consumer workflow that only installs a package:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>permissions:\n  contents: read\n  packages: read\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For cross-repository use of granular packages, grant the consumer repository access under the package&#8217;s&nbsp;<strong>Manage Actions access<\/strong>&nbsp;settings.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Important distinction: package clients vs REST API tokens<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Do not generalize token rules blindly.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Package-manager authentication is documented around PAT classic and\u00a0<code>GITHUB_TOKEN<\/code>\u00a0in Actions.<\/li>\n\n\n\n<li>Some Packages REST API endpoints also support GitHub App user\/installation access tokens or fine-grained token types as documented per endpoint.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Always follow the authentication section of the exact API endpoint you are using.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Secure token handling<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Recommended:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Prefer\u00a0<code>GITHUB_TOKEN<\/code>\u00a0in Actions.<\/li>\n\n\n\n<li>Use minimal permissions.<\/li>\n\n\n\n<li>Store long-lived credentials in GitHub secrets or an external secret manager.<\/li>\n\n\n\n<li>Rotate PATs.<\/li>\n\n\n\n<li>Revoke unused credentials.<\/li>\n\n\n\n<li>Avoid tokens in checked-in\u00a0<code>.npmrc<\/code>,\u00a0<code>settings.xml<\/code>,\u00a0<code>nuget.config<\/code>,\u00a0<code>Gemfile<\/code>, shell scripts, or Dockerfiles.<\/li>\n\n\n\n<li>Treat self-hosted runners as part of your security boundary.<\/li>\n\n\n\n<li>Pin third-party Actions to trusted commit SHAs for high-assurance release workflows.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Avoid:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Shared developer PATs<\/li>\n\n\n\n<li>Admin-scoped tokens for read-only consumers<\/li>\n\n\n\n<li>Printing tokens in diagnostic output<\/li>\n\n\n\n<li>Publishing from untrusted pull-request contexts<\/li>\n\n\n\n<li>Reusing the same token across unrelated systems<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">7. Package Lifecycle<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A healthy package lifecycle is deliberate and auditable.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>stateDiagram-v2\n    &#91;*] --&gt; Build\n    Build --&gt; Test\n    Test --&gt; Publish: pass\n    Test --&gt; Build: fail\/fix\n    Publish --&gt; Active\n    Active --&gt; NewVersion\n    NewVersion --&gt; Active\n    Active --&gt; Deprecated: ecosystem supports deprecation\n    Active --&gt; Deleted\n    Deleted --&gt; Restored: within restore rules\n    Deleted --&gt; &#91;*]: retention window expires\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Typical lifecycle stages<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Build source.<\/li>\n\n\n\n<li>Run unit\/integration\/security tests.<\/li>\n\n\n\n<li>Choose version.<\/li>\n\n\n\n<li>Authenticate.<\/li>\n\n\n\n<li>Publish package.<\/li>\n\n\n\n<li>Verify package metadata.<\/li>\n\n\n\n<li>Grant consumer access.<\/li>\n\n\n\n<li>Install\/pull by consumers.<\/li>\n\n\n\n<li>Publish new versions rather than overwriting releases.<\/li>\n\n\n\n<li>Retire old versions based on retention policy.<\/li>\n\n\n\n<li>Restore only when supported and still inside the restoration window.<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Semantic Versioning<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">For libraries, Semantic Versioning is a useful convention:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>MAJOR<\/code>: incompatible change<\/li>\n\n\n\n<li><code>MINOR<\/code>: backward-compatible feature<\/li>\n\n\n\n<li><code>PATCH<\/code>: backward-compatible fix<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>2.7.4\n\u2502 \u2502 \u2514\u2500 patch\n\u2502 \u2514\u2500\u2500\u2500 minor\n\u2514\u2500\u2500\u2500\u2500\u2500 major\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Pre-releases can use identifiers such as:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>2.8.0-alpha.1\n2.8.0-beta.2\n2.8.0-rc.1\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Container image tags can mirror SemVer, but production deployment should prefer immutable digests.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">8. Getting Started: Publish a Container to GHCR<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Containers are the easiest way to see the complete GitHub Packages workflow end to end.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Prerequisites<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>GitHub account<\/li>\n\n\n\n<li>Repository such as\u00a0<code>acme\/hello-api<\/code><\/li>\n\n\n\n<li>Docker installed locally, or GitHub Actions<\/li>\n\n\n\n<li>Permission to publish to the target namespace<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Example application<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Create a minimal&nbsp;<code>Dockerfile<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>FROM nginx:alpine\nCOPY .\/index.html \/usr\/share\/nginx\/html\/index.html\n\nLABEL org.opencontainers.image.source=\"https:\/\/github.com\/acme\/hello-api\"\nLABEL org.opencontainers.image.description=\"Hello API training image\"\nLABEL org.opencontainers.image.licenses=\"MIT\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Create&nbsp;<code>index.html<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;h1&gt;Hello from GitHub Container Registry&lt;\/h1&gt;\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Authenticate locally<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Export a PAT classic with the minimum required scopes:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>export CR_PAT=\"YOUR_TOKEN\"\necho \"$CR_PAT\" | docker login ghcr.io -u YOUR_GITHUB_USERNAME --password-stdin\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Build and tag<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>docker build -t hello-api:1.0.0 .\ndocker tag hello-api:1.0.0 ghcr.io\/acme\/hello-api:1.0.0\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Push<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>docker push ghcr.io\/acme\/hello-api:1.0.0\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Verify<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>docker pull ghcr.io\/acme\/hello-api:1.0.0\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For a public container, an anonymous pull can work without a registry login.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Pull by digest<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">After publishing, capture the image digest and deploy the immutable reference:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker pull ghcr.io\/acme\/hello-api@sha256:REPLACE_WITH_DIGEST\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Expected result<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The package appears under the owning user\/organization&#8217;s Packages page. If source metadata is configured correctly, the package page can link back to its repository.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">9. GitHub Container Registry Deep Dive<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">What GHCR stores<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">GHCR supports Docker Image Manifest V2 Schema 2 and OCI image specifications. It can host Docker and OCI images, including foreign layers such as Windows image layers.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Common reference structure<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>ghcr.io\/OWNER\/IMAGE:TAG\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Examples:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ghcr.io\/acme\/payments-api:2.3.1\nghcr.io\/acme\/payments-api:sha-4c912ab\nghcr.io\/acme\/payments-api@sha256:...\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">OCI metadata<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Useful OCI annotations\/labels include:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>org.opencontainers.image.source\norg.opencontainers.image.description\norg.opencontainers.image.licenses\norg.opencontainers.image.revision\norg.opencontainers.image.version\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">These improve traceability and package presentation.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Tagging strategy<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A strong release can publish several tags pointing to the same digest:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>2.4.1\n2.4\n2\nsha-a1b2c3d\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use&nbsp;<code>latest<\/code>&nbsp;only if your team defines exactly what it means. Never use&nbsp;<code>latest<\/code>&nbsp;as the sole production deployment reference.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Multi-platform images<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">With Buildx, one logical image tag can point to an OCI image index containing multiple platform manifests.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker buildx build \\\n  --platform linux\/amd64,linux\/arm64 \\\n  -t ghcr.io\/acme\/api:2.4.1 \\\n  --push .\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart TD\n    A&#91;ghcr.io\/acme\/api:2.4.1] --&gt; B&#91;OCI Image Index]\n    B --&gt; C&#91;linux\/amd64 manifest]\n    B --&gt; D&#91;linux\/arm64 manifest]\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Container registry best practices<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Use multi-stage builds.<\/li>\n\n\n\n<li>Use small, maintained base images.<\/li>\n\n\n\n<li>Add OCI source\/license metadata.<\/li>\n\n\n\n<li>Publish immutable release tags.<\/li>\n\n\n\n<li>Add commit-SHA tags for traceability.<\/li>\n\n\n\n<li>Capture and promote digests.<\/li>\n\n\n\n<li>Generate provenance attestations.<\/li>\n\n\n\n<li>Generate an SBOM.<\/li>\n\n\n\n<li>Scan dependencies and base images.<\/li>\n\n\n\n<li>Use least-privilege workflow permissions.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">10. npm Registry<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub&#8217;s npm registry is used for Node.js packages. It supports granular user\/organization-scoped permissions.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Package naming<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub Packages npm packages are scoped packages. A typical package name is:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"name\": \"@acme\/shared-utils\",\n  \"version\": \"1.2.0\",\n  \"description\": \"Shared utilities for Acme services\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https:\/\/github.com\/acme\/shared-utils.git\"\n  }\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Configure&nbsp;<code>.npmrc<\/code><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>@acme:registry=https:\/\/npm.pkg.github.com\n\/\/npm.pkg.github.com\/:_authToken=${NODE_AUTH_TOKEN}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Do not commit a literal token into&nbsp;<code>.npmrc<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Publish locally<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>export NODE_AUTH_TOKEN=\"YOUR_PAT_CLASSIC\"\nnpm publish\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Install<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>npm install @acme\/shared-utils@1.2.0\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">GitHub Actions publishing<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>name: Publish npm package\n\non:\n  release:\n    types: &#91;published]\n\njobs:\n  publish:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n      packages: write\n\n    steps:\n      - uses: actions\/checkout@v6\n\n      - uses: actions\/setup-node@v7\n        with:\n          node-version: '20.x'\n          registry-url: 'https:\/\/npm.pkg.github.com'\n          scope: '@acme'\n\n      - run: npm ci\n      - run: npm test\n      - run: npm publish\n        env:\n          NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">npm use case<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A company maintains a shared TypeScript library consumed by ten internal services. The package is owned by the organization, write access is limited to the platform team, and each consuming repository receives read access through Manage Actions access.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">npm gotchas<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Ensure the package is scoped to the expected user\/organization.<\/li>\n\n\n\n<li>Ensure\u00a0<code>.npmrc<\/code>\u00a0routes only the intended scope to GitHub Packages.<\/li>\n\n\n\n<li>Avoid routing all npm traffic to a private registry unless that is explicitly intended.<\/li>\n\n\n\n<li>Keep npmjs.org and GitHub Packages sources clear to avoid dependency confusion and accidental publication.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">11. Apache Maven Registry<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Maven packages on GitHub Packages are repository-scoped. This means the package follows repository permissions rather than having an independent granular permission model.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Maven coordinates<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A Maven artifact is identified by:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>groupId:artifactId:version\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>com.acme:payments-sdk:2.1.0\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><code>pom.xml<\/code><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;project xmlns=\"http:\/\/maven.apache.org\/POM\/4.0.0\"\n         xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\"\n         xsi:schemaLocation=\"http:\/\/maven.apache.org\/POM\/4.0.0 https:\/\/maven.apache.org\/xsd\/maven-4.0.0.xsd\"&gt;\n  &lt;modelVersion&gt;4.0.0&lt;\/modelVersion&gt;\n\n  &lt;groupId&gt;com.acme&lt;\/groupId&gt;\n  &lt;artifactId&gt;payments-sdk&lt;\/artifactId&gt;\n  &lt;version&gt;2.1.0&lt;\/version&gt;\n\n  &lt;distributionManagement&gt;\n    &lt;repository&gt;\n      &lt;id&gt;github&lt;\/id&gt;\n      &lt;name&gt;GitHub Packages&lt;\/name&gt;\n      &lt;url&gt;https:\/\/maven.pkg.github.com\/acme\/payments-sdk&lt;\/url&gt;\n    &lt;\/repository&gt;\n  &lt;\/distributionManagement&gt;\n&lt;\/project&gt;\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><code>settings.xml<\/code><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;settings xmlns=\"http:\/\/maven.apache.org\/SETTINGS\/1.0.0\"&gt;\n  &lt;servers&gt;\n    &lt;server&gt;\n      &lt;id&gt;github&lt;\/id&gt;\n      &lt;username&gt;${env.GITHUB_ACTOR}&lt;\/username&gt;\n      &lt;password&gt;${env.GITHUB_TOKEN}&lt;\/password&gt;\n    &lt;\/server&gt;\n  &lt;\/servers&gt;\n&lt;\/settings&gt;\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Publish<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>mvn --batch-mode deploy\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Consume<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A consumer repository can configure the GitHub Packages Maven repository and add a dependency:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;dependency&gt;\n  &lt;groupId&gt;com.acme&lt;\/groupId&gt;\n  &lt;artifactId&gt;payments-sdk&lt;\/artifactId&gt;\n  &lt;version&gt;2.1.0&lt;\/version&gt;\n&lt;\/dependency&gt;\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">GitHub Actions<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>name: Publish Maven package\n\non:\n  release:\n    types: &#91;created]\n\njobs:\n  publish:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n      packages: write\n\n    steps:\n      - uses: actions\/checkout@v6\n\n      - uses: actions\/setup-java@v4\n        with:\n          distribution: temurin\n          java-version: '21'\n\n      - run: mvn --batch-mode verify\n      - run: mvn --batch-mode deploy\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Maven design implication<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Because Maven packages are repository-scoped, do not design access as though the package has independent organization-level ACLs. If many teams need the package, model the repository permissions accordingly.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">12. Gradle Registry<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub&#8217;s Gradle support uses Maven-format package publishing and is repository-scoped.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Groovy DSL example<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>plugins {\n    id 'java-library'\n    id 'maven-publish'\n}\n\nversion = '1.3.0'\ngroup = 'com.acme'\n\npublishing {\n    publications {\n        mavenJava(MavenPublication) {\n            from components.java\n        }\n    }\n\n    repositories {\n        maven {\n            name = \"GitHubPackages\"\n            url = uri(\"https:\/\/maven.pkg.github.com\/acme\/shared-java\")\n            credentials {\n                username = System.getenv(\"GITHUB_ACTOR\")\n                password = System.getenv(\"GITHUB_TOKEN\")\n            }\n        }\n    }\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Kotlin DSL example<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>plugins {\n    `java-library`\n    `maven-publish`\n}\n\ngroup = \"com.acme\"\nversion = \"1.3.0\"\n\npublishing {\n    publications {\n        create&lt;MavenPublication&gt;(\"mavenJava\") {\n            from(components&#91;\"java\"])\n        }\n    }\n\n    repositories {\n        maven {\n            name = \"GitHubPackages\"\n            url = uri(\"https:\/\/maven.pkg.github.com\/acme\/shared-java\")\n            credentials {\n                username = System.getenv(\"GITHUB_ACTOR\")\n                password = System.getenv(\"GITHUB_TOKEN\")\n            }\n        }\n    }\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Publish<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>.\/gradlew publish\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">GitHub Actions<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>name: Publish Gradle package\n\non:\n  release:\n    types: &#91;created]\n\njobs:\n  publish:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n      packages: write\n\n    steps:\n      - uses: actions\/checkout@v6\n      - uses: actions\/setup-java@v4\n        with:\n          distribution: temurin\n          java-version: '21'\n\n      - name: Setup Gradle\n        uses: gradle\/actions\/setup-gradle@v4\n\n      - run: .\/gradlew test\n      - run: .\/gradlew publish\n        env:\n          GITHUB_ACTOR: ${{ github.actor }}\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For high-assurance release pipelines, pin third-party actions to a reviewed commit SHA rather than a moving tag.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">13. NuGet Registry<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub Packages supports NuGet packages with granular user\/organization-scoped permissions.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Add a package source<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>dotnet nuget add source \\\n  --username YOUR_GITHUB_USERNAME \\\n  --password YOUR_PAT_CLASSIC \\\n  --store-password-in-clear-text \\\n  --name github \\\n  \"https:\/\/nuget.pkg.github.com\/acme\/index.json\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The&nbsp;<code>--store-password-in-clear-text<\/code>&nbsp;option is convenient for demonstrations but is a security tradeoff. In CI, prefer injected credentials and ephemeral runners.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Pack<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>dotnet pack --configuration Release\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Publish<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>dotnet nuget push \"bin\/Release\/Acme.Tools.1.0.0.nupkg\" \\\n  --api-key YOUR_PAT_CLASSIC \\\n  --source github\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Install<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>dotnet add package Acme.Tools --version 1.0.0 --source github\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><code>nuget.config<\/code>&nbsp;with source mapping<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Source mapping helps prevent dependency confusion by ensuring private package IDs resolve only from the intended source.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;configuration&gt;\n  &lt;packageSources&gt;\n    &lt;add key=\"nuget.org\" value=\"https:\/\/api.nuget.org\/v3\/index.json\" \/&gt;\n    &lt;add key=\"github\" value=\"https:\/\/nuget.pkg.github.com\/acme\/index.json\" \/&gt;\n  &lt;\/packageSources&gt;\n\n  &lt;packageSourceMapping&gt;\n    &lt;packageSource key=\"nuget.org\"&gt;\n      &lt;package pattern=\"*\" \/&gt;\n    &lt;\/packageSource&gt;\n    &lt;packageSource key=\"github\"&gt;\n      &lt;package pattern=\"Acme.*\" \/&gt;\n    &lt;\/packageSource&gt;\n  &lt;\/packageSourceMapping&gt;\n&lt;\/configuration&gt;\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Current size note<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub documents that a NuGet&nbsp;<code>.nupkg<\/code>&nbsp;archive for a package version must be smaller than 2.147 GB.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">14. RubyGems Registry<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub Packages supports Ruby gems with granular user\/organization-scoped permissions.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Prerequisites<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub&#8217;s current documentation lists RubyGems 2.4.1+ and Bundler 1.6.4+ as minimums. In production, use a supported modern Ruby\/RubyGems\/Bundler toolchain.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Authenticate with RubyGems<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>gem sources --add \\\n  https:&#47;&#47;YOUR_USERNAME:YOUR_PAT_CLASSIC@rubygems.pkg.github.com\/acme\/\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Authenticate with Bundler<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>bundle config https:\/\/rubygems.pkg.github.com\/acme \\\n  YOUR_USERNAME:YOUR_PAT_CLASSIC\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Gem metadata<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>Gem::Specification.new do |spec|\n  spec.name        = \"acme-tools\"\n  spec.version     = \"1.0.0\"\n  spec.summary     = \"Internal Acme Ruby utilities\"\n  spec.files       = Dir&#91;\"lib\/**\/*\"]\n  spec.require_paths = &#91;\"lib\"]\n  spec.metadata = {\n    \"github_repo\" =&gt; \"ssh:\/\/github.com\/acme\/acme-tools\"\n  }\nend\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Build and publish<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>gem build acme-tools.gemspec\ngem push --key github \\\n  --host https:\/\/rubygems.pkg.github.com\/acme \\\n  acme-tools-1.0.0.gem\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Bundler consumption<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>source \"https:\/\/rubygems.org\"\n\nsource \"https:\/\/rubygems.pkg.github.com\/acme\" do\n  gem \"acme-tools\", \"1.0.0\"\nend\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">15. Connecting Packages to Repositories<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">For granular registries, package ownership and repository ownership are separate concepts.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Why connect a package to a repository?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A connection can provide:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Source repository link<\/li>\n\n\n\n<li>README\/context on the package page<\/li>\n\n\n\n<li>Permission inheritance, if enabled<\/li>\n\n\n\n<li>Automatic Actions access from the linked repository<\/li>\n\n\n\n<li>Better provenance and discoverability<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Manual connection<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Typical UI flow:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Open the package landing page.<\/li>\n\n\n\n<li>Open\u00a0<strong>Package settings<\/strong>.<\/li>\n\n\n\n<li>Find the repository connection area.<\/li>\n\n\n\n<li>Choose\u00a0<strong>Connect repository<\/strong>.<\/li>\n\n\n\n<li>Select the source repository.<\/li>\n\n\n\n<li>Decide whether repository permissions should be inherited.<\/li>\n\n\n\n<li>Verify Actions\/Codespaces access if needed.<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Automatic container linkage<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">OCI source metadata can establish repository context:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>LABEL org.opencontainers.image.source=\"https:\/\/github.com\/acme\/payments-api\"\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Design pattern: one producer, many consumers<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    P&#91;Producer repository] --&gt; W&#91;Build\/Test workflow]\n    W --&gt; R&#91;Organization package]\n    R --&gt; C1&#91;Consumer repo A]\n    R --&gt; C2&#91;Consumer repo B]\n    R --&gt; C3&#91;Consumer repo C]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For this pattern, granular permissions are ideal: the package can remain organization-owned while individual repositories receive only read access.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">16. GitHub Actions and GitHub Packages<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub Actions is the preferred automation layer for build-test-publish workflows.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Standard pipeline<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    A&#91;Push \/ Tag \/ Release] --&gt; B&#91;Checkout]\n    B --&gt; C&#91;Build]\n    C --&gt; D&#91;Test]\n    D --&gt; E&#91;Package]\n    E --&gt; F&#91;Authenticate]\n    F --&gt; G&#91;Publish]\n    G --&gt; H&#91;Verify]\n    H --&gt; I&#91;Attest \/ SBOM]\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Trigger choices<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Trigger<\/th><th class=\"has-text-align-left\" data-align=\"left\">Good for<\/th><th class=\"has-text-align-left\" data-align=\"left\">Risk \/ consideration<\/th><\/tr><\/thead><tbody><tr><td><code>push<\/code>&nbsp;to branch<\/td><td>Snapshot\/dev builds<\/td><td>Can create many versions<\/td><\/tr><tr><td>Git tag<\/td><td>Version-driven releases<\/td><td>Protect tag creation<\/td><\/tr><tr><td>GitHub Release<\/td><td>Human-reviewed release flow<\/td><td>Release creation must be governed<\/td><\/tr><tr><td><code>workflow_dispatch<\/code><\/td><td>Manual controlled publish<\/td><td>Requires operational discipline<\/td><\/tr><tr><td><code>schedule<\/code><\/td><td>Periodic rebuilds<\/td><td>Must avoid accidental republish collision<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Recommended release workflow permissions<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>permissions:\n  contents: read\n  packages: write\n  attestations: write\n  id-token: write\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Only request&nbsp;<code>attestations<\/code>&nbsp;and&nbsp;<code>id-token<\/code>&nbsp;when the workflow actually generates attestations.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Test before publish<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@v6\n      - run: .\/scripts\/test.sh\n\n  publish:\n    needs: test\n    if: github.event_name == 'release'\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n      packages: write\n    steps:\n      - uses: actions\/checkout@v6\n      - run: .\/scripts\/publish.sh\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The important control is the dependency: publishing does not start unless tests succeed.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">17. Container Publishing Workflow with Metadata and Provenance<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A production-quality container workflow should build, test, publish, capture the digest, and generate provenance.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>name: Publish container\n\non:\n  release:\n    types: &#91;published]\n\nenv:\n  REGISTRY: ghcr.io\n  IMAGE_NAME: ${{ github.repository }}\n\njobs:\n  publish:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n      packages: write\n      attestations: write\n      id-token: write\n\n    steps:\n      - uses: actions\/checkout@v6\n\n      - name: Log in to GHCR\n        uses: docker\/login-action@v3\n        with:\n          registry: ${{ env.REGISTRY }}\n          username: ${{ github.actor }}\n          password: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: Generate image metadata\n        id: meta\n        uses: docker\/metadata-action@v5\n        with:\n          images: ${{ env.REGISTRY }}\/${{ env.IMAGE_NAME }}\n          tags: |\n            type=semver,pattern={{version}}\n            type=semver,pattern={{major}}.{{minor}}\n            type=sha\n\n      - name: Build and push\n        id: push\n        uses: docker\/build-push-action@v6\n        with:\n          context: .\n          push: true\n          tags: ${{ steps.meta.outputs.tags }}\n          labels: ${{ steps.meta.outputs.labels }}\n\n      - name: Generate provenance attestation\n        uses: actions\/attest@v4\n        with:\n          subject-name: ${{ env.REGISTRY }}\/${{ env.IMAGE_NAME }}\n          subject-digest: ${{ steps.push.outputs.digest }}\n          push-to-registry: true\n<\/code><\/pre>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">For high-assurance production workflows, pin non-GitHub and third-party Actions to a reviewed full commit SHA. The major-version tags above are easier for training and should not be mistaken for the strongest supply-chain posture.<\/p>\n<\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\">Multi-platform extension<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Add Buildx setup and configure:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>with:\n  platforms: linux\/amd64,linux\/arm64\n  push: true\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">18. Actions and Codespaces Package Access<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Granular packages can grant access to repositories separately for GitHub Actions and GitHub Codespaces.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Actions access<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Use package settings when a workflow in one repository needs a package owned elsewhere.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Typical flow:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Open the package.<\/li>\n\n\n\n<li>Open\u00a0<strong>Package settings<\/strong>.<\/li>\n\n\n\n<li>Find\u00a0<strong>Manage Actions access<\/strong>.<\/li>\n\n\n\n<li>Add the workflow repository.<\/li>\n\n\n\n<li>Choose the minimum required role.<\/li>\n\n\n\n<li>Set workflow\u00a0<code>permissions<\/code>\u00a0to\u00a0<code>packages: read<\/code>\u00a0or\u00a0<code>packages: write<\/code>\u00a0as appropriate.<\/li>\n\n\n\n<li>Test with\u00a0<code>GITHUB_TOKEN<\/code>\u00a0before introducing a PAT.<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Codespaces access<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">For supported granular packages, package settings can also allow selected repositories&#8217; Codespaces to install the package. This is useful when developer environments need private dependencies but you do not want developers maintaining broad package credentials.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Cross-repository access decision<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart TD\n    A&#91;Workflow needs package] --&gt; B{Same repository\/package relationship?}\n    B --&gt;|Yes| C&#91;Use GITHUB_TOKEN]\n    B --&gt;|No| D{Granular registry?}\n    D --&gt;|Yes| E&#91;Grant repository package access]\n    E --&gt; C\n    D --&gt;|No \/ repo-scoped| F&#91;Use repository permission model]\n    F --&gt; G&#91;Validate token and repository access]\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">19. Deleting, Restoring, and Cleaning Up Packages<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Deletion is a destructive operation and should be governed like a release operation.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Current GitHub.com rules<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub documents that users with sufficient access can delete:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>An entire private package.<\/li>\n\n\n\n<li>A specific version of a private package.<\/li>\n\n\n\n<li>A public package if no version has more than 5,000 downloads.<\/li>\n\n\n\n<li>A public package version if that version does not have more than 5,000 downloads.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">For a public package\/version above the documented 5,000-download threshold, GitHub Support is required for deletion assistance.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Restoration<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A deleted package or package version can normally be restored when:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>It is restored within 30 days of deletion.<\/li>\n\n\n\n<li>The package namespace\/version has not been reused in a way that blocks restoration.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">GitHub Actions deletion\/restoration<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub currently documents workflow-based delete\/restore through the REST API as&nbsp;<strong>public preview<\/strong>. For granular packages, the workflow repository must have package admin access and the workflow token must be configured appropriately.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Safe cleanup policy<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A practical organization policy could be:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Package class<\/th><th class=\"has-text-align-left\" data-align=\"left\">Retention suggestion<\/th><\/tr><\/thead><tbody><tr><td>Production releases<\/td><td>Keep indefinitely or per compliance policy<\/td><\/tr><tr><td>Supported major\/minor releases<\/td><td>Keep while supported<\/td><\/tr><tr><td>Release candidates<\/td><td>Keep last 5\u201310<\/td><\/tr><tr><td>Branch\/dev builds<\/td><td>Keep 7\u201330 days<\/td><\/tr><tr><td>Pull-request builds<\/td><td>Keep only while PR is active plus grace period<\/td><\/tr><tr><td>Untagged container manifests<\/td><td>Clean after verifying no digest references depend on them<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Example: list container versions with GitHub CLI<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>gh api \\\n  -H \"Accept: application\/vnd.github+json\" \\\n  -H \"X-GitHub-Api-Version: 2026-03-10\" \\\n  \"\/orgs\/acme\/packages\/container\/payments-api\/versions?per_page=100\"\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Example: delete a version<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>gh api \\\n  --method DELETE \\\n  -H \"Accept: application\/vnd.github+json\" \\\n  -H \"X-GitHub-Api-Version: 2026-03-10\" \\\n  \"\/orgs\/acme\/packages\/container\/payments-api\/versions\/PACKAGE_VERSION_ID\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use a dry-run\/reporting step before automated deletion.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">20. REST API for GitHub Packages<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The Packages REST API can list and manage packages and versions for users and organizations.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Common package types<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The REST API recognizes package types including:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>npm\nmaven\nrubygems\ndocker\nnuget\ncontainer\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Gradle packages use the API package type&nbsp;<code>maven<\/code>. Legacy Docker-registry images can be represented by&nbsp;<code>docker<\/code>, while GHCR images use&nbsp;<code>container<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">List organization packages<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>gh api \\\n  -H \"Accept: application\/vnd.github+json\" \\\n  -H \"X-GitHub-Api-Version: 2026-03-10\" \\\n  \"\/orgs\/acme\/packages?package_type=container&amp;per_page=100\"\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Get a package<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>gh api \\\n  -H \"Accept: application\/vnd.github+json\" \\\n  -H \"X-GitHub-Api-Version: 2026-03-10\" \\\n  \"\/orgs\/acme\/packages\/container\/payments-api\"\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">List versions<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>gh api \\\n  -H \"Accept: application\/vnd.github+json\" \\\n  -H \"X-GitHub-Api-Version: 2026-03-10\" \\\n  \"\/orgs\/acme\/packages\/container\/payments-api\/versions?state=active&amp;per_page=100\"\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Pagination<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The API defaults to paginated responses. For large estates:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>gh api --paginate \\\n  -H \"X-GitHub-Api-Version: 2026-03-10\" \\\n  \"\/orgs\/acme\/packages?package_type=container&amp;per_page=100\"\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">API authentication caution<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Endpoint support differs by operation. PAT classic is the baseline package token model, while current REST documentation also lists GitHub App and fine-grained token support for selected endpoints. Do not assume every endpoint accepts every token type.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">21. GitHub CLI for Package Administration<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">There is no need to wait for a dedicated&nbsp;<code>gh package<\/code>&nbsp;command to automate package operations.&nbsp;<code>gh api<\/code>&nbsp;can call the Packages REST API directly.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Inventory script<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/usr\/bin\/env bash\nset -euo pipefail\n\nORG=\"${1:?usage: $0 ORG}\"\nTYPE=\"${2:-container}\"\n\ngh api --paginate \\\n  -H \"Accept: application\/vnd.github+json\" \\\n  -H \"X-GitHub-Api-Version: 2026-03-10\" \\\n  \"\/orgs\/${ORG}\/packages?package_type=${TYPE}&amp;per_page=100\" \\\n  --jq '.&#91;] | &#91;.name, .visibility, .version_count, .updated_at] | @tsv'\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Example output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>payments-api    private    42    2026-09-24T03:10:21Z\nshared-ui       internal   18    2026-09-20T09:04:03Z\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Why&nbsp;<code>gh api<\/code>&nbsp;is useful<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Auth can reuse GitHub CLI login.<\/li>\n\n\n\n<li>Pagination is easy.<\/li>\n\n\n\n<li><code>--jq<\/code>\u00a0supports lightweight reporting.<\/li>\n\n\n\n<li>Scripts can run locally or in Actions.<\/li>\n\n\n\n<li>It exposes new REST features without waiting for a specialized CLI subcommand.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">22. GraphQL and GitHub Packages<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Treat GraphQL support as a compatibility area, not the default management API for GitHub Packages.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub documents that the Packages GraphQL API cannot be used with registries that support granular permissions. GraphQL remains relevant mainly to repository-scoped package models and historical integrations.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For new automation across Container, npm, NuGet, and RubyGems, prefer the REST API unless a documented GraphQL object is specifically required.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Decision table<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Need<\/th><th class=\"has-text-align-left\" data-align=\"left\">Preferred interface<\/th><\/tr><\/thead><tbody><tr><td>List\/manage modern granular packages<\/td><td>REST API<\/td><\/tr><tr><td>Automate from shell<\/td><td><code>gh api<\/code><\/td><\/tr><tr><td>Receive publish event<\/td><td>Webhook<\/td><\/tr><tr><td>Package manager publish\/install<\/td><td>Native package client<\/td><\/tr><tr><td>Historical\/repository-scoped graph use case<\/td><td>GraphQL where documented<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">23. Package Webhooks and Event Automation<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub exposes a&nbsp;<code>package<\/code>&nbsp;webhook event for GitHub Packages activity. The documented&nbsp;<code>published<\/code>&nbsp;action fires when a package is published.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Typical automation<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>sequenceDiagram\n    participant Publisher\n    participant GitHubPackages as GitHub Packages\n    participant Webhook\n    participant Scanner\n    participant Chat as Notification System\n\n    Publisher-&gt;&gt;GitHubPackages: Publish package\n    GitHubPackages-&gt;&gt;Webhook: package.published\n    Webhook-&gt;&gt;Scanner: Start external scan\n    Scanner--&gt;&gt;Webhook: Result\n    Webhook-&gt;&gt;Chat: Publish status\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Example use cases<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Start an external vulnerability scan after publication.<\/li>\n\n\n\n<li>Synchronize metadata to a CMDB.<\/li>\n\n\n\n<li>Notify a release channel.<\/li>\n\n\n\n<li>Trigger downstream deployment orchestration.<\/li>\n\n\n\n<li>Register the artifact in an internal catalog.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\"><code>package<\/code>&nbsp;vs&nbsp;<code>registry_package<\/code><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub&#8217;s webhook documentation recommends the newer&nbsp;<code>package<\/code>&nbsp;event over the older&nbsp;<code>registry_package<\/code>&nbsp;event. Existing integrations should be reviewed before migration rather than changed blindly.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Webhook security<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Use a webhook secret.<\/li>\n\n\n\n<li>Validate GitHub signatures.<\/li>\n\n\n\n<li>Reject stale\/replayed requests where practical.<\/li>\n\n\n\n<li>Make handlers idempotent.<\/li>\n\n\n\n<li>Do not treat an incoming publish event as sufficient authorization for production deployment.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">24. GitHub App Integration<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub Apps are appropriate when a service needs organization-wide automation without relying on a person&#8217;s credentials.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Possible responsibilities:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Receive package webhooks.<\/li>\n\n\n\n<li>Read package metadata through supported REST endpoints.<\/li>\n\n\n\n<li>Create audit records.<\/li>\n\n\n\n<li>Enforce internal policy outside GitHub.<\/li>\n\n\n\n<li>Integrate package events with deployment or security tooling.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">App design principles<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Request only required repository\/organization permissions.<\/li>\n\n\n\n<li>Use short-lived installation access tokens.<\/li>\n\n\n\n<li>Verify webhook signatures.<\/li>\n\n\n\n<li>Store App private keys securely.<\/li>\n\n\n\n<li>Model each REST endpoint&#8217;s accepted token types explicitly.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">25. Billing, Storage, Data Transfer, and Budgets<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub Packages usage is tied to the owning account and plan.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Included usage currently documented by GitHub<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Plan<\/th><th class=\"has-text-align-right\" data-align=\"right\">Included storage<\/th><th class=\"has-text-align-right\" data-align=\"right\">Included data transfer \/ month<\/th><\/tr><\/thead><tbody><tr><td>GitHub Free<\/td><td class=\"has-text-align-right\" data-align=\"right\">500 MB<\/td><td class=\"has-text-align-right\" data-align=\"right\">1 GB<\/td><\/tr><tr><td>GitHub Pro<\/td><td class=\"has-text-align-right\" data-align=\"right\">2 GB<\/td><td class=\"has-text-align-right\" data-align=\"right\">10 GB<\/td><\/tr><tr><td>GitHub Free for organizations<\/td><td class=\"has-text-align-right\" data-align=\"right\">500 MB<\/td><td class=\"has-text-align-right\" data-align=\"right\">1 GB<\/td><\/tr><tr><td>GitHub Team<\/td><td class=\"has-text-align-right\" data-align=\"right\">2 GB<\/td><td class=\"has-text-align-right\" data-align=\"right\">10 GB<\/td><\/tr><tr><td>GitHub Enterprise Cloud<\/td><td class=\"has-text-align-right\" data-align=\"right\">50 GB<\/td><td class=\"has-text-align-right\" data-align=\"right\">100 GB<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub documents that the included storage pool is shared with GitHub Actions artifacts.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Container registry billing status<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">As of September 2026, GitHub&#8217;s billing documentation says Container registry image storage and bandwidth are currently free, with at least one month of notice promised before a policy change.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Treat that as a current policy, not a permanent architecture assumption.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Actions package transfer<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub documents that package downloads performed by GitHub Actions using a&nbsp;<code>GITHUB_TOKEN<\/code>&nbsp;do not count against the hosting repository&#8217;s data-transfer usage. Current billing documentation further distinguishes token and runner type:&nbsp;<code>GITHUB_TOKEN<\/code>&nbsp;access is free for GitHub-hosted and self-hosted runners; PAT-based package access is documented as free on GitHub-hosted runners but billable on self-hosted runners.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Cost controls<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Recommended:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Define version-retention rules.<\/li>\n\n\n\n<li>Clean snapshot\/dev packages.<\/li>\n\n\n\n<li>Avoid duplicating the same large binary under unnecessary versions.<\/li>\n\n\n\n<li>Monitor package and Actions storage together.<\/li>\n\n\n\n<li>Configure budgets\/spending limits where applicable.<\/li>\n\n\n\n<li>Enable budget alerts at 75%, 90%, and 100% where useful.<\/li>\n\n\n\n<li>Enable included-usage alerts at 90% and 100% for GitHub Packages storage\/bandwidth.<\/li>\n\n\n\n<li>Review sudden changes in download patterns.<\/li>\n\n\n\n<li>Track organization ownership before moving repositories\/packages.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Storage forecasting<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A simple estimate:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Monthly retained storage growth\n\u2248 average package size \u00d7 new retained versions per month \u00d7 package count\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For containers, deduplicated layer behavior can make real storage different from a naive image-size multiplication, so billing\/usage reports remain the source of truth.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">26. Organization Package Administration<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Organization owners should treat package configuration as part of platform governance.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Organization-level concerns<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Who may create packages?<\/li>\n\n\n\n<li>Which visibility levels are allowed?<\/li>\n\n\n\n<li>Should granular packages inherit repository permissions automatically?<\/li>\n\n\n\n<li>Which teams can administer shared packages?<\/li>\n\n\n\n<li>Who may publish production packages?<\/li>\n\n\n\n<li>What is the naming convention?<\/li>\n\n\n\n<li>What is the retention policy?<\/li>\n\n\n\n<li>How are external\/public packages reviewed?<\/li>\n\n\n\n<li>What is the response plan for a compromised package?<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Suggested organization standard<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Area<\/th><th class=\"has-text-align-left\" data-align=\"left\">Recommended standard<\/th><\/tr><\/thead><tbody><tr><td>Ownership<\/td><td>Organization, not individual users, for business packages<\/td><\/tr><tr><td>Publishing<\/td><td>CI-first; developer-laptop publishing only for controlled exceptions<\/td><\/tr><tr><td>Credentials<\/td><td><code>GITHUB_TOKEN<\/code>&nbsp;in Actions; minimal PAT classic for local use<\/td><\/tr><tr><td>Visibility<\/td><td>Private\/internal by default for proprietary packages<\/td><\/tr><tr><td>Source link<\/td><td>Required<\/td><\/tr><tr><td>Versioning<\/td><td>SemVer for libraries; immutable release tags\/digests for containers<\/td><\/tr><tr><td>Release<\/td><td>Protected tags\/releases or environment approval<\/td><\/tr><tr><td>Retention<\/td><td>Explicit per package class<\/td><\/tr><tr><td>Provenance<\/td><td>Attest production artifacts<\/td><\/tr><tr><td>SBOM<\/td><td>Generate for production\/security-sensitive artifacts<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">27. Enterprise Considerations<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Enterprise Managed Users<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub documents that Enterprise Managed Users can publish to organization namespaces but cannot publish packages to their own user namespace because there is no personal storage allocation.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">GitHub Enterprise Cloud<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Enterprise Cloud is relevant when you need organization\/enterprise governance, internal visibility, broader security capabilities, or private\/internal artifact attestations.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For&nbsp;<strong>GitHub Enterprise Cloud with data residency<\/strong>, do not assume the GitHub.com registry matrix is identical. Current GitHub documentation says the Maven and Gradle registries are unavailable in the data-residency offering. Service URLs can also differ; for example, the Container registry uses a tenant-specific&nbsp;<code>containers.SUBDOMAIN.ghe.com<\/code>&nbsp;endpoint rather than&nbsp;<code>ghcr.io<\/code>. Review the exact GHE.com feature matrix before migration.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Artifact attestations and plan behavior<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub currently documents artifact attestations as available on current plans, with an important visibility distinction:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>On Free, Pro, and Team, attestations are available for public repositories.<\/li>\n\n\n\n<li>Private\/internal repository attestations require GitHub Enterprise Cloud.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Always recheck plan requirements before making compliance commitments.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">GitHub Enterprise Server<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub Enterprise Server capabilities vary by server version. Do not copy GitHub.com registry availability, action versions, API versions, or security-feature assumptions directly to GHES. Check the documentation for the exact GHES release you operate.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">28. Package Architecture Patterns<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Centralized organization package registry<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart TB\n    subgraph Producers\n      A&#91;Service A Repo]\n      B&#91;Library Repo]\n      C&#91;UI Repo]\n    end\n\n    A --&gt; P&#91;Organization Packages]\n    B --&gt; P\n    C --&gt; P\n\n    P --&gt; X&#91;Consumer Repo 1]\n    P --&gt; Y&#91;Consumer Repo 2]\n    P --&gt; Z&#91;Runtime \/ Deployment]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Benefits:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Central ownership<\/li>\n\n\n\n<li>Easier discovery<\/li>\n\n\n\n<li>Consistent access model<\/li>\n\n\n\n<li>Shared CI patterns<\/li>\n\n\n\n<li>Easier audit and retention<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Multi-repository shared library<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Producer repository publishes&nbsp;<code>@acme\/auth-client<\/code>. Many services consume a pinned version. The producer team owns write\/admin; consumers receive read-only access.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Monorepo with multiple packages<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>repo\/\n\u251c\u2500\u2500 packages\/\n\u2502   \u251c\u2500\u2500 api-client\/\n\u2502   \u251c\u2500\u2500 ui-kit\/\n\u2502   \u2514\u2500\u2500 config\/\n\u251c\u2500\u2500 .github\/workflows\/\n\u2514\u2500\u2500 package.json\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Publishing options:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Independent package versions<\/li>\n\n\n\n<li>One shared release version<\/li>\n\n\n\n<li>Changed-package detection<\/li>\n\n\n\n<li>Matrix publishing<\/li>\n\n\n\n<li>Path filters<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Use tools such as workspace managers\/change detection only when their additional complexity is justified.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">29. Build Once, Deploy Many<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A mature release pipeline should avoid rebuilding the artifact independently for each environment.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    A&#91;Commit] --&gt; B&#91;Build + Test]\n    B --&gt; C&#91;Immutable Artifact]\n    C --&gt; D&#91;Dev]\n    D --&gt; E&#91;Stage]\n    E --&gt; F&#91;Production]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The artifact moving through environments should be the same bytes.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Containers<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Good:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ghcr.io\/acme\/api@sha256:abc123...\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Riskier:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ghcr.io\/acme\/api:latest\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Promotion patterns<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Promote a digest, not a rebuild.<\/li>\n\n\n\n<li>Add an environment\/release tag to an existing digest if your workflow needs tags.<\/li>\n\n\n\n<li>Store release metadata that records the exact digest\/version.<\/li>\n\n\n\n<li>Roll back by redeploying a previously approved immutable version.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Why this matters<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Rebuilding per environment creates a provenance gap: production may contain different bytes from the artifact tested in staging even when source revision is the same.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">30. Versioning and Release Strategy<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Libraries<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Recommended:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Semantic versions<\/li>\n\n\n\n<li>Clear compatibility policy<\/li>\n\n\n\n<li>Pre-release identifiers<\/li>\n\n\n\n<li>Changelog\/release notes<\/li>\n\n\n\n<li>No version reuse<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Containers<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Recommended tag set:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>2.7.3\n2.7\n2\nsha-74cce21\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Optional:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>latest\nmain\nrelease-candidate\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Only use mutable tags if your automation understands that they are pointers, not identities.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Release-driven publishing<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A controlled release path:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    A&#91;Merge to main] --&gt; B&#91;CI]\n    B --&gt; C&#91;Create version tag]\n    C --&gt; D&#91;GitHub Release]\n    D --&gt; E&#91;Publish package]\n    E --&gt; F&#91;Attest]\n    F --&gt; G&#91;Promote exact version]\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Rollback<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Do not overwrite&nbsp;<code>2.7.3<\/code>&nbsp;with fixed bytes. Publish&nbsp;<code>2.7.4<\/code>&nbsp;or redeploy a prior known-good immutable artifact.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">31. Software Supply-Chain Security<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Publishing a package is only one part of the problem. A secure software supply chain must answer:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Where did this artifact come from?<\/li>\n\n\n\n<li>Which source revision produced it?<\/li>\n\n\n\n<li>Which workflow built it?<\/li>\n\n\n\n<li>Was the build trusted?<\/li>\n\n\n\n<li>Has the artifact changed?<\/li>\n\n\n\n<li>What dependencies are inside it?<\/li>\n\n\n\n<li>Is it deployed to production?<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Security model<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    A&#91;Source] --&gt; B&#91;Trusted Workflow]\n    B --&gt; C&#91;Build]\n    C --&gt; D&#91;Package]\n    D --&gt; E&#91;Provenance]\n    D --&gt; F&#91;SBOM]\n    D --&gt; G&#91;Registry]\n    G --&gt; H&#91;Verification]\n    H --&gt; I&#91;Deployment]\n    I --&gt; J&#91;Runtime \/ Linked Artifact Context]\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Core strategies<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Pin dependencies where practical.<\/li>\n\n\n\n<li>Use lockfiles.<\/li>\n\n\n\n<li>Protect release branches\/tags\/workflows.<\/li>\n\n\n\n<li>Minimize workflow token permissions.<\/li>\n\n\n\n<li>Pin third-party Actions for high-assurance builds.<\/li>\n\n\n\n<li>Use immutable package versions.<\/li>\n\n\n\n<li>Use container digests for deployment.<\/li>\n\n\n\n<li>Generate provenance attestations.<\/li>\n\n\n\n<li>Generate SBOMs.<\/li>\n\n\n\n<li>Scan dependencies and images.<\/li>\n\n\n\n<li>Record runtime\/deployment context.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">32. Artifact Attestations and Build Provenance<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Artifact attestations establish verifiable information about where and how an artifact was built.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Why provenance matters<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Without provenance, a package digest proves identity but not origin. Provenance connects an artifact to trusted build metadata such as repository, workflow, and commit.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Workflow permissions<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A provenance workflow commonly needs:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>permissions:\n  contents: read\n  id-token: write\n  attestations: write\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For linked-artifact storage records using registry publishing, GitHub&#8217;s current linked-artifacts flow can also require:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>artifact-metadata: write\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Verify an attestation<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">For a built artifact:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>gh attestation verify .\/dist\/my-binary \\\n  -R acme\/my-project\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For a container, verification uses the subject image\/digest and the repository\/owner context supported by&nbsp;<code>gh attestation verify<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">SLSA<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">SLSA is a framework for reasoning about supply-chain security and provenance. GitHub attestations can provide signed build provenance that contributes to a stronger SLSA-oriented build process. Do not claim a particular SLSA level solely because an attestation exists; the complete build system and controls matter.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">33. Software Bill of Materials (SBOM)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">An SBOM is a machine-readable inventory of software components and dependencies.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Common formats include:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>SPDX<\/li>\n\n\n\n<li>CycloneDX<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Why SBOMs matter<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">They help teams answer:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Which components are inside this build?<\/li>\n\n\n\n<li>Do we use a vulnerable library?<\/li>\n\n\n\n<li>Which version is deployed?<\/li>\n\n\n\n<li>What should be reviewed during an incident?<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Attesting an SBOM<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">After generating an SBOM file, GitHub Actions can attach an SBOM attestation. GitHub&#8217;s current documentation supports SPDX and CycloneDX predicate flows.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Example verification for an SPDX SBOM attestation:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>gh attestation verify PATH\/TO\/ARTIFACT \\\n  -R acme\/repository \\\n  --predicate-type https:\/\/spdx.dev\/Document\/v2.3\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">SBOM is not a vulnerability scan<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">An SBOM tells you what is present. Vulnerability-management tooling tells you whether known vulnerabilities affect those components. Use both.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">34. Dependency Graph and Dependabot<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The dependency graph summarizes dependencies detected from repository manifests\/lockfiles and dependencies submitted by supported automation\/API flows.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It can show:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Direct dependencies<\/li>\n\n\n\n<li>Transitive dependencies where supported<\/li>\n\n\n\n<li>Versions<\/li>\n\n\n\n<li>Licenses<\/li>\n\n\n\n<li>Known vulnerabilities<\/li>\n\n\n\n<li>Dependents for public ecosystems where available<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Dependency data sources<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub can populate the graph through:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Static manifest\/lockfile analysis<\/li>\n\n\n\n<li>Dependabot graph jobs where supported<\/li>\n\n\n\n<li>Automatic dependency submission<\/li>\n\n\n\n<li>Dependency submission API<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Why lockfiles matter<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A lockfile records the exact resolved dependency set. That improves reproducibility and gives security tooling better evidence than a broad version range alone.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Dependabot integration<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Once dependency data is present, GitHub can use it for:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Dependabot alerts<\/li>\n\n\n\n<li>Dependabot security updates<\/li>\n\n\n\n<li>Dependabot version updates<\/li>\n\n\n\n<li>Dependency review in pull requests<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Private registry access<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">If Dependabot must resolve private dependencies, configure private registry access according to the ecosystem and repository security settings. Do not place credentials in the dependency manifest itself.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">35. Package Vulnerability Management<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A package-security response should connect advisories, releases, and consumers.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Example response flow<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart TD\n    A&#91;Vulnerability identified] --&gt; B&#91;Assess affected versions]\n    B --&gt; C&#91;Fix source\/dependency]\n    C --&gt; D&#91;Build + test]\n    D --&gt; E&#91;Publish patched version]\n    E --&gt; F&#91;Create advisory\/release notes]\n    F --&gt; G&#91;Update consumers]\n    G --&gt; H&#91;Verify production rollout]\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Practical controls<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Track CVEs and GitHub Security Advisories (GHSA).<\/li>\n\n\n\n<li>Publish patched versions rather than replacing existing versions.<\/li>\n\n\n\n<li>Use Dependabot\/security campaigns where appropriate.<\/li>\n\n\n\n<li>Identify production impact using deployment\/runtime context.<\/li>\n\n\n\n<li>Revoke compromised credentials immediately.<\/li>\n\n\n\n<li>Quarantine or delete malicious\/accidentally published packages where safe and permitted.<\/li>\n\n\n\n<li>Preserve evidence during supply-chain incidents.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">36. Linked Artifacts<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Linked artifacts provide an organization-level view of software artifacts built with GitHub Actions, even when the artifact is stored outside GitHub Packages.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The linked artifacts page is metadata-centric: it does not act as the registry storing the artifact bytes.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">What it connects<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    A&#91;GitHub Repository] --&gt; B&#91;GitHub Actions Build]\n    B --&gt; C&#91;Artifact]\n    C --&gt; D&#91;Storage Record]\n    C --&gt; E&#91;Deployment Record]\n    D --&gt; F&#91;Linked Artifacts]\n    E --&gt; F\n    F --&gt; G&#91;Security \/ Audit \/ Policy Context]\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Storage records<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Storage records can describe:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Source repository<\/li>\n\n\n\n<li>Artifact registry<\/li>\n\n\n\n<li>Artifact repository, where applicable<\/li>\n\n\n\n<li>Provenance\/attestations<\/li>\n\n\n\n<li>Build information<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Deployment records<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Deployment records can include:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Environment<\/li>\n\n\n\n<li>Cluster\/runtime context<\/li>\n\n\n\n<li>Production status<\/li>\n\n\n\n<li>Runtime risks such as public internet exposure<\/li>\n\n\n\n<li>Sensitive-data context<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub explicitly distinguishes these records from the repository deployments dashboard; they are separate data sources.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Ways to populate linked artifacts<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Current GitHub documentation lists:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Artifact-attestation workflows<\/li>\n\n\n\n<li>JFrog Artifactory integration<\/li>\n\n\n\n<li>Dynatrace integration<\/li>\n\n\n\n<li>Microsoft Defender for Cloud integration<\/li>\n\n\n\n<li>Custom integrations using the artifact metadata REST API<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub currently marks the Microsoft Defender for Cloud integration as public preview.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Attestation-based storage records<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub documents automatic storage-record creation when the attestation action uses:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>push-to-registry: true\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">and the workflow has:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>permissions:\n  artifact-metadata: write\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Why linked artifacts are useful<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Trace a built artifact to source.<\/li>\n\n\n\n<li>See where it is stored.<\/li>\n\n\n\n<li>See whether it is deployed.<\/li>\n\n\n\n<li>Prioritize Dependabot\/code-scanning findings affecting production.<\/li>\n\n\n\n<li>Export evidence for audit\/compliance.<\/li>\n\n\n\n<li>Associate runtime risk with source repositories.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">37. Access Governance<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A registry can become a shadow production system if package governance is weak.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Recommended access model<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Persona<\/th><th class=\"has-text-align-right\" data-align=\"right\">Read<\/th><th class=\"has-text-align-right\" data-align=\"right\">Write<\/th><th class=\"has-text-align-right\" data-align=\"right\">Admin<\/th><\/tr><\/thead><tbody><tr><td>Application developer<\/td><td class=\"has-text-align-right\" data-align=\"right\">Yes<\/td><td class=\"has-text-align-right\" data-align=\"right\">Usually no<\/td><td class=\"has-text-align-right\" data-align=\"right\">No<\/td><\/tr><tr><td>Package maintainer<\/td><td class=\"has-text-align-right\" data-align=\"right\">Yes<\/td><td class=\"has-text-align-right\" data-align=\"right\">Yes<\/td><td class=\"has-text-align-right\" data-align=\"right\">Sometimes<\/td><\/tr><tr><td>CI release workflow<\/td><td class=\"has-text-align-right\" data-align=\"right\">Yes<\/td><td class=\"has-text-align-right\" data-align=\"right\">Yes<\/td><td class=\"has-text-align-right\" data-align=\"right\">Only when operationally required<\/td><\/tr><tr><td>Platform team<\/td><td class=\"has-text-align-right\" data-align=\"right\">Yes<\/td><td class=\"has-text-align-right\" data-align=\"right\">Controlled<\/td><td class=\"has-text-align-right\" data-align=\"right\">Yes for shared packages<\/td><\/tr><tr><td>Consumer service<\/td><td class=\"has-text-align-right\" data-align=\"right\">Yes<\/td><td class=\"has-text-align-right\" data-align=\"right\">No<\/td><td class=\"has-text-align-right\" data-align=\"right\">No<\/td><\/tr><tr><td>Security\/audit automation<\/td><td class=\"has-text-align-right\" data-align=\"right\">Metadata read<\/td><td class=\"has-text-align-right\" data-align=\"right\">No<\/td><td class=\"has-text-align-right\" data-align=\"right\">No<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Team-based permissions<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Prefer team access over individual grants for organization packages. This reduces long-term permission drift.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Repository inheritance<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Use inheritance when the package&#8217;s lifecycle matches one repository. Break inheritance when the package is a shared organization resource requiring a different audience.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">38. Visibility Governance<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Default posture<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">For proprietary packages:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Start private\/internal.<\/li>\n\n\n\n<li>Grant read access intentionally.<\/li>\n\n\n\n<li>Make a package public only after review.<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Public package checklist<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Before changing visibility to public:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Confirm package contains no proprietary code.<\/li>\n\n\n\n<li>Confirm no embedded credentials\/secrets.<\/li>\n\n\n\n<li>Confirm source\/license metadata is correct.<\/li>\n\n\n\n<li>Confirm public distribution is legally approved.<\/li>\n\n\n\n<li>Confirm package name\/namespace is correct.<\/li>\n\n\n\n<li>Confirm published versions cannot disclose internal build paths or sensitive metadata.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Public package deletion has additional constraints, so publication should be treated as potentially irreversible at scale.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">39. Credential Governance<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Preferred order<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">For GitHub Actions:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><code>GITHUB_TOKEN<\/code><\/li>\n\n\n\n<li>GitHub App token when a service\/app model is needed and endpoint support exists<\/li>\n\n\n\n<li>PAT classic only when required by package-client or cross-boundary behavior<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">For developer package-client use, PAT classic remains the normal GitHub Packages model.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Rotation policy<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Define:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Owner<\/li>\n\n\n\n<li>Purpose<\/li>\n\n\n\n<li>Required scopes<\/li>\n\n\n\n<li>Expiry<\/li>\n\n\n\n<li>Rotation period<\/li>\n\n\n\n<li>Revocation procedure<\/li>\n\n\n\n<li>Incident response<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Machine users<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Do not create a machine user merely to avoid understanding package permissions. Prefer GitHub Apps or repository\/organization automation patterns when they better match the use case.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">40. Publication Governance<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">CI-only publishing<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The strongest default is to publish production packages only from controlled CI.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Benefits:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Reproducibility<\/li>\n\n\n\n<li>Centralized credentials<\/li>\n\n\n\n<li>Audit trail<\/li>\n\n\n\n<li>Consistent tests<\/li>\n\n\n\n<li>Provenance generation<\/li>\n\n\n\n<li>Easier policy enforcement<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Protected release workflow<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A common design:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart TD\n    A&#91;PR reviewed] --&gt; B&#91;Merge]\n    B --&gt; C&#91;CI passes]\n    C --&gt; D&#91;Create protected tag\/release]\n    D --&gt; E&#91;Approval if required]\n    E --&gt; F&#91;Publish]\n    F --&gt; G&#91;Attest + SBOM]\n    G --&gt; H&#91;Promote immutable artifact]\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Naming convention example<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>Libraries:\n  @acme\/&lt;domain&gt;-&lt;purpose&gt;\n\nContainers:\n  ghcr.io\/acme\/&lt;service&gt;\n\nJava:\n  com.acme.&lt;domain&gt;:&lt;artifact&gt;\n\nNuGet:\n  Acme.&lt;Domain&gt;.&lt;Package&gt;\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">41. Consumption Governance<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Consumption is as important as publication.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Recommended controls<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Approved registries<\/li>\n\n\n\n<li>Approved package sources<\/li>\n\n\n\n<li>Lockfiles<\/li>\n\n\n\n<li>Version constraints<\/li>\n\n\n\n<li>Digest pinning for production containers<\/li>\n\n\n\n<li>Vulnerability review<\/li>\n\n\n\n<li>License review<\/li>\n\n\n\n<li>Source mapping where supported<\/li>\n\n\n\n<li>Explicit private registry authentication<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Dependency confusion<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A private package name can collide with a package in a public registry if source resolution is not controlled. Mitigations include scoped npm packages, NuGet package source mapping, explicit Maven\/Gradle repository ordering, and private namespace standards.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">42. CI\/CD Design Patterns<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Build \u2192 Test \u2192 Publish<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The baseline pattern:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>build \u2192 unit test \u2192 integration test \u2192 package \u2192 publish \u2192 verify\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Release-driven publishing<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Only release\/tag events produce production packages.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Multi-job pipeline<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    A&#91;Build] --&gt; B&#91;Unit Tests]\n    B --&gt; C&#91;Integration Tests]\n    C --&gt; D&#91;Security Checks]\n    D --&gt; E&#91;Publish]\n    E --&gt; F&#91;Attest]\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Environment-gated publish<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub Environments can add approvals or protection controls before a production publishing job executes.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Reusable workflow<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Centralize release logic in a reusable workflow so many repositories inherit the same permission model, tagging rules, and attestation behavior.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">43. Monorepo and Multi-Package Publishing<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Challenges<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Which package changed?<\/li>\n\n\n\n<li>Which version should increment?<\/li>\n\n\n\n<li>Should all packages share one version?<\/li>\n\n\n\n<li>How do you avoid publishing unaffected packages?<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Changed-path matrix pattern<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>strategy:\n  matrix:\n    package:\n      - api-client\n      - ui-kit\n      - config\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Combine a matrix with changed-path detection or your monorepo tool of choice.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Independent versions<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Best when packages have separate consumers and release cadences.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Shared versions<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Best when packages form one tightly coupled product release.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Avoid publishing every package on every commit just because the repository contains many packages.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">44. Container CI\/CD Advanced Patterns<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Multi-stage build<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>FROM node:22-alpine AS build\nWORKDIR \/app\nCOPY package*.json .\/\nRUN npm ci\nCOPY . .\nRUN npm run build\n\nFROM nginx:alpine\nCOPY --from=build \/app\/dist \/usr\/share\/nginx\/html\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Advantages:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Smaller runtime image<\/li>\n\n\n\n<li>Build tools absent from final image<\/li>\n\n\n\n<li>Reduced attack surface<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Layer caching<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">BuildKit\/Buildx can reuse unchanged layers. Cache configuration should never cause secrets or credentials to be baked into image layers.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Digest capture<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The build\/push action exposes the published digest. Save it as release metadata and use it for deployment\/attestation.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Multi-architecture<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Use Buildx and QEMU\/native builders to publish AMD64 and ARM64 manifests under one tag.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Secure publishing<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Minimal\u00a0<code>packages: write<\/code><\/li>\n\n\n\n<li>Trusted trigger<\/li>\n\n\n\n<li>Protected source<\/li>\n\n\n\n<li>Pinned Actions<\/li>\n\n\n\n<li>Provenance<\/li>\n\n\n\n<li>SBOM<\/li>\n\n\n\n<li>Vulnerability scanning<\/li>\n\n\n\n<li>Digest-based deployment<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">45. Deployment Integration<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub Packages containers can be consumed by Docker and container orchestration platforms such as Kubernetes and cloud container services.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Kubernetes private image pull<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A typical secret-based approach:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl create secret docker-registry ghcr-pull \\\n  --docker-server=ghcr.io \\\n  --docker-username=YOUR_USERNAME \\\n  --docker-password=YOUR_PAT_CLASSIC \\\n  --namespace=my-app\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Pod snippet:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: v1\nkind: Pod\nmetadata:\n  name: api\nspec:\n  imagePullSecrets:\n    - name: ghcr-pull\n  containers:\n    - name: api\n      image: ghcr.io\/acme\/api@sha256:REPLACE_WITH_DIGEST\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For production, evaluate workload identity, secret rotation, external secret managers, and platform-native registry integration rather than relying on manually maintained long-lived pull secrets.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Common deployment targets<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Target<\/th><th class=\"has-text-align-left\" data-align=\"left\">Typical GitHub Packages use<\/th><th class=\"has-text-align-left\" data-align=\"left\">Main consideration<\/th><\/tr><\/thead><tbody><tr><td>Docker hosts<\/td><td>Pull GHCR image directly<\/td><td>Credential rotation and digest pinning<\/td><\/tr><tr><td>Kubernetes<\/td><td><code>imagePullSecrets<\/code>&nbsp;or platform identity<\/td><td>Namespace secret scope and rotation<\/td><\/tr><tr><td>Amazon ECS \/ EKS<\/td><td>Deploy GHCR-hosted OCI image<\/td><td>Private-registry credentials \/ secret integration<\/td><\/tr><tr><td>Azure AKS \/ Container Apps<\/td><td>Deploy OCI image<\/td><td>Registry credentials and platform secret model<\/td><\/tr><tr><td>Google GKE \/ Cloud Run<\/td><td>Deploy OCI image<\/td><td>External-registry authentication and egress policy<\/td><\/tr><tr><td>Other OCI runtimes<\/td><td>Pull by tag or digest<\/td><td>Verify OCI compatibility and auth model<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub Packages is the artifact source; each runtime still has its own secure secret, identity, network, and deployment controls.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">46. Package Automation Patterns<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Automated publishing<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Triggers can include:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Tag<\/li>\n\n\n\n<li>GitHub Release<\/li>\n\n\n\n<li>Protected branch<\/li>\n\n\n\n<li>Manual dispatch<\/li>\n\n\n\n<li>Schedule for rebuilds<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Automated cleanup<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A safe cleanup automation should:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>List versions.<\/li>\n\n\n\n<li>Classify protected releases.<\/li>\n\n\n\n<li>Identify candidates by age\/count\/tag.<\/li>\n\n\n\n<li>Print a dry-run report.<\/li>\n\n\n\n<li>Require approval for destructive classes if risk is high.<\/li>\n\n\n\n<li>Delete by immutable version ID.<\/li>\n\n\n\n<li>Log results.<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Automated promotion<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">For containers, promotion should retag or record an existing digest rather than rebuild.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Event automation<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Use package webhooks to trigger notifications, scanning, catalog registration, or downstream orchestration.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">47. Troubleshooting Authentication<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Authentication failures are usually caused by the wrong token type, insufficient scopes, SSO authorization, an incorrect registry endpoint, or a credential not being presented by the client.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Symptom<\/th><th class=\"has-text-align-left\" data-align=\"left\">Likely cause<\/th><th class=\"has-text-align-left\" data-align=\"left\">Diagnose<\/th><th class=\"has-text-align-left\" data-align=\"left\">Fix<\/th><\/tr><\/thead><tbody><tr><td><code>401 Unauthorized<\/code><\/td><td>Invalid\/expired token<\/td><td>Re-authenticate; inspect token age\/scopes<\/td><td>Replace\/rotate token<\/td><\/tr><tr><td>Login succeeds but install fails<\/td><td>Missing&nbsp;<code>read:packages<\/code>&nbsp;or package ACL<\/td><td>Check package access and token scope<\/td><td>Add minimum read access<\/td><\/tr><tr><td>Publish denied<\/td><td>Missing&nbsp;<code>write:packages<\/code>&nbsp;or write role<\/td><td>Check token + package\/repository permission<\/td><td>Grant write only where required<\/td><\/tr><tr><td>Works locally, fails in Actions<\/td><td><code>GITHUB_TOKEN<\/code>&nbsp;permissions too narrow<\/td><td>Inspect workflow&nbsp;<code>permissions<\/code><\/td><td>Add&nbsp;<code>packages: read\/write<\/code><\/td><\/tr><tr><td>Cross-repo package not found<\/td><td>Workflow repo not granted package access<\/td><td>Package settings \u2192 Actions access<\/td><td>Grant repo access<\/td><\/tr><tr><td>Organization access fails<\/td><td>PAT not SSO-authorized<\/td><td>Check org SSO authorization<\/td><td>Authorize token for org<\/td><\/tr><tr><td>Wrong endpoint<\/td><td>Client points to public\/default registry<\/td><td>Inspect client config<\/td><td>Use correct GitHub Packages endpoint<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Diagnostic principle<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Separate the layers:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Can the client reach the registry?<\/li>\n\n\n\n<li>Is the credential accepted?<\/li>\n\n\n\n<li>Does the credential have the required scope?<\/li>\n\n\n\n<li>Does the underlying user\/repository have package permission?<\/li>\n\n\n\n<li>Is the package in the expected namespace?<\/li>\n<\/ol>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">48. Troubleshooting Authorization<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A valid token can still be unauthorized.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Read denied<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Check:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Package visibility<\/li>\n\n\n\n<li>User\/team read role<\/li>\n\n\n\n<li>Repository inheritance<\/li>\n\n\n\n<li>Actions access grant<\/li>\n\n\n\n<li><code>packages: read<\/code>\u00a0workflow permission<\/li>\n\n\n\n<li>Repository-scoped package permissions<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Write denied<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Check:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Package write role<\/li>\n\n\n\n<li><code>write:packages<\/code>\u00a0PAT scope<\/li>\n\n\n\n<li><code>packages: write<\/code>\u00a0workflow permission<\/li>\n\n\n\n<li>Publishing namespace<\/li>\n\n\n\n<li>Repository ownership for Maven\/Gradle<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Admin denied<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Deletion\/access changes require admin-level control. A CI workflow should not receive admin unless its specific operation needs it.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Inheritance confusion<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">If a package inherits repository permissions, changing explicit package ACLs may not behave as expected until inherited access is removed. Understand which model is active before changing permissions.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">49. Troubleshooting Publishing<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Problem<\/th><th class=\"has-text-align-left\" data-align=\"left\">Likely cause<\/th><th class=\"has-text-align-left\" data-align=\"left\">Better approach<\/th><\/tr><\/thead><tbody><tr><td>Version already exists<\/td><td>Immutable version collision<\/td><td>Increment version; do not reuse release number<\/td><\/tr><tr><td>Namespace conflict<\/td><td>Wrong owner\/package scope<\/td><td>Verify package name and owner<\/td><\/tr><tr><td>Package published to wrong repo<\/td><td>Incorrect source\/destination metadata<\/td><td>Check repository\/package config<\/td><\/tr><tr><td>npm publish goes to npmjs.org<\/td><td><code>.npmrc<\/code>\/<code>publishConfig<\/code>&nbsp;incorrect<\/td><td>Explicitly configure scope\/registry<\/td><\/tr><tr><td>Maven deploy fails<\/td><td><code>distributionManagement<\/code>\/server ID mismatch<\/td><td>Match&nbsp;<code>pom.xml<\/code>&nbsp;server ID to&nbsp;<code>settings.xml<\/code><\/td><\/tr><tr><td>Gradle credentials missing<\/td><td>Environment variables unavailable<\/td><td>Inject&nbsp;<code>GITHUB_ACTOR<\/code>\/<code>GITHUB_TOKEN<\/code><\/td><\/tr><tr><td>NuGet source not found<\/td><td><code>nuget.config<\/code>&nbsp;source mismatch<\/td><td>Validate source URL\/name<\/td><\/tr><tr><td>Container push denied<\/td><td>Wrong namespace\/token permission<\/td><td>Validate&nbsp;<code>ghcr.io\/OWNER\/IMAGE<\/code>&nbsp;and write access<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Verify before publish<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code><em># npm<\/em>\nnpm pack --dry-run\n\n<em># Maven<\/em>\nmvn --batch-mode verify\n\n<em># Gradle<\/em>\n.\/gradlew build\n\n<em># .NET<\/em>\ndotnet pack --configuration Release\n\n<em># Ruby<\/em>\ngem build your-package.gemspec\n\n<em># Container<\/em>\ndocker build -t local\/test:verify .\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">50. Troubleshooting Installation and Dependency Resolution<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Package not found<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Possible causes:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Wrong package name\/version<\/li>\n\n\n\n<li>Wrong registry URL<\/li>\n\n\n\n<li>Package is private and authentication is missing<\/li>\n\n\n\n<li>Cross-repository access is missing<\/li>\n\n\n\n<li>Client is querying the public registry first\/only<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Dependency resolution failure<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Inspect the package manager&#8217;s source configuration before blaming GitHub Packages.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Examples:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>npm config list\nmvn help:effective-settings\n.\/gradlew repositories\n\ndotnet nuget list source\nbundle config list\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Version mismatch<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Use explicit versions while diagnosing. Avoid floating ranges or mutable tags until basic connectivity is confirmed.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">51. Container Registry Troubleshooting<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Login<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>echo \"$CR_PAT\" | docker login ghcr.io -u \"$GITHUB_USER\" --password-stdin\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If login fails, verify the PAT classic, scopes, SSO authorization, and account name.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Image not found<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>docker pull ghcr.io\/acme\/api:1.0.0\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Check:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Owner capitalization\/name<\/li>\n\n\n\n<li>Package visibility<\/li>\n\n\n\n<li>Tag existence<\/li>\n\n\n\n<li>Package ACL<\/li>\n\n\n\n<li>Authentication<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Architecture mismatch<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Symptom:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>exec format error\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Inspect image platforms:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker buildx imagetools inspect ghcr.io\/acme\/api:1.0.0\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Publish a multi-architecture image or deploy the matching platform.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Tag vs digest confusion<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>docker inspect --format='{{index .RepoDigests 0}}' ghcr.io\/acme\/api:1.0.0\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use digests when validating whether two environments run identical content.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">52. CI\/CD Troubleshooting<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Workflow permission error<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Inspect the job&#8217;s effective permissions:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>permissions:\n  contents: read\n  packages: write\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Do not assume repository defaults provide write access.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Pull-request security<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Do not publish packages from untrusted fork PR code using privileged secrets\/tokens. Split validation from trusted publish jobs.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Self-hosted runners<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A compromised self-hosted runner may expose credentials, source, build outputs, or package tokens. Isolate runner groups and use ephemeral runners for high-value release workloads where practical.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Version collisions<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">If many parallel jobs publish the same version, only one can logically own that immutable release. Generate the version once and coordinate publishing.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">53. Registry Migration<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A registry migration is not just copying files. It includes metadata, credentials, consumers, automation, access, and rollback.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Migration phases<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    A&#91;Inventory] --&gt; B&#91;Map namespaces\/versions]\n    B --&gt; C&#91;Design access]\n    C --&gt; D&#91;Copy\/publish artifacts]\n    D --&gt; E&#91;Update CI publishers]\n    E --&gt; F&#91;Update consumers]\n    F --&gt; G&#91;Validate]\n    G --&gt; H&#91;Freeze old registry]\n    H --&gt; I&#91;Retire]\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Inventory<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Capture:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Packages<\/li>\n\n\n\n<li>Versions\/tags\/digests<\/li>\n\n\n\n<li>Owners<\/li>\n\n\n\n<li>Visibility<\/li>\n\n\n\n<li>Consumers<\/li>\n\n\n\n<li>CI publishers<\/li>\n\n\n\n<li>Credentials<\/li>\n\n\n\n<li>Retention rules<\/li>\n\n\n\n<li>Download\/usage patterns<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Consumer migration<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Update:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Registry URL<\/li>\n\n\n\n<li>Package source config<\/li>\n\n\n\n<li>Credentials<\/li>\n\n\n\n<li>Lockfiles if source metadata changes<\/li>\n\n\n\n<li>CI workflows<\/li>\n\n\n\n<li>Deployment pull secrets<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Rollback<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Keep the previous registry read-only for a defined period when business requirements allow. Do not delete source artifacts until consumers have been verified.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">54. Legacy Docker Registry Migration<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The legacy endpoint:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker.pkg.github.com\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">has been replaced by:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ghcr.io\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">What changes<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Image naming<\/li>\n\n\n\n<li>Authentication configuration<\/li>\n\n\n\n<li>Workflow registry endpoint<\/li>\n\n\n\n<li>Package permission model<\/li>\n\n\n\n<li>Repository association behavior<\/li>\n\n\n\n<li>Consumer references<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Example conceptual change<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Old:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker.pkg.github.com\/acme\/api\/api:1.0.0\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Modern:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ghcr.io\/acme\/api:1.0.0\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Do not start new platform designs on the legacy Docker registry.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">55. Observability, Activity, and Auditing<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Package observability asks more than &#8220;is the registry up?&#8221;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Track:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Package inventory<\/li>\n\n\n\n<li>Version count<\/li>\n\n\n\n<li>Publication activity<\/li>\n\n\n\n<li>Download activity where exposed<\/li>\n\n\n\n<li>Package ownership<\/li>\n\n\n\n<li>Repository links<\/li>\n\n\n\n<li>Storage usage<\/li>\n\n\n\n<li>Data transfer<\/li>\n\n\n\n<li>Deletion events<\/li>\n\n\n\n<li>Provenance<\/li>\n\n\n\n<li>Deployment context<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Audit questions<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Who can publish this package?<\/li>\n\n\n\n<li>Which workflow published version\u00a0<code>2.7.3<\/code>?<\/li>\n\n\n\n<li>Which commit produced this digest?<\/li>\n\n\n\n<li>Which repositories can consume it?<\/li>\n\n\n\n<li>Is it deployed to production?<\/li>\n\n\n\n<li>Does it have provenance?<\/li>\n\n\n\n<li>Is an SBOM available?<\/li>\n\n\n\n<li>Which vulnerable dependencies affect it?<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Linked-artifact value<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Linked artifacts help connect build, storage, provenance, and deployment metadata into an auditable view rather than forcing teams to reconstruct the story manually.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">56. Organization Usage Reporting<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">At minimum, establish a periodic inventory report containing:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Field<\/th><th class=\"has-text-align-left\" data-align=\"left\">Why it matters<\/th><\/tr><\/thead><tbody><tr><td>Package<\/td><td>Asset identity<\/td><\/tr><tr><td>Type<\/td><td>Registry\/ecosystem<\/td><\/tr><tr><td>Visibility<\/td><td>Exposure risk<\/td><\/tr><tr><td>Owner<\/td><td>Accountability<\/td><\/tr><tr><td>Source repository<\/td><td>Traceability<\/td><\/tr><tr><td>Versions<\/td><td>Retention pressure<\/td><\/tr><tr><td>Last updated<\/td><td>Staleness<\/td><\/tr><tr><td>Production use<\/td><td>Business criticality<\/td><\/tr><tr><td>Provenance<\/td><td>Supply-chain assurance<\/td><\/tr><tr><td>Storage<\/td><td>Cost\/capacity<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Simple REST inventory<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>gh api --paginate \\\n  -H \"X-GitHub-Api-Version: 2026-03-10\" \\\n  \"\/orgs\/acme\/packages?package_type=container&amp;per_page=100\" \\\n  --jq '.&#91;] | {name,visibility,version_count,updated_at,html_url}'\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Repeat per package type as needed.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">57. Best Practices<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Package design<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Recommended:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Clear, stable names<\/li>\n\n\n\n<li>Organization ownership for company artifacts<\/li>\n\n\n\n<li>Semantic versions for libraries<\/li>\n\n\n\n<li>Immutable release versions<\/li>\n\n\n\n<li>Source repository linkage<\/li>\n\n\n\n<li>License\/description metadata<\/li>\n\n\n\n<li>Installation documentation<\/li>\n\n\n\n<li>Explicit package owner\/team<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Avoid:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Ambiguous names<\/li>\n\n\n\n<li>User-owned business-critical packages<\/li>\n\n\n\n<li>Version reuse<\/li>\n\n\n\n<li>Packages without source traceability<\/li>\n\n\n\n<li>Packages nobody owns<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Security<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Recommended:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Least privilege<\/li>\n\n\n\n<li><code>GITHUB_TOKEN<\/code>\u00a0in Actions<\/li>\n\n\n\n<li>Minimal PAT classic scopes<\/li>\n\n\n\n<li>Credential rotation<\/li>\n\n\n\n<li>Protected releases<\/li>\n\n\n\n<li>Pinned third-party Actions for high-assurance workflows<\/li>\n\n\n\n<li>Provenance<\/li>\n\n\n\n<li>SBOM<\/li>\n\n\n\n<li>Vulnerability scanning<\/li>\n\n\n\n<li>Digest-based production deployments<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Avoid:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Long-lived admin tokens<\/li>\n\n\n\n<li>Shared PATs<\/li>\n\n\n\n<li>Secrets in package config files<\/li>\n\n\n\n<li>Publishing from untrusted PR jobs<\/li>\n\n\n\n<li>Mutable-only production tags<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">CI\/CD<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Recommended:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Test before publish<\/li>\n\n\n\n<li>Release\/tag-driven production publishing<\/li>\n\n\n\n<li>Reusable workflows<\/li>\n\n\n\n<li>Build once, deploy many<\/li>\n\n\n\n<li>Automated cleanup with dry run<\/li>\n\n\n\n<li>Release metadata that captures digest\/version<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Avoid:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Rebuilding per environment<\/li>\n\n\n\n<li>Manual production publishing as the normal path<\/li>\n\n\n\n<li>Publishing every commit forever without retention<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Organization governance<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Recommended:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Team-based permissions<\/li>\n\n\n\n<li>Default visibility rules<\/li>\n\n\n\n<li>Naming policy<\/li>\n\n\n\n<li>Versioning policy<\/li>\n\n\n\n<li>Retention policy<\/li>\n\n\n\n<li>Usage monitoring<\/li>\n\n\n\n<li>Audit ownership<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">58. Common Anti-Patterns<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Anti-pattern<\/th><th class=\"has-text-align-left\" data-align=\"left\">Why it is risky<\/th><th class=\"has-text-align-left\" data-align=\"left\">Better approach<\/th><\/tr><\/thead><tbody><tr><td>Hardcoded token<\/td><td>Credential leak<\/td><td>Secret manager \/&nbsp;<code>GITHUB_TOKEN<\/code><\/td><\/tr><tr><td>One PAT shared by team<\/td><td>No individual accountability<\/td><td>Team ACL + individual auth<\/td><\/tr><tr><td>Admin token for downloads<\/td><td>Excess privilege<\/td><td>Read-only token\/access<\/td><\/tr><tr><td>Publish only from laptop<\/td><td>Weak reproducibility\/audit<\/td><td>CI-first publishing<\/td><\/tr><tr><td>Reuse version numbers<\/td><td>Breaks immutability\/trust<\/td><td>New version per change<\/td><\/tr><tr><td>Only use&nbsp;<code>latest<\/code><\/td><td>Cannot identify artifact<\/td><td>SemVer + SHA + digest<\/td><\/tr><tr><td>No source link<\/td><td>Weak traceability<\/td><td>Connect package to repo<\/td><\/tr><tr><td>Unlimited snapshots<\/td><td>Storage\/cost noise<\/td><td>Retention policy<\/td><\/tr><tr><td>Public by accident<\/td><td>Data exposure<\/td><td>Private\/internal default<\/td><\/tr><tr><td>No provenance<\/td><td>Hard to verify origin<\/td><td>Artifact attestations<\/td><\/tr><tr><td>No SBOM<\/td><td>Poor component visibility<\/td><td>Generate SBOM<\/td><\/tr><tr><td>Rebuild for prod<\/td><td>Different bytes than tested<\/td><td>Promote same immutable artifact<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">59. Quick Reference Cheat Sheet<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Registry endpoints<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Registry<\/th><th class=\"has-text-align-left\" data-align=\"left\">Endpoint \/ pattern<\/th><\/tr><\/thead><tbody><tr><td>Container<\/td><td><code>ghcr.io\/OWNER\/IMAGE<\/code><\/td><\/tr><tr><td>npm<\/td><td><code>https:\/\/npm.pkg.github.com<\/code><\/td><\/tr><tr><td>Maven<\/td><td><code>https:\/\/maven.pkg.github.com\/OWNER\/REPOSITORY<\/code><\/td><\/tr><tr><td>Gradle<\/td><td>Maven-compatible GitHub Packages repository URL<\/td><\/tr><tr><td>NuGet<\/td><td><code>https:\/\/nuget.pkg.github.com\/NAMESPACE\/index.json<\/code><\/td><\/tr><tr><td>RubyGems<\/td><td><code>https:\/\/rubygems.pkg.github.com\/NAMESPACE<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Token scopes<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>read:packages\nwrite:packages\ndelete:packages\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Workflow permissions<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Read:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>permissions:\n  contents: read\n  packages: read\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Publish:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>permissions:\n  contents: read\n  packages: write\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Attest:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>permissions:\n  contents: read\n  packages: write\n  attestations: write\n  id-token: write\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Container commands<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>docker login ghcr.io\ndocker build -t ghcr.io\/acme\/api:1.0.0 .\ndocker push ghcr.io\/acme\/api:1.0.0\ndocker pull ghcr.io\/acme\/api:1.0.0\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">npm<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>npm publish\nnpm install @acme\/pkg@1.0.0\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Maven<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>mvn --batch-mode verify\nmvn --batch-mode deploy\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Gradle<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>.\/gradlew build\n.\/gradlew publish\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">NuGet<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>dotnet pack --configuration Release\ndotnet nuget push PACKAGE.nupkg --source github --api-key TOKEN\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">RubyGems<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>gem build package.gemspec\ngem push --host https:\/\/rubygems.pkg.github.com\/NAMESPACE package.gem\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">REST with GitHub CLI<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>gh api \"\/orgs\/ORG\/packages?package_type=container\"\ngh api \"\/orgs\/ORG\/packages\/container\/NAME\/versions\"\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">60. Beginner \u2192 Intermediate \u2192 Advanced Learning Map<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Level<\/th><th class=\"has-text-align-left\" data-align=\"left\">Learn and practice<\/th><\/tr><\/thead><tbody><tr><td>Beginner<\/td><td>Registry purpose, supported ecosystems, auth, publish\/install, visibility, versions<\/td><\/tr><tr><td>Intermediate<\/td><td>Actions integration, cross-repo access, REST API, cleanup, billing, repository links<\/td><\/tr><tr><td>Advanced<\/td><td>Build-once promotion, attestations, SBOM, linked artifacts, governance, migration, organization reporting<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Recommended training order<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Publish\/pull one public or private container.<\/li>\n\n\n\n<li>Publish one language package.<\/li>\n\n\n\n<li>Move publishing into GitHub Actions.<\/li>\n\n\n\n<li>Add a separate consumer repository.<\/li>\n\n\n\n<li>Add versioning and release triggers.<\/li>\n\n\n\n<li>Add provenance\/SBOM.<\/li>\n\n\n\n<li>Add retention automation.<\/li>\n\n\n\n<li>Build an organization-level governance model.<\/li>\n<\/ol>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">61. Hands-On Exercises<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Exercise 1 \u2014 Beginner: Publish a container<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Objective:<\/strong>&nbsp;Publish and pull a versioned container from GHCR.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Tasks:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Create a repository with a small Dockerfile.<\/li>\n\n\n\n<li>Build\u00a0<code>1.0.0<\/code>\u00a0locally.<\/li>\n\n\n\n<li>Authenticate to GHCR.<\/li>\n\n\n\n<li>Push the image.<\/li>\n\n\n\n<li>Open the package page.<\/li>\n\n\n\n<li>Pull the image on a clean machine\/session.<\/li>\n\n\n\n<li>Record the digest.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Expected outcome: a package exists under the intended owner and can be pulled using the expected access model.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Exercise 2 \u2014 Intermediate: CI package publishing<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Objective:<\/strong>&nbsp;Publish from GitHub Actions without storing a PAT.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Tasks:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Create a release-triggered workflow.<\/li>\n\n\n\n<li>Set\u00a0<code>contents: read<\/code>\u00a0and\u00a0<code>packages: write<\/code>.<\/li>\n\n\n\n<li>Authenticate with\u00a0<code>GITHUB_TOKEN<\/code>.<\/li>\n\n\n\n<li>Run tests before publishing.<\/li>\n\n\n\n<li>Publish the package.<\/li>\n\n\n\n<li>Verify the package version from a second workflow.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Expected outcome: publishing is reproducible and tied to a GitHub workflow run.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Exercise 3 \u2014 Intermediate: Cross-repository consumption<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Objective:<\/strong>&nbsp;Allow a consumer repository to read a private granular package.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Tasks:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Publish a private package from repo A.<\/li>\n\n\n\n<li>Add repo B under package Actions access.<\/li>\n\n\n\n<li>Give repo B read access only.<\/li>\n\n\n\n<li>Set\u00a0<code>packages: read<\/code>\u00a0in repo B workflow.<\/li>\n\n\n\n<li>Install\/pull the package.<\/li>\n\n\n\n<li>Remove access and observe the failure.<\/li>\n\n\n\n<li>Restore read access.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Expected outcome: students understand package ACLs separately from token scopes.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Exercise 4 \u2014 Advanced: Immutable promotion<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Objective:<\/strong>&nbsp;Demonstrate build once, deploy many.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Tasks:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Build and push a container.<\/li>\n\n\n\n<li>Capture digest.<\/li>\n\n\n\n<li>Deploy that digest to dev.<\/li>\n\n\n\n<li>Promote the same digest to stage.<\/li>\n\n\n\n<li>Verify both environments use identical digest.<\/li>\n\n\n\n<li>Promote to production.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Expected outcome: no environment performs a rebuild.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Exercise 5 \u2014 Security: Provenance and SBOM<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Objective:<\/strong>&nbsp;Add supply-chain evidence.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Tasks:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Add artifact-attestation permissions.<\/li>\n\n\n\n<li>Generate provenance after publishing.<\/li>\n\n\n\n<li>Generate an SPDX or CycloneDX SBOM.<\/li>\n\n\n\n<li>Attest the SBOM.<\/li>\n\n\n\n<li>Verify the artifact with GitHub CLI.<\/li>\n\n\n\n<li>Inspect the attestation output.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Expected outcome: students can prove where an artifact came from and inspect its component inventory.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">62. Complete Practical Project \u2014 Organization Package Platform<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Requirement<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Design a small but production-oriented GitHub Packages platform for an organization named&nbsp;<code>acme<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The organization has:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>payments-api<\/code>\u00a0\u2014 containerized service<\/li>\n\n\n\n<li><code>orders-api<\/code>\u00a0\u2014 containerized service<\/li>\n\n\n\n<li><code>shared-types<\/code>\u00a0\u2014 npm package<\/li>\n\n\n\n<li><code>payments-sdk<\/code>\u00a0\u2014 Maven package<\/li>\n\n\n\n<li>Staging and production environments<\/li>\n\n\n\n<li>GitHub Actions for CI\/CD<\/li>\n\n\n\n<li>A requirement for least privilege, traceability, reproducibility, and cleanup<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Target architecture<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart TB\n    subgraph Source\n      P&#91;payments-api]\n      O&#91;orders-api]\n      N&#91;shared-types]\n      M&#91;payments-sdk]\n    end\n\n    P --&gt; PA&#91;Payments CI]\n    O --&gt; OA&#91;Orders CI]\n    N --&gt; NA&#91;npm CI]\n    M --&gt; MA&#91;Maven CI]\n\n    PA --&gt; GHCR&#91;(GHCR)]\n    OA --&gt; GHCR\n    NA --&gt; NPM&#91;(GitHub npm Registry)]\n    MA --&gt; MAVEN&#91;(GitHub Maven Registry)]\n\n    GHCR --&gt; STG&#91;Staging]\n    GHCR --&gt; PROD&#91;Production]\n\n    PA --&gt; AT&#91;Attestations]\n    OA --&gt; AT\n    AT --&gt; LA&#91;Linked Artifacts]\n    STG --&gt; LA\n    PROD --&gt; LA\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Design decisions<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Containers use\u00a0<code>ghcr.io\/acme\/&lt;service><\/code>.<\/li>\n\n\n\n<li>Production image deployment uses digest references.<\/li>\n\n\n\n<li>npm package is organization scoped with granular permissions.<\/li>\n\n\n\n<li>Maven package remains repository scoped.<\/li>\n\n\n\n<li>Publishing happens only through trusted workflows.<\/li>\n\n\n\n<li>Workflows use\u00a0<code>GITHUB_TOKEN<\/code>\u00a0wherever the package access model allows it.<\/li>\n\n\n\n<li>Production publishing\/deployment requires protected release controls.<\/li>\n\n\n\n<li>Production containers receive provenance attestations.<\/li>\n\n\n\n<li>SBOMs are generated for production images.<\/li>\n\n\n\n<li>Snapshot\/dev package retention is automated.<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Repository structure<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>payments-api\/\n\u251c\u2500\u2500 .github\/\n\u2502   \u2514\u2500\u2500 workflows\/\n\u2502       \u251c\u2500\u2500 ci.yml\n\u2502       \u2514\u2500\u2500 release.yml\n\u251c\u2500\u2500 src\/\n\u251c\u2500\u2500 tests\/\n\u251c\u2500\u2500 Dockerfile\n\u2514\u2500\u2500 README.md\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Release workflow<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>name: Release container\n\non:\n  release:\n    types: &#91;published]\n\nenv:\n  REGISTRY: ghcr.io\n  IMAGE_NAME: ${{ github.repository }}\n\njobs:\n  release:\n    runs-on: ubuntu-latest\n    environment: production-release\n    permissions:\n      contents: read\n      packages: write\n      attestations: write\n      id-token: write\n\n    steps:\n      - uses: actions\/checkout@v6\n\n      - name: Test\n        run: .\/scripts\/test.sh\n\n      - name: Login\n        uses: docker\/login-action@v3\n        with:\n          registry: ghcr.io\n          username: ${{ github.actor }}\n          password: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: Metadata\n        id: meta\n        uses: docker\/metadata-action@v5\n        with:\n          images: ghcr.io\/${{ github.repository }}\n          tags: |\n            type=semver,pattern={{version}}\n            type=sha\n\n      - name: Build and push\n        id: build\n        uses: docker\/build-push-action@v6\n        with:\n          context: .\n          push: true\n          tags: ${{ steps.meta.outputs.tags }}\n          labels: ${{ steps.meta.outputs.labels }}\n\n      - name: Attest\n        uses: actions\/attest@v4\n        with:\n          subject-name: ghcr.io\/${{ github.repository }}\n          subject-digest: ${{ steps.build.outputs.digest }}\n          push-to-registry: true\n\n      - name: Save digest\n        run: echo '${{ steps.build.outputs.digest }}' &gt; image-digest.txt\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Staging deployment<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Consume the digest produced by the release pipeline rather than rebuilding:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>containers:\n  - name: payments-api\n    image: ghcr.io\/acme\/payments-api@sha256:RELEASE_DIGEST\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Production promotion<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The production change references the exact same digest that passed staging validation.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">npm shared package access<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><code>shared-types<\/code>&nbsp;is granted:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Write: owning platform\/package-maintainer team<\/li>\n\n\n\n<li>Read:\u00a0<code>payments-api<\/code>\u00a0and\u00a0<code>orders-api<\/code>\u00a0repositories through Actions access<\/li>\n\n\n\n<li>Admin: small package-owner group<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Consumer workflow:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>permissions:\n  contents: read\n  packages: read\n\nsteps:\n  - uses: actions\/checkout@v6\n  - uses: actions\/setup-node@v7\n    with:\n      node-version: '20.x'\n      registry-url: 'https:\/\/npm.pkg.github.com'\n      scope: '@acme'\n  - run: npm ci\n    env:\n      NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Cleanup policy<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Production release versions: protected from automated deletion.<\/li>\n\n\n\n<li>Release candidates: keep last 10.<\/li>\n\n\n\n<li>Branch builds: keep 14 days.<\/li>\n\n\n\n<li>Pull-request builds: keep 7 days after PR close.<\/li>\n\n\n\n<li>Cleanup workflow first generates a report, then deletes only approved candidates.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Validation checklist<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Package visible under correct organization\/repository.<\/li>\n\n\n\n<li>Source connection correct.<\/li>\n\n\n\n<li>Producer has write; consumers have read only.<\/li>\n\n\n\n<li>Workflow succeeds without long-lived PAT.<\/li>\n\n\n\n<li>Release tags\/version match policy.<\/li>\n\n\n\n<li>Container digest recorded.<\/li>\n\n\n\n<li>Staging and production use same digest.<\/li>\n\n\n\n<li>Provenance verifies.<\/li>\n\n\n\n<li>SBOM is retrievable\/inspectable.<\/li>\n\n\n\n<li>Cleanup excludes protected releases.<\/li>\n\n\n\n<li>Audit owner is documented.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Improvement ideas<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Pin all non-GitHub Actions to reviewed full commit SHAs.<\/li>\n\n\n\n<li>Add reusable organization release workflows.<\/li>\n\n\n\n<li>Add policy-as-code around naming and allowed registries.<\/li>\n\n\n\n<li>Integrate linked artifact deployment metadata.<\/li>\n\n\n\n<li>Add runtime risk context through supported integrations\/API.<\/li>\n\n\n\n<li>Export monthly package inventory and orphan-package report.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">63. Interview and Knowledge-Check Questions<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Conceptual<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>What problem does GitHub Packages solve?<\/li>\n\n\n\n<li>How is a package registry different from GitHub Releases?<\/li>\n\n\n\n<li>Which GitHub Packages registries support granular permissions?<\/li>\n\n\n\n<li>Which registries are repository scoped?<\/li>\n\n\n\n<li>What is the difference between package visibility and package role?<\/li>\n\n\n\n<li>What is the difference between a container tag and digest?<\/li>\n\n\n\n<li>Why is\u00a0<code>GITHUB_TOKEN<\/code>\u00a0usually preferable to a PAT in GitHub Actions?<\/li>\n\n\n\n<li>Why should release versions be immutable?<\/li>\n\n\n\n<li>What is repository permission inheritance?<\/li>\n\n\n\n<li>What happens to granular packages when a connected repository is transferred?<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Scenario based<\/h3>\n\n\n\n<ol start=\"11\" class=\"wp-block-list\">\n<li>A workflow in repo B must install a private package published from repo A. How would you design access?<\/li>\n\n\n\n<li>A team deploys\u00a0<code>latest<\/code>\u00a0to production. What risks does that introduce?<\/li>\n\n\n\n<li>A package has 100 old development versions. How would you automate cleanup safely?<\/li>\n\n\n\n<li>An npm package publishes to npmjs.org instead of GitHub Packages. What would you inspect?<\/li>\n\n\n\n<li>A container works on AMD64 but fails on ARM64. How would you diagnose it?<\/li>\n\n\n\n<li>A developer can authenticate to GHCR but cannot push. What layers of authorization do you check?<\/li>\n\n\n\n<li>You must prove which commit produced a production image. Which GitHub features help?<\/li>\n\n\n\n<li>You are migrating from\u00a0<code>docker.pkg.github.com<\/code>\u00a0to\u00a0<code>ghcr.io<\/code>. What must change beyond image copying?<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Troubleshooting<\/h3>\n\n\n\n<ol start=\"19\" class=\"wp-block-list\">\n<li>What is the difference between HTTP 401 and a permission-denied workflow failure?<\/li>\n\n\n\n<li>Why can a valid PAT still fail to download a package?<\/li>\n\n\n\n<li>How can repository transfer break package consumption?<\/li>\n\n\n\n<li>Why can rebuilding for production break provenance even when the Git commit is unchanged?<\/li>\n\n\n\n<li>When would REST be preferred over GraphQL for packages?<\/li>\n\n\n\n<li>What should be checked before deleting a public package?<\/li>\n<\/ol>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">64. FAQ<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Do GitHub Packages support public and private packages?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Yes, with behavior depending on registry, permission model, and account\/plan context. Granular registries can support independent visibility settings. Repository-scoped packages follow their repository.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Can I pull a public package without authentication?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">For public GHCR container images, yes. Most other GitHub Packages registries still require authentication even when the package is public.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Can I use a fine-grained PAT to authenticate npm\/Docker\/Maven package clients?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub&#8217;s package-client documentation continues to specify personal access token&nbsp;<strong>classic<\/strong>&nbsp;for GitHub Packages authentication. Some REST endpoints separately support GitHub App or fine-grained token types. Treat those as different authentication surfaces.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Should I use&nbsp;<code>GITHUB_TOKEN<\/code>&nbsp;or a PAT in Actions?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Prefer&nbsp;<code>GITHUB_TOKEN<\/code>&nbsp;when the package\/repository access model supports the operation. It is automatically generated, short lived, and can be permission-scoped in the workflow.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Can one organization package be consumed by many repositories?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Yes for granular registries. Grant the required repositories read access through package settings\/Actions access.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Are Maven and Gradle packages organization-scoped with independent ACLs?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">No. Their GitHub Packages permission model is repository scoped.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Can I delete any public package?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Not always. GitHub documents a 5,000-download restriction for public package\/version deletion; higher-download cases require GitHub Support.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How long can deleted packages be restored?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub documents a 30-day restoration window, provided the required namespace\/version conditions remain satisfied.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Is GHCR storage permanently free?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Do not assume that. As of September 2026, GitHub documents Container registry storage and bandwidth as currently free and says notice will be given before a policy change.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">What is the safest container deployment reference?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">An immutable digest, ideally connected to provenance and release metadata.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Do linked artifacts store my packages?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">No. Linked artifacts store\/aggregate metadata about artifacts, storage, provenance, and deployments. The artifact itself can live in GitHub Packages or an external registry.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">What is the difference between an SBOM and an attestation?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">An SBOM inventories components. An attestation is a signed statement about an artifact, such as its build provenance or an SBOM statement.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Should production packages be published from developer laptops?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Prefer controlled CI for normal production publishing because it improves reproducibility, auditing, permissions, and provenance.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">65. Topic Coverage Map<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The source curriculum contained 123 topic groups. This guide consolidates related topics into teachable chapters rather than repeating the same concepts under many headings.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Source topic groups<\/th><th class=\"has-text-align-left\" data-align=\"left\">Covered primarily in<\/th><\/tr><\/thead><tbody><tr><td>1\u20134<\/td><td>Sections 1\u20134<\/td><\/tr><tr><td>5\u201310<\/td><td>Sections 2\u20135<\/td><\/tr><tr><td>11\u201314<\/td><td>Section 6<\/td><\/tr><tr><td>15\u201319<\/td><td>Sections 7\u20139, 19<\/td><\/tr><tr><td>20\u201325<\/td><td>Sections 15, 18<\/td><\/tr><tr><td>26\u201332<\/td><td>Sections 8\u20139, 17, 44, 54<\/td><\/tr><tr><td>33<\/td><td>Section 10<\/td><\/tr><tr><td>34<\/td><td>Section 11<\/td><\/tr><tr><td>35<\/td><td>Section 12<\/td><\/tr><tr><td>36<\/td><td>Section 13<\/td><\/tr><tr><td>37<\/td><td>Section 14<\/td><\/tr><tr><td>38\u201345<\/td><td>Sections 16\u201317, 42\u201344<\/td><\/tr><tr><td>46\u201348<\/td><td>Section 19, 46<\/td><\/tr><tr><td>49\u201351<\/td><td>Sections 20\u201322<\/td><\/tr><tr><td>52\u201353<\/td><td>Sections 23\u201324<\/td><\/tr><tr><td>54\u201356<\/td><td>Sections 5, 18, 26\u201327<\/td><\/tr><tr><td>57\u201361<\/td><td>Section 25<\/td><\/tr><tr><td>62\u201365<\/td><td>Sections 28\u201329<\/td><\/tr><tr><td>66\u201368<\/td><td>Section 30<\/td><\/tr><tr><td>69\u201374<\/td><td>Sections 31\u201335<\/td><\/tr><tr><td>75\u201379<\/td><td>Section 36<\/td><\/tr><tr><td>80\u201384<\/td><td>Sections 37\u201341<\/td><\/tr><tr><td>85\u201389<\/td><td>Sections 42\u201343<\/td><\/tr><tr><td>90\u201393<\/td><td>Sections 44\u201345<\/td><\/tr><tr><td>94\u201397<\/td><td>Section 46<\/td><\/tr><tr><td>98\u2013103<\/td><td>Sections 47\u201352<\/td><\/tr><tr><td>104\u2013106<\/td><td>Sections 53\u201354<\/td><\/tr><tr><td>107\u2013109<\/td><td>Sections 55\u201356<\/td><\/tr><tr><td>110\u2013114<\/td><td>Section 57<\/td><\/tr><tr><td>115<\/td><td>Section 58<\/td><\/tr><tr><td>116\u2013123<\/td><td>Sections 16, 23\u201324, 31\u201336, 59, and references<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">66. Summary<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">You should now be able to:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Explain GitHub Packages and its registry architecture.<\/li>\n\n\n\n<li>Distinguish granular from repository-scoped package permissions.<\/li>\n\n\n\n<li>Publish and consume containers, npm, Maven, Gradle, NuGet, and RubyGems packages.<\/li>\n\n\n\n<li>Use PAT classic safely for package clients.<\/li>\n\n\n\n<li>Use\u00a0<code>GITHUB_TOKEN<\/code>\u00a0for package operations in GitHub Actions.<\/li>\n\n\n\n<li>Design cross-repository package access.<\/li>\n\n\n\n<li>Connect granular packages to source repositories.<\/li>\n\n\n\n<li>Version and promote immutable artifacts.<\/li>\n\n\n\n<li>Automate publication, cleanup, and reporting with Actions and the REST API.<\/li>\n\n\n\n<li>Understand deletion\/restoration rules.<\/li>\n\n\n\n<li>Manage billing\/storage considerations.<\/li>\n\n\n\n<li>Generate provenance attestations and SBOMs.<\/li>\n\n\n\n<li>Use dependency graph and Dependabot as part of supply-chain security.<\/li>\n\n\n\n<li>Understand linked artifacts and production context.<\/li>\n\n\n\n<li>Troubleshoot authentication, authorization, publishing, installation, container, and CI failures.<\/li>\n\n\n\n<li>Plan registry and legacy Docker migrations.<\/li>\n\n\n\n<li>Establish organization-level package governance.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Recommended next steps<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Build the complete practical project in a sandbox organization.<\/li>\n\n\n\n<li>Add a second consumer repository and test least-privilege access.<\/li>\n\n\n\n<li>Replace mutable deployment tags with digests.<\/li>\n\n\n\n<li>Add provenance and an SBOM.<\/li>\n\n\n\n<li>Create an inventory\/cleanup workflow using the REST API.<\/li>\n\n\n\n<li>Document your organization&#8217;s naming, publication, retention, and visibility policy.<\/li>\n\n\n\n<li>Review your actual GitHub plan and enterprise settings before turning the examples into production standards.<\/li>\n<\/ol>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">67. Authoritative References<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The guide was verified primarily against current GitHub documentation in September 2026.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">GitHub Packages fundamentals<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/docs.github.com\/en\/packages\">GitHub Packages documentation<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.github.com\/en\/packages\/learn-github-packages\/introduction-to-github-packages\">Introduction to GitHub Packages<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.github.com\/en\/packages\/learn-github-packages\/about-permissions-for-github-packages\">About permissions for GitHub Packages<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.github.com\/en\/packages\/learn-github-packages\/configuring-a-packages-access-control-and-visibility\">Configuring a package&#8217;s access control and visibility<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.github.com\/en\/packages\/learn-github-packages\/connecting-a-repository-to-a-package\">Connecting a repository to a package<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.github.com\/en\/packages\/learn-github-packages\/viewing-packages\">Viewing packages<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.github.com\/en\/packages\/learn-github-packages\/deleting-and-restoring-a-package\">Deleting and restoring a package<\/a><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Registry documentation<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/docs.github.com\/en\/packages\/working-with-a-github-packages-registry\/working-with-the-container-registry\">Working with the Container registry<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.github.com\/en\/packages\/working-with-a-github-packages-registry\/working-with-the-npm-registry\">Working with the npm registry<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.github.com\/en\/packages\/working-with-a-github-packages-registry\/working-with-the-apache-maven-registry\">Working with the Apache Maven registry<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.github.com\/en\/packages\/working-with-a-github-packages-registry\/working-with-the-gradle-registry\">Working with the Gradle registry<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.github.com\/en\/packages\/working-with-a-github-packages-registry\/working-with-the-nuget-registry\">Working with the NuGet registry<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.github.com\/en\/packages\/working-with-a-github-packages-registry\/working-with-the-rubygems-registry\">Working with the RubyGems registry<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.github.com\/en\/packages\/working-with-a-github-packages-registry\/working-with-the-docker-registry\">Working with the legacy Docker registry<\/a><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">GitHub Actions and automation<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/docs.github.com\/en\/actions\/tutorials\/publish-packages\">Publishing packages with GitHub Actions<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.github.com\/en\/actions\/tutorials\/publish-packages\/publish-docker-images\">Publishing Docker images<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.github.com\/en\/actions\/tutorials\/publish-packages\/publish-nodejs-packages\">Publishing Node.js packages<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.github.com\/en\/actions\/tutorials\/publish-packages\/publish-java-packages-with-maven\">Publishing Java packages with Maven<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.github.com\/en\/actions\/tutorials\/publish-packages\/publish-java-packages-with-gradle\">Publishing Java packages with Gradle<\/a><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">API, webhooks, and billing<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/docs.github.com\/en\/rest\/packages\/packages\">REST API endpoints for packages<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.github.com\/en\/webhooks\/webhook-events-and-payloads\">Webhook events and payloads<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.github.com\/en\/billing\/concepts\/product-billing\/github-packages\">GitHub Packages billing<\/a><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Supply-chain security<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/docs.github.com\/en\/code-security\/concepts\/supply-chain-security\/dependency-graph\">Dependency graph<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.github.com\/en\/code-security\/concepts\/supply-chain-security\/dependency-graph-data\">How the dependency graph recognizes dependencies<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.github.com\/en\/actions\/how-tos\/secure-your-work\/use-artifact-attestations\/use-artifact-attestations\">Using artifact attestations<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.github.com\/en\/code-security\/concepts\/supply-chain-security\/linked-artifacts\">About linked artifacts<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.github.com\/en\/code-security\/how-tos\/secure-your-supply-chain\/establish-provenance-and-integrity\/upload-linked-artifacts\">Uploading linked artifact storage and deployment data<\/a><\/li>\n<\/ul>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">GitHub features, limits, plan entitlements, action versions, and preview status can change. Re-check the authoritative documentation before production rollout or compliance sign-off.<\/p>\n<\/blockquote>\n","protected":false},"excerpt":{"rendered":"<p>Last Verified:&nbsp;September 2026Scope:&nbsp;GitHub.com \/ GitHub Enterprise Cloud unless explicitly stated otherwiseAudience:&nbsp;Developers, DevOps engineers, platform engineers, administrators, architects, trainers, and engineering teams GitHub Packages is GitHub&#8217;s package-hosting platform&#8230;. <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-1191","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/posts\/1191","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/comments?post=1191"}],"version-history":[{"count":1,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/posts\/1191\/revisions"}],"predecessor-version":[{"id":1192,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/posts\/1191\/revisions\/1192"}],"wp:attachment":[{"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/media?parent=1191"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/categories?post=1191"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/tags?post=1191"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}