OpenClaw EnterpriseDOCSGitHub

Enterprise container publication

The OCC CLI release publishes matching versioned command line binaries through a separate protected workflow.

container-publish.yml prepares the existing controller (Dockerfile, target runtime) and combined gateway/Agent runtime (deploy/runtime/Dockerfile) as OCI archives containing both linux/amd64 and linux/arm64. Each image has one multi-platform index digest; Docker selects the matching architecture when pulling it. Chart publication is opt-in through publish_chart: true; it packages the OCC Helm chart under the same OCE release version. It does not deploy Kubernetes resources or change the existing CI test matrix.

Source visibility

No-push preparation supports private or public source in openclaw/openclaw-enterprise only when this workflow receives publish: false (PUBLISH is the exact string "false"). The trusted main workflow, immutable source SHA, successful exact-source CI, and approved base-image checks still apply. Actual GHCR publication and recovery require the public openclaw/openclaw-enterprise repository and public GHCR image packages. Optional chart publication requires the public chart package too. Docker Hub promotion copies from public GHCR and keeps the separately authorized Docker Hub destinations private.

GitHub warns that granting a public repository Actions access to private packages can expose those packages to forks. Do not use private GHCR packages for real publication from this public repository. Bootstrap can create or verify private marker packages only so an operator can finish package setup; make the packages public before source images or the chart publish.

Operator setup

Configure publication settings and native runners before dispatching. That page owns environment policies, variables, secrets, and package requirements. Then confirm package linkage and use the marker bootstrap for missing packages.

No-push preparation validates the approved base image and trusted source CI. Publication also checks package access, identity, and public visibility. Missing settings, inaccessible metadata, or conflicting linkage stop publication. GitHub may omit repository metadata; follow the linkage checks below.

Confirm package linkage

GitHub's package response may omit repository or return null even for a connected GHCR package. The workflow always verifies package identity and public visibility. An explicit repository must match the public Enterprise repository; conflicting metadata stops publication.

During package setup, open Package settings for each destination and confirm its connected repository is openclaw/openclaw-enterprise, its visibility is Public, and that repository has the required Manage Actions access grant. Source labels alone do not prove linkage. When GitHub omits repository metadata, the publisher relies on this configured package access and still checks package identity, public visibility, and immutable tag contents. It does not claim to verify omitted linkage through the API.

To migrate an existing environment, first merge this manual-publication change and wait for its main-push CI. Then apply the current publication configuration. Old runs execute their original workflow code; use a new recovery dispatch to publish retained artifacts under the updated policy.

Bootstrap GHCR packages

After configuring the environment and both destination variables, merge the reviewed bootstrap workflow and wait for its exact main-push CI run to succeed. An operator must first confirm that the organization permits creation of container packages under those names. Dispatch Bootstrap Enterprise Container Packages on main with that ci_run_id. The manual dispatch authorizes the run.

The workflow uses its short-lived GITHUB_TOKEN to push scratch image markers and a nondeployable Helm chart marker. Its temporary image contexts and chart contain no checkout files or credentials. Existing public or private packages remain unchanged when they match the expected package and repository metadata; conflicting repository metadata fails. An authenticated metadata 404 permits only this harmless push; it does not prove the package is absent. Other metadata errors stop the run. After each push, the metadata lookup retries only 404 responses up to five times at two-second intervals for registry propagation; persistent 404 responses fail. The remote marker digest must verify before bootstrap succeeds. Confirm package linkage during setup before real-image publication.

The job summary records package coordinates and marker digests. The unique bootstrap-<run-id>-<attempt> image tags and chart version are not deployable. Bootstrap does not change visibility or access grants; if verification fails, inspect the package settings and fix the reported cause before retrying. A partial result is retained, and a subsequent dispatch leaves valid existing packages untouched. The ordinary publisher still requires verified public packages before copying any source. See the bootstrap execution flow.

Public GHCR image pulls do not require a workstation login. Authenticated pulls are still required for private marker packages before they are made public and for separately authorized private mirrors. Workflow package permissions do not grant a workstation credential additional scopes.

Check native images without publication

Use Check Native Container Images to build and smoke-test controller and runtime images on native AMD64 and ARM64 runners. Configure the native runners and base image. Select a ref containing this workflow version and its manual source_sha input.

Replace both placeholders. Set expected_sha to the full 40-character commit SHA recorded independently during source review:

sh
selected_ref='REPLACE_WITH_BRANCH_OR_TAG'expected_sha='REPLACE_WITH_REVIEWED_FULL_COMMIT_SHA'gh workflow run container-check.yml --repo openclaw/openclaw-enterprise \  --ref "$selected_ref" -f source_sha="$expected_sha"

The workflow checks out github.sha and compares it with source_sha. A mismatch, including a branch moved between review and dispatch, fails before image builds. An omitted or empty source_sha defaults to github.sha, with no independent check. Reusable callers must supply it.

The workflow does not push images to a registry or install a release. Repository readers can download its artifacts; review the image contents and audience, especially for public source.

Retain native export evidence

Before using exports, require a successful terminal workflow with all four Prepare <controller|runtime> (<amd64|arm64>) jobs and both Smoke runtime (<amd64|arm64>) jobs successful. Controller preparation includes smoke and qualified upload. Runtime preparation supplies build digests; its separate smoke jobs verify the handoff, run startup tests, clean up and upload qualified artifacts. All four image/architecture outcomes are required.

After smoke failure, rerun the whole native workflow. A smoke-only retry cannot consume the previous attempt's build artifact.

Download the selected artifacts before their one-day retention expires. Their names are platform-<controller|runtime>-<amd64|arm64>-<run-id>-<attempt>. The intermediate built-runtime-* artifacts are unqualified. Keep the original download and artifact metadata, including its ID and SHA-256 digest, together with the run, attempt, source SHA and successful job records. Compare the downloaded ZIP's checksum with the artifact metadata; this checksum identifies the ZIP bytes, not an OCI image digest.

Extract the verified ZIP into a private directory and leave it unchanged during verification. Select the expected values from the reviewed source and producer job records. Use the Digest output from Build native OCI image without publication (steps.build.outputs.digest) and the pinned Node base used by that job. For a direct Check Native Container Images dispatch, leave both CI fields empty. For example, check its runtime export from the repository root:

sh
SOURCE_SHA="$expected_sha" GITHUB_WORKFLOW_SHA="$workflow_sha" \GITHUB_RUN_ID="$run_id" GITHUB_RUN_ATTEMPT="$attempt" \CI_RUN_ID='' CI_ATTEMPT='' NODE_BASE_IMAGE="$pinned_node_base" \IMAGE=runtime PLATFORM=linux/arm64 IMAGE_DIGEST="$build_digest" \node scripts/ci/container-release.mjs verify-platform "$extracted_layout"

For a manual CI run that requests native images, use that CI run’s own ID and attempt. For Enterprise Containers, use the validated source CI run ID and attempt. The command checks platform.json and the reachable OCI bytes offline and prints the platform, build root, manifest and config digests. Preserve these identities through transport. ZIP verification and the six-job success check above are separate requirements. Registry and deployment checks use their own evidence. For local use, follow the populated controller return rehearsal.

Prepare and publish

  1. Merge the reviewed workflow and source changes to main. Wait for that exact main push run of .github/workflows/ci.yml to finish successfully, including its CI Required job. A PR merge SHA or a matching check name is insufficient.
  2. Manually dispatch Enterprise Containers, selecting branch main, its full current source_sha, and that ci_run_id. Leave publish false for no-push preparation. If main moved, select the new SHA and its own completed CI run.
  3. To publish, dispatch with publish true after reviewing the SHA, CI run, and package setup. Leave image_tag blank to update latest for both images, or enter a custom tag to update that tag instead. The tag can contain up to 128 ASCII letters, digits, underscores, periods, and hyphens; it must start with a letter, digit, or underscore. Tags beginning with sha- or bootstrap- are reserved, case-insensitively. The selected alias is mutable and an existing tag can be replaced. Each platform builds natively; runtime smoke loads that export on a fresh native runner, while controller smoke remains with its build. Preparation verifies the loaded config against the export using Docker's image ID on the legacy store or the verified config digest on the containerd store, where the image ID identifies a manifest or index. After both platforms pass startup smoke, the workflow assembles and seals those exports from the same run and attempt into one archive per image. The publisher rechecks the source, CI attempt, environment branch policy, and package visibility, then copies each sealed archive with all child manifests using Skopeo and verifies the remote index digest.
  4. Use the image@sha256:... references in the image job summary and container-publication-<run-id>-<attempt> receipt for deployment. Each image receives an immutable sha-<source-sha> tag, the selected mutable alias, and, only with publish_chart: true, an OCE version tag matching the chart. The image-only container-publication receipt records the source tags and alias for promotion and recovery. Existing source and version tags cannot be replaced by different bytes. Publication does not create a Git tag, GitHub release, or deployment.

Image publication defaults to publish_chart: false and needs no chart package. For a versioned chart release, also select publish_chart: true and follow chart publication and installation. With publish: false, preparation never publishes images or a chart regardless of publish_chart.

Images and the optional chart have separate jobs. A chart failure leaves the verified images and their successful job intact, but fails the combined run. The workflow holds the shared publication lock from preparation through both publication jobs; preparation-only runs use independent groups. A queued run can be superseded before it starts, but an active release retains the lock. The final summary reports both outcomes. Chart publication produces its own chart-publication-<run-id>-<attempt> receipt.

The multi-platform publisher requires both architectures in every seal. Earlier amd64-only tags retain their original bytes and digests; building this workflow requires a new source SHA. Old single-platform archives are not accepted by the current recovery or promotion validator. See the publication execution flow.

OCI archives are retained for seven days and publication receipts for 30 days. Uploaded archives follow the repository's workflow-artifact access: repository readers can download them. Public-source preparation can therefore expose the built images to public repository readers even with publish: false. No registry push does not mean artifact confidentiality; review the image contents and artifact audience before dispatch.

Preparation pins image timestamps to the source commit and removes exporter-only annotations from the assembled multi-platform index. Repeating a build with the same resolved inputs therefore produces the same registry image identity. A rerun can still be rejected if mutable external package inputs resolve to different bytes. Do not delete or overwrite existing tags to evade that rejection. Publication of the two images is not transactional; on a partial failure inspect each recorded registry digest before deciding on recovery. The publisher's concurrency lock serializes these workflow writes, not external registry administrators. Both source tags are verified before either alias moves, but the two alias updates are not atomic. A failure may leave them pointing to different publications; inspect both packages and use digest references until a later successful publication updates the selected alias for both images. Recovery restores source tags only and does not move aliases.

Recover a partial publication

Preserve the original seven-day OCI artifacts. Do not rerun their producer: its attempt identity must remain unchanged. If those archives have expired, this recovery path cannot reconstruct their exact bytes.

After merging reviewed recovery code and waiting for its successful main-push CI, dispatch Resume Enterprise Container Publication on main with:

Native preparations that ran smoke inside Prepare jobs are unsupported by the current recovery gate. They require fresh full qualification, which does not reconstruct their original bytes.

Review the original digests and current public package settings before dispatch. Recovery validates artifact IDs and downloads the exact artifact IDs from the original run, verifies seals and archive hashes, and rechecks original source CI as well as recovery workflow CI. The original source commit must remain in main history. It must match the recovery workflow commit or be one of its ancestors. The approved Node base must still match the original seals.

An existing source tag must match both package metadata and its remote manifest digest; recovery leaves it untouched even when package metadata lags. Only an authenticated manifest-unknown response permits copying an unlisted tag; other inspection failures stop the run. Missing tags receive the original OCI bytes, with digest preservation and remote verification. Conflicting tags, expired or changed producer evidence, or failed checks stop the run. Neither recovery nor ordinary publication automatically rolls back a copy. GitHub metadata GET transport failures are retried up to a set limit (normally three attempts) and report the failing endpoint; authorization failures and other HTTP errors still fail immediately, except the bootstrap's explicit 404 retry.

Success produces container-recovery-publication-<run-id>-<attempt> with a publication.json receipt containing original image seals, preparation artifact IDs/digests, and separate recovery publication run/attempt/workflow/CI identity. Both remote digests must match before the receipt is written. Its 30-day retention matches ordinary receipts. This recovery receipt is not accepted by the existing Docker Hub promotion workflow, which requires an ordinary successful producer. No build, visibility change, tag replacement, or deployment occurs during recovery.

For a separately authorized copy of ordinary publication digests to private Docker Hub repositories, use Docker Hub promotion.

Proof boundaries

Four existing CI lanes gate the selected source: Images and Packaging, and the three Image Runtime Startup lanes. Preparation reuses their controller/runtime startup tests against the newly prepared bytes: source CI alone cannot prove a subsequent build with newly resolved npm transitives. Loading does not rebuild an image. The assembler checks each platform export against its smoke receipt before including it in the final archive. Skopeo preserves manifest digests during publication. These are not provider, cluster, or registry end-to-end tests.

Local gate and recovery tests: node --test tests/integration/container-{release,resume,promote}.test.mjs. Recovery tests use HTTP and transport fixtures; they prove gate ordering, original identity, conflict rejection, unchanged existing images, and receipt behavior. The registry integration uses real Skopeo and a disposable registry to check alias replacement and both platforms. Neither test proves a live GHCR transfer. Workflow syntax: actionlint .github/workflows/*.yml. Actual no-push builds and the first public GHCR transfer still require their respective authorized hosted runs; configuration and unit tests alone do not prove them.

Search documentation