From 30101f2455bf320b1df33eea630d6989c93088e9 Mon Sep 17 00:00:00 2001 From: tonythethompson Date: Wed, 1 Jul 2026 21:55:03 -0700 Subject: [PATCH 1/2] docs: add ADR 0001 (ecosystem trust, upstream contribution, fork stewardship) Formalizes how Numan handles third-party plugins, modules, scripts, completions, and nupm-discovered packages across all sources (official registry, custom registries, nupm) -- not just the curated official registry. Evaluated against the current codebase before writing this up, not transcribed blind: - Confirmed the "Registry Trust Requirements" section describes already-implemented behavior (RegistryManager's atomic index promote + last-known-good fallback in src/core/registry.rs), not a new ask. Recorded that explicitly so this ADR doesn't read as a TODO where the work is done. - Confirmed no contradiction with existing CLAUDE.md invariants (install is inert, activate is the only Nu-touching command, Ed25519 signatures, mandatory plugin SHA-256, lockfile snapshots). - Confirmed the package-status taxonomy here is orthogonal to, not a duplicate of, docs/nupm-compatibility.md's NupmOutcome taxonomy -- one is "can we import this nupm package," the other is "what's our trust/maintenance relationship with its origin." Added a cross-reference in nupm-compatibility.md so the two don't get conflated by a future reader. - Confirmed the inspect/audit/provenance UX and metadata schema are genuinely new work -- no such commands exist in src/cmd/ today. - Cited nushell/nu_scripts#1265 (opened this session, fixing a broken seed-package candidate upstream instead of forking it) as a live example of Lane 1 already working in practice. Status: Proposed. Implementation priorities in the ADR sequence the metadata schema, inspect/audit UX, and patch/fork workflows as separate follow-up work, not part of this change. --- ...-upstream-contribution-fork-stewardship.md | 289 ++++++++++++++++++ docs/nupm-compatibility.md | 2 + 2 files changed, 291 insertions(+) create mode 100644 docs/adr/0001-ecosystem-trust-upstream-contribution-fork-stewardship.md diff --git a/docs/adr/0001-ecosystem-trust-upstream-contribution-fork-stewardship.md b/docs/adr/0001-ecosystem-trust-upstream-contribution-fork-stewardship.md new file mode 100644 index 00000000..c06140f8 --- /dev/null +++ b/docs/adr/0001-ecosystem-trust-upstream-contribution-fork-stewardship.md @@ -0,0 +1,289 @@ +# ADR 0001: Ecosystem Trust, Upstream Contribution, and Fork Stewardship Policy + +## Status + +Proposed. + +## Context + +Nushell's ecosystem has fragmented discovery, uneven maintenance, weak +provenance conventions, and no single authoritative package registry. Users +encounter packages through Git repositories, community lists (e.g. +`awesome-nu`), crates.io, `nupm` installations, copied scripts, or abandoned +repositories. Because Nu plugin registration can execute plugin binaries, +package discovery and activation are supply-chain decisions, not merely +convenience features. + +Numan must not solve this by silently becoming the owner of every broken +package it touches. Its role is to make provenance, compatibility, +integrity, maintenance status, and risk visible and enforceable — not to +absorb unbounded maintenance burden. + +This ADR governs **all** package sources Numan can discover or install +from — the official registry, custom registries, and `nupm`-discovered +packages — not just `tonythethompson/numan-registry`. It is a client-side +(`tonythethompson/numan`) policy document. + +### Relationship to existing work + +This formalizes and extends invariants and mechanisms that already exist: + +- **Already implemented, unchanged by this ADR:** + - Install is inert; only `activate` touches Nu (`CLAUDE.md` Critical Rule 1). + - Registry signatures are mandatory (Ed25519, `src/core/official_registry.rs`, + `src/core/registry.rs`). + - Artifact SHA-256 is mandatory for plugin artifacts (Critical Rule 4). + - Lockfile snapshots before mutation; activation uses journal recovery + (Critical Rule 5, Phase 3). + - Registry sync already does atomic index promotion with last-known-good + fallback on verification failure + (`RegistryManager::last_known_good_index_path`, + `load_last_known_good`, `src/core/registry.rs`) — the "Registry Trust + Requirements" section below records this as a standing requirement + rather than proposing new behavior. +- **A distinct, narrower layer this ADR does not replace:** + `docs/nupm-compatibility.md` owns the *mechanics* of importing one + specific source format (`nupm.nuon` parsing, `NupmOutcome`, + `NupmReasonCode`, import provenance in `state/nupm-imports.json`). That + document answers "can this nupm-format package be imported at all." This + ADR answers an orthogonal question that applies regardless of source + format: "what is Numan's trust and maintenance relationship with this + package's origin." A package can simultaneously be `importable_now` in + nupm-compatibility terms and `unreviewed discovery result` in this ADR's + terms — the two taxonomies compose, they don't collapse into one. +- **Not yet implemented — genuinely new work:** + the package status taxonomy, the metadata model fields (`fork_of`, + `patches`, `maintenance_status`, etc.), and the `inspect` / `audit` / + `provenance` / `compatibility` UX surface. `src/cmd/info.rs` is the + closest existing analog and is the natural extension point; there is no + `audit`, `provenance`, or `compat` command today. + +### A concrete instance of the policy this ADR describes + +While evaluating `nushell/nu_scripts` as a source for a registry seed +package (`tonythethompson/numan-registry` PR #2), `modules/maths/math_functions.nu` +was found broken on current Nu (deprecated `merge` closure syntax, plus a +call to an undefined `column2` helper only ever defined in an unrelated +file). Rather than patching a local copy or forking, the fix was pushed +upstream: [nushell/nu_scripts#1265](https://github.com/nushell/nu_scripts/pull/1265). +This is Lane 1 (Upstream contribution) from this ADR, exercised before the +ADR existed — evidence the policy matches practice that already works, +not just a theoretical preference. + +## Decision + +Adopt the following policy. + +### Core principle + +Separate **verification**, **compatibility work**, and **maintenance +ownership**. Verification is evidence. Maintenance is responsibility. +Numan must never use a vague label like "approved" that could imply an +unsupported security or quality guarantee — package records use factual +status labels only. + +### Package status classes + +1. **Upstream tracked** — original upstream source and release/commit, no + Numan source changes, installable only with clear provenance metadata. +2. **Numan verified upstream artifact** — provenance, integrity, license, + Nu-version compatibility, supported platforms, and CI evidence + recorded. Numan does not claim upstream source is security-audited and + does not own upstream maintenance. +3. **Numan compatibility-patched** — a small, explicit, reviewable + patchset against a pinned upstream revision, normally corresponding to + an open upstream issue/PR. A temporary bridge, not a hidden permanent + fork. Every downstream change is visible and reproducible. +4. **Numan maintained fork** — a distinct Numan-owned distribution, used + only where Numan has deliberately accepted ongoing responsibility for + compatibility, releases, security response, and support. Must never + silently replace an installation request for the original upstream + package. +5. **Known incompatible** — real, identified package that doesn't work + with a specified Nu version, platform, or Numan safety requirement, + with the reason and scope stated. +6. **Unreviewed discovery result** — surfaced but not endorsed; must never + be presented alongside verified artifacts without obvious + differentiation. +7. **Quarantined or unsupported** — unclear license, unavailable source, + insufficient provenance, unsafe behavior, unacceptably stale + maintenance, or validation is not reproducible. Numan explains why it + is not installable through trusted flows. + +### Boundary: upstream fix vs. Numan patch or fork + +Default rule: **push the fix upstream first.** + +Upstream owns product behavior: plugin command behavior, parsing +correctness, data handling, user-facing semantics, plugin functionality, +UX defects, project-specific build logic where upstream supports the +affected platform. + +Numan owns distribution and compatibility behavior: package metadata, +registry integrity, source provenance, artifact hashes, platform +packaging, reproducible build recipes, Nu-version compatibility +declarations, activation safety, lockfile/rollback behavior, installation +layout, machine-readable compatibility evidence. + +Examples: + +- A plugin command mishandles JSON → submit an upstream fix. +- A healthy upstream package lacks metadata needed for safe Numan + installation → solve it in Numan packaging metadata. +- A plugin doesn't compile against a current Nu plugin protocol → + upstream PR first; Numan patch only when users are blocked and the + patch is bounded. +- A package can't support a platform because upstream intentionally + doesn't → don't assume Numan should take ownership; evaluate whether + that platform is strategically worth maintaining. + +### Decision lanes + +- **Lane 1 — Upstream contribution.** Upstream is active/reachable, the + change fits the project's purpose, the patch is likely accepted, + waiting for review/release doesn't materially block users. Numan + distributes the upstream release or an explicit pinned commit. +- **Lane 2 — Temporary compatibility patch.** Concrete user-facing + breakage exists, an upstream issue/PR has been opened (unless urgency + or security makes that impractical), the patch is narrow/testable/ + reversible, users need a working package before upstream merges, Numan + can build and test reproducibly. Must record `upstream`, + `upstream_revision`, `patch_status: temporary`, `patches`, + `upstream_issue`, `exit_condition`. Never silently substituted for + upstream — the user must be able to inspect the exact patch and choose + the upstream artifact. +- **Lane 3 — Numan maintained fork.** Only when *all* of: (1) concrete + meaningful breakage or unmet need; (2) upstream abandoned, + unresponsive, unable to release, rejects the necessary direction, or + structurally unable to support the outcome; (3) license permits + redistribution/modification/ongoing maintenance; (4) bounded initial + scope with a credible maintenance plan; (5) Numan can build, test, + sign, and release for every claimed platform; (6) Numan accepts + ownership of Nu protocol changes, dependency CVEs, CI failures, bug + reports, and releases over time; (7) maintaining it is cheaper/safer + than repeatedly asking users to repair it manually. Governing question: + *"Are we willing to be responsible for this package when Nu changes, + when its dependencies receive CVEs, and when users report breakage two + years from now?"* If no, do not fork. +- **Lane 4 — Unsupported.** Source provenance can't be established, + licensing is unclear, source can't be built/reviewed, artifact can't + meet integrity/reproducibility requirements, unsafe to activate, or + maintenance cost is disproportionate to value. Surfaced only as an + explicitly unsupported discovery item, with reasons. + +### Fork identity and provenance + +A maintained or patched package must never impersonate upstream identity. +Use a separate distribution identity (e.g. `upstream: owner/plugin`, +`distribution: numan-compat/owner-plugin`, `fork_of: github.com/owner/plugin@`). +Installing `owner/plugin` must never silently install +`numan-compat/owner-plugin`. + +Inspection output and machine-readable metadata must distinguish: original +source repository and revision/tag; Numan distribution identity; fork/patch +status; exact patchset; signer; artifact SHA-256; build recipe and +environment fingerprint where available; Nu compatibility range; tested +platforms; test date; known risks; maintainer and escalation path; exit +condition for temporary patches. + +### Metadata model (versioned, extensible) + +At minimum: `source_url`, `source_revision`, `release_tag`, `license`, +`publisher_identity`, `artifact_sha256`, `registry_signer`, +`nu_version_range`, `tested_platforms`, `last_verified_at`, +`maintenance_status`, `fork_of`, `patches`, `upstream_issue`, +`build_recipe`, `build_environment_fingerprint`, `review_notes`, +`known_risks`, `maintainers`, `support_scope`, `exit_condition`. + +### User-facing UX + +Users must be able to answer, before installing or activating: where did +this come from; is it upstream, patched, forked, or merely discovered; who +signed the registry entry and artifact metadata; what source revision +produced this artifact; does it work with my Nu version/platform; what did +Numan modify; is this temporary or a maintained fork; what known risks +exist; who owns future maintenance. This information must not be buried in +verbose output or external docs only — extend `numan info` (or add +`inspect` / `audit` / `provenance` / `compatibility`) to surface it +directly. + +### Registry trust requirements (standing requirement, largely already met) + +Registry sync must: download index+signature to temporary paths; require a +signature for trusted registries; verify against the configured Ed25519 +trust key over the exact downloaded bytes; parse and validate; atomically +promote into the local cache; preserve the prior verified cache on any +failure; treat unsigned use as an explicit, narrowly-scoped +development-only escape hatch. Never write an unverified index into the +active cache and verify later. + +### Governance + +Every Numan-maintained fork requires a stewardship record (e.g. +`MAINTAINERS.md`) stating: why the fork exists; original upstream and +revision; why upstream contribution was insufficient; named maintainers; +supported Nu versions/platforms; CI/release evidence; known technical +debt; security update responsibility; review cadence; upstreaming or +retirement exit condition. + +## Implementation Priorities + +Do not begin by creating forks. Sequence: + +1. This ADR and status taxonomy. +2. Versioned package metadata schema. +3. `inspect` / `audit` output exposing provenance and maintenance state. +4. Atomic verified registry sync (already implemented — confirm coverage + and add regression tests tied explicitly to this ADR). +5. CI evidence capture for verified artifacts. +6. Temporary compatibility-patch workflow. +7. Maintained-fork workflow, only after the above exists. +8. Discovery-only and quarantined-package handling for `nupm` + interoperability (composes with, does not replace, + `docs/nupm-compatibility.md`). + +## Consequences + +**Positive:** + +- A user can tell whether an artifact is upstream, patched, forked, or + unreviewed before installation. +- Numan never silently substitutes a fork for an upstream package. +- Every Numan patch or fork traces to an upstream source revision and + explicit downstream changes. +- Numan doesn't claim more security assurance than its evidence supports. +- The default path for ordinary bugs remains an upstream contribution, not + a Numan fork — bounding long-term maintenance burden. +- Numan becomes a trustworthy compatibility/stewardship layer rather than + an uncontrolled collection of copied packages. + +**Negative / costs:** + +- A new metadata schema and UX surface (`inspect`/`audit`/`provenance`) is + real, unscoped implementation work — this ADR sequences it but doesn't + reduce it. +- Two taxonomies now exist side by side (this ADR's package status classes + vs. `nupm-compatibility.md`'s `NupmOutcome`) — future contributors need + to understand both apply, to different questions, and don't collapse + into one field. +- Lane 3 (maintained fork) is deliberately high-friction by design; if a + genuinely important package needs forking, this policy will slow that + down rather than speed it up. That's the intended trade-off, not an + oversight. + +## Acceptance Criteria + +- A user can tell whether an artifact is upstream, patched, forked, or + unreviewed before installation. +- Numan never silently substitutes a fork for an upstream package. +- Every Numan patch or fork can be traced to an upstream source revision + and explicit downstream changes. +- Numan does not claim more security assurance than its evidence supports. +- Registry indexes are cryptographically verified before becoming active + cache state. +- A maintained fork exists only where Numan has consciously accepted + ongoing maintenance responsibility. +- The default path for ordinary bugs remains an upstream contribution, not + a Numan fork. +- Numan becomes a trustworthy compatibility and stewardship layer rather + than an uncontrolled collection of copied packages. diff --git a/docs/nupm-compatibility.md b/docs/nupm-compatibility.md index 55edcae2..8eec465b 100644 --- a/docs/nupm-compatibility.md +++ b/docs/nupm-compatibility.md @@ -8,6 +8,8 @@ This document is the **versioned compatibility contract** for Numan's nupm inter **Phase 6.1 ships:** read-only `numan nupm status` and `numan nupm inspect`. Phase 6.2 adds `numan nupm import`. Phase 6.3 adds drift detection, bulk manifest import, and activation verification. +**Relationship to [ADR 0001](adr/0001-ecosystem-trust-upstream-contribution-fork-stewardship.md):** this document owns the *mechanics* of importing one specific source format — `nupm.nuon` parsing, `NupmOutcome`/`NupmReasonCode`, and import provenance. ADR 0001 owns the orthogonal question of trust and maintenance relationship (upstream / patched / forked / unreviewed / quarantined) that applies across all package sources, nupm included. A package's `NupmOutcome` and its ADR 0001 status class are independent fields, not the same taxonomy. + --- ## Pinned nupm revision From db490f19efb96be9cfa25a74e1f78cf2eed76acd Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 2 Jul 2026 05:23:09 +0000 Subject: [PATCH 2/2] docs: correct ADR registry trust references Co-authored-by: Anthony Thompson --- ...-upstream-contribution-fork-stewardship.md | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/docs/adr/0001-ecosystem-trust-upstream-contribution-fork-stewardship.md b/docs/adr/0001-ecosystem-trust-upstream-contribution-fork-stewardship.md index c06140f8..935ecd2c 100644 --- a/docs/adr/0001-ecosystem-trust-upstream-contribution-fork-stewardship.md +++ b/docs/adr/0001-ecosystem-trust-upstream-contribution-fork-stewardship.md @@ -26,21 +26,23 @@ packages — not just `tonythethompson/numan-registry`. It is a client-side ### Relationship to existing work -This formalizes and extends invariants and mechanisms that already exist: +This separates invariants and mechanisms that already exist from follow-up +requirements this ADR introduces: - **Already implemented, unchanged by this ADR:** - Install is inert; only `activate` touches Nu (`CLAUDE.md` Critical Rule 1). - - Registry signatures are mandatory (Ed25519, `src/core/official_registry.rs`, - `src/core/registry.rs`). + - Ed25519 trust-store and registry verification primitives exist in + `src/core/trust.rs` and `src/core/registry.rs` + (`RegistryManager::verify_and_load`, `RegistryManager::load_verified`). - Artifact SHA-256 is mandatory for plugin artifacts (Critical Rule 4). - Lockfile snapshots before mutation; activation uses journal recovery (Critical Rule 5, Phase 3). - - Registry sync already does atomic index promotion with last-known-good - fallback on verification failure - (`RegistryManager::last_known_good_index_path`, - `load_last_known_good`, `src/core/registry.rs`) — the "Registry Trust - Requirements" section below records this as a standing requirement - rather than proposing new behavior. +- **Not yet implemented — follow-up requirement from this ADR:** + atomic verified registry sync that downloads index+signature to temporary + paths, verifies/parses the downloaded bytes, and preserves the active cache + on failure. Today `numan registry sync` is implemented in + `src/cmd/registry.rs` and does not provide that promotion/rollback behavior; + the "Registry Trust Requirements" section below records the target behavior. - **A distinct, narrower layer this ADR does not replace:** `docs/nupm-compatibility.md` owns the *mechanics* of importing one specific source format (`nupm.nuon` parsing, `NupmOutcome`, @@ -51,7 +53,7 @@ This formalizes and extends invariants and mechanisms that already exist: package's origin." A package can simultaneously be `importable_now` in nupm-compatibility terms and `unreviewed discovery result` in this ADR's terms — the two taxonomies compose, they don't collapse into one. -- **Not yet implemented — genuinely new work:** +- **Also not yet implemented — genuinely new work:** the package status taxonomy, the metadata model fields (`fork_of`, `patches`, `maintenance_status`, etc.), and the `inspect` / `audit` / `provenance` / `compatibility` UX surface. `src/cmd/info.rs` is the @@ -207,7 +209,7 @@ verbose output or external docs only — extend `numan info` (or add `inspect` / `audit` / `provenance` / `compatibility`) to surface it directly. -### Registry trust requirements (standing requirement, largely already met) +### Registry trust requirements (target behavior, partially implemented) Registry sync must: download index+signature to temporary paths; require a signature for trusted registries; verify against the configured Ed25519 @@ -233,8 +235,7 @@ Do not begin by creating forks. Sequence: 1. This ADR and status taxonomy. 2. Versioned package metadata schema. 3. `inspect` / `audit` output exposing provenance and maintenance state. -4. Atomic verified registry sync (already implemented — confirm coverage - and add regression tests tied explicitly to this ADR). +4. Atomic verified registry sync with active-cache preservation on failure. 5. CI evidence capture for verified artifacts. 6. Temporary compatibility-patch workflow. 7. Maintained-fork workflow, only after the above exists.