Opening the remote KISS Sorcar IDE from any machine with SSH keys

Host ksen@34.42.88.157 (Ubuntu 24.04, GCP) · changes to sorcar-cloud + new sorcar-connect · commit 1c27aa44 · 4 August 2026

The remote editor used to be reachable only from the laptop that deployed it, and only during that deploy. It is now a permanently-running service that any machine with SSH access can discover and open with a single 300-line file — sorcar-connect.

1. What was wrong

sorcar-cloud pushes your working directory to a Linux host, installs KISS Sorcar there and starts code-server — VS Code running as a web app. The editor binds 127.0.0.1 only (it runs with --auth none, so it must never face the internet), and an SSH tunnel is the way in. That part was fine. Two things were not.

problem 1  The editor was a detached process, not a service

It was launched like this, from inside the deploy's SSH session:

setsid bash -c "... exec code-server --bind-addr 127.0.0.1:$PORT --auth none ... '$WORKSPACE'" \
    > "$LOG_DIR/code-server.log" 2>&1 < /dev/null &

A detached process has no supervisor: if it crashed or was OOM-killed it stayed dead, and after a reboot the machine came back with no editor at all. The only way to get it back was another full redeploy (10–20 minutes, because it re-copies the tree and re-runs install.sh).

problem 2  Nothing on the host knew how to reach it

The IDE port, the webapp port and the workspace path existed only as shell variables in the deploying laptop's script run. A second machine — your desktop, a borrowed laptop, a phone with a shell — had no way to ask the host "which port is the editor on, and what folder is it serving?", and no supported command to open it. The tunnel logic lived in the middle of a 1000-line deploy script, so it could not be run on its own.

2. The fix, in two halves

laptop that deployed ./sorcar-cloud user@host deploy, then hands over any other machine ./sorcar-connect user@host one file, bash + ssh only nothing installed at all ssh -N -L 8080:127.0.0.1:8080 the command --print gives you SSH keys only — no password, no account remote host — Linux, systemd systemd user manager (lingering enabled) starts at boot with nobody logged in; restarts anything that dies kiss-code-server.service code-server --bind-addr 127.0.0.1:8080 Restart=always · WantedBy=default.target ~/.kiss/sorcar-cloud.env the ports + workspace, written on each deploy so a stranger machine can look them up
The editor never leaves loopback. Every client reaches it through an SSH port forward, and the host itself holds the two pieces of state a client needs: a supervised service and a small file that says where it is.

Half one — the IDE becomes a supervised, boot-started service

Step 7 of the deploy now writes a systemd user unit (no sudo anywhere) and enables lingering for the remote user:

# ~/.config/systemd/user/kiss-code-server.service
[Unit]
Description=KISS Sorcar IDE (code-server) on 127.0.0.1:8080
After=network-online.target

[Service]
Type=exec
EnvironmentFile=-%h/.kiss/api_keys.systemd.env
Environment=PATH=%h/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
Environment="KISS_PROJECT_PATH=/home/ksen/kiss"
WorkingDirectory=/home/ksen/kiss
ExecStart=/home/ksen/.local/bin/code-server --bind-addr 127.0.0.1:8080 --auth none \
    --disable-workspace-trust --enable-proposed-api ksenxx.kiss-sorcar "/home/ksen/kiss"
Restart=always
RestartSec=3
StandardOutput=append:%h/.kiss/code-server.log
StandardError=append:%h/.kiss/code-server.log

[Install]
WantedBy=default.target

Why each line is there:

Two ordering details matter. Lingering is enabled before systemctl --user enable, because without a lingering user manager "enabled at boot" is an empty promise. And the deploy's wipe step now stops the unit before its pkill: with Restart=always, killing the process would just make systemd start a new editor on the directory about to be deleted.

Half two — the host publishes how to reach it, and one file consumes that

The deploy writes the facts a client needs into the remote home directory:

# ~/.kiss/sorcar-cloud.env  (chmod 600, no secrets)
SORCAR_IDE_PORT=8080
SORCAR_WEB_PORT=8787
SORCAR_WORKSPACE=/home/ksen/kiss
SORCAR_IDE_SERVICE=kiss-code-server

It is excluded from the ~/.kiss mirror (which runs with --delete), so a later deploy from a laptop that has no such file cannot wipe the host's record of itself.

The new sorcar-connect is the client. It is deliberately one self-contained file that needs nothing but bash and ssh:

scp user@host:kiss/sorcar-connect .    # once, on the new machine
./sorcar-connect user@host             # every time after that

What it does, in order:

  1. Asks the host, in a single SSH round trip, for its sorcar-cloud.env facts.
  2. Checks the editor with code-server's own unauthenticated /healthz route — which proves the editor is serving, not merely that something holds the port — and systemctl --user starts the unit if it is silent. So even a host where someone stopped the service is repaired by connecting to it.
  3. Picks free local ports (preferring the familiar 8080/8787, falling back to +10000) and forwards both the IDE and the kiss-web webapp with ssh -N -n -o ExitOnForwardFailure=yes -o ServerAliveInterval=30.
  4. Opens your browser, waits for the webapp, and holds the tunnel until Ctrl+C — which closes the forward and leaves the remote editor running for the next machine.

--print skips all of that and just prints the two commands the host supports, for a machine where you would rather not run a script at all:

$ ./sorcar-connect --print ksen@34.42.88.157
[STEP]  Asking ksen@34.42.88.157 about its KISS Sorcar deployment ...
[ksen-vm-32] IDE is answering on 127.0.0.1:8080 (workspace: /home/ksen/kiss).

  Forward the ports yourself, then open http://localhost:8080 :
    ssh -N -n -o ExitOnForwardFailure=yes -o ServerAliveInterval=30 \
        -L 8080:127.0.0.1:8080 -L 18787:127.0.0.1:8787 ksen@34.42.88.157

  Or, with desktop VS Code + the Remote-SSH extension, skip the tunnel:
    code --remote ssh-remote+ksen@34.42.88.157 /home/ksen/kiss

The deploy itself now ends with exec ./sorcar-connect … — about 110 lines of tunnel, browser and hold logic were deleted from sorcar-cloud rather than duplicated. That is not just tidiness: the path your other machines take is therefore exercised on every single deploy, so it cannot quietly rot. ./sorcar-cloud --connect user@host is a shortcut to the same script for people who only remember one command.

3. Why SSH tunnelling, and not something flashier

OptionWhat it needs on the clientVerdict
SSH port forward (chosen)An SSH client and your key. Nothing else. Coder's own documentation recommends it: If you have an SSH server on your remote machine, this approach doesn't require any additional setup at all. The editor stays on loopback, and browsers treat localhost as a secure context, so web views work.
Expose the port publiclyNothing Rejected. Never expose code-server directly to the internet without some form of authentication and encryption, otherwise someone can take over your machine via the terminal.
Caddy/NGINX + Let's EncryptNothing (a real URL) Needs a domain, an open port and a certificate — real infrastructure for a personal dev VM. Kept in mind for tablets, which have no SSH client.
VS Code Remote Tunnels (code tunnel)Desktop VS Code + a GitHub/Microsoft account Rejected: it authenticates with an account, not your SSH keys, and relays traffic through a third-party service.
VS Code Remote-SSHDesktop VS Code + the Remote-SSH extension Genuinely nice when you have it — so --print hands you the exact code --remote ssh-remote+user@host /path command. Not universal, hence not the default.

One more research finding that made this safe: a single code-server instance is happy with several concurrent browsers and tunnels (unlike Microsoft's VS Code Server, which documents an instance of the server is designed to be accessed by one user or client at a time). Your desktop and your laptop can be connected at once.

4. Evidence — eight live tests on the real VM

#TestObserved
1Full deploy installs the service Installing the kiss-code-server user service (127.0.0.1:8080, workspace: /home/ksen/kiss)code-server is up on 127.0.0.1:8080pass
2Unit and state are as designed is-active=active, is-enabled=enabled, Linger=yes, /healthz answers, and ss -tlnp shows only 127.0.0.1:8080 — nothing publicpass
3Self-healing: kill -9 the editor MainPID 181482 killed → 8 s later active, MainPID 181698, NRestarts=1, /healthz OKpass
4Foreign machine: a directory holding only sorcar-connect, with the remote service deliberately stopped IDE not answering on 127.0.0.1:8080 — starting kiss-code-server …IDE is answering … → tunnel up → curl http://localhost:8080/ returns 302 → ./?folder=/home/ksen/kiss with the workbench HTMLpass
5The IDE really loads in a browser through the tunnel Workbench renders the deployed tree; the KISS SORCAR panel activates; status bar shows Forwarded Ports: 8787, … and 0 problemspass
6The extension still starts the webapp under systemd With kiss-web stopped and its fingerprint deleted beforehand, both came back: ActiveEnterTimestamp Tue 2026-08-04 01:14:12 UTC, fingerprint recreatedpass
7A real agent task runs on the VM Newest task_history row on the VM: result hostname → ksen-vm-32.c.r2eg-441800.internal, python3 -c "print(6*7)" → 42, model=claude-opus-5, work_dir=/home/ksen/kisspass
8Reboot the VM Editor active at 01:20:33 UTC; my first successful SSH login was at 01:20:46 — it started before anyone logged in. ./sorcar-connect --print afterwards needed no repair: IDE is answering …pass

Test 7 is the one that closes the loop on the riskiest part of the change. Moving off the setsid launch meant the editor no longer inherits a login shell's environment, so the agent could have lost its API keys. A successful claude-opus-5 call — recorded in the VM's own database, reporting the VM's own GCP-internal hostname — proves the EnvironmentFile path works. Test 3 also confirmed the tunnel hygiene from the earlier review still holds: SIGTERM to the client prints Closing the SSH tunnels…, frees the local port and leaves the remote editor untouched.

5. A bug found by accident, and fixed

bugfixed  A 200 MB transfer thrown away, silently

My first test deploy wrote its log to ./tmp/deploy.log — inside the very tree being copied. Twenty minutes later the script was gone, the log ended mid-sentence and nothing was printed. The remote had the complete tree; the deploy had simply died.

Cause: tar exits 1 for warnings, and the classic warning is "file changed as we read it" — which any live checkout produces (a log being written, an editor autosaving). Under set -e with pipefail that warning aborted the script, and set -e exits without a message. So a normal, harmless condition destroyed a finished transfer and explained nothing.

The copy now classifies the two halves of the pipe instead of trusting a single status:

COPYFILE_DISABLE=1 tar --no-xattrs --exclude='.venv' --exclude='.kiss-worktrees' \
    -C "$src" -czpf - . | ssh "$TARGET" "mkdir -p -- '$dst' && tar -C '$dst' -xzpf -" || true
local tar_rc="${PIPESTATUS[0]}" ssh_rc="${PIPESTATUS[1]}"
((ssh_rc == 0)) || die "The remote refused the copy (ssh/tar exit $ssh_rc)."
((tar_rc <= 1)) || die "Local tar failed with exit $tar_rc — the copy is incomplete."
((tar_rc == 0)) || warn "tar reported warnings (exit 1) — usually a file that changed while being read."

The receiving ssh is the half that can lose data, so any failure there is still fatal; a real tar error (≥2) is fatal; a warning is a warning.

6. What changed, file by file

FileChange
sorcar-connect new ~310 lines. The whole client: discovery, service repair, local-port probing, the two forwards, browser, webapp wait, hold-and-teardown. Flags --print and --no-browser. Depends only on bash and ssh.
sorcar-cloud 189 insertions, 210 deletions — it got shorter while gaining features. Step 7h installs the systemd unit and enables lingering; new step 7i writes ~/.kiss/sorcar-cloud.env; the wipe step stops the unit before pkill; the ~/.kiss mirror excludes the new state file; copy_tree() classifies tar/ssh exits; steps 8–11 are replaced by exec ./sorcar-connect; --connect added; the duplicated local-port probes were removed (they live in the client now).

Both files pass bash -n and shellcheck (only the pre-existing intentional informational notes remain: client-side expansion in ssh "VAR='$X' bash -s", and tildes inside message strings).

7. How to use it

# From the machine with the checkout — deploy, then it opens the IDE for you:
./sorcar-cloud ksen@34.42.88.157

# From that same machine later, without redeploying:
./sorcar-cloud --connect ksen@34.42.88.157

# From ANY machine that can ssh into the host:
scp ksen@34.42.88.157:kiss/sorcar-connect .
./sorcar-connect ksen@34.42.88.157

# Or with no script at all:
./sorcar-connect --print ksen@34.42.88.157   # copy the ssh -L line it prints

If the host is ever truly stuck, the two commands that tell you everything are systemctl --user status kiss-code-server and tail ~/.kiss/code-server.log.