§ 02.03Core Concepts
Binary Protocol
Every frame on the wire is a 6-byte header followed by a variable-length payload. No framing ambiguity, no partial reads, no text encoding — parsing is a couple of pointer reads.
Frame header
version1 byte
type1 byte
length4 bytes
payloadlength bytes
total header = 6 bytes fixedtotal message = 6 + length bytes
- version
- Protocol revision. Currently 0x01. The daemon rejects any other version with MSG_ERROR 0x02 so protocol evolution is additive and opt-in.
- type
- Message opcode — one of the 11 types below. The daemon routes on type alone; producers and workers speak the same header shape.
- length
- 32-bit big-endian unsigned integer: the payload size in bytes. Zero is legal for MSG_READY, MSG_WAIT, MSG_HEARTBEAT, MSG_PONG and MSG_STATS.
- payload
- Exactly length bytes. The per-type layouts below are the full contract — there is no escaping, no delimiters and no padding.
Message types
| Type | Name | Direction |
|---|---|---|
0x01 | MSG_SUBMIT | producer → daemon |
0x02 | MSG_OK | daemon → producer |
0x03 | MSG_ERROR | daemon → producer |
0x04 | MSG_READY | worker → daemon |
0x05 | MSG_TASK | daemon → worker |
0x06 | MSG_DONE | worker → daemon |
0x07 | MSG_FAILED | worker → daemon |
0x08 | MSG_WAIT | daemon → worker |
0x09 | MSG_HEARTBEAT | either direction |
0x0A | MSG_PONG | either direction |
0x0B | MSG_STATS | monitor → daemon |
0x0C | MSG_STATS_RESPONSE | daemon → monitor |
Payload formats
0x01MSG_SUBMIT
[type_len : 1 byte][type : type_len bytes][payload : rest of bytes]type = "send_email"
payload = {"to":"user@gmail.com"}
bytes = [10][send_email][{"to":"user@gmail.com"}]0x02MSG_OK
[task_id : 4 bytes]task_id = 0x00000A42 → accepted task id = 26260x03MSG_ERROR
[error_code : 1 byte][message : rest of bytes]| Code | Meaning |
|---|---|
0x01 | queue full — PMAD pool exhausted |
0x02 | invalid message (bad version / length / type) |
0x03 | payload too large for the largest size class |
0x04 | unknown task type |
0x04MSG_READY
(no payload — length = 0)Sent once per connection, immediately after connect, to register
the socket as an idle worker. The daemon replies with MSG_TASK or
MSG_WAIT.0x05MSG_TASK
[task_id : 4 bytes][type_len : 1 byte][type : type_len bytes][payload : rest]Mirror of MSG_SUBMIT with the task id prepended. The worker dispatches
by type and replies with MSG_DONE or MSG_FAILED carrying the same id.0x06MSG_DONE
[task_id : 4 bytes]Confirms successful completion. The daemon frees the task slot back
to PMAD before responding to any producer waiting on this id.0x07MSG_FAILED
[task_id : 4 bytes][reason : rest of bytes]reason is a UTF-8 string propagated verbatim to producers that
observe the task, and logged by the daemon.0x08MSG_WAIT
(no payload — length = 0)Sent in place of MSG_TASK when the queue is empty. The worker keeps
the socket open and issues another MSG_READY after a short backoff.0x09MSG_HEARTBEAT
(no payload — length = 0)Liveness probe. Either side may send it; the receiver replies with
MSG_PONG. Intended for long-idle worker sockets behind stateful
load balancers.0x0AMSG_PONG
(no payload — length = 0)The only valid reply to MSG_HEARTBEAT. Shape-symmetric with
MSG_HEARTBEAT for trivial framing.0x0BMSG_STATS
(no payload — length = 0)Requests a one-shot metrics snapshot. The daemon responds with
MSG_STATS_RESPONSE.0x0CMSG_STATS_RESPONSE
[queue_depth : 4 bytes][workers_total : 4 bytes][workers_idle : 4 bytes][pmad_bytes_used : 8 bytes][pmad_bytes_total : 8 bytes]All integers are big-endian. The 28-byte body is a fixed shape so
dashboards can parse it without a schema.