# Plugins

Declare the native plugins a project uses, where to discover them, and which releases Atmos may install.
Declarations and the release lock are committed with the project, so every machine and CI run restores the
same reviewed release.

> ⚠️ Experimental

See the [Plugins overview](/plugins) for a quick start and links to official packages, commands,
development, and policy guides.

## Usage

**File:** `atmos.yaml`

```yaml
plugins:
  max_concurrency: 4
  packages:
    corporate:
      source: oci://registry.example.com/team/oidc
      version: "^0.1.0"
      timeout: 5m
  registries:
    team:
      host: registry.example.com
      identity: registry-reader
      repositories:
        - oci://registry.example.com/team/oidc
  discovery:
    cache_ttl: 15m
  policy:
    require_signature: true
    trusted_publishers:
      - publisher: acme
        identity: "https://github.com/acme/atmos-oidc/.github/workflows/release.yaml@refs/tags/*"
        issuer: "https://token.actions.githubusercontent.com"
```

Manage the declarations with [`atmos plugin install`](/cli/commands/plugin/install),
[`update`](/cli/commands/plugin/update), [`remove`](/cli/commands/plugin/remove), and
[`registry add`](/cli/commands/plugin/registry-add). These commands edit only the keys they own and keep
your comments and formatting. The selected release is recorded in `.atmos.plugins.lock.yaml`, next to
`atmos.yaml`.

## plugins

- **max\_concurrency** — Maximum parallel downloads and discovery requests. Defaults to 4; 1 installs sequentially. Negative values are invalid.
- **cache\_dir** — Storage root for downloaded releases and discovery metadata. Defaults to the Atmos XDG data and cache directories.

## plugins.packages.\<alias>

- **source** — OCI repository of the release, such as oci://registry.example.com/team/oidc. Required while the package is enabled, unless path is set.
- **version** — Semantic version constraint. Required while the package is enabled, unless path is set. plugin install records ^1.4.2 (or ^0.1.0 for a 0.x release, and an exact version for a pre-release) when you install a new package without --version.
- **path** — Directory of a locally built plugin: the folder that holds plugin.yaml and the executable it names. A relative path is relative to the project directory (the directory of the root atmos.yaml); an absolute path is used as is. When set, it wins over source and version, which stay valid and apply again when path is not set. Atmos reads the manifest and the executable from the directory on every activation, so rebuilding is enough; the package has no lock entry, is not cached, and is not checked for a signature or version. Set it in a profile of your own so the local build applies only while that profile is active. plugin install skips the package, install --frozen refuses while it is set, and plugin list reports state path. Atmos prints one warning per alias when it starts the plugin.
- **enabled** — false keeps the declaration but skips installation and activation, including a configured path. Source and version are not required while disabled.
- **timeout** — Positive Go duration, such as 30s or 5m, that bounds every call into the plugin. Omit for no deadline.
- **command** — Command export that plugin run starts when the package exports several.
- **config** — Settings passed to the plugin command, validated against the command's schema when it declares one.
- **scope** — Only project is supported.

Packages install automatically only when a command needs one of their exports, and CI refuses to create a
lock entry that is not already committed. See [automatic installation](/cli/commands/plugin/usage#automatic-installation).

Atmos includes the `cloudposse` registry with catalog `oci://ghcr.io/cloudposse/atmos/plugins/catalog:stable`. An explicit `registries.cloudposse` replaces that entire entry. Removing it with `atmos plugin registry remove cloudposse` disables the implicit default as well.

## plugins.registries.\<name>

- **host** — Registry authority used for authentication, independent of discovery. Inferred from catalog or repositories when omitted. All sources must share one host.
- **identity** — Name of an auth.identities entry that authenticates to the host. An identity binding is exclusive: a failure does not fall back to Docker or ambient credentials.
- **auth** — Only docker (the shared Docker and stored-credential chain) is supported. registry add writes it when no identity is set.
- **catalog** — Optional OCI reference to a curated discovery catalog.
- **repositories** — OCI repositories that Atmos indexes directly, without a catalog.

The order Atmos uses to pick credentials is in [credential precedence](/cli/commands/plugin/registry-login#credential-precedence).

## plugins.discovery

- **default\_registry** — Include the built-in Cloud Posse catalog. Defaults to true. Set false for private-only discovery. Explicit registry entries remain active, including an explicit cloudposse override. Direct OCI installation does not need discovery.
- **cache\_ttl** — How long discovery metadata stays fresh, as a nonnegative Go duration. Defaults to 15m; 0 revalidates on every online search.

## plugins.policy

- **require\_signature** — Admit only releases signed by a trusted publisher. It governs released packages; a package that sets path has no signature and is governed by plugin policy rules over origin: development and input.path. Needs at least one entry in trusted\_publishers in some loaded configuration file. Later files cannot turn it off.
- **trusted\_publishers** — List of publisher (the namespace before / in the package name), identity (the signer's certificate subject; \* is the only wildcard), and issuer (exact). A non-empty list enables enforcement.
- **offline** — Forbid registry access. Installation, restoration, and corrupt-cache repair then need an explicit --from-layout, and discovery serves the cache even when stale.

For signature setup and offline verification, see [trusted publishers](/cli/commands/plugin/usage#require-trusted-publishers).
To restrict registries, package names, and local builds for the whole organization, use [plugin policies](/policies/plugins).
For the development loop that `path` enables, see the [plugin development tutorial](/tutorials/plugin-development).

## Official secret-store authentication

The `cloudposse/auth-secrets` plugin exports `secrets/store`, a provider that reads declared Atmos secret references, and `secrets/env`, an identity that maps selected values into a command's environment.

**File:** `atmos.yaml`

```yaml
plugins:
  packages:
    secrets:
      source: oci://ghcr.io/cloudposse/atmos/plugins/auth-secrets
      version: 1.1.0

stores:
  team:
    type: aws-ssm-parameter-store
    secret: true
    options:
      region: us-east-1

auth:
  providers:
    service-secrets:
      kind: secrets/store
      spec:
        credentials:
          token:
            store: team
            path: /services/example/token
  identities:
    service:
      kind: secrets/env
      via:
        provider: service-secrets
      spec:
        env:
          SERVICE_TOKEN: '{{ .credentials.token }}'
```

Install with `atmos plugin install`, commit the plugin lockfile, and select the `service` identity when running a command. Only the mapped fields enter the command environment. Values are masked, bounded to 1 MiB, and must be text without NUL bytes. Environment names must use letters, digits, and underscores, start with a letter or underscore, and be unique ignoring case.

Each reference requires `store` and `path`; optionally supply both `stack` and `component` for a scoped lookup. For a structured backend such as Vault KV v2, add `field` to select one exact top-level document field. The `path` is relative to the configured KV mount, without the KV v2 `/data/` API segment. Field selection does not use the backend's default scalar convention, so a document may contain both `value` and the explicitly selected field. The store must be secret-enabled and support field selection when `field` is present. Its explicit store identity is respected; otherwise the backend uses ambient credentials. Circular dependencies between the provider and a store's identity fail before reading secrets.

For example, two credential names can select different fields of the same document:

```yaml
auth:
  providers:
    service-credentials:
      kind: secrets/store
      spec:
        credentials:
          api-access-key:
            store: bootstrap
            path: service/credentials
            field: access_key
          api-secret-key:
            store: bootstrap
            path: service/credentials
            field: secret_key
  identities:
    service-session:
      kind: secrets/env
      via:
        provider: service-credentials
      spec:
        env:
          API_ACCESS_KEY: '{{ index .credentials "api-access-key" }}'
          API_SECRET_KEY: '{{ index .credentials "api-secret-key" }}'
```

The `bootstrap` store must already be configured and secret-enabled. The identity's `spec.env` is a nonempty map of environment names to Go text templates, evaluated after upstream authentication. Only the upstream credential envelope's data is available as `.credentials`. Dot references and `index` are supported; plain strings remain literal, and template composition is allowed. Missing keys through either lookup form and invalid templates stop authentication without including values in errors. Templates have no filesystem, process-environment, shell, or secret-store helpers. Rendered values are bounded to 1 MiB and cannot contain NUL bytes; at most 256 environment variables may be defined.

Keep the structural YAML keys `auth`, `identities`, `spec`, and `env` literal. Component and identity names may use templates. Write credential templates as quoted strings or block scalars, and put template control actions around auth mappings on standalone lines. Ambiguous structural keys and malformed credential declarations are rejected before general template helpers run.

Malformed template YAML is rejected before general helpers run when Atmos cannot prove auth credential ownership. The error explains: `Auth credential ownership cannot be proven from malformed YAML.` This conservative policy can reject dynamic keys or auth-shaped application data under `vars` or `settings`, even when rendering would produce valid YAML. Keep raw YAML valid, balance template controls, and use safely quoted values or block scalars.

The unreleased `principal.environment` syntax has been replaced by `spec.env`. Move those mappings to `spec.env` and use explicit credential templates. Credential references use `path` in place of the former `key` name.

Authentication rereads the stores. Five-minute credentials allow reuse within the current process; a fresh process reads them again. Logout clears the identity's environment. Atmos does not change its own process environment or write cloud credential files for this identity. The native plugin runs as your operating-system user and must still be trusted.

The package version is independent of the Atmos version. Releases contain all six supported platforms in an OCI index; installation downloads only your platform's executable payload. See the [official package release guide](https://github.com/cloudposse/atmos/tree/main/plugins) for publication and public GHCR setup.

### Native identity schema selection

Native identity exports validate `principal` by default. Plugin authors whose configuration belongs in `spec` can select that target in their `plugin.yaml` capability metadata:

```yaml
spec:
  capabilities:
    auth:
      identities:
        secrets/env:
          schema: schemas/identity.json
          schema_target: spec
          accepts: [secret-values/v1]
          credentials: [environment/v1]
```

The `schema_target` accepts `principal` or `spec`; omitting it preserves validation of `principal`. Both configuration objects are already part of the native identity protocol, so choosing the schema target does not change the wire format. The `secrets/env` identity selects `spec` for its `env` templates.
