---
title: Secrets
description: Store values that reach the machine but never come back, and that a captured disk never carries.
---

## Set

<CodeGroup>

```ts TypeScript
await machine.secrets.set("GITHUB_TOKEN", token);
```

```python Python
machine.secrets.set("GITHUB_TOKEN", token)
```

</CodeGroup>

A name must be an uppercase environment variable name — `^[A-Z_][A-Z0-9_]*$`. Accepting a name a
guest cannot export would store a secret that silently never arrives. Values are up to 64 KB and
are stored encrypted.

Setting an existing name replaces the value and bumps its `version`, so you can tell a change from
a no-op.

## A stored value is never returned

There is no read method, and the response shape has **no field a value could travel in**. That is
not an omission: a product that can hand a secret back is a product whose secrets are in every
response log, error report and browser cache that ever touched one.

<CodeGroup>

```ts TypeScript
for await (const s of machine.secrets.list()) {
  console.log(s.name, s.version, s.delivered_at);
}
```

```python Python
for s in machine.secrets.list():
    print(s["name"], s["version"], s["delivered_at"])
```

</CodeGroup>

`delivered_at` is null when a secret is stored and not yet delivered — the normal state for one set
on a machine that is not running. It lands on the next boot; a running machine takes delivery
within seconds.

## Reading them inside the machine

Secrets arrive at `/run/computer/secrets.env`, mode `0600`, and a login shell sources them:

<CodeGroup>

```ts TypeScript
const { stdout } = await machine.terminal.execShell("echo $GITHUB_TOKEN");
```

```python Python
stdout = machine.terminal.exec_shell("echo $GITHUB_TOKEN")["stdout"]
```

</CodeGroup>

`/run` is a tmpfs, which is the entire point: **a disk capture cannot contain what was never on the
disk**. A [snapshot, fork or template](/docs/sdk/snapshots) does not carry a secret, and a machine
that restarts takes delivery again from storage.

## Delete

<CodeGroup>

```ts TypeScript
await machine.secrets.delete("GITHUB_TOKEN");
```

```python Python
machine.secrets.delete("GITHUB_TOKEN")
```

</CodeGroup>

Removes it from storage and from the running machine. It cannot remove a copy the guest already
made — a process that read the value and wrote it elsewhere still holds it. Revoking at the source
is this product's half; rotating at the issuer is yours.

## Deployment requirement

Secrets need an encryption keyring configured. Without one the API raises `unsupported_operation`
rather than storing a value in a form it could be read back from.

## Secrets versus the browser profile

The two are deliberately opposite, and picking the wrong one is the common mistake:

|                                                             | Carried by a capture? | Use for                                              |
| ----------------------------------------------------------- | --------------------- | ---------------------------------------------------- |
| A [browser session](/docs/sdk/browser#the-profile-persists) | yes                   | a login every future machine should inherit          |
| A secret                                                    | no                    | a credential that must not end up in a captured disk |
