VibeVM
Contents
On this page
en
Publisher
org.vibevm.core
Version
1.0.0latest
Audiences
dev
Reading time
4 min
Rendered
Read aloud
never

How vibe is built

01vibe is one binary built from a set of Rust libraries, each owning one concern: reading the description of a project, choosing versions, fetching from where packages are published, writing the tree on disk, talking to agents. This page maps the libraries, the seams between them, and the path an install takes through them.

Five layers

02Read the product bottom-up. Identity: a package is a coordinate plus a content fingerprint, and the kind is metadata. Registry: ordered package sources with mirrors, overrides and an optional index. Store: every fetched package version kept once per machine.

03Materialisation: the resolved graph copied into the project's dependency tree and recorded in the lock file. Computed boot: the packages' contributions projected into the two generated files an agent reads. Everything a user sees is one of these five layers or a surface over them.

04 21. Surface floor — which channels a capability owes

The crates

05
Concern Crates What they own
core vocabulary vibe-core, vibe-wire manifests, the lock file, identities and content hashes; the generated types of every registered machine format
specifications vibe-spec, vibe-specdoc, progress-core, vibe-facts, vibe-trace addresses and the deterministic router; the document model with its Markdown and XML frontends and backends; the status markup parser and reports; the adoption-facts registry; traceability queries
registries and the store vibe-registry, vibe-index, vibe-publish, vibe-package-source git transport, mirrors and overrides, the clone cache and the machine store; the searchable index and its server; publishing; the one production composition of package sources
resolution and installation vibe-resolver, vibe-install, vibe-workspace, vibe-safefs the solver seams and cells; plan and apply; workspace discovery, materialisation, the computed boot; capability-relative filesystem mutation
the lifecycle vibe-lifecycle, vibe-extension-registry, vibe-orchestrator, vibe-ext, vibe-native-loader, vibe-llm, vibe-scrape the nine-phase model and chaining; the pure extension registry; surface-neutral orchestration; the safe author SDK and the quarantined loader of native extensions; the model provider seam; scrape planning
agents and preferences vibe-mcp, vibe-agent-projection, vibe-settings, vibe-actions, vibe-requirements the MCP server and integration manager; skill projection into agents; three-level preferences; frontend-agnostic actions; the read-only requirements query
surfaces and checks vibe-cli, vibe-check the command line; the deterministic project linter
documentation vibe-doc, vibe-doc-server, vibe-doc-shell the page pipeline behind vibe doc: build, check, manifest, surface snapshots, the maintenance queue and the site builder; the local reader's server on the loopback address; the reader's shell carried inside the binary
reserved and tooling vibe-graph, vibe-test-support, xtask a reserved task-graph slot; test isolation of the settings home; the maintainer gates: code generation, the traceability map, engine synchronisation, mirroring, the release build

06The dependency direction is fixed: a surface calls an orchestrator, an orchestrator calls a library through a seam, a library returns typed values. Domain libraries never prompt, never format terminal output and never decide authentication; those choices are made at the composition root, in the CLI or the MCP server.

07 17. Production architecture in the prototype phase

08Four decisions hold the tree together. The repository is one Cargo workspace with every crate under crates/. Each ability of the product lives in a library, and the command line, the terminal interface and the MCP server are thin surfaces over it. JSON Type Definition schemas are the single source of truth for every machine contract. And the commit discipline is the installed git-practices family, read at the start of every session.

09 Decision: Single Cargo workspace at repo root. Crates live under crates/ per VIBEVM-SPEC.md §10.2:
10 Decision: a capability lives in a library; the CLI, the TUI and the MCP server are thin surfaces over it. The rule and its vocabulary are the installed omnichannel flow: spec://org.vibevm.world/omnichannel/flows/omnichannel/OMNICHANNEL-PROTOCOL#root. This section declares only vibevm's own floor, which is what that flow asks each project to state for itself.
11 Decision: JSON Type Definition (RFC 8927) schemas are the single source of truth for every client/server and machine-to-machine contract in this project.
12 The repository's commit-and-push discipline is the git-practices family (a host dependency), whose members carry the full text:

The path of an install

131. Discover the workspace root and read the manifests, the lock file, the user configuration, the registries, mirrors, overrides and local package sources.

142. Compare the manifests with the lock file; if nothing changed, skip resolution.

153. Otherwise qualify every requested coordinate and build the solver's view of available versions. Solve the whole graph, holding every pin the change does not touch.

164. Fetch every selected identity: a store hit is reused, a miss walks the allowed sources and inserts the verified tree into the store.

175. Build the plan, validate the managed blocks of the instruction files, and ask for confirmation.

186. Materialise the graph into the dependency tree by diff, regenerate the boot files, prune stale slots.

197. Record the graph, the provenance and the fingerprints in the lock file.

208. Render the human, quiet or JSON report.

21 Decision. vibe install is understood as two phases, optimised independently — the current code conflates them.

The seams

22GitBackend isolates git: the production implementation shells out to the system git, so SSH agents and credential helpers behave as they do everywhere else. Registry enumerates, resolves and fetches across local and git-backed sources; MultiRegistryResolver owns the ordered walk, mirrors, overrides, authentication and the offline posture. DepProvider is the solver's view of the world and DepSolver turns roots into a graph; the default cell is resolvo, with a backtracking SAT cell and a naive cell selectable. InstallSource separates the transaction from the construction of cells. RepoCreator isolates the hosts' repository creation for publishing. Each seam has more than one implementation, and tests run the seam, not the production cell.

23 Decision. Add a second DepSolver impl, SatDepSolver, alongside NaiveDepSolver. Both implement the same crates/vibe-resolver/src/lib.rs::DepSolver trait (fn solve(&self, roots: &[PackageRef]) -> Result<ResolvedGraph, SolveError>). NaiveDepSolver stays in tree as the "small graphs / no features / no disjunctions" fast path. The default clause is superseded (PROP-017): both impls shipped (naive.rs, sat.rs), but the production default became resolvo, not sat.

Wire formats and authored formats

24Two kinds of text cross the product's boundary. Machine formats, the JSON reports, the lock file's records, the release manifests, are described by JSON Typedef schemas and their types are generated; a handwritten parser of our own format is a defect the build counts. Authored formats, the manifest and the specifications, are parsed by handwritten code on purpose, because a person writes them and the errors must speak the person's language.

25 16. JTD + codegen for wire contracts
26 4.1 The format registry. formats/REGISTRY.toml inventories every surface a foreign parser reads: id, epoch, schema path, recoverable-or-not, independent-parser count, sunset date, golden-corpus path. From it the FormatId enum is generated, and all wire I/O goes through wire::publish(FormatId, …) / wire::load(FormatId, …) — an unregistered format is inexpressible in the type system, not merely discouraged. An unnumbered format is a format that will be broken without anyone noticing.

27The schema language has no 64-bit integer, so any integer wider than 32 bits rides the wire as a decimal string.

28 4.2b Integers wider than 32 bits ride the wire as decimal strings (owner ruling 2026-08-20, the B-091 fork answered once and generally). JTD (RFC 8927) has no 64-bit integer type at all — the pinned generator rejects uint64 and int64 as InvalidType (measured 2026-08-15) — so every field wider than 32 bits would otherwise re-litigate the same bad trilemma: a uint32 that is false at and above 2³², a float64 that loses precision past 2⁵³, or an untyped {} that loses the field entirely. The general answer: such a field is encoded as a canonical decimal string — ASCII digits only, no sign, no leading zeros except "0" itself — the schema says string, the Rust type stays the true integer, conversion lives at the serde boundary, and non-canonical input is refused loudly rather than coerced. Timestamps are not this rule's business: they ride as RFC 3339 through the timestamp vocabulary. First application: the catalog manifest's file size (formats/breaks/003.md).

Where to read next

29The specifications are the authority: PROP-000 for the foundational decisions, PROP-009 for the loading model, PROP-002 and PROP-010 for registries and the store, PROP-054 for the lifecycle and the extension machine, PROP-045 for the document model, PROP-057 and PROP-058 for documentation packages, the site and how this manual is maintained. The traceability page of this manual explains how code cites them and how to ask the map which code implements which rule. The developer guide in the repository covers building, testing and the self-check panel.

For an agent

This page has a machine mirror. The citation carries the version rather than latest, so what an agent quotes does not move under it.

spec://org.vibevm.core/vibevm-docs@1.0.0/architecture/how-vibe-is-built

.md.xmlllms.txt