bak is a CLI tool that backs up, restores, and syncs your AI coding configuration across machines. Supports Claude Code, Cursor, Codex, Windsurf, Kiro, KiloCode, pi.dev, and OpenCode. Never lose your skills, MCP servers, plugins, agents, or config files again.
- 🤖 Multi-Agent Support — Auto-detects 8 AI coding tools: Claude Code, Cursor, Codex, Windsurf, Kiro, KiloCode, pi.dev, and OpenCode
- 🔄 Backup & Restore — Preset-based backups (quick, full, skills) with mandatory dry-run before restore
- 🔒 Secret Detection — Automatically excludes API keys, tokens, and generates
.env.exampletemplates - ☁️ Multi-Cloud Sync — Push/pull backups to GitHub Gist, GitHub Repo, Codeberg, Gitea/Forgejo, and rclone (Google Drive, S3, etc.)
- 🔐 Encryption at Rest — AES-256-GCM encryption with Argon2id key derivation; opt-in per profile
- 👤 Machine Profiles —
bak profilecommands to scope backups per machine with independent adapter, category, preset, provider, and encryption settings - 🖥️ Cross-Platform — Works on Windows, macOS, and Linux with path normalization
- 🎯 Interactive Picker — TUI with bubbletea for selective category backup
- ↩️ Undo — Git-backed safety with
bak undo(git revert) - 📦 Export — Export backups as portable tar.gz archives
git clone https://github.com/danielxxomg/bak-cli.git
cd bak-cli
go build -o bak .go install github.com/danielxxomg/bak-cli@latestDownload from GitHub Releases.
# Create a backup
bak backup
# Create a backup scoped to a machine profile
bak profile create work --provider github-gist --preset full --encrypt
bak backup --profile work
# Preview what would be restored
bak restore --dry-run 20260604-150405
# Restore a backup
bak restore 20260604-150405
# Undo the last restore
bak undo
# Sync to cloud (GitHub Gist, Codeberg, Gitea, rclone, etc.)
bak login
bak push --provider github-gist
bak pull
# Verify backup integrity
bak verify 20260604-150405
bak verify --verbose 20260604-150405
# Compare two backups
bak diff 20260604-150405 20260605-080000| Command | Description |
|---|---|
bak backup [--preset quick|full|skills] [--profile <name>] |
Create a backup |
bak restore [--dry-run] [--force] <id> |
Restore a backup |
bak undo |
Revert the last operation |
bak list [--provider <name>] |
List local or cloud backups |
bak pick |
Interactive TUI picker |
bak push [id] [--provider <name>] [--profile <name>] |
Push to a cloud backend |
bak pull [id] [--provider <name>] [--profile <name>] |
Pull from a cloud backend |
bak export <id> [--output path] |
Export as tar.gz |
bak login [--provider <name>] |
Authenticate with a cloud provider |
bak profile create|list|show|delete |
Manage machine profiles |
bak verify [--verbose] <id> |
Verify backup integrity |
bak diff <id1> <id2> |
Show file-level differences between two backups |
bak version |
Show version info |
bak schedule create|list|remove |
Manage OS-native backup schedules |
bak wizard |
Launch the interactive profile/backup wizard |
Backups are stored in ~/.bak/backups/<id>/:
~/.bak/
├── config.json # bak configuration
└── backups/
└── 20260604-150405/
├── manifest.json
├── .env.example
└── opencode/
├── skills/
├── commands/
├── plugins/
└── config files...
For cloud sync, configure a GitHub token:
# Option 1: Interactive (GitHub only)
bak login
# Option 2: Environment variable
export GITHUB_TOKEN=ghp_xxxxxxxxxxxx
# Option 3: Config file
bak config set github.token ghp_xxxxxxxxxxxxUse --provider to select a cloud backend for push/pull/list:
| Provider | Flag | Config Key | Env Token |
|---|---|---|---|
| GitHub Gist | github-gist (default) |
providers.github.token |
GITHUB_TOKEN |
| GitHub Repo | github-repo |
providers.github.token + .repo |
GITHUB_TOKEN |
| Codeberg | codeberg |
providers.codeberg.token + .repo |
CODEBERG_TOKEN |
| Gitea / Forgejo | gitea |
providers.gitea.token + .repo + .base_url |
GITEA_TOKEN |
| Rclone | rclone |
providers.rclone.remote |
— |
# Push to a specific provider
bak push --provider codeberg
# List cloud backups
bak list --provider github-gist
# Configure non-GitHub providers
bak config set providers.codeberg.token <your-token>
bak config set providers.codeberg.repo owner/backupsProfiles let you scope backups to specific machines with independent settings for adapters, categories, preset, provider, and encryption.
# Create a profile for your work laptop
bak profile create work-laptop --provider github-gist --preset full --encrypt
# Create a lightweight profile for your home PC
bak profile create home-pc --provider github-repo --preset quick
# Create a profile that only backs up OpenCode and Cursor config
bak profile create dev-box --provider codeberg --adapters opencode,cursor --categories config,skills
# List all profiles
bak profile list
# Show full profile details
bak profile show work-laptop
# Delete a profile
bak profile delete old-machineUse a profile with --profile on backup, push, or pull:
bak backup --profile work-laptop
bak push --profile work-laptop
bak pull --profile work-laptopWhen --profile is set, its preset, categories, and adapter list override
the equivalent CLI flags.
You can define custom backup presets as YAML files under
~/.config/bak/presets/. Each file defines a preset with a name and
category list.
Example (~/.config/bak/presets/my-full.yaml):
name: my-full
categories:
- config
- skills
- commands
- plugins
- agents
metadata:
description: "Custom full preset without MCP servers"
author: "you"If a custom preset has the same name as a built-in (quick, full, skills),
use --override to prefer the custom version:
bak backup --preset full --overrideWithout --override, name conflicts produce an error so you don't
accidentally replace built-in behavior.
Custom presets are merged with built-ins: any preset name not matching a built-in is treated as a custom preset loaded from YAML.
You can register adapters for new tools without writing Go code by
placing YAML declarations in ~/.config/bak/adapters/.
Example (~/.config/bak/adapters/myapp.yaml):
name: myapp
config_path: .config/myapp
categories:
- name: config
root_files:
- config.yaml
- settings.json
- name: skills
sub_path: skills
is_dir: true
- name: commands
sub_path: commands
is_dir: truebak backup will auto-detect your custom adapter if the config_path
directory exists under your home directory. Use --adapter myapp to
force it.
Custom adapters that share a name with a built-in adapter require
--override to replace the built-in:
bak backup --adapter opencode --overrideSee examples/presets/ and examples/adapters/ for annotated samples.
Schedule automatic backups using OS-native task schedulers (crontab on Linux/macOS, schtasks on Windows).
# Create a daily scheduled backup for a profile
bak schedule create work --every daily
# List all active bak-cli schedules
bak schedule list
# Remove a schedule
bak schedule remove workSupported intervals: daily, weekly, every-12h, every-6h.
Each schedule runs bak backup --profile <name> && bak push --profile <name>
at the configured interval.
Use --interactive on profile create or login to launch a step-by-step
TUI wizard powered by Bubble Tea.
# Create a profile interactively (no flags needed)
bak profile create my-machine --interactive
# Login with provider selection wizard
bak login --interactiveThe wizard walks through provider selection, preset choice, adapter toggling, and category selection with keyboard navigation.
Encryption is enabled per profile with the --encrypt flag on bak profile create.
Encrypted archives use AES-256-GCM with Argon2id key derivation (64 MB RAM,
3 iterations, 4 parallelism).
| Feature | Detail |
|---|---|
| Algorithm | AES-256-GCM |
| Key derivation | Argon2id (64 MB, 3 iter, 4 parallel) |
| Magic bytes | BAK_ENC\x01 — instant detection without parsing |
| Password input | Interactive prompt (stdin) or BAK_ENCRYPTION_PASSWORD env var |
| Backward compat | Plaintext archives from v0.2.0 are detected and handled automatically |
Push flow: bak push --profile work encrypts the tar.gz archive before upload.
Pull flow: bak pull detects magic bytes, prompts for password, decrypts on the fly.
# Set password via environment variable (CI/scripts)
export BAK_ENCRYPTION_PASSWORD="your-secure-password"
bak push --profile work
# Or use interactive prompt (no env var set)
bak push --profile work
# → Enter encryption password: ********Encryption metadata (algorithm, KDF, salt, nonce) is stored in the backup manifest for auditability. The password itself is never persisted to disk.
bak backup auto-detects installed agents in priority order:
| Agent | Path | Priority |
|---|---|---|
| Claude Code | ~/.claude/ |
1 |
| Cursor | ~/.cursor/ |
2 |
| Codex | ~/.codex/ |
3 |
| Windsurf | ~/.codeium/windsurf/ |
4 |
| Kiro | ~/.kiro/ |
5 |
| KiloCode | ~/.kilocode/ |
6 |
| pi.dev | ~/.pi/ |
7 |
| OpenCode | ~/.config/opencode/ |
8 |
Force a specific adapter:
bak backup --adapter cursorbak-cli/
├── cmd/ # CLI commands (cobra)
├── internal/
│ ├── adapters/ # Agent adapters (8 supported: Claude Code, Cursor, Codex,
│ │ │ # Windsurf, Kiro, KiloCode, pi.dev, OpenCode)
│ │ └── register/ # RegisterAll() wire-up
│ ├── backup/ # Backup engine + presets + secrets
│ ├── restore/ # Restore engine + dry-run + git safety
│ ├── manifest/ # Manifest schema + validation
│ ├── cloud/ # Cloud provider abstraction (GitHub Gist, GitHub Repo,
│ │ # Codeberg, Gitea/Forgejo, Rclone)
│ ├── crypto/ # AES-256-GCM encryption + Argon2id key derivation
│ ├── paths/ # Cross-platform path normalization
│ ├── git/ # Git operations (go-git)
│ ├── config/ # Configuration management + v0.1.0 → v0.3.0 migration
│ ├── presets/ # Preset definitions
│ └── schedule/ # OS-native task scheduling (crontab / schtasks)
├── .goreleaser.yaml # Cross-platform release config
└── Makefile # Development workflow
graph LR
subgraph "bak backup"
A[Detect Adapters] --> B[Resolve Preset]
B --> C[Copy Files]
C --> D[Scan Secrets]
D --> E[Generate Manifest]
E --> F[Auto-commit]
end
subgraph "bak restore"
G[Load Manifest] --> H[Validate Checksums]
H --> I[Compute Dry-run]
I --> J{User Confirms?}
J -->|Yes| K[Apply Restore]
J -->|No| L[Cancel]
K --> M[Auto-commit]
end
subgraph "Cloud Sync"
N[bak push] --> O[Package tar.gz]
O --> P[Upload to Gist]
Q[bak pull] --> R[Download Gist]
R --> S[Extract & Restore]
end
subgraph "Safety"
T[bak undo] --> U[git revert HEAD]
end
classDiagram
class Adapter {
<<interface>>
+Name() string
+Detect(homeDir) bool
+ListItems(homeDir, categories) []Item
+Backup(homeDir, backupDir, items) error
+Restore(homeDir, backupDir) error
}
class OpenCodeAdapter {
+Name() "opencode"
+Detect() ~/.config/opencode
}
class Registry {
+Register(adapter)
+DetectAll(homeDir) []DetectedAdapter
+Get(name) Adapter
}
Adapter <|.. OpenCodeAdapter
Registry --> Adapter
- ✅ Mandatory dry-run — Always preview changes before restore
- ✅ Git-backed safety — Auto-commit before/after restore
- ✅ Instant rollback —
bak undoreverts in one command - ✅ Secret exclusion — API keys/tokens never backed up
- ✅ Path validation — Prevents path traversal attacks
- ✅ Checksum verification — SHA-256 integrity checks
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'feat: add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Implement the Adapter interface:
type Adapter interface {
Name() string
Detect(homeDir string) (bool, string, error)
ListItems(homeDir string, categories []string) ([]Item, error)
Backup(homeDir, backupDir string, items []Item) error
Restore(homeDir, backupDir string) error
}Register it in cmd/backup.go:
reg := adapters.NewRegistry()
reg.Register(&youradapter.Adapter{})- Multi-agent support — Claude Code, Cursor, Codex, Windsurf, Kiro, pi.dev, KiloCode, OpenCode
- Cloud backends — GitHub private repo, Codeberg, rclone (Google Drive, OneDrive, S3), Gitea/Forgejo
-
--providerflag on push/pull/list/login - Config migration v0.1.0 → v0.2.0 with auto-backup
- Provider abstraction with
ProviderRegistry - Logo and banner image — see
docs/brand/ - GitHub Actions release workflow (goreleaser)
- Encryption at rest — AES-256-GCM with Argon2id key derivation, opt-in per profile
- Machine-specific profiles —
bak profile create work-laptop,bak profile create home-pc - Profile-scoped backups —
bak backup --profile workresolves preset, categories, and adapters - GUI — Optional terminal UI with bubbletea (beyond
bak pick)
- Backup scheduling (cron integration)
- Diff between backups (
bak diff <id1> <id2>) - Backup verification (
bak verify <id>) - Plugin system for custom backup strategies
Visual assets are in docs/brand/:
| Asset | File | Usage |
|---|---|---|
| Wordmark (color) | logo/bak-wordmark-color.png |
Primary brand mark |
| Wordmark (mono) | logo/bak-wordmark-mono-white.png |
Dark backgrounds, print |
| GitHub Banner | banner/bak-github-banner.png |
Social preview, README |
| Icon (geometric) | icon-secondary/bak-icon-geometric.png |
Official icon, favicons |
| Icon (friendly) | icon-secondary/bak-icon-friendly.png |
Stickers, swag, presentations |
| Favicon 32px | favicon/bak-favicon-32.png |
Browser tab, small icon |
| Favicon 16px | favicon/bak-favicon-16.png |
Browser tab (tiny) |
MIT License — see LICENSE for details.
