Tether CLI Documentation

Tether is an open source tool that automatically syncs your development environment—dotfiles and packages—across multiple machines with end-to-end encryption.

Key Features

  • Automatic Sync — Background daemon watches for changes and syncs in real-time
  • End-to-End Encryption — AES-256-GCM encryption with passphrase-protected keys
  • Project Configs — Sync .env files and IDE settings across machines by Git remote
  • Package Manager Support — Syncs Homebrew, npm, pnpm, bun, and gem packages
  • Secret Detection — Prevents accidental syncing of API keys and credentials
  • Git-Backed — Full version history and rollback capabilities
  • Team Secrets — Share encrypted secrets across teams with age encryption

Installation

Homebrew (recommended)

brew tap paddo-tech/tap
brew install tether-cli

Requirements

  • Platform: macOS (Linux coming soon)
  • Shell: zsh or bash
  • Git: Configured with GitHub, GitLab, or self-hosted Git server

Quick Start

Get up and running in under a minute:

1. Initialize

tether init

Follow the prompts to configure your Git repository and encryption passphrase.

2. Verify Setup

tether status

You should see the daemon running and your synced files listed.

3. On Another Machine

brew tap paddo-tech/tap && brew install tether-cli
tether init --repo git@github.com:username/dotfiles.git

Enter the same passphrase. Your configs sync automatically.


How It Works

Architecture

Tether uses a Git repository as the sync backend. When you make changes to dotfiles or install packages, the background daemon detects these changes, encrypts your dotfiles with AES-256-GCM, and pushes them to your private Git repository.

On your other machines, Tether pulls these changes and applies them automatically.

Sync Flow

  1. Daemon detects file changes (every 5 minutes or manual tether sync)
  2. Secret scanner checks for credentials
  3. Files are encrypted (if enabled)
  4. Changes committed and pushed to Git
  5. Other machines pull and decrypt

Data Layout

Local Tether data lives in ~/.tether/:

~/.tether/
├── config.toml      # Configuration
├── state.json       # Sync state (hashes, timestamps)
├── sync/            # Git repository clone
├── daemon.pid       # Daemon process ID
├── daemon.log       # Daemon logs
└── ignore           # Secret scanning patterns

The sync repository structure:

sync/
├── dotfiles/        # Encrypted dotfiles
├── configs/         # Config directories
├── projects/        # Project-local configs
├── manifests/       # Package manifests
└── machines/        # Per-machine state

Commands

tether init

Initialize Tether on this machine.

tether init [--repo <URL>] [--no-daemon]
  • --repo <URL> — Git repository URL (interactive if omitted)
  • --no-daemon — Don't start daemon after init

tether sync

Manually trigger a sync.

tether sync [--dry-run] [--force]
  • --dry-run — Show what would be synced
  • --force — Skip conflict prompts

tether status

Show current sync status, daemon state, and synced files.

tether status

tether diff

Show differences between machines.

tether diff [--machine <NAME>]
  • --machine <NAME> — Compare with specific machine

tether daemon

Control the background daemon.

tether daemon <start|stop|restart|logs|install|uninstall>
  • start — Start the daemon
  • stop — Stop the daemon
  • restart — Restart the daemon
  • logs — View last 50 log lines
  • install — Install launchd service (auto-start on login)
  • uninstall — Remove launchd service

tether machines

Manage machines in sync network.

tether machines <list|rename|remove>
  • list — List all machines
  • rename <OLD> <NEW> — Rename a machine
  • remove <NAME> — Remove a machine

tether ignore

Manage ignore patterns and per-machine exclusions.

# Secret scanning patterns
tether ignore add <PATTERN>
tether ignore list
tether ignore remove <PATTERN>

# Per-machine file exclusions
tether ignore dotfile <FILE>
tether ignore project <PROJECT> <PATH>
tether ignore sync-list
tether ignore sync-remove <FILE>

tether config

Manage configuration.

tether config get <KEY>
tether config set <KEY> <VALUE>
tether config edit
tether config dotfiles
  • get <KEY> — Get config value (supports nested keys like packages.brew.enabled)
  • set <KEY> <VALUE> — Set config value
  • edit — Open config in editor
  • dotfiles — Interactive UI for managing synced files

tether team

Manage team sync repositories, secrets, and project sharing.

# Team management
tether team setup                     # Interactive wizard
tether team add <URL>                 # Add team repo
tether team list                      # List teams
tether team status                    # Show status
tether team enable/disable            # Toggle sync

# Organization mapping
tether team orgs add <ORG>            # Map GitHub org to team
tether team orgs list                 # List mapped orgs
tether team orgs remove <ORG>         # Unmap org

# Team secrets (age encryption)
tether team secrets add-recipient <KEY>   # Add member's public key
tether team secrets list-recipients       # List recipients
tether team secrets set <NAME>            # Set a secret
tether team secrets get <NAME>            # Get a secret
tether team secrets list                  # List all secrets

# Project secrets
tether team projects add <FILE>       # Share project secret
tether team projects list             # List shared secrets
tether team projects migrate          # Auto-migrate to team
tether team projects purge-personal   # Remove personal copies

tether resolve

Resolve file conflicts.

tether resolve [FILE]

Interactive resolution: keep local, use remote, merge, or skip.

tether unlock

Unlock encryption key with passphrase.

tether unlock

tether lock

Clear cached encryption key.

tether lock

tether upgrade

Upgrade all installed packages across enabled package managers.

tether upgrade

Configuration

Configuration lives at ~/.tether/config.toml:

[backend]
url = "git@github.com:username/dotfiles.git"

[security]
encrypt_dotfiles = true
scan_secrets = true

[dotfiles]
files = [".zshrc", ".gitconfig", ".vimrc"]
dirs = [".config/nvim", ".ssh/config"]

[project_configs]
enabled = true
search_paths = ["~/Projects", "~/Work"]
patterns = [".env.local", ".claude/settings.json"]

[packages.brew]
enabled = true

[packages.npm]
enabled = true

[packages.pnpm]
enabled = false

[packages.bun]
enabled = false

[packages.gem]
enabled = false

Key Configuration Options

Key Description
backend.url Git repository URL
security.encrypt_dotfiles Enable AES-256-GCM encryption
security.scan_secrets Enable secret detection before sync
dotfiles.files List of individual dotfiles to sync
dotfiles.dirs List of directories to sync
project_configs.enabled Sync project-local configs
packages.*.enabled Enable specific package manager sync

Package Managers

Tether syncs global packages across machines. Enable managers in config:

tether config set packages.brew.enabled true
tether config set packages.npm.enabled true

Homebrew

Syncs formulae, casks, and taps via Brewfile format. Casks include desktop apps like VS Code, Slack, Microsoft Office, and most macOS applications installed via brew install --cask.

# Manifest: manifests/Brewfile
tap "homebrew/cask"
brew "ripgrep"
brew "fd"
cask "visual-studio-code"
cask "slack"
cask "1password"

npm

Syncs global npm packages.

# Manifest: manifests/npm.txt
typescript
eslint
prettier

pnpm / bun / gem

Same format as npm—one package per line.


Encryption & Security

How Encryption Works

  1. On first init, a random 256-bit key is generated
  2. Key is encrypted with your passphrase using age
  3. Encrypted key is stored in the Git repo (safe to share)
  4. Dotfiles are encrypted with AES-256-GCM before commit
  5. On other machines, enter passphrase to decrypt the key

Secret Detection

Before syncing, Tether scans for common secret patterns:

  • AWS credentials (AKIA...)
  • GitHub tokens (ghp_..., gho_...)
  • Generic patterns (API_KEY=, password=)

Add ignore patterns for false positives:

tether ignore add 'EXAMPLE_KEY='

Unlocking

If the daemon can't decrypt (key not cached), run:

tether unlock

To clear the cached key:

tether lock

Team Sync

Share configurations and encrypted secrets across a team.

Setup

# Interactive setup wizard
tether team setup

# Or add manually
tether team add git@github.com:org/team-dotfiles.git

This clones the team repo and creates symlinks to team config files.

How It Works

  • Team configs are symlinked to their target locations
  • If you have write access, your changes sync back
  • Read-only access means you receive updates only
  • Auto-inject adds source lines to your personal dotfiles (optional)

Team Secrets

Share encrypted secrets using age public-key cryptography:

# Add your public key as a recipient
tether team secrets add-recipient age1...

# Set a secret (encrypted for all recipients)
tether team secrets set API_KEY

# Get a secret
tether team secrets get API_KEY

Organization Mapping

Map GitHub organizations to teams for automatic project secret sharing:

# Map org to team
tether team orgs add github.com/acme-corp

# Project secrets from that org are now shared with the team
tether team projects list

Project Secrets

Share .env files and project configs across team members:

# Share a project secret
tether team projects add .env

# Auto-migrate secrets from mapped orgs
tether team projects migrate

# Remove personal copies (use team version)
tether team projects purge-personal

Managing Teams

tether team list          # Show all teams
tether team switch other   # Switch active team
tether team disable        # Disable without removing
tether team remove         # Remove team completely

Troubleshooting

Daemon not running

tether daemon start
tether daemon logs

Sync conflicts

tether resolve

Or force one direction:

tether sync --force

Encryption issues

# Unlock with passphrase
tether unlock

# Check if key exists
ls ~/.tether/sync/.tether-key.age

"Secret detected" blocking sync

Either remove the secret or add an ignore pattern:

tether ignore add 'MY_SAFE_PATTERN='

Reset everything

tether daemon stop
rm -rf ~/.tether
tether init

View full logs

cat ~/.tether/daemon.log