> ## Documentation Index
> Fetch the complete documentation index at: https://docs.alpha.new/llms.txt
> Use this file to discover all available pages before exploring further.

# Secrets

> Keep API keys and settings out of chat: global secrets reach every session, session secrets reach one.

A secret is a named value, such as `STRIPE_SECRET_KEY`, that Alpha hands to your session's machine as an environment variable. The agent's commands, the apps it starts and the [Shell](https://docs.alpha.new/workspace/shell.md) read it, and the value never passes through the transcript. If the service has a ready connector, such as Gmail or Notion, sign in under [Integrations](https://docs.alpha.new/integrations/overview.md) instead.

> [!WARNING]
>
> Never paste a key into the composer. Anything you send lands in the transcript and in the agent's context. When your draft holds something shaped like a credential, a warning appears above the composer: "That looks like a Stripe secret key. Keys typed here land in the transcript — add it under Secrets instead, and the agent gets it as an environment variable." It doesn't block sending.

## Global or session: where should a secret live?

| | Global secret | Session secret |
| - | - | - |
| Reaches | Every session you own | Only the session it was added in |
| Manage it in | **Account menu → Global secrets**, or the **Global** group of any session's **Secrets** tab | That session's **Secrets** tab, in the **This session** group |
| Same name in both | Loses. Its row shows an **overridden** tag ("This session has its own value for it"). | Wins in its own session. Every other session still gets the global value. |
| Good for | Keys you use everywhere | A test key, or a value that differs per app |
| Removed when | You delete it | You delete it, or delete the session |

![The Harbor bookings session with the Secrets tab open in the workspace, whose tab strip reads Plan, Preview, Shell, Database, Secrets with its ×, README.md and server.js, followed by +, the GitHub and Share icons, Publish and the files button. The chat on the left shows an answered The agent has questions card and a Proposed plan card titled Automatic class waitlist, above the composer's placeholder Describe what to plan…. In the Secrets tab, a section headed This session holds the card This session's key, with FORGE\_TOOLS\_URL reading https://alpha.new/api/tools/v1, FORGE\_TOOLS\_API\_KEY masked as dots, and the buttons Reveal, Copy .env lines and Rotate. Below it, a section headed Secrets, with the caption environment variables for this session and an Add secret button at the right, holds a panel with two empty groups: GLOBAL reads None — global secrets reach every session you own., and THIS SESSION reads None — a secret here reaches only this session. Under the panel, a grey note reads Anything the agent runs can read them — only add what you'd hand the agent directly. Values of 8+ characters are replaced with \[redacted\] in the transcript when they appear verbatim; output that transforms a value can still expose it.](https://mintcdn.com/lyzr-15a99e0f/ObBpayiae_4aNgzN/images/secrets/secrets-tab-overview.png?fit=max\&auto=format\&n=ObBpayiae_4aNgzN\&q=85\&s=5a85ebc9c0fa250ae2adbe842640dd12)

One list shows what this session gets: your global secrets above, its own below.

Anything the agent runs can read your secrets, and so can the app it builds. Only add what you'd hand the agent directly.

## Add a secret

**1. Open a list of secrets**

For global secrets, click your name at the bottom of the sidebar and choose **Global secrets**. For a session, click **+** at the end of the workspace's tab strip and choose **Secrets**, or click the **Secrets** tab if it is already open. Viewers of a [shared session](https://docs.alpha.new/sharing.md) don't get this tab.

**2. Click Add secret**

The **Add secret** form opens: "An environment variable for the commands the agent runs, from its next command on."

**3. Fill in Name and Value**

Type the name your code reads, such as `STRIPE_SECRET_KEY`, and paste the value. **Value** is a single-line field, so a service-account JSON goes in as one line.

**4. Choose Available in**

From a session's tab, pick **This session** ("Only here; beats a global of the same name") or **Global** ("Every session you own"). The account menu skips this choice and always saves globally.

![The Add secret dialog, with a close button at its top right. The description reads: An environment variable for the commands the agent runs, from its next command on. The Name field shows the placeholder STRIPE\_KEY in monospace. The Value field is empty, with an eye button at its right edge. Under the heading Available in, two option cards sit side by side: This session, selected with a check mark, with a key icon and the line Only here; beats a global of the same name, and Global, with a globe icon and the line Every session you own. The footer holds Paste a .env instead on the left, then Cancel and a dimmed Add secret on the right.](https://mintcdn.com/lyzr-15a99e0f/ObBpayiae_4aNgzN/images/secrets/add-secret-form-scope.png?fit=max\&auto=format\&n=ObBpayiae_4aNgzN\&q=85\&s=cce0218d821ba96280ab66e65607377d)

From a session, Available in asks where the secret goes, and This session is picked first.

**5. Save the secret**

Click **Add secret**. The new row appears in the list. If the name already exists there, the form says "STRIPE\_SECRET\_KEY is already set there — saving replaces its value." and the button reads **Save**.

New commands see the secret right away, even in running sessions. Anything already running, such as a dev server, keeps its old environment until it restarts. Ask the agent to restart it, or click **Restart shell** in the Shell.

## Import a .env file

1. In the **Add secret** form, click **Paste a .env instead**. The title changes to **Paste a .env**.
2. Paste the file into **.env contents**. Each line holds one `NAME=value`. Blank lines and `#` comments are ignored, `export NAME=value` lines work, and quotes around a value are removed.
3. Tick **Replace values that are already set** if the paste should overwrite names you already have. Left unticked, those names are skipped as "already set".
4. Choose **Available in** if you are in a session, then click **Import**.

![The Paste a .env dialog, with a close button at its top right. The description reads: An environment variable for the commands the agent runs, from its next command on. The empty .env contents box shows two placeholder lines in monospace, STRIPE\_KEY=sk\_live\_… and GITHUB\_TOKEN=ghp\_…. Below the box, the checkbox Replace values that are already set is unticked. Under the heading Available in, the cards show This session selected with a check mark, Only here; beats a global of the same name, and Global, Every session you own. The footer holds Add one instead on the left, then Cancel and a dimmed Import.](https://mintcdn.com/lyzr-15a99e0f/ObBpayiae_4aNgzN/images/secrets/paste-env-import.png?fit=max\&auto=format\&n=ObBpayiae_4aNgzN\&q=85\&s=75f6d2a9707e9ae28ca880755ce68537)

Names you already have are skipped unless you tick Replace values that are already set.

If anything is skipped, the paste stays on screen so you can fix it and import again. **Add one instead** switches back to the single form.

## Copy, replace or delete a secret

The eye for **Reveal** is always on a row. The other actions appear when you hover it.

| Action | What it does |
| - | - |
| **Reveal** / **Hide** (eye) | Shows the full value in place. Closing the dialog or the tab, or switching sessions, hides it again. |
| **Copy value** | Copies the value to your clipboard without showing it. |
| **Replace value** (pencil) | Opens **Replace STRIPE\_SECRET\_KEY** with a **New value** field. The name can't change; to rename, add a new secret and delete the old one. |
| **Delete** (bin) | Asks "Delete STRIPE\_SECRET\_KEY? Sandboxes already running keep it until they restart." There is no undo. |

Values of 12 or more characters show their last four, so you can tell two keys apart without revealing either. Deleting a session secret that shadowed a global one brings the global value back for that session.

## What happens when the agent needs a key?

The agent is told never to ask for keys in a message. When its work needs a key you haven't added and no connector covers the service, it posts a card, then carries on with whatever doesn't need the key and finishes its turn.

1. Click **Add STRIPE\_SECRET\_KEY** on the card. The **Add secret** form opens with the name filled in and locked, because the agent's code depends on that exact name.
2. Paste the value and click **Add secret**. It saves to this session.
3. The card changes to "STRIPE\_SECRET\_KEY is added", with "It is in the agent's environment from its next command."
4. Click **Tell the agent to continue**. Alpha sends "I added STRIPE\_SECRET\_KEY under Secrets — it is in your environment now. Continue." and the button reads **Told the agent**.

The request also waits in a tray above the composer, "Add `STRIPE_SECRET_KEY` · the agent asked for it", with **Add** and then **Continue**, so it never scrolls away. While that tray row is showing, adding the key from either place tells the agent on its own, so you don't need step 4. The agent is told once, whichever button you use. Viewers of a shared session see the card without its buttons.

The agent's own reach is deliberately narrow:

| The agent can | The agent can't |
| - | - |
| List the names of this session's secrets and of your global ones | Read a value through Alpha. Its commands return names only. |
| Ask you for a key with a card, once per name every two minutes | Add or change a global secret |
| Save a value it generated itself, such as a random `SESSION_SECRET`, as a new session secret | Replace a session secret that is already set |
| Read secrets from its environment, like your app does | Delete a secret. Only you can, from the list. |

A session secret the agent saves wins over a global one of the same name in that session, as one of yours would.

## How are values hidden in the transcript?

Alpha replaces any secret value of 8 or more characters with `[redacted]` wherever it appears verbatim. That covers the transcript, publish and setup logs, session search, and files that viewers of a shared session open. Your GitHub token and your sessions' tools keys are masked the same way.

A few values are left visible because masking them would only hide ordinary text: the words `production`, `development`, `localhost`, `postgres` and `postgresql`, an email address, and a plain web address with no credentials in it. So `https://api.openai.com/v1` shows, while a webhook URL or a database URL with a password stays masked.

> [!NOTE]
>
> Masking works only on exact matches. Output that transforms a value, such as base64-encoding it, printing part of it or reversing it, can still expose it. Values under 8 characters are never masked.

Published apps don't receive your secrets automatically. When you publish, you pick which ones production gets under **Environment**. See [Publish your app](https://docs.alpha.new/publish/publish-your-app.md).

## Name rules and limits

If a save is refused, the form shows one of these messages.

| Rule | Message you see |
| - | - |
| Names start with a letter or `_`, then letters, digits and `_`, up to 64 characters | "name must start with a letter or \_ and contain only letters, digits and \_ (max 64 chars)" |
| Names the machine's shell depends on are reserved | "PATH is reserved — setting it would break the sandbox shell" |
| A value can't be empty | "value must not be empty" |
| One value is at most 8 KB (default) | "value must be at most 8192 bytes" |
| At most 256 secrets in one place | "at most 256 secrets" |
| Values in one place: at most 32 KB in total (default) | "secrets total would exceed …", followed by the cap in bytes |

The reserved names are `PATH`, `HOME`, `PWD`, `OLDPWD`, `USER`, `LOGNAME`, `SHELL`, `SHLVL`, `PS1` to `PS4`, `IFS`, `ENV`, `BASH_ENV`, `SHELLOPTS`, `BASHOPTS`, `NODE_OPTIONS`, `PYTHONPATH`, `PYTHONSTARTUP`, `PERL5OPT`, `TMPDIR`, `TERM`, `LANG` and `CODEX_HOME`, plus any name starting with `LD_` or `FORGE_`. Case doesn't matter.

Your global secrets and each session's own secrets are counted separately. A trailing newline from copy and paste is dropped when the value is saved. Every limit is also listed in [Limits](https://docs.alpha.new/reference/limits.md#routines-setups-and-secrets).

## Next steps

> **[Connect an app](https://docs.alpha.new/integrations/connect-apps.md)**
>
> Sign in to Gmail, Slack or Notion once, so the agent gets tools rather than a raw key.

> **[Use integrations in your app](https://docs.alpha.new/integrations/in-your-app.md)**
>
> What "This session's key" is for, and the two lines an exported app needs in its `.env`.

> **[Publish your app](https://docs.alpha.new/publish/publish-your-app.md)**
>
> Choose which secrets production gets, at build time and at runtime.

> **[The Shell](https://docs.alpha.new/workspace/shell.md)**
>
> Check a variable with `printenv`, and restart the shell to load a secret you added since it opened.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.