{"id":51095703,"url":"https://github.com/mokira3d48/wrsync","last_synced_at":"2026-06-24T06:03:06.041Z","repository":{"id":362589087,"uuid":"1259854200","full_name":"mokira3d48/wrsync","owner":"mokira3d48","description":"A production-grade Bash wrapper around rsync to send files and folders to SSH servers using named config profiles.","archived":false,"fork":false,"pushed_at":"2026-06-05T00:29:11.000Z","size":662,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-06-05T01:12:19.247Z","etag":null,"topics":["open-ssh","rsync-wrapper","ssh","wrsync"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mokira3d48.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-04T23:39:46.000Z","updated_at":"2026-06-05T00:29:15.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/mokira3d48/wrsync","commit_stats":null,"previous_names":["mokira3d48/wrsync"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/mokira3d48/wrsync","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mokira3d48%2Fwrsync","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mokira3d48%2Fwrsync/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mokira3d48%2Fwrsync/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mokira3d48%2Fwrsync/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mokira3d48","download_url":"https://codeload.github.com/mokira3d48/wrsync/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mokira3d48%2Fwrsync/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34719307,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-24T02:00:07.484Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["open-ssh","rsync-wrapper","ssh","wrsync"],"created_at":"2026-06-24T06:03:05.240Z","updated_at":"2026-06-24T06:03:06.022Z","avatar_url":"https://github.com/mokira3d48.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"```text\n                                     ____\n _      ______________  ______  _____\\   \\\n| | /| / / ___/ ___/ / / / __ \\/ ___/_   _|   \u003e_ rsync over SSH,\n| |/ |/ / /  (__  ) /_/ / / / / /__  | |          minus the boilerplate.\n|__/|__/_/  /____/\\__, /_/ /_/\\___/  |_|\n                 /____/                            ~$ wrsync ./code box:~/app\n```\n\n\u003cdiv align=\"center\"\u003e\n\n\n[![Shell](https://img.shields.io/badge/built%20with-Bash-1f425f?logo=gnubash\u0026logoColor=white)](https://www.gnu.org/software/bash/)\n[![ShellCheck](https://img.shields.io/badge/lint-ShellCheck-brightgreen?logo=gnu\u0026logoColor=white)](https://www.shellcheck.net/)\n[![Powered by rsync](https://img.shields.io/badge/powered%20by-rsync-blue)](https://rsync.samba.org/)\n[![SSH](https://img.shields.io/badge/transport-OpenSSH-black?logo=openssh\u0026logoColor=white)](https://www.openssh.com/)\n[![License: MIT](https://img.shields.io/badge/license-MIT-yellow.svg)](LICENSE)\n[![Made with ♥](https://img.shields.io/badge/made%20with-%E2%99%A5%20%26%20%24%28%29-red)](#)\n\n**Push files \u0026 folders to any SSH box using named config profiles.**\n_No more typing host, port and key paths by hand. Set it once, ship forever._\n\n\n\u003c/div\u003e\n\n\nA small, production-grade Bash wrapper around [`rsync`](https://rsync.samba.org/)\nthat lets you push files and folders to an SSH server using **named\nconfiguration profiles**.\n\nInstead of typing long `rsync`/`ssh` command lines with hosts, ports and key\npaths every time, you store each server's settings once in a config file and\nrefer to it by name:\n\n\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"assets/banner.png\" alt=\"wrsync — rsync over SSH, minus the boilerplate\" width=\"100%\"\u003e\n\u003c/div\u003e\n\n\n---\n\n### Table of contents\n\n- [Features](#features)\n- [Requirements](#requirements)\n- [Installation](#installation)\n- [Configuration](#configuration)\n  - [Config file location](#config-file-location)\n  - [Supported keys](#supported-keys)\n  - [Example config](#example-config)\n- [Usage](#usage)\n  - [Synopsis](#synopsis)\n  - [Options](#options)\n  - [How the destination is resolved](#how-the-destination-is-resolved)\n  - [Examples](#examples)\n- [How it works](#how-it-works)\n- [Security notes](#security-notes)\n- [Troubleshooting](#troubleshooting)\n- [License](#license)\n\n\n## Features\n\n- **Named profiles** — one config file per server, referenced by name.\n- **Safe config parsing** — the config file is read line by line with a strict\n  key whitelist. It is **never** `source`d, so it cannot execute arbitrary code.\n- **Sensible rsync defaults** — archive mode (`-a`), compression, resumable\n  transfers (`--partial`), and progress reporting.\n- **Strict validation** — checks that the source exists, the target format is\n  correct, required keys are present, the port is a valid number, and the SSH\n  key is readable.\n- **Dry-run mode** — preview what would be transferred without copying anything.\n- **Helpful, colorized output** (only when writing to an interactive terminal).\n- Follows the [XDG Base Directory](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html)\n  spec for config location.\n\n\n## Requirements\n\n- `bash` 4.0 or newer\n- `rsync`\n- `ssh` (OpenSSH client)\n\nOn Debian/Ubuntu/Kali:\n\n```bash\nsudo apt-get install rsync openssh-client\n```\n\n`wrsync` checks for these dependencies at runtime and exits with a clear error\nif one is missing.\n\n\n## Installation\n\n1. Make the script executable and place it on your `PATH`:\n\n   ```bash\n   chmod +x wrsync\n   sudo install -m 755 wrsync /usr/local/bin/wrsync\n   ```\n\n   Prefer a per-user install (no `sudo`)? Use a directory that is on your\n   `PATH`, for example `~/.local/bin`:\n\n   ```bash\n   install -m 755 wrsync ~/.local/bin/wrsync\n   ```\n\n2. Verify it is reachable:\n\n   ```bash\n   wrsync --help\n   ```\n\n\n## Configuration\n\n### Config file location\n\n`wrsync myvps:...` looks for a profile named `myvps` at:\n\n```\n${XDG_CONFIG_HOME:-$HOME/.config}/wrsync/myvps.conf\n```\n\nIn practice, on most systems that is:\n\n```\n~/.config/wrsync/myvps.conf\n```\n\nCreate the directory and your first profile:\n\n```bash\nmkdir -p ~/.config/wrsync\ncp monvps.conf.example ~/.config/wrsync/myvps.conf\nchmod 600 ~/.config/wrsync/myvps.conf   # recommended: keep it private\n```\n\nThen edit `~/.config/wrsync/myvps.conf` with your server details.\n\n### Supported keys\n\nThe config file uses a simple `KEY=value` format. Lines starting with `#` and\nblank lines are ignored. Inline comments (`KEY=value  # comment`) are supported.\n\n| Key             | Required | Default | Description                                              |\n|-----------------|----------|---------|----------------------------------------------------------|\n| `HOST`          | **Yes**  | —       | IP address or hostname of the remote machine.            |\n| `USER`          | **Yes**  | —       | SSH username.                                            |\n| `PORT`          | No       | `22`    | SSH port (must be `1`–`65535`).                          |\n| `IDENTITY_FILE` | No       | —       | Path to the SSH private key. `~` is expanded to `$HOME`. |\n| `RSYNC_OPTS`    | No       | —       | Extra options passed verbatim to `rsync`.                |\n| `SSH_OPTS`      | No       | —       | Extra options passed verbatim to `ssh`.                  |\n\nUnknown keys are ignored with a warning.\n\n### Example config\n\n`~/.config/wrsync/myvps.conf`:\n\n```ini\n# --- Required ---\nHOST=192.168.1.42          # IP or hostname of the remote machine\nUSER=arnold                # SSH username\n\n# --- Optional ---\nPORT=22                    # SSH port (default: 22)\nIDENTITY_FILE=~/.ssh/id_ed25519   # SSH private key to use\n\n# Extra options forwarded to rsync (e.g. exclude paths).\nRSYNC_OPTS=--exclude=.git --exclude=node_modules\n\n# Extra options forwarded to ssh.\nSSH_OPTS=-o StrictHostKeyChecking=accept-new\n```\n\n\n## Usage\n\n### Synopsis\n\n```text\nwrsync \u003csource\u003e \u003cremote\u003e:\u003cremote_path\u003e\n```\n\n- `\u003csource\u003e` — a local file or directory to send.\n- `\u003cremote\u003e` — the name of a config profile in `~/.config/wrsync/`.\n- `\u003cremote_path\u003e` — the destination path on the remote machine.\n\n### Options\n\n| Option            | Description                                       |\n|-------------------|---------------------------------------------------|\n| `-n`, `--dry-run` | Simulate the transfer without copying anything.   |\n| `-h`, `--help`    | Show usage help and exit.                          |\n\n### How the destination is resolved\n\nThe `\u003cremote\u003e:\u003cremote_path\u003e` argument is split on the first colon:\n\n- The part **before** the colon is the **profile name**. `wrsync` loads\n  `~/.config/wrsync/\u003cremote\u003e.conf` and reads `HOST`, `USER`, `PORT`, etc.\n- The part **after** the colon is the **path on the remote machine**, passed\n  straight through to `rsync`. It is interpreted by the remote shell:\n  - `~/my_project_dir` resolves to `/home/\u003cUSER\u003e/my_project_dir`.\n  - `/opt/my_project_dir` is an absolute system path.\n\nWhen `\u003csource\u003e` is a directory, a trailing slash is removed so that the\ndirectory **itself** (not just its contents) is copied into the destination.\n\n### Examples\n\nSend a folder into your remote home directory:\n\n```bash\nwrsync ./my_folder_or_file myvps:~/my_project_dir\n```\n\n\u003e `my_folder_or_file` is copied to `/home/\u003cUSER\u003e/my_project_dir/` on `myvps`.\n\nSend the same item into a system directory:\n\n```bash\nwrsync ./my_folder_or_file myvps:/opt/my_project_dir\n```\n\nPreview a transfer without copying (dry run):\n\n```bash\nwrsync --dry-run ./my_folder myvps:~/backup\n```\n\nSend a single file:\n\n```bash\nwrsync ./report.pdf myvps:~/documents\n```\n\n\n## How it works\n\nUnder the hood, `wrsync` builds and runs a command equivalent to:\n\n```bash\nrsync --archive --compress --human-readable --partial --progress \\\n      -e \"ssh -p \u003cPORT\u003e -i \u003cIDENTITY_FILE\u003e \u003cSSH_OPTS\u003e\" \\\n      -- \u003csource\u003e \u003cUSER\u003e@\u003cHOST\u003e:\u003cremote_path\u003e\n```\n\nThe default `rsync` flags are:\n\n| Flag               | Purpose                                                        |\n|--------------------|----------------------------------------------------------------|\n| `--archive` (`-a`) | Recursive **and** preserves permissions, timestamps, symlinks. |\n| `--compress` (`-z`)| Compresses data during transfer.                               |\n| `--human-readable` | Prints sizes in a readable format.                             |\n| `--partial`        | Keeps partially transferred files so transfers can resume.     |\n| `--progress`       | Shows live progress per file.                                  |\n\n\u003e **Note about `-r`:** you don't need to pass `-r` for directories. `--archive`\n\u003e already implies recursion (`-r`) plus metadata preservation, which is the\n\u003e recommended default for transfers.\n\nThe SSH transport (host, port, key, extra options) is assembled from your\nconfig profile and passed to `rsync` via the `-e` option. Both the rsync and\nssh command lines are built using Bash arrays, so paths and options are quoted\nsafely.\n\n\n## Security notes\n\n- The config file may contain sensitive details. Keep it private:\n\n  ```bash\n  chmod 600 ~/.config/wrsync/myvps.conf\n  ```\n\n  `wrsync` warns you if the file is readable by group or others.\n\n- The config is parsed with a **strict key whitelist** and is never executed as\n  a shell script, so a malicious or malformed config cannot run arbitrary code.\n\n- Prefer key-based SSH authentication (`IDENTITY_FILE`) over passwords.\n\n- `StrictHostKeyChecking=accept-new` (used in the example) trusts a host's key\n  on first contact. For higher security, pre-populate `~/.ssh/known_hosts` and\n  drop that option.\n\n\n## Troubleshooting\n\n| Message                                          | Cause / fix                                                              |\n|--------------------------------------------------|--------------------------------------------------------------------------|\n| `Missing dependency: 'rsync'…`                   | Install `rsync` / `openssh-client`.                                      |\n| `Config file not found: …`                       | The profile `\u003cremote\u003e.conf` does not exist in `~/.config/wrsync/`.       |\n| `'HOST' is missing` / `'USER' is missing`        | Add the required keys to your config file.                               |\n| `Invalid PORT…`                                  | `PORT` must be an integer between 1 and 65535.                           |\n| `SSH key not found or not readable`              | Check the `IDENTITY_FILE` path and its read permissions.                 |\n| `Invalid target … expected format`               | The target must be `\u003cremote\u003e:\u003cremote_path\u003e` (a colon is required).       |\n| `Source not found`                               | The local `\u003csource\u003e` path does not exist.                               |\n\nFor more detail on any transfer, run with `--dry-run` first to see exactly what\n`rsync` would do.\n\n\n## License\n\nUse, modify and distribute freely. No warranty.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmokira3d48%2Fwrsync","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmokira3d48%2Fwrsync","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmokira3d48%2Fwrsync/lists"}