> ## 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.

# SSH

> Reach a workspace with ssh, sftp and scp.

The Mirage server can listen for SSH next to HTTP. The SSH username names the workspace. Every line runs in Mirage's shell, and every file operation goes through the workspace's mounts.

```bash theme={null}
ssh -p 2222 demo@127.0.0.1                     # a shell in workspace "demo"
ssh -p 2222 demo@127.0.0.1 'ls /data | wc -l'  # one line
ssh -T -p 2222 demo@127.0.0.1 < setup.sh       # a script
sftp -P 2222 demo@127.0.0.1                    # file transfer
scp -P 2222 notes.txt demo@127.0.0.1:/data/    # copy in
```

Only public keys log in: no passwords. Nothing is forwarded: no ports, no agent, no X11.

## Turn it on

<Steps>
  <Step title="Install the SSH library">
    <CodeGroup>
      ```bash Python theme={null}
      pip install 'mirage-ai[ssh]'
      ```

      ```bash TypeScript theme={null}
      npm install -g ssh2
      ```
    </CodeGroup>
  </Step>

  <Step title="Pick a port">
    ```bash theme={null}
    mirage config set ssh_port 2222
    ```

    SSH stays off until a port is set.
  </Step>

  <Step title="Authorize your key">
    ```bash theme={null}
    mkdir -p ~/.mirage/ssh
    cat ~/.ssh/id_ed25519.pub >> ~/.mirage/ssh/authorized_keys
    ```

    The file is read on every login, so adding or removing a key needs no restart.
  </Step>

  <Step title="Restart and connect">
    ```bash theme={null}
    mirage daemon restart
    mirage workspace create workspace.yaml --id demo
    ssh -p 2222 demo@127.0.0.1
    ```

    The server makes its host key on first start and keeps it, so your `known_hosts` entry stays valid.
  </Step>
</Steps>

## Settings

An environment variable wins over `~/.mirage/config.toml`, which wins over the default.

| Key | Env var | Default |
| - | - | - |
| `ssh_port` | `MIRAGE_SSH_PORT` | unset (off) |
| `ssh_host` | `MIRAGE_SSH_HOST` | `127.0.0.1` |
| `ssh_host_key_file` | `MIRAGE_SSH_HOST_KEY_FILE` | `~/.mirage/ssh/host_ed25519_key` |
| `ssh_authorized_keys` | `MIRAGE_SSH_AUTHORIZED_KEYS` | `~/.mirage/ssh/authorized_keys` |

## Bind a key to a profile

The `mirage-profile` option runs every login of that key under a [profile](/home/permissions). A key without it gets the workspace's default.

```text ~/.mirage/ssh/authorized_keys theme={null}
mirage-profile="guarded" ssh-ed25519 AAAA... agent-a
ssh-ed25519 AAAA... me
```

The server reads the option, not the client, so a key cannot pick looser rules. Give each agent's sandbox its own key, bound to that agent's profile.

## Sessions

Each channel (one `ssh`, `sftp` or `scp` run) gets a fresh session, closed when it ends, so a `cd` or `export` never leaks between them. The session's profile and the mount modes apply as in any shell: a read-only mount refuses `sftp put`. Ctrl-C cancels the running line and sets `$?` to 130, a dropped connection cancels it too, and `ssh host cmd` exits with the line's status.


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