Schema Reference
hams reads and writes three kinds of YAML files. All of them round-trip through the parser with comments intact — which matters, because Hamsfiles tend to collect useful “why did I install this” notes over time.
Global config
Lives at ~/.config/hams/hams.config.yaml:
profile_tag: macOS
machine_id: MacbookProM5X
store_repo: zthxxx/hams-store
store_path: ~/.local/share/hams/repo/zthxxx/hams-store
llm_cli: claude
provider_priority:
- homebrew
- apt
- pnpm
- npm
- bashThis file tells hams who this machine is, which profile to use, where the store lives, and the order in which providers run during apply.
Hamsfile
Hamsfiles are split by provider, one file per provider. The general shape is: a header that names
the schema and provider, and a groups section that holds resources organised under your own
tags.
Package-style (Homebrew, pnpm, npm, and friends)
schema_version: 1
provider: Homebrew
groups:
- tag: terminal-tool
items:
- app: lazygit
intro: Simple terminal UI for git commands.
- app: htop
intro: Improved top (interactive process viewer).Field-by-field:
app— the package name, exactly as you’d type it in the native CLIintro— a one-line reminder to your future selftags— optional, for filtering and grouping
Key-value style (defaults, git-config)
Each entry is a single setting:
configs:
- urn: "urn:hams:defaults:com.apple.dock.autohide"
args:
domain: com.apple.dock
key: autohide
type: bool
value: "true"
preview-cmd: "defaults write com.apple.dock autohide -bool true"
check: "defaults read com.apple.dock autohide"urn— hams’s internal unique ID. Make it readableargs— whatever the provider needs (domain, key, value, type)preview-cmd— what gets printed in a dry-run (optional)check— the idempotency probe
Bash-style (bash, ansible)
The core of this type is a run/check pair: run is what to do, check is how to know it’s
already done.
setup:
- urn: "urn:hams:bash:init-zsh"
step: Install ZSH theme
run: "curl -sSL .../installer.sh | bash"
check: "test -f ~/.oh-my-zsh/custom/themes/jovial.zsh-theme"Check exits 0 and hams skips. Anything else and hams runs run.
State file
Lives at <store>/.state/<machine-id>/<Provider>.state.yaml:
schema_version: 2
provider: homebrew
machine_id: MacbookProM5X
last_apply_session: "20260413T100000"
resources:
htop:
state: ok
version: "3.3.0"
first_install_at: "20260413T100000"
updated_at: "20260413T100000"The first_install_at timestamp is set once on the resource’s very first install and is
immutable thereafter — re-install, upgrade, removal, and re-install-after-remove never change it.
updated_at bumps on every transition. When a resource is removed, hams adds a removed_at field
(and clears it again on the next install). Legacy schema_version: 1 files with the old install_at
field are migrated forward to first_install_at automatically on the next read.
State files capture what’s actually true on this machine right now: is the resource installed,
at what version, as of when. .state/ is gitignored by default — it’s a local fact, not something
you share between machines.
Key bits:
last_apply_session— timestamp of the most recent successful apply for this providerresources— keyed by resource name. Each one tracks state (ok/missing/outdated/failed), version, and install timestamp
File suffixes worth knowing
| Suffix | Meaning |
|---|---|
.hams.yaml | The main Hamsfile. Checked into git |
.hams.local.yaml | Machine-specific overrides. Gitignored by default. Good for secrets and personal tools |
.state.yaml | State file. Gitignored |
hams.config.yaml | Global / project-level config |
hams.config.local.yaml | Local overrides. Gitignored |
If a provider has both .hams.yaml and .hams.local.yaml, hams merges them at apply time. The
local entries won’t leak to your store repo, but they’ll still be installed on this machine.