Skip to main content

Running Linkspan by hand

This tutorial runs Linkspan by hand inside a Slurm job. You start a Jupyter server and an SSH server through its HTTP API, reach both, stop Linkspan, and then carry the API off the node over a Dev Tunnel. It takes about fifteen minutes, most of it the first Jupyter environment build.

Airavata and CS Bridge normally launch Linkspan; doing it by hand shows whether a cluster can run sessions and where one fails.

Prerequisites​

NeedFor
A shell on a Linux compute node in a job, such as srun --time=01:00:00 --pty bashEvery step
A writable home directoryEvery download and build, all under ~/.cybershuttle/
curl, and HTTPS from the node to the download hosts belowDownloading Linkspan and building the Jupyter environment
The devtunnel CLI signed in on your laptop, and HTTPS from the node to the Dev Tunnels hosts belowReach Linkspan from your laptop
HostsNeeded by
github.com, release-assets.githubusercontent.comThe Linkspan download
astral.sh, pypi.org, files.pythonhosted.orgThe Jupyter environment
tunnelsassetsprod.blob.core.windows.net, devtunnels.ms, rel.tunnels.api.visualstudio.comThe Dev Tunnel

Start servers in a job​

Run every command in this part in the job's shell on the compute node.

1. Download Linkspan​

curl -fsSL https://github.com/cyber-shuttle/linkspan/releases/latest/download/linkspan_Linux_x86_64.tar.gz |
tar -xz linkspan
./linkspan --version

The last command prints the version, for example 0.22.1. On an arm64 node use linkspan_Linux_arm64.tar.gz.

2. Start Linkspan​

./linkspan --port 8080 > linkspan.log 2>&1 &
curl -s http://127.0.0.1:8080/api/v1/health

The reply is {"status":"ok"}, and linkspan.log has a line ending listening on 127.0.0.1:8080. Port 8080 is now the control port, the loopback port that serves the whole API. The API asks for no credential; every user on the node can call it.

If curl cannot connect and the log says fatal: tasks: listen: with address already in use, another process holds the port. Start Linkspan with another --port, or with --port 0 and read the chosen port from the log. Use that port in the commands below.

3. Start a Jupyter server​

curl -s -X POST http://127.0.0.1:8080/api/v1/jupyter/sessions \
-H 'Content-Type: application/json' -d "{\"root_dir\": \"$HOME\"}"

The reply is the server object, in state starting:

{"addr":"127.0.0.1:41235","error":"","id":"j-41235","root_dir":"/home/me","state":"starting","token":"Xq3…"}

Linkspan then builds the Python environment under ~/.cybershuttle/; the first build takes a few minutes, and linkspan.log shows the uv output. Poll the list until state is running:

curl -s http://127.0.0.1:8080/api/v1/jupyter/sessions

A state of failed carries the reason in error, most often a download host the node cannot reach.

Once it is running, query the server itself with the addr and token from the reply:

curl -s -H 'Authorization: token <token>' http://127.0.0.1:41235/api/status

Jupyter Server answers with a JSON object carrying started and kernels.

4. Start an SSH server​

Each SSH server admits exactly one public key, named when it is started. Make a throwaway key and ask Linkspan for a server that admits it:

ssh-keygen -q -t ed25519 -N '' -f ~/linkspan-demo
curl -s -X POST http://127.0.0.1:8080/api/v1/vscode/sessions \
-H 'Content-Type: application/json' \
-d "{\"authorized_key\": \"$(cat ~/linkspan-demo.pub)\"}"

The reply names the SSH server's loopback port, which already accepts connections:

{"bind_port":41237,"id":"s-41237"}

Run a command through it:

ssh -i ~/linkspan-demo -p 41237 -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no 127.0.0.1 hostname

It prints the compute node's host name. The options skip recording the host key, which is new on every start. The server refuses PTYs, so always pass a command.

5. Read the job's usage​

curl -s http://127.0.0.1:8080/api/v1/usage

The reply is the latest five-second sample of the job's cgroup, for example {"memBytes":2147483648,"cpuUsageUsec":295339339}, with gpus added on a node with nvidia-smi.

6. Stop Linkspan​

kill %1
tail -n 2 linkspan.log

The log ends with signal received, stopping and stopped, and every server Linkspan started has stopped. Delete the throwaway key with rm ~/linkspan-demo ~/linkspan-demo.pub.

Reach Linkspan from your laptop​

Compute nodes accept no connections from off the cluster, so Linkspan dials out over a transport. The link, the transport to Airavata, needs credentials only Airavata issues; without Airavata, use a Dev Tunnel that Linkspan hosts.

7. Create a Dev Tunnel​

On your laptop:

devtunnel create
devtunnel port create <tunnel ID> -p 8080
devtunnel token <tunnel ID> --scopes host
devtunnel token <tunnel ID> --scopes connect

devtunnel create prints the tunnel ID, of the form <id>.<cluster>, where <cluster> is the Dev Tunnels region. Only the control port is declared; every other server is reached through it.

TokenUsed by
Host tokenLinkspan, to host the tunnel
Connect tokenA client, to reach the tunnel

8. Host the tunnel from the job​

In the job, start Linkspan with the host token in its environment:

LINKSPAN_TUNNEL_HOST_TOKEN=<host token> ./linkspan --port 8080 \
--tunnel-enable --tunnel-mode=devtunnel --tunnel-devtunnel-args="--id <id> --cluster <cluster>" \
> linkspan.log 2>&1 &

On first use Linkspan downloads the devtunnel CLI to ~/.cybershuttle/bin/devtunnel, then runs devtunnel host. A wrong ID or token shows as devtunnel: host process exited repeating in linkspan.log, as Linkspan reruns it after a growing delay.

The host token stays off Linkspan's command line, but the devtunnel host command line carries it, where other users on the node can read it.

9. Call Linkspan through the tunnel​

On your laptop:

curl -s -H 'X-Tunnel-Authorization: tunnel <connect token>' \
https://<id>-8080.<cluster>.devtunnels.ms/api/v1/health

The reply is {"status":"ok"}, from the compute node. Every other server is reached through the /api/v1/forward/{port} WebSocket.

When you have finished, stop Linkspan in the job with kill %1. On your laptop, delete the tunnel with devtunnel delete <tunnel ID>.

Next steps​

ToRead
Carry the API over Airavata's linkThe link
Checkpoint a process before the walltimeCheckpoint/Restore
Run steps at set points in a jobLinkspan workflow format
Listen on a Unix socket, or see every flagLinkspan configuration
See every route and responseLinkspan HTTP API