Skip to content

Persist wazero compilation cache and warn on unsafe direct-memory fallback#5243

Merged
lpcox merged 8 commits into
mainfrom
copilot/go-fan-review-wazero-module
May 7, 2026
Merged

Persist wazero compilation cache and warn on unsafe direct-memory fallback#5243
lpcox merged 8 commits into
mainfrom
copilot/go-fan-review-wazero-module

Conversation

Copilot AI commented May 7, 2026

Copy link
Copy Markdown
Contributor

The gateway uses tetratelabs/wazero to run WASM DIFC guards in-process. This updates the guard runtime to persist compilation artifacts across restarts and makes the allocator fallback path more explicit and diagnosable.

  • Persisted wazero cache

    • adds --wasm-cache-dir and MCP_GATEWAY_WASM_CACHE_DIR
    • defaults the cache to <log-dir>/wazero-cache
    • configures the shared global wazero compilation cache during gateway and proxy startup
    • keeps cache lifecycle explicit in shutdown/cleanup paths
  • Safer guard execution behavior

    • emits a one-time warning per guard when alloc / dealloc exports are missing and execution falls back to direct linear-memory placement
    • keeps the existing fallback behavior, but makes the risk visible to guard authors/operators
    • uses the guard name as the instantiated WASM module name instead of the hard-coded "guard"
  • Documentation of memory ownership

    • documents that memory ceilings must be declared in the WASM module itself
    • clarifies that the gateway does not impose an additional host-side linear-memory cap
  • Coverage updates

    • adds focused tests for global cache reconfiguration and the new CLI flag/default resolution
cmd.Flags().StringVar(
    &wasmCacheDir,
    "wasm-cache-dir",
    resolveWasmCacheDir(false, "", defaultProxyLogDir),
    "Directory for disk-backed wazero compilation cache (default: <log-dir>/wazero-cache)",
)

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • example.com
    • Triggering command: /tmp/go-build904193801/b509/launcher.test /tmp/go-build904193801/b509/launcher.test -test.testlogfile=/tmp/go-build904193801/b509/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true uf@v1.36.11/type-errorsas om/segmentio/asm-ifaceassert x_amd64/vet /tmp/go-build119/opt/hostedtoolcache/go/1.25.9/x64/pkg/tool/linux_amd64/vet ce/internal/obse-unsafeptr=false x86_64-linux-gnu-unreachable=false x_amd64/vet 3325�� g_.a ache/go/1.25.9/x64/src/compress/flate/deflate.go-nolocalimports x_amd64/vet -I ntio/asm/ascii -imultiarch x_amd64/vet (dns block)
    • Triggering command: /tmp/go-build1634526129/b513/launcher.test /tmp/go-build1634526129/b513/launcher.test -test.testlogfile=/tmp/go-build1634526129/b513/testlog.txt -test.paniconexit0 -test.timeout=10m0s (dns block)
  • invalid-host-that-does-not-exist-12345.com
    • Triggering command: /tmp/go-build904193801/b491/config.test /tmp/go-build904193801/b491/config.test -test.testlogfile=/tmp/go-build904193801/b491/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true @v1.43.0/semconv/v1.27.0/attribute_group.go @v1.43.0/semconv/v1.27.0/doc.go x_amd64/vet --gdwarf-5 --64 -o x_amd64/vet 3325�� g_.a ache/go/1.25.9/x64/src/log/slog/internal/buffer/-nolocalimports x_amd64/vet --gdwarf-5 telabs/wazero/in-atomic -o x_amd64/vet (dns block)
    • Triggering command: /tmp/go-build1634526129/b495/config.test /tmp/go-build1634526129/b495/config.test -test.testlogfile=/tmp/go-build1634526129/b495/testlog.txt -test.paniconexit0 -test.timeout=10m0s 7478�� echo &#34;Running li-nxv cfg ep/bin/linux-x64/rg -c=4 -nolocalimports -importcfg ep/bin/linux-x64-buildtags 7478�� /tmp/go-build302-errorsas /home/REDACTED/go/-ifaceassert x_amd64/compile 64/src/runtime/cbash 7RwcO3vCT de/node/bin/as x_amd64/compile (dns block)
  • nonexistent.local
    • Triggering command: /tmp/go-build904193801/b509/launcher.test /tmp/go-build904193801/b509/launcher.test -test.testlogfile=/tmp/go-build904193801/b509/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true uf@v1.36.11/type-errorsas om/segmentio/asm-ifaceassert x_amd64/vet /tmp/go-build119/opt/hostedtoolcache/go/1.25.9/x64/pkg/tool/linux_amd64/vet ce/internal/obse-unsafeptr=false x86_64-linux-gnu-unreachable=false x_amd64/vet 3325�� g_.a ache/go/1.25.9/x64/src/compress/flate/deflate.go-nolocalimports x_amd64/vet -I ntio/asm/ascii -imultiarch x_amd64/vet (dns block)
    • Triggering command: /tmp/go-build1634526129/b513/launcher.test /tmp/go-build1634526129/b513/launcher.test -test.testlogfile=/tmp/go-build1634526129/b513/testlog.txt -test.paniconexit0 -test.timeout=10m0s (dns block)
  • slow.example.com
    • Triggering command: /tmp/go-build904193801/b509/launcher.test /tmp/go-build904193801/b509/launcher.test -test.testlogfile=/tmp/go-build904193801/b509/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true uf@v1.36.11/type-errorsas om/segmentio/asm-ifaceassert x_amd64/vet /tmp/go-build119/opt/hostedtoolcache/go/1.25.9/x64/pkg/tool/linux_amd64/vet ce/internal/obse-unsafeptr=false x86_64-linux-gnu-unreachable=false x_amd64/vet 3325�� g_.a ache/go/1.25.9/x64/src/compress/flate/deflate.go-nolocalimports x_amd64/vet -I ntio/asm/ascii -imultiarch x_amd64/vet (dns block)
    • Triggering command: /tmp/go-build1634526129/b513/launcher.test /tmp/go-build1634526129/b513/launcher.test -test.testlogfile=/tmp/go-build1634526129/b513/testlog.txt -test.paniconexit0 -test.timeout=10m0s (dns block)
  • this-host-does-not-exist-12345.com
    • Triggering command: /tmp/go-build904193801/b518/mcp.test /tmp/go-build904193801/b518/mcp.test -test.testlogfile=/tmp/go-build904193801/b518/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true .cfg rg/x/net@v0.52.0-ifaceassert x_amd64/vet --gdwarf-5 --64 -o x_amd64/vet .cfg�� 3325167/b384/_pkg_.a -I x_amd64/vet --gdwarf-5 g/grpc/resolver -o x_amd64/vet (dns block)
    • Triggering command: /tmp/go-build1634526129/b522/mcp.test /tmp/go-build1634526129/b522/mcp.test -test.testlogfile=/tmp/go-build1634526129/b522/testlog.txt -test.paniconexit0 -test.timeout=10m0s /tmp�� (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI linked an issue May 7, 2026 that may be closed by this pull request
5 tasks
@lpcox lpcox marked this pull request as ready for review May 7, 2026 16:00
Copilot AI review requested due to automatic review settings May 7, 2026 16:00
Copilot AI changed the title [WIP] Review Go module tetratelabs/wazero Persist wazero compilation cache and warn on unsafe direct-memory fallback May 7, 2026
Copilot finished work on behalf of lpcox May 7, 2026 16:01
Copilot AI requested a review from lpcox May 7, 2026 16:01

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds support for a persisted, disk-backed wazero compilation cache and wires it through CLI flags/env vars so repeated guard loads avoid redundant compilation work across runs.

Changes:

  • Introduces a configurable process-wide wazero CompilationCache, including a new --wasm-cache-dir flag and MCP_GATEWAY_WASM_CACHE_DIR env var.
  • Updates command startup paths (gateway + proxy) to configure the global cache and adds tests for defaulting/overrides.
  • Improves WASM guard behavior/observability (module naming; one-time warning when falling back to direct linear-memory buffers).
Show a summary per file
File Description
README.md Documents the new persisted wazero compilation cache under the log directory.
internal/guard/wasm.go Adds global compilation cache configuration and uses guard name for module naming.
internal/guard/wasm_test.go Adds coverage for reconfiguring the global cache to a disk-backed directory.
internal/guard/wasm_parse.go Emits a one-time warning when using the direct memory fallback path and clarifies memory-growth behavior.
internal/config/config_payload.go Adds a constant for the default wazero cache directory name.
internal/cmd/root.go Configures the global wazero compilation cache during gateway startup.
internal/cmd/proxy.go Adds --wasm-cache-dir to proxy mode and configures/closes the global cache.
internal/cmd/proxy_test.go Verifies the new proxy flag is registered and defaults correctly.
internal/cmd/flags.go Adds shell completion support for --wasm-cache-dir.
internal/cmd/flags_test.go Verifies the new flag is registered.
internal/cmd/flags_logging.go Adds --wasm-cache-dir / env resolution logic shared by commands.
internal/cmd/flags_logging_test.go Adds unit tests for defaulting and precedence logic.
docs/ENVIRONMENT_VARIABLES.md Documents MCP_GATEWAY_WASM_CACHE_DIR.
AGENTS.md Documents MCP_GATEWAY_WASM_CACHE_DIR for agent workflows.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comments suppressed due to low confidence (2)

internal/cmd/proxy.go:177

  • CloseGlobalCompilationCache is deferred here, but proxy mode doesn’t explicitly close the WASM guard/runtime before closing the compilation cache. wasm.go documents that closing the cache while guards are still active is undefined behavior. Consider adding an explicit proxy shutdown that closes the guard (e.g., a Close method on proxy.Server or a type-asserted Close(ctx) call) and run it before closing the global compilation cache.
	cleanupCtx := context.WithoutCancel(ctx)
	defer func() {
		if err := guard.CloseGlobalCompilationCache(cleanupCtx); err != nil {
			logger.LogError("shutdown", "Failed to close WASM compilation cache: %v", err)
		}

internal/cmd/root.go:157

  • ConfigureGlobalCompilationCache errors are treated as fatal here, unlike logger.InitGatewayLoggers which explicitly warns and falls back when the log directory can’t be created. If the default cache directory is not writable, startup will abort. Consider falling back to an in-memory cache (dir="") with a warning when the disk-backed cache cannot be initialized.
	if err := guard.ConfigureGlobalCompilationCache(ctx, resolvedWasmCacheDir); err != nil {
		return fmt.Errorf("failed to configure WASM compilation cache: %w", err)
	}
  • Files reviewed: 14/14 changed files
  • Comments generated: 3

Comment thread internal/guard/wasm.go Outdated
Comment on lines +46 to +63
globalCompilationCache = cache
globalCompilationCacheMu.Unlock()

if oldCache == nil {
return nil
}

if err := oldCache.Close(ctx); err != nil {
closeReplacementErr := cache.Close(ctx)
if closeReplacementErr != nil {
return errors.Join(
fmt.Errorf("failed to close previous compilation cache: %w", err),
fmt.Errorf("failed to close replacement compilation cache: %w", closeReplacementErr),
)
}
return fmt.Errorf("failed to close previous compilation cache: %w", err)
}

Comment thread internal/cmd/proxy.go Outdated
Comment on lines +169 to +172
resolvedWasmCacheDir := resolveWasmCacheDir(cmd.Flags().Changed("wasm-cache-dir"), proxyWasmCacheDir, proxyLogDir)
if err := guard.ConfigureGlobalCompilationCache(ctx, resolvedWasmCacheDir); err != nil {
return fmt.Errorf("failed to configure WASM compilation cache: %w", err)
}
Comment thread internal/cmd/root.go Outdated
logger.LogInfoMd("startup", "Starting MCPG with config: %s, listen: %s, log-dir: %s", configSource, listenAddr, logDir)
debugLog.Printf("Starting MCPG with config: %s, listen: %s", configSource, listenAddr)

resolvedWasmCacheDir := resolveWasmCacheDir(cmd.Flags().Changed("wasm-cache-dir"), wasmCacheDir, logDir)
@lpcox

lpcox commented May 7, 2026

Copy link
Copy Markdown
Collaborator

@copilot merge main and address the review feedback

Copilot AI and others added 2 commits May 7, 2026 16:13
…-wazero-module

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>

Copilot AI commented May 7, 2026

Copy link
Copy Markdown
Contributor Author

@copilot merge main and address the review feedback

Merged main in 1a9a2fe and addressed the review feedback in f3b2488. The branch now falls back to an in-memory wazero cache on disk-cache startup errors, resolves the cache after loading an env file, preserves the global cache on failed replacement, and passes make agent-finished.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • example.com
    • Triggering command: /tmp/go-build4190267325/b513/launcher.test /tmp/go-build4190267325/b513/launcher.test -test.testlogfile=/tmp/go-build4190267325/b513/testlog.txt -test.paniconexit0 -test.timeout=10m0s ortc�� L2U8X3FNy .cfg 64/pkg/tool/linu-importcfg . --gdwarf2 --64 64/pkg/tool/linu/home/REDACTED/work/gh-aw-mcpg/gh-aw-mcpg/internal/tracing/provider.go 9582�� olang.org/grpc@v1.80.0/binarylog/grpc_binarylog_-errorsas /tmp/go-build3959582045/b173/ x_amd64/cgo . /pflag --64 x_amd64/cgo (dns block)
  • invalid-host-that-does-not-exist-12345.com
    • Triggering command: /tmp/go-build4190267325/b495/config.test /tmp/go-build4190267325/b495/config.test -test.testlogfile=/tmp/go-build4190267325/b495/testlog.txt -test.paniconexit0 -test.timeout=10m0s -W rnQI63sHF 9582045/b342/ 64/pkg/tool/linux_amd64/vet . --gdwarf2 --64 64/pkg/tool/linux_amd64/vet 9582�� olang.org/protobuf@v1.36.11/runtime/protoimpl/impl.go olang.org/protobuf@v1.36.11/runtime/protoimpl/version.go 64/pkg/tool/linux_amd64/vet (compatibility issue with Go 1.25.0). Continuing with other checks...&#34;; \ elif command -v golan --gdwarf2 (dns block)
  • nonexistent.local
    • Triggering command: /tmp/go-build4190267325/b513/launcher.test /tmp/go-build4190267325/b513/launcher.test -test.testlogfile=/tmp/go-build4190267325/b513/testlog.txt -test.paniconexit0 -test.timeout=10m0s ortc�� L2U8X3FNy .cfg 64/pkg/tool/linu-importcfg . --gdwarf2 --64 64/pkg/tool/linu/home/REDACTED/work/gh-aw-mcpg/gh-aw-mcpg/internal/tracing/provider.go 9582�� olang.org/grpc@v1.80.0/binarylog/grpc_binarylog_-errorsas /tmp/go-build3959582045/b173/ x_amd64/cgo . /pflag --64 x_amd64/cgo (dns block)
  • slow.example.com
    • Triggering command: /tmp/go-build4190267325/b513/launcher.test /tmp/go-build4190267325/b513/launcher.test -test.testlogfile=/tmp/go-build4190267325/b513/testlog.txt -test.paniconexit0 -test.timeout=10m0s ortc�� L2U8X3FNy .cfg 64/pkg/tool/linu-importcfg . --gdwarf2 --64 64/pkg/tool/linu/home/REDACTED/work/gh-aw-mcpg/gh-aw-mcpg/internal/tracing/provider.go 9582�� olang.org/grpc@v1.80.0/binarylog/grpc_binarylog_-errorsas /tmp/go-build3959582045/b173/ x_amd64/cgo . /pflag --64 x_amd64/cgo (dns block)
  • this-host-does-not-exist-12345.com
    • Triggering command: /tmp/go-build4190267325/b522/mcp.test /tmp/go-build4190267325/b522/mcp.test -test.testlogfile=/tmp/go-build4190267325/b522/testlog.txt -test.paniconexit0 -test.timeout=10m0s -I 9582045/b456/_pkg_.a -I x_amd64/vet --gdwarf-5 -462b8113fde4 -o x_amd64/vet (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot finished work on behalf of lpcox May 7, 2026 16:22
@lpcox lpcox merged commit f1b6f83 into main May 7, 2026
13 checks passed
@lpcox lpcox deleted the copilot/go-fan-review-wazero-module branch May 7, 2026 16:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

3 participants