WebAssembly (WASM) Node Development
Harness the power of near-native performance by writing community nodes in Go, Rust, C/C++, Zig, Python, or AssemblyScript. WASM nodes provide ultimate computing power with an enterprise-grade isolated sandbox (Zero Memory Leaks). nLink utilizes the wazero runtime Engine to natively process WASM internally without any CGO dependency.
WASM Node Architecture & Security Restrictions
Instead of relying on the Node.js V8 execution context like standard JavaScript nodes, WASM nodes compile source code into an execution.wasm binary. This allows heavy computational workloads, robust type safety, and raw performance for high-throughput batch processing.
The internal setup inside nLink employs O(1) Batch Sandbox Initialization and safe memory bindings. This means no matter if you pass 1 record or 1 million records through a node in a workflow, the memory overhead scales perfectly linearly without crashing.
- No Filesystem Access: Wasm nodes cannot read or write any files on the host server (throws
fs.ErrNotExist). - No Environment Variables: Wasm nodes are completely blinded from the host server's
process.env, preventing credentials theft. - No Network Sockets: HTTP requests, TCP ports, and socket listeners are stripped out. Nodes can only execute pure computing math and standard data transformations.
- Memory Bounding: The engine automatically hard-limits Wasm execution memory to 32MB (512 Pages), neutralizing memory exhaustion attacks.
nlink-community.wasm_demo/
├── package.json # NPM Metadata
├── definition.json # Graphical Interface Configuration
├── main.go # Source code logic
└── execution.wasm # The compiled core logic binary (Replaces execution.js)Auto Scaffold Generator
You no longer need to manually create these files! Head over to your nLink dashboard: Settings > Community Store and click the "Scaffold WASM" button. nLink will automatically generate the definition.json, package.json, and the main.go boilerplate with memory hooks pre-configured. Just download the ZIP, add your logic, compile, and re-upload!
WASM Boilerplate (Golang)
If you prefer to start from scratch, or want to understand what the Auto Scaffold Generator produces, here is the boilerplate. When compiling Go to WebAssembly for nLink, we utilize wasip1 and standard c-shared build modes to construct a WASI Reactor. A Reactor maintains state between function runs without exiting, allowing nLink's execution engine to directly invoke linear memory hooks.
package main
import (
"encoding/json"
"reflect"
"unsafe"
)
// Global map to keep alive memory allocations against GC and Host GC
var allocations = make(map[uint32][]byte)
// 1. Memory Hook for nLink to inject parameter data into Guest Memory
//go:wasmexport allocate
func allocate(size uint32) uint32 {
buf := make([]byte, size)
ptr := uint32(uintptr(unsafe.Pointer(&buf[0])))
allocations[ptr] = buf
return ptr
}
// 2. Memory Hook for nLink to instantly release Memory preventing OOM
//go:wasmexport deallocate
func deallocate(ptr uint32, size uint32) {
delete(allocations, ptr)
}
// 3. Application Hook triggering business logic
//go:wasmexport execute_node
func execute_node(ptr uint32, length uint32) uint64 {
var inputBytes []byte
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&inputBytes))
hdr.Data = uintptr(ptr)
hdr.Len = int(length)
hdr.Cap = int(length)
// Step 1. Parse JSON Context from nLink
var inputObj map[string]interface{}
if err := json.Unmarshal(inputBytes, &inputObj); err != nil {
return packError("invalid input JSON")
}
// Step 2. Do Pure Computing Logic here (No network / HTTP access allowed)
text := "World"
if params, ok := inputObj["params"].(map[string]interface{}); ok {
if t, hasT := params["text"].(string); hasT {
text = t
}
}
// Step 3. Transform to standard array output format
outObj := make([]interface{}, 1)
outObj[0] = map[string]interface{}{
"json": map[string]interface{}{
"wasm_processed": true,
"message": "Hello, " + text + "! Greetings from WebAssembly Runtime! 🚀",
"original_data": inputObj["$json"],
},
}
outBytes, _ := json.Marshal(outObj)
return packResult(outBytes)
}
func packError(msg string) uint64 {
b, _ := json.Marshal(map[string]interface{}{"error": msg})
return packResult(b)
}
// 4. Memory compression to bridge 32bit architectures
func packResult(outBytes []byte) uint64 {
ptr := uint32(uintptr(unsafe.Pointer(&outBytes[0])))
allocations[ptr] = outBytes
return (uint64(ptr) << 32) | uint64(len(outBytes))
}
// Required explicitly for normal execution
func main() {}Build & Compilation Constraints
nLink's Wasm Node Runner expects execution.wasm rather than execution.js. The WASM module must satisfy the following strict criteria:
- Reactor Export Formats
It MUST export the function interface:allocate(size i32) -> i32deallocate(ptr i32, size i32)(Optional, highly recommended for zero memory leak)execute_node(ptr i32, size i32) -> u64 - Security Restrictions (Air-gapped)
WASM instances process Pure Logic in deep sandboxing. Due to security considerations, Network access (Syscalls overhead HTTP) and Filesystem access are structurally stripped and isolated by nLink Wazero engine. Focus your nodes entirely on Data Transforming. - Compilation Command (Go 1.22+)
Run the following script to create a cross-platform Reactor binary representing the WASI snapshot specification:GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared -o execution.wasm main.go
Workflow Fallback Strategy
The backend engine automatically attempts to read execution.wasm first upon loading standard Node architectures. However, developers may bundle an execution.js as an alternative polyfill! If nLink fails to run WebAssembly or detects it missing, it will gracefully fallback to the V8 context via execution.js allowing ultimate resilience.
