Skip to content

Releases: aldohushi1-stack/shipback-sftp

0.2.1: never write through a symlink on the server

Choose a tag to compare

@aldohushi1-stack aldohushi1-stack released this 20 Oct 04:42

Never write through a symlink on the server. A second outside review of 0.2 found a real hole in promise line P13. Paths in backup records were checked as strings, but a symlinked folder inside the site root would still carry a deploy or restore write to wherever it pointed. I reproduced it on a real filesystem and on a cPanel host.

  • Deploy, its dry run and plain uploads now refuse, before anything changes, any path that passes through a symlinked folder below the site root, or whose target is a symlink or a special file. The error names the path.
  • Restore treats such paths as conflicts that --force can't override.
  • The site root itself may still be a symlink (cPanel's www → public_html is normal).

P13 now says this, with a test that runs against a real SFTP server and disk. Breaking the check on purpose makes that test fail.

Also in the README: if the system reuses a dead process's ID, its lock just waits out the 2-minute timeout. A run that's still going is never mistaken for a finished one.

120 tests on Linux, macOS and Windows × Node 18, 20 and 22.

npm install -g shipback-sftp@0.2.1

0.2.0: the undo promise

Choose a tag to compare

@aldohushi1-stack aldohushi1-stack released this 20 Oct 03:46

The undo promise. UNDO-PROMISE.md now says exactly what --restore guarantees, in 13 lines. Each line has its own test in test/promise.test.js, and if undo ever breaks one, that's the first bug I fix. There's an Undo didn't work issue form for it.

Writing the promise down meant trying to break it first, on a real cPanel host. That found real gaps, all fixed in this release:

  • Never half a file. Files go up as .shipback-part-<name> beside the live file and are swapped in whole with posix-rename, keeping the old file's permissions. Before, a deploy cut off mid-upload left a truncated file live, and undo then refused because the file looked like a live edit.
  • A dropped connection fails out loud. ssh2 drops pending SFTP callbacks when the line goes. In 0.1 that made the CLI exit 0 with nothing printed, and the MCP tool call never returned. Now every pending operation fails, and the error says how many files went up and how to undo them.
  • Cut-off runs are handled. .backup.json says saving until every backup copy is made, so a run cut off before that is known to have changed nothing. A restore that is cut off is finished by running --restore again.
  • Checked backup copies. Records now carry a SHA-256 of every saved copy, and a copy that has been damaged since is never written back, even with --force.
  • One run at a time. Deploy, upload, restore and prune hold .shipback-lock (O_EXCL, refreshed every 30 s) while they write. A second run is turned away with nothing changed and exits with code 5. A lock left by a cut-off run clears itself: straight away if its process on this computer has ended, otherwise after 2 minutes.
  • Undo stays inside the site. A backup record naming any path outside the site folder, or one that can't be read, is refused instead of being restored without checks.

119 tests on Linux, macOS and Windows × Node 18, 20 and 22. Each safeguard was checked by breaking it on purpose and watching its promise test fail. The whole set also ran on a real cPanel host, including a connection cut mid-deploy and two deploys started at once.

Backups made with 0.1 still restore, but they don't carry the checksums behind P4 and P8.

npm install -g shipback-sftp@0.2.0

Shipback SFTP 0.1.0

Choose a tag to compare

@aldohushi1-stack aldohushi1-stack released this 18 Oct 09:49

First public release. Shipback deploys a folder to any SFTP host (cPanel, Plesk, DirectAdmin, a plain VPS) and every deploy can be undone.

What's in it

  • Deploy with server-side backups. The server's copy of every file a deploy overwrites or deletes goes into _backup-<time>/ on the server before the upload starts, with a .backup.json record of what was added and a SHA-256 of every file left behind.
  • --restore undoes a deploy: puts the old files back and removes the ones it added. If a live file was edited after that deploy, it stops, names the file and changes nothing (exit code 4). --force overrides, and even then the edited copy is backed up first. A restore backs up what it replaces, so it can be undone too.
  • --backups lists each backup and what that run overwrote, added and deleted.
  • MCP server (shipback-mcp, stdio). Deploy and restore only run a plan the agent previewed first (planId); if anything changed in between, they refuse. There are no tools to mirror/delete server files, skip the backup or prune backups. Passwords stay in the OS keychain and never reach the model.
  • Drop-box queue for agents that can write files but can't run commands: write queue/<name>.request.json, read back <name>.result.json.
  • Claude Code guard hook (bin/claude-guard.js) that blocks --mirror and --no-backup in Claude's shell commands, queue files and API calls.
  • CI on Linux, macOS and Windows × Node 18, 20 and 22, 106 tests, including end-to-end runs against a real SFTP server started inside the test run.

How proven is it

The deploy engine underneath has shipped aldo.today 235 times since September 2026. Restore and the MCP server are new in this release. Restore, the same-minute backup folders and the conflict refusal were checked on a real cPanel host, but they haven't had months of use yet. Please open an issue if something looks wrong.

Install

git clone https://github.com/aldohushi1-stack/shipback-sftp.git
cd shipback-sftp && npm install

Needs Node 18+. MIT licence.