From f5700591522fcf15bb7f540a0084624b7fc0ea2d Mon Sep 17 00:00:00 2001 From: bryan Date: Tue, 4 Aug 2026 15:50:57 -0700 Subject: [PATCH 1/3] vlt: package the vlt package manager from npm (BSD-2-Clause-Patent) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vlt 1.0.1 shipped today (vltpkg/vltpkg). Seeded with `pkgmgr import npm vlt` and refined by hand for the two things discovery mode can't know. Why this one is unusually clean to package: the published tarball has **zero dependencies**. `npm ci` against the committed lockfile installs exactly one package, pinned by version + integrity — no transitive tree, no native addons, no compile step. `npm audit` in-build reports 0 vulnerabilities across the whole 2-package graph. Two changes on top of the generated files: - **Bins enumerated, not globbed.** The importer emits `bins = { glob = "usr/bin/*" }`, which trips the `enumerate bins` checker for any package with a handful of bins — vlt has five (`vlt`, plus the documented shorthands `vlr`/`vlx` and their variants). The npm registry metadata lists them exactly, so nothing had to be inferred. Fixing this in the importer is pkgmgr-rs follow-up work, not a one-off. - **Real standalone tests.** The generated package had none, and `standalone tests...Pass` is what the checker reports for a package with zero tests — so "Pass" out of the box meant nothing. Added three that exercise the artifact rather than the build inputs: * `version_is_exact` — string equality against `%{version}`, not `--version | grep`. vlt's usage banner also carries the version, so a grep would pass on a build where `--version` is broken and only usage prints. * `companion_bins_work` — all four shorthands. They are PATH symlinks into the private libexec prefix; a broken relative link fails here and nowhere else. * `reads_a_manifest` — `vlt pkg get name` against a fixture package.json. Real offline work through the bundled JS; a truncated install still prints `--version` and fails this. Mutation-tested: flipping the expected version to `0.0.0-mutant` turns the suite red with `version_is_exact ... had exit code 1`, so these can fail for the reason they exist. **No `source_provenance`, deliberately.** vlt's vuln identity is its npm package name. minimal-supply-chain routes a node-flavored package with no provenance to `pkg:npm/` (`scan.rs::npm_purl_for_node_package`) where the OSV/GHSA npm advisories resolve. Declaring `GithubRepo vltpkg/vltpkg` would move it onto the repo-purl arm, where GHSA's npm advisories — keyed by package name, not repo — would stop matching. Absent is correct here. Built and checked with the min-native path (`min package build` side-op + `min check`), 15/15 Pass. --- packages/vlt/build.ncl | 105 +++++++++++++++++++++++++++++++++ packages/vlt/build.sh | 26 ++++++++ packages/vlt/package-lock.json | 31 ++++++++++ packages/vlt/package.json | 8 +++ 4 files changed, 170 insertions(+) create mode 100644 packages/vlt/build.ncl create mode 100755 packages/vlt/build.sh create mode 100644 packages/vlt/package-lock.json create mode 100644 packages/vlt/package.json diff --git a/packages/vlt/build.ncl b/packages/vlt/build.ncl new file mode 100644 index 00000000..3054a512 --- /dev/null +++ b/packages/vlt/build.ncl @@ -0,0 +1,105 @@ +# Imported from npm `vlt` by `pkgmgr import npm` (node build). +# +# vlt is a package manager (vlt.sh), shipped as a single dependency-free npm +# tarball: `npm ci` installs exactly ONE package, pinned by version + integrity +# in the committed package-lock.json. No transitive tree, no native addons, no +# compile step — the whole build is an extract. +# +# Deliberately NO `source_provenance`. vlt's vuln identity is its npm package +# name, and minimal-supply-chain routes a node-flavored package with no +# provenance to `pkg:npm/` (scan.rs `npm_purl_for_node_package`) so the +# OSV/GHSA npm advisories resolve. Declaring `GithubRepo vltpkg/vltpkg` would +# take it OFF that arm onto the repo-purl arm, where GHSA's npm advisories — +# which are keyed by package name, not repo — would no longer match. Absent is +# correct here, not an oversight. +let { standaloneTest, Attrs, BuildSpec, Local, Needs, OutputBin, OutputData, Test, .. } = import "minimal.ncl" in +let base = import "../base/build.ncl" in +let node = import "../node/build.ncl" in +let coreutils = import "../coreutils/build.ncl" in +let version = "1.0.1" in +{ + name = "vlt", + build_deps = [ + { file = "build.sh" } | Local, + { file = "package.json" } | Local, + { file = "package-lock.json" } | Local, + base, + node, + ], + runtime_deps = [coreutils, node], + needs = + { + dns = {}, + internet = {}, + } | Needs, + cmd = "./build.sh", + build_args = { + include version, + }, + outputs = { + # Enumerated, not `usr/bin/*`: the checker wants each bin named, and the + # npm registry metadata already lists them exactly, so there is nothing to + # infer. `vlt` is the CLI; the others are the documented shorthands + # (`vlr` = `vlt run`, `vlx` = `vlt exec`, plus their variants). + vlt = { glob = "usr/bin/vlt" } | OutputBin, + vlr = { glob = "usr/bin/vlr" } | OutputBin, + vlrx = { glob = "usr/bin/vlrx" } | OutputBin, + vlx = { glob = "usr/bin/vlx" } | OutputBin, + vlxl = { glob = "usr/bin/vlxl" } | OutputBin, + + libexec = { glob = "usr/libexec/vlt/**", allow_executable = true } | OutputData, + }, + attrs = + { + upstream_version = version, + license_spdx = "BSD-2-Clause-Patent", + } | Attrs, + + tests = { + smoketest = standaloneTest "/bin/vlt --version", + + version_is_exact = + { + class = 'Standalone, + test_deps = [base], + cmds = [ + # Exact equality, NOT `--version | grep '%{version}'`: vlt's usage + # banner also carries the version ("next-gen package management + # v1.0.1"), so a grep would pass on a build where `--version` itself + # is broken and only usage prints. This asserts the artifact's own + # version, independent of the build-time lockfile guard in build.sh. + ["/bin/bash", "-c", "test \"$(/bin/vlt --version)\" = \"%{version}\""], + ], + } + | Test, + + companion_bins_work = + { + class = 'Standalone, + test_deps = [base], + cmds = [ + # Five bins are declared as outputs; they are PATH symlinks into the + # private libexec prefix, so a broken relative link fails here and + # nowhere else. A test that only exercised `vlt` would ship four + # dead entries in usr/bin. + ["/bin/bash", "-c", "for b in vlr vlrx vlx vlxl; do test \"$(/bin/$b --version)\" = \"%{version}\" || { echo \"$b failed\" >&2; exit 1; }; done"], + ], + } + | Test, + + reads_a_manifest = + { + class = 'Standalone, + test_deps = [base], + cmds = [ + # Real work, offline: `vlt pkg get` parses a package.json and returns + # a field. This exercises the bundled JS beyond argv handling — a + # truncated or partially-installed tarball prints `--version` fine + # and fails here. vlt has NO default registry as of 1.0.1, so any + # network-touching command is not a candidate for a sandbox test. + ["/bin/bash", "-c", "d=/tmp/vlt-selftest; mkdir -p \"$d\"; cd \"$d\"; printf '{\"name\":\"vlt-selftest-fixture\",\"version\":\"9.9.9\"}' > package.json; test \"$(/bin/vlt pkg get name)\" = '\"vlt-selftest-fixture\"'"], + ], + } + | Test, + }, +} | BuildSpec diff --git a/packages/vlt/build.sh b/packages/vlt/build.sh new file mode 100755 index 00000000..62cb3421 --- /dev/null +++ b/packages/vlt/build.sh @@ -0,0 +1,26 @@ +#!/bin/sh +# Imported from npm `vlt` (1.0.1, node) by pkgmgr import npm. +set -eu + +# Reproducible install: `npm ci` from the committed package-lock.json pins +# the WHOLE transitive tree by version + integrity (a bare `npm install` +# re-resolves it at build time). Install into a package-PRIVATE libexec +# prefix (NOT the shared usr/lib/node_modules the node runtime owns) and +# expose bins as PATH symlinks; the inner `#!/usr/bin/env node` shebang is +# served by coreutils(env)+node, so no shell wrapper is needed. +# Guard: the committed lock must pin the build.ncl version (the updater +# regenerates it on a bump; this catches a hand-edited version drift). +grep -qF "\"$MINIMAL_ARG_VERSION\"" package.json || + { echo "package.json does not pin $MINIMAL_ARG_VERSION — regenerate the lockfile" >&2; exit 1; } +prefix="$OUTPUT_DIR/usr/libexec/vlt" +mkdir -p "$prefix" +cp package.json package-lock.json "$prefix/" +cd "$prefix" +npm ci --omit=dev + +mkdir -p "$OUTPUT_DIR/usr/bin" +for _bin in node_modules/.bin/*; do + [ -e "$_bin" ] || continue + _tool=${_bin##*/} + ln -s "../libexec/vlt/node_modules/.bin/$_tool" "$OUTPUT_DIR/usr/bin/$_tool" +done diff --git a/packages/vlt/package-lock.json b/packages/vlt/package-lock.json new file mode 100644 index 00000000..8f99dcd0 --- /dev/null +++ b/packages/vlt/package-lock.json @@ -0,0 +1,31 @@ +{ + "name": "pkgmgr-node-import", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "pkgmgr-node-import", + "version": "0.0.0", + "dependencies": { + "vlt": "1.0.1" + } + }, + "node_modules/vlt": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/vlt/-/vlt-1.0.1.tgz", + "integrity": "sha512-+EVCj7/jfTsotOQjrOvteDQSLgTVYu005eslkaAypPYBUdZ+AN8Y3cHk6F100l9mFrvdN3d99ypmPYSv2++FLA==", + "license": "BSD-2-Clause-Patent", + "bin": { + "vlr": "vlr.js", + "vlrx": "vlrx.js", + "vlt": "vlt.js", + "vlx": "vlx.js", + "vlxl": "vlxl.js" + }, + "engines": { + "node": ">=22.22.0" + } + } + } +} diff --git a/packages/vlt/package.json b/packages/vlt/package.json new file mode 100644 index 00000000..74257484 --- /dev/null +++ b/packages/vlt/package.json @@ -0,0 +1,8 @@ +{ + "name": "pkgmgr-node-import", + "version": "0.0.0", + "private": true, + "dependencies": { + "vlt": "1.0.1" + } +} From 954ba65dc8ba7e9449d2427fefb54dfcb7afe921 Mon Sep 17 00:00:00 2001 From: bryan Date: Tue, 4 Aug 2026 16:04:56 -0700 Subject: [PATCH 2/3] vlt: build and run against node-lts, not node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follows Tom's review on #370 — "I dont think a global `node_modules` is gonna go so well, particularly if someone uses `node` vs `node-lts`". The collision half of that was already handled: vlt installs into a private `usr/libexec/vlt` prefix with relative PATH symlinks, never the shared `usr/lib/node_modules` the node runtime owns. But the variant question the comment raises was still answered by the importer's hardcoded default rather than by evidence. The evidence says LTS: - vlt's own CI tests Node 22.x ONLY (`node-version: '^22.22.0'` and `[22.x]`); `engines` says `>=22.22.0`. Our node-lts is 24.14.1 and node is 25.8.2 — neither is 22.x, but LTS is two majors closer to what upstream actually tests against. - pnpm is the closest analogue in this repo (a package manager, a node CLI, same shape of package) and it is on node-lts, per #97/#98: the node most users and hosting providers run is whatever ships with current LTS. node-lts ships its own npm, so it serves the build (`npm ci`) and the runtime (`#!/usr/bin/env node`) both — no split between build_deps and runtime_deps. Rebuilt and re-checked on the min path: 15/15 Pass. --- packages/vlt/build.ncl | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/vlt/build.ncl b/packages/vlt/build.ncl index 3054a512..b298ea81 100644 --- a/packages/vlt/build.ncl +++ b/packages/vlt/build.ncl @@ -5,6 +5,17 @@ # in the committed package-lock.json. No transitive tree, no native addons, no # compile step — the whole build is an extract. # +# `node-lts`, not `node`, for both build and runtime. Two reasons, and the +# private `usr/libexec/vlt` prefix below is the third leg of the same argument +# (gominimal/pkgs#370: a global `usr/lib/node_modules` install collides with +# whichever node variant the user actually has): +# - vlt's own CI tests Node 22.x only (`node-version: '^22.22.0'`, `[22.x]`) +# and `engines` says `>=22.22.0`. Our node-lts is 24.14.1, node is 25.8.2 — +# neither is 22.x, but LTS is two majors closer to what upstream tests. +# - pnpm, the closest analogue in this repo (also a package manager, also a +# node CLI), is on node-lts for exactly this reason — see #97/#98: the node +# most users and hosting providers run is whatever ships with current LTS. +# # Deliberately NO `source_provenance`. vlt's vuln identity is its npm package # name, and minimal-supply-chain routes a node-flavored package with no # provenance to `pkg:npm/` (scan.rs `npm_purl_for_node_package`) so the @@ -14,7 +25,7 @@ # correct here, not an oversight. let { standaloneTest, Attrs, BuildSpec, Local, Needs, OutputBin, OutputData, Test, .. } = import "minimal.ncl" in let base = import "../base/build.ncl" in -let node = import "../node/build.ncl" in +let node-lts = import "../node-lts/build.ncl" in let coreutils = import "../coreutils/build.ncl" in let version = "1.0.1" in { @@ -24,9 +35,9 @@ let version = "1.0.1" in { file = "package.json" } | Local, { file = "package-lock.json" } | Local, base, - node, + node-lts, ], - runtime_deps = [coreutils, node], + runtime_deps = [coreutils, node-lts], needs = { dns = {}, From 6b6b8aaf7ce2e67a9ee9063c084e5e994613f6e1 Mon Sep 17 00:00:00 2001 From: bryan Date: Tue, 4 Aug 2026 16:25:25 -0700 Subject: [PATCH 3/3] vlt: record why this is a registry artifact, not a source build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AGENTS.md requires a prebuilt fallback be called out explicitly. The reason here is not "nobody tried" — source IS published (7.4 MB tarball, 48 real TypeScript workspaces), it just cannot be built with a toolchain we have. Measured against the extracted tarball: npm -> EUNSUPPORTEDPROTOCOL: Unsupported URL Type "catalog:" pnpm -> ERR_PNPM_CATALOG_ENTRY_NOT_FOUND_FOR_SPEC Dependency specs use the `catalog:` protocol with the catalog declared in `vlt.json`; pnpm supports catalogs but reads pnpm-workspace.yaml. The only lockfile is `vlt-lock.json` (vlt's own format), and `package.json` has no `workspaces` key, so neither tool can see the monorepo layout. Upstream CI agrees: setup-vlt@v1 then `vlt install`. The published package declares `bin: null`, no deps, and one script — `prepack: vlt-build-prepack`. Building vlt from source requires vlt. That bootstrap cycle is the "required toolchain genuinely isn't packaged yet" case AGENTS.md carves out; breaking it means a two-stage vlt-bootstrap -> vlt pair in the gawk-bootstrap shape, worth doing if vlt becomes load-bearing and not before. Comment-only. Rebuilt and re-checked: 15/15 Pass. --- packages/vlt/build.ncl | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/packages/vlt/build.ncl b/packages/vlt/build.ncl index b298ea81..66ee451e 100644 --- a/packages/vlt/build.ncl +++ b/packages/vlt/build.ncl @@ -5,6 +5,30 @@ # in the committed package-lock.json. No transitive tree, no native addons, no # compile step — the whole build is an extract. # +# WHY A REGISTRY ARTIFACT AND NOT A SOURCE BUILD — AGENTS.md requires this be +# called out explicitly, and the reason is not "nobody tried". Source IS +# published (codeload .../vltpkg/tar.gz/refs/tags/v1.0.1, 200, 7.4 MB, 48 real +# TypeScript workspaces). It cannot be built with a toolchain we have: +# +# - The only lockfile in the repo is `vlt-lock.json` — vlt's own format. +# There is no pnpm-lock.yaml and no package-lock.json. +# - Dependency specs use the `catalog:` protocol with the catalog declared in +# `vlt.json`. Measured against the extracted tarball: +# npm → EUNSUPPORTEDPROTOCOL: Unsupported URL Type "catalog:" +# pnpm → ERR_PNPM_CATALOG_ENTRY_NOT_FOUND_FOR_SPEC (pnpm supports +# catalogs, but reads pnpm-workspace.yaml, not vlt.json) +# `package.json` has no `workspaces` key at all, so neither tool can even +# see the monorepo layout. +# - Upstream CI agrees: `uses: vltpkg/setup-vlt@v1` then `vlt install`. +# - The published package (`infra/cli/package.json`) declares `bin: null`, +# `dependencies: {}`, and one script — `prepack: vlt-build-prepack`. The +# npm tarball is generated wholesale by their internal bundler. +# +# So building vlt from source requires vlt: a genuine bootstrap cycle, which is +# the "required toolchain genuinely isn't packaged yet" case AGENTS.md carves +# out. Breaking it would mean a two-stage `vlt-bootstrap` → `vlt` pair in the +# gawk-bootstrap shape; worth doing if vlt becomes load-bearing, not before. +# # `node-lts`, not `node`, for both build and runtime. Two reasons, and the # private `usr/libexec/vlt` prefix below is the third leg of the same argument # (gominimal/pkgs#370: a global `usr/lib/node_modules` install collides with