your whole environment, in one bag.
packages from any source plus your dotfiles — described once in Python, fetched, built, and deployed by a Rust core. roll back anytime.
$ curl -fsSL https://gripsack.dev/install.sh | sh
nothing to install yet — this is the shape of it. planned:
cargo install gripsack · brew install gripsack-dev/tap/gripsack
· release binaries
pick a palette
pure CSS variables — the logo re-tints too. your pick persists per browser.
how it works
describe
modules are typed Python in your repo: sources, build steps, config files, ownership.
evaluate
modules evaluate to IR — pure data. host facts & tags resolve here, nowhere else.
lock
the resolved plan pins URLs and content hashes per host. reproducible, no sandbox.
build
fetchers and builders run as a DAG into hash-addressed store paths.
activate
one atomic symlink flip. dotfiles deployed per ownership mode.
roll back
flip current to any previous generation. that's the whole mechanism.
a module
from gripsack import module, github_release, symlink, tracked_copy helix = module( "helix", source=github_release( repo="helix-editor/helix", asset="helix-{version}-x86_64-linux.tar.xz", ), install={"bin/hx": symlink("~/.local/bin/hx")}, config={ "config.toml": tracked_copy("~/.config/helix/config.toml"), "languages.toml": tracked_copy("~/.config/helix/languages.toml"), }, )
full IDE support — pyright gives you autocomplete, inline errors, and refactors, because a module is just typed Python. evaluation emits IR (JSON); the Rust core only ever consumes IR.
what it does
modules
everything is a module: how to get it, build it, where its files and configs live. modules depend on modules; build-only deps are ephemeral.
any source
github releases, tarballs, git builds, cargo, distro packages. fetchers are pluggable verbs, conditionally selected per host.
dotfiles, first-class
ownership per file: owned symlink, tracked-copy with drift detection, merge blocks, template for per-machine values.
generations
every apply — one module or the whole graph — is a new generation. activation is one atomic rename; rollback is flipping it back.
lockfiles, not sandboxes
impure by default, reproducible by pinned URLs and hashes. nix's ergonomics without nix's enforcement.
hosts & facts
one repo, many machines. fedora laptop, ubuntu desktop: facts auto-detected, tags declared, conditionals resolved at eval.
compare
| gripsack | nix + home-manager | chezmoi · stow | apt · brew · mise | |
|---|---|---|---|---|
| packages from any source | ✓ | nixpkgs | · | own repos |
| dotfiles managed | ✓ | ✓ | ✓ | · |
| generations & rollback | ✓ | ✓ | git history | · |
| config ownership modes | ✓ | owned only | templates | · |
| no custom language | python | nix lang | ✓ | ✓ |
| reproducible without sandbox | lockfiles | flakes | ✓ | · |
| no daemon, no root | ✓ | daemon · /nix | ✓ | apt needs root |
| per-host conditionals | facts + tags | modules | templates | · |
follow along
$ grip apply --repo git@github.com:you/myenv
the design is public and moving: 0001 — architecture. star the repo if this is the package manager you also wish existed.