mirror of
https://github.com/zap-proto/zip.git
synced 2026-10-19 15:38:57 +00:00
The op-call plane marshalled its body with encoding/json. That put a text format inside the binary protocol: two services on one host, holding the same struct in memory, serialized it to JSON and parsed it back to cross a socket. JSON is the BOUNDARY encoding — it belongs on the REST routes a browser reaches and in the MCP envelope an agent reads — and it has no business between our own processes. internal/zapenc derives a wire layout from the In/Out type itself: fields take slots in declaration order, each aligned to its own width, which is the rule zap's own schema builder applies. Nothing is named on the wire — a field IS its offset — so the bytes on the socket are the bytes in memory, and the type declaration is the schema both ends read. bool, every sized int/uint, float, string, bytes, nested structs, pointers and slices cross; a map or an interface is refused at encode rather than dropped, because a field that silently does not cross is how a callee decides on a value nobody sent. Refusals cross as ZAP too, status intact, so errors.As still recovers the *HTTPError the callee returned. op.invoke takes its decoder as a PARAMETER, so there is still exactly ONE handler core under REST, MCP, CLI and the plane. The encoding belongs to the transport; the contract is the same op either way. The layout is the type, so the compatibility rule is: append fields at the end, and only at the end. Reordering, inserting or retyping changes the wire. Co-authored-by: Hanzo Dev <[email protected]>