Documentation
¶
Overview ¶
Package nodedist fetches a pinned Node.js distribution by version and target platform from nodejs.org, verifies it against Node's GPG-signed SHASUMS256.txt, SHA-256-matches the downloaded archive, unpacks it, and caches the unpacked tree content-addressed under the shared atomic-rename store primitive (cstore.CommitDir).
This package is the supply-chain-verified Node provisioning layer for the sandbox toolchain (issue #1525, sub-issue 1). It is self-contained and deliberately not wired into any build path here — the call site lands in a later sub-issue. The pipeline shape mirrors cstore's connector install:
fetch (cstore.Fetcher) → verify (ChecksumVerifier) → hash-match → unpack → cache (cstore.CommitDir)
Trust property: the cache key is the archive's SHA-256 as published in Node's signed SHASUMS256.txt. The signature is checked first, then the download is matched against the signed hash, then the unpacked tree is stored under that same supply-chain-verified hash. A request whose verified hash is already cached short-circuits without re-downloading the archive.
Index ¶
- func ArchiveName(version string, target Target) (string, error)
- func DefaultKeyring() (openpgp.EntityList, error)
- func DistroName(version string, target Target) (string, error)
- func NodeBinaryPath(entryDir string, target Target) string
- type ChecksumVerifier
- type Checksums
- type Error
- type ErrorKind
- type Fetcher
- type NodeDist
- type Request
- type Target
- type URLs
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ArchiveName ¶
ArchiveName returns the archive filename for a version+target, e.g. "node-v24.2.0-linux-x64.tar.gz" (Unix) or "node-v24.2.0-win-x64.zip" (Windows). This is the exact key that appears in SHASUMS256.txt.
func DefaultKeyring ¶
func DefaultKeyring() (openpgp.EntityList, error)
DefaultKeyring returns the trusted Node release public keyring parsed from the embedded asset. It is the keyring the managed-toolchain provisioner passes to Fetcher.Keyring so a fetched SHASUMS256.txt is only ever trusted after its signature verifies against a known Node release key.
It fails closed: an empty or unparseable asset, or one that yields no entities, returns an error rather than an empty keyring (which would silently accept nothing yet also surface no diagnostic at the call site). A build that embeds a valid asset never hits the error path; the guard exists so a future asset regression is caught at construction rather than at first fetch.
func DistroName ¶
DistroName returns the top-level directory name a Node archive unpacks to, e.g. "node-v24.2.0-linux-x64". The unpack step strips this prefix.
func NodeBinaryPath ¶
NodeBinaryPath returns the absolute path to the node executable inside an unpacked entry for the given target. It is the exported wrapper the managed toolchain's offline resolver (internal/sandbox/toolchain, #1531) calls so it reuses this package's single rule for the `bin/node` (Unix) vs `node.exe` (Windows) layout rather than re-deriving it.
Types ¶
type ChecksumVerifier ¶
type ChecksumVerifier struct {
// Keyring is the set of trusted Node release public keys. A nil or empty
// keyring causes VerifyAndParse to fail closed (every signature is
// untrusted).
Keyring openpgp.EntityList
}
ChecksumVerifier verifies Node's clearsigned SHASUMS256.txt.asc against the trusted keyring and exposes the verified filename → sha256hex entries. Its contract: a checksum is only ever returned after the signature over the clearsigned message has been verified against the trusted keyring, so a caller can never act on an unsigned or tampered checksum.
func (ChecksumVerifier) VerifyAndParse ¶
func (v ChecksumVerifier) VerifyAndParse(asc []byte) (Checksums, error)
VerifyAndParse verifies the clearsigned SHASUMS256.txt.asc against the verifier's keyring, and only on success parses and returns the verified filename → sha256hex map.
Node publishes SHASUMS256.txt.asc as a PGP *clearsigned* document (an inline "BEGIN PGP SIGNED MESSAGE" wrapping the checksum lines and the signature), NOT a detached signature over a separate SHASUMS256.txt. So the checksums that are parsed are the ones embedded in — and covered by — the verified signature; there is no second, unsigned file to trust.
Order matters and is part of the contract: the signature is verified first. On anything but a good signature (not clearsigned, wrong key, tampered body, malformed armor) it returns an ErrBadSignature-kind error and no entries — nothing in the message is trusted. Only after the signature verifies are the embedded lines parsed.
type Checksums ¶
Checksums maps an artifact filename to its lowercase hex SHA-256, as published in a verified SHASUMS256.txt.
type Error ¶
Error is a structured nodedist failure carrying a classification, a human-readable message, and an optional wrapped cause.
type ErrorKind ¶
type ErrorKind string
ErrorKind is the closed-set classification of nodedist failures. It borrows cstore's failure vocabulary in spirit (fetch / checksum / signature / store) while staying local to this package, since the Node flow does not share cstore's connector-specific classes.
const ( // ErrUnsupportedTarget is returned for a GOOS/GOARCH pair this package // has no Node distribution mapping for. ErrUnsupportedTarget ErrorKind = "unsupported_target" // ErrInvalidVersion is an empty or malformed Node version string. ErrInvalidVersion ErrorKind = "invalid_version" // ErrFetchFailed is a download failure for one of the three URLs. ErrFetchFailed ErrorKind = "fetch_failed" // ErrBadSignature is a SHASUMS256.txt.asc that does not verify against // the trusted keyring. No checksum from the file is trusted in this // case. ErrBadSignature ErrorKind = "bad_signature" // ErrChecksumParse is a SHASUMS256.txt body that could not be parsed, or // that does not contain an entry for the requested archive. ErrChecksumParse ErrorKind = "checksum_parse" // ErrChecksumMismatch is a downloaded archive whose SHA-256 does not // match the signed entry in SHASUMS256.txt. Nothing is committed to the // store on this failure. ErrChecksumMismatch ErrorKind = "checksum_mismatch" // ErrUnpack is an archive extraction failure (corrupt archive, zip-slip // path traversal, unexpected layout). ErrUnpack ErrorKind = "unpack_failed" // ErrStore is a failure committing the unpacked tree to the // content-addressed cache. ErrStore ErrorKind = "store_failed" // ErrConfig is a misconfigured Fetcher (nil HTTP, nil keyring, empty // root). ErrConfig ErrorKind = "config_error" )
type Fetcher ¶
type Fetcher struct {
// HTTP downloads the archive and the clearsigned SHASUMS256.txt.asc.
// Required.
HTTP cstore.Fetcher
// Keyring is the set of trusted Node release public keys the checksums
// signature is verified against. Required and must be non-empty;
// verification fails closed otherwise.
Keyring openpgp.EntityList
// Root is the cache subtree root for unpacked Node trees. The store lays
// out entries as `<Root>/sha256/<verified-hash>/`. Required. Callers
// typically pass `filepath.Join(cstore.DefaultRoot(), "node")` so the
// Node cache never collides with the connector store's `connectors/`
// subtree.
Root string
}
Fetcher provisions a verified Node distribution and caches its unpacked tree content-addressed. It ties together the package's units: URL resolution, signature-verified checksums, SHA-256 archive matching, archive unpack, and the shared atomic-rename content-addressed store (cstore.CommitDir).
Fetcher reuses cstore.Fetcher for all three downloads so tests can substitute an in-memory fetcher with no network, exactly as the connector install pipeline does.
func (*Fetcher) Fetch ¶
Fetch provisions the requested Node distribution and returns the cached entry. The pipeline:
- Resolve archive/checksums/signature URLs for version+target.
- Download the clearsigned SHASUMS256.txt.asc.
- Verify the clearsigned message against the keyring, then parse the embedded verified checksums. A bad signature aborts here — no archive is fetched and nothing is committed.
- Look up the selected archive's verified SHA-256.
- Cache short-circuit: if a tree for that hash is already stored, return it WITHOUT downloading the archive.
- Otherwise download the archive and SHA-256 it; reject on a mismatch against the signed value (nothing is committed).
- Unpack into a temp tree (zip-slip guarded) and atomically commit it under the verified hash via cstore.CommitDir.
type NodeDist ¶
type NodeDist struct {
// Version is the resolved version (no leading "v").
Version string
// EntryDir is the absolute path to the cached unpacked tree
// (`<Root>/sha256/<hash>/`). On Unix the layout has `bin/node`; on
// Windows `node.exe` at the root.
EntryDir string
// SHA256 is the verified hex SHA-256 of the source archive — the cache
// key. Equal to the entry's SHASUMS256.txt value.
SHA256 string
// NodeBinary is the absolute path to the node executable inside
// EntryDir.
NodeBinary string
// FromCache is true when the request was served from the
// content-addressed cache without re-downloading the archive.
FromCache bool
}
NodeDist describes a provisioned (cached) Node distribution.
type Request ¶
type Request struct {
// Version is the Node version, with or without a leading "v" (e.g.
// "24.2.0" or "v24.2.0"). Required.
Version string
// Target is the platform to fetch for. When zero-valued, the running
// host's GOOS/GOARCH is used.
Target Target
}
Request selects which Node distribution to provision.
type Target ¶
Target identifies the platform a Node distribution is built for, using Go's GOOS/GOARCH vocabulary at the boundary. The mapping to Node's own os/arch naming (e.g. linux/amd64 → linux-x64) is internal to this package (see nodePlatform).
type URLs ¶
type URLs struct {
// Archive is the platform-specific distribution archive.
Archive string
// Checksums is the SHASUMS256.txt covering every artifact for the
// version. Resolved for reference; verification uses the clearsigned
// Signature below, which embeds these checksums.
Checksums string
// Signature is the clearsigned SHASUMS256.txt.asc — a PGP "SIGNED MESSAGE"
// that embeds the checksum lines inside the signed body (not a detached
// signature over a separate file).
Signature string
}
URLs holds the three nodejs.org URLs needed to fetch and verify one distribution.