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

# Service Credentials

> Give a workspace's VFSs, CLIs and machines their credentials from the environment or a secret store.

A VFS, CLI or machine never holds a credential in its config. It holds a
pointer to one, and Mirage reads the value when it builds the workspace.

## Credentials stay in memory

Mirage keeps each value only in the memory of the process running the
workspace. Workspace configs and session records keep the pointer, never the
value. A snapshot saves each credential as `<REDACTED>`, so loading one needs
the credential again. Reading a secret writes nothing to the logs, and an error
names the field and the source, not the secret.

![The workspace config gives Mirage a pointer and the secret store gives it the value. The value is fetched when the workspace starts and stays in memory with the VFS, CLI or machine.](https://mintcdn.com/struktoai/STX210DlGEU89tfB/images/service-credentials-light.svg?fit=max\&auto=format\&n=STX210DlGEU89tfB\&q=85\&s=00c8126b78be87549a61b5085acc9a03)

Mirage reads a VFS, CLI or machine credential when the workspace starts, and
an [`env` entry](#set-environment-variables) the first time a line uses it. A
rotated secret takes effect the next time the workspace starts.

## Pointer fields

Write a pointer where the credential goes:

> [tab] YAML
>
> ```yaml workspace.yaml theme={null}
> mounts:
>   /slack:
>     vfs: slack
>     config:
>       token:
>         from: aws-sm
>         ref: mirage/slack
>         key: SLACK_BOT_TOKEN
> ```

> [tab] HTTP
>
> ```bash theme={null}
> curl http://127.0.0.1:8765/v1/workspaces \
>   -H "Authorization: Bearer $MIRAGE_AUTH_TOKEN" \
>   -H 'Content-Type: application/json' \
>   -d '{"id":"agent","config":{"mounts":{"/slack":{"vfs":"slack","config":{"token":{"from":"aws-sm","ref":"mirage/slack","key":"SLACK_BOT_TOKEN"}}}}}}'
> ```

> [tab] Python
>
> ```python theme={null}
> from mirage import Workspace
> from mirage.config import load_config, resolve_secrets
>
> config = await resolve_secrets(load_config({
>     "mounts": {
>         "/slack": {
>             "vfs": "slack",
>             "config": {
>                 "token": {"from": "aws-sm", "ref": "mirage/slack", "key": "SLACK_BOT_TOKEN"},
>             },
>         },
>     },
> }))
> ws = Workspace(**config.to_workspace_kwargs())
> ```

> [tab] TypeScript
>
> ```typescript theme={null}
> import { Workspace, configToWorkspaceArgs, loadWorkspaceConfig } from '@struktoai/mirage-node'
>
> const { mounts, options } = await configToWorkspaceArgs(
>   loadWorkspaceConfig({
>     mounts: {
>       '/slack': {
>         vfs: 'slack',
>         config: { token: { from: 'aws-sm', ref: 'mirage/slack', key: 'SLACK_BOT_TOKEN' } },
>       },
>     },
>   }),
> )
> const ws = new Workspace(mounts, options)
> ```

The same pointer works in a virtual CLI's credentials and in a machine entry,
such as E2B's `api_key`.

| Key | Meaning |
| - | - |
| `from` | A source, such as `env` or `aws-sm`, or a source you named under `secrets`. |
| `ref` | Where the secret is in that source, such as a secret name or a 1Password reference. |
| `key` | The field to read from the secret. An `env` pointer defaults it to the variable's name. |

## Sources

| Source | `from` | Reads |
| - | - | - |
| [AWS Secrets Manager](https://docs.mirage.strukto.ai/home/service-credentials/aws-secrets-manager.md) | `aws-sm` | A secret, by name or ARN. |
| [1Password](https://docs.mirage.strukto.ai/home/service-credentials/1password.md) | `1password` | An item or a field, through a service account. |
| Environment | `env` | A variable of the process running Mirage. |
| dotenv file | A source you declare | A `.env` file. |
| Saved entries | `strukto` | An entry saved on a Mirage server with [`mirage vfs`, `cli`, `runtime` or `machine`](https://docs.mirage.strukto.ai/home/access/cli#saved-connections). |

### Environment

Set the variable in the environment of the process running Mirage, then point
at it by name. `env` takes no `ref`.

```bash theme={null}
export SLACK_BOT_TOKEN=xoxb-your-token
```

```yaml workspace.yaml theme={null}
token:
  from: env
  key: SLACK_BOT_TOKEN
```

The local daemon reads its environment when it starts, so run
`mirage daemon restart` after exporting a new variable.

### dotenv file

Declare the file as a source, then point at it by that name. Python needs the
`dotenv` extra where Mirage runs: `uv tool install 'mirage-ai[dotenv]'` for the
CLI, or `uv add 'mirage-ai[dotenv]'` in a project. The Node package includes it.

```yaml workspace.yaml theme={null}
secrets:
  team:
    source: dotenv
    config:
      path: .env

mounts:
  /slack:
    vfs: slack
    config:
      token:
        from: team
        key: SLACK_BOT_TOKEN
```

A relative `path` is read from the working directory of the process running
Mirage.

## Set environment variables

`env` sets the environment variables each session starts with. A string is a
literal value. A pointer fetches the value from a credential source, so it
never sits in the YAML:

```yaml workspace.yaml theme={null}
env:
  APP_ENV: production
  API_TOKEN:
    from: env
    readonly: true
```

Here `APP_ENV` is `production`, and `API_TOKEN` comes from the Mirage
process environment. Both are exported.

* A pointer defaults `key` to the variable's name.
* `readonly: true` stops the shell from assigning or unsetting the variable.
* Mirage fetches the value the first time a line uses the variable. With
  `fetch: eager`, it fetches the value before a line runs, even a line that
  does not use it.

## Declare a source

Name a source under `secrets` to give it settings, such as a second AWS
account or 1Password service account, and use that name in `from`. A pointer
inside a source's own config can read only from `env` or a dotenv file.


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