bytecodealliance/javy
JS to WebAssembly toolchain
Healthy across the board
weakest axisPermissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 1d ago
- ✓10 active contributors
- ✓Apache-2.0 licensed
Show all 6 evidence items →Show less
- ✓CI configured
- ✓Tests present
- ⚠Concentrated ownership — top contributor handles 61% of recent commits
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/bytecodealliance/javy)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/bytecodealliance/javy on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: bytecodealliance/javy
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/bytecodealliance/javy shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across the board
- Last commit 1d ago
- 10 active contributors
- Apache-2.0 licensed
- CI configured
- Tests present
- ⚠ Concentrated ownership — top contributor handles 61% of recent commits
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live bytecodealliance/javy
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/bytecodealliance/javy.
What it runs against: a local clone of bytecodealliance/javy — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in bytecodealliance/javy | Confirms the artifact applies here, not a fork |
| 2 | License is still Apache-2.0 | Catches relicense before you depend on it |
| 3 | Default branch main exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 31 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of bytecodealliance/javy. If you don't
# have one yet, run these first:
#
# git clone https://github.com/bytecodealliance/javy.git
# cd javy
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of bytecodealliance/javy and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "bytecodealliance/javy(\\.git)?\\b" \\
&& ok "origin remote is bytecodealliance/javy" \\
|| miss "origin remote is not bytecodealliance/javy (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(Apache-2\\.0)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"Apache-2\\.0\"" package.json 2>/dev/null) \\
&& ok "license is Apache-2.0" \\
|| miss "license drift — was Apache-2.0 at generation time"
# 3. Default branch
git rev-parse --verify main >/dev/null 2>&1 \\
&& ok "default branch main exists" \\
|| miss "default branch main no longer exists"
# 4. Critical files exist
test -f "crates/cli/src/main.rs" \\
&& ok "crates/cli/src/main.rs" \\
|| miss "missing critical file: crates/cli/src/main.rs"
test -f "crates/codegen/src/lib.rs" \\
&& ok "crates/codegen/src/lib.rs" \\
|| miss "missing critical file: crates/codegen/src/lib.rs"
test -f "crates/javy/src/config.rs" \\
&& ok "crates/javy/src/config.rs" \\
|| miss "missing critical file: crates/javy/src/config.rs"
test -f "crates/codegen/src/exports.rs" \\
&& ok "crates/codegen/src/exports.rs" \\
|| miss "missing critical file: crates/codegen/src/exports.rs"
test -f "Cargo.toml" \\
&& ok "Cargo.toml" \\
|| miss "missing critical file: Cargo.toml"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 31 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~1d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/bytecodealliance/javy"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
Javy is a JavaScript-to-WebAssembly compiler that embeds a JS runtime (QuickJS-based via the javy crate) into Wasm modules, enabling you to execute JavaScript code as compiled Wasm binaries. It produces very small modules (1–16 KB with dynamic linking, ~869 KB static) that can be executed by any Wasm engine like Wasmtime, and exposes low-level I/O primitives (Javy.IO.readSync/writeSync) for stdin/stdout access. Monorepo with 8 crates: crates/cli is the user-facing command-line tool (entry point main.rs, commands.rs handling build/compile logic); crates/javy is the core JS-to-Wasm compiler; crates/codegen handles bytecode generation; crates/plugin-* and crates/test-plugin-* provide plugin APIs for extensibility; crates/runner executes compiled Wasm. Tests live in crates/cli/tests/ with sample JavaScript scripts in sample-scripts/ and dynamic-linking-scripts/.
👥Who it's for
Systems engineers and serverless platform builders who need to run untrusted or user-supplied JavaScript in sandboxed, portable Wasm environments (e.g., edge computing, function-as-a-service backends). Also useful for tool authors embedding JS scripting without a full Node.js dependency.
🌱Maturity & risk
Actively developed and production-viable: the project is maintained by the Bytecode Alliance (highly credible), has comprehensive CI pipelines (see .github/workflows/ci.yml, ci-npm-javy.yml, build-assets.yml), uses semantic versioning (currently v8.1.1), and includes integration tests (crates/cli/tests/integration_test.rs, dynamic_linking_test.rs). Recent commits and tagged releases visible; no signs of abandonment.
Moderate risk factors: the codebase is tightly coupled to specific versions of wasmtime (44), wasmtime-wasi, and deterministic-wasi-ctx, so ecosystem updates could break builds; the CLI tooling depends on QuickJS (vendored or linked) which has narrower ecosystem support than V8. Monorepo structure (8 crates) means breaking changes propagate quickly. However, Bytecode Alliance backing and active CI mitigates dependency neglect risk.
Active areas of work
The repo is actively maintained with recent releases and CI passing. Based on the file structure, focus areas include: dynamic linking support (dylib_test.rs, dynamic_linking_test.rs), plugin system evolution (crates/plugin-processing/, test plugins for wasip1 and wasip2), and npm package distribution (ci-npm-javy.yml workflow). Fuzz testing infrastructure exists (check-fuzz.yml, fuzz/ crate).
🚀Get running
git clone https://github.com/bytecodealliance/javy.git
cd javy
cargo build --release
./target/release/javy --help
echo '{"n": 2, "bar": "baz"}' | ./target/release/javy build crates/cli/tests/sample-scripts/empty.js -o /tmp/test.wasm && wasmtime /tmp/test.wasm
Daily commands:
Development build: cargo build (targets crates/cli). Release build: cargo build --release. Run CLI: ./target/release/javy build <script.js> -o <output.wasm>. Tests: cargo test --workspace. Fuzzing: cargo +nightly fuzz run [fuzzer-name] in the fuzz/ crate (see check-fuzz.yml).
🗺️Map of the codebase
crates/cli/src/main.rs— Entry point for the Javy CLI; orchestrates compilation from JS to WebAssembly and handles all user-facing commandscrates/codegen/src/lib.rs— Core code generation library that transforms JavaScript AST into WebAssembly bytecode; heart of the compilation pipelinecrates/javy/src/config.rs— Configuration system for the JavaScript runtime environment; defines how the embedded JS engine is initialized and customizedcrates/codegen/src/exports.rs— Handles export code generation for JavaScript functions to WebAssembly; critical for function boundary crossingCargo.toml— Workspace configuration defining all member crates, dependencies, and versioning; essential for understanding project structurecrates/cli/src/commands.rs— Implements all CLI subcommands for compilation, linking, and module management; defines the user-facing APIcrates/codegen/src/wit.rs— WebAssembly Interface Type (WIT) generation from JavaScript type annotations; enables type-safe cross-language calls
🛠️How to make changes
Add a new JavaScript API
- Create a new module file in crates/javy/src/apis/ (e.g., crypto.rs) (
crates/javy/src/apis/crypto.rs) - Implement the API functions using Rquickjs bindings (refer to console or json.rs as template) (
crates/javy/src/apis/crypto.rs) - Register the API in the module registry (
crates/javy/src/apis/mod.rs) - Add configuration option if needed (
crates/javy/src/config.rs) - Write integration tests using sample scripts (
crates/cli/tests/sample-scripts/crypto-test.js)
Add export support for a new function signature
- Define the WIT type in your JavaScript file with JSDoc type annotations (
crates/cli/tests/sample-scripts/exported-fn.js) - Update WIT generation logic to handle the new type (
crates/codegen/src/wit.rs) - Extend the export code generation to emit correct bindings (
crates/codegen/src/exports.rs) - Add integration test with example and expected .wit file (
crates/cli/tests/integration_test.rs)
Add a new CLI command or option
- Define the command structure and options (
crates/cli/src/option.rs) - Implement the command handler logic (
crates/cli/src/commands.rs) - Wire the command into the main CLI router (
crates/cli/src/main.rs) - Add CLI tests in integration test suite (
crates/cli/tests/integration_test.rs)
Create a dynamic linking plugin
- Create plugin crate with plugin-api dependency and export trait implementations (
crates/test-plugin-wasip2/src/lib.rs) - Define the WIT interface for your plugin (
crates/test-plugin-wasip2/interface.wit) - Compile plugin and register with CLI using --plugin flag (
crates/cli/src/commands.rs) - Write test script that imports and uses plugin functions (
crates/cli/tests/dynamic-linking-scripts/plugin-test.js)
🔧Why these technologies
- Rust + Rquickjs — Type-safe, high-performance systems language with proven JS engine bindings; enables minimal footprint and predictable runtime
- WebAssembly + WASI — Universal portable bytecode format; WASI provides system abstraction for CLI tools and plugin interop
- WIT (WebAssembly Interface Type) — Language-agnostic type system for cross-language function calls; enables safe plugin composition
- Wasmtime + Wasmtime-Wizer — Fast, standards-compliant Wasm runtime; Wizer enables ahead-of-time module initialization and state snapshotting
- Cargo workspace — Monorepo organization allowing modular crate separation (CLI, codegen, runtime, plugins) with shared dependencies
⚖️Trade-offs already made
-
Static linking (default) vs dynamic linking
- Why: Static produces 869KB+ modules (self-contained, no dependencies), dynamic produces 1-16KB modules (requires external plugin linking)
- Consequence: Users choose between portability (static) and code size (dynamic); dynamic adds complexity in plugin resolution and versioning
-
Top-level await support
- Why: Enables natural async JS patterns in compiled modules
- Consequence: Module initialization is async; callers must handle Promise-based startup overhead
-
Deterministic math/random API
- Why: Reproducible output across runs and platforms
- Consequence: No true randomness by default; users get seeded PRNGs; limits use in cryptography-critical code
-
Plugin system over monolithic runtime
- Why: Keeps core small; features are opt-in
- Consequence: Plugin compatibility matrix; versioning burden on users; increased build complexity
🚫Non-goals (don't propose these)
- Full Node.js API compatibility—only minimal standard library (console, JSON, TextEncoder, random)
- Browser-style DOM or event loop—single-threaded, synchronous execution model
- Runtime module bundling—JavaScript must be bundled by external tools before compilation
- Multi-language source support—JavaScript only; other languages compile to JS first
- Real-time performance guarantees—determinism prioritized over raw speed
🪤Traps & gotchas
Wasm runtime version pinning: wasmtime, wasmtime-wasi, and wasmtime-wizer are all pinned to v44 in Cargo.toml—mixing versions will cause link errors. QuickJS vendoring: the JS engine is tightly integrated; you cannot easily swap in V8 or SpiderMonkey. WIT (WebAssembly Interface Type) generation: exported functions require .wit interface files in the same directory (see dynamic-linking-scripts/); omitting this causes compilation failure. Deterministic mode: deterministic-wasi-ctx is mandatory for reproducible builds; non-deterministic host I/O can break hermetic compilation. Plugin loading: dynamic linking requires the Wasm target to be compatible with the host OS (Linux/macOS/Windows); cross-compilation of plugins is non-trivial.
🏗️Architecture
💡Concepts to learn
- WebAssembly Interface Types (WIT) — Javy uses WIT files (.wit) to declare exported function signatures and bridge JS to Wasm boundaries; you must author .wit files alongside your JS for complex exports
- Dynamic linking (WASI imports) — Javy's killer feature for tiny modules; instead of embedding the entire JS engine in each .wasm binary, dynamic linking imports engine functions from a shared host library, reducing module size from 869 KB to 1–16 KB
- Wasm module serialization (walrus AST) — Javy uses the walrus crate to construct Wasm modules as an abstract syntax tree, manipulate exports/imports, and emit binary; understanding this pipeline is crucial for debugging codegen
- QuickJS engine integration — Javy embeds QuickJS (a minimal JS engine) via Rust FFI; the engine is the runtime that actually executes your JavaScript, so its capabilities and limitations define what JS code can run
- Deterministic WASI context — The deterministic-wasi-ctx crate sandboxes and determinizes I/O (filesystem, system time, random); critical for reproducible, hermetic builds so identical JS always produces identical Wasm binaries
- Brotli compression — Javy optionally compresses compiled Wasm modules using Brotli (faster decompression than gzip for Wasm); see how brotli crate is integrated in codegen for module shrinking
- Plugin system (dylib loading) — Javy's plugin API (crates/plugin-api, crates/plugin-processing) allows custom compilation passes via dynamically loaded .so/.dll files; enables user-defined optimizations and extensions without forking the CLI
🔗Related repos
denoland/deno— Alternative JS-to-native compiler; uses V8 + Rust FFI rather than QuickJS, targets native binaries instead of Wasmbytecodealliance/wasmtime— The Wasm runtime engine that Javy depends on (v44); understanding Wasmtime is essential for debugging Wasm executionbytecodealliance/component-model— Emerging standard for Wasm component composition; Javy's WIT support aligns with component-model's interface definitionsbellard/quickjs— The upstream QuickJS project that powers Javy's JavaScript engine; source of truth for runtime behaviorWebAssembly/binaryen— The Binaryen optimizer (via wasm-opt crate) that Javy uses to shrink and optimize final .wasm modules
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add integration tests for dynamic linking with WIT interface validation
The repo has dynamic linking test scripts (crates/cli/tests/dynamic-linking-scripts/) with .wit files, but there's no comprehensive test suite validating WIT interface compliance across different function signatures and error cases. This would catch regressions in the WIT codegen pipeline and improve confidence in the dynamic linking feature.
- [ ] Create crates/cli/tests/wit_validation_test.rs with test cases for each .wit file in dynamic-linking-scripts/
- [ ] Add tests validating type mismatches between .wit declarations and .js exports
- [ ] Add tests for edge cases like optional parameters, union types, and complex nested types in WIT
- [ ] Ensure tests cover both successful linking and expected failures with clear error messages
Add fuzz testing coverage for plugin API boundary
The repo has a fuzzing setup (fuzz/ directory) and plugin system (crates/plugin*, crates/test-plugin-*), but the existing fuzzing likely focuses on core JS execution. Fuzzing the plugin API boundary would catch memory safety issues when plugins receive malformed or unexpected input from host code.
- [ ] Create fuzz/src/plugin_boundary.rs with fuzz targets for plugin API calls (functions/parameters from plugin-api/src/)
- [ ] Add fuzzing for wasmtime host<->plugin interface with arbitrary input generation
- [ ] Integrate with existing CI fuzzing workflow (.github/workflows/check-fuzz.yml)
- [ ] Document fuzzing setup in crates/plugin-api/README or main README.md
Add benchmark suite comparing static vs dynamic linking and optimization levels
The README highlights that dynamic linking produces 1-16 KB modules vs 869 KB+ for static linking, but there's no automated benchmark suite validating these claims or detecting regressions. This would help track performance and size tradeoffs as the codebase evolves.
- [ ] Create benches/ directory with Criterion benchmarks measuring module size (static, dynamic, with/without wasm-opt)
- [ ] Add benchmarks in crates/cli/benches/ measuring compile time for sample-scripts/ with different options
- [ ] Add GitHub Actions workflow (.github/workflows/bench.yml) to track size/performance metrics on each PR
- [ ] Document results in README.md with a benchmark table or section
🌿Good first issues
- Add integration tests for the
javy runcommand (if it exists) or for error cases in crates/cli/tests/integration_test.rs; currently sample-scripts/ covers happy paths but error.js is minimal. - Document the .wit interface file format and code-generation flow in README or docs/; currently only inferred from sample-scripts/ and dynamic-linking-scripts/ examples.
- Extend crates/cli/src/option.rs to support additional CLI flags (e.g., --optimize-level, --strip-debug) and wire them into commands.rs; current implementation is minimal.
⭐Top contributors
Click to expand
Top contributors
- @dependabot[bot] — 61 commits
- @jeffcharles — 24 commits
- @saulecabrera — 8 commits
- @saga-dasgupta — 1 commits
- @guest271314 — 1 commits
📝Recent commits
Click to expand
Recent commits
0cdbac2— chore: Updateflake.lock(#1205) (saulecabrera)21a3348— Bump wasmparser from 0.244.0 to 0.248.0 (#1195) (dependabot[bot])7a774ee— Bump wasmprinter from 0.244.0 to 0.246.2 (#1199) (dependabot[bot])052a86f— Bump wit-bindgen from 0.52.0 to 0.57.1 (#1191) (dependabot[bot])e825ede— Bump wit-parser from 0.244.0 to 0.248.0 (#1200) (dependabot[bot])4be6a68— Bump wasmtime + deterministic-wasi-ctx to 44.0.1 / 4.0.2 (#1204) (saga-dasgupta)21873b2— Bump swc_core from 60.0.0 to 65.0.1 (#1198) (dependabot[bot])1017439— build: Tighten crate selection (#1203) (saulecabrera)fdeea96— Bump toml_edit from 0.22.27 to 0.23.7 (#1201) (dependabot[bot])e67b6fd— Bump wit-component from 0.244.0 to 0.248.0 (#1202) (dependabot[bot])
🔒Security observations
The Javy JavaScript-to-WebAssembly toolchain demonstrates generally good security practices with fuzzing infrastructure and multiple security-focused test suites. However, there are concerns around the invalid Rust edition specification (2024), inconsistent internal versioning, and the need for a security policy. The plugin loading mechanism warrants careful review to ensure plugins are properly sandboxed. The codebase lacks a SECURITY.md file for vulnerability reporting. Overall security posture is moderately strong with room for improvement in documentation and dependency management practices.
- Medium · Outdated Edition Specification —
Cargo.toml - workspace.package.edition. The Cargo.toml workspace specifies edition = '2024', which is not a valid Rust edition. Valid editions are 2015, 2018, and 2021. This may cause build failures or unexpected behavior. The likely intended edition is '2021'. Fix: Change edition from '2024' to '2021' to use the latest stable Rust edition. - Medium · Dependency Version Pinning —
Cargo.toml - workspace.dependencies.deterministic-wasi-ctx. The dependency 'deterministic-wasi-ctx' uses exact version pinning (=4.0.2) which may prevent security updates. While this ensures determinism, it could delay critical security patches from being applied. Fix: Consider using '~4.0.2' or '^4.0.2' to allow patch and minor version updates respectively, while monitoring for updates. Alternatively, establish a regular dependency update schedule. - Low · Inconsistent Version References —
Cargo.toml - javy dependency path version. The workspace version is defined as '8.1.1', but the javy dependency is pinned to '7.0.1-alpha.1'. This version mismatch between the workspace and internal dependency may cause confusion during releases and could lead to version inconsistencies. Fix: Ensure the javy crate version matches the workspace version, or document why they differ. If javy is still in alpha, consider moving it out of alpha before release. - Low · Missing Security Policy —
Repository root. No SECURITY.md or security policy file is visible in the repository structure. This makes it difficult for security researchers to responsibly disclose vulnerabilities. Fix: Create a SECURITY.md file that describes how to responsibly report security vulnerabilities (e.g., via private security advisories rather than public issues). - Low · Dynamic Plugin Loading —
crates/cli/src/plugin.rs. The codebase includes plugin loading functionality (crates/cli/src/plugin.rs). Dynamic plugin loading from untrusted sources can be a security risk if plugins aren't properly validated or sandboxed. Fix: Ensure all plugins are validated, signed, or loaded from trusted sources only. Document plugin security requirements and implement integrity checks for dynamically loaded plugins. - Low · Fuzzing Infrastructure Present —
fuzz directory, .github/workflows/check-fuzz.yml. While fuzzing is a positive security practice, the presence of fuzz testing (fuzz directory and check-fuzz.yml workflow) indicates active fuzzing. Ensure fuzzing is continuously run and crashes/issues are properly tracked. Fix: Maintain regular fuzzing campaigns, document any findings, and ensure a process exists for handling discovered vulnerabilities. Consider integrating fuzzing into CI/CD pipeline.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.