Files
mpc/cmd
zeekayandHanzo Dev 808b1ce3af mpcd's ZAP surface: fix the frame, and actually listen on it
Two independent faults, either of which alone made the surface unusable.

THE FRAME. A ZAP field offset is a BYTE offset and a bytes field is EIGHT of
them — SetBytes writes a uint32 pointer at the offset and a uint32 length at
offset+4. The request layout spaced five fields ONE byte apart (orgID 8,
walletID 9, payload 10, intentID 11, network 12), so every write destroyed the
one before it. Measured: the handler read back org_id "" with wallet_id intact,
and since zapKeygen refuses an empty org_id, that layout rejected every request
any client could physically send. The reply was the same shape — status 8,
data 9 — putting data's length word one byte past what StartObject(16)
reserved, so an 85-byte JSON body came back with '{' replaced by 0x00.

There was therefore no wire to preserve, only one to choose. This is now
byte-for-byte hanzoai/commerce's util/zapwire — status at 0, one bytes field at
8, object 16 — which is what the only client of this surface has always spoken.
Arguments travel as JSON in that one payload rather than as parallel text
fields, so the next argument is a struct field instead of an offset nobody can
see is wrong.

THE LISTENER. StartZAP had zero callers. The handlers existed in the binary and
answered on no port, so keygen/sign/status/wallets/intent were reachable over
HTTP only and commerce's ZAP transport dialled a listener that was never there.
It is now started from main beside StartScheduler, behind --api-zap-listen /
MPC_API_ZAP_LISTEN — an address, empty disables, exactly the shape
--kms-zap-listen already uses. StartZAP takes an address for the same reason:
mpcd's other listeners are addresses, and one shape for "where do I listen" is
worth more than saving a parse.

Tests reproduce commerce's frame rather than importing it (private module, and
this is the contract): both arguments survive a round trip, the reply parses as
JSON through the client's offsets, an error rides the same field as a success
behind the status byte, and a non-JSON body is refused as a wire error instead
of being reported as a missing argument. Mutation-checked — restoring the old
offsets fails three of them.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-08 14:25:43 -07:00
..