# Tutorial: a connection through SSH, step by step

The example project `apps/hello-ssh` reaches a Redis server **that only an SSH server (a bastion) can reach**: `ssh -W host:port`, the connection's stream is the
stream to Redis. On the computer it is the system's `ssh`; in the browser, **the real OpenSSH client compiled to WebAssembly**, linked to your PHP by a pipe.

## 1. Create the application

```bash
bin/jumphp gui:new mon-ssh "Mon SSH" && bin/jumphp install mon-ssh
```

## 2. The window

`forms/MainForm.form.yaml` (copy `apps/hello-ssh/forms/MainForm.form.yaml`): the SSH server (`sshHost`, `sshPort`, `sshUser`), a « Choose a key file… » button (`chooseKey`), the key's
passphrase (`passphrase`), the target as seen from the bastion (`host`, `port`), a `ping` button, `status`, `result`.

## 3. The key: the file picker

A browser has no `~/.ssh` folder: the file is asked from the person. `FilePicker::open()` works on the computer AND in the browser (call it from a click); the chosen
file arrives at a path that you read and then delete. It is kept in the application's **keyring** (`$this->engine->sshKeyring()`: the role of an agent, which the browser does not have).

```php
public function onChooseKey(Button $sender): void
{
    FilePicker::open(['title' => 'SSH private key'])->then(function (?string $file): void {
        if ($file === null) { return; }                                   // annulé
        $text = (string)file_get_contents($file);
        unlink($file);
        $this->engine->sshKeyring()->add(basename($file), $text, $this->passphrase->text);   // nom, clé, phrase secrète
        $this->keyState->text = 'Keys: ' . implode(', ', $this->engine->sshKeyring()->names());
    });
}
```

The key stays in the application's storage (`var/ssh_keyring.json`: IndexedDB in the browser, the data folder on the computer) and is **never sent to the server**: it goes into the
SSH client's memory. Each connection offers all the keyring's keys, like an agent.

## 4. Connect

```php
$options = ['sshHost' => 'bastion.exemple.com', 'sshPort' => 22, 'sshUser' => 'moi', 'sshKeys' => $this->engine->sshKeyring()->all()];
$connection = $this->engine->ssh()->connect('127.0.0.1', 6379, $options);      // 127.0.0.1:6379 vu depuis le bastion
$connection->on('open', fn () => $connection->write("PING\r\n"));
$connection->on('data', fn (string $bytes) => $this->result->text = trim($bytes));
$connection->on('error', fn (string $m) => $this->result->text = $m);          // « Permission denied (publickey) », « Could not resolve hostname »...
$connection->on('knownHosts', fn (string $text) => $this->engine->storage->writeJson('known_hosts.json', ['text' => $text]));   // l'empreinte du serveur, gardée
```

The same events as with `tcp()`. For code that takes a `Tcp` (like the MySQL client of `apps/jumphpsql`): `$this->engine->ssh()->via($options)` has the same `connect($host, $port, $timeout)`.
The options: `docs/ssh-wasm.md`.

## 5. Test

```bash
bin/jumphp test mon-ssh      # le trousseau, les champs manquants, un ssh qui n'atteint pas son serveur (le message dit pourquoi)
bin/jumphp shot mon-ssh
bin/jumphp run mon-ssh       # sur l'ordinateur : le ssh du système (votre agent et ~/.ssh marchent aussi)
```

## 6. The browser: two rights

```bash
bin/jumphp build mon-ssh && bin/jumphp serve mon-ssh
```

In the browser, two stages go through jumphp.net's **TCP bridge**, which only accepts approved applications: the SSH client (WebAssembly) connects to the bastion (port 22 or another) → the
**SSH** right is needed. (A SQL client that talks to a database directly needs the **TCP** right.) Studio → the project → **Accès SSH** → « Demander l'accès SSH »; the administrator approves it for the published commit.

The SSH right is broader (any port of a public address, never a private address): the administrator only grants it to code they have looked at. Statistics, log, cutting and blocking:
Administration → Pont TCP.

## 7. Check in the browser

Project page on jumphp.net: choose your key (your computer's file picker opens), enter the bastion and the target, click « Send PING through SSH »: `The server answered: +PONG`.
The first seconds show « The SSH session is starting… »: the SSH client starts in a Web Worker (a 3.8 MB file loaded on demand).

How it works (two wasm linked by a pipe), the security and the compilation of OpenSSH: `docs/ssh-wasm.md`. The first tutorial: `docs/tuto-tcp.md`.
