You drive a handful of remote boxes from a Windows laptop — some over SSH, some over serial consoles on a console-server. Over months you accumulate a pile of glue: a create-sessions script for one lab, a hand-written .cmd wrapper for another host, a saved PuTTY session for a third, a ~/.ssh/config block you tuned once and forgot. Each host was solved in isolation. Nothing knows about anything else. And when an AI agent joins the work, it has no idea any of it exists — so it fumbles, re-derives connection details, and trips the same host-key prompts you fixed by hand a year ago.

That fragmentation is the real problem. Not “which terminal” — but that the knowledge of how to reach each box is scattered across five places and reaches neither a new agent nor future-you. This post is about collapsing that into one declarative registry that generates everything downstream, and one design decision at its core: two access modes, chosen deliberately.

The two modes (and why one echoes your command back)

There are two fundamentally different ways to run a command on a remote box, and they produce different output because they use different channels.

Mode 1 — drive an interactive terminal window. A real terminal is open (a PuTTY window, a tmux/screen pane) and you type into it — on Windows, often by posting WM_CHAR messages to the window. The remote shell runs under a pseudo-terminal (PTY), and a PTY echoes back every character it receives. So a captured transcript is literally what a human sees on screen:

whoami

root
root@web-01:~#

The command is in the output. So is the prompt.

Mode 2 — one-off non-interactive exec. You run ssh web-01 whoami. OpenSSH allocates no PTY, so there’s no echo layer — you get exactly the command’s stdout:

root

Same command, same host, clean — because there’s no terminal in the loop to echo anything.

  Interactive window One-off exec
Invocation type into an open PuTTY/tmux window ssh host "cmd"
PTY allocated? yes no (unless you pass -t)
Command echoed in output? yes no
Prompt in output? yes no
Survives disconnect? yes (window stays open) no (process ends)
Good for serial consoles, watching live, boot logs scripting, parsing, clean capture

The mechanism: what a PTY actually does

A pseudo-terminal is a kernel device with two ends: the master (whatever drives — your terminal emulator, tmux, expect) and the slave (where the shell runs). The slave runs a line discipline — a small in-kernel state machine with an ECHO flag on by default. When the master writes bytes toward the shell, the line discipline echoes them back before the shell ever sees them. That’s why you see your own keystrokes in any terminal — it isn’t the shell printing them, it’s the tty layer.

stty -a | grep -o '\-\?echo'   # 'echo' = on, '-echo' = off
ssh web-01 whoami              # no PTY  -> prints: root
ssh -tt web-01 whoami          # forces a PTY -> echoes the command + a prompt

Password prompts clear ECHO temporarily (stty -echo) so keystrokes don’t show — same mechanism, deliberately off. With no PTY there’s no slave-side line discipline at all, so nothing echoes. That single design fact is the whole reason the two modes differ — and the reason an agent must choose between them consciously.

One registry, everything generated

The fix for the fragmentation is a single source of truth — a small YAML file listing every host — and one command that regenerates all the downstream artifacts from it. I call mine pcon. The registry looks like this:

testbeds:
  web-01:
    kind: ssh
    host: 10.0.0.11
    user: root
    exec: web1          # short one-off wrapper name
    manage_ssh: true    # generate a ~/.ssh/config block if none exists
  console-a:
    kind: telnet        # serial console over a console-server port
    host: 10.0.0.254
    port: 2006
    titles: [console-a]

pcon sync reads that and regenerates, for each host:

Adding a box becomes one row plus pcon sync — not a new bespoke script. And crucially, the same registry is what you point an agent’s onboarding docs at: one place, not five.

pcon list                 # every host + which windows are open
pcon add web-02 host=10.0.0.12 user=root exec=web2 manage_ssh=true
web2 uname -a             # one-off, clean output (no PTY, no echo)
pcon launch web-01        # persistent window you can see + scroll back
pcon rm web-02            # removes the row AND every generated artifact

Design decisions that actually mattered

The tool is thin. The value is in a few decisions that came from not reinventing wheels:

On the “buy vs build” question: for the SSH side there are established declarative tools (assh compiles a YAML host file into ~/.ssh/config, with gateway/ProxyJump chaining). For the GUI side, an established tabbed session manager beats hand-rolling one and imports the PuTTY sessions you already generate. The thin custom glue is worth writing only for the parts nothing off-the-shelf covers: the registry that unifies both modes plus serial consoles on Windows, and the one-off wrappers. Reach for the boring, established tool first; keep the custom surface as small as it can be.

Why the two-mode split matters for the agent

The trap: standardising the agent on the interactive-window mode “because it’s what I use” wraps every command in echo + prompt, and you end up writing fragile regexes to strip your own commands out. The fix isn’t a better regex — it’s routing parseable commands through the no-PTY path. When you are stuck in a window, a sentinel (echo __BEGIN__; cmd; echo __END__) and slicing between markers survives echo, prompts, and pagination far better than prompt-matching alone.

The rule of thumb

Put every host in one registry and generate the rest. If something reads the output, run it without a PTY (ssh host cmd). If a human — or a serial line — needs a live terminal, use the interactive window, and expect the echo.

The echoed command was never a bug in your tooling — it’s the tty line discipline doing exactly what it’s done since the 1970s. And the pile of per-host scripts was never a tooling problem either; it was a source-of-truth problem. Collapse it to one registry, pick the right side of the PTY boundary per command, and both you and your agent stop re-deriving what a file could have told them.

Source