# syncroot

Keep the same working folder on your laptop, workstation, and remote machines.
syncroot synchronizes files directly between trusted devices, with no central
syncroot server or account. Use it on a trusted LAN, private mesh network, or
other existing network that provides the security you need.

syncroot does not provide application-layer encryption, a relay, or NAT
traversal. It is pre-launch.

## Before you start

Wire and disk formats may change without migration. Run the same build on every
device, keep a backup, and do not use syncroot as the only copy of irreplaceable
data.

Files larger than 20 MiB are temporarily ignored while large-file storage and
transfer are redesigned.

## Install

Use a distribution package when one is available, or build from source:

```bash
cargo build --release
cargo install --path .
```

A package installs the systemd user unit at
`/usr/lib/systemd/user/syncroot.service`. For a source install, copy
`contrib/systemd/syncroot.service` to `~/.config/systemd/user/` and point
`ExecStart` at the installed binary.

## Connect two devices

On the first machine, start the service and create an invite for the folder:

```bash
systemctl --user enable --now syncroot
syncroot listen laptop.mesh.example
syncroot init ~/Documents
syncroot invite ~/Documents
```

Carry the printed private URL to the second machine, then run:

```bash
systemctl --user enable --now syncroot
syncroot listen desktop.mesh.example
syncroot connect ~/Documents 'syncroot://laptop.mesh.example:9494/abc123'
syncroot status
```

The first machine can be temporarily offline when you connect. syncroot saves
the invite and retries in the background. An invite may come from any existing
device, and roster gossip discovers the rest; you do not have to pair every
device by hand.

## How files move

syncroot divides files into content-addressed FastCDC chunks. Each device keeps
its own ordered log of changes. Version vectors tell syncroot whether one edit
followed another or happened concurrently. If two devices write the same path
at once, syncroot keeps every version under a predictable conflict name rather
than trusting wall-clock time. Devices share their known peer endpoints with one
another, and one per-user daemon serves every configured folder.

A file becomes readable on another device only after all of its chunks arrive
and its content hash matches. You see the complete file or nothing yet.

## Configuration

Commands normally manage the platform user configuration file:

- `$XDG_CONFIG_HOME/syncroot/config.toml`, when explicitly set;
- `~/Library/Application Support/syncroot/config.toml` on macOS;
- `~/.config/syncroot/config.toml` on other platforms.

```toml
listen = "laptop.mesh.example"
roots = ["/home/user/Documents"]
```

`listen` defaults to `127.0.0.1` and port 9494. You do not configure peers here:
each folder's roster is shared between devices and stored under `.syncroot/`.
Unknown configuration keys produce an error instead of being ignored.

## Commands

| Command | What it does |
| --- | --- |
| `daemon` | Run the per-user daemon in the foreground |
| `init <root>` | Create a syncroot folder and adopt its existing files |
| `connect <root> <url>` | Join a folder; the inviting peer may be offline |
| `listen [host]` | Show or change this machine's reachable endpoint |
| `invite [root]` | Create a private invite for another device |
| `status [root]` | Show current or last-known progress and conflicts |
| `doctor [root] [--deep]` | Check state, connectivity, and optionally chunk integrity |
| `history [--root root] <path>` | Show the retained history of a file |
| `add <root>` / `remove <root>` | Start or stop serving an existing folder |
| `reload` | Validate and apply a hand-edited configuration |

Informational commands support `--json`. For isolated invocations and tests,
use `--config <path>` and `--socket <path>`.

## When two devices write the same path

syncroot preserves concurrent versions; it cannot decide which edit is
semantically correct. The version from the lower device ID keeps the original
name. Every other version receives a deterministic conflict name, so the result
does not depend on timestamps.

See every conflict with:

```bash
syncroot status --conflicts
```

## A few things to know

- **Use trusted devices.** Every folder member can write anywhere. There are no
  per-path permissions or application-layer encryption.
- **Keep a backup.** Deletes propagate to every connected device.
- **Coordinate work elsewhere.** Files cannot atomically claim a task for one
  worker.
- **New devices receive full history.** Snapshot join is not implemented, so a
  first sync can take a while.
- **Stored state only grows.** History compaction, tombstone sweeping, and chunk
  garbage collection are not implemented.
- **Leaving is incomplete.** Roster removal is not implemented; other devices
  continue to remember a removed device.
- **Some filesystem features do not sync.** Renames appear as delete plus
  create. Empty directories, symlinks, permissions, and metadata do not sync.
- **Files larger than 20 MiB are ignored.** This is a temporary pre-launch
  limit.

## Troubleshooting

**The daemon is not running.**

```bash
systemctl --user start syncroot
systemctl --user status syncroot
```

**Another device never connects.** Run `syncroot listen`. If it reports
`127.0.0.1`, set a stable name or address that the other device can reach on
your existing network.

**The first sync is slow.** A new device currently receives the folder's full
retained history because snapshot join is not implemented.

**A normal service restart triggers a full scan.** This should not happen. Keep
the logs, run `syncroot doctor`, and file a bug. SIGTERM and SIGINT use the same
checkpointed shutdown path.

## Development

Run a configuration-free daemon against a disposable directory with:

```bash
syncroot daemon --root /tmp/a --listen 127.0.0.1:19491 --socket /tmp/a.sock
```

Source and issues: <https://github.com/yuvadm/syncroot>

## License

Apache-2.0.
