Memory and calls
- The module owns its linear memory and exports it as
memory. - The host places input by calling
alloc(len), writing at the returned pointer, and passing(ptr, len). After the call returns, the host callsfree(ptr, len). - The module returns output as one
u64:(ptr << 32) | len. The host readslenbytes atptrand callsfree(ptr, len).0means no output. - A host import that returns bytes does the same in reverse: the host calls the module’s
alloc, writes, and returns the packedu64; the module frees it. - Strings are UTF-8. JSON payloads are objects.
- Calls are serialized. The host never calls two exports of one instance at the same time, and a host import runs on the stack of the export that called it.
Exports
Required:
Optional, each announced by a name in the manifest’s
capabilities. The host refuses a capability without its export, and an export without its capability:
Optional without a capability:
dir is 0 for bytes from the client and 1 for bytes from the server.
conn identifies a connection within one instance. Under instances: per_connection (the default) each instance serves one connection and conn is always 0. Under instances: per_lane one instance serves every connection of the lane, the module keys its state by conn, and open and close bracket each one.
Imports, module hoop
A module that imports anything else fails to load. The exception is
wasi_snapshot_preview1 when the manifest says "wasi": true. WASI then sees no filesystem, no network, no environment and no arguments; the clock, random_get, fd_write to 1 and 2 (forwarded to the log), proc_exit and the memory functions work.
Manifest
describe returns it:
Payloads
Field names are the JSON names of the Sidecar’sStatement. The host refuses unknown fields.
DecodeResult, from decode
- The host fills
protocoland refuses a payload that carries it, so a module cannot impersonate another protocol. directiondefaults to the call’sdir:clientorserver.operationis one ofselect,insert,update,delete,create,drop,alter,truncate,grant,revoke,call,show,set,begin,commit,rollback,get,post,put,patch,head,options,connect,trace,exec_line,env_set, thesftp_*set,ws_message,ws_close,otherorunknown. Anything else is a decode error. The native verb goes undermetadata["<protocol>.verb"], where ametadatarule reads it.- The module lowercases table and relation names. The policy compares them as they arrive.
consumedcounts the input bytes the module finished with. An incomplete trailing message is not an error: return the statements before it and stopconsumedat its first byte. The host keeps the rest and passes it again, prefixed to the next read, up tomax_reassembly.- A non-empty
errormeans the bytes are malformed for this protocol. The host drops the connection and ignoresconsumedandstatements. - A request that carries a credential never puts it on the statement. The module keeps the value in its connection state and puts an opaque handle (a counter is enough) under
metadata["<protocol>.credential"]. The host callstake_credentialonly on a lane with per-request identity; audit, policy and the analyzer on every other lane see the statement asdecodereturned it.
SQLAnalysis, from analyze_sql
complete: false means the scan could not finish; operation is then unknown and reason says why. Copy the fields onto the statement.
CredentialResult, from take_credential
metadata["<protocol>.credential"], returns the credential it stands for, removes the key from statement and forgets the entry, so a second call with the same handle answers ok: false. ok: false also means the request carried none. With ok: true the module returns statement with neither the handle nor the credential on it; the host replaces the statement with it before policy, audit or the analyzer see it.
ContentResult, from content
ok: false means nothing to classify. cache_key names the statement’s shape with literals stripped, so one verdict serves its repeats; empty means never cache.
RewriteResult, from rewrite and flush
bytes may be empty: the module may hold rows until it can rebuild them. flush releases everything held; the host calls it when the connection ends. cells counts the values mask returned changed (compare the bytes it returned with the bytes it received); rows counts the rows that hold at least one. They feed the audit trail’s masked counts. A non-empty error field fails the stream closed.
deny and filter return raw bytes, no JSON. A trap in filter fails the stream closed.
Masking
The host callsenable_rewrite(conn) before any server bytes arrive, and only when the lane has a masker. The module then receives every server chunk through rewrite after decode saw it, calls mask(column, value) for each cell it can name, and returns rebuilt frames. Column is empty when the protocol names none. The returned value may differ in length, so the module recomputes every length prefix. The host forwards unmasked any value the module did not pass through mask: the module decides which bytes are data.
Failure rules
Each of these fails closed, and none reaches another connection or lane:- A trap (unreachable, out-of-bounds, stack overflow), a call past
call_timeout_ms, or memory abovememory_limit_pagescloses the instance. The call reports an error; the host drops the connection with the deny frame whendenyis available and a close otherwise. Underper_lanethe lane refuses new connections until the host rebuilds the instance. - Malformed output (not JSON, an unknown field, an
operationoutside the list,protocolpresent) is a decode error for that connection. - A failing
describe, a capability without its export, an export without its capability, an undeclared import, aprotocolwithout thex-prefix, or a sha256 mismatch refuses the module at load, so the lane never starts.
Conformance
hoop start sidecar --codec-test <module.wasm> [fixtures.json...] proves the contract against a module. Checks that need no knowledge of the protocol run on every module; the ones that need bytes run over the author’s fixtures.
A fixture file holds one or more scripts:
expect.statements compares only the fields it names. expect.error: true means decode must report an error.
Next
Writing a Plug-in
The SDKs that implement this contract for you.
Codec Plug-ins
Configure a lane with a plug-in and read how it fails.