# 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.