x-rsync
v0.1.1
Published
A tiny rsync-style tool using SFTP + SHA-256 diffing. Push only changed files to remote servers.
Downloads
10
Maintainers
Readme
x-rsync
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.
✨ Features
🚀 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
synccommand automatically pulls from remote first.🗑️ Optional remote deletion Use
--delete(orXSYNC_DELETE=1) to remove remote files not present locally.🔐 SSH support
- OpenSSH private key
- Password auth
- Custom ports
- Works perfectly on Windows (including key conversion from
.ppk)
⚙️ Flexible configuration
- Config file support (
xsync.config.jsorxsync.config.ts) - Environment variables
.envfile support
- Config file support (
📦 Bundled CLI (single JS file via esbuild)
📦 Installing in Your Project
In your project (the one you want to sync from):
npm install -D x-rsync ssh2-sftp-clientAdd 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"
}
}⚙️ Configuration
You can configure x-rsync using either a config file (recommended) or environment variables.
Option 1: Config File (Recommended)
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"]
};Option 2: Environment Variables
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/websiteAuthentication (choose ONE):
# Using a private key (recommended)
XSYNC_PRIVATE_KEY_PATH=C:/Users/you/.ssh/id_rsa
# OR using a password
XSYNC_PASSWORD=your_passwordOptional:
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 patternsOption 3: .env File
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🚀 Usage
Command Overview
x-rsync provides three commands:
sync - The all-in-one command (recommended)
x-rsync sync <localDir>Combines pull + push into one command:
- Checks if manifest exists
- If no manifest: runs
pullto download remote file list - Runs
pushto upload changed files
Example:
npm run sync # sync current directory
npm run sync -- ./dist # sync ./dist directorypull - Download remote file list
x-rsync pullConnects to your remote server, scans all files, and creates/updates .xsync/manifest.json. Use this when:
- You want to manually update the manifest from remote
- Someone made changes directly on the server
- You're setting up sync for the first time
push - Upload changed files
x-rsync push <localDir>Scans your local directory, compares with manifest, and uploads only changed files. Use this when:
- You already have a manifest and just want to push changes
- You want more control over the sync process
Note: Most of the time, you'll just use sync - it handles everything automatically!
Fast Mode
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 -- --fastVia config file:
export default {
// ... other config
fast: true
};Fast mode:
- Skips SHA-256 hashing
- Compares only file size and modification time (mtime)
- Significantly faster for large codebases
- May miss changes if file size and mtime are unchanged
- CLI flag
--fastoverrides config file setting
Dry Run Mode
To preview what would be changed without actually uploading or deleting files:
npm run sync -- --dryDry run mode:
- Scans local files and compares with manifest
- Shows how many files would be uploaded/deleted
- Does not connect to the server
- Does not modify any files
- Useful for testing before actual sync
Exclude/Include Patterns
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:
- By default, all files are included
excludepatterns mark files to skipincludepatterns overrideexclude- use this to include specific files that would otherwise be excluded- Example:
--exclude="config/*" --include="config/production.json"excludes all config files except production.json - Patterns use minimatch glob syntax
- Patterns are matched against the relative file path from the local directory
Common 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 directory
🗑️ Deleting Remote Files
To remove remote files that no longer exist locally:
npm run sync -- --deleteOr set:
XSYNC_DELETE=1🔐 Using SSH Keys on Windows
x-rsync does NOT support .ppk files (PuTTY format).
Convert to OpenSSH using PuTTYgen:
- Open PuTTYgen
- Load your
.ppk - Go to: Conversions → Export OpenSSH key
- Save as:
id_rsa(or any name) - Use the saved file path in
privateKeyPath(config file) orXSYNC_PRIVATE_KEY_PATH(env var)
📁 Project Structure
.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🛠️ Development
Build the CLI:
npm run buildThis bundles src/cli.ts → dist/cli.cjs using esbuild.
📝 Example Workflow
Using Config File
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 exclusionsUsing Environment Variables
# 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📄 License
MIT
🤝 Contributing
Contributions welcome! Feel free to open issues or pull requests.
