# Nxv

> 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. Use when this capability is needed.

- Skill: `tomevault-io/nxv` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/nxv`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/nxv/raw
- Safety review: pending (external: skill-scanner PASS, skillspector WARNING)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/nxv

---


# 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

```bash
nxv search python                        # All python packages (most recent per version)
nxv search python 2.7                    # Filter by version (prefix match)
nxv search python --exact                # Exact attribute name only
nxv search "json parser" --desc          # Full-text search package descriptions
nxv info python311                       # Detailed info for current version
nxv info python311 3.11.4                # Detailed info for specific version
nxv history python311                    # Version timeline (first/last seen)
nxv history python311 3.11.4             # When was 3.11.4 available?
nxv stats                                # Index statistics
nxv update                               # Refresh index + check for newer nxv binary
nxv update --no-self-update              # Refresh index only (CI / systemd timers)
nxv serve --host 0.0.0.0 --port 8080     # Start HTTP API + web UI
nxv completions zsh                      # Generate shell completions
nxv skill install                        # Install this skill for detected AI agents
nxv skill list                           # Agents, skill paths, install status
```

## 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:

```bash
# Use the public hosted instance — no local index needed
NXV_API_URL=https://nxv.urandom.io nxv search nodejs 15
NXV_API_URL=https://nxv.urandom.io nxv info python311

# Or your own private instance
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:

```bash
nxv search python                                    # Recent per (pkg, version)
nxv search python 3.11                               # Version prefix filter
nxv search python --exact                            # Exact attribute name only
nxv search python --license MIT                      # Filter by license
nxv search python --sort date --reverse              # Oldest first
nxv search "web server" --desc                       # FTS5 description search
nxv search python --show-platforms                   # Add platforms column
nxv search python --full                             # All commits (no dedup)
nxv search python --limit 5                          # Cap at 5 results
nxv search python --format json                      # Machine-readable JSON
nxv search python --format plain                     # TSV for shell scripts
```

**JSON shape per row:**

```json
{
  "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):

```bash
nxv info python311                       # Latest known version
nxv info python311 3.11.4                # Specific version (positional)
nxv info python311 -V 3.11.4             # Specific version (flag form)
nxv info python311 --format json
```

## History

Version timeline — when each version first appeared and when it was last seen:

```bash
nxv history python311                    # All versions of python311
nxv history python311 3.11.4             # Just one version's window
nxv history python311 --full             # Add commits, license, homepage, etc.
nxv history python311 --format json
```

**JSON shape per row:**

```json
{
  "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:

```bash
# Drop into a shell with that exact version
nix shell nixpkgs/e4a45f9#python

# Run it once
nix run nixpkgs/e4a45f9#python

# Add to a flake input
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

```bash
nxv update                               # Refresh index + check for newer nxv binary
nxv update --force                       # Force full re-download of the index
nxv update --no-self-update              # Only refresh the index, leave binary alone
nxv update --skip-verify                 # Skip minisign signature check (INSECURE)
nxv update --public-key /path/key.pub    # Use a custom public key (self-hosted index)
nxv update --manifest-url <URL>          # Use a custom manifest (self-hosted index)
nxv stats                                # Index size, commit range, last update
```

`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

```bash
nxv serve                                            # 127.0.0.1:8080 (default)
nxv serve --host 0.0.0.0 --port 3000                 # Public bind
nxv serve --cors                                     # Enable CORS for all origins
nxv serve --cors-origins https://app.example.com     # Restrict CORS
nxv serve --rate-limit 10 --rate-limit-burst 20      # Per-IP rate limit
```

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:**

```bash
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](https://agentskills.io):

```bash
nxv skill install                        # Install user-wide for detected agents
nxv skill install --project              # Install into the current project (.claude + .agents)
nxv skill install claude codex           # Install for specific agents only
nxv skill install --all                  # Install for every supported agent
nxv skill install copilot --dir ~/repo   # Project install into another directory
nxv skill list                           # Show agents, paths, install status
nxv skill show                           # Print the SKILL.md to stdout
nxv skill uninstall --project            # Remove project-level installs
```

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:

```bash
# Ingest new channel releases (default channels: nixpkgs-unstable + nixos-unstable-small)
nxv index                                            # Incremental: only new releases
nxv index --channel nixpkgs-unstable                 # Restrict to one channel
nxv index --since 2024-01-01 --until 2024-06-30      # Bound by release date
nxv index --strict --report report.json              # CI mode: gates fatal, JSON report
nxv index --backfill-evals                           # One-time 2016-2020 era (needs `nix`, ~2-3h)
nxv index --head-eval                                # Evaluate master HEAD when channels stall (needs `nix`)
nxv index --retry-failed                             # Re-attempt failed/parked releases
nxv index --max-releases 5                           # Bound a run (testing)

# Repair duplicate rows in pre-v4 databases (also runs during v3->v4 migration)
nxv dedupe --dry-run                                 # Preview
nxv dedupe                                           # Run

# Publish distribution-ready compressed artifacts + manifest
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-

# Generate a minisign keypair for signing manifests
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):**

```bash
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):**

```bash
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:**

```bash
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):**

```bash
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):**

```bash
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:

```bash
nxv update                               # Get the latest nxv (also refreshes the index)
nxv skill install                        # Rewrite user-wide installs from the new binary
nxv skill install --project              # ...or refresh a project-level install
```

Without an nxv binary on hand, fetch the canonical copy from the repository:

```bash
curl -sL https://raw.githubusercontent.com/utensils/nxv/main/.claude/skills/nxv/SKILL.md \
  -o ~/.claude/skills/nxv/SKILL.md
```

---
> Source: [utensils/nxv](https://github.com/utensils/nxv) — distributed by [TomeVault](https://tomevault.io).
<!-- tomevault:4.0:skill_md:2026-07-05 -->

