# Vaults & Secrets

Org-level encrypted key-value storage for credentials, with safe runtime injection.





Vaults are where you store anything sensitive your agents need: API keys, database passwords, OAuth tokens, webhook signing secrets. Values are encrypted at rest, hidden from the dashboard, and injected at the network layer instead of the filesystem so they never end up in a log file or a process memory dump.

For the encryption and injection details, see [How Nairi manages secrets](/help/vaults/security-model). This page is the practical setup guide.

<Callout type="warn">
  Vaults are a **managed-runtime** feature. Self-hosted agents don't have a secret proxy and can't attach vaults. Use plain environment variables or your own secret store on self-hosted runtimes.
</Callout>

## Mental model [#mental-model]

* A **vault** is a named bundle of secrets. You group related credentials together: "Production", "HubSpot creds", "Stripe test keys".
* A **secret** has an env-var name, a value, and an allowed-domains list.
* Vaults live at the **org level**. Attach them to specific agents from the agent editor.
* At runtime, the agent sees a placeholder (e.g. `CCASECRET_HUBSPOT_API_KEY`) in its environment. The real value is swapped in over the wire when the agent makes an outbound HTTPS request to one of the allowed domains.

## Best paired with skills [#best-paired-with-skills]

The canonical use case for a vault: a [skill](/help/skills) that talks to a third-party API, with the API key held in a vault.

Say you've packaged a skill called `vercel-deploy` that triggers Vercel deployments. The skill's script just reads `$CCASECRET_VERCEL_API_KEY` and `curl`s the Vercel API. You wire it together by:

1. Creating a vault with a `VERCEL_API_KEY` secret, allowed domain `api.vercel.com`.
2. Uploading the skill at [Settings → Artifacts → Skills](https://app.nairi.ai/settings/artifacts?tab=skills).
3. Attaching both the vault and the skill to the same agent.

Anywhere the skill (or the agent itself) sends a request to `api.vercel.com`, the proxy injects the real key. Anywhere else, it sees the placeholder.

This pattern works for any API the agent should be able to call: Render, Stripe, Linear, GitHub, internal services. One skill that knows how to call the API, one vault secret that authenticates it.

## Step 1 — Create a vault [#step-1--create-a-vault]

1. Open [Settings → Artifacts](https://app.nairi.ai/settings/artifacts?tab=vaults).
2. Click **Create Vault**.
3. **Name** — required, e.g. "Production". Up to 255 characters.
4. **Description** — optional. A note for your future self about what this vault is for.
5. Click **Create**.

The vault appears in the list with &#x2A;*"0 secrets"** next to it.

## Step 2 — Add secrets to the vault [#step-2--add-secrets-to-the-vault]

Click the vault's row to open the **Manage Vault** dialog. Scroll to the **Add Secret** form.

| Field                         | Required                          | Notes                                                                                                                                                                                                |
| ----------------------------- | --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Environment Variable Name** | yes                               | The name the agent will reference, e.g. `HUBSPOT_API_KEY`. Convention is uppercase with underscores.                                                                                                 |
| **Value**                     | yes                               | The secret itself. Multiline is fine (useful for PEM-formatted keys).                                                                                                                                |
| **Allowed Domains**           | optional but strongly recommended | Comma-separated list of domains where this secret is allowed to be injected, e.g. `api.hubapi.com`. Wildcards like `*.example.com` are supported. Leave blank to allow any domain (not recommended). |

Click **Add Secret**. The new secret appears in the list above.

### Common allowed-domain values [#common-allowed-domain-values]

| Secret              | Allowed domains              |
| ------------------- | ---------------------------- |
| `ANTHROPIC_API_KEY` | `api.anthropic.com`          |
| `OPENAI_API_KEY`    | `api.openai.com`             |
| `GITHUB_TOKEN`      | `api.github.com, github.com` |
| `STRIPE_SECRET_KEY` | `api.stripe.com`             |
| `LINEAR_API_KEY`    | `api.linear.app`             |
| `HUBSPOT_API_KEY`   | `api.hubapi.com`             |
| `SLACK_BOT_TOKEN`   | `slack.com`                  |

The narrower the list, the smaller the blast radius if anything goes wrong. See [security model](/help/vaults/security-model) for why this matters.

## Step 3 — Attach the vault to an agent [#step-3--attach-the-vault-to-an-agent]

The vault doesn't reach any agent until you attach it.

1. Open [Fleet](https://app.nairi.ai/agents/fleet) and click the agent.
2. Scroll to **Secret Vaults** on the Configuration tab.
3. Tick the checkbox next to the vault you just created. Each row shows the vault's name and a small badge like "3 secrets" so you can confirm you're picking the right one.
4. Click **Save & Redeploy** at the bottom-right.

The agent's container is rolled out, the proxy fetches the new secret list, and the agent can now use the placeholders.

## Step 4 — Reference the secret [#step-4--reference-the-secret]

Use the env-var name you set, prefixed with `CCASECRET_`. The agent sees this placeholder string verbatim. The real value gets swapped in by the secret proxy on outbound HTTPS calls to one of the allowed domains.

### From a skill [#from-a-skill]

The most common pattern. A skill's helper script reads the env var and makes the API call:

```bash
# scripts/trigger-deploy.sh inside a `vercel-deploy` skill
curl -X POST https://api.vercel.com/v13/deployments \
  -H "Authorization: Bearer $CCASECRET_VERCEL_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"name\": \"$PROJECT_NAME\", \"target\": \"production\"}"
```

The skill author doesn't need to know anything about vaults — they write the script assuming the env var is set. The operator (you) wires up the vault with `VERCEL_API_KEY` and an allowed domain of `api.vercel.com`. Same skill works for everyone, secrets stay per-org.

### From a shell command the agent runs ad-hoc [#from-a-shell-command-the-agent-runs-ad-hoc]

If the agent decides to call an API directly rather than going through a skill (e.g. "check our Vercel deployment status"), the same placeholder works from any shell command it runs:

```bash
curl -H "Authorization: Bearer $CCASECRET_VERCEL_API_KEY" \
  https://api.vercel.com/v2/deployments
```

The literal string `CCASECRET_VERCEL_API_KEY` is what ends up in the agent's memory and any logs. The real key only ever exists on the wire, briefly, on a request to `api.vercel.com`.

<Callout type="warn">
  Vault secrets are **not** for MCP credentials. MCP configs are hidden from the agent (they live in the mcp-proxy sidecar), so credentials needed by an MCP server should be inlined directly in the MCP JSON. See [Credentials in MCP configs](/help/mcp#credentials-in-mcp-configs).
</Callout>

## Rotating a secret [#rotating-a-secret]

You don't need to redeploy to rotate. Open the vault, find the secret, click the edit icon (pencil), paste the new value, click **Save**. The secret proxy refreshes its cache every two minutes, so the new value is live within that window.

If you need it instant: redeploy the agent. The new container picks up the latest secrets on boot.

## Updating allowed domains [#updating-allowed-domains]

Same flow as updating the value. Open the secret, change the domains field, save. The change takes effect on the proxy's next refresh.

## Deleting a secret [#deleting-a-secret]

In the vault's manage dialog, find the secret, click the trash icon. A confirmation dialog asks you to confirm by typing the secret's name.

The deletion takes effect within \~2 minutes (or immediately on the next redeploy). Any code still referencing the placeholder after the secret is gone will see the literal `CCASECRET_<NAME>` string on the wire.

## What about non-sensitive config? [#what-about-non-sensitive-config]

Use **Environment Variables** on the agent itself, not a vault. They're meant for things like `GITHUB_DEFAULT_BRANCH=main` or `API_BASE_URL=https://...`. Plaintext, not domain-restricted, visible in the dashboard. See the [agent editor walkthrough](/help/building-agents/how-to-deploy#environment-variables).

## Related [#related]

* [How Nairi manages secrets](/help/vaults/security-model) — the full security model
* [Adding secrets to an agent](/help/building-agents/how-to-deploy#secret-vaults)
* [MCP Tools](/help/mcp) — for credentials used by MCP servers (different path, no vault)
* [Security at Nairi](/help/security)

***

*Can't find what you're looking for? Email [support@nairi.ai](mailto:support@nairi.ai).*
