Complete command reference for cfgd. All commands respect global flags.
AI-guided configuration generation. Interactively scans your system and generates organized cfgd profiles and modules.
cfgd generate # Full flow: scan, propose structure, generate all
cfgd generate module <name> # Generate a module for a specific tool
cfgd generate profile <name> # Generate a profile| Flag | Description |
|---|---|
--model <model-id> |
Override AI model (default: from config or claude-sonnet-4-6) |
--provider <name> |
Override AI provider (default: claude) |
--yes, -y |
Skip confirmation prompts |
--scan-only |
Only scan system, don't start AI conversation |
The AI scans your installed packages, dotfiles, shell config, and system settings, then proposes a cfgd module and profile structure. Each generated file is shown to you for review before it is written. You can accept, reject, or give feedback. The session ends when all modules and profiles have been written or you exit.
Requires ANTHROPIC_API_KEY set in your environment, or spec.ai.apiKeyEnv in cfgd.yaml to name the environment variable holding the key.
See ai-generate.md for the full walkthrough, MCP server setup, and troubleshooting.
Start the MCP server for AI editor integration. Exposes cfgd's scan, inspect, and write tools over the Model Context Protocol (JSON-RPC stdin/stdout).
cfgd mcp-serverThe server runs until stdin is closed. Configure your AI client to launch it automatically rather than running it directly. See ai-generate.md for Claude Code and Cursor setup.
Initialize a new cfgd configuration repository.
cfgd init # interactive setup in current directory
cfgd init ~/dotfiles # scaffold in specific directory
cfgd init --from git@github.com:you/config.git # clone and scaffold
cfgd init --from ~/existing/config # use local config directory
cfgd init --from <source> --branch dev # specify branch
cfgd init --from <source> --apply-profile work-mac # clone, activate profile, apply
cfgd init --from <source> --apply-module nvim # clone, apply just one module
cfgd init --from <source> --apply --yes --install-daemon # full one-liner bootstrap| Flag | Description |
|---|---|
[path] |
Target directory (default: current directory) |
--from <url|path> |
Config source: git URL to clone, or local path to existing config |
--branch <name> |
Git branch (default: master) |
--name <name> |
Config name in metadata (default: directory name) |
--apply |
Apply configuration after scaffolding |
--apply-profile <name> |
Activate and apply a specific profile (implies --apply, errors if not found) |
--apply-module <name> |
Apply a specific module (repeatable, implies --apply, errors if not found) |
--yes, -y |
Skip confirmation prompts (used with --apply) |
--install-daemon |
Install daemon service after init |
--theme <name> |
Theme name (default, dracula, solarized-dark, solarized-light, minimal) |
See bootstrap.md for the full init flow.
Apply the configuration plan.
cfgd apply # apply with confirmation
cfgd apply --dry-run # preview without applying
cfgd apply --yes # skip confirmation
cfgd apply --phase packages # single phase
cfgd apply --module nvim # single module + deps (no profile required)
cfgd apply --only packages.brew # dot-notation filter
cfgd apply --skip system.sysctl # skip specific items
cfgd apply --skip-scripts # apply without running any hooks| Flag | Description |
|---|---|
--dry-run |
Preview changes without applying (supports -o json) |
--phase <name> |
Apply only a specific phase |
--yes, -y |
Skip confirmation prompt |
--module <name> |
Apply only this module and its dependencies |
--skip <path> |
Skip items by dot-notation path (repeatable) |
--only <path> |
Apply only items matching dot-notation paths (repeatable) |
--skip-scripts |
Skip all script hooks (pre/post/onChange) |
Preview the reconciliation plan without applying. This is the canonical preview command — apply --dry-run is a convenience that delegates to the same logic.
cfgd plan # preview with default (apply) context
cfgd plan --context reconcile # preview what the daemon would run
cfgd plan --module nvim # plan for a single module
cfgd plan --skip-scripts # exclude all script hooks
cfgd plan -o json # structured plan output| Flag | Description |
|---|---|
--phase <name> |
Show only a specific phase |
--module <name> |
Plan only this module and its dependencies |
--skip <path> |
Skip items by dot-notation path (repeatable) |
--only <path> |
Plan only items matching dot-notation paths (repeatable) |
--skip-scripts |
Exclude all script hooks from the plan |
--context <ctx> |
apply (default) or reconcile — selects which hooks to include |
Show configuration status, drift, and pending decisions.
cfgd status # human-readable table
cfgd status -o json # full status as JSON
cfgd status -o jsonpath='{.drift}' # extract drift events
cfgd status --module nvim # status for a single module (no profile required)Show detailed file diffs with syntax highlighting.
Check that all managed resources match desired state.
cfgd verify -o json # structured pass/fail results
cfgd verify --module nvim # verify only a single module's resources (no profile required)Check system health: available package managers, configurators, module status, dependency versions.
cfgd doctor -o json # structured health reportShow apply history from the state store.
cfgd log # last 20 entries
cfgd log --limit 50 # last 50 entries
cfgd log -o json # JSON apply history
cfgd log --show-output 42 # show captured script output for apply #42Pull from all remotes, show changes, prompt for apply.
Pull remote changes (git pull only, no apply).
Check for and install cfgd updates from GitHub releases.
cfgd upgrade # download and install latest
cfgd upgrade --check # check only (exit 0 = current, exit 1 = update available)
cfgd upgrade --require-cosign # fail if cosign signature cannot be verified
CFGD_REQUIRE_COSIGN=1 cfgd upgradeThe release's checksums.txt is verified by cosign when the release ships a
*-checksums.txt.cosign.bundle and a cosign.pub, AND the cosign CLI is
installed locally. The actual archive is then matched against the SHA256 entry
inside that signed checksums file.
By default, missing cosign artifacts (no bundle, no public key, or no local
cosign CLI) trigger a WARN and silent fallback to SHA256-only
verification — your trust root reduces to GitHub Releases asset hosting. An
attacker who can MITM both the binary and checksums.txt downloads (compromised
GHCR mirror, network attacker against objects.githubusercontent.com) gets a
fully-trusted upgrade on a host that doesn't have cosign installed.
--require-cosign (or CFGD_REQUIRE_COSIGN=1) flips the policy from
"warn and proceed" to "block the upgrade." Any of the three skip conditions
fails the upgrade with exit 1 and emits an error_doc with
error: "cosign_required" plus requireCosign: true in the payload so
alerting can route strict-mode failures separately from generic install
errors. Recommended for unattended / CI updaters where a silent SHA256-only
fallback should never happen.
The structured-output payload on a successful upgrade carries
verificationMode so downstream consumers can detect a fallback even when
strict mode is not requested:
verificationMode |
Meaning |
|---|---|
cosign |
full cosign signature verified (default policy) |
sha256-only |
cosign artifacts unavailable → SHA256-only fallback |
strict-cosign-required |
strict mode was requested and honored |
null |
no install performed (already at latest) |
Show schema and field documentation for resource types.
cfgd explain module # show Module spec
cfgd explain profile # show Profile spec
cfgd explain profile.spec.packages # show specific field
cfgd explain --recursive machineconfig # expand all fieldsResource types: module, profile, cfgdconfig, configsource, machineconfig, configpolicy, driftalert, teamconfig.
List available profiles. Marks the active one.
Show the fully resolved profile (all inheritance layers merged).
Switch the active profile in cfgd.yaml. Alias: cfgd profile use <name>.
Create a new profile. Interactive if no flags provided.
cfgd profile create work-linux \
--inherit base \
--module nvim --module tmux \
--package apt:build-essential \
--env EDITOR=vim \
--alias vim=nvim \
--file ~/.config/starship.toml \
--secret secrets/api-key.enc:~/.config/app/key \
--pre-apply scripts/setup.sh| Flag | Description |
|---|---|
--inherit <name> |
Inherit from profile (repeatable) |
--module <name> |
Include module (repeatable) |
--package <mgr:pkg> |
Add package (repeatable) |
--env <key=value> |
Set env var (repeatable) |
--alias <name=command> |
Set shell alias (repeatable) |
--system <key=value> |
Set system setting (repeatable) |
--file <path> |
Manage file (repeatable) |
--private-files |
Mark files as private (gitignored) |
--secret <source:target> |
Add secret (repeatable) |
--pre-apply <script> |
Add pre-apply script (repeatable) |
--post-apply <script> |
Add post-apply script (repeatable) |
--pre-reconcile <script> |
Add pre-reconcile script (repeatable) |
--post-reconcile <script> |
Add post-reconcile script (repeatable) |
--on-change <script> |
Add on-change script (repeatable) |
--on-drift <script> |
Add on-drift script (repeatable) |
Modify an existing profile. When no name is given, defaults to the active profile. Prefix a value with - to remove it.
cfgd profile update --package brew:jq
cfgd profile update work --module new-tool --module -old-tool
cfgd profile update work --package brew:jq --package -brew:unused --alias vim=nvim --alias -old| Flag | Description |
|---|---|
--inherit <name> |
Add/remove inherited profile (prefix with - to remove) |
--module <name> |
Add/remove module (prefix with - to remove) |
--package <mgr:pkg> |
Add/remove package (prefix with - to remove) |
--file <path> |
Add/remove file (prefix with - to remove by target) |
--env <KEY=VALUE> |
Add/remove env var (prefix with - to remove by key) |
--alias <name=cmd> |
Add/remove alias (prefix with - to remove by name) |
--system <key=val> |
Add/remove system setting (prefix with - to remove by key) |
--secret <src:tgt> |
Add/remove secret (prefix with - to remove by target) |
--pre-apply <script> |
Add/remove pre-apply script (prefix with - to remove) |
--post-apply <script> |
Add/remove post-apply script (prefix with - to remove) |
--pre-reconcile <script> |
Add/remove pre-reconcile script (prefix with - to remove) |
--post-reconcile <script> |
Add/remove post-reconcile script (prefix with - to remove) |
--on-change <script> |
Add/remove on-change script (prefix with - to remove) |
--on-drift <script> |
Add/remove on-drift script (prefix with - to remove) |
Open profile in $EDITOR with post-save validation.
Delete a profile. Refuses if it's the active profile or inherited by others.
cfgd profile delete dev --yes # skip confirmationList all available modules with status (installed, pending, outdated, error).
Show module details: packages, files, dependencies, resolved managers. Env variable values are masked by default (shows *** with last 3 chars).
cfgd module show my-tool # env values masked
cfgd module show my-tool --show-values # reveal full env valuesExport a module to another format.
cfgd module export my-tool --format devcontainer # current directory
cfgd module export my-tool --format devcontainer --dir out/ # custom output dirGenerates install.sh and devcontainer-feature.json suitable for publishing as a DevContainer Feature to GHCR or another OCI registry.
Create a new local module.
cfgd module create my-tool \
--depends node \
--package neovim \
--file ~/.config/tool/config.toml \
--post-apply "tool --setup" \
--set package.neovim.minVersion=0.9 \
--set package.neovim.prefer=brew,snap,apt| Flag | Description |
|---|---|
--description <text> |
Module description |
--depends <name> |
Dependency on another module (repeatable) |
--package <name> |
Add package (repeatable) |
--file <path> |
Import file (repeatable) |
--private-files |
Mark files as private |
--env <key=value> |
Set env var (repeatable) |
--alias <name=command> |
Set shell alias (repeatable) |
--post-apply <cmd> |
Post-apply script (repeatable) |
--set <key=value> |
Helm-style override (repeatable) |
Modify a local module. Prefix a value with - to remove it.
cfgd module update nvim --package fd --package -unused
cfgd module update nvim --depends node --env EDITOR=nvim --alias vim=nvim| Flag | Description |
|---|---|
--package <name> |
Add/remove package (prefix with - to remove) |
--file <path> |
Add/remove file (prefix with - to remove by target) |
--env <KEY=VALUE> |
Add/remove env var (prefix with - to remove by key) |
--alias <name=cmd> |
Add/remove alias (prefix with - to remove by name) |
--depends <name> |
Add/remove dependency (prefix with - to remove) |
--post-apply <cmd> |
Add/remove post-apply script (prefix with - to remove) |
--set <key=value> |
Helm-style override (repeatable) |
--description <text> |
Set description |
Open module.yaml in $EDITOR.
Delete a local module. Any files that were adopted (moved into the module and symlinked back) are automatically restored to their original locations before the module directory is removed.
cfgd module delete nvim # restores symlinked files, then deletes modules/nvim/
cfgd module delete nvim -y # skip confirmation
cfgd module delete nvim --purge # remove deployed target files instead of restoring them| Flag | Description |
|---|---|
--yes, -y |
Skip confirmation prompt |
--purge |
Remove files deployed by this module to target locations instead of restoring symlinks |
Upgrade a remote (locked) module to a new version.
cfgd module upgrade tmux # latest available
cfgd module upgrade tmux --ref tmux/v2.0 # specific version
cfgd module upgrade tmux --yes # skip confirmation
cfgd module upgrade tmux --allow-unsigned # allow unsigned modulesSearch configured registries for modules matching a query.
Manage module registries.
cfgd module registry add https://github.com/cfgd-community/modules.git
cfgd module registry add https://github.com/myorg/modules.git --name myorg
cfgd module registry list
cfgd module registry remove community
cfgd module registry rename community cfgd-communitySubscribe to a config source.
cfgd source add git@github.com:acme/dev-config.git \
--profile acme-backend \
--priority 500 \
--accept-recommended \
--sync-interval 1hList subscribed sources.
Show source details, provided profiles, policy breakdown, conflicts.
Remove a subscription.
cfgd source remove acme-corp --keep-all # keep resources as local
cfgd source remove acme-corp --remove-all # remove everythingFetch latest from sources (all or specific).
Override or reject a source's recommendation.
cfgd source override acme-corp reject packages.brew.formulae kubectx
cfgd source override acme-corp set env.EDITOR "nvim"Set or view source priority.
Replace one source with another.
Create a new cfgd-source.yaml in the current directory.
Open cfgd-source.yaml in $EDITOR.
cfgd secret init # generate age key + .sops.yaml
cfgd secret encrypt <file> # encrypt values in place
cfgd secret decrypt <file> # decrypt to stdout
cfgd secret edit <file> # decrypt, edit, re-encryptcfgd daemon # run in foreground (default)
cfgd daemon run # run in foreground (explicit)
cfgd daemon install # install as system service
cfgd daemon status # check running state
cfgd daemon uninstall # remove serviceAccept or reject pending source decisions.
cfgd decide accept packages.brew.k9s # accept one item
cfgd decide reject packages.brew.stern # reject one item
cfgd decide accept --source acme-corp # accept all from source
cfgd decide accept --all # accept everythingShow the current cfgd.yaml configuration.
Open cfgd.yaml in $EDITOR.
Get a config value by dotted key path. Outputs raw value to stdout (suitable for scripting).
cfgd config get profile # → work
cfgd config get theme # → dracula
cfgd config get theme.name # → dracula
cfgd config get daemon.reconcile.interval # → 5m
cfgd config get fileStrategy # → Symlink
cfgd config get aliases.add # → profile update --file
cfgd config get daemon # prints full daemon YAML blockSet a config value by dotted key path. Creates intermediate sections as needed.
cfgd config set profile personal
cfgd config set theme dracula
cfgd config set theme.name minimal
cfgd config set daemon.reconcile.interval 10m
cfgd config set daemon.enabled true
cfgd config set fileStrategy Copy
cfgd config set aliases.deploy "apply --yes"Remove a config value (resets to default).
cfgd config unset theme # remove entire theme section
cfgd config unset daemon.reconcile.autoApply # reset single field
cfgd config unset aliases.deploy # remove an aliasGenerate GitHub Actions workflows for config repo releases.
cfgd workflow generate --force # overwrite existingCheck in with the device gateway.
cfgd checkin --server-url https://cfgd.acme.com --api-key <key>Enroll with a device gateway using token or key-based verification.
cfgd enroll --server-url https://cfgd.acme.com --token <bootstrap-token>
cfgd enroll --server-url https://cfgd.acme.com --ssh-key ~/.ssh/id_ed25519
cfgd enroll --server-url https://cfgd.acme.com --gpg-key ABCD1234| Flag | Description |
|---|---|
--server-url <url> |
Device gateway URL |
--token <token> |
Bootstrap token for token-based enrollment |
--ssh-key <path> |
SSH key for key-based enrollment |
--gpg-key <id> |
GPG key ID for key-based enrollment |
--username <name> |
Username to enroll as (default: current system user) |
The server's enrollment method is configured by the administrator. cfgd auto-detects which method the server requires.
| Method | How it works | Best for |
|---|---|---|
| Token | Admin generates a short-lived bootstrap token, gives it to the user. User exchanges it for a permanent device credential. | Quick onboarding, automated provisioning |
| SSH key | Admin pre-registers the user's SSH public key. User proves possession via challenge-response signing. | Teams already using SSH keys for git access |
| GPG key | Admin pre-registers the user's GPG public key. User proves possession via challenge-response signing. | Teams with existing GPG infrastructure |
Challenge-response flow (SSH/GPG):
- cfgd contacts the server and requests a challenge nonce
- The server generates a random nonce with a 5-minute TTL
- cfgd signs the nonce with your local key
- cfgd sends the signature back to the server
- The server verifies the signature against pre-registered public keys
- On success, the server returns a permanent device API key
Key auto-detection: If neither --ssh-key nor --gpg-key is specified, cfgd checks the SSH agent first, then falls back to ~/.ssh/id_ed25519, ~/.ssh/id_rsa, and ~/.ssh/id_ecdsa in order. The first available key is used.
Generate shell completions.
# Add to your shell's rc file
source <(cfgd completions bash) # .bashrc
source <(cfgd completions zsh) # .zshrc
cfgd completions fish | source # config.fishScripted consumers rely on distinct exit codes to decide follow-up actions without parsing stderr. The taxonomy is stable — breaking changes bump the CLI major version.
| Code | Meaning | Emitted by |
|---|---|---|
0 |
Operation succeeded. | All commands on success. |
1 |
Generic failure (network, IO, unclassified internal error). | Any command whose Result resolves to a non-config error. |
2 |
An upgrade is available but not installed. | cfgd upgrade --check only. |
3 |
No cfgd config file at the resolved path. | Any command when --config points to a missing file. |
4 |
Config file exists but failed parse or validation. | Any command when --config is malformed, schema-invalid, or references a missing profile. |
5 |
Drift detected between actual and desired state. | cfgd diff --exit-code, cfgd status --exit-code, cfgd verify --exit-code. |
The --exit-code / -e flag on diff, status, and verify follows the git diff --exit-code convention: without the flag these commands always exit 0; with the flag they exit 5 whenever drift is present.
External-process passthrough (e.g. kubectl exec invoked by the kubectl cfgd plugin) forwards the inner tool's exit code unchanged — those codes are not part of the cfgd taxonomy.
# Fail the build if the machine has drifted from the committed profile.
cfgd verify --exit-code
# Run upgrade on a schedule but only page humans on real failures.
if ! cfgd upgrade --check; then
case $? in
2) echo "Update available — cfgd upgrade to install" ;;
*) echo "Upgrade check failed" >&2; exit 1 ;;
esac
fi