doti 0.1.0

A fast, interactive TUI dotfiles manager with copy-based sync and secret scanning
Documentation

doti

A fast, interactive TUI for managing your dotfiles

Copy-based sync · Secret scanning · No symlink mess

Crates.io License: MIT

Why doti?

Most dotfile managers either scatter symlinks across your system or require you to learn a DSL. doti takes a different approach:

  • Your configs live in a home/ directory that mirrors $HOME
  • Changes are copied, never symlinked -- your system files stay untouched until you say so
  • An interactive TUI lets you browse, diff, and sync files with a few keystrokes
  • A built-in secret scanner catches leaked keys before you push

Features

Interactive TUI Browse tracked and untracked files in a tree view with live diffs
Copy-based sync No symlinks, no magic -- explicit file copies in either direction
Inline diffs Side-by-side diff of repo vs system changes, right in the terminal
Secret scanning Detects API keys, tokens, passwords, and private keys before they leak
Lazy loading Untracked directories scanned on demand for fast startup
Backup on apply Optionally back up system files before overwriting
Run from anywhere Config file remembers your repo path after doti init

Installation

From source

Requires: Rust toolchain (1.85+)

git clone https://github.com/volcmen/doti.git
cd doti
cargo install --path .

From crates.io

cargo install doti

Quick start

# 1. Go to your dotfiles repo and initialize
cd ~/my-dotfiles
doti init

# 2. Launch the TUI (works from anywhere after init)
doti

doti init creates a config at ~/.config/doti/config.toml and ensures the home/ directory exists in your repo.

Usage

Commands

Command Description
doti Launch the interactive TUI
doti init Set up config (run from your dotfiles repo)
doti scan Scan for leaked secrets
doti help Show help

TUI overview

The TUI has two tabs:

Tracked -- files in your repo, compared against the system:

Icon Status Meaning
Synced Repo and system files are identical
Differs Content has changed -- diff shown in panel
Absent In repo but missing from system

Untracked -- system files not yet in the repo. Directories load lazily when expanded.

Key bindings

Navigation

Key Action
j / k , / Move cursor
g / G Jump to top / bottom
h / Collapse directory or go to parent
/ Space Expand directory
Enter Expand directory / open action popup
Tab Switch between Tracked and Untracked

Tracked tab

Key Action
l Apply -- copy repo to system
p Pull -- copy system to repo
b Backup system file, then apply
d Delete from repo (system untouched)

Untracked tab

Key Action
a Add -- copy system file into repo

Panel & general

Key Action
J / K Scroll diff / preview
PgDn / PgUp Scroll faster
r Refresh all
? Toggle help overlay
q / Esc Quit

How it works

Your dotfiles repo has a home/ directory that mirrors $HOME:

my-dotfiles/
└── home/
    ├── .config/
    │   ├── hypr/hyprland.conf       →  ~/.config/hypr/hyprland.conf
    │   ├── kitty/kitty.conf         →  ~/.config/kitty/kitty.conf
    │   └── fish/config.fish         →  ~/.config/fish/config.fish
    ├── .bashrc                      →  ~/.bashrc
    └── .gitconfig                   →  ~/.gitconfig
Action Direction System files
Add system → repo Untouched
Pull system → repo Untouched
Apply repo → system Overwritten
Backup + Apply repo → system Backed up first
Delete removes from repo Untouched

Configuration

Config location: ~/.config/doti/config.toml

[repo]
path = "~/dotfiles"

If you run doti inside a git repo, it uses that repo directly. Otherwise it falls back to the config file. This lets you run doti from anywhere after doti init.

Secret scanning

doti scan

Scans all git-tracked files for:

  • Private keys (-----BEGIN ... PRIVATE KEY)
  • AWS access keys (AKIA...)
  • GitHub / GitLab tokens
  • OpenAI / Anthropic API keys
  • Slack tokens
  • Generic secrets (passwords, API keys with assigned values)

Binary files and .gitignore'd paths are skipped automatically. Exits with code 1 if secrets are found -- useful in CI or as a pre-commit hook.

Project structure

src/
├── main.rs       Entry point & CLI routing
├── lib.rs        Library root
├── config.rs     Config file loading & saving
├── link.rs       File sync operations (copy, compare)
├── scan.rs       Directory scanning & tracking
├── tree.rs       Tree data structure for UI
├── ui.rs         Ratatui TUI rendering
├── secrets.rs    Secret scanning rules
└── app/
    ├── mod.rs    Core application state & event loop
    ├── input.rs  Keyboard input handling
    ├── ops.rs    File operations (add, apply, pull, delete)
    ├── panel.rs  Diff panel logic
    └── popup.rs  Action popup handling

Contributing

Contributions welcome! Feel free to open issues or submit pull requests.

cargo run -- help       # Show CLI help
cargo run               # Launch TUI in dev mode
cargo run -- scan       # Test secret scanning

License

MIT