Publish a package
01You wrote a package and want others to install it. This page publishes it to the place your projects install from, as its own repository, tags the version, and checks that a fresh project can install it.
Publish the in-tree package org.acme/notes of this project, the slot vibevm/vibepacks/org.acme/notes/v0.1.0, to the first registry of this project, using the publish token already in my environment, then create a scratch project elsewhere and install the published package into it to prove it works. Ask me before the actual push.
the vibevm skill installed for your agent; a publish token for the registry's host in the environment or under ~/.vibe/; the package's vibe.toml complete, with a version that has not been published before
a repository named after the package's coordinate exists in the registry organisation with a tag for the version; a scratch project installs it and vibe list shows the version
vibe registry publish vibevm/vibepacks/org.acme/notes/v0.1.0 --dry-run
What happens
03The agent runs vibe registry publish vibevm/vibepacks/org.acme/notes/v0.1.0 --dry-run first and shows you what would happen: the registry, the repository name derived from the coordinate, the version tag. After your yes it runs the real command: vibe creates the repository in the registry organisation through the host's API if it does not exist, pushes the package's shippable tree, and tags the version. The package is then one more repository the registry's index will pick up. To prove it, the agent creates a scratch project and installs the package by coordinate.
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.
05The publisher is a mechanical tool: it creates the repository, pushes the content and tags the version, and nothing more. The host of the registry's address picks the adapter that creates repositories; GitHub and GitVerse are known, and an unknown host is a clear error rather than a guess. In the repository the package content lies flat at the root, and the version is the tag v followed by the version number.
06
Decision. Ship a maintainer utility in v1. Scope: mechanical-only publish — create repo, push contents, tag version. Semantic review (LLM-backed safety analysis per VIBEVM-SPEC.md §8.5) remains v2+.
07 Adapter selection. The CLI picks an adapter from the registry URL's host segment.github.com(or any subdomain) →GitHubCreator;gitverse.ru→GitVerseCreator; unknown hosts surface a clean error pointing at PROP-002 §2.10 rather than guessing a Gitea-compatible shape that may not match the host's actual API.
08 Decision. A package repository contains the package content flat at the repository root:
09
Version = git tag with v<semver> prefix. The tag is a mutable logical label by default: republishing the same version moves it, while the resolved commit and content hash preserve exact identity in each consumer lock.
By hand
101. Put a publish token where vibe reads it. That is the environment variable VIBEVM_PUBLISH_TOKEN, or a file ~/.vibe/<host>.publish.token such as ~/.vibe/github.publish.token, readable by you alone. The token needs the right to create repositories in the organisation.
11 20. Token secrecy and adapter scope
12vibe looks for the token in a fixed order and takes the first it finds. First comes VIBEVM_PUBLISH_TOKEN_<HOST> for the registry's host, such as VIBEVM_PUBLISH_TOKEN_GITHUB. Then VIBEVM_PUBLISH_TOKEN, then the per-host file, then the older ~/.vibe/git.publish.token. The token is a surface secret: never printed, never logged, never written to a file vibe writes. It leaves the process only in the request to the host, over an encrypted connection.
13 Token loading. The publish token loader (crate::token::load_token(host)→load_token_for_host) iterates these sources in order, returning the first non-empty value:
14VIBEVM_PUBLISH_TOKEN_<HOST>environment variable — host-specific, and the highest-precedence source. The suffix is the uppercased first label of the host (VIBEVM_PUBLISH_TOKEN_GITHUBforgithub.com,VIBEVM_PUBLISH_TOKEN_GITVERSEforgitverse.ru); non-alphanumerics fold to_so the name stays a valid POSIX identifier. Lets CI hold tokens for several hosts in the same environment without one host-agnostic variable clobbering them all.
15<settings-dir>/<host-prefix>.publish.token— per-host file. The prefix is the first label of the host (githubforgithub.com,gitverseforgitverse.ru,gitlabforgitlab.com).
16 Token secrecy invariant. The token is a surface secret. It is never displayed in CLI output, log lines, error messages, JSON event payloads, the lockfile,.git/config, or any committed file. The only sanctioned paths through which the value crosses a process boundary are: (a) the GitHub / GitVerseAuthorization: Bearer …HTTP header, sent over TLS to the hosting API; (b) thex-access-token:<TOKEN>@…embed in the URL passed directly to the boundedgit ls-remote/git fetch/git pushinvocations of one publish, never configured as a remote; (c) the in-memoryTokenstruct, which redacts onDisplayandDebug. The CLI prints the source of the token (explicit / env-var / file path) but never the value. Implementations must verify token redaction in unit tests (cf.vibe_publish::token::tests::debug_redacts_value).
172. Rehearse:
193. Publish. --registry picks a registry by name; without it the first one in the manifest is used:
214. Install it from a fresh project to be sure: vibe init scratch and vibe install <group>/notes --path scratch.
Publishing a version again
22Publishing a version that already exists replaces it: vibe appends a commit with the new content and moves the version tag to it. The version number changes only when you change it in the manifest. A consumer who already resolved that version keeps the exact bytes the lock file recorded, and verifies the content fingerprint on every install, so the moved tag is noticed rather than silently accepted; a fresh install gets the new content. When the change matters to consumers, bump the version.
23 Versions are mutable by default (owner ruling, 2026-09-10). Publishing an already-present version is the ordinary replacement flow, not a collision and not an implicit request to bump the version. The publisher appends an exact-payload commit on the observedmain, then moves that version tag to the new commit. A version bump happens only when the author explicitly changes[package].version. An identical retry whose payload and selected tag already match is a no-op.
24 2a. Frozen and snapshot versions (owner rulings, 2026-08-10; terminology fixed 2026-08-13). A version is a snapshot by default — the word carries its Maven sense, mutable: content may change under the same version string,vibe updatebrings the fresh content without regard for hash continuity, and the lockfile pins the delivered capture'scontent_hashplus an opaque provider locator for reproduction. The freeze is the package author's one-way act:frozen = truein the manifest — never a registry's opinion, never part of the version string. The carrier decisions and their reasons: (i) the flag lives inside the hashed content, so a frozen version self-describes even offline and every registry serving those bytes necessarily agrees — in a multi-registry world with no global journal, content is the only carrier that cannot diverge; registries merely observe a freeze in their journals and project it into catalogs; (ii) the version string carries version ordering only — two entities never share one name, which keeps the full matrix expressible: a frozen prerelease (an immutable published beta) and a mutable bare version (being stabilised in place) are both legal; (iii) the transition is one-way and single — unfreezing is forbidden, further work is a new version string; a registry may never accept a frozen coordinate's re-publication with different bytes; (iv) same coordinate + different bytes + any party claiming frozen = loud conflict through the candidate machinery, never a quiet pick. Every surface that shows a version shows its frozen state — machine outputs carry the field by schema; CLI, TUI, GUI and MCP render it always (the Maven lesson: mutability a human cannot see is mutability that will surprise them). Yank remains journal-borne — it is the act frozen content can no longer carry itself.
25 a force-pushed tag upstream is caught by the same machinery on the next install.
26The publisher never rewrites the registry's history: every publish commit is a child of the head it observed, the tag moves only inside one atomic push guarded by the expected state of both refs, older tags are never touched, and the run stays inside the organisation the manifest names.
27 Never rewritemain: every changed publish commit is a child of the exact observed head. Never issue an unconditional force: moving the selected mutable version tag is permitted only inside onegit push --atomicthat carries exact--force-with-leaseexpectations for bothmainand the tag. Never fall back to sequential branch/tag pushes; if atomic push is unsupported or either lease is stale, neither ref moves. Never alter older version tags. Never create a repo in a different org than the configured one unless--org <other>is passed explicitly. Never escalate scope: a publish run targets exactly the org named in the project's[[registry]]URL — adapters MUST refuse to create or modify anything outside that org.
Several packages at once
28A repository that develops several packages publishes them with vibe workspace publish. It orders the members by their dependencies on each other and publishes each as its own repository. It stops at the first failure with a report of what was published and what remains. Each published copy carries an [origin] table naming the repository and the commit it came from, so a copy can always be traced to its source. --member restricts the run to one node; --dry-run shows the selection and the order without pushing.
29
Decision. vibe workspace publish (PROP-007 §2.7) regenerates the boot artifacts of each staged copy for the published shape — where dependencies are registry-resolved and version-pinned, not path-sourced.
Edge cases and rules
30--repo-url pushes straight to an existing git repository with your local git credentials and loads no publish token; use it for hosts without an API adapter.
31The token is never sent to a package's install script and never printed, even in machine-readable output; if a command prints it, that is a bug to report.
32 The publish token (PROP-000 §20) is never placed in a hook's environment.
33A documentation package is published the same way; what differs is how it is consumed, by vibe cache add and the site rather than by vibe install.