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.tarball URL 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-notice warn 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 audit and npm search keep 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 global mode, which is the default. corepack enable pnpm provides 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:

modeenginelayoutuse when
global (default)pnpmshared store, symlinked node_modulesmost projects
localnpmflat copy per projectmax 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 latest and 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.
  • latest only 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: publish takes allow-list patterns (*, @scope/*, exact names); admin (*) manages tokens; deny is 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 the GIVO_TOKEN env var, then the ~/.npmrc authToken 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 pathwhat
GET /npm/<pkg>packument, hosted or federated
PUT /npm/<pkg>npm publish (lands as draft)
GET /npm/<pkg>/-/<file>.tgztarball
GET /npm/<pkg>/-/<base>-<v>.stamp.jsonthe 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/browsethe catalog: hosted + recently federated
GET /npm/-/givo/scope/<@scope>the catalog fenced to one scope (givo.dev/npm/<@scope> renders it)
GET /npm/-/givo/whoamiwho the bearer token is (username / permissions) — powers givo login
POST /npm/-/givo/signupself-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/bulknpm audit proxy
GET /npm/-/pingping
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.