You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
TL;DR: wazero is used masterfully for the WASM security guard system. Three actionable improvements found: a resource-management gap in guard shutdown, fragile trap detection, and a logging namespace inconsistency.
Module Overview
github.com/tetratelabs/wazero is a zero-dependency WebAssembly runtime for Go. It provides an embeddable WASM execution engine with full WASI support, host function bridging, and JIT compilation. In this project it powers the security guard system — running Rust-compiled WASM binaries that enforce DIFC (Decentralized Information Flow Control) policies without any network access.
The wazero integration is sophisticated and shows deep understanding of the runtime:
Adaptive buffer retry — Guards communicate "buffer too small" via return code -2, with the first 4 bytes optionally containing the required size. The host retries up to 3 times, growing from 4MB → 16MB.
Guard-managed allocators — When a guard exports alloc/dealloc, the host uses them to avoid buffer aliasing with the guard's heap. Falls back gracefully to end-of-memory placement.
Trap poisoning — A WASM trap (Rust panic → unreachable) permanently marks the guard as failed. All subsequent calls return an error immediately, preventing undefined-state execution.
context.WithoutCancel for cleanup — Dealloc calls use a non-cancellable context so heap allocations aren't leaked if the request context times out during cleanup.
Stdin isolation — WithStdin(strings.NewReader("")) prevents WASM code from accidentally reading the gateway's MCP protocol stdin.
Process-level JIT cache — globalCompilationCache is shared across all WasmGuard instances, eliminating redundant compilation. Tests properly close it in TestMain.
Proper error cleanup — Every error path in NewWasmGuardWithOptions calls runtime.Close(ctx) before returning, preventing runtime leaks during initialization failures.
Improvement Opportunities
🏃 Quick Wins
1. Guard Registry Missing Close() Method
The Registry struct has no Close() method, and WasmGuard.Close() is never called during server shutdown.
// UnifiedServer.Close() — guards NOT closed:func (us*UnifiedServer) Close() error {
us.launcher.Close()
returnnil// guardRegistry.Close() never called
}
Guards are created with context.Background() (lines 49, 219 in guard_init.go), so WithCloseOnContextDone(true) is effectively disabled — the context is never cancelled.
Suggested fix: Add a Close(ctx context.Context) error method to Registry that iterates guards implementing io.Closer (or a Closer interface), and call it from UnifiedServer.Close() and InitiateShutdown().
// Registry.Close closes all registered guards that implement io.Closer.func (r*Registry) Close(ctx context.Context) {
r.mu.Lock()
deferr.mu.Unlock()
forid, g:=ranger.guards {
ifc, ok:=g.(interface{ Close(context.Context) error }); ok {
iferr:=c.Close(ctx); err!=nil {
logger.LogWarn("guard", "Failed to close guard for server %s: %v", id, err)
}
}
}
}
2. Logging Namespace Confusion in registry.go
registry.go defines var debugLog = logger.New("guard:registry") for DEBUG-gated logs, but its plain log.Printf(...) calls (lines 33, 49, 79, 119) use the package-level log variable — which is declared in context.go with namespace "guard:context". This means:
DEBUG=guard:registry will NOT show log.Printf messages from registry.go
DEBUG=guard:context will show registry messages alongside unrelated context messages
Suggested fix: Replace log.Printf in registry.go with debugLog.Printf (or logger.LogInfo for operational events), and consider making those registry messages use logger.LogInfo("guard", ...) for consistent operational logging.
This is fragile — it depends on wazero's internal error message format remaining stable. wazero's sys package provides sys.ExitError to distinguish normal WASM process exits (e.g., TinyGo exit(0) during init) from execution traps. A normal exit with code 0 should NOT poison the guard.
Suggested fix: Use errors.As to check for sys.ExitError before the string-based check:
import"github.com/tetratelabs/wazero/sys"funcisWasmTrap(errerror) bool {
iferr==nil {
returnfalse
}
// Normal process exit is not a trap — don't poison the guard.varexitErr*sys.ExitErroriferrors.As(err, &exitErr) {
returnexitErr.ExitCode() !=0
}
// Fallback for wazero execution traps (e.g., Rust panic → unreachable).returnstrings.Contains(err.Error(), "wasm error:")
}
📐 Best Practice Alignment
4. Consistent Module Naming Convention
The WasmGuard stores a name field and uses it in host_log as [guard:<name>]. This could be more consistent with the project's pkg:filename logger namespace convention if guard names always follow a predictable format. Currently guard names come from serverID (e.g., "github"), which is appropriate — just noting that instantiateHostFunctions doesn't pass the context through host_log in a way that would allow correlating with request traces.
Summary of Recommendations
Priority
Action
Effort
🔴 Medium
Add Registry.Close() and call it during server shutdown
Small
🟡 Medium
Fix logging namespace confusion in registry.go
Trivial
🟡 Medium
Use sys.ExitError typed check in isWasmTrap
Small
References
Module: github.com/tetratelabs/wazero v1.11.0
Primary file: internal/guard/wasm.go (1210 lines)
Test file: internal/guard/wasm_test.go
Generated by Go Fan 🐹 — Daily Go Module Reviewer Round-robin state: reviewed [go-sdk → wazero]
Note
🔒 Integrity filter blocked 17 items
The following items were blocked because they don't meet the GitHub integrity level.
BurntSushi/toml@8685fbaget_commit: has lower integrity than agent requires. The agent cannot read data with integrity below "unapproved".
golang/term@52b71d3get_commit: has lower integrity than agent requires. The agent cannot read data with integrity below "unapproved".
🐹 Go Fan Report: tetratelabs/wazero
Module Overview
github.com/tetratelabs/wazerois a zero-dependency WebAssembly runtime for Go. It provides an embeddable WASM execution engine with full WASI support, host function bridging, and JIT compilation. In this project it powers the security guard system — running Rust-compiled WASM binaries that enforce DIFC (Decentralized Information Flow Control) policies without any network access.Version used:
v1.11.0✅ (latest)Current Usage in gh-aw
internal/guard/wasm.go,internal/guard/wasm_test.go)github.com/tetratelabs/wazero— runtime, config, cachegithub.com/tetratelabs/wazero/api—Module,Memory,Function,GoModuleFunc,ValueType*github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1— WASI instantiationwazero.NewRuntimeConfigCompiler().WithCloseOnContextDone(true)(JIT mode)wazero.NewCompilationCache()/NewCompilationCacheWithDir()(process-level cache)runtime.NewHostModuleBuilder("env")withNewFunctionBuilder()(host functions)wasi_snapshot_preview1.Instantiate()(WASI support)module.ExportedFunction(),api.Module.Memory().Read()/.Write()/.Grow()(memory I/O)Research Findings
What This Code Does Well 🏆
The wazero integration is sophisticated and shows deep understanding of the runtime:
-2, with the first 4 bytes optionally containing the required size. The host retries up to 3 times, growing from 4MB → 16MB.alloc/dealloc, the host uses them to avoid buffer aliasing with the guard's heap. Falls back gracefully to end-of-memory placement.unreachable) permanently marks the guard as failed. All subsequent calls return an error immediately, preventing undefined-state execution.context.WithoutCancelfor cleanup — Dealloc calls use a non-cancellable context so heap allocations aren't leaked if the request context times out during cleanup.WithStdin(strings.NewReader(""))prevents WASM code from accidentally reading the gateway's MCP protocol stdin.globalCompilationCacheis shared across allWasmGuardinstances, eliminating redundant compilation. Tests properly close it inTestMain.NewWasmGuardWithOptionscallsruntime.Close(ctx)before returning, preventing runtime leaks during initialization failures.Improvement Opportunities
🏃 Quick Wins
1. Guard Registry Missing
Close()MethodThe
Registrystruct has noClose()method, andWasmGuard.Close()is never called during server shutdown.Guards are created with
context.Background()(lines 49, 219 inguard_init.go), soWithCloseOnContextDone(true)is effectively disabled — the context is never cancelled.Suggested fix: Add a
Close(ctx context.Context) errormethod toRegistrythat iterates guards implementingio.Closer(or aCloserinterface), and call it fromUnifiedServer.Close()andInitiateShutdown().2. Logging Namespace Confusion in
registry.goregistry.godefinesvar debugLog = logger.New("guard:registry")for DEBUG-gated logs, but its plainlog.Printf(...)calls (lines 33, 49, 79, 119) use the package-levellogvariable — which is declared incontext.gowith namespace"guard:context". This means:DEBUG=guard:registrywill NOT showlog.Printfmessages from registry.goDEBUG=guard:contextwill show registry messages alongside unrelated context messagesSuggested fix: Replace
log.Printfinregistry.gowithdebugLog.Printf(orlogger.LogInfofor operational events), and consider making those registry messages uselogger.LogInfo("guard", ...)for consistent operational logging.✨ Feature Opportunities
3. Use Typed Error Checking for WASM Traps
The current trap detection is string-based:
This is fragile — it depends on wazero's internal error message format remaining stable. wazero's
syspackage providessys.ExitErrorto distinguish normal WASM process exits (e.g., TinyGoexit(0)during init) from execution traps. A normal exit with code 0 should NOT poison the guard.Suggested fix: Use
errors.Asto check forsys.ExitErrorbefore the string-based check:📐 Best Practice Alignment
4. Consistent Module Naming Convention
The
WasmGuardstores anamefield and uses it inhost_logas[guard:<name>]. This could be more consistent with the project'spkg:filenamelogger namespace convention if guard names always follow a predictable format. Currently guard names come fromserverID(e.g.,"github"), which is appropriate — just noting thatinstantiateHostFunctionsdoesn't pass the context throughhost_login a way that would allow correlating with request traces.Summary of Recommendations
Registry.Close()and call it during server shutdownregistry.gosys.ExitErrortyped check inisWasmTrapReferences
github.com/tetratelabs/wazero v1.11.0internal/guard/wasm.go(1210 lines)internal/guard/wasm_test.goGenerated by Go Fan 🐹 — Daily Go Module Reviewer
Round-robin state: reviewed [go-sdk → wazero]
Note
🔒 Integrity filter blocked 17 items
The following items were blocked because they don't meet the GitHub integrity level.
get_commit: has lower integrity than agent requires. The agent cannot read data with integrity below "unapproved".get_commit: has lower integrity than agent requires. The agent cannot read data with integrity below "unapproved".get_commit: has lower integrity than agent requires. The agent cannot read data with integrity below "unapproved".get_commit: has lower integrity than agent requires. The agent cannot read data with integrity below "unapproved".get_commit: has lower integrity than agent requires. The agent cannot read data with integrity below "unapproved".get_commit: has lower integrity than agent requires. The agent cannot read data with integrity below "unapproved".get_commit: has lower integrity than agent requires. The agent cannot read data with integrity below "unapproved".get_commit: has lower integrity than agent requires. The agent cannot read data with integrity below "unapproved".get_latest_release: has lower integrity than agent requires. The agent cannot read data with integrity below "unapproved".get_latest_release: has lower integrity than agent requires. The agent cannot read data with integrity below "unapproved".get_latest_release: has lower integrity than agent requires. The agent cannot read data with integrity below "unapproved".get_latest_release: has lower integrity than agent requires. The agent cannot read data with integrity below "unapproved".get_latest_release: has lower integrity than agent requires. The agent cannot read data with integrity below "unapproved".get_latest_release: has lower integrity than agent requires. The agent cannot read data with integrity below "unapproved".get_latest_release: has lower integrity than agent requires. The agent cannot read data with integrity below "unapproved".list_tags: has lower integrity than agent requires. The agent cannot read data with integrity below "unapproved".To allow these resources, lower
min-integrityin your GitHub frontmatter: