esync

command module
v0.3.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 16, 2026 License: MIT Imports: 1 Imported by: 0

README

esync

Watch local files and sync them with rsync, shown in a live TUI

Vibecoded

Demo of esync TUI

Installation

Install with go install:

go install github.com/louloulibs/esync@latest

Or build from source:

git clone https://github.com/louloulibs/esync.git
cd esync
go build -o esync .

Quick Start

# 1. Generate a config file (imports .gitignore, detects common dirs)
esync init -r user@host:/path/to/dest

# 2. Preview what will be synced
esync check

# 3. Start watching and syncing
esync sync

Commands Reference

esync sync

Watch a local directory for changes and sync them to a destination using rsync. Launches an interactive TUI by default.

esync sync                          # use config file, launch TUI
esync sync -c project.toml          # use a specific config file
esync sync -l ./src -r server:/opt  # quick mode, no config file needed
esync sync --daemon                 # run in background (no TUI)
esync sync --dry-run                # show what would sync, don't transfer
esync sync --initial-sync           # force a full sync on startup
esync sync -v                       # verbose output (daemon mode)
Flag Short Description
--local -l Local path to watch
--remote -r Remote destination path
--daemon Run in daemon mode (no TUI)
--dry-run Show what would be synced without syncing
--initial-sync Force a full sync on startup
--verbose -v Verbose output
--config -c Config file path (global flag)

When both -l and -r are provided, esync runs without a config file (quick mode). Otherwise it searches for a config file automatically.

esync init

Generate an .esync.toml configuration file in the current directory. Inspects the project for .gitignore patterns and common directories (.venv, build, __pycache__, etc.) to auto-populate ignore rules.

esync init                          # interactive prompt for remote
esync init -r user@host:/path       # pre-fill the remote destination
esync init -c ~/.config/esync/config.toml -r server:/data  # custom path
Flag Short Description
--remote -r Pre-fill remote destination
--config -c Output file path (default: ./.esync.toml)

esync check

Validate your configuration and preview which files would be included or excluded by the ignore patterns.

esync check                         # auto-detect config
esync check -c project.toml         # check a specific config file
Flag Short Description
--config -c Config file path

esync edit

Open the config file in your $EDITOR (defaults to vi). After saving, the config is validated and a file preview is shown. If validation fails, you can re-edit or cancel.

esync edit                          # auto-detect config
esync edit -c project.toml          # edit a specific config file
Flag Short Description
--config -c Config file path

esync status

Check if an esync daemon is currently running. Reads the PID file from the system temp directory.

esync status
# esync daemon running (PID 12345)
# — or —
# No esync daemon running.

Configuration

esync uses TOML configuration files. The config file is searched in this order:

  1. Path given via -c / --config flag
  2. ./.esync.toml (current directory)
  3. ~/.config/esync/config.toml
  4. /etc/esync/config.toml

Full Annotated Example

This shows every available field with explanatory comments:

# =============================================================================
# esync configuration file
# =============================================================================

[sync]
# Local directory to watch for changes (required)
local  = "/home/user/projects/myapp"

# Remote destination — can be a local path or an scp-style remote (required)
# Examples:
#   "/backup/myapp"                   — local path
#   "server:/opt/myapp"               — remote using SSH config alias
#   "user@192.168.1.50:/opt/myapp"    — remote with explicit user
remote = "deploy@prod.example.com:/var/www/myapp"

# Polling interval in seconds (default: 1)
# This is used internally; the watcher reacts to filesystem events,
# so you rarely need to change this.
interval = 1

# --- SSH Configuration (optional) ---
# Use this section for fine-grained SSH control.
# If omitted, esync infers SSH from the remote string (e.g. user@host:/path).
[sync.ssh]
host             = "prod.example.com"
user             = "deploy"
port             = 22
identity_file    = "~/.ssh/id_ed25519"
interactive_auth = false   # set to true for 2FA / keyboard-interactive auth

# =============================================================================
[settings]

# Debounce interval in milliseconds (default: 500)
# After a file change, esync waits this long for more changes before syncing.
# Lower = more responsive, higher = fewer rsync invocations during rapid edits.
watcher_debounce = 500

# Run a full sync when esync starts (default: false)
initial_sync = false

# Path prefixes to sync, relative to local. Empty means everything.
# Keep include simple and explicit; use ignore for fine-grained filtering.
include = []

# Patterns to ignore — applied to both the watcher and rsync --exclude flags.
# Supports glob patterns. Matched against file/directory base names.
ignore = [
    ".git",
    "node_modules",
    ".DS_Store",
    "__pycache__",
    "*.pyc",
    ".venv",
    "build",
    "dist",
    ".tox",
    ".mypy_cache",
]

# --- Rsync Settings ---
[settings.rsync]
archive    = true           # rsync --archive (preserves symlinks, permissions, timestamps)
compress   = true           # rsync --compress (compress data during transfer)
backup     = false          # rsync --backup (make backups of replaced files)
backup_dir = ".rsync_backup"  # directory for backup files when backup = true
progress   = true           # rsync --progress (show transfer progress)

# Extra arguments passed directly to rsync.
# Useful for flags esync doesn't expose directly.
extra_args = []

# Additional rsync-specific ignore patterns (merged with settings.ignore).
ignore = []

# --- Logging ---
[settings.log]
# Log file path. If omitted, no log file is written.
# Logs are only written in daemon mode.
# file = "/var/log/esync.log"

# Log format: "text" or "json" (default: "text")
format = "text"

Minimal Config

The smallest usable config file:

[sync]
local  = "."
remote = "user@host:/path/to/dest"

Everything else uses sensible defaults: archive mode, compression, 500ms debounce, and standard ignore patterns (.git, node_modules, .DS_Store).

SSH Config Example

For remote servers with a specific SSH key and non-standard port:

[sync]
local  = "."
remote = "/var/www/myapp"

[sync.ssh]
host          = "myserver.example.com"
user          = "deploy"
port          = 2222
identity_file = "~/.ssh/deploy_key"

When [sync.ssh] is present, esync constructs the full destination as deploy@myserver.example.com:/var/www/myapp and passes SSH options (port, identity file, ControlMaster) to rsync automatically.

2FA / Keyboard-Interactive Authentication

If your server requires two-factor authentication:

[sync]
local  = "."
remote = "/home/user/project"

[sync.ssh]
host             = "secure-server.example.com"
user             = "admin"
identity_file    = "~/.ssh/id_ed25519"
interactive_auth = true

Custom Rsync Flags

Pass extra arguments directly to rsync using extra_args:

[sync]
local  = "./src"
remote = "server:/opt/app/src"

[settings.rsync]
archive    = true
compress   = true
extra_args = [
    "--delete",           # delete files on remote that don't exist locally
    "--chmod=Du=rwx,Dgo=rx,Fu=rw,Fgo=r",  # set permissions on remote
    "--exclude-from=.rsyncignore",          # additional exclude file
    "--bwlimit=5000",     # bandwidth limit in KBytes/sec
]

Include Filters (Monorepo Support)

In a large repo, you may only want to sync specific subtrees. Use include to name the directories you care about, then use ignore for fine-grained filtering within them:

[sync]
local  = "/home/user/monorepo"
remote = "server:/opt/monorepo"

[settings]
include = ["src", "docs/api"]
ignore  = [".git", "node_modules", ".DS_Store"]
  • include takes path prefixes relative to local (not globs)
  • Empty include (the default) means sync everything — fully backwards compatible
  • When set, only files under the listed prefixes are watched and synced
  • ignore then further refines within the included paths

Separate Watcher and Rsync Ignore Patterns

The top-level settings.ignore patterns are used by both the file watcher and rsync. If you need rsync-specific excludes (patterns the watcher should still see), use settings.rsync.ignore:

[settings]
# These patterns are used by BOTH the watcher and rsync
ignore = [".git", "node_modules", ".DS_Store"]

[settings.rsync]
# These patterns are ONLY passed to rsync as --exclude flags
ignore = ["*.log", "*.tmp", "cache/"]

Logging Config

[settings.log]
file   = "/var/log/esync.log"
format = "json"

Text format output:

15:04:05 INF started local=/home/user/project pid=12345 remote=server:/opt/app
15:04:07 INF sync_complete bytes=2048 duration=150ms files=3
15:04:12 ERR sync_failed error=rsync error: ...

JSON format output:

{"time":"15:04:05","level":"info","event":"started","local":"/home/user/project","remote":"server:/opt/app","pid":12345}
{"time":"15:04:07","level":"info","event":"sync_complete","files":3,"bytes":2048,"duration":"150ms"}

TUI Keyboard Shortcuts

The interactive TUI (default mode) provides two views: Dashboard and Logs.

Dashboard View

Key Action
q Quit
Ctrl+C Quit
p Pause / resume watching
r Force a full resync
l Switch to log view
j / Down Scroll down
k / Up Scroll up
/ Enter filter mode
Enter Apply filter (in filter mode)
Esc Clear filter (in filter mode)

Log View

Key Action
q Quit
Ctrl+C Quit
l Switch back to dashboard
j / Down Scroll down
k / Up Scroll up
PgDn Scroll down one page
PgUp Scroll up one page
G Jump to end
g Jump to top
f Toggle follow mode (tail -f)
/ Enter filter mode
Enter Apply filter (in filter mode)
Esc Clear filter (in filter mode)

Daemon Mode

Run esync in the background without the TUI:

# Start daemon
esync sync --daemon

# Start daemon with verbose output and JSON logging
esync sync --daemon -v -c project.toml

# Check if the daemon is running
esync status

# Stop the daemon
kill $(cat /tmp/esync.pid)

The daemon writes its PID to /tmp/esync.pid so you can check status and stop it later. On receiving SIGINT or SIGTERM the daemon shuts down gracefully.

When a log file is configured, the daemon writes structured entries for every sync event:

# Monitor logs in real-time
tail -f /var/log/esync.log

SSH Setup

esync uses rsync's SSH transport for remote syncing. There are two ways to configure SSH.

Inline (via remote string)

If your ~/.ssh/config is already set up, just use the host alias:

[sync]
local  = "."
remote = "myserver:/opt/app"

This works when myserver is defined in ~/.ssh/config:

Host myserver
    HostName 192.168.1.50
    User deploy
    IdentityFile ~/.ssh/id_ed25519

Explicit SSH Section

For full control without relying on ~/.ssh/config:

[sync.ssh]
host          = "192.168.1.50"
user          = "deploy"
port          = 22
identity_file = "~/.ssh/id_ed25519"

When the [sync.ssh] section is present, esync automatically enables SSH ControlMaster with these options:

  • ControlMaster=auto -- reuse existing SSH connections
  • ControlPath=/tmp/esync-ssh-%r@%h:%p -- socket path for multiplexing
  • ControlPersist=600 -- keep the connection alive for 10 minutes

This avoids re-authenticating on every sync and significantly speeds up repeated transfers.

2FA Authentication

Set interactive_auth = true in the SSH config to enable keyboard-interactive authentication for servers that require a second factor:

[sync.ssh]
host             = "secure.example.com"
user             = "admin"
identity_file    = "~/.ssh/id_ed25519"
interactive_auth = true

Examples

Local directory sync

Sync a source directory to a local backup:

esync sync -l ./src -r /backup/src

Remote sync with SSH

Sync to a remote server using a config file:

# .esync.toml
[sync]
local  = "."
remote = "deploy@prod.example.com:/var/www/mysite"

[settings]
ignore = [".git", "node_modules", ".DS_Store", ".env"]
esync sync

Quick sync (no config file)

Sync without a config file by passing both paths on the command line:

esync sync -l ./project -r user@server:/opt/project

This uses sensible defaults: archive mode, compression, 500ms debounce, and ignores .git, node_modules, .DS_Store.

Daemon mode with JSON logs

Run in the background with structured logging:

# .esync.toml
[sync]
local  = "/home/user/code"
remote = "server:/opt/code"

[settings]
initial_sync = true

[settings.log]
file   = "/var/log/esync.log"
format = "json"
esync sync --daemon -v
# esync daemon started (PID 54321)
# Watching: /home/user/code -> server:/opt/code

Custom rsync flags (delete extraneous files)

Keep the remote directory in exact sync by deleting files that no longer exist locally:

# .esync.toml
[sync]
local  = "./dist"
remote = "cdn-server:/var/www/static"

[settings.rsync]
extra_args = ["--delete", "--chmod=Fu=rw,Fgo=r,Du=rwx,Dgo=rx"]
esync sync --initial-sync

Dry run to preview changes

See what rsync would do without actually transferring anything:

esync sync --dry-run

System Requirements

  • Go 1.22+ (for building from source)
  • rsync 3.1+ (required for --info=progress2 real-time transfer progress)
  • macOS or Linux (uses fsnotify for filesystem events)

macOS note: The built-in /usr/bin/rsync is Apple's openrsync which is too old. Install a modern rsync via Homebrew: brew install rsync. esync will automatically prefer the Homebrew version when available.

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
demo
src command
internal
config
Package config handles loading, validating, and providing defaults for esync TOML configuration files.
Package config handles loading, validating, and providing defaults for esync TOML configuration files.
syncer
Package syncer builds and executes rsync commands based on esync configuration, handling local and remote (SSH) destinations.
Package syncer builds and executes rsync commands based on esync configuration, handling local and remote (SSH) destinations.
tui
Package tui provides a terminal user interface for esync built on Bubbletea and Lipgloss.
Package tui provides a terminal user interface for esync built on Bubbletea and Lipgloss.
watcher
Package watcher monitors a directory tree for file-system changes using fsnotify and debounces rapid events into a single callback.
Package watcher monitors a directory tree for file-system changes using fsnotify and debounces rapid events into a single callback.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL