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

> Run captured programs on any machine you can reach over SSH.

The `ssh` machine provider reaches a machine running `sshd`, and the `machine`
runtime runs captured programs on it. Anything you can
already `ssh` into works, with nothing to install on it. You run the
machine; Mirage only runs programs on it. Python SDK and Node.

## Installation

> [tab] Python
>
> ```bash theme={null}
> pip install 'mirage-ai[ssh]'
> ```

> [tab] TypeScript
>
> ```bash theme={null}
> pnpm add ssh2
> ```

## Usage

`captures` defaults to [`@external`](https://docs.mirage.strukto.ai/home/setup/runtime/overview#captures):
every program Mirage does not have itself. Name `@python` to run
Python on the machine too.

> [tab] Python
>
> ```python theme={null}
> from mirage import RAMVFS, MountMode, Workspace
> from mirage.runtime.machine import MachineRuntime
>
> ws = Workspace(
>     {"/data": RAMVFS()},
>     mode=MountMode.EXEC,
>     machines={
>         "box": {
>             "provider": "ssh",
>             "host": "mybox",
>             "username": "deploy",
>             "identity_file": "~/.ssh/id_ed25519",
>         }
>     },
>     runtimes=[MachineRuntime(["@python", "@external"], machine="box")],
> )
> await ws.shell("python3 -c 'print(1 + 1)'")
> ```

> [tab] TypeScript
>
> ```typescript theme={null}
> import { MountMode, MachineRuntime, RAMVFS, Workspace } from '@struktoai/mirage-node'
>
> const ws = new Workspace(
>   { '/data': new RAMVFS() },
>   {
>     mode: MountMode.EXEC,
>     machines: {
>       box: {
>         provider: 'ssh',
>         host: 'mybox',
>         username: 'deploy',
>         identityFile: '~/.ssh/id_ed25519',
>       },
>     },
>     runtimes: [new MachineRuntime({ captures: ['@python', '@external'], machine: 'box' })],
>   },
> )
> await ws.shell("python3 -c 'print(1 + 1)'")
> ```

> [tab] YAML
>
> ```yaml theme={null}
> mode: exec
> machines:
>   box:
>     provider: ssh
>     host: mybox
>     username: deploy
>     identity_file: ~/.ssh/id_ed25519
> runtimes:
>   - name: machine
>     captures: ["@python", "@external"]
>     machine: box
> mounts:
>   /data:
>     vfs: ram
> ```

## Connecting the workspace

A program on the machine opens workspace files at the same paths as in
Mirage, so the machine must see the mounts there. With `mount_type: ssh`,
Mirage serves every mount over the runtime's own connection and the machine
mounts each with `sshfs`, as for
[Docker](https://docs.mirage.strukto.ai/home/setup/machine/docker.md#connecting-the-workspace). The
machine needs `sshfs`, and passwordless `sudo` for a mountpoint its user
cannot create (`/data`).

Without `mount_type`, two ways:

**Files already on the machine.** Mount the machine's directory with the
[ssh VFS](https://docs.mirage.strukto.ai/python/vfs/ssh.md) at a prefix equal to its path there. The same
path then names the same file on both sides, with nothing to install:

```yaml theme={null}
mounts:
  /home/user/proj:
    vfs: ssh
    config:
      host: mybox
      root: /home/user/proj
machines:
  box:
    provider: ssh
    host: mybox
runtimes:
  - name: machine
    captures: ["@python", "@external"]
    machine: box
```

**Any other mount** (S3, Slack and so on). Install Mirage and `fuse3` on the
machine. Write the same mounts at the same paths into `sandbox.yaml`, and
start Mirage with it there:

```yaml theme={null}
mounts:
  /data:
    vfs: s3
    config:
      bucket: my-bucket
    backend: fuse
    mountpoint: /data
```

```bash theme={null}
ssh mybox mirage workspace create sandbox.yaml
```

Nothing checks that the paths match; a mismatch shows only when a program
misses a file. For a friendlier name, put or link the directory at that path
on the machine: Mirage never rewrites paths. A program on the machine opens its files directly, outside
this workspace's mount modes and permissions.

## Runtime behavior

| Capability | On the machine |
| - | - |
| Command | The captured program and its words, as typed |
| Piped input | stdin |
| Environment | The session's, over the config `env` |
| Working directory | The shell's, unchanged |
| Workspace files | The machine's own paths; see [above](#connecting-the-workspace) |
| Disk and network | The machine's own |

## Notes

* `host` is also the address unless `hostname` overrides it; `port`,
  `username`, `identity_file`, `private_key` and `timeout` are optional.
  Authentication is a key or an agent, never a password.
* `private_key` takes the key's own text rather than a path, so a
  [credential pointer](https://docs.mirage.strukto.ai/home/authentication.md#pointer-fields) can read it
  from a secret store.
* The machine's login shell must be POSIX-compatible. Commands use the
  session's working directory and environment.

> [!WARNING]
>
> Host keys are not verified, so treat the network path to the machine as
> trusted.


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