Skip to content

FLYIO 2025-02-07

Read original ↗

Fly.io — VSCode's SSH Agent Is Bananas

Summary

Fly.io's 2025-02-07 opinion post on VSCode Remote-SSH's architecture viewed as a security posture, from the vantage point of wiring Fly Machines into the VSCode remote-editing flow. Two architectural points dominate the piece: (1) the motivating use-case — running LLM-driven "agentic" coding loops on a clean-slate Linux instance that spins up instantly and can't screw you over — is a direct argument for using disposable VMs (Fly's business, unsurprisingly) instead of a developer's laptop as the execution environment for an LLM agent; and (2) the architectural contrast between Emacs's Tramp and VSCode Remote as two diametrically-opposite models of remote editing.

Tramp lives off the land: if it can reach an interactive Bourne-shell-speaking environment — usually an SSH session — it uses only the commands already there (ls, cat, shell redirection) to let Emacs manipulate files on the remote side. No agent is deployed. VSCode Remote does the opposite: the initial SSH connection runs a bash stager that downloads a tarball containing a full Node.js binary plus a VSCode server agent, unpacks it into the user's home directory, and starts the agent. The agent persists across sessions, listens on a port forwarded back over SSH, accepts a WebSocket connection from the client-side VSCode front-end, and exposes an RPC surface that can:

  • wander around the filesystem
  • edit arbitrary files
  • launch its own shell PTY processes
  • persist itself

Fly's framing: "in security-world, there's a name for tools that work this way. I won't say it out loud, because that's not fair to VSCode, but let's just say the name is murid in nature." (Murid = of the family Muridae, i.e. rats — Fly is calling the agent a RAT, the security term of art for Remote Access Trojan. That's the whole joke.)

Fly's posture: "I would be a little nervous about letting people VSCode-remote-edit stuff on dev servers, and apoplectic if that happened during an incident on something in production."

The post is framed as a warm-up for a future "custom connection to a Fly Machine working in VSCode" post — "we've decided to just be a blog again" — so this piece is primarily architectural commentary, not the productisation post. The architectural commentary is substantive enough to be load-bearing on its own: it introduces the canonical wiki contrast between Tramp (live off the land) and [[systems/vscode-remote-ssh|VSCode Remote] (deploy a full runtime + agent + RPC), and it names the agentic development loop as the actual motivation for why disposable-VM sandboxing is worth building.

Key takeaways

  1. The motivating use-case for remote-dev-env integration in 2025 is running LLM agents on a sandbox, not convenience for humans. "LLM-generated code is useful in the general case if you know what you're doing. But it's ultra-useful if you can close the loop between the LLM and the execution environment (with an 'Agent' setup) […] it's a semi-effective antidote to hallucination: the LLM generates the code, the agent scaffolding runs the code, the code generates errors, the agent feeds it back to the LLM, the process iterates." Canonical definition of the agentic development loop on the wiki. The defensive posture Fly calls out — "LLMs have boundary issues, and they'll iterate on your system configuration just as happily on the Git project you happen to be working in" — is the direct argument for the disposable-VM-for-agentic-loop pattern. (Source: sources/2025-02-07-flyio-vscodes-ssh-agent-is-bananas)

  2. Tramp and VSCode Remote are diametrically-opposite remote-editing architectures. "Emacs hosts the spiritual forebearer of remote editing systems, a blob of hyper-useful Elisp called Tramp. If you can hook Tramp up to any kind of interactive environment — usually, an SSH session — where it can run Bourne shell commands, it can extend Emacs to that environment." Tramp is the canonical live-off-the-land remote-editing model: no agent deployed, no runtime shipped, uses only what's already on the remote host. VSCode Remote is the opposite: "VSCode mounts a full-scale invasion: it runs a Bash snippet stager that downloads an agent, including a binary installation of Node." The two models are the two ends of the remote-editing-architecture spectrum — same problem, opposite answers. (Source: sources/2025-02-07-flyio-vscodes-ssh-agent-is-bananas)

  3. VSCode Remote's agent is a persistent long-running process with a WebSocket RPC over a port-forwarded SSH tunnel. Fly enumerates the agent's capability surface explicitly: "wander around the filesystem, edit arbitrary files, launch its own shell PTY processes, persist itself." The transport is a WebSocket (an application-layer long-lived full-duplex channel) multiplexed over a TCP port forwarded back via SSH (the SSH connection bootstraps the tunnel, then the WebSocket traffic rides the tunnel). This is distinct from a per-operation RPC model — the agent is a full remote-execution surface kept open for the lifetime of the editing session, and persists across SSH reconnects. (Source: sources/2025-02-07-flyio-vscodes-ssh-agent-is-bananas)

  4. The agent's capability surface is indistinguishable from a RAT at the architectural level. Fly's dry framing — "in security-world, there's a name for tools that work this way […] murid in nature" (RAT = Remote Access Trojan) — is making a serious architectural point: a remote process that persists on a box, listens on an exfiltrated port, accepts RPCs from an external client, and exposes filesystem + shell-exec + process-persistence through that RPC, is the RAT shape. The only thing that distinguishes the friendly case (VSCode) from the hostile case is the operator's intent. Security posture concern follows: "I would be a little nervous about letting people VSCode-remote-edit stuff on dev servers, and apoplectic if that happened during an incident on something in production." Canonical wiki instance of the observation that agent-deployment remote-dev models pay an architectural premium in blast radius that live-off-the-land models don't. (Source: sources/2025-02-07-flyio-vscodes-ssh-agent-is-bananas)

  5. Disposable-VM-as-sandbox is the natural answer to the agentic loop's boundary-issue risk. "A thing you'd really like to be able to do: run a closed-loop agent-y ('agentic'? is that what we say now) configuration for an LLM, on a clean-slate Linux instance that spins up instantly and that can't screw you over in any way." That description is architectural DNA for Fly Machines — Firecracker micro-VMs that boot in under a second and are discardable — and for the broader patterns/disposable-vm-for-agentic-loop pattern. Related instance on the wiki: Cloudflare's capability-based sandbox for Dynamic Workers, framed around exactly the same concern ("what exactly do we want this thing to be able to do?"). (Source: sources/2025-02-07-flyio-vscodes-ssh-agent-is-bananas)

  6. Fly Machines-as-dev-target integration is the motivating end-goal of the post. "It turns out we don't have to care about any of this to get a custom connection to a Fly Machine working in VSCode, so none of this matters in any kind of deep way, but: we've decided to just be a blog again, so: we had to learn this, and now you do too." Fly is signalling a productisation path — VSCode Remote targeting a Fly Machine as the dev-sandbox — and using the architectural critique as the set-up. The productisation post is explicitly deferred ("we've decided to just be a blog again" = they're not shipping a thin product post, they're shipping a thick architectural one). (Source: sources/2025-02-07-flyio-vscodes-ssh-agent-is-bananas)

Architectural primitives surfaced

Two remote-editing models

Axis Emacs Tramp VSCode Remote-SSH
What runs on the remote host Nothing — existing sh, ls, cat, etc. Deployed agent — Node.js + VSCode server binary
Transport Interactive Bourne shell over SSH SSH port-forward carrying a WebSocket
State on remote None (stateless — each operation is a shell command) Persistent agent across reconnects
Deployment step None"lives off the land" Bash stager downloads + installs + launches
Capability surface Whatever the shell can do FS read/write, PTY spawn, self-persistence, arbitrary RPC
Failure mode if compromised Session-scoped — your shell's power Persistent RAT-shape — full box, across reconnects

VSCode Remote-SSH boot flow (what the stager does)

  1. Client-side VSCode opens an SSH connection to the target host.
  2. Runs a bash snippet that detects arch/OS, downloads a tarball containing a Node.js binary (no assumption about Node being installed) and the VSCode server agent.
  3. Unpacks the tarball into the user's home directory.
  4. Starts the agent as a background process.
  5. Agent listens on a TCP port; client establishes a WebSocket over that port via the SSH port-forward.
  6. All subsequent editor operations ride the WebSocket.
  7. Agent persists across SSH session drops; reconnect resumes against the same agent process.

Post references what Fly believes is the agent source: microsoft/vscode src/vs/server/node at c9e7e1b72f…

Agent capability surface (from the post)

  • "Wander around the filesystem"
  • "Edit arbitrary files"
  • "Launch its own shell PTY processes"
  • "Persist itself"

Agentic-loop execution environment requirements

  • Clean slate — no prior state the LLM can get confused by or corrupt across runs
  • Instantly spins up — the loop needs to iterate; slow provisioning breaks the feedback cycle
  • Can't screw you over — blast radius is bounded; the LLM iterating on your system configuration cannot touch the dev laptop or production
  • Fly's implicit claim: Fly Machines (Firecracker micro-VMs) hit all three.

Operational numbers

  • None — no QPS, latency, port number, binary size, stager line count, token count, or any other quantitative disclosure. The post is opinion-piece / architectural commentary; operational numbers aren't its substance.

Caveats

  • Not an architecture paper. The load-bearing claims — agent capabilities, stager flow, WebSocket-over-SSH-port-forward — are correct but presented informally. Post links to what Fly thinks is the agent source (src/vs/server/node); doesn't walk the protocol or code in detail.
  • Tramp's framing is uncharitable-lite but accurate. "Bourne shell commands" understates Tramp's actual machinery (it does implement file transfer over scp, rsync, ftp, etc., and has optional on-wire helpers) but the core claim — no agent deployed, uses what's there — is correct.
  • RAT-shape claim is architectural, not an accusation. Fly's "murid in nature" quip is architectural commentary; VSCode Remote is not malware, the point is that the architectural shape is identical to one, which constrains what blast-radius assumptions are safe.
  • Productisation post deferred. The "custom connection to a Fly Machine in VSCode" piece is promised but not in this post. Nothing here about how Fly wires VSCode to a Fly Machine specifically.
  • No discussion of VSCode's extension-host isolation. The agent runs extensions (including Copilot, Cursor forks, etc.) on the remote host; the post doesn't get into the extension-host vs. server split or whether individual extensions can be sandboxed within the agent. (Not a flaw — out of scope for the post — but the wiki notes it so future work has a pointer.)

Source

Last updated · 200 distilled / 1,178 read