WASM & TinyGo Build Review Skill
Target triple, binary size, and syscall/js misuse checks for agents - a cookbook-style Agent Skill for Go 1.26 and TinyGo WASM builds.
What This Skill Does
Produces a WASM build review checklist: correct GOOS/GOARCH, syscall/js vs syscall/wasmimport, TinyGo target tags, size budgets, host runtime compatibility (wazero, wasmtime), and forbidden imports - before merge.
When to Invoke
- PR adds
GOOS=wasip1 GOARCH=wasmbuild targets - TinyGo firmware or constrained WASM module changes
- Agent scaffolds browser WASM with
syscall/js - Binary size regression on edge deployment
- Host integration with wazero runtime
Inputs
| Input | Why |
|---|---|
| Build targets | wasip1/wasm, js/wasm, TinyGo board |
| Host runtime | wazero, wasmtime, browser |
| Size budget | KB limit for embedded |
go.mod / TinyGo version | Feature availability |
| Main package imports | Flag cgo, net, os/exec |
Outputs
- Target triple validation table
- Import/syscall misuse findings
- Size report commands (
wasm-objdump,twiggy,tinygo size) - Host smoke test snippet for wazero
- Verification build commands
Guardrails
- No
syscall/jsin WASI modules - browser-only API. - No cgo in standard WASM cross-compile unless documented exception.
- TinyGo - verify board tag and scheduler; full Go stdlib may not apply.
- Size budget - block new heavy deps without justification.
- Do not use
os/execor raw sockets in WASM without host capability doc. - wazero API - verify at build; pin in skill header.
Recipe
Quick-reference recipe card - copy-paste ready.
# WASI module (Go 1.26)
GOOS=wasip1 GOARCH=wasm go build -o app.wasm ./cmd/wasi/
# Inspect exports and size
wasm-objdump -x app.wasm | head -40
ls -la app.wasm
# TinyGo constrained build (example - verify board)
tinygo build -target=wasi -o tiny.wasm ./cmd/edge/
# wazero smoke (host)
go test ./internal/wasmhost/... -run TestLoadModule -count=1When to reach for this skill:
- Edge team ships KB-sized WASM artifacts
- Agent used
fmt.Printlnandnet/httpin browser WASM draft - CI added WASM target without size gate
Working Example
Step 0 - Classify target
| Target | Runtime | Go API surface |
|---|---|---|
wasip1/wasm | wazero, wasmtime, Spin | WASI snapshot |
js/wasm | Browser | syscall/js |
| TinyGo board | MCU / WASM | Subset stdlib |
See Go on WebAssembly: Browser, WASI, and Embedded Paths
Step 1 - Import audit
# Forbidden patterns in WASI
grep -rn 'syscall/js\|js\.Value' --include='*.go' ./cmd/wasi/
grep -rn 'cgo\|os/exec\|"net/http"' --include='*.go' ./cmd/wasi/| Finding | Action |
|---|---|
syscall/js in wasip1 build | Blocker - split packages |
net/http server in WASM | Blocker - use host RPC |
unsafe.Pointer cgo | Blocker unless TinyGo doc exception |
Step 2 - Size budget
ls -la app.wasm
# Optional: twiggy top for Rust-linked hosts; wasm-objdump for Go wasm- Compare to budget in ticket (e.g. 500 KB gzip)
- See WASM Size Optimization: TinyGo vs GC WASM
Step 3 - Host smoke (wazero)
func TestLoadWASIModule(t *testing.T) {
ctx := context.Background()
r := wazero.NewRuntime(ctx)
defer r.Close(ctx)
mod, err := r.InstantiateWithConfig(ctx, wasmBytes,
wazero.NewModuleConfig().WithStartFunctions("_initialize"))
if err != nil {
t.Fatal(err)
}
defer mod.Close(ctx)
}- Verify instantiate,
_initialize, and exportedrun/mainper module contract - See WASI Host Runtimes: Wasmtime, wazero, and Spin
Step 4 - TinyGo-specific
tinygo flash -target=pico -size=short ./cmd/firmware/ # example - verify board- Scheduler: avoid blocking forever without host yield
- Check
//go:build tinygotags for divergent code paths
Deep Dive
Go 1.26 WASM runtime improvements may change alloc behavior - re-benchmark size after toolchain bump.
See Go 1.26 WASM Runtime and Memory Improvements
syscall/js requires DOM callbacks and js.FuncOf cleanup - leak review in browser targets.
WASI preview vs wasip1: build tags and host capability matrix must match deployment environment.
Gotchas
- Single main for both linux and wasm without build tags pulls wrong imports.
- Global variables bloat WASM init sections - prefer const and lazy init.
- Reflection and fmt cost size in TinyGo - audit format strings.
- Testing WASM on macOS host - ensure CI builds same triple as production.
Alternatives
| Approach | When |
|---|---|
| Rust WASM for size-critical | Team policy |
| Manual platform review | One module |
| CI size gate only | No import semantics |
| This skill | Agent-assisted WASM PR review |
FAQs
Should agents add syscall/js for WASI fixes?
Never. Split internal/browser and internal/wasi packages with build tags.
How do we test browser WASM?
Skill outputs build commands; browser test in headless Chrome is separate pipeline - link team doc.
TinyGo vs full Go for WASM?
Collect size and stdlib needs in inputs. TinyGo for KB budgets; full Go for richer stdlib on WASI hosts.
Related
- Agent Skills Basics - SKILL.md structure
- WebAssembly and TinyGo Basics - WASM intro
- Compiling with GOOS=wasip1 GOARCH=wasm - WASI build
- syscall/js: Go in the Browser - browser API
- TinyGo for Microcontrollers and Constrained WASM - TinyGo
- WASI Host Runtimes: Wasmtime, wazero, and Spin - hosts
Stack versions: This page was written for Go 1.26.x (Green Tea GC default, go fix modernizers - verify patch at build), chi (latest - verify at build), gin (latest - verify at build), echo (latest - verify at build), google.golang.org/grpc (latest - verify at build), sigs.k8s.io/controller-runtime (latest - verify at build), kubebuilder (latest - verify at build), tinygo (latest - verify board targets at build), wazero (latest - verify at build), and golangci-lint (latest - verify linter set at build).