ProtoCore v0.0.2
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
edge_mesh.h
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file edge_mesh.h
6 * @brief CDN edge-cache tier - mesh (sibling-cache) wire codec + async peer-query engine
7 * (PC_ENABLE_EDGE_MESH).
8 *
9 * Lets a fleet of edge nodes share one warm cache. On a full local miss a node queries its sibling peers
10 * (over a plaintext ConnProto::PROTO_MESH TCP link) with a content-addressed request and pulls a fresh copy
11 * from whichever peer has it, instead of re-fetching the origin. Pull (read-through) only: no push, no
12 * invalidation. The transfer carries the object plus its freshness/age, so a sibling-fresh object serves for
13 * its remaining lifetime with zero origin contact (RFC 9111 age propagation).
14 *
15 * This file is the pure, host-testable half: the request/response frame codec, the freshness-carrying entry
16 * frame (the shared ::edge_sd_serialize body plus a fixed timing trailer), and the async requester engine
17 * over the same EdgeFetchTransport seam the origin fetch uses (pc_client on device, a mock in host tests).
18 * The server glue (the peer table, the pre-origin query phase, and the PROTO_MESH serving listener) lives in
19 * edge_cache_proxy. Zero heap; fixed buffers.
20 *
21 * Wire format (little-endian, versioned; magic 'E','M'):
22 * request : 'E' 'M' | ver=1 | op=GET(1) | digest[32] | u16 key_len + key | u16 hdrs_len + req_hdrs
23 * response: 'E' 'M' | ver=1 | status (MISS=0 / HIT=1) | [ u16 entry_len + entry_frame ] (entry on HIT)
24 * entry : timing trailer (i64 date | i64 expires | u32 lifetime_s | u32 age_hdr | u32 current_age)
25 * followed by an ::edge_sd_serialize body (content: key/status/ct/validators/encoding/Vary/body)
26 *
27 * @author Douglas Quigg (dstroy0)
28 * @date 2026
29 */
30
31#ifndef PROTOCORE_EDGE_MESH_H
32#define PROTOCORE_EDGE_MESH_H
33
34#include "protocore_config.h"
35
36#if PC_ENABLE_EDGE_MESH
37
38#include "services/web/edge_cache/edge_cache.h" // EdgeEntry
39#include "services/web/edge_cache/edge_cache_sd.h" // PC_EDGE_SD_VALUE_MAX + the shared entry serializer
40#include "services/web/edge_cache/edge_fetch.h" // EdgeFetchTransport (reused transport seam)
41#include <stddef.h>
42#include <stdint.h>
43
44#define PC_EDGE_MESH_MAGIC0 ('E')
45#define PC_EDGE_MESH_MAGIC1 ('M')
46#define PC_EDGE_MESH_VERSION 1
47#define PC_EDGE_MESH_OP_GET 1 ///< the only request opcode: fetch by content address
48
49/** @brief The fixed timing trailer prepended to an entry frame (age propagation). */
50/** @brief Worst-case entry frame (trailer + a full ::edge_sd_serialize body). */
51/** @brief Worst-case request frame (the third field is a bounded request-header snapshot for Vary matching). */
52#define PC_EDGE_MESH_REQ_MAX (2 + 1 + 1 + 32 + 2 + PC_EDGE_KEY_MAX + 2 + PC_MESH_HDRS_MAX)
53/** @brief Worst-case response frame (header + entry on a HIT). */
54
55/** @brief Tri-state parse result for the length-delimited frames (partial reads accumulate to complete). */
56enum class EdgeMeshParse : int8_t
57{
58 MALFORMED = -1, ///< bad magic/version/opcode, or a field that cannot fit the destination
59 INCOMPLETE = 0, ///< a valid prefix so far - need more bytes
60 MISS = 1, ///< a complete response with no object
61 HIT = 2, ///< a complete request (outputs filled) / a complete response carrying an entry
62};
63
64// --- frame codec (pure) --------------------------------------------------------------------------
65
66/**
67 * @brief Build a GET request for @p digest / @p canon into @p out.
68 * @param req_hdrs the requester's header snapshot (name RS value US ...) so the peer can match Vary variants;
69 * pass "" for none. @return the frame length, or 0 if it would not fit @p cap.
70 */
71size_t edge_mesh_build_request(const uint8_t digest[32], const char *canon, const char *req_hdrs, uint8_t *out,
72 size_t cap);
73
74/**
75 * @brief Parse an accumulated request buffer.
76 * @return EdgeMeshParse::HIT with @p digest_out / @p canon_out / @p hdrs_out filled when a whole valid GET is
77 * present, INCOMPLETE if a valid prefix needs more bytes, or MALFORMED. (No MISS for a request.)
78 */
79EdgeMeshParse edge_mesh_parse_request(const uint8_t *buf, size_t len, uint8_t digest_out[32], char *canon_out,
80 size_t canon_cap, char *hdrs_out, size_t hdrs_cap);
81
82/**
83 * @brief Serialize @p e (content via the shared ::edge_sd_serialize) plus a timing trailer into @p out.
84 * @param current_age the sender's corrected age for @p e at send time (RFC 9111 sec 4.2.3), clamped >= 0.
85 * @return the entry-frame length, or 0 if it would not fit @p cap.
86 */
87size_t edge_mesh_serialize_entry(const EdgeEntry *e, long current_age, uint8_t *out, size_t cap);
88
89/**
90 * @brief Rehydrate @p e from an entry frame: content via ::edge_sd_deserialize, then the freshness fields.
91 *
92 * @p now_ms becomes the entry's insert time and the sender's transferred age becomes its initial age, so the
93 * receiver's ::edge_current_age keeps growing from where the sender left off (age propagation). The caller
94 * owns @p e's `used`/LRU linkage (typically an ::edge_store_alloc slot) and must re-check freshness + that the
95 * restored key matches the request (guards a wrong / colliding object). @return false on a short/corrupt frame.
96 */
97bool edge_mesh_deserialize_entry(const uint8_t *buf, size_t len, EdgeEntry *e, uint32_t now_ms);
98
99/** @brief Build a response (@p hit -> carry @p entry / @p entry_len; else a MISS). @return length or 0. */
100size_t edge_mesh_build_response(bool hit, const uint8_t *entry, size_t entry_len, uint8_t *out, size_t cap);
101
102/**
103 * @brief Parse an accumulated response buffer.
104 * @return HIT (with @p entry_off / @p entry_len pointing into @p buf), MISS, INCOMPLETE, or MALFORMED.
105 */
106EdgeMeshParse edge_mesh_parse_response(const uint8_t *buf, size_t len, size_t *entry_off, size_t *entry_len);
107
108// --- async requester engine (over the EdgeFetchTransport seam) ------------------------------------
109
110/** @brief Peer-query progress. */
111enum class EdgeMeshStatus : uint8_t
112{
113 PENDING, ///< still connecting / receiving
114 HIT, ///< a complete entry frame arrived (entry_off / entry_len valid)
115 MISS, ///< the peer does not have (a fresh copy of) the object
116 FAILED, ///< connect / send / timeout / closed-before-complete / malformed
117};
118
119/**
120 * @brief One in-flight peer query (zero-heap). The response accumulates into a caller-owned @c buf (>=
121 * PC_EDGE_MESH_RESP_MAX) supplied at begin - a fetch slot reuses its origin buffer, since the mesh and
122 * origin phases never run at once.
123 */
124struct EdgeMeshFetch
125{
126 EdgeMeshStatus st;
127 int cid;
128 uint32_t start_ms;
129 size_t got; ///< response bytes accumulated
130 size_t entry_off; ///< offset of the entry frame within buf (valid on HIT)
131 size_t entry_len; ///< length of the entry frame (valid on HIT)
132 uint8_t *buf; ///< caller-owned accumulation buffer
133 size_t cap; ///< its capacity (must be >= PC_EDGE_MESH_RESP_MAX)
134};
135
136/**
137 * @brief Dial @p host:@p port, send @p request, begin receiving into @p buf (@p cap >= PC_EDGE_MESH_RESP_MAX).
138 * Sets st to PENDING, or FAILED on error.
139 */
140void edge_mesh_fetch_begin(EdgeMeshFetch *m, const EdgeFetchTransport *t, const char *host, uint16_t port,
141 const uint8_t *request, size_t req_len, uint8_t *buf, size_t cap, uint32_t now_ms);
142
143/** @brief Drain available bytes and advance; honors PC_MESH_QUERY_MS. @return the current status. */
144EdgeMeshStatus edge_mesh_fetch_pump(EdgeMeshFetch *m, const EdgeFetchTransport *t, uint32_t now_ms);
145
146/** @brief Release the peer connection (idempotent). */
147void edge_mesh_fetch_end(EdgeMeshFetch *m, const EdgeFetchTransport *t);
148
149#endif // PC_ENABLE_EDGE_MESH
150
151#endif // PROTOCORE_EDGE_MESH_H
CDN edge-cache tier - pure engine (PC_ENABLE_EDGE_CACHE).
CDN edge-cache tier - L2 SD persistence (PC_ENABLE_EDGE_CACHE && PC_ENABLE_DBM).
CDN edge-cache tier - async origin-fetch engine (PC_ENABLE_EDGE_CACHE).
User-facing configuration for ProtoCore.