{"slug": "show-hn-shipback-sftp-deploys-your-ai-agent-can-undo", "title": "Show HN: Shipback – SFTP deploys your AI agent can undo", "summary": "Developer aldohushi1-stack released Shipback 0.1, an open-source Node 18+ command-line tool and MCP server that deploys a folder to any SFTP host — cPanel, Plesk, DirectAdmin or a plain VPS — and backs up every file it overwrites into a `_backup-<time>/` directory on the server so each deploy can be undone with a single `--restore` command. Shipback refuses to restore over a file edited on the server after the deploy unless `--force` is passed, and its MCP server, usable from Claude Code, Cursor or any MCP client, has no switch to delete server files or skip the backup. The author says the underlying deploy engine has shipped aldo.today 235 times since September 2026, covering 2,907 files to a cPanel host, while restore and the MCP server are new in version 0.1.", "body_md": "**Let the agent deploy. Keep the undo.**\n\nIt's late, and the agent says \"Deployed ✓\". Then the client's contact form is gone. The agent overwrote a\nfile someone had fixed by hand in cPanel last week, and shared hosting has no `git revert`.\n\nShipback deploys a folder to any SFTP host: cPanel, Plesk, DirectAdmin, a plain VPS. Every deploy can be undone with one command. You can run it yourself, script it, or hand it to Claude Code, Cursor or any MCP client.\n\n- **Backup before every overwrite.** The server's copy of each file a deploy replaces is saved into`_backup-<time>/` on the server, before the upload starts.\n- **One-command undo.**`--restore` puts those files back and removes the files that deploy added.\n- **It won't undo over a live edit.** If someone changed a file on the server after the deploy, restore\nstops and names the file. Nothing is touched unless you pass`--force` , and even then the edited copy is\nbacked up first.\n- **Restores can be undone too.** A restore backs up what it replaces, the same way a deploy does.\n- **Safe for agents.** The MCP server has no switch to delete server files or skip the backup. A deploy\nonly runs the exact plan the agent previewed: if a file changed in between, it refuses.\n- **Spots the drift.**`--pull --dry-run` shows what changed on the server since your last deploy, so\nedits made live in cPanel don't get overwritten by accident.\n\nNo git or SSH shell is needed on the server, just SFTP. It needs Node 18+. The tests run on Windows, macOS and Linux with Node 18, 20 and 22 on every push. Passwords are kept in the OS keychain and never pass through the model.\n\n**It has run in production.** The deploy engine underneath has shipped [aldo.today](https://aldo.today) 235 times\nsince September 2026 (2,907 files to a cPanel host). Restore and the MCP server are new in 0.1.\n\n```\nnpm install -g shipback-sftp          # installs the shipback and shipback-mcp commands\ncd ~/sites                            # any folder with a sites.json in it (format below)\nshipback mysite --dry-run             # see the plan; asks for the password once and stores it\nshipback mysite                       # deploy\nshipback mysite --restore             # changed your mind? undo it\n```\n\nNo global install? `npx -p shipback-sftp shipback mysite --dry-run` works the same way. `--sites <file>`\npoints at a `sites.json` in another folder.\n\nFrom source: `git clone https://github.com/aldohushi1-stack/shipback-sftp.git`, `npm install`, then\n`node bin/cli.js` wherever these docs say `shipback`.\n\n`sites.json`, one entry per site:\n\n```\n{\n  \"sites\": [\n    {\n      \"name\": \"mysite\",\n      \"host\": \"server123.example-host.com\",\n      \"port\": 22,\n      \"username\": \"cpaneluser\",\n      \"remoteRoot\": \"/home/cpaneluser/public_html\",\n      \"localDir\": \"/Users/you/sites/mysite\",\n      \"ignore\": [\"drafts/\", \"*.psd\"]\n    }\n  ]\n}\n```\n\nMany shared hosts run SFTP on their own port rather than 22 (check *SSH Access* in your control panel),\nand some only accept connections from allow-listed IPs. If a connection times out, check those two\nfirst. Use `\"privateKeyPath\"` instead of a password if your host has your key.\n\n```\nshipback <site> --dry-run                 # what a deploy would upload\nshipback <site>                           # deploy: backup, upload, record\nshipback <site> --local <folder>          # deploy a different folder than localDir\nshipback <site> --pull --dry-run          # what changed on the server since the last deploy?\nshipback <site> --pull                    # bring those changes down (local copies backed up first)\nshipback <site> --backups                 # list backups: what each run overwrote, added, deleted\nshipback <site> --restore --dry-run       # what an undo of the latest deploy would do\nshipback <site> --restore [<backup>]      # undo it (or name an older _backup-… folder)\nshipback <site> --prune --keep 10         # tidy old backups; refuses any holding the only copy of a file\nshipback --history [site]                 # what went up, when, from where\n```\n\nAdd `--json` to any command for machine-readable output. A refused restore exits with code 4.\n\n`--mirror` (delete server files that aren't in the local folder) and `--no-backup` exist for humans who\nknow they want them. The agent tools don't offer them, and the [guard hook](#claude-code-guard-hook)\nblocks Claude from typing them.\n\nEach deploy writes `_backup-YYYY-MM-DD-HHMM/` in the remote root. It holds:\n\n- the server's copy of every file the deploy **overwrote** or**deleted**\n- `.backup.json` , a record of what the deploy**added** , and a SHA-256 of every file it left on the server\n\n`--restore` reads that record and checks each live file first:\n\n| Live file now | Restore does | \n|---|---|\n| exactly what the deploy uploaded | puts the old copy back (or removes it, if the deploy added it) | \n| already the old copy | nothing; it counts as already right | \n| different from both | **conflict** : stops, names the file, changes nothing (`--force` overrides) | \n\nBefore writing anything, restore saves the current live files into a new `_backup-…` folder with its own\nrecord, so `--restore` on that folder takes you forward again. It also clears the restored paths from\n`.deploy-manifest.json`, so the next deploy compares them by content instead of trusting stale hashes.\n\nBackup folders are skipped by deploys and pulls, and are never deleted except by `--prune`.\n\n`shipback-mcp` (`bin/mcp.js`) is an MCP server over stdio.\n\n**Claude Code**\n\n```\nclaude mcp add shipback-sftp -- npx -y -p shipback-sftp shipback-mcp --sites /path/to/sites.json\n```\n\n**Cursor, Claude Desktop, and others** (`mcp.json` / `claude_desktop_config.json`):\n\n```\n{\n  \"mcpServers\": {\n    \"shipback-sftp\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"-p\", \"shipback-sftp\", \"shipback-mcp\", \"--sites\", \"/path/to/sites.json\"]\n    }\n  }\n}\n```\n\nOn Windows, if the client can't start `npx`, use `\"command\": \"cmd\"` with `\"/c\", \"npx\"` at the front of\n`args`. After a global install, `\"command\": \"shipback-mcp\"` works too, and from a clone it's `node` with\n`bin/mcp.js` as the first arg.\n\nRun any CLI command for a site once first (for example `shipback mysite --backups`). That stores\nthe password in the OS keychain, where the MCP server reads it. The model never sees it. For CI you can set\n`SFTP_PASSWORD` in the server's environment instead.\n\n| Tool | Does | \n|---|---|\n| `list_sites` | the configured sites (no passwords) | \n| `preview_deploy` | what would upload; returns a `planId` | \n| `deploy` | uploads that plan; refuses if anything changed since the preview | \n| `check_server_changes` | files edited or added on the live server since the last deploy | \n| `pull` | brings those down into the local folder (local copies backed up) | \n| `list_backups` | backups, newest first, with what each run changed | \n| `preview_restore` | what an undo would put back and remove, plus any conflicts; returns a `planId` | \n| `restore` | runs that undo; refuses on conflicts unless `force` | \n| `history` | recent deploys, pulls and restores | \n\nWhat the tools leave out is deliberate: there is no mirror/delete, no way to skip the backup, and no pruning of backups.\n\nSome agent setups can write files but can't run commands. While the local app is running\n(`npm start`, or `start.bat` on Windows), anything that can write a file can queue a job:\n\n```\nqueue/<name>.request.json   { \"action\": \"deploy\", \"site\": \"mysite\", \"dryRun\": true, \"note\": \"new homepage\" }\nqueue/<name>.result.json    ← written when it finishes, with a .log.txt beside it\n```\n\nActions: `deploy`, `preview`, `pull`, `restore`, `backups`, `prune`.\n\n`npm start` opens a small UI on `http://127.0.0.1:4646`: add sites, preview, deploy, pull, drag in\none-off uploads, browse the server, and see history. It only answers requests from your own machine.\n\nIf Claude also runs the CLI directly, add `bin/claude-guard.js` as a PreToolUse hook. It blocks\n`--mirror` and `--no-backup` in shell commands, queue files and API calls, and tells Claude why. You can\nstill run them yourself in a terminal. After `npm install -g shipback-sftp`, the file is in the folder\n`npm root -g` prints, under `shipback-sftp/bin/`.\n\n```\n{\n  \"hooks\": {\n    \"PreToolUse\": [\n      {\n        \"matcher\": \"Bash|PowerShell|Write|Edit|MultiEdit\",\n        \"hooks\": [{ \"type\": \"command\", \"command\": \"node /path/to/shipback-sftp/bin/claude-guard.js\" }]\n      }\n    ]\n  }\n}\nnpm test\n```\n\nThe suite uses `node:test` and needs no network. Deploy, restore, the CLI and the MCP server are each\nalso exercised end to end against a real SFTP server started inside the test run.\n\n- It compares by size and SHA-256. The first deploy to a site that already has files reads the server copies of any same-size files to compare them.\n- Backups live on the same server as the site. They protect against bad deploys, not against losing the hosting account. Keep your host's own backups too.\n- Files over 8 MB with the same size on both sides are assumed unchanged when there is no manifest hash to compare.\n\nMIT © 2026 Aldo Hushi\n\nRuns aldo.today. Made at [BlueprintAU](https://blueprintau.com).", "url": "https://wpnews.pro/news/show-hn-shipback-sftp-deploys-your-ai-agent-can-undo", "canonical_source": "https://github.com/aldohushi1-stack/shipback-sftp", "published_at": "2026-10-18 11:48:48+00:00", "updated_at": "2026-10-18 12:17:56.290681+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "developer-tools", "agent-protocols"], "entities": ["Shipback", "aldohushi1-stack", "Claude Code", "Cursor", "aldo.today", "cPanel", "Plesk", "DirectAdmin"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/show-hn-shipback-sftp-deploys-your-ai-agent-can-undo", "markdown": "https://wpnews.pro/news/show-hn-shipback-sftp-deploys-your-ai-agent-can-undo.md", "text": "https://wpnews.pro/news/show-hn-shipback-sftp-deploys-your-ai-agent-can-undo.txt", "jsonld": "https://wpnews.pro/news/show-hn-shipback-sftp-deploys-your-ai-agent-can-undo.jsonld"}}