Skip to content
Raph edited this page Jun 12, 2025 · 20 revisions

What is Tuckr?

Tuckr is a dotfile manager as well as a symlink farm. It aims to reduce the amount of work necessary to get up and running on a new machine. It takes inspiration from other existing dotfile managers such as chezmoi and yadm and borrows its core idea from GNU Stow's simplicity.

Since simplicity is one of the main goals of this project, the tool tries as much as possible to avoid any sort of configuration and instead is opinionated on how the dotfiles repo should be structured. This is intended to make learning how to use and setup everything intuitive at a glance and it also is how tuckr validates that everything is setup correctly.

How does Tuckr work?

Dotfiles directory

With tuckr all dotfiles have to be on a dotfile directory so that it can know about their existence and keep track of them. Tuckr aims to comply with system standards, so it uses the system's desired config directory, there's no way to use a custom config path (at least for now, or until I have been convinced that there should be alternative paths).

Tuckr looks up the following paths for dotfiles:

Platform Config Path Home Path
Linux/BSDs/etc $HOME/.config/dotfiles $HOME/.dotfiles
MacOS $HOME/Library/Application Support/dotfiles $HOME/.dotfiles
Windows %HomePath%\AppData\Roaming\dotfiles %HomePath%\.dotfiles

The dotfiles directory itself uses the following structure:

dotfiles
├── Configs # Stores all configuration files
├── Secrets # Stores encrypted files
└── Hooks # Stores scripts that configure the system

Dotfile validation

All dotfiles are stored inside the Configs directory where each directory in it is related to a certain group or program. Tuckr uses this to group the dotfiles and examine the state of these groups in the host machine. It achieves this by going through the file system and checking where symlinks point to:

  • if it points to the related dotfile in the repo it's deemed as symlinked
  • if it's not symlinked it's deemed as unsymlinked
  • if it's a symlink but points somewhere else it's put on not_owned to indicate that it's in conflict

All these checks are done right after the program's execution starts. Once the entire repo is mapped to either one of those states. The actual command execution starts.

Some commands work only on dotfiles with certain statuses, such as:

  • add: works on not_symlinked and not_owned if conflicts are being resolved
  • rm: works on symlinked only

Hooks

Hooks are just a way to run some setup scripts before and after symlinking your dotfiles. It runs in 3 phases:

Prehook -> Symlink -> Posthook

Prehook and Posthook are phases when scripts are run. As long as your scripts are prefixed by a post_ and pre_ tuckr will run them in the correct phase of the setup. Most scripts will likely not matter if they're run before or after having the dotfiles symlinked, but sometimes this is useful.

Secrets

Secrets are encrypted files using the chacha20poly1305 encryption algorithm to securely store information sensitive configuration. This is useful for storing environment related stuff or any file related to one's system configuration that might be too sensitive to store plainly.

This exists only as convenience for those that just want to store some secrets quickly and don't care much about how it's being done (the command handles everything for you, all you have to know is the file name and the group name). If you have a specific set of requirements that ought to be met in production, this probably will not suffice.

Conditional groups

Conditional deployment is used when a dotfile should only be deployed on a specific platform. All you have to do is add a suffix for the platform it is valid on and it will be ignored on every other platform by tuckr.

Example:

Configs
├── config
├── config_unix          // deployed on any unix system
├── config_linux         // only on linux
├── config_macos         // only on macos
├── config_windows       // only on windows
└── config_wsl           // only on Windows Subsystem for Linux (WSL)

The conditional groups are treated as if they were files in the regular group. So if you were on windows the config and config_windows groups are treated as one when you use config.

for some reason you need to only use the conditional group and ignore normal group, you can just name it explicitly config_windows or whatever, in the command you're running. Eg: tuckr rm config_linux (this won't remove config)

Dotfile fallback

Another nice thing about conditional dotfiles as well is that they fallback. So you can have the same config file multiple times in different conditional groups and tuckr won't complain. It will choose the most specific one for your platform.

Fallback order (in order of priority):

  1. Custom targets
  2. OS name (e.g: linux, freebsd, macos)
  3. OS family: unix, windows
  4. Non conditional groups (groups without a valid target suffix)

Any of the options available on Rust's target_family and target_os are valid targets.

Conditional groups are valid on hooks and configs but aren't used for secrets at all, since you're gonna be calling those explicitly every time anyway and they're not really aware of your configs (since the decrypted file will never be symlinked).

Custom targets

You might have noticed that the highest priority is a custom target. Custom targets are targets whose target names start with a #. They're mostly meant to be used as a way to deploy different variants of a file when under the same platform.

For example if you're running on linux but have a raspberry pi and a desktop and want different configs to be used for a certain program you can create a group_#raspberrypi and group_#dekstop. Then you can pick which to choose by using tuckr -t raspberrypi add group to tell tuckr that raspberry pi is a valid target right now.

If you want this to always be the case you can set it in the TUCKR_CUSTOM_TARGETS enviroment variable. Both the flag and environment variable support multiple targets, they can be define like so: -t raspberrypi,desktop,workpc.

Environment variables and root targeting

In tuckr it's also possible to use environment variables as paths or to deploy dotfiles starting from the root directory. These work by using either the % prefix to refer to an environment variable or the ^ prefix to target the root directory.

For example if you set an environment variable PROGRAM_PATH to /home/user/Documents/program and you have a dotfile with this file structure:

program
└── %PROGRAM_PATH
    └── config.txt

Tuckr will expand it to /home/user/Documents/program/config.txt.

If you have a dotfile:

xorg
└── ^etc
    └── X11

This will be expanded to /etc/X11.

Bear in mind that this can be anywhere inside your dotfile group, wherever it is the expansion will always be the same. You can even put one inside the other and the last one to occur is the one that takes precedence. For example if you have:

xorg
└── ^etc
    └── %PROGRAM_PATH
        └── some_file

Then some_file will end up in whatever path was in this variable and not in root. If it were the other way around (xorg/%PROGRAM_PATH/^etc/some_file) then it would be in root but not in the path in the environment variable.

Overriding the default behavior

By default tuckr will use your $HOME to symlink things and your dotfiles have to be on the standard config directory for you platform. This is generally the most desirable behavior. But if for some reason you don't like this and/or need to use other directories, you can set the TUCKR_HOME and TUCKR_TARGET environment variables to override the default behavior.

  • TUCKR_HOME: This is the parent directory of your dotfiles. So if you have your dotfiles in ~/.config/dotfiles/ then TUCKR_HOME is ~/.config.
  • TUCKR_TARGET: This is the place from which all your dotfiles' deployed paths will be computed. By default this is $HOME on Unix-like systems and %USERPROFILE% on Windows.

One use case for override these is if you're on a multi-user system and need to manage configurations for multiple users. So you can set TUCKR_HOME to your dotfiles and then TUCKR_TARGET to the place other user's home directory.

Clone this wiki locally