# SSH and IDEs

> Cordium documentation. Canonical page: <https://octelium.com/docs/cordium/latest/use/ssh>.

Cordium provides standard, secretless SSH access to running *Workspaces*. This enables everything that assumes real SSH: interactive shells, remote commands, local port forwarding, SOCKS5 proxying, SFTP/SCP file transfers, `rsync`, and remote development with VS Code, Zed, JetBrains IDEs, Cursor and any other SSH-based tool.

## How It Works

*Workspaces* do not use SSH keys or passwords at all. Every *Workspace* runs an embedded SSH server as part of its `octelium connect` process, and that server is only reachable through a dedicated Octelium `SSH` *Service* named `<REGION>-ssh.cordium` (i.e. `default-ssh.cordium` for the `default` *Region*) that is implemented by a Cordium-specific identity-aware proxy. When you connect, the proxy identifies you via your Octelium *Session* and only lets you in if:

- You own the target *Workspace*, which is identified by the SSH username (i.e. the *Workspace* name).
- The *Workspace* is in the `PREPARING` or `RUNNING` state, in the same *Region* as the proxy.
- The *Workspace*'s *Space* does not disable SSH access via its `authorization.disableSSH` field (read more [here](https://octelium.com/docs/cordium/latest/workspaces/spaces.md#disabling-ssh)).

Since the SSH *Service* is a private Octelium *Service*, you need to be connected to the *Cluster* via `octelium connect` before using SSH (read more about connecting to the *Cluster* [here](https://octelium.com/docs/octelium/latest/user/cli/connect.md)):

```bash
export OCTELIUM_DOMAIN=<DOMAIN>
# Connect in detached mode
octelium connect -d
# OR in the foreground
sudo -E octelium connect
```

> **Note:**
>
> *Workspaces* are already connected to the *Cluster*. This means that you can use `cordium ssh` and `cordium cp` from inside one of your *Workspaces* to reach your other *Workspaces* without any additional setup (read more [here](https://octelium.com/docs/cordium/latest/examples/workflows/ssh-cp.md)).

Every SSH connection counts as activity, which keeps the *Workspace* from being stopped by its inactivity timeout, and every SSH session is visible in the *Cluster*'s OpenTelemetry access logs.

## cordium ssh

`cordium ssh` opens an interactive shell or runs a remote command via an SSH client that is embedded in the `cordium` CLI. A remote command and its arguments can be passed after a double-dash (`--`). Here are some examples:

```bash
# Open an interactive shell
cordium ssh abc

# Run a single remote command and propagate its exit code
cordium ssh abc -- uptime
cordium ssh abc -- sh -c "ps aux | grep python"

# Forward the local port 5432 to the Workspace's PostgreSQL
cordium ssh abc -L 5432:localhost:5432

# Several port forwards without an interactive shell
cordium ssh abc -N \
  -L 5432:localhost:5432 \
  -L 6379:localhost:6379 \
  -L 8080:localhost:8080

# Route traffic through the Workspace's network via a SOCKS5 proxy
cordium ssh abc -D 1080 -N
curl --socks5-hostname localhost:1080 https://checkip.amazonaws.com
```

| Flag                 | Description                                                                      |
| -------------------- | -------------------------------------------------------------------------------- |
| `--local`, `-L`      | A local port forward in the `[BIND_ADDR:]PORT:HOST:HOSTPORT` format. Repeatable. |
| `--dynamic`, `-D`    | A dynamic SOCKS5 forward in the `[BIND_ADDR:]PORT` format. Repeatable.           |
| `--no-command`, `-N` | Do not execute a remote command, which is useful for port forwarding only.       |
| `--print-config`     | Print an OpenSSH config block for the *Workspace* and exit.                      |

## OpenSSH Config

To use any standard SSH-based tool, generate an OpenSSH config block for the *Workspace* and append it to your `~/.ssh/config`:

```bash
cordium ssh abc --print-config >> ~/.ssh/config
```

The generated block defines the `cordium-<WORKSPACE>` host alias and looks roughly as follows:

```text
# Cordium Workspace: abc
# Add this block to ~/.ssh/config
# Generated by: cordium ssh abc --print-config
Host cordium-abc
    HostName default-ssh.cordium.local.example.com
    User abc
    Port 22
    BatchMode yes
    PreferredAuthentications none
    PubkeyAuthentication no
    PasswordAuthentication no
    ...
```

You can now use the standard OpenSSH tooling:

```bash
ssh cordium-abc
ssh cordium-abc -- go test ./...
ssh -N -L 3000:localhost:3000 cordium-abc
```

> **Note:**
>
> The `HostName` is the private hostname of the SSH *Service*, which only resolves while you are connected to the *Cluster*. If you connect via the rootless mode and publish the SSH *Service* to a local port instead (e.g. `octelium connect -p default-ssh.cordium:2222`), re-generate the block after connecting so that it points to `localhost:2222`.

## Copying Files

`cordium cp` copies files and directories between your local filesystem and a *Workspace*, or between two *Workspaces*, using SFTP. *Workspace* paths are specified as `<WORKSPACE>:<PATH>` and local paths as plain filesystem paths. Use `-r` to copy directories recursively:

```bash
# Copy a local file to a Workspace
cordium cp ./config.json abc:/workspace/repo/config.json

# Copy a file from a Workspace to the local machine
cordium cp abc:/workspace/repo/coverage.out ./coverage.out

# Copy directories recursively
cordium cp -r ./fixtures/ abc:/workspace/repo/fixtures/
cordium cp -r abc:/workspace/repo/dist/ ./dist/

# Copy between two Workspaces
cordium cp abc:/workspace/repo/model.pt def:/workspace/models/model.pt
cordium cp -r abc:/workspace/data/ def:/workspace/data/
```

Once your OpenSSH config is set up, you can also use `scp`, `sftp` and `rsync`:

```bash
rsync -avz --delete ./src/ cordium-abc:/workspace/repo/src/
scp cordium-abc:/workspace/repo/report.html ./report.html
```

## Remote Development with IDEs

Any IDE that supports remote development over SSH can work directly inside a *Workspace* once its OpenSSH config block is added to your `~/.ssh/config`:

**VS Code, Cursor and other VS Code-based editors** with the Remote - SSH extension:

```bash
code --remote ssh-remote+cordium-abc /workspace/repo
```

For VS Code, `cordium code` does the same in one step without any SSH config, provided that the `code` command is installed and that you are connected via `octelium connect`. It opens `/workspace/repo` by default, which you can change via the `--dir` flag:

```bash
cordium code abc
cordium code abc --dir /workspace/additional-repos/shared-libs
```

**Zed**:

```bash
zed ssh://cordium-abc/workspace/repo
```

**JetBrains IDEs** (e.g. GoLand, IntelliJ IDEA, PyCharm) via JetBrains Gateway or the IDE's remote development: choose **SSH**, select the `cordium-abc` host from your SSH config and open `/workspace/repo`.

> **Note:**
>
> Persistent *Workspaces* are a great fit for IDEs since the IDE server components installed inside the *Workspace* (e.g. `~/.vscode-server`) survive restarts. You can also pre-install them in a *Template* image or via [dotfiles](https://octelium.com/docs/cordium/latest/workspaces/user-config.md#dotfiles) to speed up the first connection.

## Disabling SSH

*Space* owners can deny SSH access, and therefore `cordium ssh`, `cordium cp` and IDE access, to all the *Workspaces* of a *Space* via the *Space*'s `authorization.disableSSH` field (read more [here](https://octelium.com/docs/cordium/latest/workspaces/spaces.md#disabling-ssh)). Terminals and `cordium exec` still work via the Cordium API. Additionally, since the SSH endpoint is an Octelium *Service*, access to it can be further restricted by Octelium *Policies* (read more [here](https://octelium.com/docs/cordium/latest/management/access-control.md)).
