OpenWOP openwop.dev
FieldValue
RFC0177
Titlev2 registry, packs, and the extension tail: a v2 host reads an absent engines.openwop ceiling as <2.0.0 and refuses with pack_engine_unsupported at install, on every publication path including a mirror; registry/v2/ is a parallel published tree of re-signed manifests (a signed overlay is rejected: signatures authorize by namespace and the mirror path re-derives the signer at ingest); the peer-dependency identifier is the C.2 declaration-file key and pack_peer_dependency_undefined reads against that file, with a generated alias table for the four grammars in the wild and a scheduled removal; the 13 manifest schemas re-$id under /spec/v2/ with the RFC 0138 hatch on every pack-authored document (RFC 0138 G4 decided); signing.method's two conventions that sign different bytes become one signing.scheme; publicKeyRef and kind-absent are gone; the registry is versioned by tree and its endpoints map is the negotiation; provider-id precedence (RFC 0095 UQ4) is decided as fail-closed conflict with a qualified form; form-content packs reuse the edge-condition operator set and i18n.md's localized string; chains pin exact versions (WCP2), children are reference-counted (WCP5), and the portable parameter deferral (WCP4) is a named v2.x follow-up
StatusActive
Author(s)David Tufts (@davidscotttufts)
Created2026-09-03
Updated2026-09-03 (Draft → Active in the filing PR. Comment window waived under GOVERNANCE.md §"Sole-steward operation" and logged in MAINTAINERS.md; RFC 0001 §5 cross-org rule not yet active; RFC 0147 §A.6 overridden and named in the parent, RFC 0167 (pack trust path and signing). Adversarial review recorded below.) · 2026-09-03 (filed)
AffectsPart of: RFC 0167 — child C10. v2 (Phase 3): spec/v2/core/packs.md (NEW: the ceiling rule, install-time refusal, the alias table, one scheme), schemas/v2/*-manifest.schema.json × 13 ($id under /spec/v2/; hatch on agent-manifest and prompt-template; signing.scheme; keyId only; kind required), openwop-registry registry/v2/ tree with regenerated index, SBOMs and signatures and a .well-known/openwop-registry.json endpoints map naming both trees, spec/v2/peer-dependency-aliases.json generated by check-declaration.mjs from the 282 published manifests, spec/v2/core/connection-packs.md (precedence), spec/v2/core/form-content-packs.md (when, localized strings), spec/v2/core/workflow-chain-packs.md (exact pins, reference counting); v1.x (this PR): spec/v1/migrations.json rows openwop.migration.C10.1C10.15; codemods openwop.codemod.pack-manifest-v2 (C10.1, C10.6, C10.7, C10.8), openwop.codemod.node-pack-ui-hints (C10.2), openwop.codemod.workflow-definition-v2 (C10.3); deprecation rows engines-absent-ceiling, pack-peer-dependency-aliases, manifest-signing-method, connection-provider-version-precedence (proposed)
Compatibilitybreaking (v2). In v1.x this PR changes no wire shape; rows, codemods and register entries only
Supersedes— (amends RFC 0095 §B.6 clause 6, RFC 0133 §Unresolved 2, RFC 0137 G1/G3, RFC 0138 §Out of scope for v2)
Superseded by

Summary

156 packs, 282 published versions, each a signed quad (manifest, SBOM, detached Ed25519 signature, tarball). 136 versions pin <2.0.0; 146 have no ceiling; none declares anything else — so under any honest rule every published version refuses on a v2 host and the re-publish wave is total, not optional. 280 of 282 use the deprecated publicKeyRef; 280 declare method: manual and 2 ed25519, which the schema calls synonymous and the verifier treats as signatures over different bytes; build-index defaults to the convention almost nobody uses. peerDependencies is an open string map with no reference to the capability vocabulary, which is how host.fs/fs and host.queueBus/queueBus both got signed into the tree, next to a third grammar (openwop.agents.memoryBackends) and facet paths used as identifiers. The registry validates every pack against a vendored copy of the node-pack schema that lacks the very hatch RFC 0138 added, and it is unpinned. Two of the 13 manifest schemas still forbid the vendor field host-extensions.md requires them to carry. Three gap tables hold decisions about chain pinning, child ownership, conditional visibility, i18n and provider-id precedence that this RFC makes.

Motivation

  • The ceiling. engines.openwop over 282 manifests: >=1.0.0 145, >=1.0.0 <2.0.0 86, >=1.1.0 <2.0.0 47, >=1.1 <2.0.0 3 (a two-segment grammar the others do not use), >=1.1.0 1. Every byte is under a detached signature; nothing can be edited in place. node-pack-manifest.schema.json engines.openwop is a free string with an example, and additionalProperties: true on the block.
  • Three trust paths, one file that none of them would read. Canonical publication is a PR wave (writeApi.supported: false, publishMethod: "github-pull-request"); verify-signatures.mjs:135–142 authorizes by signingKeys[].permittedNamespaces; MyndHyve runs its own PUT /v1/packs/… registry keyed on publicKeyRef (routes/packs.ts:22–30) and a super-admin mirror that re-derives signedByKeyId at ingest (routes/openwopMirror.ts). A compatibility-overlay.json would need five per-namespace signatures and would still be invisible to the mirror.
  • Two conventions, one word. registry-version-manifest.schema.json:132 — "manual and ed25519 both denote a detached Ed25519 signature over canonical-JSON pack.json (synonymous in practice)"; registry/scripts/verify-signatures.mjs:150–171ed25519 signs the whole gzipped tarball, manual signs pack.json inside it; build-index.mjs:190 defaults to 'ed25519' while 280 of 282 say manual. Axiom 2, inside the trust path.
  • Identifiers. node-pack-manifest.schema.json:102–108 peerDependencies: { additionalProperties: string } — no enum, no pattern, no $ref. In the tree: aiProviders 67, host.agentRuntime 41, openwop.agents.memoryBackends 11, host.fs 2 / fs 1, host.queueBus 2 / queueBus 1, host.aiEnvelope.await, aiProviders.imageGeneration, aiProviders.videoGeneration. agent-manifest.schema.json:58 offers host.workspace as an example of a legitimate requirement; host-capabilities.md:2186–2188 reserves it and requires rejection with pack_peer_dependency_undefined. RFC 0169 §B.1–§B.2: the declaration file is the identifier and check-declaration.mjs fails on a peer-dependency key it does not name.
  • The hatch. RFC 0138 §Out of scope (:92) and §Unresolved 4 (:176) defer agent-manifest and prompt-template "to their owners"; agent-manifest is $ref'd from node-pack-manifest.schema.json:134, so a pack that may carry x- at its root may not carry it in its own agents[]. The registry's vendored node-pack-manifest.schema.json (openwop-registry/schemas/, 7 of 13 drifted, no CORPUS_TAG, check-vendored-sync.mjs on main) has additionalProperties: false at :320 and :641 with no patternProperties — the registry rejects the field the protocol requires it to carry (H34 class).
  • Prefix and $id. .well-known/openwop-registry.json endpoints: five of six carry /v1/, publicKey does not; endpointAliases._note already tells clients to consult endpoints. The 13 schema $ids are under /spec/v1/ and immutable (RFC 0172 row C5.7); the version manifests themselves carry no $id (the charter's "every manifest $ids on spec/v1" is true of the schemas).
  • Three gap tables. connection-packs.md:277 UQ4 (two packs claiming github; the scenario connection-provider-resolution never tests two claimants; 12 connection packs published); form-content-packs.md:180–184 G1 (a second expression language), G3 (no i18n), R1 (no reference host; one pack published); workflow-chain-packs.md:508–512 WCP2 (135 of 282 versions are chains), WCP4, WCP5 (= RFC 0133 §Unresolved 2, whose siblings were resolved by persisting the resolved version in the ownership record).
  • Register state. openwop.migration.C10.1 is a rename with codemod: null — §G.1 fails the moment this child is Active unless it has one; kind is absent on 131 of 282 manifests while index.json carries it; deprecations.json has 32 rows where the umbrella prose says 26 (the six newer rows are RFC 0172/0174/0178's).

Proposal

§A. The ceiling and the tree

§A.1 Absent ceiling reads <2.0.0. A v2 host evaluating engines.openwop MUST treat a range with no upper bound as bounded by <2.0.0 and MUST refuse to install a version whose range does not admit the host's protocol major with pack_engine_unsupported (a registered error; pack_runtime_requirement_unmet stays for runtime, not protocol). The check runs at install, on every publication path — the canonical registry, a vendor registry's write API, and a mirror ingest — so a registry-side artifact cannot be bypassed. A v2 manifest MUST declare an explicit upper bound; the schema enforces the grammar (three-segment, >= lower, < upper).

§A.2 registry/v2/. The registry publishes a parallel tree registry/v2/packs/<name>/-/<version>.{json,sbom.json,sig,tgz} of re-signed manifests produced by the same build-pack-tarball.mjs --signed path (:312, Ed25519 over canonical JSON) with SBOMs regenerated, index regenerated, and signing.scheme explicit. A signed compatibility-overlay.json is rejected: verify-signatures.mjs authorizes by namespace, so one overlay cannot cover five namespaces, and the mirror path would never read it. The v1 tree is served read-only through Phase 5.

§A.3 The registry's prefix. The registry is a static tree and cannot negotiate by header; it is versioned by tree — the C.5 rule permits one mechanism, not both. .well-known/openwop-registry.json endpoints gains the /v2/ templates beside the /v1/ ones and is the negotiation: clients MUST resolve paths through endpoints; publicKey stays unversioned by design (keys are not protocol-versioned) and the map's note says so.

§B. Identifiers

§B.1 The pack peer-dependency identifier is the C.2 declaration-file key (RFC 0169 §B.1: root key ≡ peer-dependency identifier ≡ section anchor). pack_peer_dependency_undefined is raised for a key the declaration file does not name — the hardcoded prose list at host-capabilities.md:2184–2186 is retired with it; host.workspace is workspace (RFC 0169 §B.3). Facet paths (aiProviders.imageGeneration, host.aiEnvelope.await, multiAgent.executionModel.verifier) are not identifiers: a pack requires a family and names facets in peerDependenciesMeta.<family>.facets[].

§B.2 The alias table. spec/v2/peer-dependency-aliases.json is generated by running check-declaration.mjs over every published manifest; each unresolvable key becomes a row { alias, family, facets?, removalTrigger }. At filing the guaranteed rows are host.fs → fs, host.queueBus → queueBus (the dotted twins), openwop.agents.memoryBackends → agents.memoryBackends, host.workspace → workspace, and the three facet paths; aiProviders (67) and host.agentRuntime (41) follow C.2's rename of those two keys. A v2 host MAY resolve an alias through the table through the overlap and MUST NOT after v1 end-of-support (removalTrigger: v1-end-of-support).

§C. The manifest schema family

§C.1 The 13 schemas re-$id under https://openwop.dev/spec/v2/; the v1 $ids are immutable and served read-only (RFC 0172 C5.7). §C.2 The hatch. ^(x-|vendor\.) patternProperties on every pack-authored document: the ten that have it, plus agent-manifest and prompt-template (RFC 0138 G4 decided: same treatment; the $ref boundary at node-pack-manifest.schema.json:134 no longer stops the hatch). pack-lockfile (host-generated) and security-advisory (registry-authored) stay closed; prompt-ref is a leaf. §C.3 One scheme. signing.method is deleted; signing.scheme is required with the single value ed25519-canonical-json (the manual convention: a detached 64-byte Ed25519 signature over the canonical-JSON pack.json inside a deterministic tarball); the tarball-bytes convention is retired with the two packs that use it re-signed in registry/v2/; build-index has no default. §C.4 publicKeyRef is deleted (keyId only; row C10.1 with a codemod). §C.5 kind is required on every version manifest (the 131 absent are node; the codemod writes it). §C.6 The registry's vendored schemas are pinned (RFC 0176 §E.1) and re-synced from the tag before any v2 publication; the drift that dropped the hatch is closed in that sync.

§D. Provider-id precedence (RFC 0095 UQ4)

§D.1 A provider.id is unique per host. When an installed pack and a built-in, or two installed packs, define the same bare id the host MUST refuse the later registration with connection_provider_conflict — the version-based precedence in connection-packs.md clause 6 (:86–94) is deleted (Axiom 2: a rule that picks silently by version is two names for one thing). A connector MAY name a provider by its qualified form <packName>#<id> to disambiguate; a bare id resolves only when exactly one definition exists. Built-ins are the host's own pack for this rule.

§E. Chain, form, and content packs — reuse, do not mint

§E.1 Chains. A chain reference MUST pin an exact version per referenced typeId (core.ai.callPrompt@1.0.0); ranges are a v2.x additive follow-up (WCP2 decided). §E.2 Co-registered children are reference-counted by deterministic child id and deleted when the last parent is deleted; the ownership record persists the resolved version (RFC 0133 §Unresolved 1's mechanism) and is a C.9 store (WCP5 / RFC 0133 G5 decided). §E.3 The {{params.*}} portable deferral stays forbidden in v2.0; the follow-up additive RFC is named as G4 with the variable-binding recipe from workflow-chain-packs.md:512 (WCP4 decided as "not in v2.0", a decision with an owner). §E.4 Forms. Conditional visibility uses the edge-condition operator set (when: <EdgeCondition> on a field; no second expression language — RFC 0137 G1); label/title/description are i18n.md localized strings (G3); minLength/min/max/pattern are added (G2). §E.5 The five format values stay the core set (G4 stands).

§F. The MyndHyve cohort and the wave

38 vendor.myndhyve. packs / 40 versions, all >=1.0.0 <2.0.0, are re-published under myndhyve-internal-1 into registry/v2/ in Phase 4 with the host; the mirror and the PUT API apply §A.1 at install. The core wave (109 packs) is a scripted operation on the existing generators (gen--packs.mjs, build-pack-tarball.mjs) and lands in Phase 3 as the registry's evidence.

Migration table

RowKindv1v2CodemodPersisted data
openwop.migration.C10.1renamesigning.publicKeyRefsigning.keyIdopenwop.codemod.pack-manifest-v2 (refuses when both are present and differ)never-upgraded (re-signed in registry/v2/)
openwop.migration.C10.2renamex-openwop-form.credentialProvider (node config ui hint)provideropenwop.codemod.node-pack-ui-hintsnever-upgraded
openwop.migration.C10.3renameworkflow definition config.outputArtifactType / config.chatCardnode.artifactType / node.chatCardopenwop.codemod.workflow-definition-v2translated
openwop.migration.C10.4behaviorengines.openwop free string; absent ceiling read as openabsent ceiling ⇒ <2.0.0; pack_engine_unsupported at install on every path; explicit upper bound required— (the codemod refuses a manifest with no v2-satisfiable range; the author declares)never-upgraded
openwop.migration.C10.5addone tree registry/v1/registry/v2/ parallel tree; endpoints names bothnever-upgraded
openwop.migration.C10.6renamefour peer-dependency grammars (host. dotted, bare, openwop., facet paths)declaration-file keys; peerDependenciesMeta.<family>.facets[]; alias table with v1-end-of-support removalopenwop.codemod.pack-manifest-v2never-upgraded
openwop.migration.C10.7unifysigning.method: manual | ed25519 | sigstore (two byte conventions)signing.scheme: ed25519-canonical-jsonopenwop.codemod.pack-manifest-v2 (refuses ed25519-over-tarball and sigstore: a re-sign, not a rewrite)never-upgraded
openwop.migration.C10.8requirekind absent ≡ node (131 of 282)kind requiredopenwop.codemod.pack-manifest-v2never-upgraded
openwop.migration.C10.9behavior13 schema $ids under /spec/v1/; hatch on 10$ids under /spec/v2/; hatch on every pack-authored documentnot-persisted
openwop.migration.C10.10behaviorclause 6 version precedenceconnection_provider_conflict fail-closed; <packName>#<id> qualified formnot-persisted
openwop.migration.C10.11addnoneform-content when: <EdgeCondition>, localized strings, minLength/min/max/patternnot-persisted
openwop.migration.C10.12requirechain reference pin RECOMMENDEDexact version per typeId MUSTunchanged (135 chain manifests already pin)
openwop.migration.C10.13behaviorchild ownership lifecycle open (WCP5)reference-counted by deterministic id; ownership record persists the resolved versionunchanged
openwop.migration.C10.14behaviorregistry /v1/ on five of six endpointsversioned by tree; endpoints is the negotiation; publicKey unversioned by designnot-persisted
openwop.migration.C10.15requireregistry vendored schemas unpinned, 7 of 13 driftedpinned to a tag; re-synced before any v2 publicationnot-persisted

Persisted-data disposition

Storev1 artifactDisposition
registry/v1/packs/** (282 manifests, SBOMs, signatures, tarballs)signed quadsnever-upgraded; served read-only through Phase 5
registry/v2/packs/**new; re-signed by the same keys under signingKeys[].permittedNamespaces
registry/v1/index.json, .well-known/openwop-registry.jsonindex; endpoints mapindex unchanged; the map gains the /v2/ templates
MyndHyve internal registry catalog (PUT API + mirror ingest)packs keyed on publicKeyRefnever-upgraded; §A.1 applied at install in Phase 4
Host-side installed-pack records and lockfiles (pack-lockfile)installed v1 manifestsunchanged; a v2 host re-evaluates §A.1 at next install, not retroactively
Chain ownership records (RFC 0133)resolved child versionsunchanged; §E.2 makes the record normative
Workflow definitions with config.outputArtifactType / config.chatCarddeprecated bag entriestranslated by openwop.codemod.workflow-definition-v2 on export; read through the definition adapter

Compatibility

breaking (v2). This PR changes no v1.x wire shape.

Conformance

v2 scenarios (suite 2.0.0): manifest-ceiling-refused (gated on packs: an unbounded and a <2.0.0 manifest are both refused with pack_engine_unsupported; a >=2.0.0 <3.0.0 one installs), peer-dependency-declared (gated: an undeclared key is refused with pack_peer_dependency_undefined; an alias resolves through the overlap), manifest-hatch-carried (an x- field inside agents[] and prompts[] is ignored, not rejected), one-signing-scheme (registry corpus gate: every registry/v2/ manifest declares the scheme and verifies), provider-conflict (gated on connections.packsSupported: two claimants fail closed; the qualified form resolves), chain-pin-exact (a ranged reference is refused at register), form-when-reuses-edge-conditions (gated on forms: the when grammar is the edge-condition grammar).

Falsifiability — one row per normative requirement

RequirementObservableWho can cause the conditionVerdict
§A.1 unbounded/<2.0.0 refused at installpack_engine_unsupportedopenwop.requirement.0177.manifest-ceiling-refusedthe suite, gated on packswitnessable — gated
§A.2 registry/v2/ verifies under the same keysregistry gatethe registry's registry-check.shwitnessable — unaided (corpus)
§A.3 endpoints names both treesthe well-known registry documentthe suite, unaidedwitnessable — unaided
§B.1 undeclared key refused; facet paths not identifierspack_peer_dependency_undefinedopenwop.requirement.0177.peer-dependency-declaredthe suite, gated on packswitnessable — gated
§B.2 alias table generated, not hand-keptcheck-declaration.mjs over the manifests — openwop.requirement.0169.declaration-paritythe corpus gatewitnessable — unaided (corpus)
§C.2 hatch on agents[]/prompts[]install accepts the x- field — openwop.requirement.0177.manifest-hatch-carriedthe suite, gated on packswitnessable — gated
§C.3–§C.5 one scheme, keyId, kindschema + registry gate — openwop.requirement.0177.one-signing-schemethe corpus gatewitnessable — unaided (corpus)
§D.1 conflict fails closedconnection_provider_conflictopenwop.requirement.0177.provider-conflictthe suite via the RFC 0095 install/resolve seamsseam-gated
§E.1 exact pinrefusal at register — openwop.requirement.0177.chain-pin-exactthe suite, gated on workflowChainswitnessable — gated
§E.2 reference countinghost-side lifecyclethe suite via the chain seamsseam-gated
§E.4 when is the edge-condition grammarschema + a form pack fixture — openwop.requirement.0177.form-when-reuses-edge-conditionsthe suite, gated on forms (no reference host: host-pending)witnessable — gated

Adversarial review

1. Refusing 100% of published versions on a v2 host is a cliff, not a migration. Disposition: it is the honest reading of immutable data; the wave is scripted on generators that already exist; registry/v2/ lands in Phase 3 as the registry's own evidence before any host cuts; the v1 tree keeps serving v1 hosts. 2. A signed overlay is cheaper than re-signing 282 quads. Disposition: rejected on two facts — signatures authorize by namespace (five overlays, five keys) and the mirror path re-derives the signer at ingest and would never consult it; §A.1's install-time check is what makes the rule wire-observable regardless of which registry served the pack. 3. Deleting method breaks the two ed25519-over-tarball packs. Disposition: they are re-signed in registry/v2/ under the one scheme; the codemod refuses them rather than relabeling a signature over different bytes. 4. Facet paths as identifiers were legitimate under RFC 0105. Disposition: RFC 0169 §B.3 already decided the aiProviders.* pair; peerDependenciesMeta.<family>.facets[] keeps the expressiveness with one identifier grammar. 5. The registry is "versioned by tree" while the protocol is "by header" — that is the both C.5 forbids. Disposition: C.5 forbids both mechanisms on one surface; the registry has no request handler to read a header and uses one mechanism (tree), the protocol uses one (header); the endpoints map is the indirection both share. 6. Fail-closed provider conflict breaks a host that legitimately ships a built-in github and installs the community pack. Disposition: the host names its built-in <hostPack>#github or does not install the conflicting pack; silent precedence by version is the Axiom 2 failure UQ4 asked about; the qualified form is the disambiguation. 7. WCP4 is "decided" by deferring. Disposition: the decision is that v2.0 keeps the expansion-time MUST; the follow-up is registered as G4 with the recipe and an owner, which is what the three gap tables lacked. 8. The registry's vendored schema drift is a registry bug, not a protocol rule. Disposition: it is both; §C.6 and RFC 0176 §E.1 make the pin a MUST so the H34 class cannot recur in the repository that gates publication.

Alternatives considered

1. Read an absent ceiling as open (v2-compatible). Rejected: 146 versions would install on a host whose capability names they no longer match. 2. A signed compatibility-overlay.json. Rejected: §A.2, adversarial review 2. 3. Keep method with manual and ed25519 documented as different. Rejected: Axiom 2; one scheme, one set of bytes. 4. Scoped provider ids everywhere (core.github, community.x.github). Rejected: renames 12 published connection packs for a conflict that fail-closed plus a qualified form already prevents.

Unresolved questions

1. Whether registry/v2/ should carry only versions re-published by their authors or also steward re-signs of core.openwop. under openwop-team-1. Recommended: steward re-signs for core. and vendor.openwop*; vendor cohorts re-publish themselves.

Implementation notes (non-normative)

The alias table's first generation is a dry run of check-declaration.mjs over registry/v1/packs/**/-/*.json once spec/v2/declaration.json exists (Phase 3, RFC 0169). build-index.mjs:190's default is removed in the same registry PR that adds registry/v2/.

Acceptance criteria

  • [x] Draft → Active: RFC text; rows C10.1C10.15; three codemods with fixtures; four deprecation rows with detectors; ledger row; adversarial review. (This PR.)
  • [ ] Active → Accepted (Phase 3): spec/v2/core/packs.md; the 13 v2 schemas; registry/v2/ with the core wave and a green registry-check.sh; spec/v2/peer-dependency-aliases.json generated; the seven scenarios in suite 2.0.0; openwop-app passes manifest-ceiling-refused, peer-dependency-declared, manifest-hatch-carried; the registry's schemas pinned.

References

  • spec/v1/node-packs.md §engines, §x-openwop-form; spec/v1/host-capabilities.md §"Reserved-but-undocumented surfaces"; spec/v1/connection-packs.md §Manifest clause 6, UQ4; spec/v1/form-content-packs.md §Open spec gaps; spec/v1/workflow-chain-packs.md §Open spec gaps; spec/v1/registry-operations.md
  • RFC 0095 §B.6; RFC 0107; RFC 0133 §Unresolved; RFC 0137; RFC 0138 §Out of scope, §Unresolved 4; RFC 0167 §C/§F; RFC 0169 §B; RFC 0172 §A.2, C5.7; RFC 0176 §E.1
  • openwop-registry: registry/.well-known/openwop-registry.json, registry/scripts/verify-signatures.mjs:130–186, registry/scripts/build-index.mjs:190, scripts/build-pack-tarball.mjs:312, scripts/registry-check.sh, scripts/check-vendored-sync.mjs:17–18