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

# Environment & secrets

> How to handle tokens, paths, and per-machine values without checking them in.

`gaal.yaml` is meant to live in source control. Anything sensitive needs to stay out of it. This page covers the patterns that work cleanly with gaal's design.

## gaal does not expand variables

This is the most important rule. gaal writes `gaal.yaml` values into target files **literally**, no `${VAR}` substitution, no `$HOME` expansion (other than the special `~/` prefix on file paths). If you write:

```yaml theme={"theme":"nord"}
mcps:
  - name: github
    agents: [claude-code]
    global: true
    inline:
      command: npx
      args: ["-y", "@modelcontextprotocol/server-github"]
      env:
        GITHUB_PERSONAL_ACCESS_TOKEN: ${GITHUB_TOKEN}
```

…then the literal string `${GITHUB_TOKEN}` lands in the agent's MCP config. The **agent process** that launches the MCP server is responsible for resolving `${GITHUB_TOKEN}` from the environment.

For Claude Desktop, Cursor, Codex, and most agents, this means the variable is read from the environment of the agent process when it spawns the server. Set the variable in the way that environment expects, your shell profile, a `launchctl` plist, a systemd unit file, etc.

## Where to put the secret

The right place is **outside `gaal.yaml`** in a file that already isn't in source control.

### Shell profile

```bash theme={"theme":"nord"}
# ~/.config/fish/conf.d/secrets.fish
set -gx GITHUB_TOKEN ghp_xxx...
```

```bash theme={"theme":"nord"}
# ~/.zshrc
export GITHUB_TOKEN=ghp_xxx...
```

The agent inherits these when it launches the MCP server.

### A secret manager

If you use 1Password, Bitwarden, `pass`, or similar, source the value into your shell profile from there. gaal doesn't need to know.

### Per-host overrides via the user-scope config

For host-specific *non-secret* values (paths, project roots), use the user-scope file:

```yaml theme={"theme":"nord"}
# ~/.config/gaal/config.yaml
mcps:
  - name: filesystem
    agents: [claude-code]
    global: true
    inline:
      command: uvx
      args: [mcp-server-filesystem, /Users/me/work]
```

The workspace `gaal.yaml` can then declare cross-machine defaults; the user file overrides on this specific host. See [Scopes](/concepts/scopes) for merge rules.

## Path expansion

gaal **does** expand the `~/` prefix on the registry-resolved MCP target paths to the user's home directory. Everywhere else in the file (including inside `args:`), paths are written through verbatim. If your MCP server expects `$HOME` expansion in its arguments, that's the agent's job, not gaal's.

## What never to put in gaal.yaml

* API tokens, passwords, OAuth client secrets.
* Private SSH keys.
* AWS / GCP / Azure credentials.
* Any value that would compromise something if a teammate read it.

## Related

<CardGroup cols={2}>
  <Card title="Configure MCP servers" icon="plug" href="/configure/mcp-servers" />

  <Card title="Scopes" icon="layer-group" href="/concepts/scopes" />

  <Card title="Sandbox mode" icon="box" href="/workflows/sandbox-mode" />
</CardGroup>
