Get Started
A thin layer over the package managers you already use. Desired state lives in one git-friendly genv.json. Run genv apply and the machine matches the spec.
macOS
Arch / Manjaro
Also yay -S genv or the genv-bin package.
Other Linux
Windows (PowerShell)
scoop bucket add ks1686 https://github.com/ks1686/scoop-bucket then scoop install genv (self-hosted bucket). winget and choco installers for the genv binary are unpublished. Once genv is on PATH, it still manages packages through those managers.
Any platform (Go 1.24+)
Features
Multi-target
Schema v8 splits shared defaults from OS buckets: macOS, Windows, Arch, Ubuntu-like Linux, and WSL2.
Declarative spec
Commit genv.json. The lock file stays machine-local and is never committed.
Portable mapping
genv map and genv export help move a spec across Linux families without guessing managers.
Version pinning
Pin with constraints like 0.10.*, or omit the version for latest.
Dry-run first
genv apply --dry-run prints the reconcile plan. Add --yes for CI.
PowerShell parity
Schema v7+ can target powershell for aliases, profiles, and hooks on native Windows.
Plugin adapters
Schema v8 adapters cover plugin CLIs genv does not ship built-in (claude plugin, gh extension, β¦). Set prefer to the adapter name.
Managed externals
Schema v9 can attach a managed external recipe that discovers GitHub Releases or structured HTTP releases, verifies the artifact, and installs or removes it.
Local modules
Schema v10 registers local JSON modules and each target picks them with useModules. Conflicting declarations are an error naming both origins, never a silent override.
Verified services
Schema v10 services declare requires, watch, restart_policy, and health_check; genv upgrade restarts only on a proven change. Windows gets a Task Scheduler backend via scheduled_task.
Targets & Package Managers
| Target | Detected when | Typical managers |
|---|---|---|
| macos | macOS | brew mas |
| windows | native Windows | winget scoop choco |
| arch | native Arch / Arch-like | pacman paru yay |
| ubuntu | Ubuntu-like Linux or Ubuntu-like WSL2 | apt snap linuxbrew |
| wsl-arch | Arch-like WSL2 | pacman paru yay |
| linux | optional catch-all via --target / $GENV_TARGET | apt dnf apk β¦ |
WSL2 does not inherit native arch. Put shared bits in defaults; use targets.ubuntu or targets.wsl-arch for distro-specific packages.
Quick Start
# One machine genv init genv add git genv add neovim --version "0.10.*" genv scan genv status genv apply --dry-run genv apply --yes # Multiple machines (schema v8) genv migrate --write genv map --target arch genv export --target ubuntu --out ./u genv apply --target ubuntu --dry-run genv apply --target ubuntu --yes
Active target resolution: --target β $GENV_TARGET β host classification. Spec default is ~/.config/genv/genv.json; the lock is machine-local.
CLI Reference
| Command | Purpose |
|---|---|
| add / remove | Track + install / untrack + uninstall |
| adopt / disown | Track without install / untrack without uninstall |
| scan / status / list | Bulk-adopt, show drift, show lock |
| apply | Reconcile (--dry-run, --yes, --json, --target, --force-new-lock) |
| migrate | Upgrade a v1-v7 spec to v8 targets (v10 specs are left as-is) |
| export / map | Single-target snapshot / assist-only manager suggestions |
| updates / profile / pull | Background checker, named overlays, fetch spec from repo |
| env / shell / service | Env vars, aliases, user services (service restart <name>) |
| config / explain | Composed v10 environment (--registry) / where one resource comes from |
| completion | bash, zsh, fish, powershell |
--file <path>: spec path (default under~/.config/genv/)--lock-file <path>: lock path (defaultgenv.lock.json)--target <id>: v8 target for apply / status / upgrade / mutate / export / map / scan--json: machine-readable envelope on stdout
Spec Format (schema v8 / v9 / v10)
{
"schemaVersion": "8",
"defaults": {
"env": {
"EDITOR": { "value": "nvim" }
},
"shell": {
"aliases": {
"ll": { "value": "ls -lah" }
}
}
},
"targets": {
"macos": {
"packages": [
{ "id": "git", "prefer": "brew" },
{ "id": "ripgrep", "prefer": "brew" }
]
},
"ubuntu": {
"packages": [
{ "id": "git", "prefer": "apt" }
],
"env": {
"EDITOR": null
}
},
"windows": {
"packages": [
{
"id": "git",
"prefer": "winget",
"managers": { "winget": "Git.Git", "scoop": "git", "choco": "git" }
}
]
}
},
"updates": {
"enabled": true,
"interval": "24h",
"autoApply": false,
"notify": true
},
"repo": {
"url": "https://github.com/example/dotfiles",
"ref": "main"
}
}
- Desired state lives under
defaultsand/ortargets.<id>, not as top-levelpackages/env. - No per-record
hostin v8/v9 (use target buckets). - Target map entries may be
nulltombstones to drop a default for one OS. repo,updates, and v8adaptersstay top-level. Prefer v8 unless a managed external recipe requires v9.- Schema v8
adaptersdeclare plugin CLIs. Details are in SCHEMA.md. - Schema v9 managed external releases: docs/external-releases.md and SCHEMA.md.
- Legacy v1-v7 specs still load. Convert with
genv migrate.
Schema v10 adds local modules. genv.json registers module files by name under modules, and each target selects them with useModules:
{
"schemaVersion": "10",
"modules": {
"base": "modules/base.json",
"dev": "modules/dev.json"
},
"targets": {
"macos": {
"useModules": ["dev"],
"packages": [{ "id": "ghostty" }]
}
}
}
// modules/base.json
{
"schemaVersion": "10",
"defaults": {
"packages": [{ "id": "jq" }],
"env": { "EDITOR": { "value": "nvim" } }
}
}
// modules/dev.json
{
"schemaVersion": "10",
"requiresModules": ["base"],
"defaults": { "packages": [{ "id": "ripgrep" }] }
}
- Modules are local, trusted files. Paths resolve inside the spec's directory; a module that escapes it is refused.
useModulesis additive: selectingdevalso selectsbasethroughrequiresModules. Here macOS gets ghostty, ripgrep, jq, andEDITOR.- There is no override syntax. An identical declaration in the root and a module is kept once; a conflicting one is an error naming both origins.
- Module-owned resources are read-only from the CLI. Inspect a composition with
genv config --target macos.genv migraterefuses v10. Details in SCHEMA.md.
How Apply and Locks Work
v8 merges defaults with the active target (including tombstones), then reconciles packages, env, shell, files, services, and hooks.
Read
Reads the spec and the lock, then resolves --target / $GENV_TARGET / host class.
Foreign lock
Refuses a lock from the wrong target or OS. Recover with genv apply --force-new-lock.
Install / uninstall
Installs spec-only packages; uninstalls lock entries removed from the spec.
Lock
Updates the lock after each run. v8 records target + goos.
Roadmap
The v3 line closed the old milestone backlog. v4 is schema v8 multi-target configs plus schema v9 managed external releases, and schema v10 adds local modules and dependency-aware services. Future work lives in GitHub issues and ROADMAP.md.
Ready to get started?
GENV v4.7.4 is here. It is open-source and MIT licensed. Adapter quality and test coverage are where contributions matter most.