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

# VM Access

> Connect to, execute on, and transfer files to Vers VMs via SSH.

This guide covers how to interact with your Vers VMs — connecting via SSH, executing commands, and transferring files.

## Connection Architecture

Vers uses **SSH-over-TLS** for all VM connections. This means your SSH traffic is tunneled through a TLS connection on port 443.

```
Your Machine                    Vers Infrastructure
    │                                   │
    │  TLS Connection (port 443)        │
    ├──────────────────────────────────►│ {vm-id}.vm.vers.sh
    │                                   │
    │  SSH Handshake (over TLS)         │
    ├──────────────────────────────────►│
    │                                   │
    │  Encrypted SSH Session            │
    │◄─────────────────────────────────►│ Your VM
```

**Benefits:**

* Works through corporate firewalls (uses HTTPS port 443)
* Double encryption (TLS + SSH)
* No need to expose SSH ports
* Automatic routing to your specific VM

## Ways to Access Your VM

Vers provides three main ways to interact with VMs:

| Method            | Command        | Use Case                              |
| ----------------- | -------------- | ------------------------------------- |
| Interactive Shell | `vers connect` | Development, debugging, manual work   |
| Execute Commands  | `vers execute` | Automation, scripts, CI/CD            |
| File Transfer     | `vers copy`    | Upload/download files and directories |

## Interactive Shell (vers connect)

Open a full terminal session to your VM:

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
# Connect to current HEAD VM
vers connect

# Connect to specific VM
vers connect my-vm
```

Once connected, you have full root access:

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
root@vm-abc123:~# apt update
root@vm-abc123:~# npm install
root@vm-abc123:~# python app.py
```

### Terminal Features

* **Full PTY support**: Run vim, htop, or any interactive program
* **Terminal resizing**: Window size changes are reflected in the VM
* **Color support**: Full xterm-256color terminal
* **Multiple sessions**: Connect from multiple terminals simultaneously

### Exiting

Type `exit` or press `Ctrl+D` to disconnect. Your VM keeps running.

## Executing Commands (vers execute)

Run commands without an interactive session:

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
# Basic command
vers execute my-vm "ls -la"

# Run a script
vers execute my-vm "python /app/test.py"

# Chain commands
vers execute my-vm "cd /app && npm test"
```

Output is captured and returned to your terminal. Exit codes are preserved.

### Use Cases

**Running tests:**

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
vers execute test-vm "npm test"
echo "Exit code: $?"
```

**Automation scripts:**

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
#!/bin/bash
vers execute build-vm "make clean && make build"
vers execute test-vm "make test"
vers execute deploy-vm "make deploy"
```

**Checking status:**

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
vers execute my-vm "systemctl status postgresql"
```

## File Transfer (vers copy)

Transfer files between your machine and VMs using SCP:

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
# Upload file to VM
vers copy ./local-file.txt my-vm:/remote/path/

# Download file from VM
vers copy my-vm:/remote/file.txt ./local-path/

# Upload directory (recursive)
vers copy -r ./local-dir/ my-vm:/remote/dir/

# Download directory (recursive)
vers copy -r my-vm:/remote/dir/ ./local-dir/
```

### Path Detection

The CLI automatically detects transfer direction:

* Paths starting with `/` are treated as remote (VM) paths
* Other paths are treated as local paths
* If ambiguous, the CLI checks if a local file exists

### Examples

**Deploy code to VM:**

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
vers copy -r ./src/ my-vm:/app/src/
vers execute my-vm "cd /app && npm install"
```

**Download logs:**

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
vers copy my-vm:/var/log/app.log ./logs/
```

**Backup database:**

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
vers execute my-vm "pg_dump mydb > /tmp/backup.sql"
vers copy my-vm:/tmp/backup.sql ./backups/
```

## SSH Key Management

Vers automatically manages SSH keys for you:

1. **First connection**: CLI fetches your VM's private key from the API
2. **Local caching**: Key stored at `/tmp/vers-ssh-keys/{vm-id}.key`
3. **Secure permissions**: Keys have 0600 permissions (owner read/write only)
4. **Automatic authentication**: Key used for all subsequent connections

You never need to manually configure SSH keys.

### Key Storage Location

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
/tmp/vers-ssh-keys/
├── vm-abc123.key
├── vm-def456.key
└── vm-ghi789.key
```

Keys are cached per-VM and reused across connections.

## Connection Details

### Authentication

* **Username**: Always `root`
* **Method**: Public key authentication (no passwords)
* **Keys**: Per-VM keys, automatically managed

### Network

* **Host**: `{vm-id}.vm.vers.sh`
* **Port**: 443 (TLS)
* **Protocol**: SSH tunneled over TLS

### Keep-Alive

Connections use automatic keep-alive:

* Interval: 10 seconds
* Max missed: 6 (disconnects after \~60 seconds of no response)

## Working with HEAD

Many commands work with your current HEAD VM by default:

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
# Set HEAD to a VM
vers checkout my-vm

# These all operate on HEAD
vers connect          # Connect to HEAD
vers execute "ls"     # Execute on HEAD (requires VM ID)
vers copy file /path  # Copy to HEAD (requires VM ID)
```

Check your current HEAD:

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
vers checkout
Current HEAD: my-vm (State: Running)
```

## Multiple VM Workflows

### Parallel Development

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
# Terminal 1: Frontend development
vers connect frontend-vm
root@frontend:~# npm run dev

# Terminal 2: Backend development
vers connect backend-vm
root@backend:~# python manage.py runserver

# Terminal 3: Database work
vers connect db-vm
root@db:~# psql -U postgres
```

### Test Isolation

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
# Create test branches
vers branch --alias test-a
vers branch --alias test-b

# Run different tests in parallel
vers execute test-a "npm test -- --suite=unit"
vers execute test-b "npm test -- --suite=integration"
```

## Troubleshooting

### Connection Refused

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
# Error: connection refused
```

**Cause**: VM may still be starting up
**Solution**: Wait a few seconds and retry

### VM Not Running

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
# Error: VM is not running (current state: Paused)
```

**Solution**: Resume the VM with `vers resume`

### SSH Key Errors

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
# Error: failed to get or create SSH key
```

**Cause**: Authentication issue
**Solution**: Run `vers login` to re-authenticate

### Timeout

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
# Error: context deadline exceeded
```

**Cause**: Network issues or VM unavailable
**Solution**: Check your internet connection; verify VM exists with `vers status`

## Security Best Practices

1. **Keys are temporary**: SSH keys are stored in `/tmp` and may be cleared on reboot
2. **Per-VM isolation**: Each VM has its own unique key
3. **No key sharing**: Don't copy or share VM SSH keys
4. **Session logging**: Your commands run as root - be careful with destructive operations

## TypeScript SDK Access

For programmatic VM access, the Vers TypeScript SDK provides full SSH functionality through the `withSSH` wrapper.

### Setup

```typescript theme={"theme":{"light":"min-light","dark":"min-dark"}}
import Vers, { withSSH } from 'vers';

const client = new Vers({
  apiKey: process.env.VERS_API_KEY,
});

// Wrap the VM resource with SSH capabilities
const vm = withSSH(client.vm);
```

### Execute Commands

Run commands and capture output:

```typescript theme={"theme":{"light":"min-light","dark":"min-dark"}}
// Simple command execution
const result = await vm.execute(vmId, 'ls -la /app');
console.log(result.stdout);
console.log('Exit code:', result.exitCode);

// Check for errors
if (result.exitCode !== 0) {
  console.error('Command failed:', result.stderr);
}
```

### Streaming Output

For long-running commands, stream output in real-time:

```typescript theme={"theme":{"light":"min-light","dark":"min-dark"}}
// Stream output to console
const exitCode = await vm.executeStream(vmId, 'apt update && apt upgrade -y', {
  stdout: process.stdout,
  stderr: process.stderr,
});

console.log('Finished with exit code:', exitCode);
```

### File Transfer (SFTP)

Upload and download files:

```typescript theme={"theme":{"light":"min-light","dark":"min-dark"}}
// Upload a single file
await vm.upload(vmId, './local-file.txt', '/remote/path/file.txt');

// Download a single file
await vm.download(vmId, '/remote/path/file.txt', './local-file.txt');

// Upload directory recursively with progress
await vm.upload(vmId, './my-app', '/opt/my-app', {
  recursive: true,
  onProgress: (transferred, total, filename) => {
    console.log(`${filename}: ${transferred}/${total} bytes`);
  },
});

// Download directory recursively
await vm.download(vmId, '/var/log/myapp', './logs', {
  recursive: true,
});
```

### Interactive Shell Sessions

Open an interactive terminal:

```typescript theme={"theme":{"light":"min-light","dark":"min-dark"}}
const session = await vm.connect(vmId, {
  cols: process.stdout.columns || 80,
  rows: process.stdout.rows || 24,
});

// Pipe stdin/stdout for interactive use
process.stdin.pipe(session.stdin);
session.stdout.pipe(process.stdout);
session.stderr.pipe(process.stderr);

// Handle terminal resize
process.stdout.on('resize', () => {
  session.resize(process.stdout.columns, process.stdout.rows);
});

// Wait for session to end
await session.wait();
```

### Reusable Connections

For multiple operations, reuse a single SSH connection:

```typescript theme={"theme":{"light":"min-light","dark":"min-dark"}}
const ssh = await vm.getClient(vmId);

try {
  // Run multiple commands on the same connection
  await ssh.execute('apt update');
  await ssh.execute('apt install -y nginx');
  await ssh.upload('./nginx.conf', '/etc/nginx/nginx.conf');
  await ssh.execute('systemctl restart nginx');

  const status = await ssh.execute('systemctl status nginx');
  console.log(status.stdout);
} finally {
  // Always close when done
  ssh.close();
}
```

### Connection Options

All SSH methods accept options for timeouts, retries, and cancellation:

```typescript theme={"theme":{"light":"min-light","dark":"min-dark"}}
const result = await vm.execute(vmId, 'long-running-command', {
  timeout: 60000,           // Connection timeout (ms)
  keepAliveInterval: 10000, // Keep-alive interval (ms)
  keepAliveMaxCount: 6,     // Max missed keep-alives before disconnect
  retries: 3,               // Number of connection retries
  retryDelay: 1000,         // Delay between retries (ms)
  signal: abortController.signal, // AbortSignal for cancellation
});
```

### SSH Key Caching

The SDK caches SSH keys in memory for efficiency:

```typescript theme={"theme":{"light":"min-light","dark":"min-dark"}}
// Clear cache for a specific VM (e.g., after VM restart)
vm.clearKeyCache(vmId);

// Clear all cached keys
vm.clearKeyCache();
```

### Waiting for SSH Availability

After creating a VM, wait for SSH to become available:

```typescript theme={"theme":{"light":"min-light","dark":"min-dark"}}
async function waitForSSH(vm, vmId, maxAttempts = 30) {
  for (let i = 0; i < maxAttempts; i++) {
    try {
      const result = await vm.execute(vmId, 'echo ready');
      if (result.exitCode === 0) return;
    } catch {
      console.log(`Waiting for SSH... (${i + 1}/${maxAttempts})`);
      await new Promise(r => setTimeout(r, 2000));
    }
  }
  throw new Error('SSH not available after timeout');
}

// Usage
const { vm_id } = await client.vm.createRoot({ /* config */ });
await waitForSSH(vm, vm_id);
console.log('VM ready for SSH!');
```

### Complete Example

```typescript theme={"theme":{"light":"min-light","dark":"min-dark"}}
import Vers, { withSSH } from 'vers';

async function deployApp() {
  const client = new Vers();
  const vm = withSSH(client.vm);

  // Create a VM
  const { vm_id: vmId } = await client.vm.createRoot({
    vm_config: { mem_size_mib: 1024, vcpu_count: 2 },
  });

  try {
    // Wait for SSH
    await waitForSSH(vm, vmId);

    // Deploy application
    await vm.upload(vmId, './dist', '/opt/app', { recursive: true });

    // Install dependencies and start
    await vm.executeStream(vmId, 'cd /opt/app && npm install', {
      stdout: process.stdout,
    });

    await vm.execute(vmId, 'cd /opt/app && npm start &');

    console.log('Application deployed!');
  } finally {
    // Cleanup
    await client.vm.delete(vmId);
  }
}
```

## What's next

<CardGroup cols={2}>
  <Card title="vers connect" icon="terminal" href="/cli-reference/connect">
    Interactive SSH sessions — every flag and option.
  </Card>

  <Card title="vers execute" icon="bolt" href="/cli-reference/execute">
    Run one-off commands on a VM without an interactive session.
  </Card>

  <Card title="vers copy" icon="arrows-left-right" href="/cli-reference/copy">
    Transfer files between your machine and any VM.
  </Card>

  <Card title="Architecture" icon="sitemap" href="/architecture">
    How the SSH, exec, and proxy layers actually work.
  </Card>
</CardGroup>
