# Working Across Workspaces

> Cordium documentation. Canonical page: <https://octelium.com/docs/cordium/latest/examples/workflows/ssh-cp>.

*Workspaces* are not isolated islands. Since every *Workspace* is already connected to the *Cluster* with your identity, the `cordium ssh` and `cordium cp` commands work from inside your *Workspaces* exactly as they do from your laptop, which lets your *Workspaces* exchange files and reach each other's ports without exposing anything to the network and without any SSH key (read more about SSH access [here](https://octelium.com/docs/cordium/latest/use/ssh.md)). This example shows the most common patterns between two of your *Workspaces*: `api`, a backend development *Workspace*, and `web`, a frontend development *Workspace*.

> **Note:**
>
> SSH access to a *Workspace* is only granted to its owner, which means that these patterns work between the *Workspaces* that you own. To share an application with your teammates, use application sharing instead (read more [here](https://octelium.com/docs/cordium/latest/workspaces/applications.md#sharing-applications)). To serve a *Workspace*'s port as a full-fledged Octelium *Service* to other *Users* and *Workspaces*, read [this example](https://octelium.com/docs/cordium/latest/examples/security/serving.md).

## Reaching Another Workspace's Ports

Suppose that the backend API runs on port `8080` and PostgreSQL on port `5432` inside the `api` *Workspace*, and that the frontend dev server in the `web` *Workspace* needs to reach the API. From a terminal of the `web` *Workspace*, forward both ports locally:

```bash
cordium ssh <API_WORKSPACE> -N -L 8080:localhost:8080 -L 5432:localhost:5432 &
```

The frontend can now use `http://localhost:8080` as its API URL, and you can inspect the backend's database with `psql -h localhost` from the `web` *Workspace*. To make the forwarding start automatically whenever the `web` *Workspace* starts, add it as a background `POST_START` task:

```yaml
spec:
  vars:
    - name: API_WORKSPACE
      value: k3x9
  runtime:
    tasks:
      - name: forward-api
        type: POST_START
        isBackground: true
        run: |
          until cordium ssh ${{ vars.API_WORKSPACE }} -- true; do sleep 5; done
          exec cordium ssh ${{ vars.API_WORKSPACE }} -N -L 8080:localhost:8080
```

## Copying Files Between Workspaces

`cordium cp` copies files and directories between any two of your *Workspaces*, from your laptop or from inside any of them. Here is an example that copies a production-like database dump prepared in the `api` *Workspace* to the `web` *Workspace* and restores it there:

```bash
cordium ssh <API_WORKSPACE> -- pg_dump -Fc -f /tmp/ledger.dump ledger
cordium cp <API_WORKSPACE>:/tmp/ledger.dump <WEB_WORKSPACE>:/tmp/ledger.dump
cordium ssh <WEB_WORKSPACE> -- pg_restore --clean -d ledger /tmp/ledger.dump
```

For large or frequently synchronized directories, generate an OpenSSH config block and use `rsync`, which only transfers the differences:

```bash
cordium ssh <API_WORKSPACE> --print-config >> ~/.ssh/config
rsync -az --delete cordium-<API_WORKSPACE>:/workspace/repo/gen/openapi/ ./src/api/generated/
```

> **Note:**
>
> For artifacts that several *Workspaces* need, or that must outlive the *Workspaces* that produce them, mount a shared *Volume* instead (read more [here](https://octelium.com/docs/cordium/latest/examples/workflows/volumes.md)).

## Running Commands Across Workspaces

`cordium ssh` runs commands remotely and returns their exit codes, which makes it easy to script across several *Workspaces*. Here is an example that pulls the latest changes and runs the unit tests in all your running *Workspaces* of the `api-dev` *Template*:

```bash
for ws in $(cordium get ws --template api-dev.payments.cordium -o json | jq -r '.items[] | select(.status.state == "RUNNING") | .metadata.name'); do
  echo "== $ws"
  cordium ssh "$ws" -- sh -c 'cd /workspace/repo && git pull -q && go test ./...' || echo "FAILED: $ws"
done
```

> **Note:**
>
> `cordium exec` runs commands via the Cordium API instead of SSH, which is useful when SSH is disabled for a *Space* or when you do not want to run `octelium connect` on your laptop. Use `--no-stdin` when you do not pipe any input to it (read more [here](https://octelium.com/docs/cordium/latest/use/cli.md)).
