pkh put only spoke SFTP to the PPA queue, so a failure of the SSH transport itself (TCP, banner exchange) failed the upload even though dput happily pushes the same files: its plain ppa: profile goes over the anonymous FTP queue of ppa.launchpad.net, the same destination over another port. Classify the SSH connection failures: Transport (the connection never came up: resolution, TCP, banner or key exchange) degrades to that FTP queue — the upload order (payload first, .changes last), the reverse-order DELE cleanup of a failed upload and the per-chunk progress reporting all mirror the SFTP path, sharing cleanup_list. Refused failures (host key not accepted, no matching authentication) stay errors: silently switching transport would bypass the refusal. The FTP client is suppaftp's blocking stream, with the time bounds it does not carry by itself: the control channel's reads and writes, the data channel's writes and connect (through a custom passive stream builder), and the NAT workaround for PASV replies announcing an unroutable address. The queue endpoints (host, port) join data/launchpad.yml next to the SFTP ones, and the FTP transport is covered by unit tests against an in-process fake queue plus a live control-channel handshake with the real server (ignored, network).