cfg applies host and repository configuration from a separate personalization
repository.
The tool owns configuration loading, validation, dependency resolution, planning, safe filesystem changes, and host orchestration. Personal inventory, payloads, and trusted deploy files stay outside this repository.
Until a package is published, install directly from a Git checkout:
uv tool install /path/to/cfgFor tool development:
uv sync --group test
uv run pytest
uv tool install --editable .By convention, personal configuration lives at ~/.cfg:
~/.cfg/
.cfg-root
hosts/
repos/
features/
host/
repo/
See examples/personalization for a minimal synthetic
configuration.
cfg resolves the personalization root in this order:
CFG_ROOT;- the path stored in
$XDG_CONFIG_HOME/cfg/root; - the nearest parent containing
.cfg-root; ~/.cfg.
cfg host current
cfg host settings
cfg host managed
cfg host apply --dry-run
cfg host apply -y
cfg host upgrade --dry-run
cfg repo settings
cfg repo apply --dry-run
cfg validateHost operations use pyinfra. Repo operations build and execute a deterministic local
filesystem plan. Private deploy.py files are trusted Python extensions and run with
the user's privileges. cfg host apply applies managed home files and feature deploys;
cfg host upgrade separately refreshes package metadata and upgrades installed packages.
Host apply compares the installed cfg version with main's pyproject.toml on GitHub,
including during --dry-run. If versions differ or the check cannot run, apply stops
before changing the host. Run uv tool upgrade cfg, then retry. If cfg was installed
from a local checkout, update that checkout before upgrading.
Before live host apply, cfg requires a clean personalization Git checkout with a remote
upstream. It fetches that upstream, fast-forwards when behind, tests a divergent rebase
in a temporary worktree before rebasing locally, and pushes local commits. If the checkout
is dirty or the rebase conflicts, apply stops with a fix hint. --dry-run checks local
Git state but does not fetch, rebase, or push, so its remote status may be stale.
Interactive host applies validate sudo credentials before making changes, so native sudo
can reprompt after a mistyped password. Noninteractive applies keep existing askpass behavior.
Repo apply records minimal ownership evidence in .cfg/state.json; host apply records
the same evidence in $XDG_CONFIG_HOME/cfg/state.json. When configuration stops managing
a mirror, generated file, or overlay, cfg removes the stale output only if it still
matches that evidence; user-modified replacements fail safely. Host state is written
only after pyinfra succeeds and cfg verifies actual declared outputs.
cfg validate parses and resolves the complete personalization repository without
writing to hosts, repositories, or the configuration root.
Managed repo outputs cannot target .git components or the root .cfg directory.
Mirror plans capture bytes and permissions before applying; later source edits do
not change the plan. Stale overlays are removed only when their target still
matches recorded ownership. Links without recorded ownership are left untouched.
Host feature generated paths name files created by that feature's deploy.py.
Successful host apply verifies those files and records their digests. This makes
platform-selected or tool-generated host configuration part of normal ownership
cleanup without forcing its bytes into an overlay.