Safe, Idempotent File Synchronization for Manual Transfers
curl -fsSL https://raw.githubusercontent.com/kaurifund/bucketcast/main/install.sh | bashThat's it. This installs to ~/.local/share/bucketcast/, adds it to your PATH, and sets up everything including the TUI.
To test a feature branch before it's merged:
curl -fsSL https://raw.githubusercontent.com/kaurifund/bucketcast/BRANCH_NAME/install.sh | bashReplace BRANCH_NAME with the branch (e.g., feature/outbox-inbox-symmetry).
Bucketcast is a command-line tool for safely transferring files between your home computer and remote servers. It prioritizes safety and auditability over speed, ensuring files are never accidentally deleted or overwritten.
- 🔒 Safe by Design: Never deletes files, never overwrites without consent
- 📁 Sandboxed Operations: All files stored in
~/.bucketcast/ - 🔄 Bidirectional Sync: Push to and pull from remote servers
- 🔀 Multi-Server Relay: Move files between servers via your local machine
- 📝 Comprehensive Logging: JSON and human-readable logs
- 🎯 Idempotent: Safe to run multiple times
- ☁️ Optional S3 Integration: Archive to S3 for backup
- 🖥️ Optional TUI: Interactive terminal interface
The relay command solves a common problem: moving files between two servers that can't directly connect to each other.
You have servers A and B. You want to move files from A to B, but:
- Server A can't SSH to Server B (firewalls, different networks, no credentials)
- Server B can't SSH to Server A
- Your local machine CAN reach both servers
# Step 1: Pull from server A to local
bucketcast pull -s server-a
# Step 2: Manually find and copy files to outbox
cp ~/.bucketcast/local/inbox/server-a/file.txt ~/.bucketcast/local/outbox/global/
# Step 3: Push to server B
bucketcast push -s server-b -S ~/.bucketcast/local/outbox/global/file.txtbucketcast relay --from server-a --to server-bYour local machine acts as a hub - it pulls from A, then pushes to B. The servers never need to know about each other.
- Moving deployment artifacts between staging and production servers
- Transferring logs from production to an analysis server
- Syncing configuration between servers in different networks
- Migrating files during server transitions
# 1. Install
curl -fsSL https://raw.githubusercontent.com/kaurifund/bucketcast/main/install.sh | bash
source ~/.bashrc # or restart your shell
# 2. Configure a server
nano ~/.bucketcast/config/servers.toml
# 3. Test with dry-run
bucketcast push --server myserver --source ~/myfile.txt --dry-run
# 4. Execute
bucketcast push --server myserver --source ~/myfile.txt- Bash 4.0+ (check with
bash --version) - rsync 3.0+ (check with
rsync --version) - SSH with key-based authentication configured
- AWS CLI (for S3 integration)
- Python 3 (for TUI - installer creates isolated venv)
- jq (for log parsing)
After initialization, Bucketcast creates:
~/.bucketcast/
├── config/
│ ├── bucketcast.conf # Main configuration
│ └── servers.toml # Server definitions
├── remote/
│ └── <server_id>/
│ └── files/ # Files synced with this server
├── local/
│ ├── inbox/ # Files received from remotes
│ │ └── <server_id>/ # Per-server incoming files
│ └── outbox/ # Files shared with remotes
│ ├── global/ # Available to all servers
│ └── <server_id>/ # Server-specific shares
├── logs/
│ ├── sync.log # Human-readable log
│ └── sync.jsonl # JSON Lines log
├── archive/ # Versioned backups
└── tmp/ # Temporary files
| Command | Description |
|---|---|
init |
Initialize directory structure |
push |
Push files TO a remote server |
pull |
Pull files FROM a remote server |
share |
Share files via outbox (for others to pull) |
relay |
Relay files between servers (via local) |
list servers |
List configured servers |
list files |
List files for a server |
status |
Show sync status |
tui |
Launch interactive TUI |
| Flag | Description |
|---|---|
-s, --server <id> |
Target server ID |
-S, --source <path> |
Source file/directory |
-F, --from <id> |
Source server for relay |
-T, --to <id> |
Destination server for relay |
-g, --global |
Relay only global outbox files |
-n, --dry-run |
Preview without executing |
-f, --force |
Allow overwrites (prompts) |
-v, --verbose |
Verbose output |
-q, --quiet |
Minimal output |
--s3-archive |
Archive to S3 after sync |
--global |
Share with all servers (share command) |
--list |
List shared files (share command) |
--remove |
Remove from share (share command) |
# Push a single file
bucketcast.sh push -s myserver -S ~/document.pdf
# Push a directory
bucketcast.sh push -s myserver -S ~/projects/myapp/
# Pull from a server
bucketcast.sh pull -s myserver
# Dry-run (always recommended first!)
bucketcast.sh push -s myserver -S ~/data.zip --dry-run
# Force overwrite (archives existing)
bucketcast.sh push -s myserver -S ~/updated.txt --force
# With S3 archival
bucketcast.sh push -s myserver -S ~/backup/ --s3-archive
# Share a file globally (all servers can pull)
bucketcast share --global -S ~/shared-doc.pdf
# Share with a specific server
bucketcast share -s myserver -S ~/for-myserver.txt
# List all shared files
bucketcast share --list
# Remove a file from global share
bucketcast share --global --remove -S shared-doc.pdf
# Relay files from one server to another
bucketcast relay --from serverA --to serverB --dry-run
bucketcast relay --from serverA --to serverB
# Relay only global outbox files
bucketcast relay --from serverA --to serverB --global
# Relay specific files only (multiple -S flags supported)
bucketcast relay --from serverA --to serverB -S myfile.txt -S other.txtEdit ~/.bucketcast/config/servers.toml:
[servers.myserver]
name = "My Development Server"
host = "192.168.1.100"
port = 22
user = "myuser"
identity_file = "~/.ssh/id_rsa" # optional
remote_base = "/home/myuser/.bucketcast"
enabled = true
s3_backup = false
[servers.aws-prod]
name = "AWS Production"
host = "ec2-12-34-56-78.compute-1.amazonaws.com"
port = 22
user = "ec2-user"
identity_file = "~/.ssh/my-key.pem"
remote_base = "/home/ec2-user/.bucketcast"
enabled = true
s3_backup = trueEdit ~/.bucketcast/config/bucketcast.conf:
# Log level: DEBUG, INFO, WARN, ERROR
LOG_LEVEL="INFO"
# Maximum transfer size
MAX_TRANSFER_SIZE="10G"
# Archive retention days
ARCHIVE_RETENTION_DAYS=30
# S3 settings (optional)
S3_ENABLED="true"
S3_BUCKET="my-backup-bucket"
S3_PREFIX="bucketcast-archive"- Path Validation: All paths must be within
~/.bucketcast/ - No Deletion: The tool never deletes files
- No Overwrites: Existing files are never overwritten without
--force - Confirmation Prompts: Force mode requires explicit confirmation
- Automatic Archival: Files are archived before overwrite
- Dry Run: Preview any operation without changes
- Operation Logging: Every operation is logged with UUID
Enable S3 for cloud backup:
# In bucketcast.conf
S3_ENABLED="true"
S3_BUCKET="my-bucketcast-bucket"
S3_PREFIX="archive"
# Use with any sync
bucketcast.sh push -s myserver -S ~/data/ --s3-archive
# Or use S3 as intermediate layer
bucketcast.sh s3-push -s myserver -S ~/data/
# On another machine:
bucketcast.sh s3-pull --transfer-id <uuid>Launch the interactive interface:
bucketcast tuiThe TUI is automatically set up by the installer (isolated Python venv, no global packages).
The TUI provides:
- Server list and status
- Recent operations history
- File browser for inbox/outbox
- Push/pull wizards with options
[2024-01-15 10:30:45] [INFO] Starting PUSH operation [abc123-...]
[2024-01-15 10:30:46] [INFO] Transferring: ~/file.txt -> ~/.bucketcast/remote/myserver/files/
[2024-01-15 10:30:50] [SUCCESS] Push operation completed
{"uuid":"abc123...","operation":"push","server_id":"myserver","status":"SUCCESS","bytes_transferred":1024,"timestamp_start":"2024-01-15T10:30:45Z"}# Recent operations
tail -f ~/.bucketcast/logs/sync.log
# Parse JSON logs (requires jq)
cat ~/.bucketcast/logs/sync.jsonl | jq 'select(.status=="FAILED")'| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | General error |
| 2 | Invalid arguments |
| 3 | Configuration error |
| 4 | Path validation failed |
| 5 | Transfer failed |
| 6 | Collision (no --force) |
| 7 | User cancelled |
| 8 | Required tool missing |
# Test SSH manually
ssh -p 22 user@host 'echo OK'
# Ensure key-based auth
ssh-copy-id -p 22 user@host# Check directory permissions
ls -la ~/.bucketcast/
# Fix if needed
chmod -R u+rwX ~/.bucketcast/# Run with verbose
bucketcast.sh push -s myserver -S ~/file.txt --verbose
# Common fixes:
# - Ensure rsync is installed on remote
# - Check remote path exists
# - Verify SSH connectivity- Fork the repository
- Create a feature branch
- Make your changes
- Run tests:
./tests/test_sync.sh - Submit a pull request
MIT License - See LICENSE file for details.
- Built with safety-first principles
- Inspired by the need for simple, reliable file sync
- Thanks to the rsync and SSH communities