kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

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