Skip to content

[go-fan] Go Module Review: tetratelabs/wazeroย #5142

Description

@github-actions

๐Ÿน Go Fan Report: tetratelabs/wazero

Module Overview

wazero is a zero-dependency, pure-Go WebAssembly runtime โ€” no CGo, no external C libraries. It supports the WebAssembly Core Specification plus WASI Preview 1, with both JIT (compiler) and interpreter backends. This makes it uniquely suited for embedding WASM execution in Go services like this gateway.

Version in use: v1.11.0

Current Usage in gh-aw-mcpg

The project uses wazero to run WASM-based DIFC security guards in-process. Guards are sandboxed WASM binaries that label MCP resources and responses to enforce information-flow control policies.

  • Files: 3 files (internal/guard/wasm.go, wasm_parse.go, wasm_test.go)
  • Import Packages: wazero, wazero/api, wazero/imports/wasi_snapshot_preview1, wazero/sys
  • Key APIs Used:
    • NewRuntimeConfigCompiler() + WithCloseOnContextDone(true) + WithCompilationCache()
    • NewRuntimeWithConfig() โ€” one runtime per guard instance
    • wasi_snapshot_preview1.Instantiate() โ€” WASI host support
    • NewHostModuleBuilder("env") โ€” custom call_backend and host_log host functions
    • NewModuleConfig() with stdin isolation
    • api.Module.ExportedFunction(), api.Function.Call() โ€” function dispatch
    • api.Memory.Read/Write/Grow/ReadUint32Le โ€” linear memory access
    • sys.ExitError โ€” trap/exit discrimination
    • NewCompilationCache() โ€” shared in-memory JIT cache

Research Findings

Architecture Quality

The implementation is well-structured:

  • โœ… Uses compiler (JIT) backend explicitly โ€” good performance
  • โœ… Shared CompilationCache across all guard instances โ€” avoids redundant JIT compilation
  • โœ… WithCloseOnContextDone(true) โ€” proper context-aware cancellation
  • โœ… sys.ExitError used for type-safe WASI exit discrimination
  • โœ… Guard-allocator preferred path (alloc/dealloc exports) for safe memory management
  • โœ… Trap poisoning โ€” permanently marks failed guards to prevent use of corrupted state
  • โœ… context.WithoutCancel for cleanup to avoid dealloc skips on context cancellation

Recent Updates (wazero v1.11)

The current version v1.11.0 is the latest stable. wazero has an active release cadence with improvements in memory management, WASI compatibility, and compilation speed. Disk-based compilation cache has been available since early versions.

Best Practices from Maintainers

  • Prefer CompiledModule + multiple instantiations over recreating the runtime for the same binary
  • Use wazero.NewCompilationCacheWithDir() for disk persistence across restarts
  • Memory limits should be encoded in the WASM binary itself (max pages in the memory section)

Improvement Opportunities

๐Ÿƒ Quick Wins

  1. Disk-based compilation cache โ€” Replace wazero.NewCompilationCache() with wazero.NewCompilationCacheWithDir(cacheDir) to persist JIT artifacts across gateway restarts. This eliminates cold-start compilation latency (can be significant for complex WASM guard binaries). A subdirectory of the existing --log-dir or a dedicated cache path (e.g., MCP_GATEWAY_WASM_CACHE_DIR) would work well.

  2. Warn on non-allocator path โ€” tryCallWasmFunction has a fallback that manually places I/O buffers at the end of WASM linear memory. This is fragile: if the guard's heap grows into that region, behavior is undefined. The fallback should emit a logger.LogWarn when used, encouraging guard authors to export alloc/dealloc.

โœจ Feature Opportunities

  1. Instance pooling for concurrency โ€” Currently each WasmGuard serializes all calls through a mutex because WASM modules are single-threaded. wazero supports instantiating the same CompiledModule multiple times independently. A pool of N instances per guard would allow N concurrent calls without blocking, improving throughput under load. This would require a sync.Pool or a channel-based pool of module instances.

  2. Pre-compile to CompiledModule โ€” Split guard creation into two phases: runtime.CompileModule(ctx, wasmBytes) โ†’ CompiledModule, then runtime.InstantiateModule(ctx, compiled, config). This separates compilation (done once) from instantiation (done per pool slot), and is also more idiomatic for the pooling opportunity above.

๐Ÿ“ Best Practice Alignment

  1. Trap string matching fragility โ€” isWasmTrap() uses strings.Contains(err.Error(), "wasm error:") as a fallback for non-WASI execution faults (e.g., Rust panic โ†’ unreachable). This relies on wazero's internal error message format which is not part of the public API and could change across versions. It would be worth opening a discussion with the wazero maintainers about a typed error for execution traps, or adding a version comment to flag this as a known coupling.

  2. Module name collision โ€” WithName("guard") is hard-coded for all guard instances. Currently each guard gets its own runtime, so there's no collision. But if the architecture ever changes to share runtimes, this would fail silently. A name derived from the guard's name field (e.g., WithName(name)) would be safer and more debuggable in wazero traces.

๐Ÿ”ง General Improvements

  1. Document memory limit responsibility โ€” There is no per-module memory cap enforced from the host. If a guard WASM binary omits a (memory N M) max bound, it could consume unbounded memory. A code comment or guard authorship guide should document that max memory pages must be set in the WASM binary itself, and that the gateway does not enforce an independent cap.

Recommendations (Prioritized)

Priority Item Impact Effort
High Disk-based compilation cache Eliminates cold-start penalty Low
High Warn on non-allocator fallback path Prevents subtle memory corruption Low
Medium Pre-compile to CompiledModule Cleaner architecture, enables pooling Medium
Medium Instance pooling Better concurrency under load High
Low Trap string matching โ€” document or fix API stability Low
Low Module name from guard name Defensive future-proofing Very Low

Next Steps

  • Explore wazero.NewCompilationCacheWithDir() โ€” add a MCP_GATEWAY_WASM_CACHE_DIR env var and --wasm-cache-dir flag, defaulting to a subdirectory of --log-dir
  • Add a logger.LogWarn in the non-allocator fallback path in tryCallWasmFunction
  • Consider refactoring to CompiledModule as groundwork for future instance pooling
  • Add a note to guard authorship documentation about WASM memory limits

Generated by Go Fan ๐Ÿน
Module summary saved to: /tmp/gh-aw/agent/wazero.md (specs/mods/ not writable in CI)
Run: ยง25364088625

Note

๐Ÿ”’ Integrity filter blocked 12 items

The following items were blocked because they don't meet the GitHub integrity level.

To allow these resources, lower min-integrity in your GitHub frontmatter:

tools:
  github:
    min-integrity: approved  # merged | approved | unapproved | none

Generated by Go Fan ยท โ— 1.3M ยท โ—ท

  • expires on May 12, 2026, 7:49 AM UTC

Metadata

Metadata

Assignees

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions