Configuration
Two config layers
Section titled “Two config layers”| Location | Shared? | Purpose |
|---|---|---|
<repo>/anvil.toml |
Yes (Git) | Profiles, links, hooks, packages, harden |
~/.config/anvil/config.toml |
No (local) | repo_path, profiles, age_identity, aur_helper |
~/.local/state/anvil/backups/ |
No (local) | Backup journals for anvil undo |
Local config
Section titled “Local config”repo_path = "/home/you/.dotfiles"profiles = ["base", "hyprland"]age_identity = "~/.config/age/key.txt"# Machine-local AUR helper. Not in anvil.toml.# auto (default) = first of paru, yay on PATH — never auto-picks anzenaur_helper = "auto" # or "paru" | "yay" | "anzen" | "/abs/path"anvil init writes this file automatically. Edit it by hand if you move the clone. aur_helper is not a valid key in anvil.toml.
Manifest (anvil.toml)
Section titled “Manifest (anvil.toml)”A full annotated sample lives at examples/anvil.toml.
[anvil]version = "1"default_profile = "base"# clone_dir = "~/.dotfiles" # used as init default only
[profiles.base]links = [ { src = ".zshrc", dest = "~/.zshrc" }, { src = ".gitconfig", dest = "~/.gitconfig" }, { src = ".config/nvim", dest = "~/.config/nvim" }, # optional: copy instead of symlink # { src = "mimeapps.list", dest = "~/.config/mimeapps.list", copy = true }, # optional: age-encrypted secret # { src = "secrets/npmrc.age", dest = "~/.npmrc", mode = "600", decrypt = "age" },]hooks.after_apply = ["scripts/post-apply.sh"]
packages.pacman = ["git", "ufw", "age"]# packages.aur = ["some-aur-package"]
[profiles.base.harden]mode = "check" # or "enforce" (sysctl drop-in via sudo)sysctl = [{ key = "kernel.kptr_restrict", value = "2" }]ssh = { password_auth = false, root_login = false }firewall = { backend = "ufw", default = "deny", allow = ["22/tcp"] }
[profiles.work]extends = "base"links = [ { src = "work/.gitconfig", dest = "~/.gitconfig" }, # later wins on same dest]
[machines]"framework-arch" = ["base", "hyprland"]Field reference
Section titled “Field reference”[anvil]
Section titled “[anvil]”| Field | Required | Description |
|---|---|---|
version |
yes | Schema version ("1") |
default_profile |
no | Used when no CLI -p, local profiles, or machine match |
clone_dir |
no | Default clone path for init (~/.dotfiles) |
[profiles.<name>]
Section titled “[profiles.<name>]”| Field | Description |
|---|---|
extends |
Parent profile name (chain; cycles error) |
links |
File link entries |
hooks.before_apply / after_apply |
Repo-relative scripts (no .., no absolute paths) |
packages.pacman / packages.aur |
Package names (Arch) |
harden |
Posture checks / optional enforce |
Merge rules: ordered profile lists (CLI / machines / local) are primary. extends prepends parents. Later links win on the same dest. Hooks and packages concatenate (packages deduped).
Link entry
Section titled “Link entry”| Field | Required | Description |
|---|---|---|
src |
yes | Path relative to the repo root |
dest |
yes | System path (~ and ~/... expanded) |
copy |
no | true = copy file/dir instead of symlink |
mode |
no | Octal mode string, e.g. "600" (also inferred under ~/.ssh/) |
decrypt |
no | "age" — decrypt src to dest with the age CLI |
[machines]
Section titled “[machines]”Hostname → list of profiles. Matched via the system hostname when no -p or local profiles override.