Skip to content

Repository files navigation

kitbag

kitbag

Everything this machine holds that is yours — what it is, whose it is, and how it gets onto the next machine.

ci license rust status

English · 한국어

kitbag status
$ kitbag status

  personal  (12)
  ├── = env:couchdb              COUCHDB_URI COUCHDB_DB COUCHDB_USER COUCHDB_PASSWORD
  ├── = ssh:id_ed25519           SHA256:XhCQXT9l7zas… (ED25519)
  └── + ssh:authorized_keys      5 keys: laptop desktop mini server phone

  shared · a friend  (4)
  └── = env:llm-proxy            PROXY_URL PROXY_TOKEN MODEL

  work · acme  (17)
  ├── ~ env:ci                   CI_TOKEN CI_URL DEPLOY_KEY_ID …  +5 more
  └── + file:aws-keychain        binary, 25788 bytes → ~/Library/Keychains/…

  mixed  (3)
  └── ? app:accounts             one bundle — personal, work

  + 3 new   ~ 1 changed   = 32 unchanged   ? 1 to build

Status: early, and working. Every command below does what it says, against a real machine and a real store — bw, op, pass or an age-encrypted file. What is missing is the installer half: recipes cover packages, links, macOS defaults and commands, and the rest of a machine's setup is still ahead. See DESIGN.md for the argument and the plan.

The question nothing answers

Setting up a machine is two jobs handled by two kinds of tool. Dotfile managers move your configuration. Password managers move your secrets. Neither can answer the one that matters once you have more than one machine and more than one employer:

What on this machine belongs to my company, and what happens to it when I leave?

kitbag treats packages, configuration, system settings, credentials and app data as the same kind of thing — a desired state, a source, a way to apply it, and an owner — and filters every one of them through that owner.

scope meaning
personal yours
work an employer's or a client's
shared an account someone else owns that you were given
mixed one artifact holding several — it has to say which
local this machine only, never leaves it

A machine declares which scopes it takes. A personal laptop never restores work credentials. A work machine does not install your personal toys. The same filter decides what is sent, what is written, and what a report shows.

Install

curl -LsSf https://raw.githubusercontent.com/Open330/kitbag/main/install.sh | sh

Detects the platform, checks the download against the checksums published with it, and puts one binary in ~/.local/bin. Or cargo install --path crates/kitbag-cli.

Commands

kitbag status                 what this machine has, marked against the store
kitbag plan                   what apply would change, and nothing else
kitbag apply                  make the machine match the recipes
                              (packages, links, defaults, downloads, clones, merges)
kitbag discover               find personal state nothing is tracking yet
kitbag track <path> --scope work
kitbag push / restore         move it, one scope at a time
kitbag doctor                 permissions, reachability, unscoped files, orphans
kitbag lint                   refuse the things that must not be committed
kitbag trust sync             the machines that may log in here
kitbag completions zsh        …bash, fish, elvish, powershell

--json on everything, --color auto|always|never, NO_COLOR respected, and no command ever prints a secret's value.

Three stores, and why

A public dotfiles repository is safe only if it leaves out the list of what exists. ~/.envs/kibana.env → work names an employer, a stack and a target, and nobody needs the secret to make use of that.

  repo (public)        recipes and providers — no employer, no service name
  inventory (private)  what exists, where it goes, which scope — in the store
  secret store         the values — a vault, or an encrypted file

Collection is by pattern, never by name. Scope is a marker the file carries (# scope: work), not a table in the repo. kitbag lint fails a commit that breaks either rule — and runs over this repository on every CI run, because a tool that leaked its own author's machine would have argued against its design.

Secret stores

kitbag does not implement one. It borrows yours, so that losing interest in kitbag never strands your secrets inside it.

backend store
bw Bitwarden / Vaultwarden — free tier, self-hostable v0.1
op 1Password — the best developer CLI in the category v0.1
pass pass / gopass — GPG and a git repo v0.2
age an age-encrypted file — no server at all v0.2

Adding one is a list/get/put adapter. Everything kitbag needs to know about an item travels inside its own envelope, so a store that can keep bytes under a name is enough:

kitbag/1
scope: work
owner: acme
encoding: utf8
sha256: 1f0e3d…

export TOKEN=…
Why there is no delete in the backend trait

A store holds things your machine knows nothing about — another machine's key, an account someone else added. A tool that removes what it does not recognise eventually removes something that mattered. Removal is a person's decision, taken with the store's own client.

Prior art

chezmoi and yadm manage dotfiles and do it well; neither models ownership or moves credentials. 1Password CLI and SOPS manage secrets and not the machine. Mackup moved app state and is unmaintained. kitbag is the overlap: personal state, wherever it lives, with an owner attached.

Building

cargo test --workspace
cargo clippy --workspace --all-targets -- -D warnings

Contributions welcome — read CONTRIBUTING.md first; the first rule is that nobody's machine goes in this repository.

MIT · DESIGN.md

About

Everything this machine holds that is yours: what it is, whose it is, and how it gets onto the next machine

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages