db
,p6"bo ,6"Yb. `7MM `7MMpMMMb.
6M' OO 8) MM MM MM MM
8M ,pm9MM MM MM MM
YM. , 8M MM MM MM MM
YMbmd' `Moo9^Yo..JMML..JMML JMML.
cain is my dotfiles / keybindings / programs manager, written in C with no dependencies. It works on any Linux distro, macOS and Windows. If you install it, your configs and keys become mine.
Everything lives in one file, cain.json:
| section | what it is |
|---|---|
packages |
programs to install (apt/dnf/pacman/zypper/apk/xbps, brew, winget). "terminal": true ones also go on servers |
files |
dotfiles copied between files/ and your home folder |
remaps |
key remaps, e.g. alt+h -> left, capslock -> esc |
shortcuts |
desktop shortcuts, e.g. alt+comma closes a window, alt+t opens a terminal |
settings |
wallpaper, dark mode, Mac key behavior |
Write keys once, in a neutral format (alt, lalt, ralt, ctrl, rctrl, shift, super
- a key name: see the top of
src/keys.c). cain turns them into whatever each OS needs:
| OS | key remaps | shortcuts |
|---|---|---|
| Linux | keyd (/etc/keyd/default.conf). Works on X11 and Wayland |
Cinnamon, GNOME, KDE Plasma, XFCE settings |
| Windows | AutoHotkey v2 script in your Startup folder | same script |
| macOS | Karabiner-Elements rules | same rules |
Linux / macOS (needs a C compiler: sudo apt install build-essential, or xcode-select --install):
cd && git clone https://github.com/v3ai/cain && cd cain && make && ./cain -i
Windows (PowerShell): clone the repo, put cain.exe from the
latest release in the cain folder, then:
cd ~; git clone https://github.com/v3ai/cain; cd cain; .\cain.exe -i
(Or build it yourself with build.bat from a Visual Studio developer prompt, or make windows with MinGW.)
On a fresh Linux server (Debian, Ubuntu, Fedora, Rocky/Alma/CentOS/RHEL, Arch, openSUSE, Alpine, Void), as root or a sudo user:
curl -fsSL https://raw.githubusercontent.com/v3ai/cain/master/vps.sh | sh
(no curl? wget -qO- https://raw.githubusercontent.com/v3ai/cain/master/vps.sh | sh)
vps.sh installs git and a C compiler, clones cain to ~/cain, builds it and runs
cain vps. That installs only what's used in the terminal and skips keybindings, desktop
settings and GUI apps:
- every package marked
"terminal": trueincain.json: bash, sudo, curl, git, micro, nano, python3 + pip + venv, tree, nnn, fzf, nmap, ffmpeg, yt-dlp, ssh, ... with the right package name for each distro. On Rocky/Alma/CentOS/RHEL it turns on EPEL first. If a distro doesn't package something (e.g. micro or yt-dlp), its"fallback"command installs it into~/.local/bininstead. - the dotfiles (micro, nano,
cain.sh), git name/email,cainitself on yourPATH - the shell block in
~/.bashrc, plus a line in~/.profile/~/.bash_profileso SSH logins load it. If your login shell isn't bash or zsh (Alpine'sash), cain switches it to bash.
Already cloned? cd ~/cain && ./vps.sh, or make && ./cain vps. Re-running is safe.
Just some steps: cain vps packages shell.
Install copies cain to ~/.local/bin and puts that on your PATH. After that, open
a new terminal and just type cain. The repo can live anywhere: cain remembers where it is.
cain menu: Install or Collect
cain -c collect: copy this machine's configs into the repo, commit, push
cain -i menu: pick what to install (see below)
cain -i -y install everything, no menu
cain install light pick a preset without the menu: full light dotfiles keybindings server
cain install keys only some steps: packages git files bin shell keys desktop
cain keys re-apply keybindings after editing cain.json
cain generate macos print the keyd / AutoHotkey / Karabiner config for any OS
cain vps set up a Linux server: terminal programs, configs, shell (no desktop)
cain -i --dry-run show what would happen without changing anything
cain booklister pick a book from ~/Desktop/books and open it ($CAIN_BOOKS_DIR to change)
cain listlauncher edit list.html on the website, then commit and push ($CAIN_SITE_DIR)
cain -i opens a menu (arrow keys or j/k, Enter to pick, q to quit):
What should cain install?
> Full programs, dotfiles, keybindings and desktop settings
Light dotfiles and keybindings; no programs, no desktop changes
Dotfiles just configs, shell aliases and git settings
Keybindings just key remaps and shortcuts
Server terminal programs and dotfiles, nothing graphical (Linux)
Custom tick each part yourself
Custom shows a checklist of the install steps (Space ticks, a toggles all). When cain isn't
run from a terminal (a script, CI) it skips the menu and installs everything, like -y.
Files that install overwrites are backed up to ~/.cain-backup/<date>/ first.
- Linux: keyd needs sudo once to install. If your distro doesn't package it, cain
builds it from source. If input-remapper is running, cain offers to turn it off so
keys aren't remapped twice. On Cinnamon,
cain -calso saves the wholedconfdump (theme, panels, ...), and install loads it back. booklister uses dmenu, rofi, fuzzel, wofi or zenity, whichever is installed. - Windows: the AutoHotkey script is at
%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\cain.ahk. Shell aliases go in your PowerShell profile (files/shell/cain.ps1). booklister opens a small search-as-you-type window. - macOS: the first time, open Karabiner-Elements and allow it in System Settings >
Privacy & Security. With
"mac_translate": true, outputs become Mac-style:ctrl+c -> cmd+c,ctrl+left -> option+left,home -> cmd+left."mac_alt": "option"uses the Option key as your Alt. Set it to"command"to use the key under your thumb instead. - Any remap can be overridden per OS:
{"from": "lalt+v", "to": "ctrl+shift+v", "macos": "cmd+v"}. Usenullto skip it on that OS. Any entry can be limited with"os": ["linux"]. - Packages:
{"name": "ssh", "apt": "openssh-client", "pacman": "openssh"}sets the name per package manager (apt dnf yum pacman zypper apk xbps brew winget, orlinuxfor all of them).nullskips it there."fallback": "<shell command>"runs if the package manager can't install it;"bin"is the command to look for if it isn't the same asname.CAIN_PM=apk cain vps -nshows what a distro would get. - Shortcuts can run anything:
{"action": "editor", "keys": ["alt+e"], "run": {"linux": "kate", "windows": "notepad.exe"}}.
files/shell/cain.sh (bash and zsh) and files/shell/cain.ps1 (PowerShell) hold my
aliases and functions. install, update, upgrade and the rest use whatever package
manager the machine has. cain adds a small # >>> cain >>> block to ~/.bashrc /
~/.zshrc / $PROFILE that loads them. Edit them with rc.
make build ./cain
make test check the generated configs against tests/expected/
make windows cross-compile cain.exe with MinGW
The code is in src/: main.c (commands, install steps and presets), tui.c (the menu), keys.c (key names for
every backend), keyd.c / ahk.c / karabiner.c / linux_desktop.c (the backends),
packages.c, desktop.c, tools.c (booklister, listlauncher), json.c and util.c.
GitHub Actions builds and tests on all three OSes. Push a v* tag to publish binaries.
MIT licensed, check the dotfiles for ideas.
ASCII art made with figlet -f Georgia11.flf cain.