Реестры и индекс
01Пакеты публикуются в место, которое vibe умеет читать: по умолчанию это публичная организация на GitHub, по репозиторию на пакет. Проект перечисляет такие места в том порядке, в каком им доверяет. Каталог рядом с каждым из них отвечает на поиск, ничего не клонируя.
Что такое реестр
03Реестр — не сервер, который запускает vibe. Это хостинг-организация вроде https://github.com/vibespecs, где каждый пакет — собственный git-репозиторий, названный по координате пакета. Опубликовать — значит запушить репозиторий и поставить тег версии; установить — значит клонировать по тегу. Кому можно публиковать, решают права самого хостинга, так что у vibe нет собственных аккаунтов.
04 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.
05Проект объявляет свои реестры в манифесте упорядоченным списком. У каждой записи есть локальное имя, корневой адрес организации и соглашение об именовании, которое превращает координату в имя репозитория. Когда просят пакет, vibe идёт по списку по порядку, и первый реестр, где есть подходящая версия, выигрывает.
06 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.
07Список — массив в порядке приоритета; рядом с ним зеркало — второй адрес того же реестра, а переопределение обходит обход списка для одной координаты. url записи — корень организации, никогда не репозиторий пакета, и это обычный git-адрес: https://, ssh://, git@host: или file://, без сокращений под какой-либо хост. Его naming говорит, как координата становится именем репозитория; умолчание соединяет группу и имя точкой.
08[[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.
09
url — organization root URL, not a package repo URL. A registry is a hosting-org; packages are children of it.
10 URL syntax is just git URL —git@host:…,ssh://,https://,file://. Nogithub:/gitverse:shorthands. New hosts "just work" as long asgitspeaks to them.
11naming— 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, 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.
12К следующему реестру обход переходит, только когда один ответил, что пакета у него нет. Сбой соединения, ошибка сервера или испорченный манифест останавливают установку с этой ошибкой, потому что о простое или опечатке вы хотите знать. В реестре, объявленном публичным, требование учётных данных считается за «здесь нет», и обход продолжается; в реестре, который объявил режим аутентификации, это настоящий сбой.
13 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 onUnknownPackageonly: 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.
14auth-aware 401 classification (§2.2.1). Onauth = "none"a 401 / 403 response is anUnknownPackagesignal, 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. Onauth = "token-env"or"credential-helper"a 401 is a realAuthFailedand halts: the registry was declared as authenticated, the credentials presented were rejected, this is information the operator must see.
15Запись можно выключить, не удаляя: enabled = false заставляет каждую команду пропускать этот реестр, пока вы не включите его обратно.
16 Decision. Every[[registry]]carries anenabledflag, defaulttrue. Settingenabled = falseswitches 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 defaulttrueis skipped on serialize, so only an explicitenabled = falseappears in a writtenvibe.toml. The flag applies uniformly to a projectvibe.tomland the machine-global~/.vibe/registry.toml.
17Машина может добавить собственные реестры в ~/.vibe/registry.toml; они подмешиваются после проектных, и список проекта всегда сильнее машинного. Так компания нацеливает каждый проект на ноутбуке на свой приватный реестр, не правя каждый проект. Файл несёт те же секции [[registry]], [[mirror]] и [[override]], что и манифест проекта, для любого реестра, удалённого или локального; имя, объявленное в обоих местах, принадлежит проекту.
18 Project-level[[registry]]always overrides the user-level default — the same precedence theUserConfig[env]layer already follows (the project / live value wins).
19 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 projectvibe.toml— any registry, not only local ones: a remotehttps:///ssh:///git@org (withauth) is merged and searched exactly like afile:/// path repo. A common motivation is keeping machine-local registries (afile://checkout, a path repo) out of a team-sharedvibe.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.)
20 Merge — project first, dedupe by name. The effective registry list is the project's[[registry]]entries followed by the global file's, with anamecollision 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 bypkgref(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 sharedvibe.tomlstays authoritative for the team while each machine supplements it with its own local repositories.
21Набор доверия по умолчанию, который называет спецификация, — ровно два корня: https://github.com/vibespecs и https://gitverse.ru/vibespecs. Проект, который сегодня создаёт vibe init, не несёт блока реестров вовсе, так что перед первой установкой добавьте его через vibe registry add или дайте машинному файлу его подставить. Любому другому реестру доверяют только потому, что вы его добавили.
22 The default trust set is exactly two roots —https://github.com/vibespecsandhttps://gitverse.ru/vibespecs— trusted by default as the registriesvibe initwrites. Every other registry is trusted only by the user's own act of adding it to their configuration (owner ruling, 2026-08-13).
23 Default in new projects.vibe initwrites the default registry URL (DEFAULT_REGISTRY_URLinvibe_core::manifest) into every newvibe.toml's[[registry]]entry unless the operator passes--no-registryor overrides with--registry-url <URL>/--registry-ref <REF>.
Индекс
24Клонировать репозиторий, чтобы узнать, что в нём, — медленно, а перечислить организацию для поиска без аккаунта невозможно. Поэтому реестр может держать индекс: отдельный репозиторий рядом с пакетами, где для каждой опубликованной версии записаны сводка манифеста и отпечаток содержимого. vibe search читает индекс; свежая установка читает его, чтобы пропустить круг клонов.
25
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.
26Индекс — кэш, никогда не истина. Если он расходится с репозиторием пакета, побеждает репозиторий, а пакет, разрешённый через индекс, всё равно сверяется с отпечатком содержимого, когда приходит. Реестр без индекса работает ровно как раньше, только медленнее; отсутствие индекса — не ошибка.
27 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.
28 This matters because it disambiguates the failure mode: if the index disagrees with reality, reality wins.
29Адрес индекса выводится из адреса реестра, и его можно переопределить для каждого реестра переменной окружения, названной по имени реестра, VIBEVM_INDEX_URL_<NAME>; буквальное значение none выключает обращения к индексу для этого реестра.
30 The environment variableVIBEVM_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 beatsindex_urlbeats the default, andnoneat either explicit rung disables the index. The name normalization (ASCII alphanumerics upper-cased, the rest to_``) is unchanged.
31Запись реестра может закрепить свой индекс через index_url; без него публичная организация на GitHub отображается в свой репозиторий index на хосте сырого содержимого, а любой другой хост — в <registry-url>/index. У пробы индекса три исхода: найден, отсутствует, отказано. Только «отсутствует» тихо проваливается к живому перечислению, потому что индекса никто не обещал; индекс, который есть, но не читается, — это сообщение об ошибке. vibe search задаёт вопрос каждому настроенному индексу напрямую, а не скачивает весь каталог.
32 Configurable but defaulted. A[[registry]]block pins a custom index location — the key exists inRegistrySection(one type serving both the projectvibe.tomland the machine-global~/.vibe/registry.toml, so the columns share one vocabulary), and this exact block parses (pinned by testprop005_index_url_example_parses, which carries it verbatim):
33 The bottom rung is host-aware. Canonical publichttps://github.com/<org>maps tohttps://raw.githubusercontent.com/<org>/index/<registry-ref>; other hosts retain<registry-url>/index. This makes both fresh and already- seeded GitHub configurations whoseindex_urlfield is absent consume the static repository rather than its HTML page. The full ladder remains env override → manifest key → host-aware default; exactnoneon either explicit rung disables lookup. Lookalike hosts, nested paths, userinfo, unsafe owners/refs, queries and fragments are never rewritten.
34 A probe answersfound,absent, orrefused, 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 — answersrefused, carrying the offered epochs, this build's epoch, a recipe, and whatever the document said inmin_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 forbids: a break that announces itself is normal life, a riddle is what strands users.
35 404 / connect-failure on the index → silent fallback to livels-remote: no error message, because the operator never promised an index. This half is built, and it is theabsentoutcome of##A-PROBE-HAS-THREE-OUTCOMES-NOT-TWO— the other two outcomes are never silent.
36 Walks every configured registry's index through the same client the resolver uses — probe, then query — rather than downloadingprimary.jsonl.gzand 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) under every consumer instead of two. Index is the enabling layer for M2.10;vibe searchis the headline consumer of this PROP.
Зеркала, переопределения и git-источники
37Зеркало — другой адрес того же реестра, к которому обращаются первым ради доступности и который сверяют по тем же отпечаткам; зеркало, отдающее под известной версией другие байты, отвергается, а не получает доверие. Зеркала никогда не попадают в лок-файл: записывается канонический адрес, так что смена зеркала ничего не меняет для ваших коллег.
38 Mirror integrity verification is mandatory, not optional. A mirror whosecontent_hashfor(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.
39Переопределение заменяет один пакет копией из другого места — ради хотфикса или патча, который ждёт своей очереди наверху; оно обходит обход реестров для этой одной координаты и помечено в лок-файле, чтобы никто не принял его за опубликованную версию.
40
Decision. [[override]] bypasses the registry layer for a named pkgref:
41Переопределение ничего не ослабляет: отпечаток копии по-прежнему закреплён в лок-файле и сверяется при каждой установке, а запись несёт overridden = true.
42 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 recordsoverridden = trueon that entry. Avibe list --overridesflag is specified here and not shipped — the lockfile field is the only surface today.
43Зависимость может указывать и прямо на git-репозиторий — на тег, коммит или ветку. Тег и коммит закреплены; ветка при обновлении проходится заново, а лок-файл записывает коммит, который был установлен на самом деле.
44 Mutable branch. Lockfile records the resolved commit at install time; subsequentvibe updatere-walks branch HEAD. Mutable — see "Mutability andvibe update" below.
45 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'sfoo = { git = "..." }, Bundler'sgem 'foo', git: '...', Go modules' baseline behaviour. The use cases are:
46Git-источник записывается инлайновой таблицей на требовании: адрес git и ровно одно из tag, rev или branch; ни одного или два — отказ, потому что угадывать ветку по умолчанию недопустимо там, где решается, какой код входит в ваш проект. auth источника объявляется на самом источнике и никогда не заимствуется у реестра на том же хосте. Когда репозиторий приходит, vibe читает собственный манифест пакета и отвергает тот, чьи вид и имя расходятся с тем, что вы потребовали.
47 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.
48 Exactly one oftag/rev/branchmust be present in a git-source declaration. Zero is rejected at parse time withMissingRef. Two or more rejected withConflictingRefs. There is no "default branch HEAD" fall-back — too magical for a security-sensitive surface; explicit > implicit.
49 Auth. Per-sourceauthis explicit, not host-derived. The resolver does not look at[[registry]] authfor 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:
50 The pkgref<kind>:<name>is read from the package'svibe-package.toml[package]section on the resolved git ref (same path as registry-resolved manifest fetch viagit 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 impersonateflow:walif itsvibe-package.tomldeclares it asfeat:auth.
51Источник требования выбирается в фиксированном порядке: сначала переопределение, затем git-источник, объявленный на требовании, затем обход реестров. За веткой идёт только vibe update; vibe install держит коммит, который записал лок.
52 Resolution order. When the resolver looks up a pkgref, the source is decided in this order:
53 Mutability andvibe update. Tags and revs are immutable by definition; force-push is detected via content-hash. Branches are explicitly mutable:vibe installagainst a branch resolves to the current branch HEAD and pins that commit in the lockfile.vibe updatere-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'sresolved_commitis authoritative untilupdateis called. This matches Cargo's behaviour (cargo builddoes not bump branch deps;cargo updatedoes).
Аутентификация
54Публичному реестру учётные данные не нужны, и vibe их не посылает: он глушит помощники учётных данных git, чтобы установка в скрипте никогда не зависла на запросе пароля. Приватный реестр объявляет свой режим в манифесте: токен из переменной окружения, системный помощник учётных данных или SSH-ключи. Токен приходит из вашего окружения и никогда не попадает в файл, который пишет vibe.
55 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'ssource_urlfield 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.
Пакеты на этой машине
56vibe, собранный из чекаута исходников, считает пакеты из дерева этого чекаута встроенным реестром: у такой сборки он включён по умолчанию, у распространяемой выключен, а на одну команду выключается через --no-default-registry. Перечисление версий всё равно объединяет встроенный и объявленные реестры, так что более новая опубликованная версия видна; переопределение или git-источник на требовании остаются выше встроенного реестра. --embedded-short-circuit останавливает перечисление на встроенном реестре для пакетов, которые он отдаёт, так что полностью встроенный граф разрешается вообще без сети.
57
Its default follows the install origin: on for origin = "external", off for
a distribution.
58--no-default-registry(envVIBE_NO_DEFAULT_REGISTRY=1) suppresses the embedded registry entirely for a command.
59 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.
60 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:
61--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.
62Пакет, разрешённый таким путём, записывается с source_kind = "embedded", и vibe check предупреждает, что такой лок непереносим. В --frozen и других неинтерактивных запусках встроенный реестр выключен, так что лок, который работает только на машине одного разработчика, не пройдёт на сборочном сервере.
63 A package resolved from the embedded registry recordssource_kind = "embedded"invibe.lock(a PROP-002SourceKindvariant besideregistry/git/override/path). Itssource_urlis thefile://path into<source_path>/packages.
64 Warn.vibe checkwarns (does not fail) when the lock carries anysource_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." Asource_kind = "local"entry is portable and does NOT warn.
65
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.
66Проект, у которого рядом с манифестом лежит папка packages/, получает эту папку открытой как локальный реестр вообще без объявления. Пакеты, разрешённые из неё, записываются с source_kind = "local", и это переносимо, потому что каждый чекаут разрешает ту же папку в то же содержимое. --no-prefer-local обходит папку на одну команду.
67 REQ. A project carrying<project_root>/packages/(whereproject_rootis the directory holding the project'svibe.toml, resolved byresolve_project_root) gets that directory auto-opened as aLocalRegistryand composed into the local-registry family alongside the vibe-embedded registry. No[[registry]]block, no--registry <path>, no~/.vibe/registry.tomlmachine entry needed.
68 REQ. A package resolved from project-local recordssource_kind = "local"invibe.lock(§4) — distinct fromembedded. Unlikeembedded, it is portable and the reproducibility guard (§5) does NOT warn on it: every checkout of the project resolves the samepackages/to the same content.
69 REQ.--no-prefer-localsuppresses project-packages discovery for one command (use when a project'spackages/is stale, broken, or deliberately bypassed). It does NOT suppress vibe-embedded —--no-default-registryremains the knob for that.--prefer-localis the explicit affirmation of the default (project-local wins the local family); mutually exclusive with--no-prefer-local.
Особые случаи и правила
70vibe, собранный из чекаута исходников, считает пакеты из дерева этого чекаута окружающим реестром, к которому обращается первым: разработчик vibe ставит разрабатываемые пакеты, не публикуя их. У распространяемого vibe такого реестра нет.
71 This PROP makes the in-treepackages/of a source-installedvibean ambient default registry — resolved automatically, with zero configuration in the consuming project.
72Репозиторий с исходниками самого vibe зеркалится на двух хостах, но это другое дело, чем реестр пакетов: зеркала несут исходники программы, реестр несёт пакеты, и учётные данные для них никогда не делятся.
73 This PROP governs the source repository; it is orthogonal to the package registry, and the two must not be conflated.
74С реестрами vibe работает через программу git в вашем PATH и проверяет её наличие до начала; VIBE_GIT_BINARY указывает ему на другую копию. Клон реестра старше часа обновляется перед установкой, моложе — берётся как есть, а vibe registry sync обновляет независимо от возраста.
75 Runtime dependency ongitinPATH. Acceptable: our target audience is developers who already have git installed. We perform a preflightgit --versioncheck and emit an actionable error (with a pointer tohttps://git-scm.com/downloads) if it is missing.
76 Resolved — shipped as proposed. TheVIBE_GIT_BINARYPATH override lives ingit_backend/shell.rsand its comment cites §6 of this PROP; the env-var form was chosen over a CLI flag exactly to keep the CLI surface stable.
77 Decision: the default freshness TTL is 1 hour, checked againstmeta.toml.last_pulled_at. An install whose registry cache is older than the TTL triggers an implicitupdate. An install whose cache is younger skips the pull.vibe registry syncforces an update regardless of age.
78Файл, который нужен резолверу, манифест или заглушка перенаправления, читается прямо с хоста по HTTPS, когда хост — GitHub или GitVerse, с учётными данными в заголовке и никогда в адресе; промах решает вопрос только для тега или коммита, чьё содержимое неизменно, а на ветке следующим спрашивают git. Хост, которого нет в таблице, на этот путь не попадает никогда.
79 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.comthroughraw.githubusercontent.com,gitverse.ruthrough 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.
80Чтение, которое хост отверг на минуту, из-за лимита частоты или ошибки сервера, повторяется несколько раз с короткой паузой, прежде чем читатель откатится к git; обычное «не найдено» не повторяется никогда.
81 A raw read that the host refuses with429or a5xxis retried a small, bounded number of times with a short pause, honouringRetry-Afterwithin that bound, before the read falls through to git as any other unexpected answer does. A404is never retried: on a tag or a commit it is authoritative, and on a branch it is what git will be asked about next.