---
slug: agents/secrets
title: Secrets
kind: reference
surface: agents
summary: Reference for Aetherfy agent secrets — agent-scoped and workspace-scoped values injected as environment variables, key naming rules, the reserved AETHERFY_ prefix, and the rule that changing a secret takes effect on the next deploy or run rather than immediately.
sources:
  - aetherfy-control-plane:api/routes/secrets.py
  - aetherfy-control-plane:models/secret.py
  - aetherfy-cli:cmd/secrets.go
  - dashboard/pages/api/controlplane/secrets.js
---

# Secrets

## What Aetherfy secrets are

A secret on Aetherfy is a named value — an API key, a database URL, a signing
token — that Aetherfy stores encrypted at rest and injects into your agent's
machine as a plain **environment variable** at deploy or run time.

Your code reads them exactly as it reads any environment variable. There is no
SDK call and no fetch step.

**Values are never readable back.** The Aetherfy CLI and API return only keys
and metadata. Once you set a secret you cannot retrieve its value from Aetherfy
by any route — if you lose it, rotate it at the source and set it again.

## The two secret scopes on Aetherfy

Every Aetherfy secret has exactly one scope.

| Scope | Visible to | Set with |
|---|---|---|
| Agent-scoped | One agent | `afy secrets set <agent> KEY=value` |
| Workspace-scoped | Every agent in that workspace | `afy secrets set --workspace <workspace> KEY=value` |

When both scopes define the same key, the **agent-scoped secret wins** — it
overrides the workspace-scoped value for that agent only. Every other agent in
the workspace continues to see the workspace value.

That precedence is the intended way to give one agent a different credential
from its siblings without splitting the workspace or duplicating the shared
secrets across every agent.

## Setting and listing Aetherfy secrets

Set one or more secrets on an agent in a single command:

```bash
afy secrets set my-agent API_KEY=sk-xxxxx DB_URL=postgres://user:pass@host/db
```

Setting a key that already exists updates it — Aetherfy treats this as an
upsert, not an error.

To keep a value out of your shell history and out of the process list, pipe it
in instead:

```bash
echo "sk-xxxxx" | afy secrets set my-agent API_KEY --stdin
```

List the keys Aetherfy holds for an agent. Only keys and metadata come back,
never values:

```bash
afy secrets list my-agent
```

Aetherfy enforces no limit on the number of secrets an agent may have, and no
maximum value size.

## Workspace-scoped Aetherfy secrets

Secrets shared by every agent in a workspace are set against the workspace
rather than an agent:

```bash
afy secrets list --workspace my-workspace
afy secrets set --workspace my-workspace SHARED_API_KEY=sk-xxxxx
afy secrets delete --workspace my-workspace SHARED_API_KEY
```

An agent belongs to at most one workspace, declared with the `workspace` field
in `aetherfy.yaml`:

```yaml
name: my-agent
runtime: python3.12
workspace: my-workspace
```

Workspace names on Aetherfy are immutable after creation, so a workspace-scoped
secret's audience is stable — moving an agent means changing which workspace it
declares, not renaming the workspace.

## Deleting an Aetherfy secret

```bash
afy secrets delete my-agent API_KEY
```

`afy secrets delete` always asks for confirmation on Aetherfy. There is
deliberately no `--force` flag on it, so it cannot be made silent in a script.

## Secret key rules on Aetherfy

| Rule | Detail |
|---|---|
| Length | 1–255 characters |
| First character | A letter or an underscore |
| Remaining characters | Letters, digits, `_`, and `-` |

| Key | Valid on Aetherfy |
|---|---|
| `API_KEY` | Yes |
| `_INTERNAL_TOKEN` | Yes |
| `db-url-2` | Yes |
| `2ND_KEY` | No — starts with a digit |
| `MY.KEY` | No — `.` is not allowed |

## Reserved names Aetherfy rejects

The entire **`AETHERFY_` prefix is reserved**. Attempting to set any key
beginning with it is rejected, which prevents a secret from shadowing the
environment Aetherfy provides to your agent.

These exact names are reserved:

| Reserved name |
|---|
| `AETHERFY_AGENT_ID` |
| `AETHERFY_AGENT_NAME` |
| `AETHERFY_REGION` |
| `AETHERFY_SPAWN_URL` |
| `AETHERFY_API_KEY` |
| `AETHERFY_VECTORS_URL` |
| `AETHERFY_WORKSPACE` |
| `AETHERFY_WORKSPACE_AGENTS` |
| `AETHERFY_SPAWN_PAYLOAD` |
| `AETHERFY_PARENT_AGENT_ID` |
| `AETHERFY_MODEL_NAME` |

If you need your own value for something in that space, give it a different
name — `OPENAI_API_KEY` or `MY_API_KEY` rather than `AETHERFY_API_KEY`.

## Changing a secret does not redeploy an Aetherfy agent

This is the most important operational fact on this page, and the one most
likely to cost you an hour.

Changing a secret on Aetherfy does **not** redeploy or restart a running agent.
Secrets are injected at deploy or run time, so a running machine keeps the value
it started with. A new value takes effect on the **next deploy or the next run**.

| Agent type | How the new value takes effect |
|---|---|
| `service` | Deploy again — `afy deploy` |
| `job` | On the next run, whether that is the next scheduled fire or a manual `afy agents run` |

So after rotating a credential for a long-lived `service` agent:

```bash
afy secrets set my-agent API_KEY=sk-new-value
afy deploy
```

Without that second command the agent keeps presenting the old credential until
something else redeploys it.

## Reading a secret from your Aetherfy agent code

Secrets arrive as ordinary environment variables, so read them with the
standard facility for your runtime.

Python:

```python
import os
import sys

api_key = os.environ.get("API_KEY")
if not api_key:
    print("API_KEY is not set on this agent", file=sys.stderr, flush=True)
    sys.exit(1)

print("API_KEY is present", flush=True)
```

Node:

```javascript
const apiKey = process.env.API_KEY;

if (!apiKey) {
  console.error('API_KEY is not set on this agent');
  process.exit(1);
}

console.log('API_KEY is present');
```

Never print a secret's value. Everything written to stdout and stderr becomes
the agent's logs on Aetherfy, and those logs are readable with `afy logs` for
7 days — see [/agents/runs-and-logs](/agents/runs-and-logs). Log the presence of
a secret, as above, never its contents.
