# Secrets

> Store API tokens, credentials and registry logins once, and pass them to Jobs, Sandboxes, Functions and Agents as env vars and files.

Source: https://nodus-platform-site.pages.dev/docs/guides/secrets/
Build revision: 211ad9f836655b1c3a2668c4693e442471f28614

A Secret holds named values, such as an API token or a database URL, encrypted with a key that belongs to your org. You create it once and name it in the Jobs and Sandboxes that need it. Nodus never returns a value through the API, the console or the CLI, and it redacts secret values from logs and outputs.

## Create a Secret

Terminal window

```console
nodus secret create hf-token --from-literal HF_TOKEN=hf_xxx
nodus secret create app-env --from-env-file .env
nodus secret create registry --type Registry \
  --from-literal server=ghcr.io --from-literal username=ada --from-literal password=ghp_xxx
```

Key names start with a letter or `_`, use letters, digits and `_`, and may not start with `NODUS_`. A Secret holds up to 64 keys of at most 4 KiB each. `nodus get secret hf-token` shows the key names and the current version, never the values.

## Use it in a Job

List the Secret under `secrets` to receive every key as an env var and as a read-only file `/run/secrets/<secret>/<key>`, or pick one key under another name with `valueFrom.secretKeyRef`:

job.yaml

```yaml
apiVersion: nodus.dev/v1
kind: Job
metadata:
  name: ex-secrets-env
spec:
  image: nodus/python:3.12
  secrets: [ex-secrets-env]          # every key as an env var and a file under /run/secrets/ex-secrets-env/
  env:
    - name: TOKEN                    # one key under another name
      valueFrom: {secretKeyRef: {name: ex-secrets-env, key: API_TOKEN}}
  command: [python, check.py]
```

check.py

```python
import os

token = os.environ["API_TOKEN"]
with open("/run/secrets/ex-secrets-env/API_TOKEN") as f:
    from_file = f.read()

# Print facts about the value, never the value: Nodus redacts secret values from logs anyway.
print(f"token has {len(token)} characters")
print("file matches" if from_file == token else "file differs")
print("alias matches" if os.environ["TOKEN"] == token else "alias differs")
```

Terminal window

```console
nodus apply -f job.yaml
nodus logs job/ex-secrets-env
```

The same `secrets` and `env` fields work on Sandboxes, Workspaces, Functions and Agents. In Python:

```python
hf = nodus.Secret.from_name("hf-token")
env = nodus.Secret.from_dotenv(".env")
```

A command whose literal `env` value equals one of the Secret values in its project is refused with `SecretValueInEnv`: reference the Secret instead of pasting its value. The same check applies to commands you start in a running Sandbox, Workspace or Job with `nodus exec` or the Processes API, which also refuse an `env` that sets the same name twice. Values shorter than 8 characters, and the `server` and `username` of a Registry Secret, are not checked.

A Job receives the Secret versions pinned when you submitted it, and a Sandbox the versions pinned when it started. If a Secret it uses is deleted (even if you create a new one with the same name), or a new value replaced a Job’s pinned version before a retry started, that attempt fails with `LaunchFailed`. Secret values delivered as env vars may total at most 1 MiB per container, and as files at most 8 MiB.

## Change a value

Writing a Secret again creates a new version; writing the same values again changes nothing. A Sandbox pins the newest version each time it starts, so it gets the new value on its next start, while a running Sandbox keeps the version it started with. A Job keeps the versions pinned when you submitted it, so every attempt runs with the same values; a retry that starts after you replace a value fails with `LaunchFailed`, so submit the Job again to use it. Earlier versions are deleted 7 days after they are replaced.

hf-token.yaml

```yaml
apiVersion: nodus.dev/v1
kind: Secret
metadata: {name: hf-token}
spec:
  stringData: {HF_TOKEN: hf_new}
```

Terminal window

```console
nodus apply -f hf-token.yaml
```

## Private registries

A Secret with `type: Registry` holds `server`, `username` and `password`. Name it under `imagePullSecrets` to run or build from a private image:

```yaml
spec:
  image: ghcr.io/acme/trainer:1.4
  imagePullSecrets: [{name: registry}]
```

Nodus resolves the tag to a digest when you submit, using the Secret, and the machine that runs the work pulls that digest with the same Secret version. The credential is used for the pull alone: it is not an env var or a file in the container. When the work runs on a provider that pulls containers itself, Nodus first copies the image by digest into your org’s space in the Nodus registry, so your registry credential never leaves Nodus.

## Limits and errors

|Error|Meaning|Fix|
|-|-|-|
|`SecretValueInEnv`|An `env` value equals a Secret value|Use `valueFrom.secretKeyRef` or `secrets`|
|`EncryptionFailed`|The value could not be encrypted|Retry; the Secret keeps its previous version|
|`ImagePullFailed`|The registry refused the pull Secret|Check `server`, `username` and `password`|
