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 syncs changes every 5 minutes
  • End-to-End Encryption — AES-256-GCM encryption with passphrase-protected keys
  • Machine Profiles — Per-machine control over which dotfiles and packages sync
  • Project Configs — Sync .env files and IDE settings across machines by Git remote
  • Package Manager Support — Syncs Homebrew, npm, pnpm, bun, gem, and uv 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 (versioned)
├── state.json       # Sync state (hashes, timestamps)
├── sync/            # Personal sync repo
├── teams/<name>/    # Team sync repos
├── collabs/         # Collab project configs
├── backups/         # File backups
├── identity.pub     # Age public key (for team secrets)
├── daemon.pid       # Daemon process ID
├── daemon.log       # Daemon logs
└── conflicts.json   # Conflict state

The sync repository structure:

sync/
├── profiles/<name>/ # Profile-specific dotfiles (encrypted)
├── dotfiles/        # Flat-layout dotfiles (legacy/shared)
├── configs/         # App configs (tether config, etc.)
├── projects/        # Project-local configs
├── manifests/       # Package manifests
└── machines/        # Per-machine state

Dashboard (TUI)

Run tether with no arguments to launch the interactive terminal dashboard.

tether

The dashboard has five tabs you can switch between using number keys or arrow keys:

Tabs

Tab Key Description
Overview 1 Dotfiles, packages, machines, and recent activity at a glance
Files 2 Browse synced dotfiles with file history viewer, inline diffs, and restore
Packages 3 Collapsible package manager sections with package names. Uninstall packages with confirmation
Machines 4 Connected machines with profile assignments, hostname, OS, and package counts
Config 5 Inline config editor with boolean toggles, validated text fields, and list editing

Keybindings

Key Action
q Quit dashboard
s Trigger a manual sync
d Toggle daemon start/stop
r Refresh dashboard data
? Show help overlay with all keybindings
1-5 Switch to tab by number
Tab Next tab
↑ ↓ / j k Navigate within lists
Enter Expand/collapse sections, confirm actions
p Open profile picker (assign profile to this machine)
h View file history (Files tab)
Esc / Ctrl+c Quit dashboard

Commands

tether / tether dashboard

Launch the interactive TUI dashboard. See Dashboard above for details.

tether

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 and profiles in sync network.

tether machines <list|rename|remove|profile>
  • list — List all machines with profile assignments
  • rename <OLD> <NEW> — Rename a machine
  • remove <NAME> — Remove a machine
  • profile set <PROFILE> — Assign a profile to this machine
  • profile unset — Remove profile assignment
  • profile create <NAME> — Create a new profile
  • profile edit <NAME> — Edit profile dotfile/package config
  • profile list — List all profiles and assignments

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
tether config features              # List feature toggles
tether config features enable <F>   # Enable a feature
tether config features disable <F>  # Disable a feature
  • 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
  • features — Manage feature toggles (see Feature Toggles)

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

tether history

Show file change history from the sync repo.

tether history                   # Show recent changes
tether history <FILE>            # Show history for a specific file

tether restore

Restore files from backup or git history.

tether restore list              # List available backups
tether restore file              # Interactive restore
tether restore file --from <TS>  # Restore from specific timestamp
tether restore git               # Restore dotfiles from git history

tether identity

Manage age identity for team secrets encryption.

tether identity init     # Generate new age identity
tether identity show     # Show your public key
tether identity unlock   # Unlock with passphrase
tether identity lock     # Clear cached key
tether identity reset    # Generate new (destroys old)

tether packages

List installed packages across all managers with interactive multi-select uninstall.

tether packages             # Interactive mode
tether packages --list      # Non-interactive list
  • --list — Print packages without interactive UI
  • -y — Skip confirmation prompts

tether collab

Share project secrets with GitHub collaborators (requires write access).

tether collab init              # Initialize collab for current project
tether collab join <URL>        # Join existing collab
tether collab add <FILE>        # Add secret file (e.g., .env)
tether collab refresh           # Re-encrypt for current collaborators
tether collab list              # List all collabs
tether collab remove            # Remove a collab

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

Machine Profiles

Profiles let you control which dotfiles and packages sync to each machine. For example, a "work" profile might include IDE configs that your "server" profile doesn't need.

Creating Profiles

tether machines profile create work
tether machines profile create server

Assigning a Profile

# Assign to current machine
tether machines profile set work

# View assignments
tether machines profile list

Editing Profile Config

tether machines profile edit work

Each profile has its own dotfile and package lists. Dotfiles are stored under profiles/<name>/ in the sync repo.

Cross-Profile Discovery

When syncing, Tether detects dotfiles in other profiles and offers to adopt them into yours via an interactive prompt.

Migration

Existing flat-layout dotfiles (stored in dotfiles/) are automatically migrated to profile directories on first sync after upgrading.


Feature Toggles

Control what Tether syncs with feature toggles:

tether config features              # List all toggles
tether config features enable <F>   # Enable a feature
tether config features disable <F>  # Disable a feature
Feature Default Description
personal_dotfiles true Sync personal dotfiles (.zshrc, etc.)
personal_packages true Sync personal package manifests
team_dotfiles false Sync team dotfiles (requires team setup)
collab_secrets false Enable collaborator-based secret sharing
team_layering false Merge team + personal dotfiles (experimental)

Example: Team-only mode (no personal sync):

tether config features disable personal_dotfiles
tether config features disable personal_packages
tether config features enable team_dotfiles

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.

uv (Python)

Syncs global Python packages installed via uv.

# Enable uv sync
tether config set packages.uv.enabled true

# Manifest: manifests/uv.txt
ruff
black
mypy

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