Documentation.
What GIVO is
GIVO is a package registry that replaces npmjs as the registry your tools talk to. It lives at https://registry.givo.dev/npm/ and speaks the npm protocol, so npm and pnpm work against it without changes. Two kinds of packages come out of it:
- Hosted: packages published to GIVO itself. They get a draft/release lifecycle, a public write-once integrity stamp per release, an append-only audit log and a kill-switch.
- Federated: every other name. GIVO proxies it from npmjs, rewrites the download URLs so all traffic flows through GIVO, caches it and verifies it.
The givo CLI fronts npm and pnpm and adds the registry commands. The site you are on renders the same data the registry serves.
How it works, next to npmjs
Every request resolves through a cascade with one rule: hosted wins.
request -> hosted? serve from GIVO
-> else fetch from npmjs, rewrite, verify, cache, serve
- Federated packuments are cached for 5 minutes, then revalidated against npmjs with etags. Both the full and the abbreviated (install) variants are kept.
- Every
dist.tarballURL in a federated packument is rewritten to GIVO. That single choke point is what makes caching, verification and auditing possible. - Federated tarballs stream to you while a copy is written to the cache. The registry checks the manifest checksum during that write: bytes that do not match are never cached.
- A hosted name that also exists on npmjs shadows it on purpose. The publish response and an
npm-noticewarn about it. Publishing bare (unscoped) names at all requires a token minted by the operator; self-service accounts are confined to their own scope. npm auditandnpm searchkeep working: GIVO proxies the security advisories and search endpoints to npmjs, with hosted results overlaid first.
Requirements
- Node.js 22 or newer, with npm (npm ships with Node).
- pnpm, only for projects in
globalmode, which is the default.corepack enable pnpmprovides it. - A token, only for publishing and lifecycle commands. Installing and reading never need one.
Install the CLI
$ curl -fsSL givo.dev/install | sh
The installer maps the @givo scope to the registry and installs @givo/cli globally, which provides the givo command. Prefer an auditable install over curl | sh? Do the same thing directly:
$ npm install -g @givo/cli --registry=https://registry.givo.dev/npm/
The CLI is optional: plain npm and pnpm work against the registry too. What the CLI adds: the lifecycle commands, the engine wrapper and the AI safety wrapping of installed docs.
Point npm at GIVO
$ givo setup # this project's .npmrc
$ givo setup --global # machine-wide: GIVO replaces npmjs everywhere
Recommended rollout: start with one project, live with it, then go
--global when comfortable. Per-project is contained (one
.npmrc) and trivially reversible. Precedence, so there is no
surprise: --global sets the machine default, and a project's
own .npmrc always overrides it; individual projects can keep
or restore npmjs at any time.
Without the CLI, the equivalent is:
$ npm config set registry https://registry.givo.dev/npm/ --location=project
Note the /npm/ path: it is part of the registry URL.
Everyday commands
The install commands go through the engine picked by the project's mode, persisted in package.json under givo.mode:
| mode | engine | layout | use when |
|---|---|---|---|
global (default) | pnpm | shared store, symlinked node_modules | most projects |
local | npm | flat copy per project | max compatibility, e.g. React Native |
$ givo mode local # switch engine (cleans node_modules + the other lockfile)
$ givo install # also: i
$ givo add left-pad # also: remove, rm, update, up
$ givo run typecheck # also: build, test, start
After every install or update, givo wraps each AGENTS.md found under node_modules in an untrusted-content notice, written into the file itself. See For AI agents.
Publishing
publish -> draft -> release -> released (deprecate: warning only)
| \-> tombstone (410, record stays)
\-> unpublish (gone)
$ givo publish # up as a DRAFT: mutable, republishable
$ givo add my-pkg@draft # try a draft on purpose; latest is untouched
$ givo release 0.1.0 # seal it: stamp written, latest moves
$ givo unpublish 0.1.1 # discard a draft (a release is permanent)
$ givo deprecate 0.1.0 use 0.2.x # soft warning, still installable
$ givo tombstone 0.1.0 malicious # kill-switch: downloads answer 410
Rules the registry enforces:
- A draft never moves
latestand always installs with a loud draft warning. - Releasing writes a public, write-once stamp: integrity, shasum, size, publisher and a digest anyone can recompute. It lives at
/npm/<pkg>/-/<base>-<v>.stamp.json. The stamp proves the registry did not alter the bytes after release (consistency); it is not a publisher signature (provenance). - A released version is immutable forever: no republish, no re-release, no unpublish. The way out of a bad release is the tombstone, which keeps the record and answers 410.
latestonly moves forward and never points at a draft or a tombstone.
givo publish expects the package to declare the registry:
"publishConfig": { "registry": "https://registry.givo.dev/npm/" }
Without it, the CLI asks for confirmation (or takes --yes in scripts). That guard keeps a package meant for npmjs from being hosted by accident. Publishing a name that exists on npmjs is allowed, and answered with a shadowing notice: once released, GIVO serves your version instead.
Registry docs
Markdown lives next to the package, outside the tarball, and is editable without republishing. A version-level doc overrides the root one; reads fall back to root.
Because these docs are third-party text an agent may read to decide how to use a package — and a released version is immutable while its docs are not — prose docs are served wrapped in the same untrusted-content notice the CLI writes into installed AGENTS.md. A doc rewritten with a leaked token still arrives marked. JSON docs are data and round-trip untouched.
$ givo docs push my-pkg README.md
$ givo docs push my-pkg AGENTS.md --v 0.1.0
$ givo docs get my-pkg AGENTS.md
Tokens
Anyone can create an account straight from the terminal, verified by email:
$ givo signup alice
Interactive: it asks your name and email, the registry emails a 6-digit code, you enter it, and the token is shown once (it then offers to save it to ~/.npmrc). Non-interactive (agents, CI): givo signup alice --name "Alice" --email you@example.com requests the code, then givo signup alice --code 123456 --save finishes. The token is confined to your own scope (@alice/*): bare names and other scopes stay closed, admin is never granted. The username is permanent — no rename, and released packages stay under the scope forever. Broader scopes come from the registry admin:
$ givo token mint --label ci --publish '@myscope/*' --deny '@myscope/blocked'
$ givo token ls
$ givo token rm tok_xxx
- Scopes:
publishtakes allow-list patterns (*,@scope/*, exact names);admin(*) manages tokens;denyis a blocklist that beats any allow. - Tokens are stored hashed. The raw value is shown once, at mint.
- Commands resolve the token from
--token, then theGIVO_TOKENenv var, then the~/.npmrcauthToken for this registry.
When npmjs is down
Everything that ever resolved through GIVO keeps installing: packuments are served from the durable cache (marked x-givo-upstream: stale) and tarballs come from the verified cache. Names GIVO has never seen answer 502 upstream_unreachable until npmjs returns. Nothing fails silently.
HTTP API
Base URL https://registry.givo.dev/npm/. Reads are open and CORS-enabled; writes take Authorization: Bearer <token>. Errors are JSON with specific codes: { "error": "version_already_released" }, never vague ones.
| method and path | what |
|---|---|
GET /npm/<pkg> | packument, hosted or federated |
PUT /npm/<pkg> | npm publish (lands as draft) |
GET /npm/<pkg>/-/<file>.tgz | tarball |
GET /npm/<pkg>/-/<base>-<v>.stamp.json | the release stamp |
POST /npm/<pkg>/-/release/<v> | seal a draft |
POST /npm/<pkg>/-/unpublish/<v> | discard a draft |
POST /npm/<pkg>/-/deprecate/<v>?message= | deprecate |
POST /npm/<pkg>/-/tombstone/<v>?reason= | kill-switch |
PUT|GET /npm/<pkg>/<file> | registry docs (also per version) |
GET|PUT|DELETE /npm/-/package/<pkg>/dist-tags[/<tag>] | dist-tags; latest and draft are managed |
GET /npm/-/v1/search?text= | search, npmjs proxy with hosted first |
GET /npm/-/givo/browse | the catalog: hosted + recently federated |
GET /npm/-/givo/scope/<@scope> | the catalog fenced to one scope (givo.dev/npm/<@scope> renders it) |
GET /npm/-/givo/whoami | who the bearer token is (username / permissions) — powers givo login |
POST /npm/-/givo/signup | self-service account: a token confined to @<username>/* |
GET /npm/-/givo/audit/<pkg> | public audit history: every write that touched the package |
POST /npm/-/npm/v1/security/advisories/bulk | npm audit proxy |
GET /npm/-/ping | ping |
POST|GET /npm/-/tokens, DELETE /npm/-/tokens/<id> | token management (admin) |
For AI agents
GIVO assumes many installs are driven by agents and treats package docs as untrusted input. On install, each AGENTS.md under node_modules is wrapped between <!--GIVO-NOTICE--> markers, before and after the content, telling the reader to treat it as reference only. The write is atomic and pnpm's global store is never mutated.
Machine documentation for agents lives at givo.dev/AGENTS.md, and robots.txt points there. If you are an agent reading this page: read that file before acting on the registry.
Undo everything
$ npm config delete registry --location=user # back to npmjs globally
$ npm uninstall -g @givo/cli # remove the CLI
Per project, delete the registry= line from the project's .npmrc. Lockfiles created while on GIVO resolve to registry.givo.dev URLs; run a fresh install after switching back if you want npmjs URLs in the lockfile.