# atmos secret

The `atmos secret` command group provides a GitOps-friendly, multi-cloud workflow for managing secrets. Secrets are **declared** in your stack configuration (committed to git) and their **values** are stored in a cloud secret backend (AWS SSM, AWS Secrets Manager, HashiCorp Vault, Azure Key Vault, GCP Secret Manager) or a SOPS-encrypted file. Declared secrets are resolved at runtime with the [`!secret`](/functions/yaml/secret) YAML function and are automatically masked in all output.

## Usage

```shell
atmos secret <subcommand> [flags]
```

## Subcommands

- **[`init`](/cli/commands/secret/init)**
  Provision declared secrets, prompting for any that are missing.
- **[`set`](/cli/commands/secret/set) (alias `add`)**
  Set a declared secret's value (create or update).
- **[`get`](/cli/commands/secret/get)**
  Retrieve a declared secret's value.
- **[`delete`](/cli/commands/secret/delete) (alias `rm`)**
  Remove a declared secret's value from its backend.
- **[`list`](/cli/commands/secret/list)**
  List declared secrets and their initialization status.
- **[`pull`](/cli/commands/secret/pull)**
  Download declared secrets to a local file for development.
- **[`push`](/cli/commands/secret/push)**
  Upload secret values from a local file (all keys must be declared).
- **[`import`](/cli/commands/secret/import)**
  Import secret values from a file, skipping undeclared keys.
- **[`validate`](/cli/commands/secret/validate)**
  Validate that all required declared secrets are initialized.
- **[`exec`](/cli/commands/secret/exec)**
  Run a command with declared secrets injected as environment variables.
- **[`shell`](/cli/commands/secret/shell)**
  Launch an interactive shell with declared secrets in the environment.

## How It Works

1. **Configure a secret backend** in `atmos.yaml`. A store becomes a secret backend by setting `secret: true`:

   ```yaml
   stores:
     app-secrets:
       type: aws-secrets-manager
       secret: true
       identity: aws/prod-secrets
       options:
         region: us-east-1
         prefix: atmos/secrets
   ```

2. **Declare secrets** in your stack/component config under `secrets.vars`:

   ```yaml
   components:
     terraform:
       api:
         secrets:
           vars:
             DATADOG_API_KEY:
               description: "Datadog API key"
               store: app-secrets
               required: true
         vars:
           datadog_api_key: !secret DATADOG_API_KEY
   ```

3. **Provision values** with the CLI:

   ```shell
   atmos secret set DATADOG_API_KEY --stack=prod --component=api
   atmos secret list --stack=prod --component=api
   ```

## Secret scopes

Every secret has an explicit **scope** that controls where its value is stored. Two scopes are
available (a cross-stack `global` scope is planned):

- **`stack`** — declared at the top level of a stack (a `secrets:` block that is a sibling of
  `vars:`). Stored **once per stack** and shared by every component instance. Rotate it once and
  every instance sees the new value.
- **`instance`** (default) — declared under a component. Stored **per component instance**.

Scope is **derived from where the secret is declared**, not written by hand:

```yaml
# stack manifest (top level): SHARED_TOKEN is stack-scoped — one value for the whole stack
secrets:
  vars:
    SHARED_TOKEN: { sops: default }

components:
  terraform:
    api:
      secrets:
        vars:
          # Re-declaring SHARED_TOKEN under a component pulls it to INSTANCE scope for `api` only,
          # which now owns its own value. Other components keep using the shared stack value.
          SHARED_TOKEN: { sops: default }
          # API_KEY is declared only here → always instance-scoped.
          API_KEY: { sops: default }
```

The rule is **one-way**: a stack secret can be pulled down to instance scope by re-declaring it
under a component, but an instance-declared secret can never become stack-scoped. The override is
**opt-in** — an instance only carries its own value if it declares the secret; setting an instance
value for a secret that is still stack-scoped at that component is a hard error (declare it under
the component first, or omit `--component` to set the shared stack value).

Each backend maps scope to its native primitive and rejects scopes it can't represent. For **SOPS**,
the file is derived from scope under `spec.path` (default `secrets/`): stack →
`secrets/<stack>.enc.yaml`, instance → `secrets/<stack>.<instance>.enc.yaml`. A changed SOPS file is
treated as an automatic dependency by `atmos describe affected`, so rotating a secret marks the
components that consume it.

## Interactive prompts

When `--stack` or `--component` is missing and you are in an interactive terminal, Atmos prompts you
to choose one (the same built-in selector used elsewhere). `atmos secret set` and `atmos secret init`
with no arguments walk you through stack → component → secret. In CI / non-interactive shells these
prompts are disabled and a missing required flag returns the standard error, so scripts are
unaffected.

## Masking and Inspection

`atmos secret get`, `pull`, and `push` always retrieve real values to do their job; the values are masked in display output unless you pass `--mask=false`.

Inspection commands ([`describe`](/cli/commands/describe/component) and the [`list`](/cli/commands/list/list-values) family) resolve `!secret` to `<MASKED>` **without contacting the backend** when masking is enabled (the default) — so you can inspect a stack's shape with **no cloud credentials**. Pass `--mask=false` to retrieve and reveal the real values.

## See Also

- [`!secret` YAML function](/functions/yaml/secret)
- [Secrets configuration](/cli/configuration/secrets)
- [Stores configuration](/cli/configuration/stores)
