# Use SOPS-backed secrets

Use a SOPS-backed Secret when Kix should own the Kubernetes Secret but its
values must remain encrypted in the repository and Nix store. Kix evaluates
the encrypted file into the cluster artifact, then runs `sops --decrypt`
immediately before it applies the Secret.

This guide uses an Age identity. Install
[SOPS](https://github.com/getsops/sops) and Age first, and make sure the
machine that runs `kix deploy` can access the identity.

## Create an Age identity

Generate an identity and print its recipient:

<Command
  commands={[
    "age-keygen -o age-key.txt",
    "age-keygen -y age-key.txt",
  ]}
/>

Keep `age-key.txt` outside the repository. Your deployment environment needs
the identity, while developers only need the recipient to encrypt changes.

## Encrypt the values

Create the file with the keys your package expects:

```yaml title="credentials.enc.yaml"
username: application-user
password: replace-with-a-generated-password
```

Encrypt it in place. The shell variable keeps the full recipient out of later
commands:

<Command
  commands={[
    'recipient=$(age-keygen -y age-key.txt)',
    'sops encrypt --age "$recipient" --in-place credentials.enc.yaml',
  ]}
/>

Open the file before committing it and confirm that the values have become
`ENC[...]` entries. Commit the encrypted file, but not `age-key.txt`.

## Declare and consume the Secret

Pass the encrypted file to `scope.mkSecret` and declare its keys explicitly:

<Snippet {...sopsBackedSecret} />

Kix cannot derive the key names from encrypted values, so `keys` is required.
It also lets the `out` helpers catch misspelled keys during evaluation.

This validation checks how the package uses the declared keys, not whether the
encrypted file contains them. If the file omits a declared key, Kix can apply
the Secret successfully and the workload fails when it tries to read the key.
By contrast, the deploy-time readiness check for `scope.mkSecretRef` verifies
that every declared key exists in the live Secret.

Add `hash` to catch an encrypted file that changed without the declaration
changing:

```nix title="package.nix"
credentials = scope.mkSecret {
  name = "credentials";
  source = ./credentials.enc.yaml;
  keys = [
    "username"
    "password"
  ];
  hash = "sha256-<base64>";
};
```

Kix records this value in the `kix.run/content-hash` annotation. During
deployment, it hashes the decrypted data and stops if the hash differs. On the
first attempt, use the computed hash from the error message to fill in the
declaration.

Consume the Secret in the same way as any other Kix-managed Secret:

<Snippet {...sopsSecretEnv} />

The reference from `deployment` to `credentials` also gives Kix the deploy
ordering between them.

## Check the cluster artifact

Render the cluster before deploying:

<Command {...renderedSecret} />

The output excerpt reports the declared keys and confirms that the rendered
Secret has neither `data` nor `stringData`. The artifact contains a reference
to the encrypted source, not the decrypted values.

## Deploy with access to the identity

Point SOPS at the identity on the deployment machine, then deploy normally:

<Command
  commands={[
    "export SOPS_AGE_KEY_FILE=/secure/path/age-key.txt",
    "kix deploy how-to-platform-sops",
  ]}
  cwd="kix-examples/"
/>

Kix stops the deploy if SOPS is unavailable, the identity cannot decrypt the
file, or the decrypted document is not a top-level key-value map.

Check that Kubernetes received the two declared keys without printing their
values:

<Command {...secret} />

Give the deployment identity to CI through its secret store or workload
identity mechanism. If another controller should own the Kubernetes Secret,
use an [externally managed Secret](/docs/how-to/platform-capabilities/use-externally-managed-secrets/)
instead.

:::caution
Kubernetes Secret values are base64-encoded, not encrypted by that encoding.
Configure encryption at rest and restrict Secret access in the cluster as you
would for any other Kubernetes Secret.
:::

:::note[Reference]
See [Secrets and Secret refs](/docs/reference/scope-helpers/secrets-and-secret-refs/)
for the complete `scope.mkSecret` API.
:::