- 01.What Scares You Is Usually Smaller Than You Think
- 02.SSH as Key Exchange
- 03.The Reconnection Protocol
- 04.What 11,000 Lines Actually Contains
- 05.Proof of Tractability
- 06.The Pattern
The Scariest Dependency Is the One You Haven't Read
11,000 lines of C++ stood between Pocket and its own future. Reading every one revealed that the intimidating system was 200 lines of real logic wrapped in 3,000 lines of features we'd never use.
Pocket depends on Eternal Terminal for resilient SSH connections. ET handles the hard part -- when your phone locks, when you switch from WiFi to cellular, when the train enters a tunnel. The TCP socket dies, but your terminal session survives. It works. It's also 11,000 lines of C++ we've never read, written by someone else, with no guarantee it'll keep working for our mobile use case.
The question wasn't "should we replace it?" That's premature. The question was: can we understand it well enough that if it ever becomes a blocker, we're not trapped?
What Scares You Is Usually Smaller Than You Think
The shaping session surfaced something important. ET isn't broken. There's no pain. This is proactive work -- building an escape path, not escaping. That distinction matters because it changes the approach entirely. You don't spike a replacement when the current thing works. You read the current thing until you understand whether a replacement is even warranted.
The protocol definitions were the first surprise. ET's wire format is four protobuf messages:
message ConnectRequest {
optional string clientId = 1;
optional int32 version = 2;
}
message ConnectResponse {
optional ConnectStatus status = 1; // NEW_CLIENT, RETURNING_CLIENT, INVALID_KEY
}
message SequenceHeader {
optional int32 sequenceNumber = 1;
}
message CatchupBuffer {
repeated bytes buffer = 1;
}Plus two terminal-specific messages for PTY bytes and window dimensions. That's the entire protocol surface. For a system that handles encrypted, resumable, crash-resilient terminal sessions, this felt implausibly minimal. Where's the complexity hiding?
SSH as Key Exchange
The first clever trick is in credential setup. ET doesn't implement its own key exchange -- it piggybacks on SSH, which already solved authentication and encrypted transport:
string passkey = genRandomAlphaNum(32);
string id = genRandomAlphaNum(16);
string command = "echo '" + id + "/" + passkey + "_" + clientTerm + "' | " +
etterminal_bin + " " + options;The client generates a random 32-byte passkey, opens an SSH session, and pipes the passkey to the ET server process through that already-encrypted channel. Both sides now share a symmetric key without implementing Diffie-Hellman, certificates, or any key exchange ceremony. SSH did the hard work; ET just needs a shared secret to encrypt subsequent traffic on its own TCP connection.
The crypto layer uses that passkey directly as a NaCl secretbox key (XSalsa20-Poly1305). No key derivation function, no salt, no rounds. The passkey is random and ephemeral -- it doesn't need stretching. Nonces start at zero and increment monotonically, with a direction bit in the MSB to prevent client and server from ever producing the same nonce:
CryptoHandler::CryptoHandler(const string& _key, unsigned char nonceMSB) {
memset(nonce, 0, crypto_secretbox_NONCEBYTES);
nonce[crypto_secretbox_NONCEBYTES - 1] = nonceMSB; // 0 = client→server, 1 = server→client
}Both sides count up independently. No nonce negotiation needed. The encryption layer is stateless beyond a single integer.
The Reconnection Protocol
This is the part that seemed magical before reading the code. After reading it, it's mechanical -- which is a compliment. The design is obvious in hindsight, which means it's correct.
Every message passes through a BackedWriter before hitting the wire. The writer encrypts first, then stores the ciphertext in a ring buffer with a sequence number:
BackedWriterWriteState BackedWriter::write(Packet packet) {
packet.encrypt(cryptoHandler); // Encrypt first
backupBuffer.push_front(packet); // Then backup
sequenceNumber++;
// Trim to MAX_BACKUP_BYTES
}On reconnect, both sides exchange "what's the last sequence number you received?" Each side then replays everything the other missed from its backup buffer. Because messages are backed up after encryption, the crypto state doesn't need rewinding -- the ciphertext is identical to what was originally sent. Nonces stay synchronized because both sides know exactly how many messages were encrypted.
The reconnect loop itself is almost embarrassingly simple: poll every second, send the same client ID, and if the server says RETURNING_CLIENT, run the recovery handshake. No exponential backoff, no jitter, no complexity. For a mobile terminal client that needs to reconnect fast after a network switch, this aggressive retry is exactly right.
What 11,000 Lines Actually Contains
Reading the full codebase revealed where the line count comes from. The core reconnection protocol -- the part Pocket actually needs -- is roughly 200 lines across Connection.cpp, BackedWriter.cpp, and BackedReader.cpp. The remaining bulk breaks down into features:
| Feature | Lines | Pocket needs it? |
|---|---|---|
| Core reconnection | ~200 | Yes -- this is the whole point |
| SSH bootstrap | ~400 | Yes -- credential setup |
| Crypto layer | ~150 | Yes -- encrypt/decrypt |
| Port forwarding | ~500 | Maybe later |
| Jump host support | ~300 | No (Tailscale replaces this) |
| HTM multiplexer | ~800 | No (use tmux) |
| SSH agent forwarding | ~200 | Low priority |
| Build system, tests, platform compat | ~3000+ | No |
The intimidating 11k shrinks to roughly 750 lines of logic Pocket actually uses. And those 750 lines implement a protocol simple enough to restate in a paragraph: generate a shared key over SSH, encrypt with NaCl secretbox, sequence-number every message, back them up, and replay on reconnect.
Proof of Tractability
Understanding the protocol made reimplementation feel inevitable rather than ambitious. The Go version of the crypto layer took thirty minutes and passed on the first test run:
func NewHandler(passkey string, nonceMSB byte) (*Handler, error) {
if len(passkey) != KeySize {
return nil, errors.New("passkey must be 32 bytes")
}
h := &Handler{}
copy(h.key[:], passkey)
h.nonce[NonceSize-1] = nonceMSB
return h, nil
}
func (h *Handler) Encrypt(plaintext []byte) []byte {
h.mu.Lock()
defer h.mu.Unlock()
out := secretbox.Seal(nil, plaintext, &h.nonce, &h.key)
h.incrementNonce()
return out
}Five tests covering encrypt/decrypt round-trips, nonce synchronization, direction isolation, and invalid key rejection. All passing. The Go NaCl bindings are a direct mapping of the same primitives ET uses in C++ via libsodium -- the cryptographic behavior is identical.
The full pocket-server scaffold landed at four commits: project setup, ET trace findings, architecture design, and Go scaffolding with working crypto. Estimated final size: 1,500-2,000 lines of Go for feature parity with ET's mobile-relevant subset.
The Pattern
The build-vs-buy decision is usually framed as a cost question: is it cheaper to build or to use the existing thing? But that framing assumes you understand both options. When the existing thing is opaque -- when it's "11,000 lines of C++ we've never read" -- the cost of building is unknowable because you don't know what you'd be building.
Reading first, deciding second. The trace revealed that ET's protocol is elegant precisely because it's minimal. There's no hidden complexity waiting to bite a reimplementation. The reconnection mechanism is a straightforward application of sequence numbers and message backup. The crypto is NaCl with counting nonces. The bootstrap leans on SSH instead of reinventing authentication.
The scariest dependency became the most understandable one -- not because it changed, but because we finally looked.