npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@awacloud/tool-wasm-crypto

v0.1.0

Published

Build tool that compiles and verifies the WebAssembly crypto modules used by @awacloud/fw, from each package's own C sources, validated against pinned test vectors and checked into the repo as committed bytes.

Readme

@awacloud/tool-wasm-crypto

Maturity: L2 (real builder + verify gate)

Build tool that compiles and verifies the WebAssembly crypto modules used by @awacloud/fw. Takes each package's own C sources (Argon2, BLAKE2, …, resolved via --pkg <dir>), compiles them to .wasm via the WASI SDK clang, and validates the output against pinned test vectors. The compiled bytes are checked into the repo so the framework has zero runtime npm dependencies and no mandatory bundler.

The build --pkg <dir> / verify --pkg <dir> interface enables self-contained crypto packages to define and gate their own build contract: define a targets.json in the package root, point the tool there, and the builder resolves all source paths relative to the package and emits to <pkg>/dist/.

Consumption shape

Autonomous — standalone bin. This package ships its own wasm-crypto executable (bin/wasm-crypto.ts, a thin delegate to the run() function exported by src/index.ts — no argument parsing duplicated), so it runs directly once installed:

bunx wasm-crypto build --pkg packages/front/fw-wasm-crypto

Installation

npm install --save-dev @awacloud/tool-wasm-crypto

Runs under Bun (the bin is TypeScript executed by Bun).

Quick Start

# 1. Copy and edit the config
cp node_modules/@awacloud/tool-wasm-crypto/wasm-crypto.config.example.json wasm-crypto.config.json
# Set wasiSdkPath to your WASI SDK installation, e.g. /opt/wasi-sdk

# 2. Compile to .wasm (package-relative sources under --pkg <dir>)
bunx wasm-crypto build --pkg <dir>

# 3. Verify against test vectors
bunx wasm-crypto verify --pkg <dir>

# — or run the whole pipeline:
bunx wasm-crypto all --pkg <dir>

Subcommands

| Command | Status | Description | |----------|------------------|----------------------------------------------------| | build | functional (L2) | Compile C → .wasm via WASI SDK clang; --pkg model for package-relative paths and <pkg>/dist/ emit | | verify | functional (L2), partial coverage — see below | Run KAT/ACVP vectors against compiled .wasm.js modules; gates on byte-for-byte match. Reports V verified / S skipped / F failed of T targets and exits 1 when 0 targets were verified — a skip is the absence of evidence, never a pass | | copy | functional (L2) | Copy <pkg>/dist/*.wasm to a destination + write/check a committed hash manifest | | all | functional | Run build → verify (stops on first error) |

verify coverage — 3 of 17 families, and why this is not the publication gate

verify has a real runner for only 3 of the 17 algo families declared in packages/front/fw-wasm-crypto/targets.json: sha2, sha3 (fixed-digest variants only — see below) and aes (the standard 96-bit-IV/128-bit-tag AES-GCM shape). The other 14 — argon2, ml_kem, ml_dsa, slh_dsa, blake2b, chacha20poly1305, cmac, hmac, pbkdf2, hkdf, rsa, ecc, ed25519, x25519 — have no runner at all and are always reported SKIP with reason unknown algo family; verify also prints one stderr line naming exactly which families those are on every run. A skip is never counted toward verified: a run where every selected target is skipped exits non-zero, not a silent pass.

A reader must not come away thinking verify attests the whole family set. It doesn't, and isn't meant to: the wasm-crypto publication gate's evidence is the KAT shim test suite (packages/front/fw-wasm-crypto/shims/*.kat.test.ts, run natively in TypeScript against the same vectors, covering all 17 families) consumed by the package-export gate. verify is this tool's own byte-for-byte cross-check of the compiled wasm output for the families it can drive — useful corroboration, but partial, and not the gate's source of truth.

Two further limits, discovered wiring up the 3 real families:

  • sha3's SHAKE (XOF) vectors are not run. The bare sha3 target's vector list includes both a fixed-digest folder (SHA3-256-2.0, which is run) and a SHAKE-128-1.0 folder whose ACVP tests declare a per-test output length (bits) rather than one fixed digest size — verify skips that vector path honestly (one stderr note) rather than guess a length.
  • Only byte-aligned messages and the standard AES-GCM shape are checked. The wasm ABI's inLen/dataLen are byte counts with no bit-length concept, so an ACVP hash test whose true length (bits) is not a multiple of 8 cannot be represented and is skipped (measured: 0/512 for SHA2-256-1.0's AFT group, but 892/1024 for SHA2-384-1.0 and 1043/1194 for SHA3-256-2.0). Likewise AES-GCM's aes_gcm_seal/ aes_gcm_open hardcode a 96-bit IV and a 128-bit tag; an ACVP test group outside that shape (e.g. a 120-bit IV) is skipped per-test, not failed. MCT (Monte Carlo) and LDT (Large Data) hash test groups are skipped outright — only AFT (plain single-hash) groups run.

Configuration

Config file: wasm-crypto.config.json (gitignored). Template: wasm-crypto.config.example.json.

| Key | Default | Description | |---------------|-------------------------------------------|----------------------------------------| | wasiSdkPath | "" | Path to WASI SDK (bin/clang inside) | | outDir | packages/front/fw/src/crypto/wasm | Where .wasm output files are written | | targetsFile | packages/front/fw-wasm-crypto/targets.json | Target manifest (array of Target) |

Config precedence: --config CLI flag > WASM_CRYPTO_CONF env var > wasm-crypto.config.json in the CWD.

Target Manifest

targets.json is an array of Target objects (schema: src/targets.schema.ts):

[
  {
    "algo": "argon2",
    "wasmModule": "argon2",
    "exportName": "argon2Wasm",
    "source": "argon2-20190702",
    "sourceKind": "own",
    "shim": "shims/argon2.c",
    "cSources": ["csrc/argon2.c", "csrc/blake2/blake2b.c"],
    "cflags": ["-O3"],
    "simd": true,
    "exports": ["memory", "alloc", "free", "argon2id_hash_raw"],
    "vectors": ["references/argon2-vectors.json"]
  }
]

When invoked with bunx wasm-crypto build --pkg <dir>, all paths (shim and entries in cSources) are package-relative and must start with one of the frozen source roots: csrc/, shims/, or vendor/. The builder resolves them against the package directory and applies the per-source C standard rule (csrc/shims → C23; vendor → target's cStd or native).

Compile variants: When simd: true, the builder emits both a SIMD variant (-msimd128) and a scalar fallback. Each produces a .wasm.js module in <pkg>/dist/.

Required wasm ABI exports: memory, alloc, free (plus algorithm-specific exports).

Copy + hash manifest (copy)

build --pkg <dir> emits to <dir>/dist/ — gitignored, a local build byproduct. copy promotes those bytes into a committed location and records what was promoted:

# Apply: copy <pkg>/dist/*.{scalar,simd}.wasm into --into, then write
# <pkg>/dist/MANIFEST.sha256 (the full current dist/ index).
bunx wasm-crypto copy --pkg <dir> --into <dstDir>

# Drift gate: never writes; compares each committed <dstDir>/<file>
# against the committed MANIFEST.sha256 row.
bunx wasm-crypto copy --pkg <dir> --into <dstDir> --check

The concrete gate for the @awacloud/fw-wasm-crypto → @awacloud/fw pair:

bunx wasm-crypto copy --pkg packages/front/fw-wasm-crypto \
  --into packages/front/fw/src/crypto/wasm --check

--target <name> restricts which module's variants are copied/checked; the manifest itself always reflects the full dist/ contents. Manifest format: plain sha256sum-style lines (<64-hex-digest> <filename>), sorted by filename, LF-terminated. Running copy twice with no rebuild in between is a no-op — byte-identical manifest, byte-identical destination files.

dist/ is gitignored, so the manifest needs a force-add to be committed:

git add -f packages/front/fw-wasm-crypto/dist/MANIFEST.sha256

| Exit | Meaning | |---|---| | 0 | copy applied, or (--check) no drift | | 1 | --check found drift (one line per drifted file on stderr), or --target matched no file | | 2 | config error: missing --pkg, missing --into, missing dist/, missing manifest |

The gate is manifest-vs-committed-bytes, not fresh-build-vs-committed — a full rebuild needs the wasi-sdk, which is not available in CI today (see "Toolchain acquisition" below). Build-vs-committed reproducibility is a separate, later concern.

Dependency graph

The chain is @awacloud/tool-wasm-crypto (this tool) → @awacloud/fw-wasm-crypto (the package that owns targets.json + csrc/shims/vendor and consumes build/copy) → @awacloud/fw (the package whose crypto/wasm/*.wasm are the committed copy destination). Both edges are declared workspace:* devDependencies — build/dev-only, never a runtime dependency: this tool is invoked from fw-wasm-crypto's own scripts.build/verify/copy, and fw/src/crypto/wasm/runtime.js loads only the colocated local .wasm bytes, never reaching into either sibling package at runtime — gated by a self-containment test in @awacloud/fw.

Toolchain acquisition

build-env/ is a self-contained, crypto-free toolchain-acquisition module: it owns its wasi-sdk pin table (build-env/pins.ts) and imports nothing from src/. The SDK itself is never committed — the default install directory build-env/sdk/ is gitignored.

| File | Role | |---|---| | build-env/pins.ts | Pin table: repo, release tag, per-OS asset + sha256. Single source of truth for the SDK version. | | build-env/toolchain.ts | resolveAsset · discoverWasiSdk (FS-only) · downloadWasiSdk · verifyAndUnpack · enforcePinPolicy. |

import { downloadWasiSdk } from "tools/wasm-crypto/build-env/toolchain.ts";
const sdkRoot = await downloadWasiSdk();   // stream → verify → unpack

The asset is streamed to disk, hashed while streaming, and extracted from that file — neither the compressed nor the decompressed archive is ever fully buffered in memory. The built-in tar reader handles GNU long names (L), pax extended headers (x/g), directories, symlinks and hardlinks; on Windows a symlink the OS refuses is materialised as a file copy with a warning, never silently skipped.

Pin policy (reject-by-default)

Each asset's sha256 is either a real 64-hex digest or the literal sentinel "blocked-pending-rerun", meaning no maintainer of this repository has ever fetched and verified that asset. Today 1 of 5 is pinned (win32-x64); the other four carry the sentinel.

| Situation | Behaviour | |---|---| | Real pin, digest matches | proceeds | | Real pin, digest differs | throws — always, not opt-outable | | Unpinned (sentinel/empty/malformed) | throws by default | | Unpinned + maintainer opt-in | proceeds and prints the computed hash in pin-table form |

Maintainer first fetch

Fetching an unpinned asset is a deliberate, human act:

# 1. On a trusted network, confirm the release tag in build-env/pins.ts.
# 2. Opt in explicitly (env seam; `acceptUnpinned: true` when called as a library):
AWA_WASI_SDK_ACCEPT_UNPINNED=1 bun -e \
  "import { downloadWasiSdk } from './tools/wasm-crypto/build-env/toolchain.ts'; await downloadWasiSdk();"
# 3. Copy the printed pin-table line into build-env/pins.ts and commit it.

Tests

bun test tools/wasm-crypto/

Unit tests cover: CLI contract (--help, --version, unknown subcommand), stub exit codes, validateTargets schema validation, no ANSI in non-TTY output.

Structure

tools/wasm-crypto/
├── src/
│   ├── index.ts                    # CLI entry + subcommand dispatcher; --pkg flag parsing
│   ├── index.test.ts               # CLI contract + schema validation tests
│   ├── targets.schema.ts           # Target type + cStdFor rule + validateTargets guard
│   ├── abi-host.ts                 # ABI instantiation for wasm module loading
│   ├── emit.ts                     # Base64-embedded .wasm.js module writer
│   └── cmd/
│       ├── build.ts                # Compile in SIMD+scalar variants; package-relative resolution
│       ├── verify.ts               # KAT/ACVP gate; byte-for-byte vector checking
│       └── copy.ts                 # dist/ → committed dir + MANIFEST.sha256 (--check drift gate)
├── build-env/
│   ├── pins.ts                     # wasi-sdk pin table (self-contained, crypto-free)
│   ├── toolchain.ts                # Stream-to-disk download, pin policy, tar/zip extraction
│   ├── container.ts                # Containerized linux-x64 build (podman-first)
│   └── sdk/                        # Gitignored install dir (never committed)
├── wasm-crypto.config.example.json # Config template
├── package.json
└── README.md

See also

  • WASI SDK — clang toolchain for WebAssembly.
  • @awacloud/fw — the browser framework whose src/crypto/wasm/ is this tool's compiled-output destination.
  • @awacloud/fw-wasm-crypto — the package that owns targets.json and consumes build/copy.

Licence

Apache-2.0 — see LICENSE in this package.

Copyright (c) 2026 AwaCloud SAS

Project

  • Website: https://awaforge.eu
  • Source: tools/wasm-crypto
  • Issues: this package's own repository has issues disabled — report at https://github.com/awacloud/awa/issues
  • Security policy and release verification: https://github.com/awacloud/awa/blob/main/SECURITY.md
  • Maintenance policy: https://github.com/awacloud/awa/blob/main/MAINTENANCE.md

A CycloneDX 1.6 and SPDX 2.3 SBOM is generated for each published release.

Developed by AwaCloud.