hav
Philosophy

Design principles

The rules hav is built by. They are few, they are enforced everywhere, and when a proposed feature conflicts with one of them, the feature loses. Why hav exists explains where they come from. This page states them.

The working copy is the unit of work

There is no staging area, no stash, no "uncommitted changes" limbo, and no commit verb. The working directory is a knot, and every command begins by capturing it. Naming work (describe) and landing it (tie) are the only ceremonies left, and both are one line.

The corollary is honest bookkeeping: at any instant, everything on disk is already versioned. There is no state the system hasn't seen.

Files never move under you

hav never rewrites a working tree uninvited, whether on a buoy advance, a history rewrite, or a pull. A build, a test suite, or an agent may be running against that tree, and moving it out from under them corrupts results in ways nobody can debug.

Instead, hav practices pin-with-notification: your workspace stays where it is, st tells you the world moved, and the explicit sync command is the only thing that ever changes your files in response. The same principle applies everywhere. Buoy advances notify, cascades skip workspace-held knots, and pull never touches trees.

Nothing ever blocks

Merging, syncing, landing, and rebasing always complete and always return a snapshot. A conflict is data stored inside the snap, materialized as markers in whichever tree asked for them, and resolved whenever its owner gets around to it. There is no --continue, no --abort, and no "you have unmerged paths" wall anywhere.

This is what makes automatic descendant rebasing legal, since a descendant can always hold a conflict. That in turn makes history curation cheap, which is what makes the narrative plane real.

Everything is undoable

Every mutating command is exactly one journaled operation carrying a complete before/after view of the repo's mutable state. undo restores the view and re-projects the files on disk. Failed multi-step mutations roll back atomically, leaving no partial state to clean up.

Undo always names what it did. Silent state toggles are a documented mistake hav refuses to repeat.

Descriptions name the work

A knot's description says what the work is, in a line, which is enough to pick it out of a log. The why belongs in the latest snapshot, in code, comments, and docs, where it can be corrected. hav deliberately builds no ceremony around commit-message archaeology.

Explicit beats implicit; hav is unopinionated

hav never decides for you what belongs in your repository. It ships no ignore rules, writes no config files, and auto-populates nothing. Where a wrong default would be destructive, such as the first capture of a tree full of node_modules/, the guardrail counts, notifies, and asks. It never silently decides. The .havignore file is yours, written by hand, when you want one.

One verb per idea

There is one merge verb (tie), one explicit file-moving verb (sync), one pointer-surgery verb (move), and one destroy-scope verb (sweep). Verbs do not grow modes that change their meaning, and new ideas get new names. A user who knows what a verb does should never be surprised by a flag.

Secrecy is scoped honestly

The cryptography is boring on purpose: X25519, XChaCha20-Poly1305, and BLAKE3, composed conservatively. Every claim is scoped to what it actually delivers:

  • Encryption protects bytes at rest and in transit, and that is the whole gate. Sealed rows travel to everyone, members hold the keys that open them, and removed members keep what they already read.
  • Encryption cannot withhold bytes from particular people, run accounts, or remember who fetched what, and hav does not pretend to do those things either. They belong to a hosting product built on top and named as such, rather than to a server dressed up as enforcement.

Overstating that scope was the most common fatal flaw in the designs hav rejected.

Ids are forever; placement can change

A snap id is the BLAKE3 hash of plaintext content, and it survives describing, squashing, repacking, and revealing sealed work. Everything about where and how bytes are stored (packs, chunks, ciphertext) lives beneath the id and can change freely. This identity/placement split is what lets "publish the keys later" coexist with "ids are stable forever."

Output is single-line-per-fact

Every command's output is built to be read by a human in a hurry and parsed by a machine without a library: one line per fact, a stable id grammar (kn-, sn-, op-…), and errors that name the remedy (not in sync with main — hav sync main). There are no pagers and no prompts except where a guardrail genuinely needs consent.

On this page