Mac Developer Environment Snapshot & Restore CLI
Scan. Bundle. Restore. Identical dev setup in minutes.
# One-liner install (latest release)
curl -fsSL https://raw.githubusercontent.com/moinsen-dev/machinist/main/install.sh | bash
# Or with Homebrew (coming soon)
# brew install moinsen-dev/tap/machinist
# Or build from source
git clone https://github.com/moinsen-dev/machinist.git && cd machinist && make install# 1. On your current Mac — capture everything
machinist snapshot --output setup.toml
# 2. Bundle into a portable DMG
machinist dmg setup.toml --output ~/Desktop/machinist-setup.dmg
# 3. On your new Mac — restore
# Option A: Double-click install.command in the DMG
# Option B: CLI
machinist restore setup.tomlA developer buys a new Mac. Instead of spending days installing tools, copying shell configs, and cloning repos, they run one DMG — and have an identical working environment in 20 minutes.
machinist is a Go CLI tool that:
- Scans your current Mac (dev tools, system settings, repos, configs)
- Generates a structured TOML manifest of everything found
- Bundles the manifest + config files into a DMG
- Restores everything on a new Mac via a generated shell script
machinist is not a backup tool — it's a setup automator. The DMG contains instructions (brew install, git clone, mkdir -p), not copies of your tools. Config files that can't be installed via a package manager are included directly.
┌─────────────────────────────────────────────────┐
│ Snapshot │
│ (Complete picture of a machine's dev env) │
├─────────────────────────────────────────────────┤
│ │
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │ Scanner │ │ Scanner │ │ Scanner │ │
│ │ (Homebrew) │ │ (Shell) │ │ (Git) │ │
│ └─────┬─────┘ └─────┬─────┘ └─────┬─────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ Manifest (TOML) │ │
│ │ Packages, Configs, Repos, Defaults... │ │
│ └─────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ Bundler │ │
│ │ Manifest + Files → DMG │ │
│ └─────────────────────────────────────────┘ │
└─────────────────────────────────────────────────┘
# Snapshot — capture your existing system
machinist snapshot
machinist snapshot --output ~/Desktop/machinist-snapshot.toml
machinist snapshot --interactive
machinist snapshot --dry-run
# Scan — run individual scanners
machinist scan homebrew
machinist scan shell
machinist scan git-repos --search-paths ~/Code,~/Projects
# Compose — build a setup from a profile or existing manifest
machinist compose flutter-ios
machinist compose fullstack-js --add docker,postgres
machinist compose flutter-ios --output setup.toml
machinist compose --from-file manifest.toml --output setup.toml
# DMG — bundle manifest into a self-contained DMG
machinist dmg manifest.toml
machinist dmg manifest.toml --output ~/Desktop/setup.dmg
# Restore — on a new Mac, from mounted DMG or local manifest
machinist restore
machinist restore manifest.toml
machinist restore --skip homebrew,fonts
machinist restore --dry-run
machinist restore --only shell,git,ssh
machinist restore --yes
# MCP Server — let AI tools drive machinist
machinist serve # stdio (Claude Code, Cursor)
machinist serve --port 3333 # SSE (Claude Desktop, web clients)
# Info
machinist list
machinist list scanners
machinist list profiles
machinist versionmachinist includes an MCP (Model Context Protocol) server, allowing any MCP-compatible AI to compose and build Mac setups interactively.
User → AI: "I'm a Flutter developer, I work with Firebase and Docker,
and I use VS Code."
AI calls machinist MCP tools:
1. list_profiles() → finds "flutter-ios" as base
2. compose_manifest( → adds Docker, Firebase, VS Code extensions
base: "flutter-ios",
add: ["docker", "firebase"])
3. build_dmg(manifest) → generates the DMG
User runs DMG on new Mac → done.
Configure in Claude Code or Claude Desktop:
{
"mcpServers": {
"machinist": {
"command": "machinist",
"args": ["serve"]
}
}
}| Profile | Description |
|---|---|
minimal |
Homebrew, Git, zsh+starship, VS Code |
fullstack-js |
Node.js, pnpm, Docker, Postgres, VS Code |
flutter-ios |
Flutter, Xcode, Simulators, CocoaPods |
python-data |
Python, Jupyter, conda, Docker |
rust-dev |
Rust, rustup+nightly, cargo tools |
go-dev |
Go, Docker, Postgres, VS Code |
devops |
Docker, Kubernetes, Terraform, AWS/GCP CLI |
Profiles are composable — use them as a base and add/remove packages as needed.
machinist uses modular scanners, each responsible for a specific domain. Only scanners that find results produce output in the manifest.
Homebrew (taps, formulae, casks, services), Node.js (nvm/fnm + globals), Python (pyenv/uv + globals), Rust (rustup + cargo), Ruby, Go, Java/Kotlin (SDKMAN), Flutter/Dart, Deno, Bun, asdf/mise
Shell config files (.zshrc, .zshenv, etc.), frameworks (oh-my-zsh, prezto), prompts (starship, p10k), terminal emulators (iTerm2, Warp, Alacritty, WezTerm), tmux, direnv
Git config, global gitignore, templates, signing keys (GPG/SSH), credential helpers, GitHub CLI (config + extensions), all git repos (remote URLs, branches, submodules)
VSCode, Cursor, JetBrains (IntelliJ, PyCharm, WebStorm, Android Studio), Neovim, Vim, Xcode
Docker (config, frequently used images), Colima, OrbStack, Podman
AWS CLI, GCP, Azure, Kubernetes, Terraform, Vercel, Fly.io, Firebase, Cloudflare
Dock, Finder, Keyboard, Trackpad, Mission Control, Accessibility, Login Items, Hosts file, Spotlight, Screenshots, Locale/Timezone, Menu Bar, Computer Name
Wi-Fi networks, VPN configurations, DNS settings, Proxy settings, Tailscale
SSH keys (encrypted with age), GPG keys (encrypted), age keys
- Fonts — user-installed fonts + Nerd Fonts via Homebrew
- Productivity tools — Raycast, Alfred, Karabiner-Elements, Rectangle, BetterTouchTool, 1Password CLI
- Database clients — PostgreSQL, MySQL, TablePlus, DBeaver, Redis
- Package registries — npm, pip, Cargo, Gem, CocoaPods, Pub
- Browser — default browser, extensions checklist
- AI developer tools — Claude Code, Ollama models
- API tools — Postman/Insomnia exports, ngrok, mkcert
- XDG config catch-all — bat, lazygit, htop, btop, aerospace, and custom paths
- Workspace — folder structure, git repos, .env files (encrypted)
- Scheduled tasks — crontab, LaunchAgents
The restore script executes 50+ stages in dependency order — SSH keys are decrypted before git repos are cloned, Homebrew is installed before language runtimes, etc. Each stage is:
- Skippable (
--skip stage-name) - Idempotent (checks if already installed/present)
- Logged to
~/.machinist/restore.log - Fault-tolerant (logs errors, continues to next stage)
A post-restore checklist is generated for things that can't be automated: macOS permissions (TCC), browser extensions, Bluetooth pairing, VPN passwords, etc.
| Concern | Solution |
|---|---|
| SSH keys | Encrypted with age — set passphrase during snapshot, enter during restore |
| .env files | Same age encryption |
| Sensitive defaults | Scanner asks explicitly ("Include SSH keys? [y/N]") |
| DMG password | Optional: encrypt DMG itself via hdiutil |
Data is categorized into three sensitivity levels:
- public — Homebrew packages, VSCode extensions, macOS defaults (plaintext in manifest)
- sensitive — .npmrc, AWS config, .gitconfig (included with user warning)
- secret — SSH keys, .env files, .pgpass (age-encrypted, explicit opt-in only)
- Go for scanner + bundler — fast compilation, excellent
os/execfor shell commands, single binary,text/templatein stdlib - Shell script for restore — must run on vanilla Mac without Go
- TOML for manifest — human-readable, human-editable (BurntSushi/toml)
- filippo.io/age for encryption — the reference implementation, written in Go
- hdiutil for DMG — macOS-native, no extra dependency
- cobra for CLI — de-facto standard for Go CLIs (used by kubectl, gh, docker)
- text/template (stdlib) for restore script generation
# Build
make build
./machinist version
# Run
go run ./cmd/machinist snapshot --dry-run
go run ./cmd/machinist scan homebrew
go run ./cmd/machinist list scanners
# Test
make testReleases are built with goreleaser and published to GitHub Releases.
# Test a release build locally (no publish)
make release-local
# Create a tagged release
make release TAG=v0.1.0This produces signed macOS binaries for both Apple Silicon (arm64) and Intel (amd64). The install.sh script automatically detects the architecture and downloads the right binary.