Provider Catalog
hams ships with 14 builtin providers that cover the bulk of what sits on a typical workstation. Each one wraps a tool you already use and adds auto-record, state tracking, and diff-based apply on top.
The 14 builtin providers
| Provider | Wraps | Platforms | What it manages |
|---|---|---|---|
| Homebrew | brew | macOS, Linux | Formulae and casks via Homebrew |
| apt | apt-get | Linux (Debian/Ubuntu) | System packages |
| pnpm | pnpm | macOS, Linux | Global Node.js packages |
| npm | npm | macOS, Linux | Global Node.js packages |
| uv | uv | macOS, Linux | Python command-line tools |
| goinstall | go install | macOS, Linux | Go binaries |
| cargo | cargo install | macOS, Linux | Rust binaries |
| code | code | macOS, Linux | VS Code extensions |
| mas | mas | macOS only | Mac App Store apps |
| git | git | macOS, Linux | Global git config + repo clones |
| defaults | defaults | macOS only | macOS system settings |
| duti | duti | macOS only | Default app associations for file types |
| bash | bash | macOS, Linux | Any shell script, with a check: for idempotency |
| Ansible | ansible | macOS, Linux | Ansible playbooks and ad-hoc tasks |
Four flavours of resource
Every provider’s resources fit one of four classes. Each class has its own way of figuring out “is this already in the desired state?”
Package — ask the native list command
Providers: Homebrew, apt, pnpm, npm, uv, goinstall, cargo, mas, code
How it probes: Run the native list command (brew list --formula, npm list -g, etc.),
check whether the package shows up, grab the version from the output.
- app: ripgrep
intro: Fast recursive grep alternative.KV Config — read it back and compare
Providers: defaults, git (config subcommand), duti
How it probes: Run the corresponding read command (defaults read, git config --get),
compare the returned value to the expected one. Match? ok.
- urn: "urn:hams:defaults:finder-show-extensions"
step: Show all file extensions in Finder
domain: NSGlobalDomain
key: AppleShowAllExtensions
value: trueCheck-based — you write the test
Providers: bash, ansible
How it probes: You supply a check: shell command. Exits 0, it’s done. Exits non-zero, time
to run. Idempotency is on you — which means these providers can manage anything you can describe.
- urn: "urn:hams:bash:install-homebrew"
step: Install Homebrew
run: /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
check: command -v brewFilesystem — does the path exist?
Providers: git (clone subcommand), file
How it probes: Check whether the target path exists. The clone subcommand also confirms the
remote URL matches.
- urn: "urn:hams:git-clone:dotfiles"
step: Clone dotfiles repository
remote: https://github.com/zthxxx/dotfiles.git
local_path: ~/Projects/dotfiles
default_branch: mainThe order providers run in
During hams apply, providers go in the order defined by provider_priority. The default order
is deliberate — bash runs before Homebrew, for example, so you can bootstrap Homebrew inside
a bash step before the Homebrew provider needs to install anything.
# ~/.config/hams/hams.config.yaml
provider_priority:
- brew
- apt
- pnpm
- npm
- uv
- goinstall
- cargo
- code
- mas
- git-config
- git-clone
- defaults
- duti
- bash
- ansibleNames in provider_priority match the internal registry names, which is why git appears here
as two entries (git-config + git-clone): the CLI merges them into a single hams git verb,
but the apply pipeline still treats them as independent providers with their own state files.
Want a different order? Override it in your store’s hams.config.yaml. Want to run only some of
them? Pass hams apply --only=homebrew,pnpm or --except=ansible.
Bringing your own provider
hams supports external providers through hashicorp/go-plugin (local gRPC). The full authoring
guide and Go SDK reference live in the Provider API chapter (coming soon).