The lock file and the machine store
01One file in your project records exactly which package versions it got, down to a checksum of their content, so a teammate installs the same bytes. One folder on your machine keeps those bytes once per computer, so a second project or an offline day costs nothing extra.
Identity is the content, not the address
03A package version is identified by four things: its group, its name, its version, and a fingerprint of every file it contains. The address it was fetched from is written down for information only. That is why a mirror, a moved repository or a vendored copy never changes the lock file: as long as the bytes are the same, the package is the same.
04 Decision. A package's identity is the tuple(kind, name, version, content_hash). Thecontent_hashis a digest over the deterministically-ordered concatenation of(rel_path_bytes || 0x00 || file_bytes || 0x00)for every file in the package directory, and the value names the recipe that produced it (PROP-044 §4.7):sha256-tree/1:<hex>is recipe 1, whose exclusion list, path normalisation and traversal order are carried as data informats/hash_recipes/1.toml; the baresha256:<hex>is recipe 0, the pre-recipe form, frozen verbatim in code — not configurable, because a frozen recipe that can be edited is not frozen — so that values written before recipes were named stay readable. Two hashes are comparable only at the same recipe; comparing across recipes answers a question nobody asked, and is never done silently. PROP-024 §2.2 re-scopes this to the package's shippable tree — its source, minus build output (.git/,.vibe/,target/,node_modules/,.vibeignoreglobs) — so a code-bearing package's identity is its source, not its build state; that exclusion lands with the code that implements it. The URL used to fetch the content is informational — recorded in the lockfile for debuggability, not for identity.
05The fingerprint is also the integrity gate. If a source serves different bytes under a known version, because a tag was force-pushed or a mirror was tampered with, vibe refuses before writing anything, and tells you which fingerprint it expected and which it saw.
06 a force-pushed tag upstream is caught by the same machinery on the next install.
The lock file
07vibe.lock lists every package in the resolved graph, direct and transitive, with its exact version, its fingerprint, the registry it came from and how it was resolved. vibe writes it on every install and update; you commit it and never edit it. A fresh clone with the lock file installs the identical graph, and a pull request that changes it shows precisely what moved.
08The lock file is kept even when derived state is removed: vibe clean deletes the dependency tree and the generated boot files but leaves the lock alone, because the lock is the recorded decision and the tree is only its consequence.
09 Never touched —vibe.lock. The lock is the recorded resolution, not derived state: keeping it is what makesvibe clean install --offlinereproduce the exact world from the machine cache with zero network — the mvn analogy istarget/vs the dependency resolution, and the lock sits on the resolution side.
The machine store
10Every package vibe fetches, for any project, lands in one store under your home directory, ~/.vibe/cache/, keyed by the package's identity rather than by where it came from. A version fetched for one project is available to every other project on the machine, whichever registry they configure, and the store is never emptied behind your back: reclaiming space is a command you run.
11 Decision (override clause corrected 2026-08-20 to the later, more specific ruling). The package store is machine-global, not project-scoped — one store per machine at<settings-home>/cache, relocated only with the settings home ($VIBE_SETTINGS); no store-specific override exists —##THE-STORE-IS-DOT-VIBE-CACHEis the governing ruling. (VIBE_REGISTRY_CACHE, which this decision originally named, governs the registry clone cache — a different layer that keeps its own job.)
12 Decision (owner, 2026-08-20): the store is~/.vibe/cache/, beside~/.vibe/registries/(the registry git clones, which keep their own separate job) and under the one settings home.
13 Reclaiming space is an explicit operator action (§2.8), never a surprise.
14The store can be warmed on purpose. vibe cache add fetches a package and everything it depends on without touching any project, which is how you prepare for a flight or how a machine gets the manual of a package for local reading. vibe cache list shows what the store holds; vibe cache check verifies every entry against its fingerprint.
15
content_hash is the integrity gate: a cache entry is valid only if its content hashes to the recorded hash. Two sources claiming the same identity with divergent bytes are a collision, surfaced per PROP-008's collision rules, never silently merged.
16vibe cache add <pkgref>…— deliberately pre-warm: fetch a package and its dependency closure into the cache while online, so a later--offlinerun finds it. The "I am about to go offline, pull down what I will need" workflow. It fetches from the project's[[registry]]when run inside a project, otherwise from the user-level registries (§2.4).
17
vibe cache list — the packages and versions present locally; the offline-resolvable inventory.
18vibe cache check(owner, 2026-08-20) — the integrity sweep, and the only place the store is fully re-hashed. It walks every entry, recomputes the content hash, and reports each one that no longer matches what was recorded. It is the answer to «how do you forbid overwriting»: nothing forbids it, and this is what notices. («What was recorded» is an integrity sidecarv<version>.sha256written once beside the entry at insert — beside, not inside, or the record would change the very tree it pins; an entry without one is the honestunrecordedclass, not an error.)
19vibe cache clean reclaims space, all of it, by age or by package, and only when you say which.
20
vibe cache clean — reclaim space: all, by age, or by package.
Offline
21With --offline, or VIBE_OFFLINE=1 in the environment, vibe touches no network at all. Resolution and fetch are satisfied from the store, local mirrors, the project's own dependency tree and path sources. Anything that is not there is a hard error naming the missing package; vibe never silently installs a partial result.
22
Decision. A global --offline flag forbids all network access for the invocation.
23 Anything not available locally is a hard error with an actionable message: it names the missing package and version and tells the operator how to recover (run once online,vibe cache add, orvibe registry vendor).
24A version in the store is usable even if no registry lists it any more. The store holds bytes that were verified when they arrived; a registry that has gone quiet does not make them worse.
25 Decision (owner, 2026-08-19). A package version present in the cache is usable, and materialises, even when it exists in no registry at all — deleted upstream, the whole organisation gone, every mirror down. This is not the--offlinepolicy:--offlineforbids the network, while this governs a run where the network is allowed, was consulted, and answered "no such package". (Built 2026-08-20: the fallback fires strictly on the absence forms and rides the LOCKFILE pin — provenance comes from the existing lock entry, so a version in the store but in no lock and no registry is not rescued; minting a store-provenance wire form for that case is an owner act, held open.)
Edge cases and rules
26The settings folder, including the store, is ~/.vibe/ on every platform; the environment variable VIBE_SETTINGS moves the whole folder, which is how tests and build servers keep a private copy.
27 The settings home is~/.vibe(owner, 2026-08-20). This document previously named~/.config/vibe/config.toml; the code has treated~/.vibeas canonical all along and the XDG path only as a legacy location an operator is invited to migrate out of. The correction is to this document, not to the tree.
28The store and the registry clone cache are two different folders: the store holds extracted package versions, the clone cache under ~/.vibe/registries/ holds git checkouts used to fetch them. Cleaning one does not clean the other.
29
Decision (owner, 2026-08-19). The on-disk layout is per-identity extracted directories, one per (group, name, version). Git clones indexed by identity are rejected.