Three weeks ago I wrote about building a real app with Claude Code: ShuttleSCP, a free SFTP, SCP and FTP client for Linux, macOS and Windows, written in Rust with GTK 4. It was at version 0.13.0. It is now at 0.26.0.
In between: 30 releases, the code grew from about 22,000 to about 34,000 lines of Rust, and the tests from 149 to about 200. Most of the work wasn't new buttons. It was making copies hold up on real networks and real servers, and being able to prove they did.
The "what's next" shipped first
The last article ended with one goal: make the Linux AppImage run on older systems. That was 0.14.0, the next day. It now runs on Ubuntu 20.04, 22.04 and 24.04 and on Debian 12 and 13, and every release is checked on all of them in containers before it ships.
Copying between two servers
The biggest new feature is site to site (0.15.0): one server in each pane, and you drag files from one to the other. The servers never talk to each other. ShuttleSCP reads from one and writes to the other at the same time, without storing the files on your disk, like scp -3. That means it works between any two kinds of server, even SFTP on one side and FTP on the other.
Making transfers survive real networks
Tests on a fast local network passed. Real servers found the problems:
- Slow servers. Downloads from servers slower than about 0.4 MB/s failed with a timeout. The library waited for a whole batch of requests, not for each one. Fixed in 0.21.3.
- Dropped connections. A lost SSH connection used to fail the transfer. Now ShuttleSCP logs in again by itself and continues the file where it stopped, up to three times.
- Browsing during a copy. On one connection, opening a folder waited behind the copy's queued reads: seconds per click on a slow line. Transfers now get a second connection of their own.
- Closing the app. Partial copies are kept. The next login offers to resume them, and the Login dialog has an Unfinished tab listing them.
Knowing the copy is right
Since 0.22.0, every copy is checked against its original by SHA-256 checksum. The server computes its side itself when it can (sha256sum over SSH, or FTP's HASH command). When it can't, ShuttleSCP reads the copy back, up to a size you choose. A copy that differs counts as failed, and a move keeps the original.
This is where testing on the Windows machine paid off: one FTP server answered the hash command with "501 Option not understood", and ShuttleSCP took that as a failure instead of "this server can't do it". It shipped as a fix two hours later.
A feature that changed after use
0.20.0 added a limit on transfers running at once. Using it, it was the wrong unit: one transfer of 500 files and one of a single file counted the same. 0.24.0 changed it to count files, oldest transfer first, with Start now to jump the queue. The first version wasn't wrong in the code. It was wrong for the person using it, and only using it showed that.
Two-factor logins
The newest release (0.26.0) logs in to servers that want a key or password and then a one-time code. I tested it on a VM set up with Google Authenticator, and the first finding wasn't a bug in ShuttleSCP: even with my key set up, the server still asked for my password. Its PAM settings put the Unix password in front of the code, so I was being asked for a key, a password and a code.
The design question was mine to answer. ShuttleSCP opens a second connection for transfers, and with two-factor each connection needs a fresh code. Two codes per session would be annoying. So on these servers, transfers now run on a second channel of the same SSH connection: one code per session. A dropped connection asks once more, and one login serves both.
Six languages, labelled honestly
The interface is in English, French, Spanish, German, Brazilian Portuguese and Simplified Chinese. I only speak English, so the translations were made with AI, and the app says so next to each language name until a native speaker reviews it. A test checks that every translation has the same messages and placeholders as the English.
A user guide, with clips the app recorded itself
0.25.0 added a user guide: Help › User Guide opens it offline in your browser, and it's online too. The clip above is from it.
The clips had to be anonymous: no real hosts, users or paths. Claude didn't screen-record. It wrote a debug-only recorder inside the app: a drawn pointer drives the real interface and picks the app's own Copy Here menu, in a made-up home folder, against throwaway servers with example.com names, behind a small relay that slows copies down enough to watch. GTK renders each frame, and ImageMagick builds the GIF. It took several rounds: menus didn't appear in the frames, the desktop took the focus away, and the renderer sometimes returned empty frames. Re-recording the clips is now one command.
Guardrails, and where they failed
The engineering around the app grew too: dependency checks against the RustSec advisory database on every change, weekly Dependabot updates, and secret scanning with gitleaks over the whole history.
The most useful lesson came from a failure. Claude's merge script once merged a pull request before CI had finished. The command it used to wait returned early, and a shell alias hid the exit code. Nothing broke, but it could have. The script now polls until no check is pending and merges only if every one passed. An assistant that is fast at writing code needs guardrails as strict as a new teammate's, and probably stricter.
What's next
ShuttleSCP is still a public beta, and I'm still learning Rust by reading what each change does. Next is more testing on real servers, and native-speaker reviews of the translations. If you try it, I'd like to hear what breaks, good or bad.
- Download: [shuttlescp.dsalvado.ca](https://shuttlescp.dsalvado.ca/]
- Code (GPL-3.0):github.com/dsalvado/shuttlescp
