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 deliver ultimate computing power within an enterprise-grade isolated sandbox (Zero Memory Leaks). nLink utilizes the wazero runtime engine to 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 enables heavy computational workloads, robust type safety, and raw performance for high-throughput batch processing.
nLink's internal architecture employs O(1) Batch Sandbox Initialization and safe memory bindings. Whether processing 1 record or 1 million records through a node, the memory overhead scales linearly without risking crashes.
- No Filesystem Access: Wasm nodes cannot read or write any files on the host server (throws
fs.ErrNotExist). - No Environment Variables: Wasm nodes are entirely isolated from the host server's
process.env, preventing credential theft. - No Network Sockets: HTTP requests, TCP ports, and socket listeners are strictly prohibited. Nodes can only execute pure computational 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! Navigate to your nLink dashboard: Settings > Community Store and click the Scaffold WASM button. nLink will automatically generate definition.json, package.json, and the main.go boilerplate with pre-configured memory hooks. Download the ZIP, add your logic, compile, and re-upload.
WASM Boilerplate (Golang)
If you prefer to start from scratch or want to understand the output of the Auto Scaffold Generator, review the boilerplate below. 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 invocations 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
The module MUST export the following function interfaces:allocate(size i32) -> i32deallocate(ptr i32, size i32)(Optional, but highly recommended to ensure zero memory leaks)execute_node(ptr i32, size i32) -> u64 - Security Restrictions (Air-gapped)
WASM instances process Pure Logic within deep sandboxes. For security considerations, network access (Syscalls overhead HTTP) and filesystem access are structurally stripped and isolated by the nLink Wazero engine. Focus your nodes entirely on data transformations. - Compilation Command (Go 1.22+)
Execute the following command to create a cross-platform Reactor binary adhering to 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 when loading standard Node architectures. However, developers may bundle an execution.js file as an alternative polyfill. If nLink fails to run WebAssembly or detects its absence, it will gracefully fall back to the V8 context via execution.js, ensuring maximum resilience.
