This repository contains a native Go rsync implementation: the gokr-rsync
command implements an rsync client and server, which can send or receive files
(all directions supported). Daemon mode is supported, meaning you can deploy
gokr-rsync behind SSH (anonymous or authorized), as command or daemon, or
listening directly on the network (on port 873/tcp by default).
This project accepts contributions as time permits to merge them (best effort).
This rsync implementation is not as well-tested as the original “tridge” implementation from the Samba project. gokrazy/rsync was started in 2021 and doesn’t have many users yet.
With that warning out of the way, the rsync protocol uses MD4 checksums over file contents, so at least your file contents should never be able to be corrupted.
There is enough other functionality (delta transfers, file metadata, special files like symlinks or devices, directory structures, etc.) in the rsync protocol that provides opportunities for bugs to hide.
I recommend you carefully check that your transfers work, and please do report any issues you run into!
| Language | URL | Note | Max Protocol | Server mode? |
|---|---|---|---|---|
| C | RsyncProject/rsync (formerly WayneD/rsync) | original “tridge” implementation; I found older versions easier to study | 32 | ✔ yes |
| C | kristapsdz/openrsync | OpenBSD, good docs | 27 | ✔ yes |
| Go | gokrazy/rsync | → you are here ← | 27 | ✔ yes 🎉 |
| Go | jbreiding/rsync-go | rsync algorithm | ❌ no | |
| Go | kaiakz/rsync-os | only client/receiver | 27 | ❌ no |
| Go | knight42 | proxy | ❌ no | |
| Go | c4milo/gsync | ❌ no | ||
| Java | APNIC-net/repositoryd | archived | ✔ yes | |
| Java | JohannesBuchner/Jarsync | archived, internet draft RFC “The rsync Network Protocol” | ✔ yes | |
| Java | perlundq/yajsync | ✔ yes | ||
| C++ | gilbertchen/acrosync-library | commercial | ❌ no | |
| Rust | sourcefrog/rsyn | archived, client, “rsyn is rsync with no c” | 27 | ❌ no |
To serve the /usr/share/man directory via rsync on localhost:8730, use:
go install github.com/gokrazy/rsync/cmd/gokr-rsync@latest
gokr-rsync --daemon --gokr.listen=localhost:8730 --gokr.modulemap=man=/usr/share/man
You can then copy the contents of the current directory with clients such as
rsync(1):
% rsync -v --archive --port 8730 rsync://localhost/man/ man
receiving file list ... done
created directory man
./
ar/
ar/man1/
[…]
zh_TW/man8/userdel.8.gz
zh_TW/man8/usermod.8.gz
sent 658.973 bytes received 88.012.067 bytes 3.940.935,11 bytes/sec
total size is 84.504.170 speedup is 0,95
…or openrsync(1), shown doing a
differential update:
% openrsync -v --archive --port 8730 rsync://localhost/man/ man
openrsync: warning: connect refused: ::1, localhost
Transfer starting: 40202 files
[…]
zh_TW/man8/userdel.8.gz (732 B, 100.0% downloaded)
zh_TW/man8/usermod.8.gz (1.8 KB, 100.0% downloaded)
Transfer complete: 83.93 MB sent, 643.5 KB read, 80.59 MB file size
| setup | encrypted | authenticated | private files? | privileges | protocol version | config required |
|---|---|---|---|---|---|---|
| 1. rsync daemon protocol (TCP port 873) | ❌ no | ⚠ rsync (insecure) | ❌ only world-readable | ✔ dropped + namespace | ✔ negotiated | config required |
| 2. anon SSH (daemon) | ✔ yes | ✔ rsync | ❌ only world-readable | ✔ dropped + namespace | ✔ negotiated | config required |
| 3. SSH (command) | ✔ yes | ✔ SSH | ✔ yes | ⚠ full user | ⚠ assumed | no config |
| 4. SSH (daemon) | ✔ yes | ✔ SSH (+ rsync) | ✔ yes | ⚠ full user | ✔ negotiated | ~/.config/gokr-rsyncd.toml required |
Regarding protocol version “assumed”: the flags to send over the network are
computed before starting SSH and hence the remote rsync process. You might
need to specify --protocol=27 explicitly on the client. Once the connection is
established, both sides do negotiate the protocol, though.
Serving rsync daemon protocol on TCP port 873 is only safe where the network layer ensures trusted communication, e.g. in a local network (LAN), or when using Tailscale or similar. In untrusted networks, attackers can eavesdrop on file transfers and possibly even modify file contents.
Prefer setup 2 instead.
Example:
* Server: gokr-rsync --daemon --gokr.modulemap=module=/srv/rsync-module
* Client: rsync rsync://webserver/module/path
This setup is well suited for serving world-readable files without authentication.
Example:
* Server: gokr-rsync --daemon --gokr.modulemap=module=/srv/rsync-module --gokr.anonssh_listen=:22873
* Client: rsync -e ssh rsync://webserver/module/path
This setup is well suited for interactive one-off transfers or regular backups, and uses SSH for both encryption and authentication.
Note that because gokr-rsync is invoked with user privileges (not root
privileges), it cannot do namespacing
and hence retains more privileges. When serving public data, it is generally
preferable to use setup 2 instead.
Note that rsync(1) assumes the server process understands all flags that it
sends, i.e. is running the same version on client and server, or at least a
compatible-enough version. You can either specify --protocol=27 on the client,
or use setup 4, which negotiates the protocol version, side-stepping possible
compatibility gaps between rsync clients and gokr-rsync.
Example:
* Server will be started via SSH
* Client: rsync --rsync-path=gokr-rsync webserver:path
This setup is more reliable than setup 3 because the rsync protocol version will
be negotiated between client and server. This setup is slightly inconvenient
because it requires a config file to be present on the server in
~/.config/gokr-rsyncd.toml.
Note that this mode of operation is only implemented by the original “trigde”
rsync, not in openrsync. Apple started shipping openrsync with macOS 15 Sequoia.
For a while, /usr/libexec/rsync/rsync.samba was still available, but on more
recent macOS versions you need to use homebrew or Nix to get tridge rsync.
Example:
* Server will be started via SSH
* Client: rsync -e ssh --rsync-path=gokr-rsync rsync://webserver/module/path
In my tests, gokr-rsync can easily transfer data at > 6 Gbit/s. The current
bottleneck is the MD4 algorithm itself (not sure whether in the “tridge” rsync
client, or in gokr-rsync). Implementing support for more recent protocol
versions would help here, as these include hash algorithm negotiation with more
recent choices.
Supported environments:
root, or in a user namespace)In all environments, the default instructions will take care that:
gokr-rsync uses Go’s Traversal-resistant os.Root file
APIs to restrict all file access to the specified
paths.writable) into a
Linux mount namespace for gokr-rsync, to guard against data modification and
data exfiltration.unveil(2) API.gokr-rsync drops privileges to user nobody,
to limit the scope of what an attacker can do when exploiting a vulnerability.Known gaps:
gokr-rsync does not guard against denial of service attacks, i.e. consuming
too many resources (connections, bandwidth, CPU, …).We provide a gokr-rsyncd.socket and gokr-rsyncd.service
file for systemd. These
files enables most of systemd’s security features. You can check by running
systemd-analyze security gokr-rsyncd.service, which should result
$ claude mcp add rsync \
-- python -m otcore.mcp_server <graph>