S00-011: Verify public npm access and exact-version tarball integrity

Changes: Add a scoped fresh-cache registry probe, safe reproduction instructions and artifact ignore rule; record actual TLS-enabled metadata/download results and review handoff without selecting a stack or changing the harness.
Validation: Original npm view command and verify.py returned exit 0; react@19.3.1 tarball (26394 bytes) matched registry SHA-512. make check passed validation and 66 tooling tests. git diff --check passed. App install/build and different-harness connectivity remain unverified.

Task: S00-011
Agent: auto-ci-cd-d105e8027730
Role: ci-cd
Action: claim
Status: review

Task title: Restore verified npm registry access for the stack spike
This commit is contained in:
wt committed 2026-10-15 01:10:10 +07:00
1 parent b8afc70e5f
commit e4672fba91
10 files changed
+264 -8

No files matched your search

@@ -0,0 +1 @@
artifacts/
@@ -0,0 +1,79 @@
# Verified public npm registry path — S00-011
Input: S00-002 failed to resolve registry.npmjs.org. Output: a repeatable public
metadata and exact-version tarball probe in the current executor harness.
This is connectivity evidence, not a framework choice or an application build.
## Execution environment and reproduction
Verified in the fresh auto-ci-cd-d105e8027730 executor on 2026-10-15:
Node v26.3.0, npm 11.16.0, Python 3, macOS paths. These are observed tools,
not accepted project runtime versions. The supplied harness has network access
enabled, workspace-write sandboxing and approval policy never. No harness,
runner, DNS, proxy, certificate or Git configuration was changed by this task.
The initial working tree contained only the coordinator's state/tasks.json claim.
The next fresh executor must retain network-enabled execution, working DNS and
HTTPS egress to registry.npmjs.org:443, normal certificate trust, and project-only
source writes with temporary writes under /private/tmp. No credentials are needed.
Do not disable TLS or relax the write sandbox. The previous DNS failure was not
reproduced here; its external correction mechanism is unknown. This report does
not promise network availability in a different or future harness.
From the project root:
```sh
npm view react version license engines --json --cache /private/tmp/luma-stack-npm-cache --fetch-retries=0 --fetch-timeout=15000
python3 experiments/0001-stack/registry-access/verify.py
```
The first command exactly repeats the original failure. It returned exit 0:
```json
{"version":"19.3.1","license":"MIT","engines":{"node":">=0.10.0"}}
```
`npm config get strict-ssl` returned `true` (exit 0). The second command uses a
new TemporaryDirectory under /private/tmp with empty user/global npmrc files
and a fresh cache, runs from there to avoid project npmrc, and removes that
temporary directory after execution. It refuses NODE_TLS_REJECT_UNAUTHORIZED=0.
It invokes these argument arrays via subprocess, without a shell:
```text
npm view react@19.3.1 version license engines dist --json <common-options>
npm pack react@19.3.1 --ignore-scripts --json --pack-destination=<absolute-scope>/artifacts <common-options>
```
The exact common options are:
```text
--registry=https://registry.npmjs.org/ --strict-ssl=true
--fetch-retries=0 --fetch-timeout=15000
--cache=<fresh-temp>/cache --userconfig=<fresh-temp>/user.npmrc
--globalconfig=<fresh-temp>/global.npmrc
```
The probe installs no dependencies, extracts no archive, runs no package scripts
and adds no package manifest/lockfile. React is only a public connectivity sample.
It checks the metadata version and exact HTTPS tarball URL, downloads through
npm, and independently compares Python hashlib SHA-512/base64 with dist.integrity
and npm pack's integrity. It uses standard libraries, not custom cryptography.
Errors/mismatches exit nonzero. Do not print private environment/configuration.
## Actual result
Both child npm commands and verify.py returned exit 0, using the fresh cache:
- Package: react@19.3.1; metadata license MIT.
- URL: https://registry.npmjs.org/react/-/react-19.3.1.tgz
- Retrieved size: 26394 bytes.
- Registry and independently computed integrity:
`sha512-QNhWdt7nrgVWWQG58QuGsAR3jv3Hbe4mb4vz7w2csnHavciZJgda1CPNaldF6eVxv90ud3jUXpKlHL69gO38yw==`
The local `artifacts/` directory contains the tarball, public metadata.json and
result.json; it is deliberately ignored by Git. Rerun the probe for independent
review instead of trusting an archived result. No secrets are recorded. Registry
signature/provenance verification is not claimed; the acceptance check is registry
integrity over verified HTTPS. Browser/CORS, dependency install, build, framework
compatibility and license acceptance remain outside this task. S00-002 must run
its own clean install/build and make its own stack/runtime decision after review.
@@ -0,0 +1,68 @@
#!/usr/bin/env python3
"""Fetch public npm metadata/tarball without installing or running package code."""
import base64
import hashlib
import json
import os
from pathlib import Path
import subprocess
import tempfile
ROOT = Path(__file__).resolve().parent
PACKAGE = "react@19.3.1" # Connectivity sample only; not a stack decision.
def main():
if os.environ.get("NODE_TLS_REJECT_UNAUTHORIZED") == "0":
raise SystemExit("Refusing disabled Node TLS verification")
artifacts = ROOT / "artifacts"
artifacts.mkdir(exist_ok=True)
# Empty configuration files prevent inherited npm credentials/config from
# being needed for this public probe. Never print the process environment.
with tempfile.TemporaryDirectory(prefix="luma-registry-", dir="/private/tmp") as temp:
temp = Path(temp)
for name in ("user.npmrc", "global.npmrc"):
(temp / name).write_text("", encoding="utf-8")
common = [
"--registry=https://registry.npmjs.org/", "--strict-ssl=true",
"--fetch-retries=0", "--fetch-timeout=15000",
f"--cache={temp / 'cache'}", f"--userconfig={temp / 'user.npmrc'}",
f"--globalconfig={temp / 'global.npmrc'}",
]
def npm(args):
# Run outside the project so no project .npmrc is inherited.
result = subprocess.run(["npm", *args, *common], cwd=temp,
check=True, capture_output=True, text=True,
timeout=60)
print("npm " + " ".join(args) + " [isolated TLS/cache options]: exit 0")
return json.loads(result.stdout)
metadata = npm(["view", PACKAGE, "version", "license", "engines", "dist", "--json"])
if metadata["version"] != "19.3.1":
raise SystemExit("Unexpected package version")
if metadata["dist"]["tarball"] != "https://registry.npmjs.org/react/-/react-19.3.1.tgz":
raise SystemExit("Unexpected tarball origin/path")
integrity = metadata["dist"]["integrity"]
algorithm, expected = integrity.split("-", 1)
if algorithm != "sha512":
raise SystemExit("Expected SHA-512 registry integrity")
base64.b64decode(expected, validate=True)
packed = npm(["pack", PACKAGE, "--ignore-scripts", "--json",
f"--pack-destination={artifacts}"])
if len(packed) != 1 or packed[0]["filename"] != "react-19.3.1.tgz":
raise SystemExit("Unexpected pack output")
tarball = artifacts / "react-19.3.1.tgz"
actual = "sha512-" + base64.b64encode(hashlib.sha512(tarball.read_bytes()).digest()).decode("ascii")
if actual != integrity or packed[0]["integrity"] != integrity:
raise SystemExit("Tarball integrity mismatch")
(artifacts / "metadata.json").write_text(json.dumps(metadata, indent=2) + "\n", encoding="utf-8")
result = {"package": PACKAGE, "bytes": tarball.stat().st_size,
"integrity": actual, "registry_integrity_match": True}
print(json.dumps(result, indent=2))
(artifacts / "result.json").write_text(json.dumps(result, indent=2) + "\n", encoding="utf-8")
if __name__ == "__main__":
main()