wasmhub 0.3.2

Download and manage WebAssembly runtimes for multiple languages
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
# AGENTS.md — AI Coding Agent Instructions for WasmHub

> Instructions for Claude Code, pi, Cursor, Copilot, and other AI coding agents working on this project.

---

## Project Overview

**WasmHub** is a centralized registry and package manager for WebAssembly language runtimes. It provides versioned WASM runtime binaries (Go, Rust, and more coming) that can be downloaded, cached, and verified — usable as a Rust library, CLI tool, or via CDN.

- **Repository:** https://github.com/anistark/wasmhub
- **Crate:** https://crates.io/crates/wasmhub
- **Docs:** https://docs.rs/wasmhub
- **License:** MIT
- **Minimum Rust Version:** 1.85

---

## ⚠️ The Runtime Build Pipeline — Read This First

The most critical part of this project is the pipeline that builds, verifies, and publishes WASM runtime binaries. Getting this wrong means shipping broken runtimes to every downstream consumer.

### How a Runtime Gets Built

```
runtimes/<lang>/source code
        ↓ scripts/build-<lang>.sh (inside Docker)
build/<lang>/<lang>-<version>.wasm
        ↓ wasm-opt optimization
runtimes/<lang>/<lang>-<version>.wasm
        ↓ scripts/verify-binary.sh (magic number, SHA256)
        ↓ scripts/generate-metadata.sh (updates per-language manifest)
runtimes/<lang>/manifest.json
        ↓ scripts/generate-global-manifest.sh
manifest.json (root)
```

### How Runtimes Get Released

1. **Local or CI build:** `scripts/build-all.sh` runs inside Docker (`wasmhub-builder` image) to compile all runtimes.
2. **Optimization:** `scripts/optimize-wasm.sh` runs `wasm-opt` with `-O3 --enable-bulk-memory`, generates gzip/brotli compressed variants.
3. **Verification:** `scripts/verify-binary.sh` checks WASM magic number (`0061736d`) and SHA256 checksums.
4. **Manifest generation:** `scripts/generate-metadata.sh` updates `runtimes/<lang>/manifest.json` with new size/sha256/date. `scripts/generate-global-manifest.sh` aggregates all per-language manifests into `manifest.json`.
5. **Release:** `just release <version>` or `scripts/publish.sh` creates a git tag, GitHub Release, and uploads all assets. The `release.yml` CI workflow then:
   - Builds all WASM runtimes in Docker
   - Optimizes and compresses them
   - Uploads `.wasm`, `.wasm.gz`, `.wasm.br`, per-language manifests, global `manifest.json`, and `SHA256SUMS` as release assets
   - Builds CLI binaries for Linux (x86_64, aarch64), macOS (x86_64, aarch64), and Windows (x86_64)
   - Publishes to crates.io

### Where Binaries Live

| Location | What | Committed? |
|----------|------|------------|
| `runtimes/<lang>/*.wasm` | Built WASM binaries | **No** — gitignored |
| `build/<lang>/` | Intermediate build output | **No** — gitignored |
| `release-assets/` | Staged release files | **No** — created by `publish.sh` |
| GitHub Releases | Published WASM + CLI binaries | Yes — the distribution point |
| `~/.cache/wasmhub/` | User's local cache (via library/CLI) | N/A — runtime |

### Critical Rules

1. **WASM binaries are never committed.** They're gitignored and published via GitHub Releases only.
2. **SHA256 checksums must match.** Every manifest entry has a `sha256` field. If you rebuild a runtime, the checksum changes and the manifest **must** be regenerated.
3. **Manifests must stay in sync.** If you update a runtime version, update both `runtimes/<lang>/manifest.json` and regenerate `manifest.json` with `just manifest`.
4. **Builds must be reproducible.** All runtime builds happen inside the Docker image (`Dockerfile`). Don't add build steps that depend on host-specific tooling.
5. **Optimization flags matter.** `--enable-bulk-memory` is required for both Go and Rust WASM outputs. Missing it breaks compatibility.

---

## Adding a New Language Runtime

This is the most common non-trivial task. Follow every step.

### 1. Create the runtime source

```
runtimes/<lang>/
├── <source files>       # The actual program to compile to WASM
└── manifest.json        # Will be 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

Create `scripts/build-<lang>.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` optimization if available
- Copy output to `runtimes/<lang>/`
- Call `scripts/generate-metadata.sh` to create/update the manifest

### 3. Register in build-all.sh

Add a build block to `scripts/build-all.sh` with an env flag (`BUILD_<LANG>`) for selective builds.

### 4. Update the Dockerfile

Add the language's compiler/toolchain to `Dockerfile`. Keep it in the same pattern as existing tools (Go, TinyGo, Rust).

### 5. Update the Rust library

- Add variant to `Language` enum in `src/runtime.rs`
- Add `as_str()`, `FromStr`, and `Display` implementations
- Add to `Language::all()` array

### 6. Update CI

- Add matrix entry in `.github/workflows/build-runtimes.yml`
- The release workflow (`release.yml`) picks up all runtimes automatically via `scripts/build-all.sh`

### 7. Update global manifest

Add 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
```

---

## Updating an Existing Runtime for a New Version

1. Update the source code in `runtimes/<lang>/` if needed (e.g., new language features).
2. Update the version default in `scripts/build-<lang>.sh` (e.g., `GO_VERSION` or `RUST_VERSION`).
3. Update toolchain versions in `Dockerfile` if the compiler version changes.
4. Rebuild: `just docker-build && just docker-build-runtimes`
5. The build script calls `generate-metadata.sh` which updates `runtimes/<lang>/manifest.json` — it adds the new version entry and sets it as `latest`.
6. Regenerate global manifest: `just manifest`
7. Verify: the build script runs `verify-binary.sh` automatically, but you can re-run manually.

**Important:** A new version creates a new entry in the manifest's `versions` map. Old versions remain listed. The `latest` field is updated automatically.

---

## After Every Set of Changes

1. **`just format`** — Format all Rust code.
2. **`just lint`** — Run clippy (zero warnings enforced).
3. **`just test`** — Run the full test suite.

For a full CI-equivalent check:

4. **`just ci`** — Runs format-check → lint → test.

Do not consider a change complete until all of the above pass cleanly.

### Additional Housekeeping

- **Prefer `just` commands** over raw `cargo` commands. The justfile handles feature flags correctly.
- **Prompt the user if `AGENTS.md` needs updating.** If your changes alter the build pipeline, add a language, change manifest format, or modify release process, tell the user.

### Documentation

- **README.md** — GitHub front page. Tagline, quick install, feature highlights, link out to the docs site. Keep it lean; long-form lives on the docs site.
- **Docs site** (`docs/`) — Eleventy 3.x + [eleventy-libdoc]https://github.com/ita-design-system/eleventy-libdoc starter, deployed to GitHub Pages at https://anistark.github.io/wasmhub/. Owns the deep content: getting-started, CLI reference, library guide, architecture, manifest format, contributor guides, per-runtime catalog.
- **API reference** is auto-generated at [docs.rs/wasmhub]https://docs.rs/wasmhub from inline doc comments.

### Working in `docs/`

- Content is plain Markdown with YAML frontmatter. To add a page: drop a `.md` file under `docs/content/`, follow libdoc's frontmatter conventions for navigation/title, cross-link with the Eleventy `url` filter (required for the `/wasmhub/` path prefix).
- Local preview: `cd docs && pnpm install && pnpm run serve` → http://localhost:8080/
- Build: `pnpm run build` → output in `docs/_site/` (gitignored).
- **Use pnpm, not npm** — lockfile is `pnpm-lock.yaml`, CI uses `pnpm/action-setup@v4`.
- Theming lives in `docs/assets/theme.css` (CSS custom-property overrides). **Do not edit libdoc's vendored CSS directly** — it makes future upstream pulls painful.
- Deployment is automatic: any push to `main` touching `docs/**` triggers `.github/workflows/docs.yml`.

### Planning Documents

- **`plan/` directory** is for all planning and design docs. It's gitignored — never commit it.
- **Check `plan/` for existing plans** before starting related work. Files like `plan/PLAN.md` and any `plan/*_IMPLEMENTATION.md` contain context, checklists, and phase tracking.
- **Create new planning docs here** when scoping features or multi-step work (e.g., `plan/NEW_LANG_PYTHON.md`, `plan/MANIFEST_V2.md`). Keep them focused — one doc per initiative.

### Git Discipline

- **Do not run `git add` or `git commit` unless the user explicitly asks.**
- **When the user asks to commit**, prepare:
  - A **brief title** following conventional commits (`feat:`, `fix:`, `chore:`, etc.)
  - A **detailed description** summarizing what changed and why

---

## Architecture

### 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 (thiserror), Result type alias
└── bin/
    └── wasmhub.rs      # CLI binary (feature-gated behind "cli")

runtimes/
├── go/
│   ├── main.go             # Go runtime source (compiled with TinyGo)
│   └── manifest.json       # Per-language manifest
└── rust/
    ├── src/main.rs          # Rust runtime source (compiled with wasm32-wasip1)
    ├── Cargo.toml
    └── manifest.json        # Per-language manifest

scripts/
├── build-all.sh             # Orchestrates all runtime builds
├── build-go.sh              # Go → WASM via TinyGo
├── build-rust.sh            # Rust → WASM via wasm32-wasip1
├── optimize-wasm.sh         # wasm-opt optimization + compression
├── verify-binary.sh         # WASM magic number + SHA256 verification
├── generate-metadata.sh     # Creates/updates per-language manifest.json
├── generate-global-manifest.sh  # Aggregates into root manifest.json
├── publish.sh               # Full release workflow
└── install-wasi-sdk.sh      # WASI SDK installer

docs/                        # Eleventy + libdoc docs site (deployed to GitHub Pages)
├── .eleventy.js
├── package.json
├── settings.json
├── content/                 # Markdown pages with frontmatter
├── assets/theme.css         # Wasmhub-branded overrides on libdoc CSS variables
└── _site/                   # Build output — gitignored

manifest.json               # Global manifest (all languages, aggregated)
Dockerfile                   # Reproducible build environment
justfile                     # Task runner
```

### Cargo Feature Flags

| Feature | What it enables | Default |
|---------|----------------|---------|
| *(none)* | Library only — no CLI deps ||
| `progress` | Download progress bars (`indicatif`) | No |
| `cli` | CLI binary + `clap`, `anyhow`, `colored` + `progress` | No |

The library must always compile with zero features. CLI-only dependencies (`clap`, `anyhow`, `colored`) must never appear in library modules.

### Manifest Format

**Per-language** (`runtimes/<lang>/manifest.json`):
```json
{
    "language": "go",
    "latest": "1.23",
    "versions": {
        "1.23": {
            "file": "go-1.23.wasm",
            "size": 266712,
            "sha256": "efa1e...",
            "released": "2026-02-03T13:23:13Z",
            "wasi": "wasip1",
            "features": []
        }
    }
}
```

**Global** (`manifest.json`):
```json
{
    "version": "0.1.4",
    "build_date": "...",
    "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" }
    }
}
```

---

## Tech Stack & Tooling

| Tool | Purpose |
|------|---------|
| **Rust** (Edition 2021, MSRV 1.85) | Core library + CLI |
| **Just** | Task runner (`just` for available commands) |
| **Docker** | Reproducible runtime build environment |
| **TinyGo** | Go → WASM compilation |
| **wasm-opt** (Binaryen) | WASM binary optimization |
| **WASI SDK** | C/C++ WASM compilation (future runtimes) |
| **tokio** / **reqwest** | Async runtime + HTTP client |
| **serde** / **serde_json** | Manifest serialization |
| **sha2** | Integrity verification |
| **clap** | CLI argument parsing (feature-gated) |
| **jq** | Manifest JSON manipulation in build scripts |
| **gh** (GitHub CLI) | Release creation |

---

## Build & Development

```sh
just build              # Library only
just build-all          # Library + CLI
just build-release      # Release binary
just test               # All tests (--all-features)
just format             # rustfmt
just lint               # clippy -D warnings
just ci                 # format-check → lint → test
just install            # Install CLI locally
just manifest           # Regenerate global manifest.json
just docker-build       # Build Docker image
just docker-build-runtimes  # Build all runtimes in Docker
just optimize           # Run wasm-opt on all runtimes
just publish            # Full release (tag + GitHub + crates.io)
just publish-check      # Dry-run crates.io publish
just publish-github     # GitHub release only (no crates.io)
just docs               # Serve docs site locally (Eleventy)
just docs-install       # Install docs site deps (pnpm)
just docs-build         # Build docs site for production
just docs-clean         # Remove docs site _site/ output
just api-docs           # Open Rust API docs (cargo doc)
```

---

## CI Workflows

| Workflow | Trigger | What it does |
|----------|---------|-------------|
| `ci.yml` | Push to main, PRs | Format check + clippy + tests (Linux/macOS/Windows) |
| `build-runtimes.yml` | Changes to `runtimes/` or `scripts/`, manual | Builds runtimes in Docker, uploads as artifacts |
| `release.yml` | GitHub Release created, manual | Builds runtimes + CLI binaries for all platforms, uploads to release |
| `docs.yml` | Push to main with `docs/**` changes, manual | Builds Eleventy site in `docs/`, deploys to GitHub Pages |

---

## Code Conventions

- Standard Rust naming: `snake_case` for functions/variables, `PascalCase` for types.
- Use `thiserror` for library errors (`src/error.rs`). CLI uses `anyhow` on top.
- Keep the public API surface minimal. Only `pub use` what consumers need in `src/lib.rs`.
- All network operations go through `reqwest`. All async uses `tokio`.
- Build scripts must work inside the Docker container. Use portable shell constructs.

### Commit Messages

```
feat: description          # New feature
fix: description           # Bug fix
chore: description         # Maintenance, deps, CI
docs: description          # Documentation only
refactor: description      # Code restructuring
test: description          # Adding/fixing tests
```

### Branching

- `main` — stable release branch
- `feat/*`, `fix/*`, `chore/*`, `docs/*` — topic branches

---

## Versioning

- Version source of truth: `Cargo.toml`
- Follow [Semantic Versioning]https://semver.org/
- `just publish` handles: version check → CI → tag → GitHub Release → crates.io

---

## CLI Commands Reference

```sh
wasmhub get <language> [version]          # Download runtime (default: latest)
wasmhub get <language> <version> --force  # Force re-download
wasmhub list [language]                   # List available runtimes
wasmhub info <language> [version]         # Show runtime details
wasmhub cache show                        # Show cache contents
wasmhub cache clear <language> <version>  # Clear specific cached runtime
wasmhub cache clear-all [--yes]           # Clear all cache
```

**Languages:** `nodejs`/`node`, `python`/`py`, `ruby`/`rb`, `php`, `go`/`golang`, `rust`/`rs`

---

## Important Files

| File | Why It Matters |
|------|----------------|
| `Cargo.toml` | Version, dependencies, feature flags |
| `src/lib.rs` | Public API surface |
| `src/loader.rs` | Core download/cache/manifest logic |
| `src/runtime.rs` | `Language` enum — add new languages here |
| `src/manifest.rs` | Manifest types — update when format changes |
| `src/error.rs` | All error types |
| `src/bin/wasmhub.rs` | CLI commands and output |
| `manifest.json` | Global manifest — aggregated from per-language manifests |
| `runtimes/*/manifest.json` | Per-language manifests (versions, URLs, checksums) |
| `scripts/build-all.sh` | Runtime build orchestration |
| `scripts/build-go.sh` | Go build script (reference for new languages) |
| `scripts/build-rust.sh` | Rust build script (reference for new languages) |
| `scripts/generate-metadata.sh` | Creates/updates per-language manifest entries |
| `scripts/generate-global-manifest.sh` | Aggregates manifests + adds source/license info |
| `scripts/publish.sh` | Full release workflow |
| `Dockerfile` | Build environment — update when adding new language toolchains |
| `justfile` | All dev commands |

---

## Gotchas & Pitfalls

- **Feature flags:** `cargo build` alone won't build the CLI. Use `--features cli` or `--all-features`.
- **WASM binaries are gitignored.** `runtimes/**/*.wasm` is in `.gitignore`. They're built in CI and published via GitHub Releases.
- **Docker is required for runtime builds.** Build scripts expect TinyGo, wasm32-wasip1 target, wasm-opt, jq, etc. — all in the Docker image.
- **`jq` is required by manifest scripts.** The metadata and global manifest generators use `jq` for JSON manipulation.
- **`gh` CLI is required for releases.** `just release` and `scripts/publish.sh` use `gh release create`.
- **SHA256 verification is mandatory.** Every downloaded runtime must pass integrity checks. The library enforces this — don't bypass it.
- **`--enable-bulk-memory` is required** for `wasm-opt` optimization of both Go and Rust runtimes. Without it, optimized binaries may be broken.
- **Manifest `latest` is updated automatically** by `generate-metadata.sh`. Don't manually set it unless you know what you're doing.
- **`stat` syntax differs between macOS and Linux.** Build scripts handle both (`stat -f%z` vs `stat -c%s`). Keep this pattern when adding new scripts.
- **clippy must pass with zero warnings** — CI enforces `-D warnings`.
- **`plan/` is gitignored.** Local planning only — never commit anything in it.