# atmos plugin publish

Publish a verified release layout to an OCI registry.

> ⚠️ Experimental

## Usage

```shell
atmos plugin publish <layout> --to=<repository> [--sign] [--verify-public]
```

Before the first registry write, Atmos validates the destination and the signing flags locally. It
then re-inspects every platform payload in the layout. Layouts can come from other tools or older
versions, so `publish` never trusts how a layout was built. It applies the same rules as
[`plugin pack`](pack.mdx#what-goes-into-a-release): a payload may contain only `plugin.yaml`,
`schemas/**/*.json`, and the declared executable, and anything hidden, credential-like, state-like,
linked, or not a native binary for the platform fails the publish. Nothing is uploaded in that case.

The default output confirms publication and shows the package, version, platforms,
immutable digest, repository, and signing result, followed by the inventory of files published for
each platform with their sizes and modes. Request a structured format explicitly for automation.

The release tag always comes from `metadata.version` in `plugin.yaml`. Publishing the same bytes
again is a no-op, and a version tag that already points at different content is rejected, because
published releases are immutable.

## Arguments and flags

- **layout** — OCI layout produced by plugin pack.
- **--to** — Destination OCI repository, with or without the oci:// prefix, and without a tag or digest,
  for example oci://registry.example.com/team/oidc. A destination that includes a tag or digest is
  rejected, because the tag is derived from the manifest version. Existing version tags cannot be replaced
  with different content.
- **--sign** — Sign the published index digest with Sigstore. Uses the same signing flow as atmos plugin sign.
- **--verify-public** — After upload and any requested signing, anonymously download and verify the published digest and
  every platform payload. Disabled by default so private registries remain supported. This checks access;
  it does not change registry visibility. Verification failure returns an error and leaves the upload intact.
- **--cosign-version** — Exact managed Cosign 3 version used with --sign. Defaults to 3.1.3. Requires --sign;
  without it the command fails instead of ignoring the flag.
- **--yes, -y** — Accept Sigstore transparency log publication without prompting when signing. Requires --sign;
  without it the command fails instead of ignoring the flag.
- **--format** — Output format: table (default), json, yaml, csv, or tsv. Also configurable with ATMOS\_PLUGIN\_FORMAT.

## Examples

```shell
atmos plugin publish ./oidc-release --to=oci://registry.example.com/team/oidc
atmos plugin publish ./oidc-release --to=oci://registry.example.com/team/oidc --format=json
```

Publish and sign a release:

```shell
atmos plugin publish ./oidc-release --to=oci://ghcr.io/acme/oidc --sign --yes
```

Publishing and signing are separate registry operations. If signing fails, the release remains
published and the command returns an error with its immutable digest.
Retry the same command, or run `atmos plugin sign` against the reported digest. For repositories
requiring signatures, publish with `--sign` before enabling a release for consumers.

Publishing never modifies a discovery catalog. For small collections, configure the repository
under `plugins.registries.<name>.repositories`; search builds a cached discovery index.
For curated collections, a dedicated catalog maintainer can use
[`atmos plugin catalog publish`](/cli/commands/plugin/catalog-publish) to publish snapshots separately.
Serialize catalog writes through one owner or CI concurrency group: revision checks detect stale
updates but are not a distributed lock or an atomic compare-and-swap guarantee.

For source-to-release publication, use [plugin release](release.mdx). For automated batch catalog updates, use [plugin catalog sync](catalog-sync.mdx).

## Public packages on GHCR

Use `--verify-public` when consumers must be able to download without credentials:

```shell
atmos plugin publish ./oidc-release --to=oci://ghcr.io/acme/oidc --sign --yes --verify-public
```

GHCR creates new packages **private**, even when the source repository is public. Publishing through
GitHub Actions or adding `org.opencontainers.image.source` associates the package with a repository;
it does not inherit the repository's public visibility. GitHub currently provides no documented,
supported REST or GraphQL API to change package visibility.

The first-publication flow is:

1. Run the publishing command. The upload creates the package; anonymous verification fails while it is private.
2. Open the package in GitHub, then **Package settings → Danger Zone → Change visibility → Public**.
3. Rerun the same command with identical inputs. Atmos reuses the existing immutable release and verifies anonymous access again.

No separate bootstrap artifact is needed. The error identifies the uploaded reference and provides
recovery instructions. Later versions retain the package's visibility. A plugin and its discovery
catalog are separate GHCR packages, so configure each independently. An inventory release may stop
at the first private plugin; after fixing it and retrying, the catalog may require the same one-time step.

If access verification fails for an already-public package, inspect the underlying network, access,
or integrity error rather than changing visibility again. A failed check does not remove uploaded content.
Omit `--verify-public` for intentionally private distribution.

See GitHub's [package visibility instructions](https://docs.github.com/en/packages/learn-github-packages/configuring-a-packages-access-control-and-visibility#configuring-visibility-of-packages-for-an-organization)
and [Packages API reference](https://docs.github.com/en/rest/packages/packages).
