Skip to content

Releases: shreeve/transfer

Transfer 0.3.1

Choose a tag to compare

@shreeve shreeve released this 28 Sep 04:13
  • In column view, Command-click and Shift-click select several folders in a column, as in Finder, so they can be copied, dragged, or deleted together; no column opens beside them.

Install or update with one command:

curl -fsSL https://raw.githubusercontent.com/shreeve/transfer/main/Scripts/install.sh | bash

Installed copies update themselves through Transfer → Check for Updates…

Transfer 0.3.0

Choose a tag to compare

@shreeve shreeve released this 27 Sep 02:37

A second pass over the whole app. It closes a way for a server's script to run on a double-click and a way for a ProxyJump host to get a saved password, fixes rare ways to lose an edit or an original, and changes a few behaviors (listed below). The library's format does not change: 0.2.0 and 0.1.7 still open it.

Data safety

  • A move no longer removes an original when a file that looks like its copy reached the destination during the copy and you chose Skip: a file counts as the copy only where this move wrote it.
  • A Live save made while a move between servers is copying keeps the original, including a save whose reply was lost.
  • A Live upload no longer makes a document-based editor, such as TextEdit, save text you had not saved yet and upload it.
  • A Live save no longer replaces another writer's file that happens to have the edit's size and modification second; that is a conflict. An edit refused because the server's file changed can no longer be marked synced later without having uploaded.
  • Reopening a Live file that an editor re-saved unchanged picks up the server's newer version, instead of leaving you to edit old bytes into a conflict.
  • Live files survive a moved or restored library folder: unsynced edits are no longer forgotten, and a damaged record can never delete anything outside Transfer's Live folder.
  • A file a replace set aside, including one Transfer 0.2.0 set aside, is never deleted if you go back to Transfer 0.1.7.
  • A folder listing no longer stops at a page holding only names Transfer drops (such as . and ..), so a folder copy no longer reports success with files missing.
  • A file shortened on the server while it downloads is read again instead of being saved short.
  • Moving a folder onto a folder of the same name that is the folder itself, reached another way (a bind mount, or one network share at two paths), is refused instead of copying it onto itself and removing the only copy.
  • Pasting a Mac folder into a folder inside itself, through a server that reaches this Mac's disk, is refused instead of copying itself about a hundred levels deep.

Security

  • A .command or script opened Live no longer runs in Terminal without Gatekeeper's check: Live copies are quarantined like every other download, and a file whose default app would run it opens in the text editor (see Behavior changes). A Live .command or .tool, which Gatekeeper lets no app open while quarantined, is instead written without the execute bit.
  • A saved password answers only the prompt ssh itself writes for that server, so a ProxyJump host can no longer get it by asking for it by name.
  • A password typed into a login sheet that was withdrawn, and its Save in Keychain, no longer fill in the next login sheet, which could be another server's.
  • A server that lists wrong or missing sizes can no longer fill the disk: copying for the Finder stops staging at 1 GB actually received, and the inspector previews a file only up to 8 MB whatever its listing said. A huge listed size no longer crashes a copy or a drag to the Finder.
  • The launch removes a leftover download temp only when it is a file with a temp's name, so a copied or edited library cannot make it delete a folder. Copies passing through this Mac, between servers or on a server without copy-data, go through a folder in Transfer's caches that each launch empties, instead of $TMPDIR.
  • A name holding a slash followed by a combining accent is refused as a file name, as any name with a slash is.
  • xfer shows C1 control characters in a file name as ?, as it does other control characters, so a crafted name cannot drive the terminal.

Logins and reliability

  • A window that moves away or closes during a login no longer stops it for another window waiting on the same server; that window is asked instead.
  • After a server is edited or removed, windows, queued transfers, and pastes from it use its current settings: the old login can no longer start again and end the new one. Saving a server without changes no longer cancels a login in progress.
  • Clicking a star, Live file, or conflict while the window logs in to another server no longer cancels that login.
  • The title names the server as soon as its login lands, and a Live conflict found during the login still gets its sheet.
  • Choosing Trust after leaving a host-key question up for a long time no longer fails the login with a timeout, and Cancel during the host-key check ends the login in about a second, not two.
  • A connection that drops at the very end of a login is retried instead of failing.
  • Browsing quickly no longer leaks the server's folder and file handles until it stops listing folders.
  • Retrying a move on one server after the connection dropped or timed out during a rename no longer fails on an item that had already moved, and the timeout ends the move instead of starting a copy.
  • An item a move kept because it changed during the move says to move it again: Retry keeps it again.
  • One file that changes on the server during a folder copy fails alone and is retried, instead of restarting the whole copy.
  • A library file whose version is damaged is refused with a message instead of crashing Transfer at every launch, and a saved server with a damaged id no longer shows up as one that cannot be edited or removed.
  • Right-clicking a folder that is not selected in column view opens its menu, instead of failing and leaving the view out of step. The right-click menu's Quick Look and Rename act on the item clicked, and Open Live works on a link to a file.
  • Escape in the search field gives the keyboard back to the files, and Command-F with the search item in the toolbar's overflow no longer turns off Return and Space. Clicking back into a search field that holds text keeps Space, Return, and Command-Delete in the field instead of acting on the files.
  • Finder's Stop always stops a drag's download.
  • Choosing Keep Local again after its upload's reply was lost clears the conflict, instead of raising it again over your own bytes.
  • Retry on a Live conflict's row keeps the conflict on the shelf, and two refused Live discards each ask in turn.
  • An Extensions edit made just before closing Settings is saved.
  • View → Clear Preview Cache works in a window with no server open.
  • Go to Remote Folder refuses a path holding a control character, instead of sending a NUL that ended the server's SFTP channel.
  • A server's error message shows as one short line, and a failed case-only rename shows the real reason.
  • The inspector names a transfer's state as the shelf does.

Performance

  • A preview, a view, or a Live open no longer waits behind a folder copy.
  • Copies return to full speed about 10 s after the server refused an extra channel (for example, while a Terminal tab was open), instead of staying slow to the end.
  • Select All in a large folder followed by a filter keystroke no longer stalls the window.
  • An editor slow to answer no longer stalls Live sync for every server.
  • Quick Look no longer fetches a large UTF-8 text file whole when its first 512 KB end inside a character.

Behavior changes

  • Every download is quarantined, Live working copies included, so macOS checks an app or script from a server before it first runs. A Live .command or .tool is the exception, as macOS would let no editor open it; it is written private and not executable instead.
  • A file whose default app would run it (Terminal, iTerm2, Ghostty, Python Launcher, or Jar Launcher) opens in the default text editor instead, whether opened Live or to view. Choosing such an app when Transfer asks which app opens a type no longer makes it the Mac's default for that type.
  • Copies opened to view or preview are read-only, so an editor says the file is locked instead of saving edits that never upload and are later overwritten.
  • A move on one server onto a name the destination already holds asks Replace, Keep Both, or Skip, instead of failing.
  • Saved servers are listed in Finder's order.
  • In icon view, Shift-click adds or removes an item, and clicking one of several selected items selects just that one, as in Finder. In list and icon view, once a folder has listed, the selection keeps only the items shown, so a filter never leaves items selected out of sight.
  • A click or drag on an item in a background window selects or drags it at once, as in Finder, instead of only bringing the window forward.
  • Rename selects a file's name up to its extension, as Finder does; a folder's whole name stays selected.
  • A folder's shortcut menu no longer offers Open Live or Quick Look.
  • The inspector shows the time in the Mac's own clock format, for example 24-hour; the date stays year-month-day.
  • Command-Delete in the filter or rename field edits the text instead of offering to delete files.
  • Open in Terminal says why when it does nothing.
  • Compare is offered for a Live conflict only up to 16 MB, so a huge file is no longer read whole.
  • Downloaded files take the server's permissions under your umask, as sftp get does.
  • config.json is written from the built-in defaults on first launch; the app no longer bundles a copy.

For maintainers

  • A TransferUITests target drives the real window model against fake sessions and a fake library; swift test runs it without a server.
  • With the local sshd, the full test run takes about 18 s instead of about 75: EditorMatrix, MoveServerTests, and ThroughputServerTests run their cases in parallel.
  • A file a replace set aside is recorded in the temps table under the owner <uuid> aside, which 0.1.7 and 0.2.0 never select; records 0.2.0 wrote are still read. There is no schema change.

Install or update with one command:

curl -fsSL https://raw.githubusercontent.com/shreeve/transfer/main/Scripts/install.sh | bash

Installed copies update themselves through Transfer → Check for Updates…

Transfer 0.2.0

Choose a tag to compare

@shreeve shreeve released this 25 Sep 01:54

A revamp of the whole app. Transfer looks much the same, but what it does with your files is safer, logins are sturdier, copies are much faster, and a few behaviors change (listed below). The library upgrades itself on first launch.

Data safety

  • A move removes an original only once it has checked that this move wrote a complete copy. A file of the same name, size, and time already at the destination no longer passes for the copy: it is asked about, and an original the user did not replace it with stays. Two saved servers that reach one disk, or a destination that is the originals' own folder reached another way, never copy an item onto itself and delete it. A folder holding a socket or FIFO, which no copy can write, keeps its original. Two items of one move with the same name (p/VERSION and q/VERSION) never count each other's copy. Between servers a move removes only what it checked: anything added to the original or changed after the check stays and is named, and so does an original whose Live file saved during the move.
  • Live files never lose edits the server lacks: a delete or move of a folder above one, Discard, Forget Synced Live Files, a conflict choice, and Remove Server all leave unsynced bytes alone unless you confirm. Delete discards only the edits its sheet named, and only after the server's delete succeeded. Keep Local and Keep Both wait until the working copy holds still, Keep Remote or Keep Both never overwrite a save made after the choice, and Keep Remote on a file removed from the server since keeps both copies. A save by someone else with the same size and second is no longer taken for your own upload, which left your edit marked synced and never uploaded.
  • A server's names can no longer reach outside the folder you chose: a name that is not one path component is dropped from listings, and a link on the Mac is never followed or removed while downloading. A server link that met a local folder of the same name used to delete that folder.
  • A change of case alone (a.txt to A.txt) no longer replaces a different file on a case-sensitive server. A server without posix-rename keeps the old file until the new one is in place. A name that appears after a folder was listed is never overwritten without asking, and a Replace removes only what you were asked about: a file that took the name while the question was up stays.
  • A folder copy into itself through a link on the server is refused; it used to copy until the disk filled.
  • A temp that could not be removed stays recorded, and the next login removes it. On a server without posix-rename, a file a replace set aside is recorded too, and the next login puts it back.
  • A download checks the file it opened against its listing, so a file that changed on the server since is read again, never placed cut short.
  • Quit asks when Transfer cannot tell within 3 s whether Live edits are unsynced (it used to quit without asking), and names transfers that have not finished, closed windows' included; its question comes to the front.
  • Stars, Live files, and temps whose names are not UTF-8 keep their exact bytes.

Security

  • Downloads are quarantined as a browser's are, so Gatekeeper checks an app or script before it first runs, and never take setuid, setgid, or sticky bits from the server.
  • A saved password or passphrase answers only that server's own prompt, never a ProxyJump host's. A saved key passphrase is stored apart from the server's password, so it is never sent to the server as a password.
  • A channel whose login has gone fails at once instead of logging in on its own with the default port and keys.
  • A drop honors remote paths only from a drag that began in Transfer, so another app cannot have a drop rename files on your server.
  • An sftp:// link with a control character in its path is refused, and Copy Remote URL brackets an IPv6 host.
  • Open in Terminal refuses a folder name holding a control character, which would have run the rest of the line as a command.
  • A malformed, silent, or non-reading server fails its channel with a clear error instead of hanging it or filling memory.
  • The installer installs only an app signed with Transfer's Developer ID and accepted by Gatekeeper as notarized, and follows redirects only over HTTPS.

Logins and reliability

  • ssh checks host keys itself, so keys known through a global known-hosts file, @cert-authority, or KnownHostsCommand log in without a question, a known host costs one connection instead of two, and a @revoked key is refused. Always Trust honors HashKnownHosts.
  • Two windows or a retry logging in to one server share one login instead of asking twice and leaving a stray master behind. Cancel on any login question stops the login at once, with no error, and a window that moves to another server takes back that login's questions. Always Trust fails with a message when ssh -G cannot read the configuration, instead of trusting for one login only. A login no longer runs a remote performance-version command.
  • ControlPersist, ForkAfterAuthentication, RemoteCommand, RequestTTY, LocalForward, and PermitLocalCommand in ~/.ssh/config no longer break channels, hold your forwarded ports, or leave a master Transfer cannot stop.
  • A server whose shell prints text before SFTP starts fails with a clear error within 15 s, and one that stops answering for 60 s fails its requests, which then retry.
  • Every sftp:// link in a burst gets its own window; one could be lost before.
  • The column view no longer crashes when a drag crosses the area beyond its last column.
  • A paste or folder copy goes on past an item that fails and names every failure at the end.
  • A file whose name nearly fills the 255-byte limit copies both ways, and a Live file with such a name still gets its (server) copy.
  • Copy Remote URL writes a name that is not UTF-8 byte for byte, and such a link opens that file. A server's error is no longer labeled twice ("Permission denied: Permission denied").
  • A sheet never replaces another: one that arrives meanwhile waits its turn. A sheet about a server closes when the window leaves it, and Upload…, Download Copy…, and pastes act on the server they were started on or do nothing and say so.
  • The rename bar's field takes the focus with Full Keyboard Access on, and emptying the search field keeps its focus, so Return no longer starts a rename.
  • An unreadable config.json is kept as config.json.bak before the defaults take over.

Performance

Measured at 20 ms round trip against 0.1.7:

  • 2,000 small files: download 151 → 2,221 files/s, upload 44 → 1,616, copy on the server 60 → 1,488. Small files share a data channel, sixteen at a time, and a file's requests go out together.
  • One 256 MB file: download 83 → 306 MB/s, upload 83 → 292 MB/s. A file of 8 MB or more uses up to four data channels.
  • Listing 10,000 names: 670 → 224 ms.
  • Column view with 10,000 items: Select All took one to ten minutes and takes about 12 ms; opening the column sorted by date, 3.3 s → 0.1 s.
  • The inspector fetches only the first 64 KB of a text file it previews, and progress reaches the window ten times a second instead of once per 64 KB.

Behavior changes

  • A name collision with no window left to ask fails its operation instead of skipping the file.
  • Dragging between folders on one server moves; Option-drag copies (it used to move). A drag from another server's window copies (it used to be refused).
  • Right-clicking an empty area acts on that folder: the list and icon views clear the selection, and the column view makes that column's folder the location.
  • Command-Down and Space act once when held.
  • A transfer's shelf row offers Stop and Restart, since a stopped transfer starts its current file over; a Live file's row keeps Pause and Resume.
  • Duplicate works on folders and links, not only files, and a folder keeps its whole name: v1.2 copy, and v1.2 2 for Keep Both.
  • Keep Both for a Live conflict names the Mac's copy notes (from this Mac).txt, so it keeps its extension.
  • A move that kept originals reads Kept on the shelf, in grey, with the reason. Later on a Live conflict keeps its sheet away until you click its sidebar row.
  • Opening a link follows the whole chain, as the server resolves it, not one hop.
  • Errors stay in a bar at the top of the window until dismissed; a successful listing used to clear them at once.
  • A paste or move between servers of a folder holding two names this Mac's disk cannot tell apart (README and readme, or Straße and STRASSE) keeps that item and says why; such a copy is not staged for the Finder either.
  • Go to Remote Folder understands ~ (the server's start folder), ~/path, relative paths, and ...
  • The list view keeps Name as its first column.
  • Transfer refuses to open a library written by a newer version, says why, and quits.
  • Only one copy of Transfer opens a library at a time; a second copy says so and quits. (Two copies swept each other's login files and temps and could end each other's connections.)
  • The installer stops, changing nothing, on a Mac where Gatekeeper's assessments are turned off.

The library

transfer.sqlite now carries a schema version and upgrades itself on first launch, one step at a time: version 1 is the 0.1.0–0.1.7 schema; version 2 drops the unused recents table; version 3 changes no row and marks that paths are kept as the server's exact bytes, as text when they are UTF-8 (as before) and as bytes only when not. The file switches to WAL mode, so transfer.sqlite-wal and transfer.sqlite-shm appear beside it. Transfer 0.1.7 can still open an upgraded library, and still unstars and forgets what it finds there. The preview cache is keyed per server, so existing previews are fetched again once, and clipboard staging moves into one Staging folder, emptied at launch.

For maintainers

  • docs/SPEC.md replaces PLAN.md; HANDOFF.md and AGENTS.md describe the code ...
Read more

Transfer 0.1.7

Choose a tag to compare

@shreeve shreeve released this 24 Sep 07:52

Transfer for macOS 27 (Apple Silicon), ad-hoc signed.

Install or update with one command:

curl -fsSL https://raw.githubusercontent.com/shreeve/transfer/main/Scripts/install.sh | bash

A copy downloaded in a browser is quarantined: allow it under System Settings → Privacy & Security → Open Anyway, or run xattr -dr com.apple.quarantine Transfer.app. Installed copies update themselves through Transfer → Check for Updates…

Transfer 0.1.6

Choose a tag to compare

@shreeve shreeve released this 24 Sep 05:50

Transfer for macOS 27 (Apple Silicon), ad-hoc signed.

Install or update with one command:

curl -fsSL https://raw.githubusercontent.com/shreeve/transfer/main/Scripts/install.sh | bash

A copy downloaded in a browser is quarantined: allow it under System Settings → Privacy & Security → Open Anyway, or run xattr -dr com.apple.quarantine Transfer.app. Installed copies update themselves through Transfer → Check for Updates…

Transfer 0.1.5

Choose a tag to compare

@shreeve shreeve released this 23 Sep 23:44

Transfer for macOS 27 (Apple Silicon), ad-hoc signed.

Install or update with one command:

curl -fsSL https://raw.githubusercontent.com/shreeve/transfer/main/Scripts/install.sh | bash

A copy downloaded in a browser is quarantined: allow it under System Settings → Privacy & Security → Open Anyway, or run xattr -dr com.apple.quarantine Transfer.app. Installed copies update themselves through Transfer → Check for Updates…

Transfer 0.1.4

Choose a tag to compare

@shreeve shreeve released this 23 Sep 22:50

Transfer for macOS 27 (Apple Silicon), ad-hoc signed.

Install or update with one command:

curl -fsSL https://raw.githubusercontent.com/shreeve/transfer/main/Scripts/install.sh | bash

A copy downloaded in a browser is quarantined: allow it under System Settings → Privacy & Security → Open Anyway, or run xattr -dr com.apple.quarantine Transfer.app. Installed copies update themselves through Transfer → Check for Updates…

Transfer 0.1.3

Choose a tag to compare

@shreeve shreeve released this 23 Sep 03:55

Transfer for macOS 27 (Apple Silicon), ad-hoc signed.

Install or update with one command:

curl -fsSL https://raw.githubusercontent.com/shreeve/transfer/main/Scripts/install.sh | bash

A copy downloaded in a browser is quarantined: allow it under System Settings → Privacy & Security → Open Anyway, or run xattr -dr com.apple.quarantine Transfer.app. Installed copies update themselves through Transfer → Check for Updates…

Transfer 0.1.2

Choose a tag to compare

@shreeve shreeve released this 23 Sep 03:03

Transfer for macOS 27 (Apple Silicon), ad-hoc signed.

Install or update with one command:

curl -fsSL https://raw.githubusercontent.com/shreeve/transfer/main/Scripts/install.sh | bash

A copy downloaded in a browser is quarantined: allow it under System Settings → Privacy & Security → Open Anyway, or run xattr -dr com.apple.quarantine Transfer.app. Installed copies update themselves through Transfer → Check for Updates…

Transfer 0.1.1

Choose a tag to compare

@shreeve shreeve released this 23 Sep 02:22

Transfer for macOS 27 (Apple Silicon), ad-hoc signed.

Install or update with one command:

curl -fsSL https://raw.githubusercontent.com/shreeve/transfer/main/Scripts/install.sh | bash

A copy downloaded in a browser is quarantined: allow it under System Settings → Privacy & Security → Open Anyway, or run xattr -dr com.apple.quarantine Transfer.app. Installed copies update themselves through Transfer → Check for Updates…