protocol.h (5734B)
1 #ifndef KIT_BUILD_PROTOCOL_H 2 #define KIT_BUILD_PROTOCOL_H 3 4 #include <kit/build_coord.h> /* KitBuildKV */ 5 #include <kit/core.h> 6 #include <stddef.h> 7 #include <stdint.h> 8 9 #include "build.h" 10 11 /* 12 * The recipe protocol: the command set and length-prefixed framing both sides 13 * speak over a KitBuildTransport. Pure encode/decode — no I/O, no transport 14 * (the coordinator's runner drives the server side, the client drives the 15 * recipe side). Every request both returns a value and logs a dependency; the 16 * contract that makes caching correct is that an input the recipe reads but 17 * does not request through one of these commands is invisible to the cache. 18 * 19 * Wire framing: each frame is a single command/response body, carried whole by 20 * the transport's read_frame/write_frame. Byte-stream transports encode that 21 * body as: 22 * 23 * u32le body_len 24 * u8[body_len] body 25 * 26 * `body_len` must be <= BUILD_FRAME_MAX. An in-process transport may pass the 27 * body directly, but must enforce the same cap. The body itself is the compact, 28 * byte-stable encoding below. All integers are little-endian fixed-width. All 29 * strings/slices are `u16le len` followed by `len` raw bytes, with no trailing 30 * NUL; decoders reject trailing bytes and command-specific length overflows. 31 * 32 * One response exception — GLOB. A glob match set can exceed one frame, so a 33 * glob response is a SEQUENCE of frames: zero or more match-batch frames, each a 34 * BUILD_RESP_OK frame carrying a run of paths, terminated by a final 35 * BUILD_RESP_GLOB_END frame. The server (runner) writes batches until the 36 * matches are exhausted then the END frame; the client/runner-decoder loops 37 * read_frame, invoking the per-match callback on each batch, until END. Every 38 * OTHER command is exactly one request frame and one response frame. 39 */ 40 41 /* Largest single frame the protocol will encode or accept. Bounds a config 42 * value, an overlay set, or ONE glob match-batch (large match sets span 43 * multiple batch frames — see the framing note above). */ 44 #define BUILD_FRAME_MAX 65536u 45 46 typedef enum BuildCmd { 47 BUILD_CMD_CONFIG_GET = 1, /* arg: key + optional default -> value | unset */ 48 BUILD_CMD_SOURCE = 2, /* arg: path -> blob-id + realpath */ 49 BUILD_CMD_GLOB = 3, /* arg: pattern -> sorted match paths */ 50 BUILD_CMD_NEED = 4, /* arg: target + k=v[] + argv[] -> tree-id + path (blocking) */ 51 BUILD_CMD_NEED_SUBMIT = 5, /* arg: target + k=v[] + argv[] -> token (non-blocking) */ 52 BUILD_CMD_NEED_AWAIT = 6, /* token -> tree-id + path */ 53 BUILD_CMD_FETCH = 7, /* arg: hex blob-id + url[] -> blob-id + path */ 54 } BuildCmd; 55 56 typedef enum BuildRespStatus { 57 BUILD_RESP_OK = 0, 58 BUILD_RESP_UNSET = 1, /* config-get on an unset key (not an error) */ 59 BUILD_RESP_ERROR = 2, /* the coordinator failed/refused the request */ 60 BUILD_RESP_GLOB_END = 3, /* terminates a multi-frame glob match stream */ 61 BUILD_RESP_ABSENT = 4, /* source on an absent path (not an error) */ 62 BUILD_RESP_DEFAULT = 5, /* config-get unset key returned supplied default */ 63 } BuildRespStatus; 64 65 /* A decoded request. `overrides` and `argv` (need / need-submit only) point into 66 * the frame buffer. `argv` is the dep's local argv; argc==0 (argv==NULL) means 67 * the empty argv. `token` carries the need-token for NEED_AWAIT. */ 68 typedef struct BuildReq { 69 uint8_t cmd; /* BuildCmd */ 70 KitSlice arg; /* key | path | pattern | target (unused for AWAIT) */ 71 const KitBuildKV* overrides; /* need/submit: propagated-config overlay */ 72 size_t noverrides; 73 const KitSlice* argv; /* config-get: optional default; need/submit: local argv; fetch: URL hints */ 74 size_t argc; /* config-get: 0/1; need/submit argv count; fetch URL count */ 75 uint64_t token; /* need-await: which submitted need to collect */ 76 } BuildReq; 77 78 /* A decoded response. The value union is selected by `cmd`. Slices/ids borrow 79 * the frame buffer until the next read. */ 80 typedef struct BuildResp { 81 uint8_t status; /* BuildRespStatus */ 82 uint16_t error_status; /* ERROR only: KitStatus value to return */ 83 uint8_t id[BUILD_HASH_LEN]; /* source: blob-id; need/await: output tree-id */ 84 KitSlice text; /* config value; source/need/await path; error */ 85 uint64_t token; /* need-submit: the coordinator-assigned token */ 86 /* glob results are streamed: the decoder invokes a callback per match rather 87 * than materializing a list in the struct. */ 88 } BuildResp; 89 90 /* Encode a request/response into `buf` (cap >= BUILD_FRAME_MAX); *n gets the 91 * byte count. The transport adds the length prefix. */ 92 int build_proto_encode_req(const BuildReq*, uint8_t* buf, size_t cap, 93 size_t* n); 94 int build_proto_encode_resp(const BuildReq* for_cmd, const BuildResp*, 95 uint8_t* buf, size_t cap, size_t* n); 96 97 /* Decode a request/response from a received frame. `cmd` tells the response 98 * decoder which value shape to expect. A NEED request's overlay pairs fill 99 * `ovr_storage` and its argv fills `argv_storage`. Malformed input returns 100 * BUILD_ERR. */ 101 int build_proto_decode_req(const uint8_t* buf, size_t len, BuildReq* out, 102 KitBuildKV* ovr_storage, size_t ovr_cap, 103 KitSlice* argv_storage, size_t argv_cap); 104 105 /* Reports one glob match while decoding a glob response. Return non-zero to 106 * stop. */ 107 typedef int (*BuildProtoGlobFn)(void* user, KitSlice path); 108 109 int build_proto_decode_resp(const uint8_t* buf, size_t len, uint8_t cmd, 110 BuildResp* out, BuildProtoGlobFn glob_cb, 111 void* glob_user); 112 113 #endif