# Use a private registry {#root}

@status:doc/work @audience:user

[p01] A company can keep its packages in its own place and still use vibe unchanged. This page points a project or a whole machine at that place, with or without a search catalogue beside it, and keeps the public one as a fallback.

[p02]
```prompt
Add the private registry named acme at git@github.com:acme-specs as the first registry of the VibeVM project in the current folder, authenticated over SSH, keep the public vibespecs registry as a fallback, and test that both are reachable.
```

- needs: the vibevm skill installed for your agent; an SSH key that the private host accepts, loaded in the agent; a project with `vibe.toml`

outcome: `vibe registry list` shows `acme` first and `vibespecs` second; `vibe registry test` reports both reachable

- assert: `vibe registry list --quiet`
- assert: `vibe registry test`

## What happens {#what-happens}

[p03] The agent runs `vibe registry add acme git@github.com:acme-specs --auth ssh --position primary`, which writes a new [registry](../glossary/index.xml#registry) block at the top of the [manifest](../glossary/index.xml#manifest)'s list. From now on every resolution asks `acme` first and the public registry second, and a package that exists in both comes from `acme`. `vibe registry test` probes each registry for reachability and authentication without fetching anything.

> [p04] 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>

## By hand {#by-hand}

[p05] 1. Add the registry to the project. The address is the organisation root, not a package repository:

[p06]
```sh
vibe registry add acme git@github.com:acme-specs --path hello-vibe --position primary
```

```output
  → Added `[[registry]]` `acme` as primary → git@github.com:acme-specs on host github.com (adapter: github)

vibe registry add: `acme` registered (1 total registry).
```

[p07] 2. Choose the authentication regime with `--auth`. The values are `none` for public read, `ssh` for keys and `credential-helper` for the system's git helper. `token-env` reads a token from an environment variable named after the host.

> [p08] **Decision.** Each `[[registry]]` declares its authentication regime via an `auth` field. Four variants:
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#AUTH-REGIMES>

[p09] 3. Probe with `vibe registry test`: it reports, per registry, whether the organisation answered and which authentication it accepted, without fetching anything.

## For every project on a machine {#machine-wide}

[p10] Put the same registry block into `~/.vibe/registry.toml`. It is merged after each project's own list, so projects that name their registries are unaffected and projects that do not inherit the machine's. A project that names no registry, as `vibe init` creates them today, uses the machine's list, which is how a company's default becomes every new project's default.

> [p11] **Decision (corrected 2026-08-20 — the original text named the wrong file AND the wrong directory, and both were already settled in code).** A **user-level default registry configuration** lives in its own file, `~/.vibe/registry.toml`, beside — not inside — the general user settings at `~/.vibe/config.toml`. It supplies registry configuration when no project does, and seeds a new one:
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-010#USER-LEVEL-REGISTRIES>

## Delegating a package to another registry {#redirects}

[p12] A registry may point at a package that lives elsewhere. Instead of the package, the repository named after it carries one file, `vibe-redirect.toml`, with a `[redirect]` table: `target_url` names the repository where the package really is, and `ref_policy` says which tag to fetch there, the same tag by default or one `pinned_ref` for every version. A consumer installing the package sees no difference; the resolver follows the pointer once, never a chain, and records the stub's address in the [lock file](../glossary/index.xml#lock-file) as `via_redirect`.

> [p13] **Decision.** A registry org may host a **stub repo** for a package — a normal `<org>/<kind>-<name>` repository whose content is **not** the package itself but a single file pointing at an external git repository where the package actually lives. The resolver, when fetching the package manifest, transparently follows the pointer; consumers `vibe install <pkgref>` see no difference from a direct registry-resolved package. The use case is **delegation**: an org owner wants the package to live in their namespace (so consumers find it via the org's `[[registry]]` walk without knowing about the external author) but offload the development, the PR queue, and the hosting platform's permission management to a different team or person who already has their own repo.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#REDIRECT-STUB>

> [p14] **Marker file.** A stub repo carries `vibe-redirect.toml` at its root **instead of** `vibe-package.toml`. Both files in the same repo at the same ref is rejected at parse as `AmbiguousStub`.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#REDIRECT-MARKER-FILE>

> [p15] **Hop limit: 1.** If `target_url`'s content-root is itself a stub (carries `vibe-redirect.toml`), reject with `RedirectChainNotAllowed`. There is no chain-following — stubs are flat indirection, not a redirect graph.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#RD-STEP-HOP-LIMIT>

[p16] The versions a consumer can see are the tags of the stub, not of the target: the registry's owner admits each version by tagging the stub. `vibe registry redirect` creates a stub, `redirect-sync` copies the target's tags into it after confirmation, and `redirect-update` changes the pointer. When a stub's target changes, because the maintainer moved to another host, a consumer accepts the switch with `--trust-redirect`; vibe never follows a changed target silently.

> [p17] **Tag visibility.** `list_versions(stub_url)` returns the tags of the **stub** repo, not the target. The org owner controls which versions surface in their namespace by managing stub tags — which is exactly the gating mechanism a registry already has via `vibe registry publish`. Adding a new version to the namespace = `git tag v<ver> && git push origin v<ver>` against the stub repo (or `vibe registry redirect-sync <pkgref>` if it auto-mirrors target tags — see "Sync helper" below). The stub itself need contain no actual code — only `vibe-redirect.toml`, optionally a `README.md` for humans browsing the repo.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#REDIRECT-TAG-VISIBILITY>

> [p18] **Publish helper (`vibe registry redirect`).** A new CLI command:
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#REDIRECT-CLI>

> [p19] **`--trust-redirect`** flag (parallel to `--trust-mirror` from §2.1) lets an operator accept a deliberate target switch — e.g. when the external maintainer migrates their hosting from GitLab to Forgejo. Never silent; always operator-initiated.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#RD-TRUST-FLAG>

> [p20] **Sync helper (`vibe registry redirect-sync`).** Org owner can run `vibe registry redirect-sync <pkgref>` to copy target-side tags into the stub repo (with operator confirmation per tag, or `--all` for batch). This is opt-in convenience tooling; the stub repo is just a normal git repo and tags can equally be managed by hand or CI.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#REDIRECT-SYNC-HELPER>

> [p21] **Lockfile shape.** A new `via_redirect` field per `[[package]]` records the stub URL when a redirect was followed. `null` (or absent) for non-redirected packages.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#REDIRECT-LOCKFILE-FIELD>

## Tokens {#tokens}

[p22] A token never lands in a file vibe writes and never appears in its output. With `--auth token-env` vibe reads it from `VIBEVM_REGISTRY_TOKEN_<HOST>` at run time and sends it over an encrypted connection only. Keep the variable in your shell profile or your CI secret [store](../glossary/index.xml#store), not in the repository.

> [p23] **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>

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

[p24] A private registry without an [index](../glossary/index.xml#index-registry) still works; searches skip it and installs clone what they need. Add an index repository beside the packages and point `VIBEVM_INDEX_URL_ACME` at it to make searches and cold installs fast.

> [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] A [mirror](../glossary/index.xml#mirror) is not a second registry. Use `vibe registry set-mirror` for an alternative address of the same packages, verified against the same [fingerprints](../glossary/index.xml#fingerprint); use `registry add` for a different source of packages.

> [p27] A `[[mirror]]` is an **availability copy of the same source** — same naming, same identity, same `content_hash`. The mirror walk falls through on **any availability failure** (`NetworkUnreachable`, `AuthFailed` on the mirror, server error, `content_hash` mismatch). `RepoNotFound` from a mirror bubbles up to the registry-walk layer (same policy as if the canonical primary had said `UnknownPackage`), because absence-of-package is a registry-level fact, not a mirror-level one.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#MIRROR-WALK-SEMANTICS>

[p28] In a script that must notice a private registry being down, pass `--auth-required` to `vibe install`: an authentication error then stops the install instead of walking on to the public fallback.

