
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
A tiny rsync-style tool using SFTP + SHA-256 diffing. Push only changed files to remote servers.
A tiny rsync-style tool built for Node/TypeScript using SFTP + SHA-256 diffing.
It lets you sync only changed files to a remote server over SSH — no rsync or native binaries required. Works on Windows, macOS, and Linux.
🚀 Three simple commands
sync - Pull remote state + push local changes (one command to rule them all)pull - Download remote file list and create/update manifestpush - Upload only changed files based on manifest🔄 Smart auto-pull
No manifest? The sync command automatically pulls from remote first.
🗑️ Optional remote deletion
Use --delete (or XSYNC_DELETE=1) to remove remote files not present locally.
🔐 SSH support
.ppk)⚙️ Flexible configuration
xsync.config.js or xsync.config.ts).env file support📦 Bundled CLI (single JS file via esbuild)
In your project (the one you want to sync from):
npm install -D x-rsync ssh2-sftp-client
Add to your package.json:
{
"scripts": {
"sync": "x-rsync sync ./dist",
"pull": "x-rsync pull",
"push": "x-rsync push ./dist"
}
}
Or just the essentials:
{
"scripts": {
"sync": "x-rsync sync ./dist"
}
}
You can configure x-rsync using either a config file (recommended) or environment variables.
Create xsync.config.js in your project root:
export default {
host: "your.server.ip",
user: "root",
port: 22,
remoteDir: "/var/www/website",
privateKeyPath: "C:/Users/you/.ssh/id_rsa",
// OR use password:
// password: "your_password",
// Optional settings:
delete: false, // set to true to delete remote files not present locally
fast: false, // set to true to skip hashing (compare size+mtime only)
// Exclude/Include patterns (glob syntax):
exclude: [
"node_modules/**",
"config/**",
"*.log"
],
include: [
"config/production.json" // include this even if excluded by exclude patterns
]
};
Or use TypeScript (xsync.config.ts):
export default {
host: "your.server.ip",
user: "root",
port: 22,
remoteDir: "/var/www/website",
privateKeyPath: "~/.ssh/id_rsa",
delete: false,
fast: false,
exclude: ["node_modules/**", ".git/**"],
include: ["config/production.json"]
};
Set these before running sync. Environment variables override config file settings.
Minimum:
XSYNC_HOST=your.server.ip
XSYNC_USER=root
XSYNC_REMOTE_DIR=/var/www/website
Authentication (choose ONE):
# Using a private key (recommended)
XSYNC_PRIVATE_KEY_PATH=C:/Users/you/.ssh/id_rsa
# OR using a password
XSYNC_PASSWORD=your_password
Optional:
XSYNC_PORT=22
XSYNC_DELETE=1 # enable deletes during sync
XSYNC_EXCLUDE="node_modules/**,.git/**,*.log" # comma-separated glob patterns
XSYNC_INCLUDE="config/production.json" # comma-separated glob patterns
Create a .env file in your project root:
XSYNC_HOST=your.server.ip
XSYNC_USER=root
XSYNC_REMOTE_DIR=/var/www/website
XSYNC_PRIVATE_KEY_PATH=~/.ssh/id_rsa
x-rsync provides three commands:
sync - The all-in-one command (recommended)x-rsync sync <localDir>
Combines pull + push into one command:
pull to download remote file listpush to upload changed filesExample:
npm run sync # sync current directory
npm run sync -- ./dist # sync ./dist directory
pull - Download remote file listx-rsync pull
Connects to your remote server, scans all files, and creates/updates .xsync/manifest.json. Use this when:
push - Upload changed filesx-rsync push <localDir>
Scans your local directory, compares with manifest, and uploads only changed files. Use this when:
Note: Most of the time, you'll just use sync - it handles everything automatically!
For faster syncs (with slightly lower accuracy), use the --fast flag or set fast: true in your config file:
Via CLI flag:
npm run sync -- --fast
Via config file:
export default {
// ... other config
fast: true
};
Fast mode:
--fast overrides config file settingTo preview what would be changed without actually uploading or deleting files:
npm run sync -- --dry
Dry run mode:
Control which files are synced using glob patterns:
Via CLI flags:
# Exclude files
npm run sync -- --exclude="node_modules/**" --exclude=".git/**"
# Include files (overrides exclude)
npm run sync -- --exclude="config/*" --include="config/production.json"
Via config file:
export default {
// ... other config
exclude: [
"node_modules/**",
".git/**",
"*.log",
"test/**"
],
include: [
"config/production.json", // include this specific file
"assets/critical/**" // include this directory even if excluded
]
};
Via environment variables:
XSYNC_EXCLUDE="node_modules/**,.git/**,*.log"
XSYNC_INCLUDE="config/production.json"
How it works:
exclude patterns mark files to skipinclude patterns override exclude - use this to include specific files that would otherwise be excluded--exclude="config/*" --include="config/production.json" excludes all config files except production.jsonCommon patterns:
src/**/*.{js,ts} all JS and TS files in src or src subdirectories**/*.log - all .log files in any directorynode_modules/** - everything in node_modules*.tmp - all .tmp files in root onlytest/** - all files in test directoryTo remove remote files that no longer exist locally:
npm run sync -- --delete
Or set:
XSYNC_DELETE=1
x-rsync does NOT support .ppk files (PuTTY format).
Convert to OpenSSH using PuTTYgen:
.ppkid_rsa (or any name)privateKeyPath (config file) or XSYNC_PRIVATE_KEY_PATH (env var).xsync/manifest.json # Remote server state (auto-generated on first sync)
dist/cli.js # Bundled CLI (ESM)
dist/cli.cjs # Bundled CLI (CommonJS)
src/
cli.ts # Entry point
sync/ # Remote sync utilities
shared.ts # Shared utilities
types.ts # TypeScript types
Build the CLI:
npm run build
This bundles src/cli.ts → dist/cli.cjs using esbuild.
1. Create xsync.config.js:
export default {
host: "192.168.1.100",
user: "root",
privateKeyPath: "~/.ssh/id_rsa",
remoteDir: "/var/www/myapp",
fast: true // Optional: enable fast mode by default
};
2. Use the CLI:
# Most common: just sync everything
npm run sync
# Sync with options
npm run sync -- --delete # Delete remote files not in local
npm run sync -- --fast # Skip hashing (faster, less accurate)
npm run sync -- --dry # Preview changes without uploading
# Combine flags
npm run sync -- --fast --delete
# Manual control (advanced)
npm run pull # Update manifest from remote
npm run push # Push local changes only
npm run push -- --exclude="*.log" # Push with exclusions
# 1. Set up environment variables
export XSYNC_HOST=192.168.1.100
export XSYNC_USER=root
export XSYNC_PRIVATE_KEY_PATH=~/.ssh/id_rsa
export XSYNC_REMOTE_DIR=/var/www/myapp
# 2. Sync (first run will auto-pull from remote, then push changes)
npm run sync
MIT
Contributions welcome! Feel free to open issues or pull requests.
FAQs
A tiny rsync-style tool using SFTP + SHA-256 diffing. Push only changed files to remote servers.
We found that x-rsync demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.