fix(registry): the CLI reads the flat checksum field — write, document and validate it - #83
Merged
Merged
Conversation
The CLI installer's registry parser (nself-org/cli internal/plugin/registry_parse.go pluginEntry.Checksum, json:"checksum") and installer_locked.go's verifyChecksum() only ever read the FLAT `checksum` field on a plugin entry. build-and-upload-tarballs.sh and registry-schema.json only ever wrote/documented the nested `checksums.sha256` field, which no CLI code path consumes (confirmed via `git grep checksums origin/main -- internal` on nself-org/cli). Result: every plugin without a hand-written flat checksum installs with checksum verification silently skipped by the CLI (a stderr warning only) for any plugin not explicitly status=stable. - build-and-upload-tarballs.sh: write the flat `checksum` field alongside the nested `checksums.sha256` (both raw lowercase hex, no "sha256:" prefix — the previous nested write had the prefix, which would have broken any future checksum == checksums.sha256 consistency check). - registry-schema.json: document `checksum` as the field the CLI reads; require it (non-empty) when a plugin's `status` is the literal string "stable"; document `checksums` as legacy/unread. - shared/validate-registry.sh: new CHECK-15 — errors when a status=stable plugin lacks a valid flat checksum, when a present checksum is not 64 lowercase hex chars, or when checksum and checksums.sha256 disagree. - scripts/verify-published-checksums.sh: new read-only spot-check that downloads a release tag's real tarball assets and hashes them, since the other 121 plugins' checksums were never computed against what the CLI would actually download and recomputing them from the source tree would not verify that. Does not write registry.json. Does not touch the 8 plugin entries PR #81 already populated (both fields, no "sha256:" prefix) for v1.0.1 — rebase-safe with that PR. No plugin in the current registry has an explicit status=stable, so this changes no install-time behavior yet; it fixes the field the installer reads once entries are marked stable.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What / Why
nself plugin install <name>verifies a tarball's checksum against theflat
checksumfield on a registry entry(
internal/plugin/registry_parse.gopluginEntry.Checksum,json:"checksum";enforced by
verifyChecksumininternal/plugin/installer_locked.go).registry-schema.jsonandscripts/build-and-upload-tarballs.shonly everwrote/documented a nested
checksums.sha256field, which no code pathin
nself-org/clireads (git grep checksums origin/main -- internalreturns no registry-entry consumer — only unrelated
checksums.txtrelease-asset handling for the CLI's own binary). Net effect: every plugin
without a hand-populated flat
checksuminstalls with checksumverification silently skipped (a stderr warning only).
Changes
scripts/build-and-upload-tarballs.sh— writes the flatchecksumfield alongside the nested
checksums.sha256(both raw lowercase hex,no
sha256:prefix — the prior nested write had the prefix, whichwould have broken any future
checksum == checksums.sha256check).registry-schema.json— documentschecksumas the field the CLIreads; makes it required (non-empty) when a plugin's
statusis theliteral string
"stable"via anallOf/if/thenconditional;documents
checksumsas legacy/unread by the CLI. Validated withjsonschema.Draft7Validatoragainst the currentregistry.json(0 errors) and against simulated stable-with/without-checksum entries.
shared/validate-registry.sh— new CHECK-15: errors when astatus=stableplugin lacks a valid flat checksum, when a presentchecksum isn't 64 lowercase hex chars, or when
checksumandchecksums.sha256disagree.scripts/verify-published-checksums.sh(new) — read-only spot-checkthat downloads a release tag's real tarball assets and hashes them
directly, since the other 121 plugins' checksums were never computed
against what the CLI actually downloads (recomputing from the source
tree wouldn't verify that — the published tarball's bytes depend on
the exact
tarinvocation at release time). Never writesregistry.json.Not done here (explicitly out of scope)
No checksums were recomputed/written for the other 121 plugins. Only
PR #81's 8 entries (content-progress, cron, donorbox, maintenance,
notifications, notify, search, storage) currently carry a flat checksum,
and this PR does not touch those entries — rebase-safe with #81.
Spot-check: registry vs. real v1.0.0 assets (5 plugins)
scripts/verify-published-checksums.sh v1.0.0 ai-cli alerts api backup audit— downloaded each plugin's real
v1.0.0tarball asset and hashed it:checksumchecksums.sha256ec3c9163...8e6600dcc785e...68363840660cf...aacd6b05b46b1b...ea9bca18e941d...409fabNo registry value to compare against for any of the 5 (none of the 121
untouched plugins have ever had a checksum written) — 0 mismatches,
vacuously. This confirms the script works end-to-end against real
release assets and that these 5 have no stale/wrong checksum sitting in
the registry today.
CLI consumer check (item 5 of the ticket)
git -C <cli-checkout> grep -n "checksums" origin/main -- internalonnself-org/clifinds no registry-entry consumer ofchecksums.sha256—every hit is either the CLI's own release-binary
checksums.txtmechanism (unrelated) or doc/test text. Confirms the nested field is
dead weight for install-time verification; no CLI change needed by this
PR. Separately (bigger finding, not fixed here — flagged to the
manager):
verifyChecksum/verifyPluginSignaturegate on the literalstring
status == "stable", butPublishStatus(json:"status,omitempty")is
""when the field is absent — so despite the registry schema's owncomment ("Missing status defaults to 'stable' for backwards
compatibility") and the lifecycle switch in
installer_locked.goexplicitly treating
""and"stable"the same for warnings, thechecksum/signature enforcement gate does NOT treat absent status as
stable. Every one of this registry's 129 plugins currently has no
explicit
statusfield, so checksum/signature enforcement iseffectively off registry-wide today, independent of this PR's field-name
fix. That's a
clibehavior question (does "" mean stable forenforcement, matching the docs, or not, matching current code?) outside
this PR's scope.
Local gate (self-hosted CI backlog — brief §8, not watched)
bash shared/validate-registry.sh— 0 errors, 1 pre-existing warning(alphabetical sort), same as PR release(prep): v1.2.1 for all 129 free plugins (70 were unpublished; monotonic versions) #81's baseline.
shellcheckon all three touched/added shell scripts — clean.python3 -c "import json; json.load(open(...))"— bothregistry-schema.jsonandregistry.jsonvalid JSON.python3 -c "import jsonschema; jsonschema.Draft7Validator.check_schema(...)"— schema itself is a valid draft-07 schema.
jsonschema.Draft7Validator(schema).iter_errors(registry.json)— 0errors against the live registry, plus simulated stable-without-checksum
(1 error, as expected) and stable-with-checksum (0 errors) cases.
pnpm run ci:local— passes (no tsconfig in this repo, gate no-ops bydesign, same as release(prep): v1.2.1 for all 129 free plugins (70 were unpublished; monotonic versions) #81).
This is a PUBLIC repo — this account cannot self-approve, so left open
per the P6 crunch brief. GitHub-hosted Actions will run automatically;
not watched here per brief §8.