# Registries and the index {#root}

@status:doc/work @audience:user

[p01] Packages are published to a place vibe knows how to read: by default a public organisation on GitHub, one repository per package. A project lists such places in the order it trusts them. A catalogue beside each one answers searches without cloning anything.

[p02]
```sh
vibe registry list --path hello-vibe
```

```output
No `[[registry]]` entries in `vibe.toml`. Use `--registry <path>` on `vibe install` for a local-directory source, or add a `[[registry]]` block.
```

## What a registry is {#what-a-registry-is}

[p03] A [registry](../glossary/index.xml#registry) is not a server vibe runs. It is a hosting organisation, such as `https://github.com/vibespecs`, where every package is its own git repository named after the package's [coordinate](../glossary/index.xml#coordinate). Publishing means pushing a repository and tagging a version; installing means cloning at a tag. Who may publish is decided by the hosting service's own permissions, so vibe carries no accounts of its own.

> [p04] Each package is its **own** git repository — no monorepo. Per-package maintainer permissions are hosting-native (a package repo's owner controls access); no central merge queue.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#SHAPE-OWN-REPO>

[p05] A project declares its registries in the [manifest](../glossary/index.xml#manifest) as an ordered list. Each entry has a local name, the organisation's root address, and a naming convention that maps a coordinate to a repository name. When a package is requested, vibe walks the list in order and the first registry that has a matching version wins.

> [p06] Resolution: the solver iterates registries in array order; the first that has a satisfying match for a pkgref wins. Versions of the same pkgref are **not** unioned across registries — this prevents a lower-trust registry from influencing resolve when a higher-trust one already has a valid answer.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#REGISTRY-WALK-ORDER>

[p07] The list is an array in priority order; beside it a [mirror](../glossary/index.xml#mirror) is a second address for the same registry, and an [override](../glossary/index.xml#override) bypasses the walk for one coordinate. An entry's `url` is the organisation's root, never a package repository, and it is a plain git address, `https://`, `ssh://`, `git@host:` or `file://`, with no shorthand for any particular host. Its `naming` says how a coordinate becomes a repository name; the default joins the group and the name with a dot.

> [p08] `[[registry]]` is an **array**, priority-ordered. `[[mirror]]` is a first-class fallback layer, transparent to the lockfile. `[[override]]` bypasses the resolver for pins. Schema and code path support all three from day one.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#SHAPE-REGISTRY-ARRAY>

> [p09] `url` — **organization root URL**, not a package repo URL. A registry is a hosting-org; packages are children of it.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#REG-FIELD-URL>

> [p10] URL syntax is **just git URL** — `git@host:…`, `ssh://`, `https://`, `file://`. No `github:` / `gitverse:` shorthands. New hosts "just work" as long as `git` speaks to them.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#SHAPE-PLAIN-GIT-URL>

> [p11] `naming` — convention for mapping a pkgref to a package repo name under this org. Values: `"fqdn"` (**default** — `org.vibevm.world/wal` → `<org>/org.vibevm.world.wal`; introduced and made the default by [PROP-008 §2.5](PROP-008-qualified-naming.xml#repo-naming), shipped M1.19 as a `_`-joined form and re-ruled to the dot join 2026-08-13), `"kind-name"` (legacy — `flow:wal` → `<org>/flow-wal`; the default this section originally declared, superseded by PROP-008), `"name"` (if name collisions are impossible in a given registry), `"kind/name"` (for hosts supporting nested repos). Other registries may ship with different conventions; the setting is per-registry, not global.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#REG-FIELD-NAMING>

[p12] The walk moves on to the next registry only when one answers that it does not have the package. A connection failure, a server error or a malformed manifest stops the install with that error, because an outage or a typo is something you want to know about. On a registry declared public, a demand for credentials counts as «not here» and the walk continues; on a registry that declares an authentication regime, it is a real failure.

> [p13] A `[[registry]]` is a **distinct package source** — its own naming convention, its own publishing identity, its own trust scope. The priority-ordered registry walk falls through on **`UnknownPackage` only**: a registry that confidently answers "I don't have this package" is free to defer to the next one. Any other primary failure — connect-failure (DNS / TCP), auth-failure on a registry that explicitly requires authentication, server error, malformed manifest — halts the install with an actionable error. This is the same policy Cargo and npm apply to a registry that errors out: the operator wants to know about a typo or an outage, not paper over it with a different registry that may carry a different version.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#REGISTRY-WALK-SEMANTICS>

> [p14] **`auth`-aware 401 classification (§2.2.1).** On `auth = "none"` a 401 / 403 response is an `UnknownPackage` signal, not an auth-failure: the registry is declared public, anything that responds with "you cannot read this without credentials" is — from this consumer's standpoint — equivalent to "this package does not have a public answer here." The walk falls through to the next registry, exactly like a 404. This is what unblocks the common case where one host (GitVerse) returns 401 for a missing repo while another (GitHub) returns 404 — the resolver treats both uniformly. On `auth = "token-env"` or `"credential-helper"` a 401 is a real `AuthFailed` and halts: the registry was declared as authenticated, the credentials presented were rejected, this is information the operator must see.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#AUTH-AWARE-401>

[p15] An entry can be switched off without being deleted: `enabled = false` makes every command skip that registry until you flip it back.

> [p16] **Decision.** Every `[[registry]]` carries an `enabled` flag, default `true`. Setting `enabled = false` switches a registry off **without deleting its entry** — it is skipped by **every** resolution path (`install` / `outdated` / `search` / `registry sync` / `vendor`), because the filter lives at the one resolver-construction point (`MultiRegistryResolver::from_manifest`): a disabled registry is never built, so nothing downstream can consult it. Re-enable by flipping the flag back; no re-add. The default `true` is skipped on serialize, so only an explicit `enabled = false` appears in a written `vibe.toml`. The flag applies uniformly to a project `vibe.toml` and the machine-global `~/.vibe/registry.toml`.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#ENABLED-FLAG>

[p17] A machine may add registries of its own in `~/.vibe/registry.toml`, merged after the project's; a project's list always wins over the machine's. This is how a company points every project on a laptop at its private registry without editing each project. The file carries the same `[[registry]]`, `[[mirror]]` and `[[override]]` sections as a project manifest, for any registry, remote or local; a name declared in both places is the project's.

> [p18] Project-level `[[registry]]` always overrides the user-level default — the same precedence the `UserConfig` `[env]` layer already follows (the project / live value wins).
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-010#PROJECT-OVERRIDES>

> [p19] **Decision.** Registry settings may also live in a per-user file resolved through the settings chokepoint (`vibe_core::settings::registry_config_path` → `~/.vibe/registry.toml`, or `$VIBE_SETTINGS/registry.toml`). It carries the same `[[registry]]` / `[[mirror]]` / `[[override]]` sections as a project `vibe.toml` — **any** registry, not only local ones: a remote `https://` / `ssh://` / `git@` org (with `auth`) is merged and searched exactly like a `file://` / path repo. A common motivation is keeping **machine-local** registries (a `file://` checkout, a path repo) out of a team-shared `vibe.toml`, where a hard-coded local path would differ per teammate; but a whole extra remote registry can be added machine-wide the same way. (Locality matters only to `--offline`, §2.2.2.1.)
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#GLOBAL-REGISTRY-FILE>

> [p20] **Merge — project first, dedupe by name.** The effective registry list is the project's `[[registry]]` entries followed by the global file's, with a `name` collision resolved in the **project's** favour (the project entry wins; the global one is dropped). Mirrors are concatenated (project first). Overrides are project-first, deduped by `pkgref` (project wins). The merge is a pure function (`vibe_core::merge_effective`), verified in isolation. A project's explicit declaration always outranks a machine default, so a shared `vibe.toml` stays authoritative for the team while each machine supplements it with its own local repositories.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#MERGE-PROJECT-FIRST>

[p21] The default trust set the [specification](../glossary/index.xml#specification) names is exactly two roots, `https://github.com/vibespecs` and `https://gitverse.ru/vibespecs`. A project `vibe init` creates today carries no registry block at all, so before the first install add one with `vibe registry add`, or let the machine-wide file supply it. Every other registry is trusted only because you added it.

> [p22] **The default trust set is exactly two roots** — `https://github.com/vibespecs` and `https://gitverse.ru/vibespecs` — trusted by default as the registries `vibe init` writes. Every other registry is trusted **only** by the user's own act of adding it to their configuration (owner ruling, 2026-08-13).
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-008#DEFAULT-TRUSTED-REGISTRIES>

> [p23] **Default in new projects.** `vibe init` writes the default registry URL (`DEFAULT_REGISTRY_URL` in [`vibe_core::manifest`](../../crates/vibe-core/src/manifest/project.rs)) into every new `vibe.toml`'s `[[registry]]` entry unless the operator passes `--no-registry` or overrides with `--registry-url <URL>` / `--registry-ref <REF>`.
>
> <spec://org.vibevm.core/vibevm/common/PROP-000#INIT-DEFAULT-REGISTRY>

## The index {#the-index}

[p24] Cloning a repository to learn what is in it is slow, and listing an organisation to search it is impossible without an account. So a registry may keep an *[index](../glossary/index.xml#index-registry)*: a separate repository beside the packages that records, for every published version, the manifest's summary and the content [fingerprint](../glossary/index.xml#fingerprint). `vibe search` reads the index; a fresh install reads it to skip a round of clones.

> [p25] **Decision.** The index layer is **strictly additive**. Every existing vibevm code path keeps working exactly as today when no index is present. No registry is required to have an index. No project is required to consume one; a consumer that finds none falls back to the live `git ls-remote` path that exists today.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-index/PROP-005#INDEX-OPTIONAL>

[p26] The index is a cache, never the truth. If it disagrees with a package repository, the repository wins, and a package resolved through the index is still verified against its content fingerprint when it arrives. A registry without an index works exactly as before, only slower; a missing index is not an error.

> [p27] **Decision.** Package repositories are the **source of truth** for content (manifests, files, tags). The index is a **derived hot cache**, regeneratable from the authoritative package state.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-index/PROP-005#REPOS-AUTHORITATIVE>

> [p28] This matters because it disambiguates the failure mode: if the index disagrees with reality, **reality wins**.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-index/PROP-005#REALITY-WINS>

[p29] The index location is derived from the registry's address and can be overridden per registry with an environment variable named after the registry, `VIBEVM_INDEX_URL_<NAME>`; the literal value `none` switches index lookups off for that registry.

> [p30] **The environment variable `VIBEVM_INDEX_URL_<REGISTRY>` is the ladder's top rung — the operator's per-run re-point, no longer the only source.** Until 2026-08-20 it was the sole locator, deliberately weaker than the manifest field it stood in for (per-shell, per-run, travelling with neither project nor lockfile) — which is why it never closed the requirement above. Now it *overrides* the key: env beats `index_url` beats the default, and `none` at either explicit rung disables the index. The name normalization (`ASCII alphanumerics upper-cased, the rest to `_``) is unchanged.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-index/PROP-005#INDEX-URL-TODAY-IS-AN-ENVIRONMENT-VARIABLE>

[p31] A registry entry may pin its index with `index_url`; without one, a public GitHub organisation maps to its `index` repository on the raw content host and any other host to `<registry-url>/index`. A probe of the index has three outcomes: found, absent, or refused. Only absent falls through quietly to a live listing, because nobody promised an index; an index that is there and cannot be read is reported. `vibe search` asks each configured index the question directly rather than downloading the whole catalogue.

> [p32] **Configurable but defaulted.** A `[[registry]]` block pins a custom index location — the key exists in `RegistrySection` (one type serving both the project `vibe.toml` and the machine-global `~/.vibe/registry.toml`, so the columns share one vocabulary), and this exact block parses (pinned by test `prop005_index_url_example_parses`, which carries it verbatim):
>
> <spec://org.vibevm.core/vibevm/modules/vibe-index/PROP-005#INDEX-URL-CONFIG>

> [p33] The bottom rung is host-aware. Canonical public `https://github.com/<org>`
> maps to `https://raw.githubusercontent.com/<org>/index/<registry-ref>`;
> other hosts retain `<registry-url>/index`. This makes both fresh and already-
> seeded GitHub configurations whose `index_url` field is absent consume the static
> repository rather than its HTML page. The full ladder remains env override →
> manifest key → host-aware default; exact `none` on either explicit rung
> disables lookup. Lookalike hosts, nested paths, userinfo, unsafe owners/refs,
> queries and fragments are never rewritten.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-index/PROP-005#INDEX-URL-DEFAULT>

> [p34] **A probe answers `found`, `absent`, or `refused`, and the third is what keeps this section honest.** «Absent» is the only outcome that falls through quietly, because it is the one that means what the fall-through assumes: nothing is published here. An index that IS there and cannot serve this consumer — it refused us (401/403), its body does not parse as a handshake, its handshake format is one this build does not read, or it publishes no world of this build's epoch — answers **`refused`**, carrying the offered epochs, this build's epoch, a recipe, and whatever the document said in `min_client` / `notice` / `successor`. Collapsing that into «absent» would make a private, broken or newer-than-us index indistinguishable from a missing one, which is the silence [PROP-044 §2](../../common/PROP-044-change-native-formats.xml#laws) forbids: a break that announces itself is normal life, a riddle is what strands users.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-index/PROP-005#A-PROBE-HAS-THREE-OUTCOMES-NOT-TWO>

> [p35] 404 / connect-failure on the index → **silent** fallback to live `ls-remote`: no error message, because the operator never promised an index. This half is built, and it is the `absent` outcome of [`##A-PROBE-HAS-THREE-OUTCOMES-NOT-TWO`](#optional) — the other two outcomes are never silent.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-index/PROP-005#AN-ABSENT-INDEX-FALLS-BACK-WITHOUT-A-WORD>

> [p36] Walks every configured registry's index through the same client the resolver uses — probe, then query — rather than downloading `primary.jsonl.gz` and scanning it locally. The whole-file scan was the shape this document first imagined and is not what shipped: asking the index a question keeps the bandwidth proportional to the answer instead of to the catalog, and it puts one discovery ladder ([§2.1](#optional)) under every consumer instead of two. Index is the enabling layer for M2.10; `vibe search` is the headline consumer of this PROP.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-index/PROP-005#INT-SEARCH>

## Mirrors, overrides and git sources {#mirrors-and-overrides}

[p37] A *mirror* is another address for the same registry, tried first for availability and verified against the same fingerprints; a mirror that serves different bytes for a known version is refused, not trusted. Mirrors never appear in the [lock file](../glossary/index.xml#lock-file): the canonical address is what gets recorded, so switching mirrors changes nothing for your teammates.

> [p38] Mirror integrity verification is **mandatory**, not optional. A mirror whose `content_hash` for `(kind, name, version)` differs from the lockfile pin fails the install with an actionable error. This closes the supply-chain hole where a hijacked mirror could substitute content.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#MIRROR-INTEGRITY-MANDATORY>

[p39] An *override* replaces one package with a copy from elsewhere, for a hotfix or a patch waiting upstream; it short-circuits the registry walk for that one coordinate and is marked as such in the lock file, so nobody mistakes it for a published version.

> [p40] **Decision.** `[[override]]` bypasses the registry layer for a named pkgref:
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#OVERRIDE-SHORT-CIRCUIT>

[p41] An override relaxes nothing: the copy's fingerprint is still pinned in the lock file and verified on every install, and the entry carries `overridden = true`.

> [p42] The resolver short-circuits: it does not consult `[[registry]]` for this pkgref at all; it fetches directly from the given URL at the given ref. Content hash is still pinned in the lockfile and verified on each install — an override does not relax integrity. The lockfile records `overridden = true` on that entry. A `vibe list --overrides` flag is specified here and **not shipped** — the lockfile field is the only surface today.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#OVERRIDE-SEMANTICS>

[p43] A dependency may also point straight at a git repository, at a tag, a commit or a branch. A tag and a commit are pinned; a branch is re-walked on update, and the lock file records the commit that was actually installed.

> [p44] Mutable branch. Lockfile records the resolved commit at install time; subsequent `vibe update` re-walks branch HEAD. **Mutable** — see "Mutability and `vibe update`" below.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#ROW-GS-BRANCH-MEANING>

> [p45] **Decision.** A dependency may be declared as a first-class git-source in `[requires.packages]` — fetching the package from an arbitrary git repository instead of resolving it through `[[registry]]`. This is the vibevm analogue of Cargo's `[dependencies] foo = { git = "..." }`, npm's `"foo": "git+https://..."`, Poetry's `foo = { git = "..." }`, Bundler's `gem 'foo', git: '...'`, Go modules' baseline behaviour. The use cases are:
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#GIT-SOURCE-DECL>

[p46] A [git source](../glossary/index.xml#git-source) is written as an inline table on the requirement, with a `git` address and exactly one of `tag`, `rev` or `branch`; none or two is refused, because guessing a default branch is not acceptable on a surface that decides what code enters your project. The source's `auth` is declared on the source itself, never borrowed from a registry on the same host. When the repository arrives, vibe reads the package's own manifest and refuses one whose kind and name differ from what you required.

> [p47] **Wire form.** `[requires.packages]` becomes a TOML table whose values are either a version-constraint **string** (registry-resolved, the M1.13 shape) or an inline-table (registry-resolved with options, or git-source). The legacy array-of-strings shape (`packages = ["flow:wal@^0.3"]`) parses transparently into table-form on read; on round-trip the manifest writes table-form.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#GS-WIRE-FORM>

> [p48] **Exactly one** of `tag` / `rev` / `branch` must be present in a git-source declaration. Zero is rejected at parse time with `MissingRef`. Two or more rejected with `ConflictingRefs`. There is no "default branch HEAD" fall-back — too magical for a security-sensitive surface; explicit > implicit.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#GS-EXACTLY-ONE-REF>

> [p49] **Auth.** Per-source `auth` is **explicit, not host-derived**. The resolver does not look at `[[registry]] auth` for the same host and apply it transitively to a git-source pointing at that host — too magical, creates implicit ordering dependencies between sections of the manifest. If a project has multiple packages from the same private host, the operator can either:
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#GS-AUTH-EXPLICIT>

> [p50] The pkgref `<kind>:<name>` is read from the package's `vibe-package.toml` `[package]` section on the resolved git ref (same path as registry-resolved manifest fetch via `git archive`). The resolver verifies that the `(kind, name)` declared in `[requires.packages]` matches what the repo actually carries; mismatch = `PackageIdentityMismatch`. This means a malicious git-source cannot impersonate `flow:wal` if its `vibe-package.toml` declares it as `feat:auth`.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#GS-IDENTITY-VERIFICATION>

[p51] The source of a requirement is decided in a fixed order: an override first, then a git source declared on the requirement, then the registry walk. A branch is followed only by `vibe update`; `vibe install` keeps the commit the lock recorded.

> [p52] **Resolution order.** When the resolver looks up a pkgref, the source is decided in this order:
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#GS-RESOLUTION-ORDER>

> [p53] **Mutability and `vibe update`.** Tags and revs are immutable by definition; force-push is detected via content-hash. Branches are explicitly mutable: `vibe install` against a branch resolves to the current branch HEAD and pins that commit in the lockfile. `vibe update` re-walks each branch-declared git-source, and if HEAD has moved, re-resolves and re-locks. `vibe install` (no flag) **does not** chase a branch's HEAD on subsequent runs — the lockfile's `resolved_commit` is authoritative until `update` is called. This matches Cargo's behaviour (`cargo build` does not bump branch deps; `cargo update` does).
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#GS-MUTABILITY>

## Authentication {#authentication}

[p54] A public registry needs no credentials, and vibe sends none: it silences git's credential helpers so an install in a script never hangs on a password prompt. A private registry declares its regime in the manifest: a token read from an environment variable, the system's credential helper, or SSH keys. The token comes from your environment and never lands in a file vibe writes.

> [p55] **Token never lands on disk via vibevm.** The token comes from the operator's environment. Vibe reads it, builds the credentialed URL in memory, hands it to the spawned git process, and discards. The lockfile's `source_url` field always carries the **canonical** URL (no embedded credentials) — symmetric with the `[[mirror]]` invariant in §2.3. Token discipline (PROP-000 §20) applies: the value is treated as surface-secret; it does not appear in any vibevm-emitted output. Modern git (≥2.31) auto-redacts passwords from its own stderr, so even on errors the token is not echoed.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#TOKEN-NEVER-ON-DISK>

## Packages on this machine {#local-sources}

[p56] A vibe built from a source checkout treats that checkout's in-tree packages as an [embedded registry](../glossary/index.xml#embedded-registry): on by default for such a build, off for a distributed one, and off for one command with `--no-default-registry`. Version enumeration still unions the embedded and the declared registries, so a newer published version is seen; an override or a git source on a requirement stays above the embedded registry. `--embedded-short-circuit` stops enumeration at the embedded registry for the packages it serves, so a fully embedded graph resolves with no network at all.

> [p57] Its **default follows the install origin**: on for `origin = "external"`, off for
>   a distribution.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-030#KNOB-DEFAULT>

> [p58] `--no-default-registry` (env `VIBE_NO_DEFAULT_REGISTRY=1`)
>   suppresses the embedded registry entirely for a command.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-030#KNOB-SUPPRESS>

> [p59] But **version enumeration** (the candidate set the solver picks
>   from) **unions across embedded *and* declared** by default, so the solver can
>   see a newer published version even for a package the embedded registry already
>   carries.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-030#ENUM-UNION>

> [p60] Resolution keeps PROP-002's explicit-source short-circuits **above** the
> embedded registry — an explicit per-dependency source or pin is always
> deliberate and always wins:
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-030#EXPLICIT-ABOVE>

> [p61] **`--embedded-short-circuit`** — keep the declared walk available, but
>   short-circuit version enumeration at the embedded registry for any coordinate
>   it serves: the network is reached **only** for packages the embedded registry
>   lacks. A fully-embedded dependency graph resolves with zero network access
>   (no enumeration round-trip, no credential prompt), while a genuinely missing
>   package is still fetched from the network. Implies embedded-first precedence;
>   mutually exclusive with `--no-prefer-embedded`.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-030#FLAG-EMBEDDED-SHORT-CIRCUIT>

[p62] A package resolved this way is recorded with `source_kind = "embedded"`, and `vibe check` warns that such a lock is not portable. In `--frozen` and other non-interactive runs the embedded registry is off, so a lock that only works on one developer's machine cannot pass in a build server.

> [p63] A package resolved from the embedded registry records `source_kind = "embedded"`
> in `vibe.lock` (a [PROP-002](PROP-002-decentralized-registry.xml) `SourceKind`
> variant beside `registry` / `git` / `override` / `path`). Its `source_url` is the
> `file://` path into `<source_path>/packages`.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-030#LOCK-EMBEDDED>

> [p64] **Warn.** `vibe check` **warns** (does not fail) when the lock carries any
>   `source_kind = "embedded"` entry: "this lockfile depends on the embedded
>   registry of a source install and is not portable; publish or vendor these
>   packages before sharing the lock." A `source_kind = "local"` entry is
>   portable and does NOT warn.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-030#GUARD-WARN>

> [p65] **CI-off.** In `--frozen` (and any non-interactive CI resolution), the
>   vibe-embedded registry is **disabled by default** — CI must resolve from
>   declared registries (and, since §3.3, project-local), so a machine-local lock
>   cannot silently pass there. Project-local is NOT suppressed by this gate — it
>   is per-project and portable.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-030#GUARD-CI-OFF>

[p66] A project that carries a `packages/` folder beside its manifest gets that folder opened as a local registry with no declaration at all. Packages resolved from it record `source_kind = "local"`, which is portable, because every checkout resolves the same folder to the same content. `--no-prefer-local` bypasses the folder for one command.

> [p67] REQ. A project carrying `<project_root>/packages/` (where `project_root` is
>   the directory holding the project's `vibe.toml`, resolved by
>   `resolve_project_root`) gets that directory auto-opened as a `LocalRegistry`
>   and composed into the local-registry family alongside the vibe-embedded
>   registry. No `[[registry]]` block, no `--registry <path>`, no
>   `~/.vibe/registry.toml` machine entry needed.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-030#LOCAL-AUTO-OPEN>

> [p68] REQ. A package resolved from project-local records `source_kind = "local"`
>   in `vibe.lock` (§4) — distinct from `embedded`. Unlike `embedded`, it is
>   **portable** and the reproducibility guard (§5) does NOT warn on it: every
>   checkout of the project resolves the same `packages/` to the same content.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-030#LOCAL-SOURCE-KIND>

> [p69] REQ. `--no-prefer-local` suppresses project-packages discovery for one
>   command (use when a project's `packages/` is stale, broken, or deliberately
>   bypassed). It does NOT suppress vibe-embedded — `--no-default-registry`
>   remains the knob for that. `--prefer-local` is the explicit affirmation of
>   the default (project-local wins the local family); mutually exclusive with
>   `--no-prefer-local`.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-030#LOCAL-NO-PREFER-FLAG>

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

[p70] A vibe built from a source checkout treats that checkout's in-tree packages as an ambient registry, consulted first: a developer of vibe installs the packages being developed without publishing them. A distributed vibe has no such registry.

> [p71] This PROP makes the in-tree `packages/` of a source-installed `vibe` an
> **ambient default registry** — resolved automatically, with zero configuration
> in the consuming project.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-030#AMBIENT-DEFAULT>

[p72] The source repository of vibe itself is mirrored on two hosts, but that is a different thing from the package registry: the mirrors carry the program's source, the registry carries packages, and the credentials for the two are never shared.

> [p73] This PROP governs the **source repository**; it is orthogonal to the **package registry**, and the two must not be conflated.
>
> <spec://org.vibevm.core/vibevm/common/PROP-016#ORTHOGONALITY-LAW>

[p74] vibe drives registries through the `git` program on your `PATH` and checks for it before it starts; `VIBE_GIT_BINARY` points it at another copy. A registry clone older than one hour is refreshed before an install, a younger one is used as is, and `vibe registry sync` refreshes regardless of age.

> [p75] **Runtime dependency on `git` in `PATH`.** Acceptable: our target
>   audience is developers who already have git installed. We perform a
>   preflight `git --version` check and emit an actionable error (with
>   a pointer to `https://git-scm.com/downloads`) if it is missing.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-001#RISK-GIT-IN-PATH>

> [p76] **Resolved — shipped as proposed.** The `VIBE_GIT_BINARY`
>   PATH override lives in `git_backend/shell.rs` and its comment cites §6 of this
>   PROP; the env-var form was chosen over a CLI flag exactly to keep the CLI
>   surface stable.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-001#OPEN-GIT-BINARY-PATH>

> [p77] **Decision:** the default freshness TTL is **1 hour**, checked against
> `meta.toml.last_pulled_at`. An install whose registry cache is older
> than the TTL triggers an implicit `update`. An install whose cache is
> younger skips the pull. `vibe registry sync` forces an update
> regardless of age.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-001#FRESHNESS-TTL>

[p78] A file the resolver needs, a manifest or a redirect stub, is read straight from the host over HTTPS when the host is GitHub or GitVerse, with a credential sent as a header and never in the address; a miss settles the question only for a tag or a commit, whose content is fixed, and on a branch git is asked next. A host the table does not name never enters this path.

> [p79] Before a single file is asked of git, the backend reads it over HTTPS from the host's own raw endpoint when the host is one it knows — `github.com` through `raw.githubusercontent.com`, `gitverse.ru` through its contents API — with any credential sent only as a bearer header, never in the address. A hit is the file. A miss is authoritative only for a tag or a commit SHA, whose content is fixed; on a branch, or for a manifest, the read falls through to git, whose answer stands as it always did. A host the table does not name never enters this path.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#RAW-READ-FAST-PATH>

[p80] A read the host refuses for the moment, with a rate limit or a server error, is retried a few times with a short pause before the reader falls back to git; a plain «not found» is never retried.

> [p81] A raw read that the host refuses with `429` or a `5xx` is retried a small, bounded number of times with a short pause, honouring `Retry-After` within that bound, before the read falls through to git as any other unexpected answer does. A `404` is never retried: on a tag or a commit it is authoritative, and on a branch it is what git will be asked about next.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#RAW-READ-BACKOFF>

