Why does SSH send 100 packets per keystroke?

2026-01-2219:27662362eieio.games

I made my next game twice as fast by forking go's crypto library

And why do I care?

Jan 22, 2026

Here are a few lines of summarized tcpdump output for an ssh session where I send a single keystroke:

$ ./first_lines_of_pcap.sh single-key.pcap
 1 0.000s CLIENT->SERVER 36 bytes
 2 0.007s SERVER->CLIENT 564 bytes
 3 0.015s CLIENT->SERVER 0 bytes
 4 0.015s CLIENT->SERVER 36 bytes
 5 0.015s SERVER->CLIENT 36 bytes
 6 0.026s CLIENT->SERVER 0 bytes
 7 0.036s CLIENT->SERVER 36 bytes
 8 0.036s SERVER->CLIENT 36 bytes
 9 0.046s CLIENT->SERVER 0 bytes
 10 0.059s CLIENT->SERVER 36 bytes

I said a “few” because there are a lot of these lines.

$ ./summarize_pcap.sh single-key.pcap
Total packets: 270

 36-byte msgs: 179 packets ( 66.3%) 6444 bytes
 Other data: 1 packet ( 0.4%) 564 bytes
 TCP ACKs: 90 packets ( 33.3%)

 Data sent: 6444 bytes in 36-byte messages, 564 bytes in other data
 Ratio: 11.4x more data in 36-byte messages than other data

 Data packet rate: ~90 packets/second (avg 11.1 ms between data packets)

That is a lot of packets for one keypress. What’s going on here? Why do I care?

here's those scripts if you're curious

tshark -r "$1" \
 -T fields -e frame.number -e frame.time_relative -e ip.src -e ip.dst -e tcp.len | \
 awk 'NR<=10 {dir = ($3 ~ /71\.190/ ? "CLIENT->SERVER" : "SERVER->CLIENT");
 printf "%3d %6.3fs %-4s %3s bytes\n", $1, $2, dir, $5}'

tshark -r "$1" -Y "frame.time_relative <= 2.0" -T fields -e frame.time_relative -e tcp.len | awk '
 {
 count++
 payload = $2

 if (payload == 0) {
 acks++
 } else if (payload == 36) {
 mystery++
 if (NR > 1 && prev_data_time > 0) {
 delta = $1 - prev_data_time
 sum_data_deltas += delta
 data_intervals++
 }
 prev_data_time = $1
 } else {
 game_data++
 game_bytes = payload
 if (NR > 1 && prev_data_time > 0) {
 delta = $1 - prev_data_time
 sum_data_deltas += delta
 data_intervals++
 }
 prev_data_time = $1
 }
 }
 END {
 print "Total packets:", count
 print ""
 printf " 36-byte msgs: %3d packets (%5.1f%%) %5d bytes\n", mystery, 100*mystery/count, mystery*36
 printf " Other data: %3d packet (%5.1f%%) %5d bytes\n", game_data, 100*game_data/count, game_bytes
 printf " TCP ACKs: %3d packets (%5.1f%%)\n", acks, 100*acks/count
 print ""
 printf " Data sent: %d bytes in 36-byte messages, %d bytes in other data\n", mystery*36, game_bytes
 printf " Ratio: %.1fx more data in 36-byte messages than other data\n", (mystery*36)/game_bytes
 print ""
 avg_ms = (sum_data_deltas / data_intervals) * 1000
 printf " Data packet rate: ~%d packets/second (avg %.1f ms between data packets)\n", int(1000/avg_ms + 0.5), avg_ms
 }'

Discovery

I am working on a high-performance game that runs over ssh. The TUI for the game is created in bubbletea 1 and sent over ssh via wish.

1

I have also forked bubbletea to make it faster. Stay tuned!

The game is played in an 80x60 window that I update 10 times a second. I’m targeting at least 2,000 concurrent players, which means updating ~100 million cells a second. I care about performance.

So I have a script that connects a few hundred bots over ssh and has them make a move a second. Then I use go’s outstanding profiling tools to look at what’s going on.

Yesterday I inadvertently broke my test harness. Instead of regularly sending game data, my server sent the bots a single message that said “your screen is too small.” This cut my game’s CPU and bandwidth usage in half.

At first I was disappointed. I (briefly) thought I had a free massive speedup on my hands, but it was actually a testing error.

But wait.

If I wasn’t sending game data back to my bots, why did CPU usage drop by 50% instead of 100%?

Investigation

As part of debugging the test harness issue, I used tcpdump to log game traffic with and without the breaking change. Something like:


timeout 30s tcpdump -i eth0 'port 22' -w with-breaking-change.pcap

timeout 30s tcpdump -i eth0 'port 22' -w without-breaking-change.pcap

Our breaking change stopped us from rendering our game over ssh. So with-breaking-change.pcap contains packets that represent the overhead of each connection without actually rendering the game.

I was debugging this with Claude Code, so I asked it to summarize what it saw in the pcap.

Wanna take a look yourself? I put with-breaking-change.pcap in this directory

--

Wow! Here's what I found:

Packet Size Distribution (413,703 total packets):
274,907 packets (66%): Exactly 36 bytes
138,778 packets (34%): 0 bytes (TCP ACKs)
18 packets (<0.1%): 72 bytes

Further analysis on a smaller pcap pointed to these mysterious packets arriving ~20ms apart.

This was baffling to me (and to Claude Code). We kicked around several ideas like:

  • SSH flow control messages
  • PTY size polling or other status checks
  • Some quirk of bubbletea or wish

One thing stood out - these exchanges were initiated by my ssh client (stock ssh installed on MacOS) - not by my server.

On a hunch, I took a tcpdump of a regular ssh session.


sudo tcpdump -ien0 'port 22'


ssh $some_vm_of_mine

I waited for the initial connection chatter to die down, sent one keystroke to my remote vm, and looked at the tcpdump output.

I saw the exact same pattern! What in the world?

Root cause

Once I realized that this was a property of stock ssh and not my game, debugging got a lot easier.

Running ssh -vvv gave me a pretty good sense of what was going on:

debug3: obfuscate_keystroke_timing: starting: interval ~20ms
debug3: obfuscate_keystroke_timing: stopping: chaff time expired (49 chaff packets sent) debug3: obfuscate_keystroke_timing: starting: interval ~20ms
debug3: obfuscate_keystroke_timing: stopping: chaff time expired (101 chaff packets sent)

That 20ms is a smoking gun - it lines up perfectly with the mysterious pattern we saw earlier! And the rest of the message is pretty helpful too - we sent 49 “chaff” packets for the first keystroke and 101 “chaff” for around the second one.

In 2023, ssh added keystroke timing obfuscation. The idea is that the speed at which you type different letters betrays some information about which letters you’re typing. So ssh sends lots of “chaff” packets along with your keystrokes to make it hard for an attacker to determine when you’re actually entering keys.

That makes a lot of sense for regular ssh sessions, where privacy is critical. But it’s a lot of overhead for an open-to-the-whole-internet game where latency is critical.

Keystroke obfuscation can be disabled client-side. After reverting my original breaking change, I tried updating my test harness to pass ObscureKeystrokeTiming=no when starting up ssh sessions.

This worked great. CPU usage dropped dramatically and bots still received valid data.

But this is hardly a solution in the real world. I want ssh mygame to Just Work without asking users to pass options that they might not understand.

Claude Code originally didn’t have much faith that we could disable this functionality server-side.

Screenshot of a claude code transcript. Full text:
User
Is there any way to disable responding to the chaff server-side? Assistant
Probably not. Here's why: The chaff packets are legitimate SSH protocol messages (likely SSH_MSG_CHANNEL_WINDOW_ADJUST). The server must respond to maintain the SSH connection according to the protocol spec - if it ignored them, the connection would stall or break.

generated with simon wilson's excellent claude-code-transcripts tool

Fortunately, the description I found of SSH keystroke obfuscation made it easy to look up the relevant code in go’s ssh library (which I was transitively depending on).

Log message:
Introduce a transport-level ping facility

This adds a pair of SSH transport protocol messages SSH2_MSG_PING/PONG
to implement a ping capability. These messages use numbers in the "local
extensions" number space and are advertised using a "[email protected]"
ext-info message with a string version number of "0".

The “chaff” messages that ssh uses to obscure keystrokes are SSH2_MSG_PING messages. And they’re sent to servers that advertise the availability of the [email protected] extension. What if we just…don’t advertise [email protected]?

I searched go’s ssh library for [email protected] and found the commit where support was added. The commit was tiny and seemed very easy to revert.

I cloned the go crypto repo and told Claude to revert this change and update our dependencies to use our clone (go’s replace directive makes forking a library very easy).

Then I re-ran my test harness. The results were…very good:

Total CPU 29.90% -> 11.64%
Syscalls 3.10s -> 0.66s
Crypto 1.6s -> 0.11s
Bandwidth ~6.5 Mbit/sec -> ~3 Mbit/sec

Claude was also pretty pumped:

Chat message from claude code. Full text:
HOLY COW! Look at that CPU usage: Duration: 30.15s, Total samples = 3.51s (11.64%)

yes it's 1:30 am what of it

Obviously forking go’s crypto library is a little scary, and I’m gonna have to do some thinking about how to maintain my little patch in a safe way.

But this is a huge improvement. I’ve spent much of the last week squeezing out small single-digit performance wins. A >50% drop was unimaginable to me.

Debugging with LLMs was fun

I’ve been thinking about whether LLMs remove parts of the problem-solving process that I enjoy. But I’ve gotta say, debugging this problem using Claude Code was super fun.

I am familiar enough with tcpdump, tshark, and friends to know what they can do. But I don’t use them regularly enough to be fast with them. Being able to tell an agent “here’s a weird pcap - tell me what’s going on” was really lovely. And by watching commands as the agent ran them I was able to keep my mental model of the problem up to date.

There were still edge cases. At some point in my confusion I switched to ChatGPT and it very confidently told me that my tcpdump output was normal ssh behavior:

ChatGPT message. Full text:
Yeah, that trace looks wild at first glance, but it’s mostly “normal” SSH/TCP behavior plus the fact that SSH is optimized for latency, not packet efficiency. Let me unpack what you’re seeing and why it’s chewing CPU. 1. What those tcpdump lines actually are From your snippet:

do all chatgpt messages have this tone and formatting now?

And then doubled down when I pushed back:

ChatGPT message. Full text:
Thought for 42s Gotcha, that context helps a lot. Short version:
What you’re seeing is almost certainly a ton of tiny writes being turned into a ton of tiny SSH records, not some special “per-keypress flow-control storm” in SSH itself.

no!!!

Similarly, I had to push Claude Code to consider forking go’s ssh library. And I had to make the original leap of “wait…if our test harness was broken, why was usage not 0%?”

When you say “LLMs did not fully solve this problem” some people tend to respond with “you’re holding it wrong!”

I think they’re sometimes right! Interacting with LLMs is a new skill, and it feels pretty weird if you’re used to writing software like it’s 2020. A more talented user of LLMs may have trivially solved this problem.

But the best way to develop a skill is by practicing it. And for me, that means figuring out how to transfer my problem-solving intuitions to the tools that I’m using.

Besides. Being in the loop is fun. How else would I write this post?


Read the original article

Comments

  • By swiftcoder 2026-01-2219:528 reply

    > Obviously forking go’s crypto library is a little scary, and I’m gonna have to do some thinking about how to maintain my little patch in a safe way

    This should really be upstreamed as an option on the ssh library. Its good to default to sending chaff in untrusted environments, but there are plenty of places where we might as well save the bandwidth

    • By gerdesj 2026-01-230:423 reply

      "where we might as well save the bandwidth"

      I come from a world (yesteryear) where a computer had 1KB of RAM (ZX80). I've used links with modems rocking 1200 bps (1200 bits per second). I recall US Robotics modems getting to speeds of 56K - well that was mostly a fib worse than MS doing QA these days. Ooh I could chat with some bloke from Novell on Compuserve.

      In 1994ish I was asked to look into this fancy new world wide web thing on the internet. I was working at a UK military college as an IT bod, I was 24. I had a Windows 3.1 PC. I telnetted into a local VAX, then onto the X25 PAD. I used JANET to get to somewhere in the US (NIST) and from there to Switzerland to where this www thing started off. I was using telnet and WAIS and Gopher and then I was apparently using something called "www".

      I described this www thing as "a bit wank", which shows what a visionary I am!

      • By drzaiusx11 2026-01-231:233 reply

        Fellow old here, I had several 56k baud modems but even my USR (the best of the bunch) never got more than half way to 56k throughput. Took forever to download shit over BBS...

        • By beagle3 2026-01-235:402 reply

          The real analog copper lines were kind of limited to approx 28K - more or less the nyquist limit. However, the lines at the time were increasingly replaced with digital 64Kbit lines that sampled the analog tone. So, the 56k standard aligned itself to the actual sample times, and that allowed it to reach a 56k bps rate (some time/error tolerance still eats away at your bandwidth)

          If you never got more than 24-28k, you likely still had an analog line.

          • By mgiampapa 2026-01-238:382 reply

            56k was also unidirectional, you had to have special hardware on the other side to send at 56k downstream. The upstream was 33.6kbps I think, and that was in ideal conditions.

            • By cestith 2026-01-2315:23

              The special hardware was actually just a DSP at the ISP end. The big difference was before 56k modems, we had multiple analog lines coming into the ISP. We had to upgrade to digital service (DS1 or ISDN PRI) and break out the 64k digital channels to separate DSPs.

              The economical way to do that was integrated RAS systems like the Livingston Portmaster, Cisco 5x00 seriers, or Ascend Max. Those would take the aggregated digital line, break out the channels, hold multiple DSPs on multiple boards, and have an Ethernet (or sometimes another DS1 or DS3 for more direct uplink) with all those parts communicating inside the same chassis. In theory, though, you could break out the line in one piece of hardware and then have a bunch of firmware modems.

            • By dspillett 2026-01-2315:52

              The asymmetry of 56k standards was 2:1, so if you got a 56k6 link (the best you could get in theory IIRC) your upload rate would be ~28k3. In my expereience the best you would get in real world use was ~48k (so 48kbpd down, 24kbps up), and 42k (so 21k up) was the most I could guarantee would be stable (baring in mind “unstable” meant the link might completely drop randomly, not that there would be a blip here-or-there and all would be well again PDQ afterwards) for a significant length of time.

              To get 33k6 up (or even just 28k8 - some ISPs had banks of modems that supported one the 56k6 standards but would not support more than 28k8 symmetric) you needed to force your modem to connect using the older symmetric standards.

          • By drzaiusx11 2026-01-235:492 reply

            Yeah 28k sounds more closer to what I got when things were going well. I also forget if they were tracking in lower case 'k' (x1000) or upper case 'K' (x1024) units/s which obviously has an effect as well.

            • By mnw21cam 2026-01-2312:271 reply

              The lower case "k" vs upper case "K" is an abomination. The official notation is lower case "k" for 1000 and lower case "ki" for 1024. It's an abomination too, but it's the correct abomination.

              • By tracker1 2026-01-2316:45

                That's a newer representation, mostly because storage companies always (mis)represented their storage... I don't think any ISPs really misrepresent k/K in kilo bits/bytes

            • By encom 2026-01-239:55

              Line speed is always base 10. I think everything except RAM (memory, caches etc.) is base 10 really.

        • By dspillett 2026-01-2315:431 reply

          * 56k baud modems but even my USR (the best of the bunch) never got more than half way to 56k throughput*

          56k modem standards were asymmetric, the upload rate being half that of the download. In my experience (UK based, calling UK ISPs) 42kbps was usually what I saw, though 46 or even 48k was stable¹ for a while sometimes.

          But 42k down was 21k up, so if I was planning to upload anything much I'd set my modem to pretend it as a 36k6 unit: that was more stable and up to that speed things were symmetric (so I got 36k6 up as well as down, better than 24k/23k/21k). I could reliably get a 36k6 link, and it would generally stay up as long as I needed it to.

          --------

          [1] sometimes a 48k link would last many minutes then die randomly, forcing my modem to hold back to 42k resulted in much more stable connections

          • By tracker1 2026-01-2316:431 reply

            Even then, it required specialized hardware on the ISP side to connect above 33.6kbps at all, and almost never reliably so. I remember telling most of my friends just to get/stick with the 33.6k options. Especially considering the overhead a lot of those higher modems took, most of which were "winmodems" that used a fair amount of CPU overhead insstead of an actual COM/Serial port. It was kind of wild.

            • By dspillett 2026-01-2317:29

              Yep. Though I found 42k reliable and a useful boost over 36k6 (14%) if I was planning on downloading something big¹. If you had a 56k capable modem and had a less than ideal line, it was important to force it to 36k6 because failure to connect using the enhanced protocol would usually result in fallback all the way to 28k8 (assuming, of course, that your line wasn't too noisy for even 36k6 to be stable).

              I always avoided WinModems, in part because I used Linux a lot, and recommended friends/family do the same. “but it was cheaper!” was a regular refrain when one didn't work well, and I pulled out the good ol' “I told you so”.

              --------

              [1] Big by the standards of the day, not today!

        • By Jedd 2026-01-231:374 reply

          > several 56k baud modems

          These were almost definitely 8k baud.

          • By tfvlrue 2026-01-232:002 reply

            In case anyone else is curious, since this is something I was always confused about until I looked it up just now:

            "Baud rate" refers to the symbol rate, that is the number of pulses of the analog signal per second. A signal that has two voltage states can convey two bits of information per symbol.

            "Bit rate" refers to the amount of digital data conveyed. If there are two states per symbol, then the baud rate and bit rate are equivalent. 56K modems used 7 bits per symbol, so the bit rate was 7x the baud rate.

            • By AlpineG 2026-01-237:52

              Not sure about your last point but in serial comms there are start and stop bits and sometimes parity. We generally used 8 data bits with no parity so in effect there are 10 bits per character including the stop and start bits. That pretty much matched up with file transfer speeds achieved using one of the good protocols that used sliding windows to remove latency. To calculate expected speed just divide baud by 10 to covert from bits per second to characters per second then there is a little efficiency loss due to protocol overhead. This is direct without modems once you introduce those the speed could be variable.

            • By fkarg 2026-01-233:53

              Yes, except that in modern infra i.e. WiFi 6 is 1024-QAM, which is to say there are 1024 states per symbol, so you can transfer up to 10bits per symbol.

          • By davrosthedalek 2026-01-233:132 reply

            Yes, because at that time, a modem didn't actually talk to a modem over a switched analog line. Instead, line cards digitized the analog phone signal, the digital stream was then routed through the telecom network, and the converted back to analog. So the analog path was actually two short segments. The line cards digitized at 8kHz (enough for 4kHz analog bandwidth), using a logarithmic mapping (u-law? a-law?), and they managed to get 7 bits reliably through the two conversions.

            ISDN essentially moved that line card into the consumer's phone. So ISDN "modems" talked directly digital, and got to 64kbit/s.

            • By nyrikki 2026-01-2320:37

              An ISDN BRI (basic copper) actually had 2 64kbps b channels, for pots dialup as an ISP you typically had a PRI with 23 b, and 1 d channel.

              56k only allowed one ad/da from provider to customer.

              When I was troubleshooting clients, the problem was almost always on the customer side of the demarc with old two line or insane star junctions being the primary source.

              You didn’t even get 33k on analog switches, but at least US West and GTE had isdn capable switches backed by at least DS# by the time the commercial internet took off. Lata tariffs in the US killed BRIs for the most part.

              T1 CAS was still around but in channel CID etc… didn’t really work for their needs.

              33.6k still depended on DS# backhaul, but you could be pots on both sides, 56k depended on only one analog conversion.

            • By namibj 2026-01-2310:32

              56k relied on the TX modem to be digitally wired to the DAC that fed the analog segment of the line.

          • By da_chicken 2026-01-2311:391 reply

            Confusing baud and bit rates is consistent with actually being there, though.

            • By Jedd 2026-01-2312:341 reply

              As someone that started with 300/300 and went via 1200/75 to 9600 etc - I don't believe conflating signalling changes with bps is an indication of physical or temporal proximity.

              • By drzaiusx11 2026-01-2323:162 reply

                I think it was a joke implying you'd be old enough to forget because of age, which in my case is definitely true...

                • By Jedd 2026-01-246:05

                  Oh, I got the implication, but I think it was such a common mistake back then, that I don't think it's age-related now - it's a bit of a trope, to assume baud and bps mean the same thing, and people tend to prefer to use a more technical term even when it's not fully understood. Hence we are where we are with terms like decimate, myriad, nubile, detox etc, forcefully redefined by common (mis)usage. I need a cup of tea, clearly.

                  Anyway, I didn't think my throw-away comment would engender such a large response. I guess we're not the only olds around here!

                • By da_chicken 2026-01-242:071 reply

                  No, just that confusing the two was ubiquitous at the time 14.4k, 28k, and 56k modems were the standard.

                  Like it was more common than confusing Kbps and KBps.

                  I mean, the 3.5" floppy disk could store 1.44 MB... and by that people meant the capacity was 1,474,560 bytes = 1.44 * 1024 * 1000. Accuracy and consistency in terminology has never been particularly important to marketing and advertising, except marketing and advertising is exactly where most laypersons first learn technical terms.

                  • By drzaiusx11 2026-01-2417:04

                    I started out with a 2400 baud US Robotics modem with my "ISP" being my local university to surf gopher and BBS. When both baud rates and bits per second were being marketed side by side I kinda lost the thread tbh. Using different bases for storage vs transmission rates didn't help.

          • By drzaiusx11 2026-01-234:041 reply

            Yeah I got baud and bit rates confused. I also don't recall any hayes commands anymore either...

      • By quesera 2026-01-231:443 reply

        > I've used links with modems rocking 1200 bps

        Yo, 300 baud, checking in.

        Do I hear 110?

        +++ATH0

        • By robflynn 2026-01-232:481 reply

          Ah, the good old days. I remember dialing up local BBSes with QMODEM.

          AT&C1&D2S36=7DT*70,,,5551212

          • By codazoda 2026-01-234:122 reply

            PoiZoN BBS Sysop chiming in. I ran the BBS on a free phone line I found in my childhood bedroom. I alerted the phone company and a tech spent a day trying to untangle it, but gave up at the end of his shift. He even stopped by to tell me it wouldn’t be fixed.

            I didn’t know the phone number, so I bought a Caller ID box, hooked it to my home line, and phoned home. It wasn’t long before every BBS in town had a listing for it.

            • By quesera 2026-01-2317:32

              That's awesome.

              I had to wait til I was old enough to get a phone line in my own name before running a BBS. And also til I had a modem that would auto-answer, which was not a given back then!

              But I confess my first question for a working but unassigned phone line would be: who gets the bill for long distance calls?

              I had access to no-cost long distance calling through other administrative oversights, but they were a bit more effort to maintain! :)

            • By nwellinghoff 2026-01-234:201 reply

              Man that tech was cool and did you a solid.

              • By bigfatkitten 2026-01-237:02

                Many techs went to work for the phone companies for a reason.

        • By ochrist 2026-01-239:201 reply

          My first modem (from 1987) was 300 baud, but it could be used in a split mode called 75/1200.

          Before that I used 50 baud systems in the military as well as civil telex systems.

          • By quesera 2026-01-2317:28

            Mine was 300 baud, probably 1982?

            And I felt privileged because the configuration for my TI-99/4A Terminal Emulator (which I believe was called Terminal Emulator) had options for 110 or 300 baud, and I felt lucky to be able to use the "fast" one. :)

            My first modem (you always remember your first) had no carrier detection (and no Hayes commands, and no speaker...), so I would dial the number manually, then flip a switch when I heard the remote end pick up and send carrier to get the synchronization started.

            It was incredibly exciting at the time.

        • By guiambros 2026-01-235:12

          Ha, same! On a TRS-80 Color, nonetheless. But I think I used four times, because no one else in the country had a BBS at the time (small city in Latin America).

          It took a couple of years until it would catch on, and by then 1200 and 2400 bps were already the norm - thankfully!

      • By bandrami 2026-01-235:251 reply

        Same year, I tried this cool new "Mosaic" software and thought it was a cool proof of concept, but there was no way this web thing could ever displace gopher

        • By egeozcan 2026-01-236:03

          Which was right, today gopher has more users than ever! :)

    • By reincarnate0x14 2026-01-2223:261 reply

      It sort of already is. This behavior is only applied to sessions with a TTY and then the client can disable it, which is a sensible default. This specific use case is tripping it up obviously since the server knows ahead of time that the connection is not important enough to obfuscate and this isn't a typical terminal session, but in almost any other scenario there is no way to make that determination and the client expects its ObscureKeystrokeTiming to be honored.

      • By CaptainNegative 2026-01-231:202 reply

        What's a concrete threat model here? If you're sending data to an ssh server, you already need to trust that it's handling your input responsibly. What's the scenario where it's fine that the client doesn't know if the server is using pastebin for backing up session dumps, but it's problematic that the server tells the client that it's not accepting a certain timing obfuscation technique?

        • By reincarnate0x14 2026-01-231:32

          The behavior exists to prevent a 3rd party from inferring keystrokes from active terminal sessions, which is surprisingly easy, particularly with knowledge about the user's typing speed, keyboard type, etc. The old CIA TEMPEST stuff used to make good guesses at keystrokes from the timing of AC power circuit draws for typewriters and real terminals. Someone with a laser and a nearby window can measure the vibrations in the glass from the sound of a keyboard. The problem is real and has been an OPSEC sort of consideration for a long time.

          The client and server themselves obviously know the contents of the communications anyway, but the client option (and default behavior) expects this protection against someone that can capture network traffic in between. If there was some server side option they'd probably also want to include some sort of warning message that the option was requested but not honored, etc.

        • By TruePath 2026-01-254:07

          To clarify the point in the other reply -- imagine it sent one packet per keystroke. Now anyone sitting on the network gets a rough measurement of the delay between your keystrokes. If you are entering a password for something (perhaps not the initial auth) it can guess how many characters it is and turns out there are some systemic patterns in how that relates to the keys pressed -- eg letters typed with the same finger have longer delays between them. Given the redundancy in most text and especially structured input that's a serious security threat.

    • By BoppreH 2026-01-2221:101 reply

      Yes, but I wouldn't be surprised if the change is rejected. The crypto library is very opinionated, you're also not allowed to configure the order of TLS cipher suites, for example.

      • By mystraline 2026-01-2221:473 reply

        [flagged]

        • By throawayonthe 2026-01-2222:26

          that's the point of opinionated crypto libraries, yes

        • By JTbane 2026-01-2223:08

          Personally I like that it's secure by default.

        • By otabdeveloper4 2026-01-2222:312 reply

          Those same security guys also think that "just hope that no bad guy ever gets root access, lol" is a valid threat model analysis, so whatever.

          • By anonymous908213 2026-01-2223:201 reply

            That is a completely valid threat model analysis, though? "Just hope no bad guy ever gets into the safe" is rather the entire point of a safe. If you have a safe, in which you use the contents of the safe daily, does it make sense to lock everything inside the safe in 100 smaller safes in some kind of nesting doll scheme? Whatever marginal increase in security you might get by doing so is invalidated by the fact that you lose all utility of being able to use the things in the safe, and we already know that overburdensome security is counterproductive because if something is so secure that it becomes impossible to use, those security measures just get bypassed completely in the name of using the thing. At some level of security you have to have the freedom to use the thing you're securing. Anything that could keep a bad guy from doing anything ever would also keep the good guy, ie. you, from doing anything ever.

            • By otabdeveloper4 2026-01-237:131 reply

              > That is a completely valid threat model analysis, though?

              No it isn't. Here in 2026 timesharing accounts aren't a thing anymore and literally everyone who ever logs into your server has root access.

              "Just make sure all those outsourced sysadmins working for a contractor you've never met are never bad guys" is not a valid security threat model.

              • By KAMSPioneer 2026-01-2316:25

                > literally everyone

                Perhaps figuratively? I manage several servers where the majority of (LDAP) accounts have no special privileges at all. They get their data in the directories and can launch processes as their user, that's...pretty much it.

                Though the upstream comment is gone and I am perhaps missing some important context here.

          • By fwip 2026-01-231:491 reply

            When the question is "how do I communicate securely with a third party," there's nothing you can do if the third party in question gets possessed by a demon and turns evil. (Which is what happens if an attacker has root.)

            • By otabdeveloper4 2026-01-237:152 reply

              Incorrect.

              Random sysadmins who have access to your server have the permissions to steal whatever is communicated between third parties unrelated to this sysadmin.

              Just because some random outsourced nightshift dude has the permissions to do "sudo systemctl restart" shouldn't mean he gets to read all the secret credentials the service uses.

              As it is now, the dude has full unfettered access to all credentials of all services on that machine.

              • By fwip 2026-01-2316:32

                I guess if your org usually gives the keys to the castle to random idiots, then yeah, I can see why you'd wish the master key didn't exist.

    • By pseudohadamard 2026-01-239:301 reply

      It's not just the pointless chaff, the SSH protocol is inherently very chatty, and SFTP even more so. The solution, for a high-performance game, is don't use SSH. Either run it over Wireguard or grab some standard crypto library and encrypt the packets yourself. You'll probably make a few minor mistakes but unless the other player is the NSA it'll be good enough.

      For that matter, why does it need to be encrypted at all? What's the threat model?

      If there really is a genuine need to encrypt and low latency is critical, consider using a stream cipher mode like AES-CTR to pregenerate keystream at times when the CPU is lightly loaded. Then when you need to encrypt (say) 128 bytes you peel off that many bytes of keystream and encrypt at close to zero cost. Just remember to also MAC the encrypted data, since AES-CTR provides zero integrity protection.

      • By tracker1 2026-01-2316:591 reply

        Serious question, why not just use websockets? AFAIK, it's effectively a TLS socket with a little bit of handshake overhead when starting.

        I'm literally working on a web interface I want to use for classic BBS door play... currently working on a DOS era EGA interface, and intend to do similar for PETSCII/Comodore64/128 play as well. I've got a couple rendering bugs to explore for ansis submitted that messed up in the viewer test mode.

        https://github.com/bbs-land/webterm-dos-ansi

        It's been an opportunity to play with AI dev as well... spent as much time getting the scrollback working how I want as it took on the general rendering.

        • By pseudohadamard 2026-01-249:091 reply

          Websockets is just another layer on top of TLS, so you've got the size explosion and complexity/latency of TLS and then another layer on top of that. The OP hasn't provided additional info on what the requirements are but if it's a realtime game then they'll probably be "as close to zero latency and size increase as possible (compared to unencrypted messaging)", which websockets over TLS isn't.

          • By tracker1 2026-01-2616:10

            Unless I'm completely misunderstanding, once you "upgrade" the connection to a websocket connection, it's pretty much a bog standard TLS socket... I'm not sure what you mean by a size explosion, compared to what? As to latency or overhead, yeah there's some, but generally very minimal on anything resembling modern hardware, there are literally trillions of bytes transported over HTTPS/TLS every day from watches to super computers.

            Beyond this, there are libraries and tunnels for everything under the sun, and it's one of the least likely options to see mass breakages in general given it handshakes over 443 (https). Assuming you want encryption... if you don't then use raw sockets, or websockets without https and/or raw sockets... You can use whatever you like.

    • By Calvin02 2026-01-2221:391 reply

      Threats exist in both trusted and untrusted environments though.

      This feels like a really niche use case for SSH. Exposing this more broadly could lead to set-it-and-forget-it scenarios and ultimately make someone less secure.

      • By smallmancontrov 2026-01-2222:43

        Resource-constrained environments might be niche to you, but they are not niche to the world.

    • By eikenberry 2026-01-2220:262 reply

      +1... Given how much SSH is used for computer-to-computer communication it seems like there really should be a way to disable this when it isn't necessary.

      • By mkj 2026-01-2221:20

        It looks like it is only applied for PTY sessions, which most computer-computer connections wouldn't be using.

        https://github.com/openssh/openssh-portable/blob/d7950aca8ea...

      • By jacquesm 2026-01-2221:081 reply

        In practice I've never felt this was an issue. But I can see how with extremely low bandwidth devices it might be, for instance LoRa over a 40 km link into some embedded device.

        • By geocar 2026-01-2222:014 reply

          Hah no.

          Nobody is running TCP on that link, let alone SSH.

          • By Rebelgecko 2026-01-230:433 reply

            Once upon a time I worked on a project where we SSH'd into a satellite for debugging and updates via your standard electronics hobbiest-tier 915mhz radio. Performance was not great but it worked and was cheap.

            • By jacquesm 2026-01-230:471 reply

              This is still done today in the Arducopter community over similar radio links.

              • By drzaiusx11 2026-01-231:302 reply

                I haven't heard much about the ArduCopter (and ArduPilot) projects for a decade, are those projects still at it? I used to run a quadroter I made myself a while back until I crashed it in a tree and decided to find cheaper hobbies...

                • By mardifoufs 2026-01-233:02

                  Well at least crashing drones into trees has never been cheaper hahaha. So it's super easy to get into nowadays, especially if it's just to play around with flight systems instead of going for pure performance.

                • By jacquesm 2026-01-232:13

                  They're alive and well and producing some pretty impressive software.

                  Crashing your drone is a learning experience ;)

                  Remote NSH over Mavlink is interesting, your drone is flying and you are talking to the controller in real time. Just don't type 'reboot'!

            • By geocar 2026-01-2314:142 reply

              ELRS?

              • By Rebelgecko 2026-01-2322:21

                Nope this predated ELRS by a bit. I wasn't super involved with the RF stuff so not sure if we rolled our own or used an existing framework

              • By jacquesm 2026-01-2318:14

                You can run ELRS on 900 MHz but the bitrate is atrocious.

          • By jacquesm 2026-01-2222:27

            https://github.com/markqvist/Reticulum

            and RNode would be a better match.

          • By dsrtslnd23 2026-01-2223:231 reply

            In aerial robotics, 900MHz telemetry links (like Microhard) are standard. And running SSH over them is common practice I guess.

            • By BenjiWiebe 2026-01-233:58

              Why do you guess? I wouldn't expect SSH to be used on a telemetry link. Nor TCP, and probably not IP either.

          • By nomel 2026-01-230:442 reply

            what's wrong with tcp, on a crappy link, when guaranteed delivery is required? wasn't it invented when slow crappy links were the norm?

            • By OhMeadhbh 2026-01-231:32

              Because TCP interprets packet loss as congestion and slows down. If you're already on a slow, lossy wireless link, bandwidth can rapidly fall below the usability threshold. After decades of DARPA attending IETF meetings to find solutions for this exact problem [turns out there were a lot of V4 connections over microwave links in Iraq] there are somewhat standard ways of setting options on sockets to tell the OS to consider packet loss as packet loss and to avoid slowing down as quickly. But you have to know what these options are, and I'm pretty sure the OP's requirement of having `ssh foo.com` just work be complicated by TCP implementations defaulting to the "packet loss means congestion" behavior. Hmm... now that I think about it, I'm not even sure if the control plane options were integrated into the Linux kernel (or Mac or Wintel)

              Life is difficult sometimes.

            • By direwolf20 2026-01-231:45

              It will time out before your packet gets through, or it will retransmit faster than the link can send packets.

    • By KennyBlanken 2026-01-2318:121 reply

      The guy in charge of Go's security decreed that SSL 1.3 (which he was a contributor to) was so secure that silly programmers should not be able to override what algorithms are allowed or not allowed, because why would they possibly need to do that, because he's such a genius, and even if someone DID find a security vulnerability, well....they can just wait for Google to publicly disclose it and release a patch, compile the new version, update their code to work with that version of Go, rebuild their containers, put stuff through testing, and then release it into production.

      Versus...seeing there's a vulnerability, someone adding a one-line change to disable the vulnerable algorithm, compile, image update, test. And a lot less testing because you're not moving to a new version of the language / compiler.

      The man has no practical experience in running a production network service, an ego the size of a small moon, and yet was a major contributor to a security protocol now in use by billions of people.

      But hey, you can be a handbag designer and end up head of design at Apple soooooooo

      • By TruePath 2026-01-254:11

        Lots of the real world vulnerabilities out there exist exactly because of people choosing to support a range of crypto algorithms.

        Sure, if it's an internal tool you can recompile both ends and force a universal update. But anything else and you need to stay compatible with clients and anytime you allow negotiation of the cryptosuit you open yourself up to quite a few subtle attacks. Not saying that choice about go is clearly a good one but i don't think it's obviously wrong.

    • By PunchyHamster 2026-01-238:28

      Relying on not advertising some feature for it is very janky way to do it.

      The proper fix would be adding option server-side to signal client it's not needed and have client side have option to accept or warn about that

  • By zamadatix 2026-01-2219:562 reply

    Very interesting, I hadn't heard of this obfuscation before so it was well worth clicking.

    Another good trick for debugging ssh's exact behavior is patching in "None" cipher support for your test environment. It's about the same work as trying to set up a proxy but lets you see the raw content of the packets like it was telnet.

    For terminal games where security does not matter but performance and scale does, just offering telnet in the first place can also be worth consideration.

  • By flumpcakes 2026-01-2221:546 reply

    I don't see how Claude helped the debugging at all. It seemed like the author knew what to do and it was more telling Claude to think about that.

    I've used Claude a bit and it never speaks to me like that either, "Holy Cow!" etc. It sounds more annoying than interacting with real people. Perhaps AIs are good at sensing personalities from input text and doesn't act this way with my terse prompts..

    • By AceJohnny2 2026-01-2222:343 reply

      Even if the chatbot served only as a Rubber Ducky [1], that's already valuable.

      I've used Claude for debugging system behavior, and I kind of agree with the author. While Claude isn't always directly helpful (hallucinations remain, or at least outdated information), it helps me 1) spell out my understanding of the system (see [1]) and 2) help me keep momentum by supplying tasks.

      [1] https://en.wikipedia.org/wiki/Rubber_duck_debugging

      • By NewJazz 2026-01-2223:103 reply

        A rubber ducky demands that you think about your own questions, rather than taking a mental back seat as you get pummeled with information that may or may not be relevant.

        • By supern0va 2026-01-2223:251 reply

          I assure you that if you rubber duck at another engineer that doesn't understand what you're doing, you will also be pummeled with information that may or may not be relevant. ;)

          • By stephenr 2026-01-231:163 reply

            That isn't rubber duck debugging. It's just talking to someone about the problem.

            The entire point of rubber duck debugging is that the other side literally cannot respond - it's an inanimate object, or even a literal duck/animal.

            • By fc417fc802 2026-01-2313:022 reply

              I don't think that's right. When you explain a technical problem to someone who isn't intimately familiar with it you're forced to think through the individual steps in quite a bit of detail. Of course that itself is an acquired skill but never mind that.

              The point or rubber duck debugging then is to realize the benefit of verbally describing the problem without needing to interrupt your colleague and waste his time in order to do so. It's born of the recognition that often, midway through wasting your colleague's time, you'll trail off with an "oh ..." and exit the conversation. You've ended up figuring out the problem before ever actually receiving any feedback.

              To that end an LLM works perfectly well as long as you still need to walk through a full explanation of the problem (ie minimal relevant context). An added bonus being that the LLM offers at least some of the benefits of a live person who can point out errors or alert you to new information as you go.

              Basically my quibble is that to me the entire point of rubber duck debugging is "doesn't waste a real person's time" but it comes with the noticeable drawback of "plastic duck is incapable of contributing any useful insights".

              • By dspillett 2026-01-2316:121 reply

                > When you explain a technical problem to someone who isn't intimately familiar with it you're forced to think through the individual steps in quite a bit of detail.

                The point of Rubber Ducking (or talking/praying to the Wooden Indian, to use an older phrase that is steeped in somewhat racist undertones so no longer generally used) is that it is an inanimate object that doesn't talk back. You still talk to it as if you were explaining to another person, so are forcing yourself to get your thoughts in order in a way that would make that possible, but actually talking to another person who is actively listening and actually asking questions is the next level.

                • By fc417fc802 2026-01-2316:37

                  I guess I can see where others are coming from (the LLM is different than a literal rubber duck) but I feel like the "can't reply" part was never more than an incidental consequence. To me the "why" of it was always that I need to solve my problem and I don't want to disturb my colleagues (or am unable to contact anyone in the first place for some reason).

                  So where others see "rubber ducking" as explaining to an object that is incapable of response, I've always seen it as explaining something without turning to others who are steeped in the problem. For example I would consider explaining something to a nontechnical friend to qualify as rubber ducking. The "WTF" interjections definitely make it more effective (the rubber duck consistently fails to notify me if I leave out key details).

              • By NewJazz 2026-01-2316:031 reply

                To that end a notepad works just as well.

                • By fc417fc802 2026-01-2316:38

                  In reality vim is my usual approach. But I think LLMs are better in a lot of regards.

            • By quesera 2026-01-231:561 reply

              Oh it can definitely be a person. I've worked with a few!

              • By stephenr 2026-01-234:13

                Cue obligatory Ralph Fiennes "You're an inanimate fucking object".

            • By nurettin 2026-01-245:18

              Before you expand the definition to every object in the universe, maybe we could call it parrot debugging.

        • By grimgrin 2026-01-231:142 reply

          I'm not saying you should do this, but you can do this:

          https://gist.github.com/shmup/100a7529724cedfcda1276a65664dc...

        • By MBCook 2026-01-231:051 reply

          They also don’t waste electricity, water, drive up the prices of critical computer components, or DDOS websites to steal their content.

          • By AceJohnny2 2026-01-232:131 reply

            Not to defend the extravagant power use of the AI datacenters, but I invite you to look up the ecological footprint of a human being.

            • By MBCook 2026-01-233:08

              The human being in this scenario exists either way.

              The AI does not.

      • By dspillett 2026-01-2316:06

        > Even if the chatbot served only as a Rubber Ducky [1], that's already valuable.

        I use the Other Voices for that. I can't entirely turn them off, I might as well make use of them!

      • By specialist 2026-01-2313:221 reply

        Rubber Ducky is a terrific name for a GPT.

        Also, always reminds me of Kermit singing "...you make bath time so much fun!..."

    • By dcdc123 2026-01-233:06

      You’re absolutely right!

    • By eieio 2026-01-232:221 reply

      Claude is much faster at extracting fields from a pcap and processing them with awk than I am!

    • By H8crilA 2026-01-2222:421 reply

      AIs are exceptional at sensing personalities from text. Claude nailed it here, the author felt so good about the "holy cow" comments that he even included them in the blog post. I'm not just poking this, but saying that the bots are fantastic sycophants.

      • By IshKebab 2026-01-2223:032 reply

        No they aren't. Current LLMs always have that annoying over-eager tone.

        The comment about Claude being pumped was a joke.

        • By simondotau 2026-01-230:04

          It depends how much the LLM has been beaten into submission by the system prompt.

        • By stackghost 2026-01-230:17

          ChatGPT set to "terse and professional" personality mode is refreshingly sparse on the "you're absolutely right" bullshit

    • By bitwize 2026-01-231:54

      It's like I keep saying, it probably wasn't a good idea to give our development tools Genuine People Personalities...

    • By nerdsniper 2026-01-2314:54

      I think the blog post itself was partially written with LLMs, which explains some of the odd narrative style.

HackerNews