| name | nxv |
| description | Find any version of any Nix package across nixpkgs git history using the nxv CLI or HTTP API. Use when asked which nixpkgs commit shipped a specific package version (e.g. "python 2.7", "nodejs 15", "ruby 2.6"), when looking up package metadata/license/homepage, when generating a `nix shell nixpkgs/<commit>#pkg` invocation for an old version, or when querying the public/private nxv server. Triggers include "find python 2.7 in nixpkgs", "which commit had nodejs 15.14", "when was foo added/removed", "give me the nix shell command for ruby 2.6", "search nixpkgs for X", or any question about historical Nix package versions. |
| license | MIT |
| allowed-tools | Bash, Read, Glob, Grep |
nxv — Nix Version Index
nxv is a Rust CLI + HTTP API that indexes nixpkgs channel-release history (2016+) into a local SQLite database with a bloom filter for fast lookups. It answers: "which exact nixpkgs commit shipped version X of package Y?" and produces the nix shell nixpkgs/<commit>#pkg command you need to actually use it.
Quick Reference
nxv search python
nxv search python 2.7
nxv search python --exact
nxv search "json parser" --desc
nxv info python311
nxv info python311 3.11.4
nxv history python311
nxv history python311 3.11.4
nxv stats
nxv update
nxv update --no-self-update
nxv serve --host 0.0.0.0 --port 8080
nxv completions zsh
nxv skill install
nxv skill list
How to Use This Skill
Parse $ARGUMENTS to determine the action:
- If arguments look like a subcommand (
search, info, history, stats, update, serve, completions, skill, and indexer-only index, dedupe, publish, keygen), run that subcommand.
- If arguments look like a package name (e.g.
python, nodejs 15, ruby 2.6), default to nxv search.
- If arguments look like a question ("when was X added", "which commit has Y"), pick
search or history accordingly.
- If no arguments, run
nxv stats to give the user a quick health check of their index.
For agents: always pass --format json (CLI) or hit the HTTP API and pipe to jq. The table format is human-only; column widths are terminal-dependent. Exception: nxv stats has no --format flag — use GET /api/v1/stats when you need stats as JSON.
Global Options
Every CLI invocation accepts:
| Flag | Description |
|---|
--db-path <PATH> | Path to the index database (default: platform data dir) |
-v, --verbose | -v info, -vv debug (SQL queries, HTTP requests) |
-q, --quiet | Suppress all output except errors |
--no-color | Disable colored output (also honors NO_COLOR) |
--api-timeout <SECS> | API request timeout when using remote backend (default: 30) |
Local vs Remote Backend
nxv transparently runs against either a local SQLite index or a remote nxv serve instance. Set NXV_API_URL to switch:
NXV_API_URL=https://nxv.urandom.io nxv search nodejs 15
NXV_API_URL=https://nxv.urandom.io nxv info python311
export NXV_API_URL=http://gpu-host:8080
nxv search rust 1.70
If NXV_API_URL is unset, the CLI uses the local index at ~/Library/Application Support/nxv/index.db (macOS) or ~/.local/share/nxv/index.db (Linux). Run nxv update to download the latest published index on first use.
Search
Find packages and the commits where each version existed:
nxv search python
nxv search python 3.11
nxv search python --exact
nxv search python --license MIT
nxv search python --sort date --reverse
nxv search "web server" --desc
nxv search python --show-platforms
nxv search python --full
nxv search python --limit 5
nxv search python --format json
nxv search python --format plain
JSON shape per row:
{
"attribute_path": "python311",
"version": "3.11.4",
"description": "A high-level dynamically-typed programming language",
"license": "Python-2.0",
"first_commit_hash": "abc123...",
"first_commit_date": "2023-06-15T00:00:00Z",
"last_commit_hash": "def456...",
"last_commit_date": "2023-12-01T00:00:00Z"
}
Info
Detailed metadata for one package version (description, license, homepage, platforms, source path, known vulnerabilities):
nxv info python311
nxv info python311 3.11.4
nxv info python311 -V 3.11.4
nxv info python311 --format json
History
Version timeline — when each version first appeared and when it was last seen:
nxv history python311
nxv history python311 3.11.4
nxv history python311 --full
nxv history python311 --format json
JSON shape per row:
{
"version": "3.11.4",
"first_seen": "2023-06-15T00:00:00Z",
"last_seen": "2023-12-01T00:00:00Z",
"is_insecure": false
}
Using a Found Version
The whole point. Take a first_commit_hash (or last_commit_hash) from search/history output and feed it to Nix:
nix shell nixpkgs/e4a45f9#python
nix run nixpkgs/e4a45f9#python
inputs.nixpkgs-python27.url = "github:NixOS/nixpkgs/<commit>";
Pick first_commit_hash for the canonical "introduced in" commit; pick last_commit_hash if you want the most recent commit that still shipped that version.
Index Management
nxv update
nxv update --force
nxv update --no-self-update
nxv update --skip-verify
nxv update --public-key /path/key.pub
nxv update --manifest-url <URL>
nxv stats
nxv update always refreshes the package index first. Then it checks GitHub for a newer nxv release:
- Local install (install.sh / manual download): downloads the platform binary, verifies SHA-256, atomically swaps the running executable.
- Nix / cargo / Homebrew: prints the matching upgrade hint (e.g.
brew upgrade nxv) and exits successfully.
Set NXV_NO_SELF_UPDATE=1 for CI/systemd timers that should only refresh the index.
API Server
nxv serve
nxv serve --host 0.0.0.0 --port 3000
nxv serve --cors
nxv serve --cors-origins https://app.example.com
nxv serve --rate-limit 10 --rate-limit-burst 20
The server bundles:
- Web UI at
/ (Tailwind v4 + vanilla JS, embedded at build time)
- OpenAPI docs at
/docs (Scalar UI)
- REST API at
/api/v1/* — see endpoints below
- Cache headers: 1h on cacheable package routes; never on
/health, /metrics
HTTP Endpoints
All paths are under /api/v1. Wrapped responses always look like { "data": ..., "meta": {...} } for paginated lists, { "data": ... } for single items.
| Method | Path | Purpose |
|---|
GET | /search?q=<name>&limit=&offset=&sort=&exact= | Search packages |
GET | /search/description?q=<text>&limit=&offset= | Full-text search descriptions (FTS5) |
GET | /packages/{attr} | All version records for a package |
GET | /packages/{attr}/history | Version timeline (first/last seen) |
GET | /packages/{attr}/versions/{version} | All records for one version |
GET | /packages/{attr}/versions/{version}/first | First-seen commit |
GET | /packages/{attr}/versions/{version}/last | Last-seen commit |
GET | /stats | Index statistics |
GET | /health | Liveness probe (uncached) |
GET | /metrics | Server metrics (uncached) |
Search query parameters:
| Parameter | Type | Description |
|---|
q | string | Search query (required) |
version | string | Version filter (prefix match) |
exact | boolean | Exact attribute name match |
license | string | License filter |
sort | string | date, version, or name |
reverse | boolean | Reverse sort order |
limit | integer | Max results (default 50) |
offset | integer | Results to skip (default 0) |
Quick examples against the public instance:
curl -s "https://nxv.urandom.io/api/v1/search?q=python&version=3.11&limit=5" | jq
curl -s "https://nxv.urandom.io/api/v1/packages/python311/history" | jq '.data[0:3]'
curl -s "https://nxv.urandom.io/api/v1/packages/nodejs_15/versions/15.14.0/first" | jq
curl -s "https://nxv.urandom.io/api/v1/stats" | jq '.data'
Skill Management
nxv can install this very skill for any major AI coding agent — the binary embeds the SKILL.md and writes it where each agent looks, per the Agent Skills standard:
nxv skill install
nxv skill install --project
nxv skill install claude codex
nxv skill install --all
nxv skill install copilot --dir ~/repo
nxv skill list
nxv skill show
nxv skill uninstall --project
Supported agents and where the skill lands (<dir>/nxv/SKILL.md):
| Agent | User-wide | Project-level |
|---|
claude | ~/.claude/skills/ | .claude/skills/ |
codex | ~/.codex/skills/ | .agents/skills/ |
pi | ~/.pi/agent/skills/ | .pi/skills/ |
openclaw | ~/.openclaw/skills/ | .agents/skills/ |
copilot | ~/.copilot/skills/ | .github/skills/ |
cursor | ~/.cursor/skills/ | .agents/skills/ |
gemini | ~/.gemini/skills/ | .agents/skills/ |
amp | ~/.config/amp/skills/ | .agents/skills/ |
goose | ~/.config/goose/skills/ | .agents/skills/ |
agents | ~/.agents/skills/ | .agents/skills/ |
The table shows each agent's primary directory — the one nxv skill install <agent> writes to. Several agents read additional locations: Copilot reads .github/skills/, .claude/skills/, or .agents/skills/ in a repository, and Pi reads .agents/skills/ as well as .pi/skills/.
Semantics:
- With no agent arguments, a user-wide install targets the agents detected on the machine (their config dir exists), falling back to the generic
agents directory if none are found. A project install (--project / --dir) defaults to the .claude + .agents pair — per the read paths above, every supported agent picks up one of the two.
- Agents sharing a directory (e.g. codex/cursor/gemini at project level) are deduplicated into a single write.
- Install overwrites
skills/nxv/SKILL.md unconditionally and never touches other files; uninstall removes only that file (and the nxv/ directory if it is then empty).
Indexer Commands (feature-gated)
These require nxv built with --features indexer (cargo build --features indexer or nix build .#nxv-indexer). The indexer ingests channel-release snapshots from releases.nixos.org — no nixpkgs checkout and no Nix evaluation needed for the main path:
nxv index
nxv index --channel nixpkgs-unstable
nxv index --since 2024-01-01 --until 2024-06-30
nxv index --strict --report report.json
nxv index --backfill-evals
nxv index --head-eval
nxv index --retry-failed
nxv index --max-releases 5
nxv dedupe --dry-run
nxv dedupe
nxv publish --output ./publish --url-prefix https://your-server/nxv
nxv publish --output ./publish --url-prefix https://... --sign --secret-key nxv.key
nxv publish --output ./publish --url-prefix https://... --artifact-name-prefix run-123-
nxv keygen --secret-key ./nxv.key --public-key ./nxv.pub
Every recorded commit is a real, Hydra-built channel commit — nix shell commands produced from the index hit the binary cache instead of compiling from source. Version ranges mean "observed at both endpoints"; a version that lived shorter than one channel advance (~a day) may be missed.
Retired commands: nxv backfill and nxv reset are gone — snapshots carry complete metadata (source_path, homepage, known_vulnerabilities), and there is no checkout to reset.
Most users never need these — they consume a pre-built published index via nxv update. Only run these when self-hosting an index. Use --artifact-name-prefix for mutable stores such as GitHub Releases so payload assets can be uploaded under immutable names before replacing manifest.json.
Key Environment Variables
| Variable | Purpose |
|---|
NXV_API_URL | Point CLI at a remote nxv serve instead of the local DB |
NXV_DB_PATH | Override local SQLite path |
NXV_API_TIMEOUT | HTTP client timeout in seconds (default 30) |
NXV_MANIFEST_URL | Override the manifest URL used by nxv update |
NXV_PUBLIC_KEY | Public key for manifest verification (path or raw key) |
NXV_SECRET_KEY | Secret key for nxv publish --sign (path or raw content) |
NXV_SKIP_VERIFY | Skip minisign signature check (INSECURE — dev/testing only) |
NXV_NO_SELF_UPDATE | Skip the binary self-update check during nxv update |
NXV_VERSION | Pin the version installed by install.sh (self-update targets latest) |
NXV_HOST | nxv serve bind host |
NXV_PORT | nxv serve listen port |
NXV_RATE_LIMIT | nxv serve per-IP rate limit (req/sec) |
NXV_FRONTEND_DIR | nxv serve reads frontend assets from disk on every request (dev mode) |
NO_COLOR | Disable ANSI colors |
Data Paths
The local index lives in platform data directories:
- macOS:
~/Library/Application Support/nxv/
- Linux:
~/.local/share/nxv/
Files:
index.db — SQLite database with package versions
bloom.bin — Bloom filter sibling for fast negative lookups (loaded at search time)
Safe to delete; nxv update will rebuild from the published manifest.
Agent Patterns
Find the commit that introduced a specific version (CLI):
nxv search python 3.11.4 --exact --format json | \
jq '.[0] | {pkg: .attribute_path, version, commit: .first_commit_hash, date: .first_commit_date}'
Generate the nix shell invocation directly (CLI):
nxv search nodejs 15 --exact --format json | \
jq -r '.[0] | "nix shell nixpkgs/\(.first_commit_hash | .[0:7])#\(.attribute_path)"'
Find a package version on the public API:
curl -s "https://nxv.urandom.io/api/v1/packages/python27/versions/2.7.18/first" | \
jq -r '.data | "nix shell nixpkgs/\(.first_commit_hash | .[0:7])#\(.attribute_path)"'
Check whether a version of a package was ever in nixpkgs (HTTP):
curl -s "https://nxv.urandom.io/api/v1/packages/ruby/history" | \
jq '.data[] | select(.version | startswith("2.6"))'
Get the index freshness (newest ingested channel commit):
curl -s "https://nxv.urandom.io/api/v1/stats" | \
jq '.data | {commit: .last_indexed_commit, date: .last_indexed_date, packages: .unique_names}'
Key Invariants for Agents
- Search is dedup-by-default: by default, only the most recent record per
(attribute_path, version) pair is returned. Pass --full (CLI) or use the /packages/{attr} HTTP endpoint to see every commit.
- Version filters are prefix matches:
nxv search python 3.11 matches 3.11.0, 3.11.4, 3.11.10, etc. Use --exact for whole-attribute matches, not for whole-version matches.
- Bloom filter gives instant negatives: a search for a nonsense package name returns in <1 ms because the bloom filter rejects it before SQLite is touched. False positives are possible but rare.
- Coverage starts in September 2016: nixpkgs commits before then are not indexed. Anything older needs raw git spelunking.
- Self-hosted indexes need a public key: pass
--public-key or set NXV_PUBLIC_KEY when consuming a manifest you signed yourself, otherwise nxv update rejects the signature.
--format json shape is stable: safe to pipe to jq. Breaking shape changes would be a semver bump.
/api/v1 data responses always wrap in {data, meta} (or {data} for single items): do jq '.data' first. Exceptions: the operational /health and /metrics endpoints are unwrapped.
Practical Tips
- Just want a python 2.7 shell?
nxv search python 2.7 --exact --format json | jq -r '.[0].first_commit_hash', then nix shell nixpkgs/<hash>#python27.
- Use
--exact when the package name is unambiguous; otherwise python returns dozens of variants (python27, python311, python311Packages.numpy, etc.).
- Use
--desc for fuzzy intent ("a package that does X") instead of exact name searches.
- Set
NXV_API_URL=https://nxv.urandom.io to skip the ~190MB index download entirely if you only need occasional lookups.
- Update regularly: the public index is republished every 6 hours (
publish-index.yml); nxv update pulls the latest.
- For CI: pin to
NXV_NO_SELF_UPDATE=1 nxv update so the runner refreshes the index but never tries to swap its own binary.
Updating This Skill
This skill is generated by the nxv binary itself. To refresh it after upgrading nxv:
nxv update
nxv skill install
nxv skill install --project
Without an nxv binary on hand, fetch the canonical copy from the repository:
curl -sL https://raw.githubusercontent.com/utensils/nxv/main/.claude/skills/nxv/SKILL.md \
-o ~/.claude/skills/nxv/SKILL.md