WASM Edge Gateway: 5 Core Designs for Building Plugin-Based API Gateway with Go

边缘计算

Your API Gateway Requires a Full Release Just to Add a Feature

At 2 AM, an urgent rate limit policy change comes in — you discover Kong plugins only support Lua, APISIX plugins only run in OpenResty, and Envoy native Filters require recompiling the entire C++ binary. Plugin language constraints, no hot updates, poor security isolation — every feature change is a full release, and the gateway team becomes the bottleneck for the entire company.

In 2026, the WASM Edge Gateway changes everything. Go host process loads WASM plugins, choose TinyGo/Rust/AssemblyScript, plugins run in sandboxes they cannot escape, hot reload with zero downtime, and seamless Envoy WASM Filter integration. This guide walks you through 5 core designs, from gateway architecture to Go host runtime, from WASM plugin development to Envoy Filter integration, from edge deployment to hot reload, with complete code throughout.


WASM Edge Gateway Core Concepts

Concept Description
WASM Gateway API gateway based on WebAssembly plugin system, plugins developed in multiple languages, sandboxed, hot-loadable
WASM Plugin Gateway extension module compiled to .wasm binary, runs in sandbox, supports auth, rate limiting, transformation
Envoy WASM Filter Envoy proxy WASM filter, developed with Proxy-WASM SDK, embeddable in Go/C++/Rust
Proxy-WASM Envoy's WASM proxy interface specification, defines lifecycle hooks like onHttpRequest/onHttpResponse
Edge Routing API routing rules executed at CDN/edge nodes, dispatching requests to nearest backend services
Plugin Sandbox WASM linear memory isolation model, plugins cannot access host process memory or system resources
Hot Reload Runtime replacement of WASM plugin modules without restarting the gateway process, zero request loss

WASM Edge Gateway Architecture

┌──────────────────────────────────────────────────────┐
│                   Edge Gateway (Go)                   │
│  ┌──────────┐  ┌───────────┐  ┌──────────────────┐  │
│  │  Route   │  │  Plugin   │  │  Plugin Chain    │  │
│  │  Matcher │  │  Loader   │  │  ┌──┐┌──┐┌──┐   │  │
│  └────┬─────┘  └─────┬─────┘  │  │Au││RL││Co│   │  │
│       │              │        │  │th││im││RS│   │  │
│  ┌────▼──────────────▼────┐   │  └──┘└──┘└──┘   │  │
│  │    Wazero Runtime      │   └────────┬─────────┘  │
│  │  ┌──────┐ ┌──────┐    │            │            │
│  │  │Auth  │ │Rate  │    │  ┌─────────▼──────────┐ │
│  │  │WASM  │ │Limit │    │  │  Reverse Proxy     │ │
│  │  │Plugin│ │WASM  │    │  │  (Upstream)        │ │
│  │  └──────┘ └──────┘    │  └────────────────────┘ │
│  └───────────────────────┘                          │
└──────────────────────────────────────────────────────┘

Problem Analysis: 5 Limitations of Traditional API Gateways

  1. Plugin language constraints: Kong only supports Lua, APISIX only supports Lua/Go (requires compilation), Envoy native Filters require C++ recompilation — your team's tech stack is held hostage by the gateway
  2. No hot updates: Modifying plugin logic requires restarting the gateway process or reloading configuration — high risk and long maintenance windows
  3. Poor security isolation: Lua plugins directly access host process memory — a single malicious or buggy plugin can crash the entire gateway instance
  4. High extension cost: Adding new auth methods or custom rate limiting requires deep understanding of gateway source code, long development cycles, and broad regression testing
  5. Inconsistent multi-gateway: Kong/APISIX/Envoy/Nginx each have their own plugin systems — cross-gateway policies cannot be reused, doubling maintenance costs

Design 1: WASM Gateway Architecture

The core idea of a WASM gateway is decomposing gateway functionality into pluggable WASM modules. The Go host handles route dispatching and plugin orchestration, while WASM plugins handle specific business logic.

package gateway

type GatewayConfig struct {
    ListenAddr    string            `json:"listenAddr"`
    Routes        []RouteConfig     `json:"routes"`
    PluginChain   []PluginConfig    `json:"pluginChain"`
    UpstreamPool  map[string]string `json:"upstreamPool"`
}

type RouteConfig struct {
    Path        string   `json:"path"`
    Methods     []string `json:"methods"`
    PluginChain []string `json:"pluginChain"`
    Upstream    string   `json:"upstream"`
    Rewrite     string   `json:"rewrite,omitempty"`
}

type PluginConfig struct {
    Name    string `json:"name"`
    WasmURL string `json:"wasmUrl"`
    Config  []byte `json:"config"`
}

type EdgeGateway struct {
    config   GatewayConfig
    runtime  *WASMRuntime
    routes   *RouteMatcher
    plugins  *PluginChainManager
    proxy    *ReverseProxy
}

func NewEdgeGateway(cfg GatewayConfig) (*EdgeGateway, error) {
    rt, err := NewWASMRuntime()
    if err != nil {
        return nil, fmt.Errorf("init wasm runtime: %w", err)
    }

    pcm, err := NewPluginChainManager(rt, cfg.PluginChain)
    if err != nil {
        return nil, fmt.Errorf("init plugin chain: %w", err)
    }

    return &EdgeGateway{
        config:  cfg,
        runtime: rt,
        routes:  NewRouteMatcher(cfg.Routes),
        plugins: pcm,
        proxy:   NewReverseProxy(cfg.UpstreamPool),
    }, nil
}

Design 2: Go Host Runtime Implementation

Using the wazero runtime to load WASM plugins, each plugin runs in an independent sandbox, implementing request/response interception through exported functions.

package gateway

import (
    "context"
    "net/http"

    "github.com/tetratelabs/wazero"
    "github.com/tetratelabs/wazero/api"
    "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1"
)

type WASMPlugin struct {
    Name    string
    runtime wazero.Runtime
    module  api.Module
}

type WASMRuntime struct {
    runtime wazero.Runtime
    ctx     context.Context
    plugins map[string]*WASMPlugin
}

func NewWASMRuntime() (*WASMRuntime, error) {
    ctx := context.Background()
    rt := wazero.NewRuntime(ctx)

    _, err := rt.NewHostModuleBuilder("env").
        NewFunctionBuilder().
        WithFunc(func(m api.Module, offset uint32, size uint32) {
            buf := make([]byte, size)
            m.Memory().Read(offset, buf)
        }).
        Export("log").
        NewFunctionBuilder().
        WithFunc(func(m api.Module, statusCode uint32, bodyOff uint32, bodyLen uint32) {
            buf := make([]byte, bodyLen)
            m.Memory().Read(bodyOff, buf)
        }).
        Export("sendLocalReply").
        Instantiate(ctx)
    if err != nil {
        return nil, err
    }

    wasi_snapshot_preview1.MustInstantiate(ctx, rt)

    return &WASMRuntime{
        runtime: rt,
        ctx:     ctx,
        plugins: make(map[string]*WASMPlugin),
    }, nil
}

func (wr *WASMRuntime) LoadPlugin(name string, wasmBytes []byte) (*WASMPlugin, error) {
    mod, err := wr.runtime.Instantiate(wr.ctx, wasmBytes)
    if err != nil {
        return nil, fmt.Errorf("instantiate plugin %s: %w", name, err)
    }

    plugin := &WASMPlugin{
        Name:    name,
        runtime: wr.runtime,
        module:  mod,
    }
    wr.plugins[name] = plugin
    return plugin, nil
}

func (p *WASMPlugin) ExecuteRequestFilter(req *http.Request) (*http.Response, bool) {
    ctx := context.Background()
    fn := p.module.ExportedFunction("onHttpRequest")
    if fn == nil {
        return nil, true
    }

    results, err := fn.Call(ctx)
    if err != nil {
        return nil, true
    }

    return nil, results[0] == 0
}

func (p *WASMPlugin) ExecuteResponseFilter(resp *http.Response) bool {
    ctx := context.Background()
    fn := p.module.ExportedFunction("onHttpResponse")
    if fn == nil {
        return true
    }

    results, err := fn.Call(ctx)
    if err != nil {
        return true
    }

    return results[0] == 0
}

Design 3: WASM Plugin Development

Using TinyGo to write auth and rate limiting plugins, compiled to WASI target, exporting onHttpRequest and onHttpResponse lifecycle hooks.

package main

import "unsafe"

var memoryBuffer []byte

//export allocate
func allocate(size int32) unsafe.Pointer {
    memoryBuffer = make([]byte, size)
    return unsafe.Pointer(&memoryBuffer[0])
}

//export onHttpRequest
func onHttpRequest() int32 {
    headers := getHeaders()
    token := getHeader(headers, "Authorization")
    if token == "" {
        sendLocalReply(401, "Unauthorized")
        return 1
    }
    if !validateToken(token) {
        sendLocalReply(403, "Forbidden")
        return 1
    }
    return 0
}

//export onHttpResponse
func onHttpResponse() int32 {
    addHeader("X-Response-Time", getCurrentTime())
    addHeader("X-Gateway-Version", "1.0.0")
    return 0
}

func validateToken(token string) bool {
    if len(token) < 10 {
        return false
    }
    return true
}

func getHeaders() map[string]string {
    return make(map[string]string)
}

func getHeader(headers map[string]string, key string) string {
    return headers[key]
}

func sendLocalReply(statusCode int32, body string) {}

func addHeader(key string, value string) {}

func getCurrentTime() string {
    return "2026-06-18T00:00:00Z"
}

func main() {}

Rate limiting plugin example:

package main

import "unsafe"

var requestCount int32 = 0
var maxRequests int32 = 1000

//export onHttpRequest
func onHttpRequest() int32 {
    requestCount++
    if requestCount > maxRequests {
        sendLocalReply(429, "Too Many Requests")
        return 1
    }
    addHeader("X-RateLimit-Remaining", int32ToStr(maxRequests-requestCount))
    return 0
}

func int32ToStr(n int32) string {
    if n == 0 {
        return "0"
    }
    var buf [12]byte
    pos := len(buf)
    for n > 0 {
        pos--
        buf[pos] = byte('0' + n%10)
        n /= 10
    }
    return string(buf[pos:])
}

func sendLocalReply(code int32, body string) {}
func addHeader(key, val string) {}

func main() {}

Build commands:

tinygo build -o auth-plugin.wasm -target=wasi -no-debug -scheduler=none .
tinygo build -o ratelimit-plugin.wasm -target=wasi -no-debug -scheduler=none .

Design 4: Envoy WASM Filter Integration

Developing Envoy-compatible WASM Filters through the Proxy-WASM SDK, enabling seamless integration with Envoy/Istio service mesh.

package main

import (
    "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm"
    "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types"
)

func main() {
    proxywasm.SetVMContext(&vmContext{})
}

type vmContext struct {
    types.DefaultVMContext
}

func (v *vmContext) NewPluginContext(contextID uint32) types.PluginContext {
    return &pluginContext{}
}

type pluginContext struct {
    types.DefaultPluginContext
    maxRequests int32
}

func (p *pluginContext) OnPluginStart(configurationSize int) types.OnPluginStartStatus {
    data, err := proxywasm.GetPluginConfiguration(configurationSize)
    if err != nil && err != types.ErrorStatusNotFound {
        proxywasm.LogCriticalf("error reading config: %v", err)
        return types.OnPluginStartStatusFailed
    }
    if len(data) > 0 {
        p.maxRequests = parseInt32(data)
    }
    return types.OnPluginStartStatusOK
}

func (p *pluginContext) NewHttpContext(contextID uint32) types.HttpContext {
    return &httpContext{maxRequests: p.maxRequests}
}

type httpContext struct {
    types.DefaultHttpContext
    maxRequests  int32
    requestCount int32
}

func (ctx *httpContext) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action {
    ctx.requestCount++
    if ctx.requestCount > ctx.maxRequests {
        proxywasm.SendLocalReply(429, "rate limited", nil, 0, "")
        return types.ActionPause
    }

    token, err := proxywasm.GetHttpRequestHeader("authorization")
    if err != nil || token == "" {
        proxywasm.SendLocalReply(401, "unauthorized", nil, 0, "")
        return types.ActionPause
    }

    proxywasm.AddHttpRequestHeader("x-wasm-filter", "envoy-auth-v1")
    return types.ActionContinue
}

func (ctx *httpContext) OnHttpResponseHeaders(numHeaders int, endOfStream bool) types.Action {
    proxywasm.AddHttpResponseHeader("x-wasm-processed", "true")
    return types.ActionContinue
}

func parseInt32(data []byte) int32 {
    var result int32
    for _, b := range data {
        if b >= '0' && b <= '9' {
            result = result*10 + int32(b-'0')
        }
    }
    return result
}

Envoy configuration integration:

name: envoy.filters.http.wasm
typed_config:
  "@type": type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm
  config:
    name: auth-filter
    root_id: auth-filter
    vm_config:
      vm_id: auth-filter-vm
      runtime: envoy.wasm.runtime.v8
      code:
        local:
          filename: /etc/envoy/auth-filter.wasm
    configuration:
      "@type": type.googleapis.com/google.protobuf.StringValue
      value: "1000"

Design 5: Edge Deployment and Hot Reload

Implementing plugin remote loading and version management, supporting WASM module fetching from S3/OSS, zero-downtime version rollback.

package gateway

import (
    "bytes"
    "crypto/sha256"
    "encoding/hex"
    "fmt"
    "io"
    "net/http"
    "sync"
    "sync/atomic"
    "time"
)

type PluginVersion struct {
    Name     string `json:"name"`
    Version  string `json:"version"`
    SHA256   string `json:"sha256"`
    WasmURL  string `json:"wasmUrl"`
    LoadedAt int64  `json:"loadedAt"`
}

type PluginRegistry struct {
    runtime    *WASMRuntime
    versions   map[string]*PluginVersion
    current    sync.Map
    mu         sync.RWMutex
    httpClient *http.Client
}

func NewPluginRegistry(rt *WASMRuntime) *PluginRegistry {
    return &PluginRegistry{
        runtime:  rt,
        versions: make(map[string]*PluginVersion),
        httpClient: &http.Client{
            Timeout: 30 * time.Second,
        },
    }
}

func (pr *PluginRegistry) LoadFromRemote(name string, version PluginVersion) error {
    resp, err := pr.httpClient.Get(version.WasmURL)
    if err != nil {
        return fmt.Errorf("fetch wasm from %s: %w", version.WasmURL, err)
    }
    defer resp.Body.Close()

    var buf bytes.Buffer
    if _, err := io.Copy(&buf, resp.Body); err != nil {
        return fmt.Errorf("read wasm body: %w", err)
    }

    wasmBytes := buf.Bytes()
    hash := sha256.Sum256(wasmBytes)
    checksum := hex.EncodeToString(hash[:])
    if checksum != version.SHA256 {
        return fmt.Errorf("checksum mismatch: expected %s, got %s", version.SHA256, checksum)
    }

    plugin, err := pr.runtime.LoadPlugin(name, wasmBytes)
    if err != nil {
        return fmt.Errorf("load plugin %s: %w", name, err)
    }

    pr.mu.Lock()
    old, exists := pr.versions[name]
    pr.versions[name] = &version
    pr.mu.Unlock()

    pr.current.Store(name, plugin)

    if exists {
        fmt.Printf("plugin %s upgraded: %s -> %s\n", name, old.Version, version.Version)
    } else {
        fmt.Printf("plugin %s loaded: v%s\n", name, version.Version)
    }

    return nil
}

type PluginChainManager struct {
    registry *PluginRegistry
    chains   map[string][]string
    mu       sync.RWMutex
}

func NewPluginChainManager(rt *WASMRuntime, configs []PluginConfig) (*PluginChainManager, error) {
    registry := NewPluginRegistry(rt)
    pcm := &PluginChainManager{
        registry: registry,
        chains:   make(map[string][]string),
    }

    for _, cfg := range configs {
        version := PluginVersion{
            Name:    cfg.Name,
            Version: "1.0.0",
            WasmURL: cfg.WasmURL,
        }
        if err := registry.LoadFromRemote(cfg.Name, version); err != nil {
            return nil, fmt.Errorf("load plugin %s: %w", cfg.Name, err)
        }
    }

    return pcm, nil
}

func (pcm *PluginChainManager) ExecuteChain(chainName string, req *http.Request) (*http.Response, bool) {
    pcm.mu.RLock()
    chain, exists := pcm.chains[chainName]
    pcm.mu.RUnlock()

    if !exists {
        return nil, true
    }

    for _, pluginName := range chain {
        plugin, ok := pcm.registry.current.Load(pluginName)
        if !ok {
            continue
        }
        wasmPlugin := plugin.(*WASMPlugin)
        resp, allowed := wasmPlugin.ExecuteRequestFilter(req)
        if !allowed {
            return resp, false
        }
    }

    return nil, true
}

Complete gateway routing + plugin chain example:

package main

import (
    "log"
    "net/http"

    "github.com/yourorg/edge-gateway/gateway"
)

func main() {
    cfg := gateway.GatewayConfig{
        ListenAddr: ":8080",
        Routes: []gateway.RouteConfig{
            {
                Path:        "/api/v1/users",
                Methods:     []string{"GET", "POST"},
                PluginChain: []string{"auth-plugin", "ratelimit-plugin"},
                Upstream:    "user-service",
            },
            {
                Path:        "/api/v1/orders",
                Methods:     []string{"GET", "POST", "PUT"},
                PluginChain: []string{"auth-plugin", "ratelimit-plugin", "cors-plugin"},
                Upstream:    "order-service",
            },
        },
        PluginChain: []gateway.PluginConfig{
            {Name: "auth-plugin", WasmURL: "https://cdn.example.com/plugins/auth-v1.2.0.wasm"},
            {Name: "ratelimit-plugin", WasmURL: "https://cdn.example.com/plugins/ratelimit-v1.1.0.wasm"},
            {Name: "cors-plugin", WasmURL: "https://cdn.example.com/plugins/cors-v1.0.0.wasm"},
        },
        UpstreamPool: map[string]string{
            "user-service":  "http://127.0.0.1:8081",
            "order-service": "http://127.0.0.1:8082",
        },
    }

    gw, err := gateway.NewEdgeGateway(cfg)
    if err != nil {
        log.Fatalf("create gateway: %v", err)
    }

    mux := http.NewServeMux()
    mux.HandleFunc("/", gw.HandleRequest)

    log.Printf("WASM Edge Gateway listening on %s", cfg.ListenAddr)
    log.Fatal(http.ListenAndServe(cfg.ListenAddr, mux))
}

Pitfall Guide: 5 Common Traps

Trap 1: WASM Plugins Directly Operating HTTP Connections

// ❌ Wrong: Cannot establish TCP connections inside WASM sandbox
conn, _ := net.Dial("tcp", "backend:8080")

// ✅ Correct: Proxy network requests through host-provided Host Functions
results, _ := hostNetDial.Call(ctx, uint64(offset), uint64(len(host)), uint64(port))

Trap 2: Shared State Between Plugins Causing Data Races

// ❌ Wrong: Multiple WASM plugin instances sharing global variables
var tokenCache = make(map[string]bool)

// ✅ Correct: Each plugin instance uses independent memory, state shared through host
// Host side provides KV Store Host Function

Trap 3: Ignoring WASM Plugin Execution Timeout

// ❌ Wrong: Wait indefinitely for plugin return, malicious/infinite-loop plugins block entire request
results, err := fn.Call(ctx)

// ✅ Correct: Use context.WithTimeout to control execution time
timeoutCtx, cancel := context.WithTimeout(ctx, 50*time.Millisecond)
defer cancel()
results, err := fn.Call(timeoutCtx)

Trap 4: Not Handling In-Flight Requests During Hot Reload

// ❌ Wrong: Directly unload old module, in-flight requests access freed memory
oldModule.Close(ctx)

// ✅ Correct: Reference counting + graceful wait, unload only after all requests complete
atomic.AddInt32(&refCount, 1)
defer atomic.AddInt32(&refCount, -1)
// During hot reload, wait for refCount to reach 0 before unloading

Trap 5: TinyGo Build Without WASI Target

# ❌ Wrong: Using -wasm target causes missing WASI imports
tinygo build -o plugin.wasm -target=wasm .

# ✅ Correct: Use wasi target for standard system call support
tinygo build -o plugin.wasm -target=wasi -no-debug -scheduler=none .

Error Troubleshooting: 10 Common Errors

# Error Message Cause Solution
1 out of bounds memory access Host reads/writes WASM memory offset out of bounds Check allocate return value and data length calculation
2 function export not found: onHttpRequest WASM module does not export lifecycle hook Ensure TinyGo exports //export onHttpRequest during compilation
3 import not found: wasi_snapshot_preview1 WASI module not initialized Call wasi_snapshot_preview1.MustInstantiate
4 instantiation error: memory size exceeds limit Plugin initial memory exceeds limit Increase WithMemoryLimitPages or optimize memory usage
5 context deadline exceeded in plugin execution Plugin execution timeout Use context.WithTimeout and check plugin logic
6 checksum mismatch Remote loaded WASM file verification failed Confirm file integrity, re-upload WASM module
7 plugin version conflict Same-name plugin version conflict Use semantic versioning, ensure uniqueness
8 goroutine stack overflow in wasm TinyGo default scheduler causes stack overflow Add -scheduler=none during compilation
9 wasm validation error: invalid section WASM binary file corrupted Recompile WASM module, check build command
10 hot reload: old module still referenced Old module references not released during hot reload Ensure request reference count reaches zero before unloading

Advanced Optimization Tips

1. WASM Plugin Pre-compiled Cache

type CompiledCache struct {
    cache map[string]wazero.CompiledModule
    mu    sync.RWMutex
}

func (cc *CompiledCache) GetOrCompile(ctx context.Context, rt wazero.Runtime, key string, wasmBytes []byte) (wazero.CompiledModule, error) {
    cc.mu.RLock()
    if compiled, ok := cc.cache[key]; ok {
        cc.mu.RUnlock()
        return compiled, nil
    }
    cc.mu.RUnlock()

    compiled, err := rt.CompileModule(ctx, wasmBytes)
    if err != nil {
        return nil, err
    }

    cc.mu.Lock()
    cc.cache[key] = compiled
    cc.mu.Unlock()

    return compiled, nil
}

2. Plugin Chain Parallel Execution

func (pcm *PluginChainManager) ExecuteChainParallel(chainName string, req *http.Request) (*http.Response, bool) {
    pcm.mu.RLock()
    chain := pcm.chains[chainName]
    pcm.mu.RUnlock()

    type result struct {
        resp    *http.Response
        allowed bool
    }
    ch := make(chan result, len(chain))

    for _, pluginName := range chain {
        go func(name string) {
            plugin, _ := pcm.registry.current.Load(name)
            if plugin == nil {
                ch <- result{nil, true}
                return
            }
            resp, allowed := plugin.(*WASMPlugin).ExecuteRequestFilter(req)
            ch <- result{resp, allowed}
        }(pluginName)
    }

    for i := 0; i < len(chain); i++ {
        r := <-ch
        if !r.allowed {
            return r.resp, false
        }
    }
    return nil, true
}

3. Plugin Metrics Collection

type PluginMetrics struct {
    Name        string
    ExecCount   atomic.Int64
    ErrorCount  atomic.Int64
    TotalTimeNs atomic.Int64
}

func (m *PluginMetrics) Record(execTime time.Duration, hasError bool) {
    m.ExecCount.Add(1)
    m.TotalTimeNs.Add(execTime.Nanoseconds())
    if hasError {
        m.ErrorCount.Add(1)
    }
}

func (m *PluginMetrics) AvgTime() time.Duration {
    count := m.ExecCount.Load()
    if count == 0 {
        return 0
    }
    return time.Duration(m.TotalTimeNs.Load() / count)
}

4. Plugin Configuration Hot Injection

func (wr *WASMRuntime) InjectConfig(pluginName string, configJSON []byte) error {
    plugin, ok := wr.plugins[pluginName]
    if !ok {
        return fmt.Errorf("plugin %s not found", pluginName)
    }

    fn := plugin.module.ExportedFunction("onConfigUpdate")
    if fn == nil {
        return nil
    }

    offset, err := writeString(plugin.module, string(configJSON))
    if err != nil {
        return err
    }

    _, err = fn.Call(wr.ctx, uint64(offset), uint64(len(configJSON)))
    return err
}

5. Edge Node Plugin Distribution

type EdgeDistributor struct {
    registry   *PluginRegistry
    nodes      []string
    httpClient *http.Client
}

func (ed *EdgeDistributor) PushToEdge(pluginName string, version PluginVersion) error {
    for _, node := range ed.nodes {
        url := fmt.Sprintf("http://%s/api/v1/plugins/load", node)
        payload, _ := json.Marshal(map[string]interface{}{
            "name":    pluginName,
            "version": version.Version,
            "wasmUrl": version.WasmURL,
            "sha256":  version.SHA256,
        })

        resp, err := ed.httpClient.Post(url, "application/json", bytes.NewReader(payload))
        if err != nil {
            fmt.Printf("push to %s failed: %v\n", node, err)
            continue
        }
        resp.Body.Close()
        fmt.Printf("pushed %s v%s to %s\n", pluginName, version.Version, node)
    }
    return nil
}

Comparison: WASM Gateway vs Kong vs APISIX vs Envoy Native

Dimension WASM Gateway (Go) Kong APISIX Envoy Native
Plugin Language Go/TinyGo/Rust/AS Lua Lua/Go (external) C++
Hot Reload WASM module hot-load Requires restart/DB sync etcd watch Requires binary recompilation
Security Isolation WASM sandbox strong isolation Lua shared process memory Lua shared process memory Process-level isolation
Plugin Size 100KB-2MB Lua script <50KB Lua script <50KB Requires recompiling entire binary
Execution Performance Near-native (90-95%) LuaJIT high performance LuaJIT high performance Native C++ fastest
Multi-gateway Reuse WASM module universal Kong only APISIX only Envoy only
Learning Curve Requires WASM+Go knowledge Lua simple Lua simple C++ high barrier
Ecosystem Maturity Rapid growth (2026) Mature (500+ plugins) Mature (100+ plugins) Mature (Istio ecosystem)

When developing WASM edge gateways, the following online tools can significantly boost productivity:

  1. JSON Formatter — Quickly format and validate JSON when debugging gateway plugin configurations and routing rules
  2. Hash Calculator — Calculate SHA256 checksums for WASM modules to verify plugin integrity during remote loading
  3. Curl to Code Converter — Convert curl requests to Go HTTP code for quickly writing gateway test cases

Summary and Outlook

The WASM edge gateway doesn't replace Kong or Envoy — it provides a layer of language-agnostic, secure, hot-reloadable plugin abstraction on top of them. The 2026 trend is: Envoy as the data plane proxy, WASM Filter as the portable plugin layer, and Go host as the lightweight gateway orchestrator. Combined, you get Envoy's production-grade stability + WASM's plugin flexibility + Go's development efficiency. Next, watch for the WASM Component Model and WASI Preview 3 standardization progress — they will completely solve cross-language plugin interoperability.


Further Reading

  1. Proxy-WASM ABI Specification — Envoy WASM Filter interface specification
  2. Wazero Runtime Documentation — Pure Go WASM runtime official docs
  3. Envoy WASM Filter Design — Envoy WASM filter design documentation
  4. WebAssembly Component Model — WASM component model proposal
  5. TinyGo WASI Target Guide — TinyGo WASI compilation guide

Try these browser-local tools — no sign-up required →

#WASM边缘网关#Go WASM网关#WASI代理#边缘API网关#WASM插件网关#2026#Envoy WASM Filter