# The lock file and the machine store {#root}

@status:doc/work @audience:user

[p01] One 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.

[p02]
```sh
vibe cache path
```

```output
<TMP>/home/cache
```

## Identity is the content, not the address {#identity}

[p03] A package version is identified by four things: its group, its name, its version, and a [fingerprint](../glossary/index.xml#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](../glossary/index.xml#lock-file): as long as the bytes are the same, the package is the same.

> [p04] **Decision.** A package's identity is the tuple `(kind, name, version, content_hash)`. The `content_hash` is 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](../../common/PROP-044-change-native-formats.xml#machinery)): `sha256-tree/1:<hex>` is recipe 1, whose exclusion list, path normalisation and traversal order are carried as data in `formats/hash_recipes/1.toml`; the bare `sha256:<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](../../common/PROP-024-code-bearing-packages.xml#shippable-tree) re-scopes this to the package's **shippable tree** — its source, minus build output (`.git/`, `.vibe/`, `target/`, `node_modules/`, `.vibeignore` globs) — 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.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#IDENTITY-TUPLE>

[p05] The 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.

> [p06] a force-pushed tag upstream is caught by the same machinery on the next install.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#EFF-FORCE-PUSH-CAUGHT>

## The lock file {#the-lock-file}

[p07] `vibe.lock` lists every package in the resolved graph, direct and transitive, with its exact version, its fingerprint, the [registry](../glossary/index.xml#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.

[p08] The 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.

> [p09] **Never touched — `vibe.lock`.** The lock is the recorded resolution, not derived state: keeping it is what makes `vibe clean install --offline` reproduce the exact world from the machine cache with zero network — the mvn analogy is `target/` vs the dependency resolution, and the lock sits on the resolution side.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-053#CLEAN-KEEPS-THE-LOCK>

## The machine store {#the-store}

[p10] Every package vibe fetches, for any project, lands in one [store](../glossary/index.xml#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.

> [p11] **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-CACHE`](#layout) is the governing ruling. (`VIBE_REGISTRY_CACHE`, which this decision originally named, governs the registry **clone** cache — a different layer that keeps its own job.)
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-010#CACHE-MACHINE-GLOBAL>

> [p12] **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.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-010#THE-STORE-IS-DOT-VIBE-CACHE>

> [p13] Reclaiming space is an explicit operator action (§2.8), never a surprise.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-010#EXPLICIT-RECLAIM>

[p14] The 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.

> [p15] `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.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-010#HASH-INTEGRITY-GATE>

> [p16] `vibe cache add <pkgref>…` — deliberately pre-warm: fetch a package and its dependency closure into the cache while online, so a later `--offline` run 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).
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-010#CMD-ADD>

> [p17] `vibe cache list` — the packages and versions present locally; the offline-resolvable inventory.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-010#CMD-LIST>

> [p18] `vibe 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 sidecar `v<version>.sha256` written 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 honest `unrecorded` class, not an error.)*
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-010#CMD-CHECK>

[p19] `vibe cache clean` reclaims space, all of it, by age or by package, and only when you say which.

> [p20] `vibe cache clean` — reclaim space: all, by age, or by package.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-010#CMD-CLEAN>

## Offline {#offline}

[p21] With `--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.

> [p22] **Decision.** A global `--offline` flag forbids all network access for the invocation.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-010#OFFLINE-FLAG>

> [p23] 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`, or `vibe registry vendor`).
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-010#OFFLINE-HARD-ERROR>

[p24] A 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.

> [p25] **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 `--offline` policy: `--offline` forbids 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.)*
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-010#A-CACHE-HIT-IS-AUTHORITATIVE-FOR-AVAILABILITY>

## Edge cases and rules {#edge-cases}

[p26] The 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.

> [p27] **The settings home is `~/.vibe`** (owner, 2026-08-20). This document previously named `~/.config/vibe/config.toml`; the code has treated `~/.vibe` as 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.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-010#THE-SETTINGS-HOME-IS-DOT-VIBE-NOT-XDG>

[p28] The 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.

> [p29] **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.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-010#LAYOUT-EXTRACTED-DIRECTORIES>

