Skip to content
kochj23Public

About

Native macOS rsync GUI: 100+ flag editor, live progress, launchd scheduling, multi-destination sync, delta reports, widget, loopback API, and optional AI 'describe it in English' (NovaAIKit: Ollama/MLX/OpenRouter/OpenAI…, built-in fallback, never auto-runs)

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

5 stars

Watchers

0 watching

Forks

Repository files navigation

RsyncGUI

A native macOS front end for rsync: a visual editor for 100+ flags, live progress, launchd scheduling, multi-source / multi-destination sync, delta reports, built-in backup insights, a desktop widget, a loopback API — and an optional AI assistant (NovaAIKit) that turns plain English into a reviewed rsync command.

macOS 14.0+ Swift 5 Apple Silicon License Version Tests AI


Features

Feature Description
100+ rsync flags Tabbed visual editor across 14 categories (Basic, Transfer, Preserve, Filters, Comparison, Bandwidth, Output, SSH, Ownership, Backup, Logging, Network, I/O, Checksum)
Real-time progress Animated progress circle with speed, ETA, file count and the current file
Multi-source / multi-destination Fan-out (1:N), fan-in (N:1) and full mesh (N:N) with parallel or sequential execution
launchd scheduling Hourly, daily, weekly, monthly or custom schedules that run even when the app is closed
SSH remote sync Public-key auth, connection testing, key path validation
iCloud Drive sync One-click iCloud destination with automatic .icloud placeholder exclusion
Describe it in English (AI, optional) Type an intent; AI — or the built-in phrase matcher when AI is off — proposes an rsync command. It is validated against a strict flag allow-list, shown for review, and only pre-fills the builder. Never auto-executed
One AI feature set (NovaAIKit) AI on/off, backend picker (Auto, Ollama, MLX, TinyLLM, TinyChat, OpenWebUI, Nova Gateway, OpenRouter, OpenAI), model picker, editable URLs, Auto failover with timeouts and retries, status indicator, Keychain-only keys (https only), output validation
Backup insights (built-in) Error diagnosis, change summary, anomaly detection, smart scheduling, storage prediction, exclusion suggestions, health scoring, recovery assistant, sensitive-file detection — rule-based, run locally, no AI needed
Delta reporting Files added / modified / deleted with byte-level statistics after every run
Job dependencies Chain jobs; CryptoKit change detection skips unchanged sources
Pre/post sync scripts Absolute-path scripts with JOB_NAME, JOB_STATUS, FILES_TRANSFERRED
Dry run Preview every change before running
Desktop widget WidgetKit (Small / Medium / Large): health score, last and next sync, recent activity
Menu bar Status item with quick job access
Nova API server Loopback HTTP API on port 37424 for automation

Architecture

graph TD
    subgraph UI["SwiftUI"]
        CV[ContentView] --> JL[JobListView]
        CV --> JD[JobDetailView]
        CV --> JE[JobEditorView]
        JE --> RAS["RsyncAssistSection<br/>(Describe it in English)"]
        JE -->|"Browse / iCloud / typed path"| SDP["SyncJob.setDestinationPath(id:path:)"]
        CV --> SP[SyncProgressView]
        CV --> AIV[AIInsightsView]
        CV --> HT[JobHistoryTabView]
        ST[SettingsView] --> AIS_UI["Settings › AI<br/>NovaAISettingsView"]
        MB[MenuBarManager] --> CV
    end

    subgraph AI["AI (optional) — NovaAIKit 1.0.0"]
        RAS --> VM[RsyncAssistViewModel]
        VM -->|"generate(request, validate:, fallback:)"| APPAI["AppAI.shared (NovaAI)"]
        VM -->|fallback| HEU["RsyncIntentHeuristics<br/>(built-in phrase matcher)"]
        APPAI -->|validator| VAL["parseRsyncSuggestion<br/>flag allow-list"]
        APPAI --> BACK["Ollama · MLX · TinyLLM · TinyChat · OpenWebUI<br/>Nova Gateway · OpenRouter · OpenAI"]
        APPAI --> KC["Keychain<br/>com.jkoch.rsyncgui.*"]
        RAS --> IND[NovaAIStatusIndicator]
    end

    subgraph Services["Services"]
        JM[JobManager] --> RE[RsyncExecutor]
        JM --> AES[AdvancedExecutionService]
        JM --> SM[ScheduleManager]
        RE -->|"Process.arguments (no shell)"| RSYNC["/usr/bin/rsync"]
        AES -->|parallel / sequential| RE
        SM --> LA["~/Library/LaunchAgents"]
        INS["AIInsightsService<br/>(rule-based)"]
        WDS[WidgetDataSync] -->|App Group| WK[Widget]
    end

    subgraph Data["Persistence"]
        JOBS[jobs.json]
        HIST[ExecutionHistory]
        DR[DeltaReport]
    end

    API["Nova API :37424 (loopback)"] --> JM
    UI --> Services
    JM --> JOBS
    RE --> HIST
    RE --> DR
    HIST --> INS
    HIST --> WDS
    INS --> AIV
Loading

AI

AI is optional and off-switchable. It powers one feature — Describe it in English in the Job Editor's Basic tab — through NovaAIKit 1.0.0, the shared AI layer used by all of Jordan's macOS apps.

Toggle Settings › AI › Use AI. Off = nothing is sent anywhere; the built-in phrase matcher answers.
Backends Auto (failover in order: Ollama, MLX, TinyChat, TinyLLM, OpenWebUI, Nova Gateway, OpenRouter, OpenAI), or pin one. Model picker and editable URL per backend, "Test connection".
Keys Only in the macOS Keychain, service com.jkoch.rsyncgui.<backend>, account apiKey. Shown as "stored", never as text. Keys are only ever sent over https.
Status NovaAIStatusIndicator next to the assistant: green online, orange unreachable, grey off. Click to re-check.
Reliability Per-request timeouts, bounded retries with backoff, Auto failover across reachable backends.
Validation Every AI reply must parse as one clean rsync command (no ; `
Source label Each suggestion shows where it came from: "AI · Ollama (qwen3:8b)" or "Built-in".

Fallback per feature

Feature AI off / unreachable / reply rejected
Describe it in English RsyncIntentHeuristics maps phrases to flags (mirror / delete extras → --delete, preview / dry run → --dry-run, skip video / music / caches / node_modules / .git → --exclude=…, remote / ssh → -z, only new → --ignore-existing, newer → --update, …) on top of -a, using the job's known source and destination.
Backup insights Always rule-based; never use AI.
sequenceDiagram
    participant U as User
    participant VM as RsyncAssistViewModel
    participant AI as AppAI (NovaAIKit)
    participant B as Backend (Auto failover)
    participant V as parseRsyncSuggestion
    participant H as RsyncIntentHeuristics

    U->>VM: "mirror Photos to the NAS, skip videos"
    VM->>AI: generate(prompt, validate, fallback)
    alt AI off
        AI->>H: fallback()
    else AI on
        AI->>B: chat (timeouts, retries, failover)
        alt no backend reachable
            AI->>H: fallback()
        else reply
            AI->>V: validate(reply)
            alt clean rsync command
                V-->>AI: RsyncCommand
            else rejected
                AI->>B: one corrective retry
                AI->>H: fallback() if still rejected
            end
        end
    end
    AI-->>VM: value + source ("AI · …" / "Built-in")
    VM-->>U: review card — "Apply flags to builder" (never runs)
Loading

Upgrading from 1.7.x: on first launch the old settings and keys are migrated once — the OpenAI key from com.digitalnoise.RsyncGUI and the OpenRouter key from com.jordankoch.rsyncgui.openrouter move to com.jkoch.rsyncgui.* (legacy items deleted after a verified write), the old Ollama / Nova Gateway URLs carry over, and secrets of the removed Google Cloud, Azure, AWS and IBM Watson backends are deleted. Those backends are no longer supported.


Sync Process Flow

sequenceDiagram
    participant User
    participant UI as SwiftUI
    participant JM as JobManager
    participant AES as AdvancedExecutionService
    participant RE as RsyncExecutor
    participant RS as /usr/bin/rsync
    participant INS as AIInsightsService
    participant WK as Widget

    User->>UI: Run job
    UI->>JM: executeJob(id)
    JM->>JM: Check dependencies (CryptoKit hash)
    alt Multi-destination
        JM->>AES: fan-out / fan-in / mesh
        AES->>RE: parallel or sequential runs
    else Single destination
        JM->>RE: execute(job)
    end
    RE->>RE: Build arguments, run pre-sync script
    RE->>RS: Process.run(arguments)
    loop stdout
        RS-->>RE: progress
        RE-->>UI: SyncProgressView (speed, ETA, file)
    end
    RS-->>RE: exit code + stats
    RE->>RE: DeltaReport, post-sync script
    RE->>JM: ExecutionHistory entry
    JM->>INS: anomalies, health score (rule-based)
    JM->>WK: App Group sync
    JM-->>UI: done
Loading

Installation

From DMG

  1. Download RsyncGUI-v1.8.0-build20.dmg from Releases.
  2. Drag RsyncGUI onto Applications and open it from there.

Local builds are ad hoc signed. If macOS says the developer cannot be verified: Control-click the app › Open, or System Settings › Privacy & Security › Open Anyway, or xattr -dr com.apple.quarantine /Applications/RsyncGUI.app. Developer-ID-signed, notarized releases are produced with scripts/release.sh — see RELEASE.md.

Requirements

Requirement Minimum
macOS 14.0 (Sonoma)
Architecture Apple Silicon and Intel
rsync The one bundled with macOS, or Homebrew's
AI (optional) Any one of: Ollama, MLX server, TinyLLM, TinyChat, OpenWebUI, Nova Gateway, OpenRouter key, OpenAI key

Building

Requires Xcode 16 or later. The only package dependency is NovaAIKit (resolved automatically by Xcode / SwiftPM).

git clone git@github.com:kochj23/RsyncGUI.git
cd RsyncGUI
xcodebuild -project RsyncGUI.xcodeproj -scheme RsyncGUI -configuration Release build   # zero warnings

Testing

xcodebuild test -project RsyncGUI.xcodeproj -scheme RsyncGUI -destination 'platform=macOS'

479 tests across 19 classes, all passing. AI tests use a stub URLProtocol and an in-memory key store — no real network, no real Keychain.

Test class Tests Category
RsyncOptionsTests 57 Unit — argument generation, archive mode, filter sanitization, Codable
ProgressParsingTests 38 Unit — speed / time / bytes parsing, final stats
RsyncSuggestionTests 30 Security / Unit / Functional — validator injection rejection, prompt builder, AI-off path
SyncJobTests 29 Unit — job model, sync modes, Codable
NovaAPITests 29 Functional — API routing and payloads
FrameTests 28 Frame — view and model instantiation
PathValidationTests 26 Security — tilde expansion, iCloud, SMB/USB, traversal
SecurityTests 25 Security — credential exposure, sanitization, binary resolution
DestinationEditingTests 25 All seven — destination path editing (issue #4) regression lock
ScheduleConfigTests 24 Unit — plist generation, XML injection prevention
CommandBuilderTests 24 Unit — delta report filenames, parallel file-splitting strategies
CommandInjectionTests 23 Security — SSH host/user validation, shell escaping
AIKitAdoptionTests 21 Security 3, Performance 2, Retry 2, Unit 5, Integration 2, Functional 3, Frame 3, + legacy-code scan — NovaAIKit contract
WidgetDataTests 18 Unit — widget data, App Group
FunctionalFlowTests 18 Functional — end-to-end job flows
ExecutionHistoryTests 17 Unit — history entries
DeltaReportTests 17 Unit — itemize parsing
IntegrationTests 15 Integration — real rsync against temp dirs
DependencyCheckTests 15 Unit — job dependencies

Project Structure

RsyncGUI/
├── RsyncGUI/
│   ├── RsyncGUIApp.swift            App entry, Settings scene
│   ├── NovaAPIServer.swift          Loopback HTTP API (37424)
│   ├── AI/AppAI.swift               The app's one NovaAI instance + one-time legacy migration
│   ├── Design/ModernDesign.swift
│   ├── Models/                      SyncJob, RsyncOptions, ScheduleConfig, ExecutionHistory, DeltaReport, …
│   ├── Services/
│   │   ├── JobManager.swift, RsyncExecutor.swift, AdvancedExecutionService.swift, ScheduleManager.swift
│   │   ├── RsyncSuggestionService.swift   Prompt builder, validator, built-in phrase matcher
│   │   ├── AIInsightsService.swift        Rule-based backup insights
│   │   ├── MenuBarManager.swift, WidgetDataSync.swift
│   └── Views/                       ContentView, JobEditorView, LLMAssistViews (assistant + Settings › AI), …
├── RsyncGUI Widget/                 WidgetKit extension
├── RsyncGUITests/                   479 tests
├── scripts/release.sh               Developer ID archive → DMG → notarize
└── RsyncGUI.xcodeproj

Nova API Server

Port 37424 (127.0.0.1 loopback only). No authentication required.

Method Path Description
GET /api/status App status, version, job count, uptime
GET /api/ping Health check
GET /api/jobs List all sync jobs
GET /api/jobs/:id Single job detail
POST /api/jobs/:id/run Execute a job
POST /api/jobs/:id/dryrun Dry-run a job
GET /api/history Recent execution history
GET /api/jobs/:id/history History for a specific job
curl -s http://127.0.0.1:37424/api/status | python3 -m json.tool
curl -X POST http://127.0.0.1:37424/api/jobs/<uuid>/run

Changelog

See CHANGELOG.md. 1.8.0: AI migrated to NovaAIKit 1.0.0, built-in fallback for the assistant, Keychain migration, Google/Azure/AWS/IBM backends removed, zero-warning Release build.

License

MIT License — Copyright (c) 2026 Jordan Koch. See LICENSE.


Written by Jordan Koch

About

Native macOS rsync GUI: 100+ flag editor, live progress, launchd scheduling, multi-destination sync, delta reports, widget, loopback API, and optional AI 'describe it in English' (NovaAIKit: Ollama/MLX/OpenRouter/OpenAI…, built-in fallback, never auto-runs)

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages