Skip to content

Plugin contract ​

Use the independent SDK generated by the scaffold rather than implementing exports and pointer ownership manually. This reference explains the host boundary and compatibility rules.

Package and manifest ​

A package contains a manifest, Wasm module, and optional schema. Required fields are id, version, abi_version:1, module, protocols, memory_mib, and call_timeout_ms. Optional fields include tcp_ports/udp_ports, schema, sha256, detection, capabilities, and host_schema_version.

LimitRange
Linear memory per package instance1–64 MiB; Go template 16, Rust template 8
Data call wall time1–1000 ms; Go template 500, Rust template 100
Loaded setAt most 32 packages; unique protocols; module size at most 8 MiB
Compilation / initialization10 seconds / separate 1-second budget
SchemaRegular file, at most 1 MiB, no host-field overrides

The host currently supports host_schema_version 0..6. Higher declarations reject loading. See protocol support for aggregate generation budgets.

Stream callbacks ​

ABI exportSignature
nfs_abi_version() -> i32, returns 1
nfs_alloc(size:i32) -> ptr:i32
nfs_free(ptr:i32,size:i32)
nfs_process(kind:i32,handle:i64,originator:i32,timestamp_ns:i64,ptr:i32,len:i32) -> i32

Kinds are OPEN=1, DATA=2, GAP=3, CLOSE=4, TIMEOUT=5, DATAGRAM=6, QUIC_STREAM=7. Direction is relative to the connection originator, not the local process. DATA contains ordered bytes without message boundaries; UDP delivers one datagram. Handles are instance-local and callbacks are serial within an instance.

Input pointers are valid only during a callback; copy retained data. The host may reuse a buffer for equal-length input. Plugins must not free it or assume each callback allocates again. Empty input has zero ptr/len. Nonzero return values retire the current stream.

Record output ​

nfs.emit(handle:i64,ptr:i32,len:i32) -> i32 accepts JSON containing only type, fields, incomplete, and truncated. The host injects time, connection, addresses, service, and process metadata. Fields must match schema; declare raw content sensitive.

json
{"type":"acme_tcp.message","fields":{"acme_tcp.value":"hello","acme_tcp.request":true},"incomplete":false,"truncated":false}

Each callback allows at most 512 records / 4 MiB, with at most 1 MiB per record. Output is staged until the whole callback and every record succeed. Panic, trap, and invalid output cannot leave a partially accepted callback.

Fault isolation ​

No host filesystem, network, environment, or stdio access is granted. Restricted WASI imports grant neither directories nor sockets. Bounded pure functions support decompression, WebSocket accept, and static QPACK, without arbitrary I/O.

Timeout, trap, memory violation, or invalid output rebuilds the instance and retires active streams of the same module/shard as incomplete. Other modules/shards continue; new connections can use the replacement. Isolation is not fully per connection. Parsers must still bound buffers and correlation queues.

Detection and version changes ​

Detection uses bounded-offset hex signatures with optional direction and masks. Ambiguity rejects detection; explicit-parser errors do not trigger guessing another protocol. UDP uses port routing.

A compatible reload validates the complete candidate set before switching new connections. Existing connections retain their generation. Schema/sensitivity changes need coordinated restarts. Recompile older Go projects after SDK updates, and reassess timeout budgets retained in older manifests.