---
question: Do workspace secrets and engine secrets sync?
kind: overview
---

# Secrets

The secret service holds the credentials a project needs: API keys, passwords, tokens. It is the kernel service `services.secret`. `config.yaml` reads it through `{{ secret.NAME }}`, and modules call it at run time. By default it is the project's `.env` file.

Super admins can add, reveal, replace and delete engine secrets in Nexus, under System › Data › Secrets. Workspaces have a separate store, Settings › Secrets: see [Two stores](#two-stores).

<!-- matrix -->

## Backends

`services.secret.secret_adapters` is a list. A read takes the first adapter that has the key. A write or a removal goes to every adapter.

| Adapter | Storage | Keys |
|---|---|---|
| `dotenv` | A dotenv file | `path` (default `.env`), `read_only` (default `false`), `environment_first` (default `false`) |
| `naas` | The Naas API | `naas_api_key`, `naas_api_url` |
| `base64` | One base64-encoded `.env` stored under a key of another adapter | `secret_adapter` (that adapter's block), `base64_secret_key` |

A new project writes one `dotenv` adapter with `config: {}`. The `dotenv` file must exist, or the engine does not start. With `read_only: true` the file is read but never written, as for a file another process owns. The block is on the [Configuration Reference](/user-guide/get-started/configuration#secret).

## Two stores

| | Engine secrets | Workspace secrets |
|---|---|---|
| Where | The secret adapters (`.env` by default) | The Nexus database, encrypted, per workspace |
| Who manages them | The project owner; super admins under System › Data › Secrets | Workspace admins and owners, under Settings › Secrets |
| Read by | `{{ secret.NAME }}` in config, `secret.get` in modules | Nexus chat, for the provider key of a workspace agent (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, ...) |

The two do not sync. A workspace secret never fills `{{ secret.NAME }}`, and changing a value in one store leaves the other as it was.

## Key concepts

- **Strings by name.** A secret is a name and a text value. Use names that work as environment variables and in `{{ secret.NAME }}`: letters, digits and `_`, not starting with a digit.
- **Config reads the environment first.** `{{ secret.NAME }}` takes a process environment variable of that name before any adapter. `secret.get` reads the `dotenv` file first, then the environment, unless `environment_first: true`.
- **Read at start.** The `dotenv` adapter loads its file when the engine starts, and config is rendered once. A value changed by hand in the file is seen after a restart: see [Rotate a secret](/user-guide/build/secrets/rotate-a-secret).
- **Never logged.** `SecretSet`, `SecretRemoved` and `SecretError` events carry the name, not the value. System › Data › Secrets masks values until a super admin reveals one, and audits the reveal.
- **Dependency.** A module lists `Secret` in `ModuleDependencies.services`. The attribute is `self.engine.services.secret`.
