Skip to content

Add browser WebAssembly support #5

Description

@ddurst-nvidia

Summary

Add supported browser-oriented WebAssembly behavior for the four runtime
crates on wasm32-unknown-unknown and expose the existing compose, decompose,
sign, and verify operations through a small JavaScript-facing boundary.

This issue covers source changes and local validation. It does not include
publishing a JavaScript package or distributing a compiled .wasm artifact.

Motivation

Most of the runtime is already portable Rust. The core and transcription
crates compile for wasm32-unknown-unknown, and the parsing, protobuf, hashing,
Ed25519, and ECDSA P-256 dependencies compile without native system services.
The public operations are synchronous and do not require sockets,
subprocesses, threads, or an async runtime.

The repository does not provide complete browser support today:

  • yaml-sigil-signing and yaml-sigil-verification select dependency
    features that pull in getrandom 0.2 without its JavaScript backend.
  • The optional JSON Schema helper reads its vendored schema from a runtime
    filesystem path, which is unavailable in a browser.
  • The Rust APIs use borrowed slices, Rust key types, and typed outcome enums
    that JavaScript cannot call directly.
  • Target compilation alone does not establish runtime behavior, byte-array
    ownership, or safe error handling across the JavaScript/WASM boundary.

A feasibility check at commit
f404309d0b5d94dd17238e628231611fcc1f1ef8 found that
yaml-sigil-core and yaml-sigil-transcription compiled directly for
wasm32-unknown-unknown. The signing and verification crates failed only at
the getrandom target guard. An external probe that enabled the JavaScript
randomness backend compiled all four crates, both with and without
json-schema-validate. These checks used Rust 1.97.0; they did not establish
support on the declared Rust 1.95.0 minimum version or exercise a browser
runtime.

Proposed Approach

Make dependency features target-compatible

Adjust the direct cryptography dependency features in
crates/yaml-sigil-signing/Cargo.toml and
crates/yaml-sigil-verification/Cargo.toml so each crate compiles for
wasm32-unknown-unknown without relying on feature unification in a downstream
application.

First determine whether disabling unneeded default or key-generation features
removes the randomness dependency. The current API accepts caller-provided
keys and uses deterministic signature operations, so the runtime path may not
need a random-number generator. If a supported operation does require
randomness, select the browser backend explicitly and only for the applicable
target.

Embed the optional JSON Schema

Replace the runtime CARGO_MANIFEST_DIR path construction and
std::fs::read_to_string call in
crates/yaml-sigil-core/src/tier_a_schema.rs with a compile-time embedded copy
of the vendored schema. Preserve lazy validator construction and the existing
validation behavior on native targets.

Add a narrow JavaScript boundary

Keep JavaScript-specific types out of the existing runtime crates where
possible. Add a small boundary crate or module that:

  • Accepts payloads, artifacts, signatures, and keys as Uint8Array values.
  • Parses algorithm and artifact-form selectors at the boundary.
  • Constructs and validates the Rust key types internally.
  • Returns composed artifacts and verified payloads as byte arrays.
  • Distinguishes malformed input, unsupported operations, invocation errors,
    and failed verification through a stable JavaScript-visible result shape.
  • Never includes secret key bytes in diagnostics or error messages.
  • Documents when inputs and outputs are copied and how long key material may
    remain in JavaScript or WASM memory.

The boundary should expose the existing conservative Rust behavior rather than
create a second implementation of signing, verification, or transcription.

Add local WASM validation

Add browser-compatible WASM runtime tests for:

  • YAML and protobuf compose/decompose round trips.
  • Ed25519 signing and verification.
  • ECDSA P-256 signing and verification.
  • Incorrect keys and failed verification.
  • Malformed signatures, malformed artifacts, invalid UTF-8, and unsupported
    algorithms.
  • Optional JSON Schema validation without runtime filesystem access.
  • JavaScript ownership and copying behavior for input and output byte arrays.

Run the existing native quality checks as part of the change. Test the WASM
target with Rust 1.95.0 before documenting support at the repository's
declared minimum version.

Acceptance Criteria

  • All four runtime crates compile directly for
    wasm32-unknown-unknown without a downstream feature workaround.
  • The default configuration and json-schema-validate configuration both
    compile for the WASM target.
  • JSON Schema validation performs no runtime filesystem access and retains
    its native behavior.
  • JavaScript callers can compose, decompose, sign, and verify both YAML and
    protobuf artifact forms through byte-array APIs.
  • The JavaScript boundary supports Ed25519 and ECDSA P-256 and exposes
    stable, documented error and verification-result shapes.
  • Browser-compatible runtime tests cover the successful and negative paths
    listed above.
  • The supported WASM configuration passes on Rust 1.95.0, or the support
    documentation states a separate minimum Rust version with the reason.
  • Existing native formatting, linting, and all-feature tests continue to
    pass.
  • Documentation states the supported target, features, API boundary, and
    cryptographic limitations.
  • Local validation does not leave executable WASM outputs behind, and no
    executable WASM artifact is committed, uploaded, attached, or published.

Non-goals

  • WASI or WebAssembly component-model support.
  • A published npm package, bundler-specific integration, or a stable package
    distribution policy.
  • Browser-side key generation unless an existing supported operation requires
    randomness.
  • A claim that browser JIT execution provides the same side-channel properties
    as a hardened native deployment.
  • gRPC, transport adapters, or generated service bindings.
  • A module-size or performance guarantee. Record optimized sizes with and
    without json-schema-validate to inform later decisions.

Alternatives Considered

Rely on downstream feature unification

A downstream crate can currently enable the getrandom JavaScript backend and
make the runtime crates compile. This is fragile because the crates do not
declare their own target requirements and can fail when dependency resolution
changes.

Support only Rust consumers targeting WASM

Making the runtime crates compile is useful for Rust applications that provide
their own bindings, but it does not give JavaScript callers a supported API or
test the boundary where key bytes, errors, and owned buffers cross runtimes.

Publish a JavaScript package as part of this work

A published package requires separate decisions about ABI stability,
versioning, bundler support, generated TypeScript declarations, and artifact
distribution. Those decisions can follow once the source API and local runtime
behavior are proven.

Security Considerations

Treat browser signing as a distinct threat model. Review key copying,
zeroization limits, exception text, and JavaScript/WASM memory lifetime before
describing the wrapper as suitable for secret-key operations. Functional tests
do not demonstrate constant-time behavior in a browser JIT.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions