feat(registry): graduate verification and its type/lexicon dependencies to main#2067
Conversation
…es to main Graduates the plugin-registry release-artifact verification package to main as a standalone, coordinated promotion, per the ratified W7.1 decision and today's three-package amendment (sequencing inverted: the labelling branch's W7 needs these on main before the delegated-release-service branch merges). Lifted byte-identical from origin/feat/delegated-release-service: - packages/registry-verification @ 9f429c0 - packages/plugin-types @ 5473b88 (adds src/manifest-schema.ts, src/declared-access.ts and their re-exports -- required by registry-verification's bundle/records modules) - packages/registry-lexicons @ 7549878 (adds the packageProfileExtension lexicon and a release-extension provenance field -- required by registry-verification's records module) Also lifts patches/@sigstore__core@4.0.1.patch and its pnpm-workspace.yaml patchedDependencies entry (a repo-wide patch). The patch makes @sigstore/core's verify() derive the hash algorithm from the public key type when the algorithm is omitted and reject unsupported keys, instead of passing undefined through to crypto.verify. registry-verification depends on it: without it, provenance verification accepts unsupported RSA keys and its test suite fails. Claude-Session: https://claude.ai/code/session_014rikrGMh25h1rJczUQyTMM
🦋 Changeset detectedLatest commit: 17d30af The changes in this PR will be included in the next version bump. This PR includes changesets to release 21 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Scope checkThis PR changes 5,294 lines across 55 files. Large PRs are harder to review and more likely to be closed without review. If this scope is intentional, no action needed. A maintainer will review it. If not, please consider splitting this into smaller PRs. See CONTRIBUTING.md for contribution guidelines. |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
emdash-playground | 17d30af | Jul 16 2026, 06:59 AM |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
emdash-demo-do | 17d30af | Jul 16 2026, 07:00 AM |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
emdash-demo-cache | 17d30af | Jul 16 2026, 07:00 AM |
@emdash-cms/admin
@emdash-cms/auth
@emdash-cms/auth-atproto
@emdash-cms/blocks
@emdash-cms/cloudflare
@emdash-cms/contentful-to-portable-text
emdash
create-emdash
@emdash-cms/gutenberg-to-portable-text
@emdash-cms/plugin-cli
@emdash-cms/plugin-types
@emdash-cms/registry-client
@emdash-cms/registry-lexicons
@emdash-cms/registry-verification
@emdash-cms/sandbox-workerd
@emdash-cms/x402
@emdash-cms/plugin-ai-moderation
@emdash-cms/plugin-atproto
@emdash-cms/plugin-audit-log
@emdash-cms/plugin-color
@emdash-cms/plugin-embeds
@emdash-cms/plugin-field-kit
@emdash-cms/plugin-forms
@emdash-cms/plugin-webhook-notifier
commit: |
There was a problem hiding this comment.
This is a clean, additive graduation PR: it moves an already-reviewed slice of the plugin-registry work from feat/delegated-release-service to main without touching core runtime code. The contents are well-scoped (plugin-types, registry-lexicons, and the new registry-verification package) and come with comprehensive tests for both Node and workerd.
What I checked:
- The three changed packages and their new/changed source files.
- The
@sigstore/corepnpm patch: it is correctly declared inpnpm-workspace.yaml, reflected in the lockfile, and directly exercised by the provenance tests. - Changesets for all three published packages.
- AGENTS.md conventions:
.jsinternal imports,import typeusage, changeset style, and package export shapes.
Headline: no logic bugs, security regressions, or broken contracts. I did find two non-blocking maintainability issues: a circular import in plugin-types and a test that imports an internal path from @sigstore/verify. Both are suggestions, not blockers.
|
|
||
| import { z } from "zod"; | ||
|
|
||
| import { capabilitiesToDeclaredAccess, declaredAccessToCapabilities } from "./index.js"; |
There was a problem hiding this comment.
[suggestion] manifest-schema.ts imports runtime helpers (capabilitiesToDeclaredAccess, declaredAccessToCapabilities) and PluginManifest from ./index.js, while src/index.ts re-exports everything from ./manifest-schema.js (line 477). This creates a circular module dependency. It loads safely today only because the imported values are initialized before the re-export is evaluated, but it is fragile: reordering or adding an eager side-effect could turn it into a TDZ/runtime initialization failure later.
Move the two access-mapping helpers and the DeclaredAccess/PluginManifest types they need into a small shared module (e.g. src/access.ts) and have both index.ts and manifest-schema.ts import from it:
import {
capabilitiesToDeclaredAccess,
declaredAccessToCapabilities,
type PluginManifest,
} from "./access.js";That breaks the cycle cleanly without changing any public export.
| import type { TransparencyLogEntry } from "@sigstore/bundle"; | ||
| import { crypto as sigstoreCrypto } from "@sigstore/core"; | ||
| import type { TLogAuthority } from "@sigstore/verify"; | ||
| import { verifyCheckpoint } from "@sigstore/verify/dist/tlog/checkpoint.js"; |
There was a problem hiding this comment.
[suggestion] The test contract imports verifyCheckpoint from @sigstore/verify/dist/tlog/checkpoint.js, which is a non-public internal path. This will break on any patch/minor release of @sigstore/verify that moves or renames that file, even if the API surface is otherwise compatible.
Prefer using a public @sigstore/verify export, or inline a minimal Ed25519 checkpoint verifier for this contract test so the test is pinned to the dependency's stable API rather than its internal layout.
|
Both suggestions are valid and deliberately not addressed in this PR: every lifted file is byte-identical to ~ 🤖 Claude Opus 4.8 |
There was a problem hiding this comment.
Pull request overview
Graduates the new @emdash-cms/registry-verification package (and its additive dependencies in plugin-types and registry-lexicons) onto main, introducing runtime-neutral primitives for verifying plugin release artifacts (checksums, bounded fetching, bundle validation, and Sigstore provenance) alongside required workspace-level dependency patching for @sigstore/core.
Changes:
- Adds
@emdash-cms/registry-verificationwith Node + workerd test coverage and build packaging checks. - Extends
@emdash-cms/plugin-typeswith manifest validation (pluginManifestSchema,reconcileManifestAccess) and declared-access canonicalization/diff helpers. - Extends
@emdash-cms/registry-lexiconswithpackageProfileExtensionand an optionalprovenancereference onreleaseExtension, plus a repo-wide pnpm patch for@sigstore/core@4.0.1.
Reviewed changes
Copilot reviewed 50 out of 55 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| pnpm-workspace.yaml | Registers a workspace-level patch for @sigstore/core@4.0.1. |
| pnpm-lock.yaml | Locks patched dependency hash and adds new package importer entries. |
| patches/@sigstore__core@4.0.1.patch | Patches sigstore verify algorithm selection to fail-closed when omitted. |
| packages/registry-verification/wrangler.jsonc | Adds Wrangler config for workerd test runs. |
| packages/registry-verification/vitest.workerd.config.ts | Adds Vitest config for workerd pool tests. |
| packages/registry-verification/vitest.config.ts | Adds Vitest config for Node environment tests. |
| packages/registry-verification/tsdown.config.ts | Configures neutral + Node builds; bundles Sigstore for published workerd path. |
| packages/registry-verification/tsconfig.json | Adds TS config for the new package (includes tests). |
| packages/registry-verification/tests/workerd/worker.ts | Test worker entrypoint for workerd pool. |
| packages/registry-verification/tests/workerd/smoke.test.ts | Workerd smoke tests for checksums/fetch/bundle validation. |
| packages/registry-verification/tests/workerd/provenance.test.ts | Runs shared provenance contract in workerd. |
| packages/registry-verification/tests/workerd/packed-output.test.ts | Ensures packed dist entrypoints execute correctly in workerd. |
| packages/registry-verification/tests/records.test.ts | Unit tests for record + provenance policy verification. |
| packages/registry-verification/tests/provenance.test.ts | Runs shared provenance contract in Node. |
| packages/registry-verification/tests/provenance-contract.ts | Comprehensive provenance verification contract + regression vectors. |
| packages/registry-verification/tests/fetch.test.ts | Tests for safe, bounded, redirect-aware fetching. |
| packages/registry-verification/tests/checksum.test.ts | Tests for multihash encoding/decoding and digest comparisons. |
| packages/registry-verification/tests/check-packed-output.test.ts | Validates packaging script fails closed without package manager entrypoint. |
| packages/registry-verification/tests/bundle.test.ts | Tests for canonical plugin bundle validation and limits. |
| packages/registry-verification/src/trust-roots/sigstore-public-good-v1.json | Vendored Sigstore “public-good” trusted root subset for verification. |
| packages/registry-verification/src/repository.ts | Canonicalizes repository URLs for signed identity binding. |
| packages/registry-verification/src/records.ts | Validates signed profile/release records and applies provenance policy. |
| packages/registry-verification/src/provenance.ts | Implements GitHub workflow Sigstore provenance verification. |
| packages/registry-verification/src/index.ts | Public exports for checksum/fetch/bundle/provenance/record verification APIs. |
| packages/registry-verification/src/fetch.ts | Implements bounded fetch with redirect, DNS, and size/time limits. |
| packages/registry-verification/src/fetch-entry.ts | Worker-safe fetch entrypoint exports. |
| packages/registry-verification/src/errors.ts | Defines stable verification error codes and result shape. |
| packages/registry-verification/src/checksum.ts | Implements multibase multihash checksum compute/verify helpers. |
| packages/registry-verification/src/bundle.ts | Validates gzip+tar plugin bundle structure and manifest constraints. |
| packages/registry-verification/src/bundle-limits.ts | Centralizes bundle size/count limits for validation. |
| packages/registry-verification/scripts/check-packed-output.mjs | Verifies packed output is worker-safe and contains the patched algorithm mapping. |
| packages/registry-verification/package.json | Adds publishable package metadata, scripts, and dependencies. |
| packages/registry-verification/fixtures/records/release.json | Fixture for release record verification tests. |
| packages/registry-verification/fixtures/records/profile.json | Fixture for profile record verification tests. |
| packages/registry-verification/fixtures/provenance/sigstore-core-4.0.1-slsa.bundle.json | Fixture Sigstore bundle used for provenance contract tests. |
| packages/registry-verification/fixtures/provenance/README.md | Documents fixture provenance and trust-root update process. |
| packages/registry-lexicons/tests/types.test.ts | Expands lexicon smoke tests for new extension types and delegated permission helper. |
| packages/registry-lexicons/src/index.ts | Exports new NSIDs/types and adds getDelegatedReleasePermission(). |
| packages/registry-lexicons/src/generated/types/com/emdashcms/experimental/package/releaseExtension.ts | Adds optional provenance embedded object schema/type. |
| packages/registry-lexicons/src/generated/types/com/emdashcms/experimental/package/profileExtension.ts | Adds generated types for the new profile extension lexicon. |
| packages/registry-lexicons/src/generated/types/com/emdashcms/experimental/package/profile.ts | Adds extensions field to profile schema as opaque container. |
| packages/registry-lexicons/src/generated/index.ts | Re-exports generated profileExtension types. |
| packages/registry-lexicons/README.md | Documents delegated release grant scoping helper. |
| packages/registry-lexicons/lexicons/com/emdashcms/experimental/package/releaseExtension.json | Adds provenance ref to the release extension lexicon. |
| packages/registry-lexicons/lexicons/com/emdashcms/experimental/package/profileExtension.json | Adds new profileExtension lexicon definition. |
| packages/registry-lexicons/lexicons/com/emdashcms/experimental/package/profile.json | Documents extensions field semantics in the profile lexicon. |
| packages/plugin-types/tests/declared-access.test.ts | Adds tests for declared-access canonicalization, diffing, and escalation detection. |
| packages/plugin-types/tests/capabilities.test.ts | Extends capability tests to cover manifest schema validation. |
| packages/plugin-types/src/manifest-schema.ts | Adds Zod manifest schema + reconciliation helpers. |
| packages/plugin-types/src/index.ts | Re-exports manifest schema and declared-access helpers/types. |
| packages/plugin-types/src/declared-access.ts | Adds declared-access canonicalization/diff logic and digest preimage builder. |
| packages/plugin-types/package.json | Adds zod dependency for runtime manifest validation. |
| .changeset/plugin-types-manifest-schema.md | Changeset for @emdash-cms/plugin-types minor feature additions. |
| .changeset/lexicons-profile-extension.md | Changeset for @emdash-cms/registry-lexicons minor additions. |
| .changeset/graduate-registry-verification.md | Changeset for first release of @emdash-cms/registry-verification. |
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| export interface CanonicalDeclaredAccess { | ||
| readonly content?: Readonly<{ | ||
| read?: CanonicalAccessConstraints; | ||
| write?: CanonicalAccessConstraints; | ||
| }>; | ||
| readonly email?: Readonly<{ | ||
| events?: CanonicalAccessConstraints; | ||
| send?: CanonicalAccessConstraints; | ||
| transport?: CanonicalAccessConstraints; | ||
| }>; | ||
| readonly media?: Readonly<{ | ||
| read?: CanonicalAccessConstraints; | ||
| write?: CanonicalAccessConstraints; | ||
| }>; | ||
| readonly network?: Readonly<{ | ||
| request?: CanonicalAccessConstraints & { readonly allowedHosts?: readonly string[] }; | ||
| }>; | ||
| readonly page?: Readonly<{ fragments?: CanonicalAccessConstraints }>; | ||
| readonly users?: Readonly<{ read?: CanonicalAccessConstraints }>; | ||
| } |
| import { z } from "zod"; | ||
|
|
||
| import { capabilitiesToDeclaredAccess, declaredAccessToCapabilities } from "./index.js"; | ||
| import type { PluginManifest } from "./index.js"; | ||
|
|
| { | ||
| "extends": "../../tsconfig.base.json", | ||
| "compilerOptions": { | ||
| "outDir": "./dist", | ||
| "rootDir": "./src", | ||
| "lib": ["es2024", "dom", "esnext.typedarrays"], | ||
| "types": ["@cloudflare/vitest-pool-workers/types"] | ||
| }, | ||
| "include": ["src/**/*", "tests/**/*"], | ||
| "exclude": ["node_modules", "dist", "tests"] | ||
| } |
| const limits = { | ||
| headerTimeoutMs: options.headerTimeoutMs ?? DEFAULT_FETCH_LIMITS.headerTimeoutMs, | ||
| totalTimeoutMs: options.totalTimeoutMs ?? DEFAULT_FETCH_LIMITS.totalTimeoutMs, | ||
| maxBytes: options.maxBytes ?? DEFAULT_FETCH_LIMITS.maxBytes, | ||
| maxRedirects: options.maxRedirects ?? DEFAULT_FETCH_LIMITS.maxRedirects, | ||
| }; | ||
| if (!areValidLimits(limits)) | ||
| return verificationError("INVALID_URL", "Fetch limits must be positive integers."); |
What does this PR do?
Graduates
packages/registry-verification— and the two additive package slices it hard-depends on — fromfeat/delegated-release-serviceto main, per the ratified W7.1 inversion rule in the labelling-service plan (".opencode/plans/plugin-registry-labelling-service/implementation-plan.md": if W7 starts first, graduate the package to main in a standalone PR; three-package scope + the pnpm patch ratified 2026-07-16). The labelling branch's W7 (verified artifact acquisition) builds on this once merged.Everything is lifted byte-identical from
origin/feat/delegated-release-service—git diffagainst that branch over the lifted paths is empty; zero deviations were forced by main's toolchain. Source commits: registry-verification9f429c0e, plugin-types5473b880, registry-lexicons75498780.What lands
@emdash-cms/registry-verification(new, publishable): runtime-neutral release-artifact verification — multihash checksums, guarded artifact fetch (size/redirect limits), canonical tarball/bundle validation, Sigstore build provenance. Node + workerd.@emdash-cms/plugin-typesadditions:manifest-schema.ts(manifest validation,reconcileManifestAccess) anddeclared-access.ts(canonicalization, diff, escalation detection) + index re-exports and tests.@emdash-cms/registry-lexiconsadditions: thepackageProfileExtensionlexicon + generated types, and aprovenancereference on the release extension.patches/@sigstore__core@4.0.1.patch+ apatchedDependenciesentry inpnpm-workspace.yaml— main's first dependency patch. It fixes a provenance-verification weakness: when a signature omits the hash algorithm, upstream@sigstore/corepassesundefinedintocrypto.verify(accepting unsupported RSA keys); the patch derives the algorithm from the key type (P-256/384/521 → sha256/384/512, ed25519) and rejects unsupported keys. registry-verification's build config documents relying on it and three of its tests enforce the rejection — the package cannot land correct or green without it.Honest flag for the delegated-branch owners
The verbatim
registry-lexicons/src/index.tslift also carriesgetDelegatedReleasePermission()/DELEGATED_RELEASE_PERMISSION— inert delegated-publishing scaffolding (pure function + frozen const, unwired), kept to preserve byte-identity so the delegated branch reconciles conflict-free on its next main sync. Deliberately not advertised in the changesets.Type of change
Checklist
pnpm typecheckpassespnpm lintpassespnpm testpasses (or targeted tests for my change)pnpm formathas been runAI-generated code disclosure
Screenshots / test output