Skip to content

PATTERN Cited by 1 source

Stager downloads agent for remote control

Shape

A small shell snippet (the stager), sent over an already-established transport (typically SSH), bootstraps the installation of a full-featured agent — often including its own language runtime — on the remote host. Once the agent is running, the stager exits and the client connects to the agent over a second protocol (usually an RPC channel carried over a port forwarded through the original transport). The agent persists across reconnects of the transport; it owns filesystem I/O, process spawning, and whatever domain-specific capabilities the client tool needs.

Client                    SSH                    Remote
  │                        │                       │
  │─── open SSH session ───┼──────────────────────▶│
  │                        │                       │
  │─── exec stager bash ───┼─────────────────────▶ sh -c "..."
  │                                                │
  │                                        detect arch/OS
  │                                        download tarball
  │                                        unpack to $HOME
  │                                        exec agent in background
  │                                                │
  │                                          agent listens :P
  │                                                │
  │── SSH port-forward :P (localhost:P') ──────── :P
  │                                                │
  │─── WebSocket(localhost:P') ◀─── agent ────────▶│
  │                                                │
  │          [agent handles: FS read/write,        │
  │           edit, PTY spawn, persistence,        │
  │           arbitrary RPC]                       │

Canonical instance

VSCode Remote-SSH. Fly.io's 2025-02-07 description:

"Unlike Tramp, which lives off the land on the remote connection, VSCode mounts a full-scale invasion: it runs a Bash snippet stager that downloads an agent, including a binary installation of Node. […] The agent runs over port-forwarded SSH. It establishes a WebSockets connection back to your running VSCode front-end. The underlying protocol on that connection can: Wander around the filesystem; Edit arbitrary files; Launch its own shell PTY processes; Persist itself." (Source: sources/2025-02-07-flyio-vscodes-ssh-agent-is-bananas)

Key architectural commitments:

  • Ships its own runtime. The stager downloads a Node.js binary; it does not assume Node is pre-installed on the remote. Any version skew / missing-dep risk between client and remote runtime is taken off the table.
  • Persistent agent. Survives SSH reconnects; reconnect resumes against the same process rather than re-staging.
  • RPC channel distinct from the transport. SSH is the bootstrap + port-forward carrier; the in-session protocol is a WebSocket spoken by the agent, multiplexed over a forwarded TCP port.

When the pattern makes sense

  • You need a capability surface richer than what the shell natively exposes (language servers, structured RPC, extension hosting, debugger protocol).
  • You need state across reconnects (open editors, dirty-file buffers, long-running compile tasks).
  • The remote's existing runtime inventory cannot be assumed.
  • The performance cost of per-operation shell roundtrips is unacceptable (many small FS ops, live file-system watches).

When the pattern is the wrong answer

  • Dev-laptop target. Fly.io's critique: "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." A persistent-agent footprint on a multi-tenant or privileged host is the same architectural shape as a RAT (remote access trojan); the only difference is operator intent. Blast radius is wrong.
  • Ephemeral-purpose task. If the task is "read this one file over SSH", a stager + download + install is architectural overkill and a live-off-the-land shell pipeline would do.
  • Audit-sensitive contexts. Persistent agent = persistent state = persistent thing to audit. Live-off-the-land leaves nothing behind.

Compensating control: sandbox the target

The pattern's blast-radius problem has an architectural answer: don't fix the agent, fix the target host. If the target is a disposable VM whose compromise doesn't matter, the RAT-shape of the agent becomes a non-issue. Fly.io's 2025-02-07 post is effectively arguing for this composition — use VSCode Remote-SSH, but point it at a Fly Machine.

Seen in

  • sources/2025-02-07-flyio-vscodes-ssh-agent-is-bananas — Fly.io's architectural critique of the pattern via VSCode Remote-SSH. The post names the stager flow, the agent's capability surface, the WebSocket-over-SSH-port-forward transport, and the RAT-shape security framing.
Last updated · 200 distilled / 1,178 read