SQLite-backed dotfiles manager for silly cats.
dotfiles-manager sqlite dotfiles
cattery docs repository-layout.md
4.4 kB · 125 lines
Markdown
at main

Repository layout #

A Cattery repository is an ordinary directory tree. Its files are the desired contents of $HOME; there is no template language, marker file, or generated manifest to maintain. Cattery validates this layout before it applies anything.

Files, directories, and groups #

A file at the repository root maps directly below $HOME:

repo/.bashrc                    -> ~/.bashrc
repo/.config/starship.toml       -> ~/.config/starship.toml

Top-level dot-directories are also direct $HOME trees. Any other ordinary top-level directory is a group: a named bundle you can select independently.

repo/atuin/.config/atuin/config.toml       -> ~/.config/atuin/config.toml
repo/atuin/.config/fish/conf.d/atuin.fish  -> ~/.config/fish/conf.d/atuin.fish

Group names are one path segment. They cannot start with . or _, contain a slash, or be . or ... Names that only differ by Unicode case or normalization are rejected so the repository stays portable between filesystems.

Put a non-dot target tree such as Library/ or bin/ inside a group. Root directories with those names are groups, not direct target trees.

Controls #

At the repository root and each group root, names beginning with _ are reserved:

Control Purpose
_darwin/ macOS-only file overrides.
_linux/ Linux-only file overrides.
_secrets/ SOPS-encrypted sources.
_hooks/ Trusted scripts run around apply.
_routes.toml Explicit target symlinks.

Unknown underscore-prefixed entries are ignored. Within a normal target tree, underscore-prefixed names are literal: app/.config/app/_cache/value deploys to ~/.config/app/_cache/value.

Platform overrides #

Use _darwin/ or _linux/ beside a base tree when a file differs by platform:

repo/ghostty/.config/ghostty/config              base
repo/ghostty/_darwin/.config/ghostty/config      macOS override
repo/ghostty/_linux/.config/ghostty/config       Linux override

An override wins at the same path. Directories merge recursively; an override file replaces a base directory at that path, and an override directory replaces a base file. cattery validate compiles both Linux and macOS plans, including the inactive one, so an error does not wait for another machine to discover it.

Source symlinks are rejected. If a target needs a symlink, declare it explicitly in _routes.toml instead. The canonical path must name a managed file or a managed directory in the same scope.

version = 1

[symlinks.linux]
".config/zed" = [".var/app/dev.zed.Zed/config/zed"]

[symlinks.darwin]
".config/Code/User" = ["Library/Application Support/Code/User"]

The paths on the right become relative symlinks to the path on the left. Cattery does not follow the destination while it creates a route. It will not replace an occupied directory automatically; resolve that case yourself, then apply again.

Secrets #

Put encrypted sources under _secrets/ and preserve the target path below it:

repo/app/_secrets/.config/app/credentials          -> ~/.config/app/credentials
repo/app/_darwin/_secrets/.config/app/credentials  -> macOS-only secret

See secret operations for setup and handling details.

Repository-only files #

At the repository root, .git, .github, .gitignore, .gitattributes, .gitmodules, .sops.yaml, and .catteryignore are metadata and never deploy. Use .catteryignore for other helper material:

# Repository-only material
README.md
scripts/
**/*.example

Patterns are relative to the repository root. Blank lines and # comments are ignored; *, ?, **, and trailing / are supported. Negated patterns are not supported.

What can be a source #

Regular files, including binary and empty files, are supported. Directories are structural. Symlinks, FIFOs, sockets, and device entries are rejected. Cattery does not preserve timestamps, ownership, ACLs, or extended attributes.

Selecting groups #

validate, status, diff, and apply accept group names. With no names, they select the root scope and every current group; status-like commands also include state-only groups so removed sources can be retired safely. Unknown or repeated group names are errors.

Next: reconciliation and recovery, secret operations, and trusted hooks.