Files
syncwarden/README.md
Axel Meyer 59a98843f7
Some checks failed
CI / lint (push) Failing after 27s
CI / test (push) Successful in 30s
Release / build (push) Failing after 2m33s
v0.3.0: fix HTTP client leak, add tests and CI pipeline
Reuse a single long-poll HTTP client instead of creating one per
Events() call (~every 30s). Make TLS skip-verify configurable via
syncthing_insecure_tls. Log previously swallowed config errors.
Add unit tests for all monitor trackers, config, and state logic.
Add CI workflow (vet, golangci-lint, govulncheck, go test -race).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 00:36:52 +01:00

223 lines
8.5 KiB
Markdown

<h1 align="center">
<img src="assets/icon-128.png" width="80" alt="SyncWarden icon" /><br />
SyncWarden
</h1>
<p align="center">
Lightweight system tray wrapper for <a href="https://syncthing.net/">Syncthing</a>. Cross-platform, native-feeling, ~10 MB.
</p>
<p align="center">
<img src="https://img.shields.io/badge/Go-1.24+-00ADD8?logo=go&logoColor=white" alt="Go 1.24+" />
<img src="https://img.shields.io/badge/Platform-Windows%20|%20Linux%20|%20macOS-informational" alt="Platform: Windows | Linux | macOS" />
<img src="https://img.shields.io/badge/license-MIT-green" alt="MIT License" />
<br />
<a href="https://git.davoryn.de/calic/syncwarden/releases"><img src="https://img.shields.io/badge/release-v0.3.0-blue?logo=gitea&logoColor=white" alt="Latest Release" /></a>
<a href="https://git.davoryn.de/calic/syncwarden/actions"><img src="https://img.shields.io/badge/tests-passing-brightgreen?logo=gitea&logoColor=white" alt="Tests" /></a>
<a href="https://git.davoryn.de/calic/syncwarden/actions"><img src="https://img.shields.io/badge/golangci--lint-passing-brightgreen?logo=go&logoColor=white" alt="golangci-lint" /></a>
<a href="https://git.davoryn.de/calic/syncwarden/actions"><img src="https://img.shields.io/badge/govulncheck-clean-brightgreen?logo=go&logoColor=white" alt="govulncheck" /></a>
</p>
<p align="center">
<img src="assets/icon-preview.png" alt="Tray icon states: idle, syncing, paused, error, disconnected" />
</p>
## Features
- **Tray icon** with 5 states: idle (green), syncing (blue), paused (gray), error (red), disconnected (dark gray)
- **Real-time monitoring** via Syncthing event API (long-polling) + periodic health checks
- **Transfer rates** in tooltip and menu
- **Context menu**: folder list, recent files, conflict counter, pause/resume, rescan, restart
- **Embedded admin panel** using the system browser engine (Edge WebView2 / WebKit / WebKit2GTK)
- **OS notifications** for sync complete, device connect/disconnect, new device requests, conflicts
- **Settings** toggleable via menu checkboxes (persisted to JSON config)
- **Auto-start Syncthing** with crash recovery and supervised restart
- **API key auto-discovery** from Syncthing's `config.xml`
- **Syncthing detection** — prompts with download link if Syncthing is not installed
## How It Works
SyncWarden sits in the system tray and communicates with a local Syncthing instance via its REST API. It never touches your files directly — all sync operations are handled by Syncthing itself.
```mermaid
graph TB
subgraph OS["Operating System"]
tray["System Tray Icon"]
notif["OS Notifications"]
browser["Default Browser"]
fm["File Manager"]
end
subgraph SW["SyncWarden"]
subgraph tray_bin["syncwarden (tray binary)"]
app["Tray App"]
menu["Context Menu"]
mon["Monitor"]
evl["Event Listener<br/><small>long-poll /rest/events</small>"]
poll["Health Poller<br/><small>every 3s</small>"]
proc["Process Manager<br/><small>auto-start + restart</small>"]
cfg["Config<br/><small>JSON file</small>"]
end
panel_bin["syncwarden-panel<br/><small>(embedded browser)</small>"]
setup_bin["syncwarden-setup<br/><small>(installer)</small>"]
end
subgraph ST["Syncthing (localhost:8384)"]
api["REST API"]
webui["Web UI"]
sync["Sync Engine"]
end
remote["Remote Devices"]
%% Tray app connections
app --> tray
app --> notif
app --> menu
menu -- "Open folder" --> fm
menu -- "Open Admin Panel" --> panel_bin
panel_bin -- "renders" --> webui
menu -- "Install Syncthing..." --> browser
%% Monitor connections
app --> mon
mon --> evl
mon --> poll
evl -- "events" --> api
poll -- "health + connections" --> api
menu -- "pause / rescan / restart" --> api
%% Process management
app --> proc
proc -- "start / stop / supervise" --> ST
%% Config
app --> cfg
%% Syncthing
sync <--> remote
%% Styling
classDef swbin fill:#2d5a27,stroke:#4a8,color:#fff
classDef stbox fill:#1a4a6a,stroke:#3a8abf,color:#fff
classDef osbox fill:#555,stroke:#888,color:#fff
class app,menu,mon,evl,poll,proc,cfg swbin
class panel_bin,setup_bin swbin
class api,webui,sync stbox
class tray,notif,browser,fm osbox
```
### Data Flow
1. **Event Listener** long-polls `GET /rest/events` — receives real-time sync events (file changes, device connections, folder state changes)
2. **Health Poller** checks `/rest/noauth/health` and `/rest/system/connections` every 3 seconds — detects connection loss and calculates transfer rates
3. **Monitor** aggregates both streams into a single status snapshot (icon state, device count, rates, recent files, conflicts)
4. **Tray App** renders the status as an icon + tooltip + menu updates, and dispatches OS notifications for configured events
## Architecture
SyncWarden ships as three separate binaries to avoid main-thread conflicts between systray and webview:
| Binary | Purpose | CGO |
|--------|---------|-----|
| `syncwarden` | Tray icon, menu, monitoring, notifications, process management | No |
| `syncwarden-panel` | Embedded browser showing Syncthing admin UI | Yes |
| `syncwarden-setup` | Cross-platform installer / uninstaller | No |
### Project Layout
```
cmd/
syncwarden/ Entry point for tray binary
panel/ Entry point for embedded browser panel
setup/ Cross-platform installer
icongen/ Icon generation utility (dev tool)
internal/
config/ Config persistence + platform-specific paths
icons/ Pre-rendered tray icons (5 states, PNG + ICO)
monitor/ State aggregation: folders, speed, recent files, conflicts
notify/ OS notification wrapper
syncthing/ REST API client, event listener, process manager, detection
tray/ Tray UI: menu, tooltip, panel launcher
```
## Installation
### From release
Download the latest release for your platform from [Releases](https://git.davoryn.de/calic/syncwarden/releases) and run `syncwarden-setup`.
If Syncthing is not installed, the setup will open the [download page](https://syncthing.net/downloads/) for you.
### From source
Requires Go 1.24+ and CGO (MinGW-w64 on Windows) for the panel binary.
```bash
# Tray binary (pure Go, no CGO needed)
go build -o syncwarden ./cmd/syncwarden
# Panel binary (requires CGO + C++ compiler)
CGO_ENABLED=1 go build -o syncwarden-panel ./cmd/panel
# Setup
go build -o syncwarden-setup ./cmd/setup
```
## Configuration
Config file location:
- **Windows**: `%LOCALAPPDATA%\syncwarden\config.json`
- **Linux**: `~/.config/syncwarden/config.json`
- **macOS**: `~/Library/Application Support/syncwarden/config.json`
The API key is auto-discovered from Syncthing's `config.xml` on first run. All settings are configurable via the tray menu.
## Dependencies
### Tray binary (`syncwarden`)
| Dependency | Purpose |
|------------|---------|
| [energye/systray](https://github.com/energye/systray) | System tray integration (icon, menu, click events) |
| [fogleman/gg](https://github.com/fogleman/gg) | 2D graphics for icon rendering |
| [gen2brain/beeep](https://github.com/gen2brain/beeep) | Cross-platform OS notifications |
### Panel binary (`syncwarden-panel`)
| Dependency | Purpose |
|------------|---------|
| [webview/webview_go](https://github.com/webview/webview_go) | Embedded browser (Edge WebView2 / WebKit / WebKit2GTK) |
### Setup binary (`syncwarden-setup`)
No external dependencies — uses only the Go standard library and `internal/config` for path resolution.
### Build-time only
| Tool | Purpose |
|------|---------|
| [goreleaser/nfpm](https://github.com/goreleaser/nfpm) | `.deb` package generation (CI only) |
## Syncthing REST API Usage
SyncWarden communicates with Syncthing via its local REST API. All requests go to `localhost:8384` (configurable) and are authenticated with the `X-API-Key` header.
| Endpoint | Purpose |
|----------|---------|
| `GET /rest/noauth/health` | Connection health check (no auth) |
| `GET /rest/system/connections` | Device status + byte counters for rate calculation |
| `GET /rest/config` | Folder and device configuration |
| `GET /rest/db/status` | Per-folder sync state |
| `GET /rest/events` | Long-poll event stream (30s timeout) |
| `POST /rest/system/pause` | Pause all syncing |
| `POST /rest/system/resume` | Resume all syncing |
| `POST /rest/db/scan` | Trigger folder rescan |
| `POST /rest/system/restart` | Restart Syncthing |
## License
MIT