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.
Symlink routes #
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.