# WasmHub > Open-source WASM Hub of language runtimes — download, cache, and verify versioned WebAssembly runtimes for Go, Rust, and more. Source: https://anistark.github.io/wasmhub Repository: https://github.com/anistark/wasmhub This file concatenates every documentation page as plain markdown, intended for LLM context ingestion. --- # WasmHub URL: https://anistark.github.io/wasmhub/ **Open-source WASM Hub of language runtimes.** Download and manage versioned WebAssembly runtimes for Go, Rust, Node.js, and more — usable as a Rust library, a CLI tool, or via CDN. ## Why WasmHub? - **Multi-language** — Go, Rust, Node.js (alpha), and the swc TypeScript transpiler (alpha) today; Python, Ruby, PHP planned - **Smart caching** — download once, use forever - **Type-safe library** — Rust API with compile-time guarantees - **Multi-CDN fallback** — GitHub Releases + jsDelivr with automatic failover - **SHA256 verification** — every download is integrity-checked - **Cross-platform** — Linux, macOS, Windows ## Quick install As a CLI: ```sh cargo install wasmhub --features cli wasmhub get go 1.23 ``` As a library: ```toml [dependencies] wasmhub = "0.3" tokio = { version = "1", features = ["full"] } ``` ```rust use wasmhub::{RuntimeLoader, Language}; #[tokio::main] async fn main() -> wasmhub::Result<()> { let loader = RuntimeLoader::new()?; let go = loader.get_runtime(Language::Go, "1.23").await?; println!("Runtime at: {}", go.path.display()); Ok(()) } ``` ## Where next - [Getting Started](/getting-started/) — install, first runtime, basic usage - [CLI Reference](/cli/) — every command and flag - [Library Guide](/library/) — Rust API, builder config, feature flags - [Architecture](/architecture/) — how runtimes flow from source to your cache - [Manifest Format](/manifest-format/) — the JSON schema - [Adding a Runtime](/adding-a-runtime/) — contributor guide --- # Getting Started URL: https://anistark.github.io/wasmhub/getting-started/ ## Install ### CLI ```sh cargo install wasmhub --features cli ``` This installs the `wasmhub` binary. Run `wasmhub --help` to verify. ### Library ```toml [dependencies] wasmhub = "0.3" tokio = { version = "1", features = ["full"] } ``` For download progress bars, enable the `progress` feature: ```toml wasmhub = { version = "0.3", features = ["progress"] } ``` ## Your first runtime ```sh wasmhub get go 1.23 # or try Node.js (alpha) wasmhub get nodejs 20 ``` What happens: 1. WasmHub fetches `go-manifest.json` from GitHub Releases 2. Resolves the entry for version `1.23` 3. Downloads the WASM binary (with retries, backoff, multi-CDN fallback) 4. Verifies the SHA256 against the manifest 5. Caches it under `~/.cache/wasmhub/go/1.23/` Subsequent calls return the cached binary instantly. ## Inspect the cache ```sh wasmhub cache show ``` ```sh wasmhub info go 1.23 ``` ## Use from Rust ```rust use wasmhub::{RuntimeLoader, Language}; #[tokio::main] async fn main() -> wasmhub::Result<()> { let loader = RuntimeLoader::new()?; let go = loader.get_runtime(Language::Go, "1.23").await?; println!("Path: {}", go.path.display()); println!("Size: {} bytes", go.size); println!("SHA256: {}", go.sha256); Ok(()) } ``` The returned `Runtime` is a small struct — `path`, `size`, `sha256`, `language`, `version`. Pass `path` to your WASM runtime of choice (wasmtime, wasmer, etc.). ## Next steps - [CLI Reference](/cli/) — full command list - [Library Guide](/library/) — builder config, custom CDN sources, retry tuning --- # Go runtime URL: https://anistark.github.io/wasmhub/runtimes/go/ ## At a glance | | | |--|--| | **Compiler** | TinyGo 0.34.0 | | **Target** | `wasm32-wasip1` | | **Available versions** | 1.23 | | **Binary size** | ~261 KB (post `wasm-opt -O3`) | | **License** | BSD-3-Clause (Go), BSD-3-Clause (TinyGo) | | **Source** | | ## Capabilities - Filesystem (read/write) - Environment variables - Command-line args - Standard I/O (stdin/stdout/stderr) ## Limitations - No cgo (TinyGo restriction) - Reduced standard library (TinyGo subset — see [TinyGo's compatibility matrix](https://tinygo.org/docs/reference/lang-support/stdlib/)) - No runtime reflection beyond what TinyGo supports - Single-threaded — goroutines cooperatively schedule via the scheduler, but no OS threads ## Install ```sh wasmhub get go 1.23 ``` ## Use from Rust ```rust use wasmhub::{RuntimeLoader, Language}; let loader = RuntimeLoader::new()?; let go = loader.get_runtime(Language::Go, "1.23").await?; // Pass go.path to your WASM runtime ``` --- # CLI Reference URL: https://anistark.github.io/wasmhub/cli/ ## Install ```sh cargo install wasmhub --features cli ``` ## Commands ```sh wasmhub get [version] # Download runtime (default: latest) wasmhub get --force # Force re-download wasmhub list [language] # List available runtimes wasmhub info [version] # Show runtime details wasmhub cache show # Show cache contents wasmhub cache clear # Clear specific cached runtime wasmhub cache clear-all [--yes] # Clear all cache ``` ## Language aliases | Language | Accepted values | |----------|----------------| | Node.js | `nodejs`, `node`, `node.js` | | Python | `python`, `py` | | Ruby | `ruby`, `rb` | | PHP | `php` | | Go | `go`, `golang` | | Rust | `rust`, `rs` | | swc | `swc` | ## Examples Download the latest Go runtime: ```sh wasmhub get go ``` Pin a specific version: ```sh wasmhub get rust 1.82 ``` Force a re-download even if cached: ```sh wasmhub get go 1.23 --force ``` Inspect what's cached: ```sh wasmhub cache show ``` Clear everything (with confirmation): ```sh wasmhub cache clear-all ``` Skip the prompt: ```sh wasmhub cache clear-all --yes ``` ## Exit codes | Code | Meaning | |------|---------| | `0` | Success | | `1` | General error (network, IO, integrity check failed, etc.) | | `2` | Bad arguments | --- # Rust runtime URL: https://anistark.github.io/wasmhub/runtimes/rust/ ## At a glance | | | |--|--| | **Compiler** | rustc 1.82.0 | | **Target** | `wasm32-wasip1` | | **Available versions** | 1.82 | | **Binary size** | ~76 KB (post `wasm-opt -O3`) | | **License** | MIT/Apache-2.0 | | **Source** | | ## Capabilities - Full `std` library - Filesystem - Environment + args - Standard I/O ## Limitations - No threading on `wasip1` (single-threaded only) - No `std::net` socket support yet (waiting on WASI networking) - Async works with single-threaded executors (`futures::executor::block_on`, custom runtimes) ## Install ```sh wasmhub get rust 1.82 ``` ## Use from Rust ```rust use wasmhub::{RuntimeLoader, Language}; let loader = RuntimeLoader::new()?; let rust = loader.get_runtime(Language::Rust, "1.82").await?; ``` --- # Library Guide URL: https://anistark.github.io/wasmhub/library/ ## Add the dependency ```toml [dependencies] wasmhub = "0.3" tokio = { version = "1", features = ["full"] } ``` ## Default usage ```rust use wasmhub::{RuntimeLoader, Language}; #[tokio::main] async fn main() -> wasmhub::Result<()> { let loader = RuntimeLoader::new()?; let runtime = loader.get_runtime(Language::Go, "1.23").await?; println!("Path: {}", runtime.path.display()); println!("SHA256: {}", runtime.sha256); let manifest = loader.list_available().await?; for (lang, info) in &manifest.languages { println!("{}: latest = {}", lang, info.latest); } let latest = loader.get_latest_version(Language::Go).await?; println!("Latest Go: {}", latest); loader.clear_cache(Language::Go, "1.23")?; loader.clear_all_cache()?; Ok(()) } ``` ## Builder configuration Customize cache location, CDN sources, and retry behavior: ```rust use wasmhub::{RuntimeLoader, CdnSource}; use std::path::PathBuf; let loader = RuntimeLoader::builder() .cache_dir(PathBuf::from("/tmp/my-cache")) .cdn_sources(vec![CdnSource::GitHubReleases]) .max_retries(5) .initial_backoff_ms(1000) .max_backoff_ms(60_000) .build()?; ``` ## Feature flags | Feature | What it enables | Default | |-----------|------------------------------------------------------------|---------| | *(none)* | Library only | yes | | `progress`| Download progress bars (`indicatif`) | no | | `cli` | CLI binary + `clap`, `anyhow`, `colored` + `progress` | no | ## Error handling All fallible APIs return `wasmhub::Result`. The `Error` enum (in `wasmhub::error`) covers: - `RuntimeNotFound` — manifest didn't list that language/version - `Network` — wrapped reqwest error - `Io` — wrapped std::io error - `IntegrityCheckFailed` — downloaded SHA256 didn't match the manifest Match on these for finer control, or just propagate with `?`. ## Async runtime The library is async-first and uses `tokio`. If your app uses a different runtime, you'll need to bridge — there's no sync API surface yet. --- # Node.js runtime URL: https://anistark.github.io/wasmhub/runtimes/nodejs/ ## Status **Available** — `nodejs-20.wasm` is fully working. Built with [QuickJS](https://bellard.org/quickjs/) compiled to WASM via the WASI SDK. ## At a glance | | | |--|--| | **Engine** | QuickJS 2024-01-13 (ES2020) | | **Node.js compat** | v20.x API surface | | **Binary size** | ~1.1 MB (optimized) | | **Target** | `wasm32-wasi` (WASI Preview 1) | | **License** | MIT | | **Source** | | ## Capabilities - `eval` — evaluate JavaScript expressions (including complex ES2020) - `run` — execute a `.js` file with CommonJS `require()` (requires WASI filesystem pre-open) - `echo` — print arguments to stdout - `env` — print environment variables - `version` — print runtime info - Environment variables via WASI - Command-line args - Standard I/O (stdin/stdout/stderr) - Filesystem read/write (via WASI pre-open) - ES2020: async/await, optional chaining, nullish coalescing, BigInt - **CommonJS `require()`** with relative paths (`./foo`), absolute paths (`/abs`), JSON imports, `package.json` `main` resolution, and `node_modules` lookup walking up the directory tree - `module.exports`, `exports`, `__filename`, `__dirname`, `require.cache`, `require.resolve`, `require.main` - **Built-in modules:** `path`, `fs`, `os`, `buffer`, `events`, `util`, `assert`, `stream` (also under the `node:` prefix) - **`events`** — full `EventEmitter` (`on`/`once`/`off`/`prependListener`/`removeAllListeners`/`emit`/`listeners`/`listenerCount`/`eventNames`, the `error` special-case, `newListener`/`removeListener` meta-events, static `EventEmitter.once`) - **`util`** — `format`, `inspect`, `inherits`, `promisify`, `callbackify`, `deprecate`, `debuglog`, `isDeepStrictEqual`, `types.*`, `TextEncoder`/`TextDecoder` - **`assert`** — `ok`/`equal`/`strictEqual`/`deepStrictEqual`/`throws`/`rejects`/`ifError`/`match`/… plus `assert.strict` and `AssertionError` - **`stream`** — `Readable` (incl. `Readable.from`), `Writable`, `Duplex`, `Transform`, `PassThrough`, `pipeline`, `finished`, `.pipe()` - **`Buffer`** — full `Uint8Array`-subclass implementation: `from`/`alloc`/`allocUnsafe`/`concat`/`isBuffer`/`byteLength`/`compare`, `toString`/`write`/`slice`/`copy`/`fill`/`equals`/`indexOf`/`includes`, and fixed-width int/float accessors (`readUInt32BE`, `writeDoubleLE`, …). Encodings: `utf8`, `hex`, `base64`, `base64url`, `latin1`, `ascii`, `utf16le` - **`TextEncoder` / `TextDecoder`** (utf-8), plus `atob` / `btoa` globals - **Binary file I/O:** `fs.readFileSync(path)` returns a `Buffer` (or a string when an encoding is given); `fs.writeFileSync` / `appendFileSync` accept a `Buffer`/`Uint8Array` or string - **Globals:** `process` (`argv`, `env`, `cwd()`, `exit()`, `platform`, `stdout.write`, `stderr.write`, `nextTick`, `hrtime`), `global`, `console` - **Timers & event loop:** `setTimeout`, `clearTimeout`, `setInterval`, `clearInterval`, `setImmediate`, `clearImmediate`, `queueMicrotask`, and a deferred `process.nextTick` — driven by the QuickJS event loop. `async`/`await`, Promise chains, and timer callbacks resolve after the entry script returns and the loop drains. - **Web platform globals:** `URL` / `URLSearchParams` (WHATWG parsing, relative resolution against a base, `searchParams` kept in sync with the URL), `crypto.getRandomValues` / `crypto.randomUUID` (entropy from the WASI `random_get` syscall via `os.getentropy`), `structuredClone` (cycles, `Map`/`Set`/`Date`/`RegExp`/`ArrayBuffer`/TypedArrays; functions and symbols throw `DataCloneError`), and `fetch` — defined but always rejecting with a clear network-unsupported error (`code: 'ERR_NETWORK_UNSUPPORTED'`) rather than a bare `ReferenceError` ## Limitations - No networking (WASI Preview 1 has no socket API); `fetch` exists but rejects with a clear error - No worker threads - No native addons (.node files) - Built-in modules cover common APIs but not everything — `require('crypto')`/`require('url')` are not implemented as modules (the `crypto`/`URL` *globals* are — see above), and `http`/`https`/`net` (no sockets under WASI), `querystring`, `zlib`, `child_process`, `worker_threads` are not implemented; `fs` is synchronous-only (no callback/promise API, no `fs.createReadStream`) - `Buffer` covers the common API but not everything (e.g. `swap16`/`swap32`, `BigInt64` accessors); `TextDecoder` is utf-8 only - `stream` is a pragmatic subset (no full backpressure/highWaterMark semantics, no async iterators); `util.inspect` output approximates Node's but is not byte-identical - Timers return a numeric id (browser-style), not a Node `Timeout` object — `.ref()`/`.unref()` are unavailable. `process.nextTick` is a microtask (no separate higher-priority queue), and the trailing-args forms are supported ## Install ```sh wasmhub get nodejs 20 ``` ## Usage examples ```sh # Print version info wasmrun exec nodejs-20.wasm -- version # Evaluate JavaScript wasmrun exec nodejs-20.wasm -- eval "1 + 1" # → 2 # Complex expressions wasmrun exec nodejs-20.wasm -- eval "[1,2,3].map(x => x * x).join(',')" # → 1,4,9 # Echo arguments wasmrun exec nodejs-20.wasm -- echo hello world # → hello world # Print env wasmrun exec nodejs-20.wasm -- env # Run a JS file (requires --dir mount) wasmrun exec --dir /path/to/scripts nodejs-20.wasm -- run /path/to/scripts/app.js ``` ## CommonJS `require()` A worked example is in `tests/runtimes/nodejs/fixtures/`: ```js // app.js const path = require("path"); const { square } = require("./math"); const config = require("./config.json"); const greet = require("greet"); // resolves via node_modules/greet/package.json console.log(square(4), config.name, greet("world")); console.log("entry:", path.basename(__filename)); console.log("require.main===module:", require.main === module); ``` Resolution rules (mirroring Node.js for the supported subset): 1. **Built-in** — `path`, `fs`, `os`, `node:path`, `node:fs`, `node:os`. 2. **Relative / absolute** — `./x`, `../x`, `/abs/x`. Tries `x`, `x.js`, `x.json`, `x/package.json` `main` field, `x/index.js`, `x/index.json`. 3. **Bare specifier** — walks up from the requiring file's directory, looking for `node_modules/` and applying the same file/dir rules. Modules are evaluated inside `new Function('exports','require','module','__filename','__dirname', src)`, the same wrapper Node.js uses. Cached in `require.cache` keyed by resolved filename. ## Use from Rust ```rust use wasmhub::{RuntimeLoader, Language}; let loader = RuntimeLoader::new()?; let nodejs = loader.get_runtime(Language::NodeJs, "20").await?; // Pass nodejs.path to your WASM runtime (wasmtime, wasmrun, etc.) ``` ## Building from source ```sh just build-nodejs ``` Requires Docker (runs inside `wasmhub-builder`). The build: 1. Downloads QuickJS 2024-01-13 source 2. Compiles `main.js` to C bytecode via native `qjsc` 3. Cross-compiles all sources with WASI SDK clang (`wasm32-wasi` target) 4. Links with 8 MB C stack (required for QuickJS's parser depth) 5. Optimizes with `wasm-opt -O3` ## Technical notes The runtime is built from QuickJS rather than full Node.js because Node.js (V8 + libuv) cannot currently compile to WASM/WASI. QuickJS is a complete ES2020 engine in ~210 KB of C, and compiles cleanly with the WASI SDK. Three non-obvious build issues were debugged and fixed: - **C stack overflow** — QuickJS's parser uses deep call frames. The default WASM C stack (64 KB) is too small; fixed with `-Wl,-z,stack-size=8388608`. - **`-fbignum` incompatibility** — `qjsc -fbignum` emits BigNum intrinsics that fail in WASI; removed. - **Module linking phase** — QuickJS runs the module body during _linking_ before C module `init_func`s run, so `std.out` is `undefined` at that point; guarded with `if (std.out)`. ## Roadmap - [ ] Node.js v22 and v24 builds - [x] `node:fs` shim via WASI filesystem APIs (minimal synchronous subset) - [x] CommonJS `require()` support — implemented in `main.js` (no bundler pre-pass needed) - [x] `Buffer` and binary `fs` reads — `Uint8Array`-subclass `Buffer`, `TextEncoder`/`TextDecoder`, and `fs.readFileSync`→`Buffer` - [x] `events`, `util`, `assert`, `stream` built-ins - [ ] `crypto`, `url`, `querystring`, `zlib` built-ins - [x] Event-loop driven `setTimeout` / `setInterval` exposed as globals (plus `setImmediate`, `queueMicrotask`, deferred `process.nextTick`) --- # Architecture URL: https://anistark.github.io/wasmhub/architecture/ ## High-level flow ``` runtimes//source code ↓ scripts/build-.sh (inside Docker) build//-.wasm ↓ wasm-opt optimization (--enable-bulk-memory) runtimes//-.wasm ↓ scripts/verify-binary.sh (magic number, SHA256) ↓ scripts/generate-metadata.sh (per-language manifest) runtimes//manifest.json ↓ scripts/generate-global-manifest.sh manifest.json (root, aggregated) ↓ release.yml CI workflow GitHub Releases (.wasm + .wasm.gz + .wasm.br + manifests + SHA256SUMS) ↓ RuntimeLoader (with retry + multi-CDN fallback) ~/.cache/wasmhub/// ``` ## Source layout ``` src/ ├── lib.rs Library entry point, public exports ├── loader.rs RuntimeLoader — download, cache lookup, manifest fetching ├── cache.rs CacheManager — local filesystem cache ├── runtime.rs Language enum, Runtime struct ├── manifest.rs GlobalManifest, RuntimeManifest, RuntimeVersion types ├── error.rs Error enum, Result type alias └── bin/ └── wasmhub.rs CLI binary (feature-gated behind "cli") ``` ## CDN fallback The loader tries CDN sources in order. On any failure (network error, 5xx, integrity mismatch), it moves to the next source. Default order: 1. **GitHub Releases** — primary, no rate limits 2. **jsDelivr** — CDN cache layer (12-24h sync delay after a release) Configure via `RuntimeLoader::builder().cdn_sources(...)`. ## Retry strategy Each download attempt uses exponential backoff: - `initial_backoff_ms` — first delay (default: 500ms) - `max_backoff_ms` — cap on the delay (default: 30s) - `max_retries` — total attempts (default: 3) Retries fire on transient failures (network, 5xx). Permanent failures (404, integrity check) fail fast. ## Cache layout ``` ~/.cache/wasmhub/ ├── go/ │ ├── 1.23/ │ │ ├── go-1.23.wasm │ │ └── manifest.json │ └── ... └── rust/ └── 1.82/ ├── rust-1.82.wasm └── manifest.json ``` The cache is content-addressed by language + version. Integrity checks happen on every read against the cached manifest entry. ## Why WASI Preview 1? All shipped runtimes target `wasm32-wasip1`. WASI Preview 2 (component model) is still stabilizing across runtimes — `wasip1` gives the broadest compatibility today. We'll add `wasip2` outputs once tooling matures across the runtime ecosystem. --- # swc transpiler URL: https://anistark.github.io/wasmhub/runtimes/swc/ ## Status **Alpha** — `swc-73.wasm` is available. A tool artifact rather than a language runtime: a small WASI CLI over [swc_core](https://swc.rs/) that transpiles TypeScript to JavaScript. It exists to feed the [Node.js runtime](/runtimes/nodejs/), which only speaks CommonJS. ## At a glance | | | |--|--| | **Engine** | `swc_core` 73.x | | **Binary size** | ~2.4 MB (optimized) | | **Target** | `wasm32-wasip1` (WASI Preview 1), **MVP-only instructions** | | **License** | Apache-2.0 | | **Source** | | ## Capabilities - `version` — print transpiler version - `swc ...` — transpile each input, writing a sibling `.js` file (directories preserved) - Types stripped (including decorators syntax in the parser) - TSX lowered to `React.createElement` - ES modules lowered to CommonJS with interop helpers **inlined** into the output (no `@swc/helpers` dependency at runtime) - Parse errors — including recoverable ones — go to stderr as `error: ::: ` referencing the original TypeScript source, and any failure exits non-zero Inputs must end in `.ts` or `.tsx`, so filenames can never collide with the `version` subcommand. ## Limitations - Transpile only — no type checking - No source maps - Output is CommonJS only (by design, for the Node.js runtime) - Reads and writes through the WASI filesystem, so the host must pre-open the directory containing the inputs ## Install ```sh wasmhub get swc 73 ``` ## Usage examples ```sh # Print version info wasmrun exec swc-73.wasm -- version # Transpile a file (requires --dir mount); writes app.js next to app.ts wasmrun exec --dir /path/to/src swc-73.wasm -- /path/to/src/app.ts # Multiple inputs, TSX included wasmrun exec --dir /path/to/src swc-73.wasm -- /path/to/src/app.ts /path/to/src/view.tsx # Then run the output with the Node.js runtime wasmrun exec --dir /path/to/src nodejs-20.wasm -- run /path/to/src/app.js ``` ## Use from Rust ```rust use wasmhub::{RuntimeLoader, Language}; let loader = RuntimeLoader::new()?; let swc = loader.get_runtime(Language::Swc, "73").await?; // Pass swc.path to your WASM runtime (wasmtime, wasmrun, etc.) ``` ## Building from source ```sh ./scripts/build-swc.sh runtimes/swc ``` Or inside Docker via the aggregate build: `BUILD_SWC=true ./scripts/build-all.sh`. ## Technical notes This is the one build that deliberately deviates from `scripts/build-rust.sh`: rustc 1.82+ emits post-MVP WASM instructions by default (multi-value, sign-ext, bulk-memory, nontrapping-fptoint), which downstream interpreters — notably wasmrun's exec mode, the primary consumer — reject at parse time. The build script therefore: 1. Rebuilds `std` with nightly `-Zbuild-std` under `-C target-cpu=mvp` 2. Runs `wasm-opt` with the plain MVP feature set, which doubles as validation that nothing post-MVP slipped through The version label is `73` — the `swc_core` major — following the upstream-major naming of `nodejs-20` and `rust-1.82`. ## Roadmap - [ ] Source map output - [ ] Configurable JSX pragma / automatic JSX runtime - [ ] ESM output mode (once the Node.js runtime supports ES modules) --- # Manifest Format URL: https://anistark.github.io/wasmhub/manifest-format/ ## Per-language manifest Every runtime ships a `manifest.json` that enumerates its versions: ```json { "language": "go", "latest": "1.23", "versions": { "1.23": { "file": "go-1.23.wasm", "size": 266712, "sha256": "efa1e13f39dfd3783d0eff5669088ab99a1ea1d38ac79f29b02e2ad8ddfea29d", "released": "2026-02-03T13:23:13Z", "wasi": "wasip1", "features": [] } } } ``` ### Fields | Field | Type | Notes | |------------|--------|-------| | `language` | string | Canonical lowercase name (e.g., `go`, `rust`) | | `latest` | string | Pointer to a key in `versions` | | `versions` | map | Keyed by version string | | `versions[].file` | string | Filename of the published WASM artifact | | `versions[].size` | number | Bytes (post-optimization) | | `versions[].sha256` | string | Hex-encoded SHA-256 of `file` | | `versions[].released` | string | ISO-8601 timestamp | | `versions[].wasi` | string | WASI variant — `wasip1` today | | `versions[].features` | array | Optional WASM proposals (e.g., `bulk-memory`) | `generate-metadata.sh` writes this file. The `latest` field auto-updates to the newest version when a build runs. Old version entries are preserved. ## Global manifest A single `manifest.json` at the repo root aggregates every per-language manifest: ```json { "version": "0.3.2", "build_date": "2026-02-15T14:00:00Z", "languages": { "go": { "latest": "1.23", "versions": ["1.23"], "source": "https://go.dev/", "license": "BSD-3-Clause" }, "rust": { "latest": "1.82", "versions": ["1.82"], "source": "https://www.rust-lang.org/", "license": "MIT/Apache-2.0" } } } ``` The `source` and `license` fields come from `scripts/generate-global-manifest.sh`. The aggregator runs after every build; it never edits per-language files. ## Where they live - **Repo:** `runtimes//manifest.json` (per-language) and `manifest.json` (global) are committed - **Releases:** Both ship as GitHub Release assets — per-language manifests are renamed to `-manifest.json` to keep the assets directory flat --- # Adding a Runtime URL: https://anistark.github.io/wasmhub/adding-a-runtime/ Adding a language runtime touches the build pipeline, the Rust library, the CI workflows, and the manifests. Follow every step. ## 1. Create the runtime source ``` runtimes// ├── # The actual program to compile to WASM └── manifest.json # Generated by build script ``` The runtime source should implement a standard set of commands: `version`, `eval`, `env`, `echo`, `cat`, `ls`, `write`. See `runtimes/go/main.go` and `runtimes/rust/src/main.rs` for the expected interface. ## 2. Create the build script `scripts/build-.sh`. Follow the pattern in `scripts/build-go.sh` or `scripts/build-rust.sh`: - Accept source path as argument - Support `--version`, `--output`, `--no-optimize` flags - Compile to WASM targeting WASI Preview 1 (`wasip1`) - Run `wasm-opt` with `--enable-bulk-memory` if available - Copy output to `runtimes//` - Call `scripts/generate-metadata.sh` to update the manifest ## 3. Register in build-all.sh Add a build block to `scripts/build-all.sh` with an env flag (`BUILD_`) for selective builds. ## 4. Update the Dockerfile Add the language's compiler/toolchain to `Dockerfile`. Match the existing pattern (Go, TinyGo, Rust). ## 5. Update the Rust library - Add a variant to `Language` enum in `src/runtime.rs` - Add `as_str()`, `FromStr`, and `Display` implementations - Add to `Language::all()` array ## 6. Update CI - Add a matrix entry in `.github/workflows/build-runtimes.yml` - The `release.yml` workflow picks up all runtimes automatically via `scripts/build-all.sh` ## 7. Update the global manifest generator Add a `source` URL and `license` to the `SOURCES` and `LICENSES` arrays in `scripts/generate-global-manifest.sh`. ## 8. Regenerate and verify ```sh just docker-build # Rebuild Docker image with new toolchain just docker-build-runtimes # Build all runtimes just manifest # Regenerate global manifest ``` ## 9. Update docs - Add a page under `docs/runtimes/.md` documenting capabilities, limitations, source link - Update [Manifest Format](/manifest-format/) if you introduce new fields ## 10. Submit the PR Run `just ci` first — clippy must pass with zero warnings, format check must be clean, all tests must pass. --- # Runtimes URL: https://anistark.github.io/wasmhub/runtimes/ | Language | Version | Size | Status | |----------|---------|------|--------| | [Go](/runtimes/go/) | 1.23 | 261 KB | ✅ Available | | [Rust](/runtimes/rust/) | 1.82 | 76 KB | ✅ Available | | [Node.js](/runtimes/nodejs/) | 20 | ~1.1 MB | 🚧 Alpha | | [swc](/runtimes/swc/) | 73 | ~2.4 MB | 🚧 Alpha | | Python | — | — | Coming soon | | Ruby | — | — | Coming soon | | PHP | — | — | Coming soon | All shipped runtimes target **WASI Preview 1** (`wasip1`). swc is a tool artifact (a TypeScript → JavaScript transpiler) rather than a language runtime.