# Read documentation locally {#root}

@status:doc/work @audience:user

[p01] Documentation of the packages you use, including private ones, can be read on your own machine with nothing sent anywhere. This page fetches the manual onto your machine and opens the reader in a browser.

[p02]
```prompt
Fetch the VibeVM manual, the package org.vibevm.core/vibevm-docs, into the machine store, open the local documentation reader, and tell me the address to open in my browser.
```

- needs: the vibevm skill installed for your agent; network access to the registry once, or the package already in the store

outcome: `vibe cache list` shows `org.vibevm.core/vibevm-docs`; `vibe doc serve` is running and prints an address on `127.0.0.1`; the browser shows the manual's first page

- assert: `vibe cache list --quiet`

## What happens {#what-happens}

[p03] The agent runs `vibe cache add org.vibevm.core/vibevm-docs`. A documentation package is never installed into a project; it is warmed into the machine [store](../glossary/index.xml#store) together with the packages it documents, so that every rule it cites can be resolved offline. Then the agent runs `vibe doc serve`: vibe starts a small web server that listens on your own machine only, renders each page from the store on request, and prints the address. Nothing is fetched from the internet while you read, and no page leaves the machine.

> [p04] `vibe install` MUST refuse a `doc` package with a hint naming the warm-up command; documentation is warmed into the machine store with `vibe cache add` (§11), and the local reader and `vibe explain` read the store.
>
> <spec://org.vibevm.core/vibevm/common/PROP-057#KIND-DOC-NOT-INSTALLED>

> [p05] `vibe doc serve` starts an HTTP server **on 127.0.0.1 only**, serves the shell and, on every request, glues in the island rendered from the machine store, the current project's lock file or a private registry. The language preference comes from the project's `[i18n].preferred` when present. The mode is fully autonomous: no request to vibevm.org, no external CDN or fonts, everything in the bundle. The server serves files only from known roots (the store, the shell), without path traversal and without directory listing, with a content security policy naming no external source.
>
> <spec://org.vibevm.core/vibevm/common/PROP-057#LOCAL-SERVE>

## By hand {#by-hand}

[p06] 1. Warm the store. Inside a project, its [registries](../glossary/index.xml#registry) are the source; outside one, the machine-wide registries:

[p07]
```sh
vibe cache add org.vibevm.core/vibevm-docs
```

```output
```

[p08] 2. Start the reader and open the printed address in a browser. Stop it with Ctrl+C:

[p09]
```sh
vibe doc serve --help
```

```output
```

[p10] 3. Pick a language with the selector on any page. Where a page has no translation, the reader shows the manual's own language and says so.

## Private packages {#private-packages}

[p11] The same reader shows the documentation of packages that live in a private registry or only on your machine: whatever the store holds, the reader renders. This is the intended path for proprietary documentation, and it is why the reader never contacts the public site.

> [p12] **The local reader listens on 127.0.0.1 only, serves only known roots, loads nothing external, and contacts the network only for a shell download the user explicitly confirmed.**
>
> <spec://org.vibevm.core/vibevm/common/PROP-057#INV-LOCAL-IS-OFFLINE>

## The reader's shell {#the-shell}

[p13] A released `vibe` carries the reader's interface inside the binary. A `vibe` you built from source carries a plain fallback; `vibe doc shell install` fetches the full interface for its version from the release assets, verified by size and digest, into `~/.vibe/opt/`, and only when you ask. The reader works, plainly, without it, and `vibe doc serve --print-shell` says which of the two it holds.

> [p14] When the instance's shell pin names a shell that is not in the shell store and there is no network, the reader falls back to the bare shell and warns; it never contacts the network without consent (§12).
>
> <spec://org.vibevm.core/vibevm/common/PROP-057#LOCAL-OFFLINE-SHELL>

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

[p15] Warming a documentation package warms its [subjects](../glossary/index.xml#subject) too, so the rules a page quotes resolve without a network.

> [p16] Warming a `doc` package with `vibe cache add` warms its subjects too, so that `spec://` citations resolve offline.
>
> <spec://org.vibevm.core/vibevm/common/PROP-057#REL-WARMUP-CLOSURE>

[p17] Inside a project whose local registry holds the package in-tree, `vibe cache add --offline` warms it without a network. The reader's language comes from the project's `[i18n].preferred` or from a flag at launch.

> [p18] The store is warmed with `vibe cache add <coordinate>` from a registry, or `vibe cache add --offline <coordinate>` from a project root whose project-local registry holds the package in-tree; the sources of `vibe-doc` are the store (`lookup`, `list_all`), the lock file (`Lockfile::read`, `slot_abs_path`), a registry (`resolve_and_fetch`) and a checkout (`LocalRegistry`).
>
> <spec://org.vibevm.core/vibevm/common/PROP-057#LOCAL-WARMUP>

> [p19] The local reader serves the same from the store; the preference comes from the project's `[i18n].preferred` or a launch flag.
>
> <spec://org.vibevm.core/vibevm/common/PROP-057#LOC-LOCAL>

[p20] On the site the same page lives under `/doc/`, with the language as the next path segment and no prefix for the source language. An address with a version number shows the current content of that version, because a version may be published again and the registry keeps no past publications.

> [p21] The site is mounted under the path `/doc` of the main domain. The language is the path segment after `/doc/`; the source language of a documentation carries no prefix. The address map is deterministic and needs no index:
>
> <spec://org.vibevm.core/vibevm/common/PROP-057#SITE-MOUNT>

> [p22] An address with a version number always shows the **current** content of that version in the registry: one number may be published ten times a day, and the site shows the last publication. There are no permanent links to past publications — they do not exist in the registry either (§14).
>
> <spec://org.vibevm.core/vibevm/common/PROP-057#SITE-VERSION-SHOWS-CURRENT>

[p23] The reader can be embedded by an editor plugin through an iframe; then the plugin passes its own origin at launch and the reader accepts frames from that origin alone.

> [p24] The policy is sent as a header, not a `<meta>`: `default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'self'; media-src 'self'; object-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors <origin>`, where `frame-ancestors` names only the origin of the host that launched the reader — the launch parameter `vibe doc serve --frame-ancestor <origin>`, because a webview's origin changes from window to window — and is `'none'` without the parameter. The reader has no CORS layer at all and sends `x-content-type-options: nosniff`. The public site is not embeddable in frames.
>
> <spec://org.vibevm.core/vibevm/common/PROP-057#LOCAL-CSP>

[p25] An agent reads the same store: `vibe explain "spec://org.vibevm.core/vibevm-docs/start/what-vibevm-is"` prints a page or a block as text.

