Motivation
The jco targets declare sha1-checked missing: platform WebCrypto carries no sha1dc, and shipping sha1dc in JS would defeat platform-backing (targets.toml already frames the remedy as "a composition needing more must supply another provider"). Composing the full in-guest provider does deliver sha1-checked on Node today, but at the price of de-platforming all crypto. This issue records a designed middle path: platform-backed keyed crypto plus wasm sha1dc, on Node (and in principle any jco host).
Why a stripped provider profile is not the answer
A provider profile exporting only the digest island {digest, sha2, sha1-checked} composes cleanly (the island is severable: digest.digest is used only by sha2 and sha1-checked, and the island touches none of the wrap-input/unwrap-input/derive-input resources that entangle the whole keyed surface). But it forces sha2 capture: the provider must own the digest resource, so host-served sha2 becomes unlinkable and all SHA-2 runs in-guest — plausibly a 4-10x throughput regression vs the platform's native hashing. The profile's first act would be de-platforming SHA-2.
Per-algorithm cargo features on the provider are the wrong mechanism generally: features cannot vary the exported world, the severable unit is the resource-ownership island (not the algorithm), and nearly every combination a flag matrix advertises is an invalid composition (the class-D gate's dangling-alias failure mode).
The design: a digest-kind virtualizing adapter
A small component that imports lann:webcrypto/{digest, sha2} (the platform's, via the jco host) and exports lann:webcrypto/{digest, sha2, sha1-checked}:
- The exported
digest resource is the adapter's own type wrapping a two-armed enum: platform(imported digest) / sha1dc(local state from lann-webcrypto-core).
sha2.make-digest proxies to the imported mint and wraps; sha1-checked's two mints serve sha1dc locally.
compute on a platform-wrapped digest forwards the stream<u8> handle to the imported compute — stream ends are transferable, so the data plane bypasses the adapter entirely (bytes never transit it for SHA-2).
Composition: guest <- wac plug adapter (internalizes the digest kind; the adapter's digest/sha2/types imports propagate outward) <- jco host serves them.
Verified with wasm-tools 1.247 (world elaboration + dummy component build):
- A world may import and export the same interface; the exported
sha2/sha1-checked use of digest binds to the exported copy (elaboration shadowing), which is exactly what the design needs.
types is resource-free, so the exported interfaces' use types.{error} binds to the imported types — no captured copy, and error passthrough is the identical structural type (no conversion boilerplate).
Why the general mediation taxes do not apply here: the closure under resource use-edges is 3 interfaces; nothing else in the package consumes a digest resource, so cross-provider operand mixing stays unrepresentable (no new runtime failure mode); the bulk data path is zero-touch.
Costs and open items
- New component artifact + world; a supported configuration means a new conformance target (a jco-composed adapter — the current jco adapter runs the uncomposed guest), targets.toml entry, matrix column. This is the dominant cost.
- Tooling to prototype-verify: wit-bindgen generating both directions of
digest/sha2 in one crate, and wac plug propagating the plug's own imports. Neither is exercised in this repo today.
- The artifact's raison d'etre is an
@unstable interface; consumers must thread the sha1-checked feature gate through their builds (silent-drop failure mode if forgotten).
- Timing policy: no new work — digest input is public, sha1dc branches only on it (class-exempt, already classified).
- The adapter needs no capabilities (digests draw no randomness).
Non-generalization
This works because digest resources have no external consumers. Every other kind is entangled through wrap-input/unwrap-input/derive-input, so a mediating adapter there would pay total mediation of the keyed surface and introduce a representable provenance-mismatch misuse the one-owner design currently excludes. This adapter is a one-off, not the first instance of a pattern.
Trigger
Build when a consumer needs sha1dc and platform-backed keyed crypto in the same component on a jco host, not before. Until then the full-provider composition covers the functional need; documenting that recipe costs a paragraph and no CI lanes.
Motivation
The jco targets declare
sha1-checkedmissing: platform WebCrypto carries no sha1dc, and shipping sha1dc in JS would defeat platform-backing (targets.toml already frames the remedy as "a composition needing more must supply another provider"). Composing the full in-guest provider does deliversha1-checkedon Node today, but at the price of de-platforming all crypto. This issue records a designed middle path: platform-backed keyed crypto plus wasm sha1dc, on Node (and in principle any jco host).Why a stripped provider profile is not the answer
A provider profile exporting only the digest island
{digest, sha2, sha1-checked}composes cleanly (the island is severable:digest.digestis used only bysha2andsha1-checked, and the island touches none of thewrap-input/unwrap-input/derive-inputresources that entangle the whole keyed surface). But it forces sha2 capture: the provider must own thedigestresource, so host-servedsha2becomes unlinkable and all SHA-2 runs in-guest — plausibly a 4-10x throughput regression vs the platform's native hashing. The profile's first act would be de-platforming SHA-2.Per-algorithm cargo features on the provider are the wrong mechanism generally: features cannot vary the exported world, the severable unit is the resource-ownership island (not the algorithm), and nearly every combination a flag matrix advertises is an invalid composition (the class-D gate's dangling-alias failure mode).
The design: a digest-kind virtualizing adapter
A small component that imports
lann:webcrypto/{digest, sha2}(the platform's, via the jco host) and exportslann:webcrypto/{digest, sha2, sha1-checked}:digestresource is the adapter's own type wrapping a two-armed enum:platform(imported digest)/sha1dc(local state from lann-webcrypto-core).sha2.make-digestproxies to the imported mint and wraps;sha1-checked's two mints serve sha1dc locally.computeon a platform-wrapped digest forwards thestream<u8>handle to the importedcompute— stream ends are transferable, so the data plane bypasses the adapter entirely (bytes never transit it for SHA-2).Composition: guest <-
wac plugadapter (internalizes the digest kind; the adapter'sdigest/sha2/typesimports propagate outward) <- jco host serves them.Verified with wasm-tools 1.247 (world elaboration + dummy component build):
sha2/sha1-checkeduseofdigestbinds to the exported copy (elaboration shadowing), which is exactly what the design needs.typesis resource-free, so the exported interfaces'use types.{error}binds to the importedtypes— no captured copy, and error passthrough is the identical structural type (no conversion boilerplate).Why the general mediation taxes do not apply here: the closure under resource
use-edges is 3 interfaces; nothing else in the package consumes adigestresource, so cross-provider operand mixing stays unrepresentable (no new runtime failure mode); the bulk data path is zero-touch.Costs and open items
digest/sha2in one crate, andwac plugpropagating the plug's own imports. Neither is exercised in this repo today.@unstableinterface; consumers must thread thesha1-checkedfeature gate through their builds (silent-drop failure mode if forgotten).Non-generalization
This works because digest resources have no external consumers. Every other kind is entangled through
wrap-input/unwrap-input/derive-input, so a mediating adapter there would pay total mediation of the keyed surface and introduce a representable provenance-mismatch misuse the one-owner design currently excludes. This adapter is a one-off, not the first instance of a pattern.Trigger
Build when a consumer needs sha1dc and platform-backed keyed crypto in the same component on a jco host, not before. Until then the full-provider composition covers the functional need; documenting that recipe costs a paragraph and no CI lanes.