Three Layers
Overview
Section titled “Overview”GoMode has three layers compiled into a single WASM binary.
CF Request → Worker (JS) → writes request as zerobuf tagged values into WASM memory → calls handle_zerobuf(reqPtr) → Go reads request, calls Zig SIMD internally (direct call) → Go writes response at fixed offsets → JS reads response directly from WASM memory → CF ResponseLayer 1: Zig ABI (zig-abi/src/)
Section titled “Layer 1: Zig ABI (zig-abi/src/)”Zig compiles to a relocatable .o and gets linked into go.wasm via wasm-ld. Go calls Zig functions through CGo — direct call instructions, zero overhead.
SIMD batch operations
Section titled “SIMD batch operations”Compiled with -mcpu=generic+simd128 for WASM SIMD v128 instructions:
zig_simd_sum_f64(ptr, count) → sum of f64 arrayzig_simd_dot_f64(a, b, count) → dot productzig_simd_scale_f64(ptr, count, s) → multiply by scalarzig_simd_minmax_f64(ptr, count, o) → min/max in one passzig_simd_add_f64(dst, a, b, count) → element-wise additionMemory management
Section titled “Memory management”zig_alloc(len) → allocate in WASM memoryzig_free(ptr, len) → free allocationzig_free_result(ptr) → free length-prefixed resultGo passes raw pointers to Zig — same linear memory, zero copy.
Layer 2: CF Worker (worker/src/)
Section titled “Layer 2: CF Worker (worker/src/)”worker.ts — Entry point. Routes requests to direct WASM or Durable Object. Uses zerobuf to write request fields and read response fields directly in WASM memory. Only provides 3 WASI imports (fd_write, proc_exit, random_get).
go-do.ts — Durable Object. Full parity with worker path — body, headers, cookies, multi-fetch, zero-copy responses. WASM instance persists for DO lifetime.
Zero-copy data exchange
Section titled “Zero-copy data exchange”JS writes request fields directly into WASM memory as zerobuf tagged value slots (16 bytes each). Go reads at fixed offsets. Response body is read as Uint8Array — no string decode/re-encode.
Request: [0]=method [1]=path+query [2]=body [3]=headers [4]=fetch-result [5]=call-index [6+]=fan-outResponse: [0]=status [1]=content-type [2]=body [3]=headersMulti-fetch two-phase protocol
Section titled “Multi-fetch two-phase protocol”Go handlers can call http.Get() multiple times. Each call triggers one JS round-trip — no Asyncify needed.
Handler calls http.Get(url1) → returns status=-1, JS fetches url1, replaysHandler calls http.Get(url1) → cached ✓, calls http.Get(url2) → returns status=-1, JS fetches url2, replaysHandler calls http.Get(url1) → cached ✓, calls http.Get(url2) → cached ✓ → handler completesLayer 3: Go + Zig Handler (examples/hello-worker/)
Section titled “Layer 3: Go + Zig Handler (examples/hello-worker/)”Standard Go net/http handlers run unchanged on GoMode. The overlay replaces net/http at build time with a WASM-compatible implementation that uses the same types and interfaces.
package main
import ( "encoding/json" "gomode" "net/http")
func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello from GoMode!")) })
http.HandleFunc("/stats", func(w http.ResponseWriter, r *http.Request) { data := []float64{1, 2, 3, 4, 5, 6, 7, 8} sum := gomode.SumF64(data) // Zig SIMD min, max := gomode.MinMaxF64(data) // Zig SIMD
json.NewEncoder(w).Encode(map[string]float64{ "sum": sum, "min": min, "max": max, }) })
http.HandleFunc("/exchange", func(w http.ResponseWriter, r *http.Request) { // Outbound fetch — triggers multi-fetch two-phase protocol resp, err := http.Get("https://api.example.com/rates") if err != nil { http.Error(w, err.Error(), 502) return } // Process resp.Body as normal... })
http.ListenAndServe(":8080", nil)}
//export handle_zerobuffunc handleZerobuf(reqBase uint32) uint32 { return http.HandleRequest(reqBase)}Go calls Zig SIMD via CGo — the C header declares Zig function signatures, and TinyGo’s wasm-ld links both into one binary:
double zig_simd_sum_f64(uint32_t ptr, uint32_t count);double zig_simd_dot_f64(uint32_t a, uint32_t b, uint32_t count);// go-sdk/gomode.go — high-level API wrapping CGo callsfunc SumF64(data []float64) float64func DotF64(a, b []float64) float64func MinMaxF64(data []float64) (min, max float64)func ScaleF64(data []float64, scalar float64)