Skip to content

Registry publishing setup

The release workflow keeps GHCR private under fydo-dev and publishes public mirrors under c0mpiler. It does not change the visibility of GitHub repositories or packages:

Destination Access Repository
GHCR Private ghcr.io/fydo-dev/claude-code-proxy
Docker Hub Public docker.io/c0mpiler/claude-code-proxy
Quay.io Public quay.io/c0mpiler/claude-code-proxy

Ordinary commits run CI. Publication happens on a new release tag or an explicit Publish container images run. Both architectures come from the release’s tested image bundles. The images include the application and setup UI; user credentials are created at runtime in a private volume.

You need administrator access to the GitHub.com repository, control of the publishing namespaces, and permission to create public repositories. Publishing uses GitHub-hosted Linux runners with Docker Buildx, QEMU, and the repository’s built-in Actions token. Docker Hub and Quay.io pulls need no registry credentials. Private GHCR pulls require an account with package access and a classic token with read:packages.

Docker Hub uses a personal access token with image read and write permissions. Quay uses the registry credential for the configured publishing identity, currently the personal c0mpiler account. Store these credentials directly in GitHub Actions secrets. Runtime provider logins and the proxy’s management key are unrelated to registry publishing.

Create c0mpiler/claude-code-proxy and select Public visibility. Under the publishing account’s personal access tokens, create a dedicated release token with read and write access. A read-only token can authenticate successfully but cannot publish.

This setup uses a personal account. Docker Team and Business organizations can use OIDC instead of a stored token, but that requires a separate organization connection and workflow login configuration. Docker token documentation and repository visibility.

Create c0mpiler/claude-code-proxy as a public container image repository. The current publisher signs in as c0mpiler, using that account’s registry credential in QUAY_TOKEN. The username must match the identity that issued the credential.

For a dedicated automation identity, create a robot account, grant it Write on this repository, and update both QUAY_USERNAME and QUAY_TOKEN. For example, robot github_actions signs in as c0mpiler+github_actions. The robot must exist before using that username.

Quay robot accounts and repository permissions.

Keep the existing GHCR package Private. The publisher rejects a public GHCR package, verifies authenticated pulls, and checks that anonymous access is denied. GHCR authentication for uploads uses the repository’s automatic GITHUB_TOKEN with packages: write; no personal GHCR token is stored as an Actions secret.

GitHub package visibility and workflow access.

In the GitHub.com repository, open Settings → Secrets and variables → Actions. Add repository variables on the Variables tab:

Variable Value
DOCKERHUB_IMAGE docker.io/c0mpiler/claude-code-proxy
DOCKERHUB_USERNAME c0mpiler
QUAY_IMAGE quay.io/c0mpiler/claude-code-proxy
QUAY_USERNAME c0mpiler

Add repository secrets on the Secrets tab:

Secret Value
DOCKERHUB_TOKEN The Docker Hub publishing token
QUAY_TOKEN Registry credential belonging to QUAY_USERNAME

The destination namespace and login identity are separate settings. A Quay robot login includes +; the image repository path does not.

Tokens belong on the Secrets tab. Actions variables are plaintext settings and are not available through the workflow’s secrets context. A local docker login does not configure GitHub Actions credentials.

These commands show configuration names without exposing secret values:

Terminal window
GH_HOST=github.com gh variable list --repo fydo-dev/claude-code-proxy
GH_HOST=github.com gh secret list --repo fydo-dev/claude-code-proxy

Update the release version and matching package metadata, update the changelog and versioned documentation examples, and run the project checks. Push the release commit to ninja, then push its matching vVERSION tag to the GitHub.com repository.

The tag must match Cargo.toml. The release workflow rejects an existing tag pointing at a different commit. Native binaries and checksummed container bundles are built and tested before publishing. Registry jobs then upload the same bundles, verify the expected access policy and both architectures, and promote each registry’s latest after its checks pass.

GitHub Actions registry credentials are used only by the publication workflow. Ordinary pushes and pull requests do not publish images.

Use this path to add an existing release to another configured registry or retry an interrupted publication. It downloads the existing checksummed release bundles and preserves native release archives:

Terminal window
GH_HOST=github.com gh workflow run publish-images.yml \
--repo fydo-dev/claude-code-proxy --ref ninja \
-f release_tag=v0.2.0 -F promote_latest=true

The workflow reuses an existing versioned image only when its image configuration matches the release bundle. A conflicting image fails instead of being replaced. Existing version manifests remain unchanged. Architecture-specific staging tags may exist after an interrupted upload; rerunning resumes publication.

Leave promote_latest disabled when checking or republishing a historical release that should not become current.

To verify all existing registry images without uploading bundles or changing latest:

Terminal window
GH_HOST=github.com gh workflow run verify-image.yml \
--repo fydo-dev/claude-code-proxy --ref ninja \
-f release_tag=v0.2.0

For each registry, the publisher checks:

  • An index containing exactly Linux ARM64 and AMD64.
  • Anonymous pulls of both architecture manifests and the version tag on Docker Hub and Quay.io, using an empty Docker credential configuration.
  • Authenticated GHCR pulls, private package visibility, and an anonymous pull that must be denied.
  • Image source revision, source repository, architecture, and executable version.
  • Guided provider login, refresh, cancellation, streaming, four model roles, shell and JSON command exports, and persistence across recreation and image replacement.
  • The promoted latest digest matching the verified version, followed by a pull using the registry’s required access policy.

ARM64 execution on the Linux publishing runner uses QEMU. Lifecycle tests use synthetic provider fixtures, rather than personal accounts. Replacement checks use a distinct image configuration of the same application version; they do not promise compatibility with a future schema migration.

Each registry runs independently. If one fails, the others can finish, and native release downloads remain available. The combined release notes and digest records update only after all three pass. Retry a partial publication with the existing-release workflow.

The release attaches container-image.txt and container-manifest.json for GHCR, plus files suffixed -dockerhub and -quay for the mirrors. Use the digest for the registry from which you deploy; registry manifest digests can differ even when the underlying application image configuration is identical.

The public registry copy is maintained in registry/description.txt and registry/README.template.md. The template contains only public setup instructions and links; it does not copy the private source README. The renderer fills the published version, image names, documentation URL, and license from configuration and release metadata.

Both registry descriptions are updated through signed-in browsers for this setup. Render the public copy from the package metadata of the published release, then paste README.md into each registry’s description editor. Docker Hub also uses description.txt for its short description:

Terminal window
DOCKERHUB_IMAGE=docker.io/c0mpiler/claude-code-proxy \
QUAY_IMAGE=quay.io/c0mpiler/claude-code-proxy \
python3 scripts/registry_metadata.py \
--manifest /path/to/published-release/Cargo.toml \
--output /path/to/rendered-registry-copy

Use Python 3.11 or newer. Select the released source checkout for --manifest, rather than an unreleased branch version. Compare the saved text through the public registry pages after saving. Updating descriptions does not rebuild, retag, or replace released images.

Image publication remains automatic on release. Description synchronization is manual: the existing Docker Hub publishing token cannot edit repository metadata, and Quay’s description API requires an OAuth token distinct from its Docker login credential. No broader credentials are required for this browser-based process.

Future image builds include the project title, homepage, and container guide as OCI labels. The existing versioned images keep their original immutable manifests and labels.