A macOS CLI tool for intentionally archiving files and folders to an external drive, freeing up local space, and reliably restoring them to their exact original paths later. macOS only (relies on /Volumes/ for drive detection).
Unlike backup tools (which use retention policies that purge old snapshots) or sync tools (which mirror deletions), Tuck treats your external drive as permanent, intentional storage — files stay archived until you explicitly restore them.
Download Tuck.dmg from the latest release and drag Tuck to Applications.
curl -fsSL https://raw.githubusercontent.com/legendword/tuck/main/install.sh | shThis downloads the latest universal binary from GitHub Releases and installs it to /usr/local/bin.
To update to the latest version:
tuck updateRequires Rust.
cargo install --path crates/tuck-clituck add ~/Documents/BigProject --drive MyDriveThis will:
- Hash all files (BLAKE3)
- Copy to the drive (preserving directory structure and modification times)
- Verify checksums on the destination
- Delete the local copy
Flags:
--drive <name>— specify drive (auto-detected if only one connected)--prefix <folder>— use a subfolder on the drive as root (see Using a prefix)--dry-run— preview without making changes--no-confirm— skip confirmation prompt--keep-local— archive without deleting the local copy
tuck restore ~/Documents/BigProjectRestores files to their exact original path. Verifies checksums before restoring.
Flags:
--force— overwrite if local path already exists--keep-archive— keep the copy on the drive after restoring--dry-run— preview without making changes
tuck listtuck status ~/Documents/BigProjecttuck verifyChecks BLAKE3 checksums of all archived files on the drive.
tuck update # download and install the latest version
tuck update --check # check for updates without installingIf your external drive contains other files, or you share a drive between multiple Macs, use --prefix to scope all tuck data under a subfolder:
tuck add ~/Documents/BigProject --prefix tuck-macbook
tuck list --prefix tuck-macbook
tuck restore ~/Documents/BigProject --prefix tuck-macbookThis stores everything under /Volumes/Drive/tuck-macbook/ instead of the drive root — including the manifest and all archived files. Each prefix gets its own independent manifest, so two machines can use the same drive with different prefixes (e.g. tuck-macbook and tuck-imac) without interfering.
To avoid passing --prefix every time, set a default:
tuck config set-prefix tuck-macbookNow all commands automatically use tuck-macbook as the prefix. You can still override it with --prefix on any individual command. Similarly, tuck config set-drive MyDrive sets a default drive.
tuck config show # view current defaults
tuck config set-prefix "" # clear the defaultConfig is stored at ~/.config/tuck/config.json.
- Drive detection: Scans
/Volumes/, skips the boot volume and hidden entries. Auto-detects if one drive is connected; asks you to specify if multiple. - Path mapping: Strips leading
/and mirrors the directory structure on the drive./Users/you/Documents/foo.txt→/Volumes/Drive/Users/you/Documents/foo.txt. With--prefix myprefix, the root becomes/Volumes/Drive/myprefix/. - Checksums: BLAKE3, streamed in 64KB chunks. Files are hashed before copy, hashed again after copy, and compared. Stored per-file for granular verification.
- Manifest: A
.tuck-manifest.jsonfile on the drive root tracks all archived entries with original paths, timestamps, sizes, and checksums. Written atomically (write.tmp, then rename). - Disk space: Both
addandrestorecheck available disk space before starting and fail early if insufficient. - Symlinks: Skipped with a warning (v1).
- Self-update:
tuck updatechecks GitHub Releases for a newer version, downloads the binary, and atomically replaces the current executable.
Run via cargo:
cargo run --bin tuck -- add ~/file.txt --drive MyDrive
cargo run --bin tuck -- list --drive MyDriveTo simulate a drive without a real external disk:
sudo mkdir /Volumes/TestDrive
cargo run --bin tuck -- add /tmp/test.txt --drive TestDrive
sudo rm -rf /Volumes/TestDriveRun unit tests:
cargo testtuck/
Cargo.toml # Workspace root
install.sh # One-line install script
build-ffi.sh # Build universal FFI lib + Swift bindings
.github/workflows/
release.yml # CI: build CLI + macOS app, publish GitHub Release
crates/
tuck-core/ # Library — all logic, no CLI/UI concerns
src/
lib.rs
error.rs # TuckError, TuckResult, IoContext
manifest.rs # Manifest load/save, entry management
checksum.rs # BLAKE3 hashing
config.rs # Persistent config (~/.config/tuck/config.json)
drive.rs # /Volumes/ scanning, drive resolution, disk space checks
copy.rs # Recursive copy with metadata preservation
pending.rs # Interrupted operation recovery
progress.rs # Progress trait for byte-level reporting
archive.rs # Plan and execute archive operations
restore.rs # Plan and execute restore operations
update.rs # Self-update via GitHub Releases
verify.rs # Checksum verification, status checks
tuck-cli/ # Binary — CLI interface only
src/
main.rs # Clap arg parsing, dispatch
commands/
add.rs
restore.rs
list.rs
status.rs
update.rs
verify.rs
tuck-ffi/ # Static library — UniFFI bridge to Swift
src/
lib.rs
types.rs # Mirror types with uniffi::Record
error.rs # FfiTuckError with uniffi::Error
progress.rs # Callback interface for progress reporting
functions.rs # Exported FFI functions
TuckApp/ # SwiftUI macOS app
project.yml # xcodegen project spec
TuckApp/ # Swift sources
Generated/ # Auto-generated Swift bindings (from build-ffi.sh)
The workspace is split into three crates:
tuck-core(library) — all business logictuck-cli(binary) — CLI interfacetuck-ffi(staticlib) — UniFFI wrapper exposingtuck-coreto Swift
The macOS app lives in TuckApp/ and uses SwiftUI with the FFI bindings.
Requires Rust and xcodegen (brew install xcodegen).
./build-ffi.sh # build universal static lib + generate Swift bindings
cd TuckApp && xcodegen generate # generate .xcodeproj from project.yml
xcodebuild build -project TuckApp.xcodeproj -scheme TuckAppImportant: After changing any Rust code (in tuck-core or tuck-ffi), you must re-run ./build-ffi.sh before rebuilding the app. The script builds the FFI library for both aarch64 and x86_64, creates a universal binary via lipo, and generates the Swift bindings at TuckApp/Generated/.