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

# SSH Access

> Connect to a sandbox over SSH from your terminal or external tools.

Sandboxes accept standard SSH connections. The Boat CLI handles the usual setup for you, including the SSH key it uses to connect.

## Connect with the CLI

Use `boat ssh` from your local machine for an interactive shell. For non-interactive commands, use the same sandbox work access through the SDK, API, curl, or CLI:

<CodeGroup>
  ```bash CLI theme={null}
  boat ssh bx_f7k2q9hd
  boat ssh bx_f7k2q9hd "cd /home/user/my-repo && npm test"
  boat ssh bx_f7k2q9hd -- bash -lc "cd /home/user/my-repo && npm test"
  boat ssh bx_f7k2q9hd -- bash -s < ./setup.sh
  ```

  ```bash curl theme={null}
  curl -sS -X POST "$BOAT_API_BASE/sandboxes/bx_f7k2q9hd/commands" \
    -H "Authorization: Bearer $BOAT_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"command":"npm test","cwd":"my-repo","timeoutSeconds":60}'
  ```

  ```ts TypeScript theme={null}
  const result = await sandbox.command({
    sandboxId: "bx_f7k2q9hd",
    commandRequest: { command: "npm test", cwd: "my-repo", timeoutSeconds: 60 },
  });
  console.log(result.stdout);
  ```

  ```python Python theme={null}
  from boat_sdk.models.command_request import CommandRequest

  result = sandbox.command(
      "bx_f7k2q9hd",
      CommandRequest(command="npm test", cwd="my-repo", timeout_seconds=60),
  )
  print(result.stdout)
  ```
</CodeGroup>

The CLI manages its SSH key at:

```text theme={null}
~/.ssh/ascii_box_ed25519
```

If the key is missing, the CLI creates or refreshes it and authorizes it on the sandbox before connecting.

<Note>
  SSH is available only when the sandbox's machine is running. If the sandbox is archived or still provisioning, resume it or wait until it is ready. In the first seconds of a resume, SSH may answer with a retryable `boat_restoring` error; retry a moment later.
</Note>

## Use another SSH client

If you want to connect from an external SSH client, first authorize a public key and inspect the sandbox for its IP address:

<CodeGroup>
  ```bash CLI theme={null}
  boat info bx_f7k2q9hd
  ssh -i ~/.ssh/ascii_box_ed25519 user@<sandbox-ip>
  ```

  ```bash curl theme={null}
  curl -sS -X POST "$BOAT_API_BASE/sandboxes/bx_f7k2q9hd/sshkey" \
    -H "Authorization: Bearer $BOAT_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"key":"ssh-ed25519 AAAAC3Nza... you@example.com"}'

  curl -sS "$BOAT_API_BASE/sandboxes/bx_f7k2q9hd" \
    -H "Authorization: Bearer $BOAT_API_KEY"
  ```

  ```ts TypeScript theme={null}
  await sandbox.sshKey({
    sandboxId: "bx_f7k2q9hd",
    sshKeyRequest: { key: "ssh-ed25519 AAAAC3Nza... you@example.com" },
  });

  const info = await sandbox.get({ sandboxId: "bx_f7k2q9hd" });
  console.log(info.sandbox.ip);
  ```

  ```python Python theme={null}
  from boat_sdk.models.ssh_key_request import SshKeyRequest

  sandbox.ssh_key("bx_f7k2q9hd", SshKeyRequest(key="ssh-ed25519 AAAAC3Nza... you@example.com"))
  info = sandbox.get("bx_f7k2q9hd")
  print(info.sandbox.ip)
  ```
</CodeGroup>

## Copy files

Use `boat scp` with the sandbox ID as the remote host, or use file/artifact API calls when you are building an integration:

<CodeGroup>
  ```bash CLI theme={null}
  boat scp ./local-file.txt bx_f7k2q9hd:/home/user/
  boat scp bx_f7k2q9hd:/home/user/output.zip ./output.zip
  ```

  ```bash curl theme={null}
  curl -sS -X PUT "$BOAT_API_BASE/sandboxes/bx_f7k2q9hd/files" \
    -H "Authorization: Bearer $BOAT_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"path":"notes/input.txt","content":"hello\n","encoding":"utf8"}'

  curl -L -o output.zip "$BOAT_API_BASE/sandboxes/bx_f7k2q9hd/artifacts?path=output.zip" \
    -H "Authorization: Bearer $BOAT_API_KEY"
  ```

  ```ts TypeScript theme={null}
  await sandbox.writeFile({
    sandboxId: "bx_f7k2q9hd",
    fileWriteRequest: { path: "notes/input.txt", content: "hello\n", encoding: "utf8" },
  });

  const artifact = await sandbox.artifact({ sandboxId: "bx_f7k2q9hd", path: "output.zip" });
  ```

  ```python Python theme={null}
  from boat_sdk.models.file_write_request import FileWriteRequest

  sandbox.write_file("bx_f7k2q9hd", FileWriteRequest(
      path="notes/input.txt",
      content="hello\n",
      encoding="utf8",
  ))
  artifact = sandbox.artifact("bx_f7k2q9hd", "output.zip")
  ```
</CodeGroup>

File read/write paths may be relative (resolved from the sandbox work directory, `/home/user`) or absolute; an absolute path must resolve under `/home/user` or `/tmp`, anything else is refused with a 400 `invalid_path` error naming the allowed roots.

See [CLI Reference](/cli-reference#boat-scp) for all `scp` options.

## Related

* [Environments](/environments)
* [Long-Running Tasks](/long-running-tasks)
* [Desktop Streaming](/desktop-streaming)
* [Hosting](/hosting)
