ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
buffer_sizing.h
Go to the documentation of this file.
1// ProtoCore v1.0.16 - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file buffer_sizing.h
6 * @brief Every capacity, buffer extent and pool borrow the enabled features are sized on.
7 *
8 * Reached from protocore_config.h, which is the single entry point and states the feature flags
9 * every block here is gated on. Including this file on its own would read those flags before they
10 * are settled.
11 *
12 * @author Douglas Quigg (dstroy0)
13 * @date 2026
14 */
15
16#ifndef PROTOCORE_BUFFER_SIZING_H
17#define PROTOCORE_BUFFER_SIZING_H
18
19#ifndef PROTOCORE_CONFIG_H
20#error "include protocore_config.h instead of this file - it is the entry point that states the feature flags"
21#endif
22
23// ---------------------------------------------------------------------------
24// Compile-time capacity constants (affect static array sizes)
25// ---------------------------------------------------------------------------
26
27/**
28 * @brief Maximum simultaneous TCP connections (fixed static pool; ~3.95 KB of internal RAM per slot).
29 *
30 * Default 8: a keep-alive/concurrency server needs headroom above its peak concurrent client count,
31 * because a connection closed by the keep-alive fairness cap (PROTOCORE_KEEPALIVE_MAX_REQUESTS) briefly
32 * holds its slot in CONN_CLOSING while it drains, and a reconnecting client needs a free slot mean-
33 * while - if concurrency equals the pool size there is none, and the overflow connection is refused
34 * (correct backpressure, but it caps clean throughput at concurrency == MAX_CONNS - 1). Set lower
35 * (e.g. -DMAX_CONNS=4, ~16 KB less RAM) on a RAM-constrained target, or higher (16/32) for a
36 * connection-heavy HTTP server; the event queue tracks it automatically (EVT_QUEUE_DEPTH below).
37 */
38#ifndef MAX_CONNS
39#define MAX_CONNS 8
40#endif
41
42/**
43 * @brief Disable Nagle's algorithm (set TCP_NODELAY) on every accepted connection.
44 *
45 * A request/response server is latency-first: the response is buffered whole (`tcp_write`) and pushed with a
46 * single `tcp_output`, so Nagle only ever delays the final sub-MSS segment of a multi-segment response (or a
47 * streamed chunk) - it waits for the peer's ACK of the prior segment, costing a ~40-200 ms delayed-ACK stall
48 * for no bandwidth benefit here. Disabling it lets that tail go out immediately. Set to 0 only if the device
49 * mainly streams bulk data and you prefer Nagle's segment coalescing over per-response latency.
50 */
51#ifndef PROTOCORE_TCP_NODELAY
52#define PROTOCORE_TCP_NODELAY 1
53#endif
54
55/**
56 * @brief Use the SWAR base64 decoder (classify 4 characters per 32-bit word). Default on.
57 *
58 * base64 decode is the one base64 path that touches a secret (the Basic-auth credential, RFC 7617, and the
59 * JWT / JWS segments, RFC 7515), so it must be **constant-time** - the character -> value mapping evaluated
60 * with branchless arithmetic masks, no data-dependent branch or table. Two constant-time implementations are
61 * available: a scalar one that classifies a character at a time, and this SWAR one that packs 4 characters
62 * into a word and classifies all four lanes in parallel with guard-bit range masks (every base64 character
63 * is < 0x80, so borrows never cross lanes). Both are byte-identical (`test_base64` runs against each) and
64 * both are constant-time; measured on the ESP32-S3, SWAR is ~1.9x faster than the scalar path and 5.36x
65 * faster than mbedTLS (882 vs 1639 vs 4728 cyc on a credential), at 0.00-cycle input-dependent variance.
66 * SWAR is the default; set to 0 for the smaller, simpler scalar decoder if code size matters more than the
67 * ~1.9x once-per-request decode win. Portable (any 32-bit target); encode is unaffected (always software).
68 */
69#ifndef PROTOCORE_BASE64_SWAR
70#define PROTOCORE_BASE64_SWAR 1
71#endif
72
73/** @brief Ring-buffer capacity in bytes per connection slot (feature floors enforced last, in
74 * derived_sizing.h - a value below what an enabled feature needs is raised there). */
75#ifndef RX_BUF_SIZE
76#define RX_BUF_SIZE 1024
77#endif
78
79/**
80 * @brief Compile-time default for connection idle timeout in milliseconds.
81 *
82 * The actual runtime value is stored in `WebServerConfig::conn_timeout_ms`,
83 * loaded by `Tcp.conn->init()` and read back with
84 * `Tcp.conn->timeout_ms()`.
85 */
86#ifndef CONN_TIMEOUT_MS
87#define CONN_TIMEOUT_MS 5000
88#endif
89
90/**
91 * @brief Request-header read deadline in milliseconds (slow-loris defense). Default 10 s; 0 disables.
92 *
93 * The idle timeout (CONN_TIMEOUT_MS) refreshes on every accepted byte, so a slow-loris that trickles one
94 * header byte just under the idle window holds a connection slot forever and, with a few connections, denies
95 * the whole fixed pool to legitimate clients (a connection-slot DoS - verified on HW). This is an ABSOLUTE
96 * deadline from the first byte of a request to the end of its HEADERS that a trickle cannot reset: a connection
97 * whose request headers are not complete within PROTOCORE_REQUEST_TIMEOUT_MS is answered 408 and closed, freeing the
98 * slot (the nginx client_header_timeout semantic). It is scoped to the header phase, so it never reaps a
99 * legitimate slow body: a large streaming upload (PARSE_BODY) is governed by the streaming handler + idle
100 * timer, not this deadline. It also does not touch WebSocket / SSE slots (long-lived by design). Lower it to
101 * tighten the window on a trusted LAN; 0 turns the defense off.
102 */
103#ifndef PROTOCORE_REQUEST_TIMEOUT_MS
104#define PROTOCORE_REQUEST_TIMEOUT_MS 10000
105#endif
106
107/**
108 * @brief Upper bound (ms) a slot may dwell in CONN_CLOSING after a graceful close
109 * before the idle sweep force-aborts it.
110 *
111 * On a graceful (local) close the slot stays in CONN_CLOSING - keeping its PCB and
112 * callbacks - until the peer ACKs the response (then it frees itself in the sent
113 * callback). If the peer never ACKs (dead/black-holed), this bound lets the
114 * timeout sweep reclaim the slot so the fixed pool cannot leak.
115 */
116#ifndef PROTOCORE_CLOSING_TIMEOUT_MS
117#define PROTOCORE_CLOSING_TIMEOUT_MS 2000
118#endif
119
120// ---------------------------------------------------------------------------
121// Worker model (server task concurrency)
122// ---------------------------------------------------------------------------
123//
124// The server pipeline (drain events -> dispatch -> send) runs in one or more
125// dedicated worker tasks instead of the user's loop(). Each worker owns a
126// disjoint partition of conn_pool slots (slot i -> worker i % PROTOCORE_WORKER_COUNT)
127// and its own pool slot, so no two workers ever touch the same slot:
128// shared-nothing, no hot-path locks, latency stays bounded (determinism
129// preserved) while cores run disjoint connections in parallel.
130//
131// PROTOCORE_WORKER_COUNT == 1 (default) is byte-for-byte the single-pipeline model:
132// one worker owns every slot, the existing single event queue. N > 1 is opt-in.
133// Each pool costs its arena size once per slot, and every worker gets a slot.
134
135/** @brief Number of server worker tasks (slots partitioned i % N). Default 1. */
136#ifndef PROTOCORE_WORKER_COUNT
137#define PROTOCORE_WORKER_COUNT 1
138#endif
139
140// The library's own worker slot, one past the server workers. Each independent task borrows from
141// its own slot, which is what makes a borrow lock-free; a library task prefers the ghost and falls
142// back to the rest. How many slots that implies is each pool's own business - see
143// PROTOCORE_REG_POOL_SLOTS and PROTOCORE_SEC_POOL_SLOTS in mmgr.
144#define PROTOCORE_GHOST_WORKER_SLOT (PROTOCORE_WORKER_COUNT)
145
146/**
147 * @brief Stack (bytes) for each server worker task (ESP32).
148 *
149 * Floor note: two heavy computations run on the worker.
150 * - RSA-2048 verification (OIDC / SSH host key / JWKS via the mbedTLS bignum
151 * modexp) uses ~7 KB (measured on a DevKitV1).
152 * - SSH modern crypto (curve25519-sha256 KEX + ssh-ed25519, software field
153 * arithmetic in radix-2^16) peaks at ~10.5 KB (measured on an ESP32-S3): the
154 * deep protocore_gf call chain plus the on-accelerator field inversion nests deeper
155 * than the RSA path.
156 * So the default adapts: 12 KB when SSH is enabled (curve/ed25519 can be
157 * negotiated), 8 KB otherwise. Do NOT lower it below the matching floor
158 * (::PROTOCORE_WORKER_STACK_CURVE_MIN for SSH, ::PROTOCORE_WORKER_STACK_RSA_MIN for
159 * OIDC) or the first handshake overflows the task stack - a build-time guard
160 * (bottom of this file) enforces the floor so a lowered stack is caught at
161 * compile time.
162 */
163// True when any feature that runs the SSH-class handshake is built. Derived, not configurable, and
164// defined unconditionally: a predicate that exists only inside one #ifndef is a trap for the next
165// person who tests it further down, where it would silently read 0.
166#if (defined(PROTOCORE_ENABLE_SSH) && PROTOCORE_ENABLE_SSH) || \
167 (defined(PROTOCORE_ENABLE_SSH_CLIENT) && PROTOCORE_ENABLE_SSH_CLIENT) || \
168 (defined(PROTOCORE_ENABLE_HTTP3) && PROTOCORE_ENABLE_HTTP3)
169#define PROTOCORE_SSH_ANY 1
170#else
171#define PROTOCORE_SSH_ANY 0
172#endif
173
174#ifndef PROTOCORE_WORKER_TASK_STACK
175// SSH (curve25519 + ssh-ed25519, server OR the reverse-SSH client) and HTTP/3 (the QUIC TLS-1.3
176// handshake reuses the same protocore_ed25519 signer for CertificateVerify) all peak at ~10.5 KB on the
177// worker task; the PQ/T hybrid (PROTOCORE_ENABLE_PQC_KEX) runs ML-KEM-768 on top, ~7 KB more. The default
178// tracks the matching floor so a hybrid build is provisioned, not starved; the guard at the bottom of
179// this file is the backstop when the stack is set by hand. (A flag set only in the config block below,
180// not via -D, is still undefined here and reads as 0 - the guard then catches any shortfall.)
181// sntrup761x25519-sha512 (on by default with the PQC hybrid, but a standalone -D can enable it without
182// ML-KEM) is the heavy case: the reverse-SSH CLIENT runs KeyGen+Decaps whose FO re-encrypt peaks ~32 KB,
183// the SERVER runs Encaps only (~22 KB). ML-KEM alone stays at 16 KB, so a build that explicitly drops
184// sntrup761 keeps the lighter floor. This default block only sees -D flags (the config defaults below
185// have not run yet), so sntrup761's default-tracks-PQC is assumed here and the guard at the bottom is
186// the backstop when it is toggled in the config block instead of via -D.
187#if (PROTOCORE_ENABLE_PQC_KEX || (defined(PROTOCORE_ENABLE_SSH_SNTRUP761) && PROTOCORE_ENABLE_SSH_SNTRUP761)) && \
188 PROTOCORE_SSH_ANY
189#if defined(PROTOCORE_ENABLE_SSH_SNTRUP761) && !PROTOCORE_ENABLE_SSH_SNTRUP761
190#define PROTOCORE_WORKER_TASK_STACK 16384
191#elif defined(PROTOCORE_ENABLE_SSH_CLIENT) && PROTOCORE_ENABLE_SSH_CLIENT
192#define PROTOCORE_WORKER_TASK_STACK 40960
193#else
194#define PROTOCORE_WORKER_TASK_STACK 32768
195#endif
196#elif PROTOCORE_SSH_ANY
197#define PROTOCORE_WORKER_TASK_STACK 12288
198#else
199#define PROTOCORE_WORKER_TASK_STACK 8192
200#endif
201#endif
202
203/**
204 * @brief Minimum worker-task stack (bytes) required once an RSA-2048 verifier is
205 * compiled in (OIDC / SSH).
206 *
207 * The mbedTLS bignum modexp alone consumes ~7 KB; 8 KB leaves room for the rest
208 * of the request call chain. Overridable only for an advanced build that marshals
209 * every RSA verify onto a dedicated larger-stack task (then the worker itself never
210 * runs one) - otherwise leave it at the default.
211 */
212#ifndef PROTOCORE_WORKER_STACK_RSA_MIN
213#define PROTOCORE_WORKER_STACK_RSA_MIN 8192
214#endif
215
216/**
217 * @brief Minimum worker-task stack (bytes) required once SSH is compiled in.
218 *
219 * SSH can negotiate curve25519-sha256 + ssh-ed25519, whose software field
220 * arithmetic peaks at ~10.5 KB of worker stack; 12 KB leaves ~1.8 KB of margin
221 * for the rest of the handshake call chain (comparable to the RSA floor's
222 * margin). Raise both this and ::PROTOCORE_WORKER_TASK_STACK together if you extend
223 * the handshake, or force RSA/DH only (ssh_kex_set_prefer_rsa) on a very tight
224 * build - but the server still advertises the modern suite, so a modern-only
225 * client would still exercise it.
226 */
227#ifndef PROTOCORE_WORKER_STACK_CURVE_MIN
228#define PROTOCORE_WORKER_STACK_CURVE_MIN 12288
229#endif
230
231/** @brief FreeRTOS priority for each server worker task (ESP32). */
232#ifndef PROTOCORE_WORKER_TASK_PRIORITY
233#define PROTOCORE_WORKER_TASK_PRIORITY 5
234#endif
235
236/**
237 * @brief Core that worker 0 pins to (ESP32). Worker k pins to (PROTOCORE_WORKER_CORE
238 * + k) % portNUM_PROCESSORS. Default 1 (APP_CPU), keeping Core 0 lean for the
239 * WiFi/lwIP stack and offloading the user's loop().
240 */
241#ifndef PROTOCORE_WORKER_CORE
242#define PROTOCORE_WORKER_CORE 1
243#endif
244
245/**
246 * @brief Depth of each worker's deferred-callback queue.
247 *
248 * App code on loop() or another task submits work to a slot's owning worker via
249 * Session.workers->defer() / protocore_defer_slot(); the worker runs it in its own single-thread
250 * context, so an async push (ws_send / protocore_sse_send from a timer) is race-free. Each
251 * worker has one queue of this depth (entries are a {fn, arg} pair, ~8 bytes).
252 */
253#ifndef PROTOCORE_DEFER_QUEUE_DEPTH
254#define PROTOCORE_DEFER_QUEUE_DEPTH 8
255#endif
256
257/**
258 * @brief Idle-sweep timeout, in FreeRTOS ticks, that a worker blocks between
259 * service iterations when no events are pending.
260 *
261 * The worker no longer free-runs a poll: it blocks on a task notification and a
262 * producer (a new connection event or a deferred submission) wakes it the moment
263 * work arrives, so event latency is independent of this value. The block still
264 * times out after this many ticks so the idle timeout sweep (check_timeouts) keeps
265 * reaping stale connections when nothing is in flight.
266 *
267 * Default 1 (1 tick at the Arduino 1 kHz FreeRTOS config) preserves the original
268 * idle cadence byte-for-byte. Because events now wake the worker immediately,
269 * raising it lowers idle wakeups (CPU/power on a battery device) WITHOUT the
270 * latency penalty the old poll-based knob carried - e.g. 100 -> a ~10 Hz idle
271 * sweep, still far below any connection timeout. The internal time base stays
272 * 1000 Hz regardless (see server/clock/clock.h).
273 */
274#ifndef PROTOCORE_WORKER_POLL_TICKS
275#define PROTOCORE_WORKER_POLL_TICKS 1
276#endif
277
278// ---------------------------------------------------------------------------
279// Preempting work queue (PROTOCORE_ENABLE_PREEMPT_QUEUE) - v5 real-time ingest
280// ---------------------------------------------------------------------------
281//
282// Fixed-capacity queues, each feeding one core-pinned processing task: a producer
283// posts a fixed-size item (from a task or an ISR) and the scheduler preempts straight
284// to the task. There are named lanes - one USER lane exposed to the app, and internal
285// DMA / forwarding / device-access lanes that run at a higher priority so internal
286// ingest always preempts user work. Queue storage is static (zero heap), so depth +
287// item size are compile-time; a task's stack is created only when its lane starts.
288// The no-lane protocore_pq_* API drives the USER lane. See preempt_queue.h.
289
290/** @brief Capacity of the preempting queue in items (static-allocated). */
291#ifndef PROTOCORE_PQ_DEPTH
292#define PROTOCORE_PQ_DEPTH 16
293#endif
294
295/** @brief Bytes per preempting-queue item (the posted item must fit). */
296#ifndef PROTOCORE_PQ_ITEM_SIZE
297#define PROTOCORE_PQ_ITEM_SIZE 32
298#endif
299
300/** @brief Stack (bytes) for each preempting-queue processing task (ESP32). */
301#ifndef PROTOCORE_PQ_STACK
302#define PROTOCORE_PQ_STACK 4096
303#endif
304
305/**
306 * @brief Base FreeRTOS priority for the internal preempting lanes (DMA / forwarding /
307 * device access). They run at this and just above, so internal ingest preempts
308 * the user lane; keep it above the user lane's priority and below the lwIP tcpip
309 * (18) / WiFi tasks so networking is never starved. See preempt_queue.h.
310 */
311#ifndef PROTOCORE_PQ_INTERNAL_PRIORITY
312#define PROTOCORE_PQ_INTERNAL_PRIORITY 8
313#endif
314
315// ---------------------------------------------------------------------------
316// DMA peripheral ingest / egress (PROTOCORE_ENABLE_DMA) - v5 hardware ingest
317// ---------------------------------------------------------------------------
318//
319// Move peripheral bytes (UART / I2C / SPI) between the wire and a static buffer
320// with the CPU free during the transfer; a DMA-complete event carries the bytes
321// to a user callback, which typically posts a descriptor into the preempting work
322// queue (PROTOCORE_ENABLE_PREEMPT_QUEUE) so the heavy processing runs off the ISR. RX
323// is double-buffered (ping-pong): the completed buffer is handed up while the DMA
324// engine fills the other. Storage is static (zero heap) - channel count and buffer
325// size are compile-time. See mmgr/dma.h.
326//
327// PROTOCORE_DMA_SIMULATE routes the transfers through an in-memory ingress/egress
328// simulator (feed bytes in, capture bytes out, optional TX->RX loopback) so the
329// whole pipeline is exercised with no physical loopback wire - on the host test
330// bench and, with the flag set, on the device itself. It is the shipped, tested
331// engine; a real silicon backend plugs into protocore_dma_hw_* when PROTOCORE_DMA_SIMULATE=0.
332
333/** @brief Number of DMA channels (static-allocated; each is one peripheral link). */
334#ifndef PROTOCORE_DMA_CHANNELS
335#define PROTOCORE_DMA_CHANNELS 2
336#endif
337
338/** @brief Bytes per DMA transfer buffer (RX is double-buffered at this size). */
339#ifndef PROTOCORE_DMA_BUF_SIZE
340#define PROTOCORE_DMA_BUF_SIZE 256
341#endif
342
343/**
344 * @brief HttpRoute DMA transfers through the ingress/egress simulator (default on).
345 * Set to 0 to drive real silicon via the protocore_dma_hw_* backend hooks.
346 */
347
348// ---------------------------------------------------------------------------
349// Trace capture: pre/post-trigger window assembler (PROTOCORE_ENABLE_TRACE_CAPTURE)
350// ---------------------------------------------------------------------------
351//
352// Sits downstream of PROTOCORE_ENABLE_DMA (or any other sample source) on a high-rate
353// acquisition front end: protocore_tc_feed() is called with every batch of arriving samples
354// and a continuously-running pre-trigger ring always holds the most recent samples;
355// protocore_tc_trigger() freezes that ring as the pre-trigger half of a window and the next
356// arriving samples fill the post-trigger half, so the emitted window straddles the
357// trigger instant like a benchtop oscilloscope's pretrigger/posttrigger split. One
358// capture in flight at a time, fail-closed. Storage is static (zero heap) - the sum of
359// the configured pretrigger + posttrigger sample counts must fit PROTOCORE_TC_MAX_WINDOW_SAMPLES.
360// See server/signaling/trace_capture.h.
361
362/** @brief Max samples a window may hold (pretrigger_samples + posttrigger_samples), static-allocated. */
363#ifndef PROTOCORE_TC_MAX_WINDOW_SAMPLES
364#define PROTOCORE_TC_MAX_WINDOW_SAMPLES 4096
365#endif
366
367// ---------------------------------------------------------------------------
368// AD9238 SPI configuration-port codec (PROTOCORE_ENABLE_AD9238)
369// ---------------------------------------------------------------------------
370//
371// A pure codec for the AD9238 dual ADC's low-speed SPI CONFIGURATION port (power-down,
372// output format, output test patterns, offset trim) - NOT its parallel sample-data bus,
373// which is out of an MCU's reach at this part's sample rates. See server/peripherals/ad9238/ad9238.h
374// for the hardware-verification caveat: the per-register bit fields are transcribed from
375// the datasheet, not yet confirmed against physical silicon.
376
377// ---------------------------------------------------------------------------
378// Interface forwarding plane (PROTOCORE_ENABLE_FORWARD) - v5 hardware ingest
379// ---------------------------------------------------------------------------
380//
381// A forwarding plane over the ingest pipeline: register interfaces (Wi-Fi STA / AP,
382// Ethernet, a peripheral bus, a radio), each with an egress send callback, then add
383// per-pair allow / deny rules with an optional rate cap. A frame arriving on one
384// interface (set Forward.src_if and Forward.frame.data / .len, then call Forward.ingress,
385// typically from a DMA-complete event posted onto the FORWARD lane) is forwarded to every
386// allowed destination, so the device bridges / routes between its interfaces instead of only
387// terminating traffic. Forward.n reports how many next hops the frame reached. Default-deny and
388// fail-closed (a full destination or an exceeded rate cap drops, never blocks). Static tables
389// (zero heap). See network_drivers/network/forward/forward.h.
390
391/** @brief Interfaces layer 1 can carry: wifi station and softAP, ethernet, a bridged bus, a radio.
392 * The registry is L1's because an interface is a physical thing; the forwarding plane reads it. */
393#ifndef PROTOCORE_PHY_MAX_IFACES
394#define PROTOCORE_PHY_MAX_IFACES 4
395#endif
396
397/** @brief Max forwarding rules (src -> dst allow/deny + rate cap; static-allocated). */
398#ifndef PROTOCORE_FWD_MAX_RULES
399#define PROTOCORE_FWD_MAX_RULES 8
400#endif
401
402/** @brief Max ingress access-control entries (byte-pattern permit/deny; static). */
403#ifndef PROTOCORE_FWD_MAX_ACL
404#define PROTOCORE_FWD_MAX_ACL 8
405#endif
406
407/** @brief Bytes an ACL entry can match (its pattern / mask length). */
408#ifndef PROTOCORE_FWD_ACL_PATLEN
409#define PROTOCORE_FWD_ACL_PATLEN 4
410#endif
411
412/** @brief Max policy routes (byte-pattern -> egress interface; static). Policy routes take
413 * precedence over the src->dst rules, so tagged traffic leaves a chosen interface. */
414#ifndef PROTOCORE_FWD_MAX_ROUTES
415#define PROTOCORE_FWD_MAX_ROUTES 8
416#endif
417
418/** @brief Build-time toggle for the forwarding-path inspection hook (default off, for cost +
419 * privacy). When 1, Forward.inspect.fn + Forward.set_inspector install a runtime callback
420 * that observes / filters each ingress frame before it is forwarded; when 0 the hook is
421 * compiled out entirely (no call site). Runtime toggle: register or clear (null) the
422 * inspector. */
423#ifndef PROTOCORE_FWD_INSPECT
424#define PROTOCORE_FWD_INSPECT 0
425#endif
426
427// ---------------------------------------------------------------------------
428// Radio / wireless gateway (PROTOCORE_ENABLE_GATEWAY) - v5 southbound-to-northbound bridge
429// ---------------------------------------------------------------------------
430//
431// The generic gateway pattern: a southbound radio (LoRa / nRF24 / Zigbee / ... reached
432// over SPI / I2C / UART) is a "port"; a frame it receives (data-ready ISR -> DMA -> the
433// FORWARD lane -> a per-radio codec) is handed to protocore_gateway_uplink(), which envelopes it with
434// its source node address / port / RSSI and publishes it northbound through the uplink
435// callback (wire it to MQTT / HTTP / WebSocket / UDP). A northbound command goes the other
436// way through protocore_gateway_downlink() to the port's transmit callback. The radio TX + the
437// northbound publish are callbacks (the seam a real radio driver / protocol binding plugs
438// into), so the bridge is host- and device-testable with no radio. Static tables (zero
439// heap). See server/net/gateway/gateway.h.
440
441/** @brief Max southbound gateway ports (radios / buses; static-allocated). */
442#ifndef PROTOCORE_GW_MAX_PORTS
443#define PROTOCORE_GW_MAX_PORTS 4
444#endif
445
446/** @brief Default northbound topic prefix (overridable at runtime via protocore_gateway_set_topic_prefix). */
447#ifndef PROTOCORE_GW_DEFAULT_PREFIX
448#define PROTOCORE_GW_DEFAULT_PREFIX "gw"
449#endif
450
451// ---------------------------------------------------------------------------
452// LoRa radio (PROTOCORE_ENABLE_LORA) - Semtech SX127x / RFM95-96 codec + driver
453// ---------------------------------------------------------------------------
454//
455// A per-radio codec + driver that plugs into the gateway (PROTOCORE_ENABLE_GATEWAY): the
456// RadioHead-compatible 4-byte frame header (to / from / id / flags) codec, and an SX127x
457// register driver over a caller-supplied register-access bus (so the SPI + chip-select
458// wiring is the integration's, and the register protocol is host-testable with a mock
459// bus). Bridge received frames northbound with protocore_gateway_uplink(); the actual RF link needs
460// the module to verify. See services/radio/lora/lora.h.
461
462/** @brief Max LoRa payload bytes (SX127x FIFO is 256; RadioHead uses 251 + 4 header). */
463#ifndef PROTOCORE_LORA_MAX_PAYLOAD
464#define PROTOCORE_LORA_MAX_PAYLOAD 251
465#endif
466
467// ---------------------------------------------------------------------------
468// nRF24 radio (PROTOCORE_ENABLE_NRF24) - Nordic nRF24L01+ 2.4 GHz driver
469// ---------------------------------------------------------------------------
470//
471// A radio driver that plugs into the gateway (PROTOCORE_ENABLE_GATEWAY). The nRF24L01+ speaks
472// an SPI command protocol (not plain register r/w) and needs a separate CE pin, so the
473// driver runs over a caller-supplied SPI transfer + CE bus (nrf_bus). Its hardware pipe
474// addressing means the "source address" of a received frame is the pipe number - no
475// in-payload header, so there is no separate codec. Bridge received payloads northbound
476// with protocore_gateway_uplink(port, pipe, ...); the RF link needs the module to verify.
477// See services/radio/nrf24/nrf24.h.
478
479/** @brief nRF24 fixed payload width in bytes (1..32; the chip's static payload size). */
480#ifndef PROTOCORE_NRF24_PAYLOAD
481#define PROTOCORE_NRF24_PAYLOAD 32
482#endif
483
484// ---------------------------------------------------------------------------
485// EnOcean ESP3 (PROTOCORE_ENABLE_ENOCEAN) - energy-harvesting 868 MHz serial codec
486// ---------------------------------------------------------------------------
487//
488// A UART telegram codec for EnOcean's ESP3 (EnOcean Serial Protocol 3), the framing used
489// by USB/serial EnOcean gateways (TCM 310 / USB 300): sync 0x55, a 4-byte header (data
490// length, optional length, packet type) protected by CRC8, then data + optional data
491// protected by a second CRC8. protocore_esp3_parse() frames one telegram out of a byte stream and
492// verifies both CRCs; protocore_esp3_build() assembles one. Pure (no UART code - you feed it the
493// serial bytes), so it is fully host-testable. See services/radio/enocean/enocean.h.
494
495/** @brief Reject an ESP3 telegram whose declared data length exceeds this (framing sanity). */
496#ifndef PROTOCORE_ENOCEAN_MAX_DATA
497#define PROTOCORE_ENOCEAN_MAX_DATA 512
498#endif
499
500// ---------------------------------------------------------------------------
501// PN532 NFC (PROTOCORE_ENABLE_PN532) - NXP PN532 NFC/RFID controller frame codec
502// ---------------------------------------------------------------------------
503//
504// The NXP PN532 (I2C / SPI / HSU) command-frame protocol - a tag read/write bridged to an
505// HTTP / MQTT event. The chip is driven by "normal information frames" (00 00 FF | LEN |
506// LCS | TFI | PData | DCS | 00) with a length checksum and a data checksum, plus a 6-byte
507// ACK frame. protocore_pn532_build_frame() / protocore_pn532_parse_frame() assemble and verify those frames
508// (the per-command PData is the application's), and protocore_pn532_is_ack() detects the ACK. Pure -
509// you carry the frame bytes over your I2C / SPI / UART - so it is fully host-testable.
510// See server/peripherals/pn532/pn532.h.
511
512/** @brief Reject a PN532 normal frame whose declared length exceeds this (framing sanity). */
513#ifndef PROTOCORE_PN532_MAX_DATA
514#define PROTOCORE_PN532_MAX_DATA 254
515#endif
516
517// ---------------------------------------------------------------------------
518// Sigfox (PROTOCORE_ENABLE_SIGFOX) - Wisol / Murata Sigfox modem AT-command codec
519// ---------------------------------------------------------------------------
520//
521// Tiny low-power uplinks over the Sigfox 0G network. A Wisol / Murata Sigfox modem is
522// driven by AT commands over UART: protocore_sigfox_build_uplink() formats an `AT$SF=<hex>` frame
523// for a <= 12-byte payload, and protocore_sigfox_parse_response() classifies the modem's reply
524// (OK / ERROR / still pending). Pure text-command codec - you carry it over your UART - so
525// it is fully host-testable. See services/radio/sigfox/sigfox.h.
526
527/** @brief Maximum Sigfox uplink payload (the network caps a message at 12 bytes). */
528#ifndef PROTOCORE_SIGFOX_MAX_PAYLOAD
529#define PROTOCORE_SIGFOX_MAX_PAYLOAD 12
530#endif
531
532// ---------------------------------------------------------------------------
533// Z-Wave (PROTOCORE_ENABLE_ZWAVE) - Silicon Labs Z-Wave Serial API frame codec
534// ---------------------------------------------------------------------------
535//
536// The host-side Serial API of a Silicon Labs 500 / 700-series Z-Wave controller over UART:
537// a Z-Wave mesh bridged to the web. Data frames are SOF (0x01) | LEN | Type | Command |
538// Data | Checksum, where the checksum is 0xFF XOR-folded over LEN..last-data; single-byte
539// ACK (0x06) / NAK (0x15) / CAN (0x18) frames flow-control them. protocore_zwave_build_frame() /
540// protocore_zwave_parse_frame() assemble and verify a data frame; the per-command payload is the
541// application's. Pure - you carry the bytes over your UART - so it is fully host-testable.
542// See services/radio/zwave/zwave.h.
543
544/** @brief Reject a Z-Wave frame whose declared length exceeds this data cap (sanity). */
545#ifndef PROTOCORE_ZWAVE_MAX_DATA
546#define PROTOCORE_ZWAVE_MAX_DATA 64
547#endif
548
549// ---------------------------------------------------------------------------
550// Zigbee (PROTOCORE_ENABLE_ZIGBEE) - Silicon Labs EZSP / ASH serial framing codec
551// ---------------------------------------------------------------------------
552//
553// The ASH (Asynchronous Serial Host) data-link layer that carries EZSP frames to a Silicon
554// Labs EmberZNet NCP over UART - a Zigbee network bridged to the web. ASH delimits frames
555// with a Flag byte (0x7E), byte-stuffs the reserved control bytes, and protects each frame
556// with a CRC-16/CCITT. protocore_ash_frame_encode() wraps a control byte + payload into a stuffed,
557// CRC'd frame; protocore_ash_frame_decode() unstuffs + verifies one. The EZSP command payload the
558// frame carries (version, stack status, an incoming APS message, ...) is the application's.
559// protocore_ash_frame_decode() removes the stuffing and verifies the CRC. Pure - you carry the bytes
560// over your UART - so it is fully host-testable. See services/radio/zigbee/zigbee.h.
561
562/** @brief Max ASH payload bytes (an EZSP frame; the ASH data field caps near 128). */
563#ifndef PROTOCORE_ZIGBEE_MAX_DATA
564#define PROTOCORE_ZIGBEE_MAX_DATA 128
565#endif
566
567// ---------------------------------------------------------------------------
568// Thread (PROTOCORE_ENABLE_THREAD) - OpenThread spinel over HDLC-lite framing codec
569// ---------------------------------------------------------------------------
570//
571// The HDLC-lite framing that carries spinel frames to an OpenThread radio co-processor
572// (RCP: an nRF52840 / EFR32) over UART - an 802.15.4 / Thread mesh bridged to IP / the web.
573// Each spinel frame is wrapped by HDLC-lite: an FCS (CRC-16/X-25) is appended, the reserved
574// bytes are byte-stuffed, and a Flag byte (0x7E) terminates it. protocore_spinel_frame_encode() /
575// protocore_spinel_frame_decode() do the framing + FCS; the spinel command inside (a property
576// get/set/insert, a stream frame) is the application's. Pure - you carry the bytes over your
577// UART - so it is fully host-testable. See services/radio/thread/thread.h.
578
579/** @brief Max spinel payload bytes carried in one HDLC-lite frame. */
580#ifndef PROTOCORE_THREAD_MAX_DATA
581#define PROTOCORE_THREAD_MAX_DATA 256
582#endif
583
584// ---------------------------------------------------------------------------
585// Wired Ethernet PHY (PROTOCORE_ENABLE_ETHERNET) - run the server over an RMII PHY
586// ---------------------------------------------------------------------------
587//
588// Bring up a wired Ethernet link (an RMII PHY: LAN8720 / TLK110 / RTL8201 / DP83848) so the
589// server runs over Ethernet instead of (or alongside) Wi-Fi. Physical.eth_init is a thin
590// wrapper over the Arduino ETH library; the PHY pins / type / clock come from the standard
591// ETH_PHY_* build flags for your board (see example Ethernet). The egress reporting
592// (Physical.egress -> Physical.if_kind == PROTOCORE_IF_ETH) and the per-route interface classifier
593// already handle a wired route, so once the link has an IP the server accepts on it with no other
594// change. Default off (zero cost / the ETH library is not linked). ESP32-only.
595
596// W5500 SPI Ethernet (arduino-esp32 3.x only). Set PROTOCORE_ETH_W5500=1 to select the SPI PHY over the RMII
597// default; the pins below are the ESP32-S3-DevKitC wiring (HSPI / SPI3). The 2.x ETH library has no W5500,
598// so Physical.eth_init falls back to the RMII ETH.begin() when the core is older.
599#ifndef PROTOCORE_ETH_W5500
600#define PROTOCORE_ETH_W5500 0
601#endif
602#ifndef PROTOCORE_ETH_W5500_CS
603#define PROTOCORE_ETH_W5500_CS 7 ///< chip select
604#endif
605#ifndef PROTOCORE_ETH_W5500_RST
606#define PROTOCORE_ETH_W5500_RST 6 ///< reset
607#endif
608#ifndef PROTOCORE_ETH_W5500_INT
609#define PROTOCORE_ETH_W5500_INT 5 ///< interrupt
610#endif
611#ifndef PROTOCORE_ETH_W5500_SCK
612#define PROTOCORE_ETH_W5500_SCK 12 ///< HSPI clock (S3-DevKitC default)
613#endif
614#ifndef PROTOCORE_ETH_W5500_MISO
615#define PROTOCORE_ETH_W5500_MISO 13 ///< HSPI MISO (S3-DevKitC default)
616#endif
617#ifndef PROTOCORE_ETH_W5500_MOSI
618#define PROTOCORE_ETH_W5500_MOSI 11 ///< HSPI MOSI (S3-DevKitC default)
619#endif
620// W5500 SPI clock in MHz. The W5500 datasheet allows up to 33.3 MHz; 20 is the arduino-esp32 default and
621// a safe value for breadboard jumper wiring. Higher clocks raise throughput (the link is SPI-bound, not
622// PHY-bound) but need clean, short wiring - marginal signal integrity at high MHz corrupts frames.
623#ifndef PROTOCORE_ETH_W5500_SPI_MHZ
624#define PROTOCORE_ETH_W5500_SPI_MHZ 20 ///< W5500 SPI clock (MHz); raise for throughput on clean wiring
625#endif
626
627// Feature / service / codec tuning knobs are consolidated at the END of this file,
628// under "Feature tuning knobs (grouped and gated by feature)" - placed there so every
629// PROTOCORE_ENABLE_* flag is already resolved and each group can gate on its own feature.
630
631/** @brief Maximum HTTP headers stored per request. */
632#ifndef MAX_HEADERS
633#define MAX_HEADERS 8
634#endif
635
636/** @brief Maximum URL path length (including leading `/`). */
637#ifndef MAX_PATH_LEN
638#define MAX_PATH_LEN 64
639#endif
640
641/**
642 * @brief Maximum header field-name length (e.g. `"Content-Type"`).
643 *
644 * Must accommodate the longest header name the app needs to read by key.
645 * Standard names reach 30+ chars (`Sec-WebSocket-Extensions` = 24,
646 * `Access-Control-Request-Headers` = 30), so the default leaves margin; an
647 * over-long key is truncated (not rejected) by the parser.
648 */
649#ifndef MAX_KEY_LEN
650#define MAX_KEY_LEN 32
651#endif
652
653/** @brief Maximum header field-value length. */
654#ifndef MAX_VAL_LEN
655#define MAX_VAL_LEN 48
656#endif
657
658/** @brief Maximum raw query-string length (everything after `?`). */
659#ifndef MAX_QUERY_LEN
660#define MAX_QUERY_LEN 128
661#endif
662
663/** @brief Maximum number of parsed query-string parameters. */
664#ifndef MAX_QUERY_PARAMS
665#define MAX_QUERY_PARAMS 8
666#endif
667
668/** @brief Maximum number of `:name` path parameters captured per route match. */
669#ifndef MAX_PATH_PARAMS
670#define MAX_PATH_PARAMS 4
671#endif
672
673/**
674 * @brief Capacity for the full `Authorization` header value (Digest auth).
675 *
676 * A Digest `Authorization` header (username, realm, nonce, uri, response,
677 * qop, nc, cnonce) is far longer than MAX_VAL_LEN, so when PROTOCORE_ENABLE_AUTH
678 * is set the parser captures it whole into a dedicated per-request buffer.
679 */
680#ifndef DIGEST_AUTH_HDR_MAX
681#define DIGEST_AUTH_HDR_MAX 384
682#endif
683
684/**
685 * @brief Lifetime of a Digest `nonce`, in milliseconds (default 5 minutes).
686 *
687 * The server mints a stateless, keyed, timestamped nonce (RFC 7616 3.3) rather
688 * than a fixed one: each challenge carries the issue time plus a MAC over the
689 * server secret, so no per-nonce table is needed. A client `Authorization` whose
690 * nonce is older than this window is treated as @c stale - the credentials are
691 * re-checked and, if correct, the server reissues a fresh challenge with
692 * `stale=true` so the client retries transparently (no re-prompt). This bounds
693 * how long a captured Digest response can be replayed without any server-side
694 * state, which the shared-nothing worker model could not hold safely.
695 */
696#ifndef PROTOCORE_DIGEST_NONCE_LIFETIME_MS
697#define PROTOCORE_DIGEST_NONCE_LIFETIME_MS (5u * 60u * 1000u)
698#endif
699
700/** @brief Maximum query-parameter key length. */
701#ifndef QUERY_KEY_LEN
702#define QUERY_KEY_LEN 24
703#endif
704
705/** @brief Maximum query-parameter value length. */
706#ifndef QUERY_VAL_LEN
707#define QUERY_VAL_LEN 48
708#endif
709
710/**
711 * @brief Maximum request body bytes stored in `HttpReq::body`.
712 *
713 * Bodies larger than this trigger a 413 Payload Too Large response -
714 * the parser detects the overflow via `Content-Length` before any body
715 * bytes arrive, so no data is read or stored for oversized requests.
716 */
717#ifndef BODY_BUF_SIZE
718#define BODY_BUF_SIZE 256
719#endif
720
721/** @brief Maximum simultaneously registered routes. */
722#ifndef MAX_ROUTES
723#define MAX_ROUTES 16
724#endif
725
726/**
727 * @brief Maximum globally-registered middleware functions.
728 *
729 * The middleware chain is a fixed array of function pointers run in
730 * registration order before a request reaches its route handler (see
731 * use()). Costs MAX_MIDDLEWARE pointers of BSS; an empty chain
732 * adds no per-request work.
733 */
734#ifndef MAX_MIDDLEWARE
735#define MAX_MIDDLEWARE 4
736#endif
737
738/**
739 * @brief Per-chunk staging buffer for send_chunked()'s ChunkSource (max bytes a
740 * source produces per call, hence the largest single chunk on the wire).
741 *
742 * Allocated on the worker stack only while a chunk is being framed - no persistent
743 * RAM cost. The pump asks the source for at most this many bytes (or fewer when the
744 * send window is smaller), so it bounds the chunk size, not the total body.
745 *
746 * Sized to one TCP segment (~MSS): the pump frames + sends each chunk in a single
747 * tcpip_thread round-trip (~23 us on-device), so a bigger chunk = fewer round-trips per
748 * byte. 1440 keeps the framed chunk within one segment; raise it (up to the send window)
749 * to cut the round-trip count further on a fast transport (e.g. Ethernet), at more stack.
750 */
751#ifndef CHUNK_BUF_SIZE
752#define CHUNK_BUF_SIZE 1440
753#endif
754
755/**
756 * @brief Maximum object/array nesting depth for the JsonWriter (see json.h).
757 *
758 * Bounds the writer's per-level comma-tracking stack (one bool per level);
759 * begin_object()/begin_array() beyond this fail the writer instead of
760 * overflowing. No heap; ~JSON_MAX_DEPTH bytes of stack inside the writer object.
761 */
762#ifndef JSON_MAX_DEPTH
763#define JSON_MAX_DEPTH 8
764#endif
765
766/**
767 * @brief Step budget for the regex route matcher (see on_regex()).
768 *
769 * The matcher is a bounded backtracker: it counts match steps and fails closed
770 * (no match) once this budget is exhausted, so a pathological pattern can never
771 * backtrack unboundedly. Keeps regex routing deterministic. Routing patterns hit
772 * only a handful of steps; the default leaves wide margin.
773 */
774#ifndef RE_MAX_STEPS
775#define RE_MAX_STEPS 2000
776#endif
777
778// ---------------------------------------------------------------------------
779// WebSocket sizing constants
780// ---------------------------------------------------------------------------
781
782/**
783 * @brief Maximum simultaneous WebSocket connections.
784 *
785 * Each connection occupies one TCP slot from MAX_CONNS and one entry in
786 * ws_pool[]. MAX_WS_CONNS + MAX_SSE_CONNS must not exceed MAX_CONNS.
787 */
788#ifndef MAX_WS_CONNS
789#define MAX_WS_CONNS 2
790#endif
791
792/**
793 * @brief Maximum WebSocket frame payload in bytes.
794 *
795 * Frames larger than this are rejected with Close code 1009 (Message Too Big).
796 * Fragmented messages are not supported; each message must fit in one frame.
797 */
798#ifndef WS_FRAME_SIZE
799#define WS_FRAME_SIZE 512
800#endif
801
802/**
803 * @brief Largest outbound payload permessage-deflate will compress, in bytes.
804 *
805 * The compressor borrows `len + len/8 + 16` from the scratch arena, so an unbounded outbound length
806 * would leave the arena with no worst case - `Ws.frame.len` is a `uint16_t`, and neither
807 * WS_FRAME_SIZE (which bounds the *inbound* reassembled message) nor PROTOCORE_WS_FRAG_SIZE (off by
808 * default, and a runtime setter besides) constrains it. This is that bound, and it is what makes
809 * PROTOCORE_PLAINTEXT_WORK_WS_SEND a compile-time constant.
810 *
811 * A larger message is still sent, uncompressed, as the per-message RSV1 flag makes legal - the same
812 * outcome as before this was declared, except chosen rather than reached by an allocation failure.
813 * Raising it costs arena: the term grows by roughly 1.125x the increase.
814 */
815#ifndef PROTOCORE_WS_DEFLATE_MAX
816#define PROTOCORE_WS_DEFLATE_MAX WS_FRAME_SIZE
817#endif
818
819// ---------------------------------------------------------------------------
820// Server-Sent Events sizing constants
821// ---------------------------------------------------------------------------
822
823/**
824 * @brief Maximum simultaneous SSE connections.
825 *
826 * Each connection occupies one TCP slot from MAX_CONNS and one entry in
827 * protocore_sse_pool[]. MAX_WS_CONNS + MAX_SSE_CONNS must not exceed MAX_CONNS.
828 */
829#ifndef MAX_SSE_CONNS
830#define MAX_SSE_CONNS 2
831#endif
832
833/**
834 * @brief Output buffer size in bytes for a single SSE event.
835 *
836 * An event larger than this is silently truncated. The buffer holds the
837 * formatted `data: ...\n\n` line before it is handed to tcp_write().
838 */
839#ifndef SSE_BUF_SIZE
840#define SSE_BUF_SIZE 256
841#endif
842
843// ---------------------------------------------------------------------------
844// Static file serving sizing constants
845// ---------------------------------------------------------------------------
846
847/**
848 * @brief Bytes read from the filesystem and passed to tcp_write() per loop().
849 *
850 * Each read+send is one tcpip_thread round-trip (~23 us on-device), so a larger chunk =
851 * fewer round-trips per byte (better throughput on a fast transport), at more peak stack.
852 * Must be <= RX_BUF_SIZE to avoid stalling the TCP send window; 1024 tracks the default
853 * RX_BUF_SIZE. Lower it (e.g. -DFILE_CHUNK_SIZE=512) on a stack-constrained target.
854 */
855#ifndef FILE_CHUNK_SIZE
856#define FILE_CHUNK_SIZE 1024
857#endif
858
859// ---------------------------------------------------------------------------
860// Basic Auth sizing constants
861// ---------------------------------------------------------------------------
862
863/**
864 * @brief Maximum username or password length for HTTP Basic Authentication.
865 *
866 * Both username and password must fit in this many bytes including the
867 * null terminator. Longer credentials are silently rejected with 401.
868 */
869#ifndef MAX_AUTH_LEN
870#define MAX_AUTH_LEN 32
871#endif
872
873// ---------------------------------------------------------------------------
874// MultipartBody form-data sizing constants
875// ---------------------------------------------------------------------------
876
877/**
878 * @brief Maximum simultaneously parsed multipart parts per request.
879 *
880 * Parts beyond this limit are silently ignored. A typical upload form
881 * has 1-4 fields; increase this for forms with more.
882 */
883#ifndef MAX_MULTIPART_PARTS
884#define MAX_MULTIPART_PARTS 4
885#endif
886
887/**
888 * @brief Maximum MIME boundary length (RFC 2046 allows up to 70 characters).
889 */
890#ifndef MAX_BOUNDARY_LEN
891#define MAX_BOUNDARY_LEN 72
892#endif
893
894// ---------------------------------------------------------------------------
895// Event queue depth
896// ---------------------------------------------------------------------------
897
898/**
899 * @brief Depth of the FreeRTOS event queue shared between lwIP callbacks and
900 * the main-loop task.
901 *
902 * Each slot holds one TcpEvt (8 bytes). The queue is the only heap
903 * allocation the library makes at begin() time:
904 *
905 * heap = sizeof(StaticQueue_t) + EVT_QUEUE_DEPTH * sizeof(TcpEvt)
906 *
907 * Must be large enough to absorb a burst of MAX_CONNS * 4 events without
908 * blocking the lwIP thread, so it tracks MAX_CONNS automatically (a raised
909 * MAX_CONNS never trips the EVT_QUEUE_DEPTH >= MAX_CONNS * 4 guard below).
910 */
911#ifndef EVT_QUEUE_DEPTH
912#define EVT_QUEUE_DEPTH (MAX_CONNS * 4)
913#endif
914
915// ---------------------------------------------------------------------------
916// Internal response buffer sizing constants
917// ---------------------------------------------------------------------------
918
919/**
920 * @brief Stack buffer for HTTP response header lines in send() / send_empty() /
921 * send_unauth() / serve_file().
922 *
923 * Must be large enough to hold the status line, Content-Type, Content-Length,
924 * Connection, and any CORS headers. The CORS block alone can reach
925 * CORS_HDR_BUF_SIZE bytes, so this value should be at least
926 * CORS_HDR_BUF_SIZE + 96.
927 */
928#ifndef RESP_HDR_BUF_SIZE
929#define RESP_HDR_BUF_SIZE 768
930#endif
931
932/**
933 * @brief Per-connection buffer for app-supplied custom response headers and
934 * cookies.
935 *
936 * Filled by proto_add_response_header() / set_cookie() and injected into send() /
937 * send_empty() / redirect() the same way the CORS block is. RESP_HDR_BUF_SIZE
938 * must be large enough to hold the status line plus the CORS block plus this
939 * block (see the assert below).
940 */
941#ifndef EXTRA_HDR_BUF_SIZE
942#define EXTRA_HDR_BUF_SIZE 256
943#endif
944
945/**
946 * @brief Stack buffer for the HTTP 101 Switching Protocols response sent during
947 * the WebSocket handshake.
948 *
949 * Must hold: status line + Upgrade + Connection + Sec-WebSocket-Accept (28
950 * base64 chars) + CRLF pairs. Minimum is ~120 bytes; default leaves margin.
951 */
952#ifndef WS_HDR_BUF_SIZE
953#define WS_HDR_BUF_SIZE 256
954#endif
955
956/**
957 * @brief Size of the pre-built CORS header block stored in PC.
958 *
959 * Built once by set_cors() and injected into every response. Must hold
960 * Access-Control-Allow-Origin, Access-Control-Allow-Methods, and
961 * Access-Control-Allow-Headers lines for the configured origin.
962 */
963#ifndef CORS_HDR_BUF_SIZE
964#define CORS_HDR_BUF_SIZE 192
965#endif
966
967/**
968 * @brief Size of the optional Cache-Control header line stored in PC.
969 *
970 * Built once by set_cache_control() and injected into static-file responses
971 * (serve_file / serve_static) beside the ETag. Holds "Cache-Control: <value>\r\n".
972 */
973#ifndef CACHE_CONTROL_BUF_SIZE
974#define CACHE_CONTROL_BUF_SIZE 64
975#endif
976
977/**
978 * @brief WebSocket outbound fragmentation size (RFC 6455 sec 5.4), in payload bytes. 0 = off.
979 *
980 * When >0, an outbound data message (text/binary) longer than this many payload bytes is split into
981 * that-sized WebSocket frames - the first carrying the opcode (and the RFC 7692 RSV1 bit if the message
982 * is compressed), the rest CONTINUATION, the last with FIN - instead of one large frame. Sizing it near
983 * the TCP MSS (e.g. 1400) keeps each frame within whole segments (MTU-aligned) and lets a peer with a
984 * bounded per-frame reassembly buffer receive an arbitrarily long message. The runtime override is
985 * Ws.set_frag_size. Compression applies to the whole message first, then the compressed bytes are
986 * split. Default 0 (one frame per message, unchanged).
987 */
988#ifndef PROTOCORE_WS_FRAG_SIZE
989#define PROTOCORE_WS_FRAG_SIZE 0
990#endif
991
992/** @brief Buffer (BSS) for a WebDAV 207 Multi-Status response, in bytes (see PROTOCORE_ENABLE_WEBDAV). */
993#ifndef PROTOCORE_WEBDAV_BUF_SIZE
994#define PROTOCORE_WEBDAV_BUF_SIZE 2048
995#endif
996
997/** @brief Maximum children listed in a WebDAV Depth-1 PROPFIND (bounds the response). */
998#ifndef PROTOCORE_WEBDAV_MAX_ENTRIES
999#define PROTOCORE_WEBDAV_MAX_ENTRIES 32
1000#endif
1001
1002/**
1003 * @brief Deepest tree a WebDAV DELETE / COPY walks before refusing (see PROTOCORE_ENABLE_WEBDAV).
1004 *
1005 * The recursive walkers carry one path and one child name per level, so this is what turns their
1006 * working storage into a fixed number instead of one that grows with the tree being walked. It was
1007 * a bare 8 in the walk's own test, which bounded the recursion but sized nothing, because the paths
1008 * were stack arrays the footprint could not see.
1009 */
1010#ifndef PROTOCORE_DAV_MAX_DEPTH
1011#define PROTOCORE_DAV_MAX_DEPTH 8
1012#endif
1013
1014/** @brief Maximum properties echoed in a WebDAV PROPPATCH 207 response (bounds the response). */
1015#ifndef PROTOCORE_WEBDAV_MAX_PROPS
1016#define PROTOCORE_WEBDAV_MAX_PROPS 16
1017#endif
1018
1019/**
1020 * @brief HTTP method-token buffer size (bytes, including the NUL).
1021 *
1022 * Sized for the longest method the server must recognize: 8 normally (OPTIONS),
1023 * grown to fit the WebDAV methods (PROPPATCH is 9 chars) when WebDAV is enabled.
1024 */
1025#ifndef PROTOCORE_METHOD_BUF_SIZE
1026#if PROTOCORE_ENABLE_WEBDAV
1027#define PROTOCORE_METHOD_BUF_SIZE 12
1028#else
1029#define PROTOCORE_METHOD_BUF_SIZE 8
1030#endif
1031#endif
1032
1033/** @brief Max header lines parsed per STOMP frame (extras beyond this are ignored). */
1034#ifndef PROTOCORE_STOMP_MAX_HEADERS
1035#define PROTOCORE_STOMP_MAX_HEADERS 16
1036#endif
1037
1038/** @brief 3964R block-body buffer size (built/received bytes: DLE-stuffed payload + DLE ETX + BCC). */
1039#ifndef PROTOCORE_SIMATIC_BLOCK_MAX
1040#define PROTOCORE_SIMATIC_BLOCK_MAX 256
1041#endif
1042
1043/** @brief 3964R QVZ (Quittungsverzugszeit): handshake acknowledge-delay timeout, ms.
1044 * Siemens AcknDelayTime, default 16#07D0. */
1045#ifndef PROTOCORE_SIMATIC_QVZ_MS
1046#define PROTOCORE_SIMATIC_QVZ_MS 2000
1047#endif
1048
1049/** @brief 3964R ZVZ (Zeichenverzugszeit): inter-character timeout while receiving a block, ms.
1050 * Siemens CharacterDelayTime, default 16#00DC. */
1051#ifndef PROTOCORE_SIMATIC_ZVZ_MS
1052#define PROTOCORE_SIMATIC_ZVZ_MS 220
1053#endif
1054
1055/** @brief Max serialized size of one Sparkplug B metric submessage (stack temp, bytes). */
1056#ifndef PROTOCORE_SPB_METRIC_MAX
1057#define PROTOCORE_SPB_METRIC_MAX 256
1058#endif
1059
1060/** @brief Number of Modbus coils (FC 1/5/15), single-bit R/W (BSS, bit-packed). */
1061#ifndef PROTOCORE_MODBUS_COILS
1062#define PROTOCORE_MODBUS_COILS 64
1063#endif
1064
1065/** @brief Number of Modbus discrete inputs (FC 2), single-bit read-only (BSS, bit-packed). */
1066#ifndef PROTOCORE_MODBUS_DISCRETE_INPUTS
1067#define PROTOCORE_MODBUS_DISCRETE_INPUTS 64
1068#endif
1069
1070/** @brief Number of Modbus holding registers (FC 3/6/16), 16-bit R/W (BSS). */
1071#ifndef PROTOCORE_MODBUS_HOLDING_REGS
1072#define PROTOCORE_MODBUS_HOLDING_REGS 64
1073#endif
1074
1075/** @brief Number of Modbus input registers (FC 4), 16-bit read-only (BSS). */
1076#ifndef PROTOCORE_MODBUS_INPUT_REGS
1077#define PROTOCORE_MODBUS_INPUT_REGS 64
1078#endif
1079
1080/** @brief Maximum simultaneous TLS connections (each holds mbedTLS record buffers). */
1081#ifndef MAX_TLS_CONNS
1082#define MAX_TLS_CONNS 1
1083#endif
1084
1085/** @brief Session-ticket lifetime / key-rotation period in seconds (see PROTOCORE_ENABLE_TLS_RESUMPTION). */
1086#ifndef PROTOCORE_TLS_TICKET_LIFETIME_S
1087#define PROTOCORE_TLS_TICKET_LIFETIME_S 86400
1088#endif
1089
1090/** @brief Maximum length of a verified mTLS peer subject DN string (incl. NUL). */
1091#ifndef PROTOCORE_MTLS_SUBJECT_MAX
1092#define PROTOCORE_MTLS_SUBJECT_MAX 128
1093#endif
1094
1095/** @brief Maximum extra variable-bindings (beyond sysUpTime/snmpTrapOID) in one notification. */
1096#ifndef PROTOCORE_SNMP_TRAP_MAX_VARBINDS
1097#define PROTOCORE_SNMP_TRAP_MAX_VARBINDS 8
1098#endif
1099
1100/** @brief Static datagram buffer for an outbound SNMP notification, bytes. */
1101#ifndef PROTOCORE_SNMP_TRAP_BUF_SIZE
1102#define PROTOCORE_SNMP_TRAP_BUF_SIZE 1024
1103#endif
1104
1105/** @brief Maximum sub-identifiers (arcs) in an SNMP object identifier. */
1106#ifndef SNMP_MAX_OID_LEN
1107#define SNMP_MAX_OID_LEN 32
1108#endif
1109
1110/**
1111 * @brief Maximum registered MIB objects (the agent's fixed OID table).
1112 *
1113 * Each entry holds its OID, a value descriptor, and optional get/set callbacks
1114 * (see src/services/net/snmp/snmp_agent.h). The table lives in BSS; entries are
1115 * scanned linearly (small table) and need not be registered in OID order.
1116 */
1117#ifndef SNMP_MAX_MIB_ENTRIES
1118#define SNMP_MAX_MIB_ENTRIES 16
1119#endif
1120
1121/**
1122 * @brief Maximum variable bindings the agent will emit in one response.
1123 *
1124 * Bounds GetBulk expansion (max-repetitions is clamped so the total response
1125 * varbind count never exceeds this) and the per-request decode scratch.
1126 */
1127#ifndef SNMP_MAX_VARBINDS
1128#define SNMP_MAX_VARBINDS 16
1129#endif
1130
1131/**
1132 * @brief Static request/response datagram buffers for the SNMP UDP agent.
1133 *
1134 * Two buffers of this size live in BSS (one in, one out) - no heap. 484 is the
1135 * RFC 1157 minimum maximum message size; the default holds a one-frame UDP
1136 * payload so GetBulk walks fit without IP fragmentation.
1137 */
1138#ifndef SNMP_MSG_BUF_SIZE
1139#define SNMP_MSG_BUF_SIZE 1472
1140#endif
1141
1142/** @brief Maximum SNMP community-string length (including null terminator). */
1143#ifndef SNMP_COMMUNITY_MAX
1144#define SNMP_COMMUNITY_MAX 32
1145#endif
1146
1147/** @brief Default read-only community (overridable at runtime via SnmpAgent.community.ro + SnmpAgent.init).
1148 * Deployments SHOULD change this from the RFC-1157 well-known "public" for anything but a closed
1149 * network. */
1150#ifndef PROTOCORE_SNMP_DEFAULT_RO_COMMUNITY
1151#define PROTOCORE_SNMP_DEFAULT_RO_COMMUNITY "public"
1152#endif
1153
1154/** @brief Maximum SNMPv3 USM user-name length (including null terminator). */
1155#ifndef SNMP_V3_USER_MAX
1156#define SNMP_V3_USER_MAX 32
1157#endif
1158
1159/** @brief Maximum SNMPv3 authoritative engine-ID length in bytes (RFC 3411 allows 5..32). */
1160#ifndef SNMP_V3_ENGINEID_MAX
1161#define SNMP_V3_ENGINEID_MAX 32
1162#endif
1163
1164// ---------------------------------------------------------------------------
1165// CoAP server sizing constants (PROTOCORE_ENABLE_COAP must be 1)
1166// ---------------------------------------------------------------------------
1167
1168/** @brief Maximum simultaneous CoAP observers (one slot per observed resource per client). */
1169#ifndef PROTOCORE_COAP_MAX_OBSERVERS
1170#define PROTOCORE_COAP_MAX_OBSERVERS 4
1171#endif
1172
1173/**
1174 * @brief CoAP message de-duplication cache size (RFC 7252 sec 4.5). A Confirmable request the server has
1175 * already answered is recognized by its (source endpoint, Message-ID) and re-answered with the
1176 * cached response WITHOUT re-running the handler - so a client's CON retransmission cannot execute
1177 * a non-idempotent request (POST/PUT/DELETE) twice. Set to 0 to compile the dedup cache out.
1178 */
1179#ifndef PROTOCORE_COAP_DEDUP_ENTRIES
1180#define PROTOCORE_COAP_DEDUP_ENTRIES 4
1181#endif
1182
1183/** @brief Largest cached response the dedup cache retains per entry; a bigger response is not cached (a
1184 * retransmission re-processes it, fine for the idempotent GET whose block-wise reply exceeds this). */
1185#ifndef PROTOCORE_COAP_DEDUP_RESP_MAX
1186#define PROTOCORE_COAP_DEDUP_RESP_MAX 256
1187#endif
1188
1189/** @brief How long (ms) a dedup entry stays fresh - RFC 7252 EXCHANGE_LIFETIME (~247 s) by default, past
1190 * which a repeat Message-ID is treated as a new exchange. */
1191#ifndef PROTOCORE_COAP_DEDUP_LIFETIME_MS
1192#define PROTOCORE_COAP_DEDUP_LIFETIME_MS 247000u
1193#endif
1194
1195/** @brief Largest block-size exponent (SZX) the server will use: block size = 2^(SZX+4) bytes, SZX 0..6 (16..1024). */
1196#ifndef PROTOCORE_COAP_BLOCK_SZX_MAX
1197#define PROTOCORE_COAP_BLOCK_SZX_MAX 6
1198#endif
1199
1200/**
1201 * @brief Reassembly buffer for a block-wise (Block1) request upload, in bytes.
1202 *
1203 * One buffer of this size lives in BSS only when PROTOCORE_ENABLE_COAP_BLOCK is set.
1204 * It bounds the largest payload a chunked POST/PUT can deliver to a handler.
1205 */
1206#ifndef PROTOCORE_COAP_BLOCK1_MAX
1207#define PROTOCORE_COAP_BLOCK1_MAX 1024
1208#endif
1209
1210/**
1211 * @brief Maximum registered CoAP resources (the server's fixed routing table).
1212 *
1213 * Each entry holds a path pointer, an allowed-methods bitmask, and a handler.
1214 * The table lives in BSS and is scanned linearly (small table).
1215 */
1216#ifndef PROTOCORE_COAP_MAX_RESOURCES
1217#define PROTOCORE_COAP_MAX_RESOURCES 8
1218#endif
1219
1220/** @brief Maximum reconstructed Uri-Path length, including separators and the leading '/'. */
1221#ifndef PROTOCORE_COAP_MAX_PATH
1222#define PROTOCORE_COAP_MAX_PATH 64
1223#endif
1224
1225/** @brief Maximum reconstructed Uri-Query length (segments joined by '&'). */
1226#ifndef PROTOCORE_COAP_MAX_QUERY
1227#define PROTOCORE_COAP_MAX_QUERY 64
1228#endif
1229
1230/**
1231 * @brief Maximum CoAP request/response payload in bytes.
1232 *
1233 * Sizes the static scratch a handler writes its response body into and bounds
1234 * the request payload handed to it. One buffer of this size lives in BSS.
1235 */
1236#ifndef PROTOCORE_COAP_MAX_PAYLOAD
1237#define PROTOCORE_COAP_MAX_PAYLOAD 256
1238#endif
1239
1240/**
1241 * @brief Static response-datagram buffer for the CoAP UDP server.
1242 *
1243 * One buffer of this size lives in BSS (the request is transport-owned). Must
1244 * hold a 4-byte header + token (<=8) + the Content-Format option + a 0xFF marker
1245 * + PROTOCORE_COAP_MAX_PAYLOAD bytes. When block-wise transfer is enabled it must
1246 * also hold one full block (2^(PROTOCORE_COAP_BLOCK_SZX_MAX+4) bytes) + option
1247 * overhead, so the default grows accordingly.
1248 */
1249#ifndef PROTOCORE_COAP_MSG_BUF_SIZE
1250#if PROTOCORE_ENABLE_COAP_BLOCK
1251#define PROTOCORE_COAP_MSG_BUF_SIZE 1152
1252#else
1253#define PROTOCORE_COAP_MSG_BUF_SIZE 512
1254#endif
1255#endif
1256
1257/** @brief Default UDP port the CoAP observe transport notifies from (IANA well-known 5683). */
1258#ifndef PROTOCORE_COAP_OBSERVE_PORT
1259#define PROTOCORE_COAP_OBSERVE_PORT 5683
1260#endif
1261
1262/**
1263 * @brief Bytes of the static BSS arena mbedTLS allocates from (PROTOCORE_ENABLE_TLS).
1264 *
1265 * All mbedTLS allocations (per-connection record buffers, handshake temporaries,
1266 * cert/key parsing) are served from this fixed arena via a custom allocator
1267 * installed with mbedtls_platform_set_calloc_free() - never the system heap. Must
1268 * cover the worst-case handshake peak for MAX_TLS_CONNS; if undersized the
1269 * handshake fails cleanly (no corruption). Measured peak for ONE ECDSA P-256
1270 * connection on Arduino-esp32 (16 KB IN + 16 KB OUT records) is ~41.5 KB, so the
1271 * default leaves a small margin. An RSA cert/larger chain needs more; query the
1272 * live peak via protocore_tls_arena_peak(). NOTE: a second concurrent TLS connection
1273 * roughly doubles the record-buffer cost (~32 KB more), which overflows the
1274 * static DRAM budget - keep MAX_TLS_CONNS at 1 unless you shrink the IDF record
1275 * sizes (CONFIG_MBEDTLS_SSL_IN/OUT_CONTENT_LEN, needs an ESP-IDF build).
1276 */
1277#ifndef PROTOCORE_TLS_ARENA_SIZE
1278// The arena is SHARED across all TLS connections, so it must cover the peak for MAX_TLS_CONNS: ~48 KB
1279// for the first handshake (ECDSA P-256, 16 KB IN + 16 KB OUT records + temporaries) plus ~32 KB of
1280// record buffers per additional concurrent connection. Auto-derive so a profile that raises
1281// MAX_TLS_CONNS (a PSRAM board, via PROTOCORE_TLS_ARENA_IN_PSRAM) grows the arena to match instead of
1282// silently starving the second handshake. MAX_TLS_CONNS == 1 keeps the historical 49152.
1283#define PROTOCORE_TLS_ARENA_SIZE (49152 + (MAX_TLS_CONNS - 1) * 32768)
1284#endif
1285
1286/**
1287 * @brief Place the TLS arena in external PSRAM instead of internal DRAM (ESP32).
1288 *
1289 * The internal static-DRAM ceiling (`dram0_0_seg`) is only ~122 KB, so a single
1290 * ~48 KB arena already uses a large slice and a second concurrent connection
1291 * (MAX_TLS_CONNS > 1) overflows it. On a board with PSRAM, set this to 1 to move
1292 * the arena to external RAM via `EXT_RAM_BSS_ATTR` / `EXT_RAM_ATTR`, freeing the
1293 * whole `PROTOCORE_TLS_ARENA_SIZE` back to internal DRAM so many connections fit.
1294 * Requires `CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY` (and PSRAM enabled) in the
1295 * ESP-IDF/PlatformIO config; without it the attribute is a no-op and the arena
1296 * stays in DRAM (safe fallback). No effect on the native host build.
1297 */
1298#ifndef PROTOCORE_TLS_ARENA_IN_PSRAM
1299#define PROTOCORE_TLS_ARENA_IN_PSRAM 0
1300#endif
1301
1302/**
1303 * @brief Cap TLS records via the Maximum Fragment Length extension (RFC 6066).
1304 *
1305 * 0 (default) leaves the 16 KB TLS record ceiling. Set to 512, 1024, 2048, or 4096
1306 * to negotiate a smaller maximum record. On a mbedTLS build with variable-length
1307 * record buffers this shrinks the per-connection arena footprint (so more concurrent
1308 * connections fit); on a fixed-buffer build it still bounds the on-wire record size
1309 * (bandwidth / latency on a constrained link) and honors a client's MFL request.
1310 * Applied to both the server and the outbound client config. Needs an mbedTLS build
1311 * with `MBEDTLS_SSL_MAX_FRAGMENT_LENGTH` (else it is a no-op).
1312 */
1313#ifndef PROTOCORE_TLS_MAX_FRAG_LEN
1314#define PROTOCORE_TLS_MAX_FRAG_LEN 0
1315#endif
1316
1317/**
1318 * @brief Lead the ECDHE curve/group preference with secp256r1 (P-256) instead of x25519.
1319 *
1320 * PER-VARIANT by default, because the ECC silicon differs wildly between dies. On a chip with a
1321 * hardware NIST-ECC accelerator (`PROTOCORE_HW_ECC` = 1: ESP32-P4/C5/C6/C61/H2/H4/... where mbedTLS routes
1322 * P-256 through the HW via `ecc_alt`), P-256 is dramatically faster than x25519, which stays software
1323 * (measured on an ESP32-P4: P-256 ECDHE ~10 ms vs x25519 ~132 ms, and the full TLS handshake ~29 ms
1324 * vs ~160 ms - a 5.5x win). On a chip WITHOUT ECC HW (`PROTOCORE_HW_ECC` = 0: ESP32-S3/S2/classic), both
1325 * curves are software and near-identical in the full handshake, so x25519 (the security-preferred
1326 * modern default) leads and this stays 0 (the S3 order is unchanged).
1327 *
1328 * So the default tracks `PROTOCORE_HW_ECC` - the profile's assertion that the die has NIST-ECC HW - and is
1329 * overridable: force `-DPROTOCORE_TLS_ECDHE_PREFER_P256=0` to mandate x25519-first even on an ECC-HW chip
1330 * (a deployment policy choice), or `=1` to prefer P-256 on a chip whose profile has not flagged HW ECC.
1331 * This only reorders PREFERENCE; every curve stays enabled, so a peer that offers just one still
1332 * connects. Applied to both the server and outbound-client configs (tls.cpp `tls_apply_curve_pref`).
1333 */
1334#ifndef PROTOCORE_TLS_ECDHE_PREFER_P256
1335#define PROTOCORE_TLS_ECDHE_PREFER_P256 PROTOCORE_HW_ECC
1336#endif
1337
1338/**
1339 * @brief Acknowledge that a MAX_TLS_CONNS > 1 build has been sized to fit.
1340 *
1341 * The whole TLS arena is static `.bss` and the internal `dram0_0_seg` ceiling is only
1342 * ~122 KB, so a second concurrent connection's arena overflows it on a stock build.
1343 * A validation guard (bottom of this file) therefore rejects MAX_TLS_CONNS > 1 unless
1344 * you have taken one of the paths in docs/KNOWN_LIMITATIONS.md - move the arena to
1345 * PSRAM (`PROTOCORE_TLS_ARENA_IN_PSRAM`, which satisfies the guard on its own), shrink the
1346 * mbedTLS records in a custom ESP-IDF build, or reclaim internal DRAM - and then set
1347 * this to 1 to confirm the build was sized deliberately.
1348 */
1349#ifndef PROTOCORE_TLS_ACK_MULTI_CONN_DRAM
1350#define PROTOCORE_TLS_ACK_MULTI_CONN_DRAM 0
1351#endif
1352
1353// ---------------------------------------------------------------------------
1354// Optional network services (ESP32-only thin wrappers; each default-off so it
1355// costs no code/RAM/flash unless explicitly enabled).
1356// ---------------------------------------------------------------------------
1357
1358/** @brief Services the responder advertises at once, `_http._tcp` included. */
1359#ifndef PROTOCORE_MDNS_MAX_SERVICES
1360#define PROTOCORE_MDNS_MAX_SERVICES 4
1361#endif
1362
1363/** @brief Bytes of packed `key=value` TXT strings, each with its own length byte ahead of it. */
1364#ifndef PROTOCORE_MDNS_TXT_MAX
1365#define PROTOCORE_MDNS_TXT_MAX 128
1366#endif
1367
1368/** @brief Longest host label, service type or proto label the responder holds, NUL included. */
1369#ifndef PROTOCORE_MDNS_LABEL_MAX
1370#define PROTOCORE_MDNS_LABEL_MAX 32
1371#endif
1372
1373/**
1374 * @brief Response datagram the responder composes.
1375 *
1376 * One answer set is an A plus, per service, two PTRs, an SRV and a TXT, so this bounds how many
1377 * services fit one packet rather than how many may be registered.
1378 */
1379#ifndef PROTOCORE_MDNS_TX_MAX
1380#define PROTOCORE_MDNS_TX_MAX 512
1381#endif
1382
1383/**
1384 * @brief Local UDP port the portable SNTP client asks from.
1385 *
1386 * Not 123: a device running PROTOCORE_ENABLE_NTP_SERVER already holds that port, and the client has to
1387 * bind one of its own to hear the reply come back.
1388 */
1389#ifndef PROTOCORE_NTP_CLIENT_PORT
1390#define PROTOCORE_NTP_CLIENT_PORT 1123
1391#endif
1392
1393/** @brief Stratum the NTP server advertises (distance from a reference clock; 1-15). */
1394#ifndef PROTOCORE_NTP_SERVER_STRATUM
1395#define PROTOCORE_NTP_SERVER_STRATUM 3
1396#endif
1397
1398/** @brief Max A records in the DNS server's fixed table. */
1399#ifndef PROTOCORE_DNS_SERVER_MAX_RECORDS
1400#define PROTOCORE_DNS_SERVER_MAX_RECORDS 8
1401#endif
1402
1403/** @brief TTL (seconds) the DNS server puts on its answers. */
1404#ifndef PROTOCORE_DNS_SERVER_TTL
1405#define PROTOCORE_DNS_SERVER_TTL 60
1406#endif
1407
1408/** @brief Max length of a queried/stored DNS name (bytes, incl NUL). */
1409#ifndef PROTOCORE_DNS_NAME_MAX
1410#define PROTOCORE_DNS_NAME_MAX 128
1411#endif
1412
1413/**
1414 * @brief Auto-inject a `Date` response header (RFC 7231 7.1.1.2) when a wall-clock
1415 * time is available.
1416 *
1417 * Default off: a clock-less device must not emit a wrong `Date`, and most embedded
1418 * responses do not need one, so it stays off the hot path. When set, every dynamic
1419 * response carries `Date: <IMF-fixdate>` - but only once a real time exists; before a
1420 * source has valid time it is silently omitted (still correct for a clock-less boot).
1421 *
1422 * The time is taken from the multi-source registry (any enabled NTP / GPS / RTC / ...
1423 * by priority) when PROTOCORE_ENABLE_TIME_SOURCE is set - register your sources with
1424 * protocore_time_source_add() (protocore_rtc_time_source, protocore_ntp_time_source, ...). Otherwise it comes
1425 * straight from the NTP client. Needs at least one such time source to emit.
1426 *
1427 * Which of the two a build has is ::HttpClock's decision, made once in server/io/http_clock rather
1428 * than by each caller: this flag is what compiles that module in, and it takes
1429 * ::PROTOCORE_HTTP_CLOCK_BORROW from the plaintext arena to hold the rendered value.
1430 */
1431#ifndef PROTOCORE_ENABLE_HTTP_CLOCK
1432#define PROTOCORE_ENABLE_HTTP_CLOCK 0
1433#endif
1434
1435/** @brief Maximum registered time sources (PROTOCORE_ENABLE_TIME_SOURCE). */
1436#ifndef PROTOCORE_TIME_SOURCE_MAX
1437#define PROTOCORE_TIME_SOURCE_MAX 4
1438#endif
1439
1440/**
1441 * @brief Shared I2C bus pins for the sensor / peripheral drivers (RTC, SHT3x, MPR121, ADS1115,
1442 * INA219, PCA9685). All of them share one bus via protocore_i2c_begin() (server/peripherals/i2c.h), so
1443 * this is the single place to move it. The default -1 uses the platform's default pins (GPIO 21
1444 * SDA / 22 SCL on the classic ESP32). Set both to free GPIOs when those pins are taken - most
1445 * importantly a **wired-Ethernet PHY**: the LAN8720 RMII uses GPIO 21 (TX_EN) and GPIO 22
1446 * (TXD1) on the classic ESP32 (WROOM/WROVER) and the ESP32-P4 (which have the RMII EMAC), so
1447 * with that Ethernet on, move the I2C bus off them (e.g. 32 / 33). The ESP32-S3/C3 have no RMII
1448 * MAC and use an SPI Ethernet (W5500) instead - relocate the bus off whatever SPI pins that
1449 * uses. UART peripherals (LD2410) take their RX/TX pins at protocore_ld2410_begin(), so remap those too.
1450 */
1451#ifndef PROTOCORE_I2C_SDA_PIN
1452#define PROTOCORE_I2C_SDA_PIN -1
1453#endif
1454#ifndef PROTOCORE_I2C_SCL_PIN
1455#define PROTOCORE_I2C_SCL_PIN -1
1456#endif
1457
1458/**
1459 * @brief Shared SPI bus pins for the peripheral drivers, the same way the I2C pins above are
1460 * shared. The default -1 uses the platform's default pins for its VSPI/HSPI host. Set them when
1461 * those pins are taken, most often by a W5500 SPI Ethernet on a part with no RMII MAC (the
1462 * ESP32-S3 / C3), which drives this same bus.
1463 */
1464#ifndef PROTOCORE_SPI_MOSI_PIN
1465#define PROTOCORE_SPI_MOSI_PIN -1
1466#endif
1467#ifndef PROTOCORE_SPI_MISO_PIN
1468#define PROTOCORE_SPI_MISO_PIN -1
1469#endif
1470#ifndef PROTOCORE_SPI_SCLK_PIN
1471#define PROTOCORE_SPI_SCLK_PIN -1
1472#endif
1473
1474/** @brief I2C address of the RTC (DS1307/DS3231 are fixed at 0x68). */
1475#ifndef PROTOCORE_RTC_I2C_ADDR
1476#define PROTOCORE_RTC_I2C_ADDR 0x68
1477#endif
1478
1479/** @brief HMMD UART baud rate (the module's factory default is 115200). */
1480#ifndef PROTOCORE_HMMD_BAUD
1481#define PROTOCORE_HMMD_BAUD 115200
1482#endif
1483
1484/** @brief UART unit the HMMD is wired to. Unit 2 is the one free of the console on most boards. */
1485#ifndef PROTOCORE_HMMD_UART
1486#define PROTOCORE_HMMD_UART 2
1487#endif
1488
1489/** @brief LD2410 UART baud rate (the module's fixed factory default is 256000). */
1490#ifndef PROTOCORE_LD2410_BAUD
1491#define PROTOCORE_LD2410_BAUD 256000
1492#endif
1493
1494/** @brief UART unit the LD2410 is wired to. Unit 2 is the one free of the console on most boards. */
1495#ifndef PROTOCORE_LD2410_UART
1496#define PROTOCORE_LD2410_UART 2
1497#endif
1498
1499/** @brief GPIO the SEN0192 OUT line is wired to. */
1500#ifndef PROTOCORE_SEN0192_PIN
1501#define PROTOCORE_SEN0192_PIN 4
1502#endif
1503
1504/** @brief Presence is held this many ms after the last active (motion) sample before it clears. */
1505#ifndef PROTOCORE_SEN0192_HOLD_MS
1506#define PROTOCORE_SEN0192_HOLD_MS 2000
1507#endif
1508
1509/** @brief SEN0192 OUT polarity: 1 = the OUT line reads HIGH on motion, 0 = active-LOW. */
1510#ifndef PROTOCORE_SEN0192_ACTIVE_HIGH
1511#define PROTOCORE_SEN0192_ACTIVE_HIGH 1
1512#endif
1513
1514/** @brief I2C address of the MPR121 (0x5A default; 0x5B/0x5C/0x5D via the ADDR pin). */
1515#ifndef PROTOCORE_MPR121_I2C_ADDR
1516#define PROTOCORE_MPR121_I2C_ADDR 0x5A
1517#endif
1518
1519/** @brief MPR121 per-electrode touch threshold (delta counts from baseline; NXP AN3944 suggests ~4..12).
1520 * Higher = less sensitive. Keep the release threshold below it for hysteresis. */
1521#ifndef PROTOCORE_MPR121_TOUCH_THRESHOLD
1522#define PROTOCORE_MPR121_TOUCH_THRESHOLD 12
1523#endif
1524
1525/** @brief MPR121 per-electrode release threshold (delta counts; should be below the touch threshold). */
1526#ifndef PROTOCORE_MPR121_RELEASE_THRESHOLD
1527#define PROTOCORE_MPR121_RELEASE_THRESHOLD 6
1528#endif
1529
1530/** @brief I2C address of the SHT3x (0x44 with ADDR low; 0x45 with ADDR high). */
1531#ifndef PROTOCORE_SHT3X_I2C_ADDR
1532#define PROTOCORE_SHT3X_I2C_ADDR 0x44
1533#endif
1534
1535/** @brief I2C address of the PCA9685 (0x40 default; the six address pins select 0x40..0x7F). */
1536#ifndef PROTOCORE_PCA9685_I2C_ADDR
1537#define PROTOCORE_PCA9685_I2C_ADDR 0x40
1538#endif
1539
1540/** @brief Default PWM output frequency in Hz (50 Hz suits hobby servos). */
1541#ifndef PROTOCORE_PCA9685_FREQ
1542#define PROTOCORE_PCA9685_FREQ 50
1543#endif
1544
1545/** @brief I2C address of the ADS1115 (0x48 with ADDR to GND; 0x49/0x4A/0x4B for VDD/SDA/SCL). */
1546#ifndef PROTOCORE_ADS1115_I2C_ADDR
1547#define PROTOCORE_ADS1115_I2C_ADDR 0x48
1548#endif
1549
1550/** @brief Default ADS1115 PGA gain code (ADS1115_GAIN_*): 0=+/-6.144V, 1=+/-4.096V, 2=+/-2.048V (default),
1551 * 3=+/-1.024V, 4=+/-0.512V, 5=+/-0.256V. Also the fallback when a read passes an invalid gain. */
1552#ifndef PROTOCORE_ADS1115_GAIN
1553#define PROTOCORE_ADS1115_GAIN 2 // ADS1115_GAIN_2 (+/- 2.048 V)
1554#endif
1555
1556/** @brief Default ADS1115 data-rate code (ADS1115_DR_*): 0=8, 1=16, 2=32, 3=64, 4=128 (default), 5=250,
1557 * 6=475, 7=860 SPS. The single-shot read waits the matching conversion time. */
1558#ifndef PROTOCORE_ADS1115_DR
1559#define PROTOCORE_ADS1115_DR 4 // ADS1115_DR_128 (128 SPS)
1560#endif
1561
1562/** @brief ADS1115 input mode: 0 = single-ended (AINx vs GND), 1 = differential. In differential mode the
1563 * channel selects the pair: 0=AIN0-AIN1, 1=AIN0-AIN3, 2=AIN1-AIN3, 3=AIN2-AIN3. */
1564#ifndef PROTOCORE_ADS1115_DIFFERENTIAL
1565#define PROTOCORE_ADS1115_DIFFERENTIAL 0
1566#endif
1567
1568/** @brief I2C address of the INA219 (0x40 default; the A0/A1 pins select 0x40..0x4F). */
1569#ifndef PROTOCORE_INA219_I2C_ADDR
1570#define PROTOCORE_INA219_I2C_ADDR 0x40
1571#endif
1572
1573/** @brief Default INA219 current LSB in microamps per bit (calibration input). The fallback when
1574 * Ina219.begin is passed 0. 100 uA/bit with a 100 mohm shunt -> a 2 A full-scale range. */
1575#ifndef PROTOCORE_INA219_CURRENT_LSB_UA
1576#define PROTOCORE_INA219_CURRENT_LSB_UA 100
1577#endif
1578
1579/** @brief Default INA219 shunt resistance in milliohms (calibration input). The fallback when
1580 * Ina219.begin is passed 0. 100 mohm is the common breakout value. */
1581#ifndef PROTOCORE_INA219_SHUNT_MOHM
1582#define PROTOCORE_INA219_SHUNT_MOHM 100
1583#endif
1584
1585/** @brief Max key/value entries in the host (test) config backend. */
1586#ifndef PROTOCORE_CONFIG_MAX_ENTRIES
1587#define PROTOCORE_CONFIG_MAX_ENTRIES 16
1588#endif
1589
1590/** @brief Max key length incl. null (NVS caps keys at 15 chars). */
1591#ifndef PROTOCORE_CONFIG_KEY_MAX
1592#define PROTOCORE_CONFIG_KEY_MAX 16
1593#endif
1594
1595/**
1596 * @brief Max value bytes per entry in the host (test) config backend.
1597 *
1598 * Holds the largest blob the seam carries, which is the SSH host key's PKCS#8 DER
1599 * (SSH_RSA_KEY_DER_MAX, 1700). A power of two keeps the row stride a shift.
1600 */
1601#ifndef PROTOCORE_CONFIG_VAL_MAX
1602#define PROTOCORE_CONFIG_VAL_MAX 2048
1603#endif
1604
1605/**
1606 * @brief Include the trademark-named themes in the embedded set (default on / open-source).
1607 *
1608 * A few themes are named after a company or product (Darcula, Windows XP, Discord, Spotify, ...). The
1609 * palette is just colors, but a commercial product should not ship the branded name, so a commercial
1610 * build sets this to 0 to drop those blobs from the registry (the list is `RESTRICTED` in
1611 * `src/web_assets/wizard/gen_themes.py`). The open-source (AGPL) build keeps them.
1612 */
1613#ifndef PROTOCORE_THEMES_INCLUDE_TRADEMARKED
1614#define PROTOCORE_THEMES_INCLUDE_TRADEMARKED 1
1615#endif
1616
1617/** @brief Maximum widgets in the dashboard table (BSS value array). */
1618#ifndef PROTOCORE_DASHBOARD_MAX_WIDGETS
1619#define PROTOCORE_DASHBOARD_MAX_WIDGETS 16
1620#endif
1621
1622/** @brief Stack buffer for the dashboard layout / values JSON (bytes). */
1623#ifndef PROTOCORE_DASHBOARD_JSON_BUF
1624#define PROTOCORE_DASHBOARD_JSON_BUF 1024
1625#endif
1626
1627/** @brief Maximum partitions the monitor reports (BSS table). */
1628#ifndef PROTOCORE_PARTITION_MAX
1629#define PROTOCORE_PARTITION_MAX 16
1630#endif
1631
1632/** @brief Stack buffer for the partition-map JSON (bytes). */
1633#ifndef PROTOCORE_PARTITION_JSON_BUF
1634#define PROTOCORE_PARTITION_JSON_BUF 1024
1635#endif
1636
1637/** @brief Maximum GPIO pins the mapper reports (BSS table). */
1638#ifndef PROTOCORE_GPIO_MAX
1639#define PROTOCORE_GPIO_MAX 40
1640#endif
1641
1642/** @brief Stack buffer for the GPIO-map JSON (bytes). */
1643#ifndef PROTOCORE_GPIO_JSON_BUF
1644#define PROTOCORE_GPIO_JSON_BUF 1024
1645#endif
1646
1647/** @brief Stack buffer for one telemetry line (bytes). */
1648#ifndef PROTOCORE_UDP_TELEMETRY_BUF
1649#define PROTOCORE_UDP_TELEMETRY_BUF 256
1650#endif
1651
1652/** @brief Default StatsD collector UDP port (StatsD/Graphite standard). */
1653#ifndef PROTOCORE_STATSD_PORT
1654#define PROTOCORE_STATSD_PORT 8125
1655#endif
1656
1657/** @brief Stack buffer for one StatsD line (bytes; caps metric name + value + tags). */
1658#ifndef PROTOCORE_STATSD_LINE_MAX
1659#define PROTOCORE_STATSD_LINE_MAX 256
1660#endif
1661
1662/** @brief Free-heap floor (bytes); below this trips the heap guardrail. */
1663#ifndef PROTOCORE_GUARDRAIL_HEAP_MIN
1664#define PROTOCORE_GUARDRAIL_HEAP_MIN 8192
1665#endif
1666
1667/** @brief Largest-free-block floor (bytes); below this trips the fragmentation guardrail. */
1668#ifndef PROTOCORE_GUARDRAIL_FRAG_MIN_BLOCK
1669#define PROTOCORE_GUARDRAIL_FRAG_MIN_BLOCK 4096
1670#endif
1671
1672/** @brief Task remaining-stack floor (bytes); below this trips the stack guardrail. */
1673#ifndef PROTOCORE_GUARDRAIL_STACK_MIN
1674#define PROTOCORE_GUARDRAIL_STACK_MIN 512
1675#endif
1676
1677/** @brief Max monitored lifelines in the fail-safe registry (static, zero-heap). */
1678#ifndef PROTOCORE_FAILSAFE_MAX_LIFELINES
1679#define PROTOCORE_FAILSAFE_MAX_LIFELINES 8
1680#endif
1681
1682/** @brief CPU clock (MHz) when there is work to do. */
1683#ifndef PROTOCORE_POWER_MHZ_MAX
1684#define PROTOCORE_POWER_MHZ_MAX 240
1685#endif
1686
1687/** @brief CPU clock (MHz) when idle, thermally throttled, or recovering from a brownout. */
1688#ifndef PROTOCORE_POWER_MHZ_MIN
1689#define PROTOCORE_POWER_MHZ_MIN 80
1690#endif
1691
1692/** @brief Load percentage at/above which the ceiling clock is used. */
1693#ifndef PROTOCORE_POWER_BUSY_PCT
1694#define PROTOCORE_POWER_BUSY_PCT 40
1695#endif
1696
1697/** @brief Die temperature (C) at/above which the clock is throttled. */
1698#ifndef PROTOCORE_POWER_TEMP_HOT_C
1699#define PROTOCORE_POWER_TEMP_HOT_C 80
1700#endif
1701
1702/**
1703 * @brief Die temperature (C) at/below which the throttle is released.
1704 *
1705 * Deliberately below PROTOCORE_POWER_TEMP_HOT_C: with a single threshold a part sitting exactly at the
1706 * limit would flap between ceiling and floor every tick, which is worse than either state.
1707 *
1708 * The gap has to be wider than the temperature swing the clock change *itself* causes, or the
1709 * governor oscillates no matter how correct the hysteresis is. Measured on an ESP32-S3: dropping
1710 * 240 -> 80 MHz cools the die about 2 C within one 500 ms tick, and going back up reheats it by the
1711 * same amount. A band narrower than that swing is self-sustaining - the throttle's own effect
1712 * carries the die back across the release threshold. The 10 C default clears it with room to spare.
1713 */
1714#ifndef PROTOCORE_POWER_TEMP_COOL_C
1715#define PROTOCORE_POWER_TEMP_COOL_C 70
1716#endif
1717
1718/** @brief How long (ms) to hold the floor clock after a brownout reset before ramping back up. */
1719#ifndef PROTOCORE_POWER_RECOVER_MS
1720#define PROTOCORE_POWER_RECOVER_MS 10000
1721#endif
1722
1723/**
1724 * @brief Consecutive I/O failures that declare a removable volume gone.
1725 *
1726 * Not 1: a single failed write is not proof a card left (a transient bus error, a full volume), and
1727 * tearing down a working mount over one error would be its own bug. Any success resets the run.
1728 */
1729#ifndef PROTOCORE_HOTSWAP_FAIL_THRESHOLD
1730#define PROTOCORE_HOTSWAP_FAIL_THRESHOLD 3
1731#endif
1732
1733/** @brief Minimum gap between remount attempts while a volume is absent or faulted (ms). */
1734#ifndef PROTOCORE_HOTSWAP_PROBE_MS
1735#define PROTOCORE_HOTSWAP_PROBE_MS 2000
1736#endif
1737
1738/**
1739 * @brief MTConnect rolling sample buffer sizing (PROTOCORE_ENABLE_MTCONNECT).
1740 *
1741 * The agent retains the most recent ::PROTOCORE_MTC_SAMPLE_BUFFER observations in a fixed ring so a
1742 * subscriber can replay them with the `sample` from/count long-poll cursor (MTC1.4 §6.7): a request
1743 * asks for observations starting at a sequence number, and the response header reports firstSequence /
1744 * lastSequence / nextSequence so the client knows what it received and where to resume. Each retained
1745 * observation stores its type / dataItemId / timestamp / value in fixed char fields; when the ring is
1746 * full the oldest is evicted and firstSequence advances. Zero-heap, compile-time sized; the buffer costs
1747 * ~PROTOCORE_MTC_SAMPLE_BUFFER * (48 + the four string caps) bytes only where a protocore_mtc_sample_buffer is used.
1748 */
1749#ifndef PROTOCORE_MTC_SAMPLE_BUFFER
1750#define PROTOCORE_MTC_SAMPLE_BUFFER 32 // observations retained for `sample` replay
1751#endif
1752
1753#ifndef PROTOCORE_MTC_STR_MAX
1754#define PROTOCORE_MTC_STR_MAX 24 // max stored type / dataItemId length (excl NUL)
1755#endif
1756#ifndef PROTOCORE_MTC_TS_MAX
1757#define PROTOCORE_MTC_TS_MAX 32 // max stored ISO-8601 timestamp length (excl NUL)
1758#endif
1759#ifndef PROTOCORE_MTC_VAL_MAX
1760#define PROTOCORE_MTC_VAL_MAX 32 // max stored observation value length (excl NUL)
1761#endif
1762
1763/**
1764 * @brief The bytes an MTConnect document runs out of: the running context and the observation ring.
1765 *
1766 * One retained observation is its four strings, its sequence and its category, and the slack covers
1767 * the padding the compiler puts between them plus the context in front of the ring. The exact layout
1768 * is mtconnect.c's, and that translation unit includes both and proves this covers it - the same
1769 * arrangement PROTOCORE_SSH_SLOT_BYTES has with the SSH offsets.
1770 */
1771#ifndef PROTOCORE_MTCONNECT_BORROW
1772#define PROTOCORE_MTCONNECT_BORROW \
1773 ((size_t)PROTOCORE_MTC_SAMPLE_BUFFER * \
1774 (2u * (PROTOCORE_MTC_STR_MAX + 1u) + (PROTOCORE_MTC_TS_MAX + 1u) + (PROTOCORE_MTC_VAL_MAX + 1u) + 16u) + \
1775 128u)
1776#endif
1777
1778// The agent is a gated module, so a build without it reserves nothing for the ring.
1779#if PROTOCORE_ENABLE_MTCONNECT
1780#define PROTOCORE_PLAINTEXT_WORK_MTCONNECT PROTOCORE_MTCONNECT_BORROW
1781#else
1782#define PROTOCORE_PLAINTEXT_WORK_MTCONNECT 0
1783#endif
1784
1785/**
1786 * @brief Largest G-code block (one line) the DNC decoder reassembles (PROTOCORE_ENABLE_DNC).
1787 *
1788 * A block longer than this overflows the decoder's fixed line buffer and is dropped whole
1789 * (::DNC_EV_OVERFLOW) rather than truncated. Sized for a normal G-code line; raise it only for
1790 * unusually long blocks (many parameters). Zero heap - this is the static per-decoder buffer.
1791 */
1792#ifndef PROTOCORE_DNC_LINE_MAX
1793#define PROTOCORE_DNC_LINE_MAX 128
1794#endif
1795
1796/**
1797 * @brief Default leader/trailer runout length for the DNC encoder (PROTOCORE_ENABLE_DNC).
1798 *
1799 * The number of NUL runout bytes ::protocore_dnc_encode_leader emits before the program (and can emit after
1800 * it). The reader skips them until the first `%`. Traditional tape leaders were a few inches of
1801 * blank feed; 32 bytes is a serial-link equivalent. Overridable per call via DncCfg::leader_len.
1802 */
1803#ifndef PROTOCORE_DNC_LEADER_LEN
1804#define PROTOCORE_DNC_LEADER_LEN 32
1805#endif
1806
1807/**
1808 * @brief Safety cap on how many times the DNC stream engine polls the reverse channel while paused
1809 * by an XOFF, before giving up with an I/O error (PROTOCORE_ENABLE_DNC).
1810 *
1811 * `dnc_stream` pauses on XOFF and polls `recv` for the XON that resumes it; a well-behaved transport
1812 * paces `recv` (blocks briefly when idle) so this cap is only a backstop against a `recv` that spins
1813 * returning no data forever. Raise it if a slow controller legitimately holds XOFF for a long time.
1814 */
1815#ifndef PROTOCORE_DNC_XOFF_MAX_POLLS
1816#define PROTOCORE_DNC_XOFF_MAX_POLLS 200000
1817#endif
1818
1819/** @brief Max concurrent address:port -> bus rules (server/net/iface_bridge). */
1820#ifndef PROTOCORE_BRIDGE_MAX_RULES
1821#define PROTOCORE_BRIDGE_MAX_RULES 8
1822#endif
1823
1824/**
1825 * @brief Max write / read payload (bytes) per TRANSACTION frame (server/net/iface_bridge).
1826 *
1827 * Bounds the per-transaction stack scratch used to clock an SPI/I2C write-then-read, and rejects a frame
1828 * whose write_len or read_len exceeds it. Device-server transactions are small register accesses, so the
1829 * default is modest; a frame over the cap closes the connection (protocol error). Keep it comfortably
1830 * under the transport RX ring so a whole frame can buffer before it is parsed.
1831 */
1832#ifndef PROTOCORE_BRIDGE_TXN_MAX
1833#define PROTOCORE_BRIDGE_TXN_MAX 256
1834#endif
1835
1836/** @brief STREAM (UART) pipe chunk size (bytes) for server/net/iface_bridge - one socket<->UART hop. */
1837#ifndef PROTOCORE_BRIDGE_STREAM_CHUNK
1838#define PROTOCORE_BRIDGE_STREAM_CHUNK 256
1839#endif
1840
1841/** @brief Chunks a STREAM target moves per poll before yielding, bounding the UART drain loop. */
1842#ifndef PROTOCORE_BRIDGE_MAX_DRAIN
1843#define PROTOCORE_BRIDGE_MAX_DRAIN 8
1844#endif
1845
1846/** @brief UART TRANSACTION read window (ms): how long a write-then-read waits for the read_len reply. */
1847#ifndef PROTOCORE_BRIDGE_UART_TXN_MS
1848#define PROTOCORE_BRIDGE_UART_TXN_MS 50
1849#endif
1850
1851/** @brief Max concurrent rover connections a caster serves corrections to (services/timing_position/gnss). */
1852#ifndef PROTOCORE_NTRIP_MAX_ROVERS
1853#define PROTOCORE_NTRIP_MAX_ROVERS 4
1854#endif
1855
1856// The base surveys in from the receiver's GGA fixes, so the NTRIP caster needs the NMEA 0183 codec.
1857#define PROTOCORE_ENABLE_NTRIP_CASTER_NEEDS_NMEA0183 PROTOCORE_ENABLE_NMEA0183
1858#if PROTOCORE_ENABLE_NTRIP_CASTER && !PROTOCORE_ENABLE_NTRIP_CASTER_NEEDS_NMEA0183
1859#error "ProtoCore: PROTOCORE_ENABLE_NTRIP_CASTER needs PROTOCORE_ENABLE_NMEA0183"
1860#endif
1861
1862/** @brief Max length (incl. NUL) of an NTRIP mountpoint name the caster serves. */
1863#ifndef PROTOCORE_NTRIP_MOUNT_MAX
1864#define PROTOCORE_NTRIP_MOUNT_MAX 32
1865#endif
1866
1867/** @brief Max NTRIP client request size (bytes) the caster buffers while reading the request headers. */
1868#ifndef PROTOCORE_NTRIP_REQ_MAX
1869#define PROTOCORE_NTRIP_REQ_MAX 512
1870#endif
1871
1872/** @brief Max distinct mountpoints a single caster serves (each = one RTCM stream). */
1873#ifndef PROTOCORE_NTRIP_MAX_MOUNTS
1874#define PROTOCORE_NTRIP_MAX_MOUNTS 2
1875#endif
1876
1877/**
1878 * @brief Per-direction relay buffer size (bytes) for server/net/relay (PROTOCORE_ENABLE_RELAY).
1879 *
1880 * Each active relay holds two buffers of this size (one per direction) for bytes read from one peer
1881 * but not yet accepted by the other (backpressure carry). Larger buffers raise throughput per step
1882 * (fewer cross-thread Tcp.conn->send marshals per KB) at the cost of RAM per concurrent relay
1883 * (2 * PROTOCORE_RELAY_BUF * PROTOCORE_RELAY_MAX_CONNS bytes).
1884 */
1885#ifndef PROTOCORE_RELAY_BUF
1886#define PROTOCORE_RELAY_BUF 2048
1887#endif
1888
1889/**
1890 * @brief Max protocore_relay_step passes per poll for the relay listener (PROTOCORE_ENABLE_RELAY).
1891 *
1892 * One poll drains up to this many PROTOCORE_RELAY_BUF chunks per direction, so a single event forwards the
1893 * whole buffered origin RX ring (PROTOCORE_CLIENT_RX_BUF) instead of one chunk - the difference between a
1894 * ~0.4 Mbps and a multi-Mbps port-forward. Bounded so one busy bridge cannot starve the others.
1895 */
1896#ifndef PROTOCORE_RELAY_DRAIN_MAX
1897#define PROTOCORE_RELAY_DRAIN_MAX 8
1898#endif
1899
1900/**
1901 * @brief Max published relay ports (bind table size) for the relay listener (PROTOCORE_ENABLE_RELAY).
1902 *
1903 * Each protocore_relay_publish() call binds one listener port to one origin `host:port`. This caps how
1904 * many distinct ports the device can front at once.
1905 */
1906#ifndef PROTOCORE_RELAY_MAX_PUBLISH
1907#define PROTOCORE_RELAY_MAX_PUBLISH 4
1908#endif
1909
1910/**
1911 * @brief Max concurrent relayed connections (bridge table size) for the relay listener
1912 * (PROTOCORE_ENABLE_RELAY). Each holds a protocore_relay (two PROTOCORE_RELAY_BUF buffers) + an origin slot.
1913 */
1914#ifndef PROTOCORE_RELAY_MAX_CONNS
1915#define PROTOCORE_RELAY_MAX_CONNS 4
1916#endif
1917
1918/** @brief Max origin hostname length (bytes, incl. NUL) stored per published relay port. */
1919#ifndef PROTOCORE_RELAY_HOST_MAX
1920#define PROTOCORE_RELAY_HOST_MAX 64
1921#endif
1922
1923/** @brief Blocking connect timeout (ms) when the relay listener dials the origin on a new inbound. */
1924#ifndef PROTOCORE_RELAY_CONNECT_MS
1925#define PROTOCORE_RELAY_CONNECT_MS 5000
1926#endif
1927
1928/**
1929 * @brief Suggested FTP control-command buffer size (PROTOCORE_ENABLE_FTP).
1930 *
1931 * A convenience cap for callers sizing the buffer they hand `protocore_ftp_build_command`; the builders
1932 * are all length-checked against the caller's `cap`, so this is only a sensible default. Large
1933 * enough for a RETR / STOR with a long path.
1934 */
1935#ifndef PROTOCORE_FTP_CMD_MAX
1936#define PROTOCORE_FTP_CMD_MAX 256
1937#endif
1938
1939/**
1940 * @brief Control-reply accumulator for the FTP session driver (PROTOCORE_ENABLE_FTP_SESSION).
1941 *
1942 * services/ftp_session buffers a whole control reply here before parsing it. Multiline greetings
1943 * and FEAT listings are the large cases; a reply that will not fit is treated as malformed rather
1944 * than waited on forever.
1945 */
1946#ifndef PROTOCORE_FTP_REPLY_BUF
1947#define PROTOCORE_FTP_REPLY_BUF 512
1948#endif
1949
1950/** @brief Bytes staged per data-channel write when the session driver streams a payload. */
1951#ifndef PROTOCORE_FTP_CHUNK
1952#define PROTOCORE_FTP_CHUNK 512
1953#endif
1954
1955/** @brief Per-step timeout for the FTP session driver: connect, and each control reply. */
1956#ifndef PROTOCORE_FTP_TIMEOUT_MS
1957#define PROTOCORE_FTP_TIMEOUT_MS 8000
1958#endif
1959
1960/** @brief Worst-case serialized L2 entry (edge_sd_serialize). */
1961#define PROTOCORE_EDGE_SD_VALUE_MAX \
1962 (1 /*version*/ + 2 /*status*/ + 2 /*body_len*/ + 7u * 2u /*str lengths*/ + PROTOCORE_EDGE_KEY_MAX + \
1963 PROTOCORE_EDGE_CTYPE_MAX + PROTOCORE_EDGE_ETAG_MAX + PROTOCORE_EDGE_LASTMOD_MAX + PROTOCORE_EDGE_CENC_MAX + \
1964 PROTOCORE_EDGE_VARY_MAX + PROTOCORE_EDGE_VARY_MAX + PROTOCORE_EDGE_BODY_MAX)
1965
1966/** @brief Fixed timing trailer prepended to a mesh entry frame (age propagation). */
1967#define PROTOCORE_EDGE_MESH_TRAILER (8 /*date*/ + 8 /*expires*/ + 4 /*lifetime_s*/ + 4 /*age_hdr*/ + 4 /*age*/)
1968
1969/** @brief Worst-case mesh entry frame (trailer + a full serialized entry). */
1970#define PROTOCORE_EDGE_MESH_ENTRY_MAX (PROTOCORE_EDGE_MESH_TRAILER + PROTOCORE_EDGE_SD_VALUE_MAX)
1971
1972/** @brief Worst-case mesh response frame (header + entry on a HIT). */
1973#define PROTOCORE_EDGE_MESH_RESP_MAX (2 + 1 + 1 + 2 + PROTOCORE_EDGE_MESH_ENTRY_MAX)
1974
1975/** @brief Worst-case mesh request frame (bounded request-header snapshot for Vary matching). */
1976#define PROTOCORE_EDGE_MESH_REQ_MAX (2 + 1 + 1 + 32 + 2 + PROTOCORE_EDGE_KEY_MAX + 2 + PROTOCORE_MESH_HDRS_MAX)
1977
1978/* A fetch slot reuses its origin buffer for the mesh query (the two phases never overlap),
1979 * so with the mesh on the buffer must also hold a mesh response. Deriving the floor here is
1980 * what makes PROTOCORE_ENABLE_EDGE_MESH work out of the box instead of failing to compile. */
1981#if PROTOCORE_ENABLE_EDGE_MESH
1982#define PROTOCORE_EDGE_FETCH_BUF_MIN ((PROTOCORE_EDGE_MESH_RESP_MAX) > 2560 ? (PROTOCORE_EDGE_MESH_RESP_MAX) : 2560)
1983#else
1984#define PROTOCORE_EDGE_FETCH_BUF_MIN 2560
1985#endif
1986
1987// PROTOCORE_EDGE_CACHE_SLOTS and PROTOCORE_EDGE_BODY_MAX come from vendor/board_profiles/ (classic floor, raised
1988// per chip/PSRAM by board_profile.h above); override with -D as usual.
1989#ifndef PROTOCORE_EDGE_KEY_MAX
1990#define PROTOCORE_EDGE_KEY_MAX 128 // largest canonical cache key (method\nhost\npath[\nquery])
1991#endif
1992#ifndef PROTOCORE_EDGE_VARY_MAX
1993#define PROTOCORE_EDGE_VARY_MAX 64 // stored Vary field-name list / captured request values (each)
1994#endif
1995
1996/** @brief Stored Content-Type to replay. */
1997#ifndef PROTOCORE_EDGE_CTYPE_MAX
1998#define PROTOCORE_EDGE_CTYPE_MAX 64
1999#endif
2000
2001/** @brief Stored validator (ETag, quotes included). */
2002#ifndef PROTOCORE_EDGE_ETAG_MAX
2003#define PROTOCORE_EDGE_ETAG_MAX 64
2004#endif
2005
2006/** @brief Stored Last-Modified (RFC 1123 date). */
2007#ifndef PROTOCORE_EDGE_LASTMOD_MAX
2008#define PROTOCORE_EDGE_LASTMOD_MAX 40
2009#endif
2010
2011/** @brief Stored Content-Encoding to replay (e.g. gzip). */
2012#ifndef PROTOCORE_EDGE_CENC_MAX
2013#define PROTOCORE_EDGE_CENC_MAX 32
2014#endif
2015#ifndef PROTOCORE_EDGE_MAP_MAX
2016#define PROTOCORE_EDGE_MAP_MAX 4 // path-prefix -> origin route mappings
2017#endif
2018#ifndef PROTOCORE_EDGE_ORIGIN_URL_MAX
2019#define PROTOCORE_EDGE_ORIGIN_URL_MAX 128 // largest origin base URL in a route mapping
2020#endif
2021// PROTOCORE_EDGE_FETCH_SLOTS comes from vendor/board_profiles/ (classic floor, raised per chip/PSRAM).
2022#ifndef PROTOCORE_EDGE_FETCH_BUF
2023#define PROTOCORE_EDGE_FETCH_BUF PROTOCORE_EDGE_FETCH_BUF_MIN // per-fetch origin-response accumulation buffer
2024#endif
2025#ifndef PROTOCORE_EDGE_FETCH_TIMEOUT_MS
2026#define PROTOCORE_EDGE_FETCH_TIMEOUT_MS 8000 // origin fetch deadline before fail-open
2027#endif
2028#ifndef PROTOCORE_EDGE_DEFAULT_TTL_S
2029#define PROTOCORE_EDGE_DEFAULT_TTL_S 60 // fallback freshness when no directive and no wall clock
2030#endif
2031// L2 (SD) tier: when PROTOCORE_ENABLE_DBM is also set, the edge cache spills evicted entries to a dbm store
2032// (edge_cache_sd) so the cached set survives a reboot. Each entry serializes to ~ its body plus ~470 B of
2033// response metadata; for a full-body spill, PROTOCORE_DBM_VAL_MAX must be >= that size (>= PROTOCORE_EDGE_BODY_MAX
2034// + ~470). Entries that do not fit simply stay L1-only, so a small PROTOCORE_DBM_VAL_MAX is safe but persists
2035// less. The L2 key is the 32-byte cache-key digest, so PROTOCORE_DBM_KEY_MAX must be >= 32 (its default).
2036
2037/**
2038 * @brief SMB2 client work-buffer size (bytes) for smb_client's request/response framing.
2039 *
2040 * Two buffers of this size live on the stack during a call, plus a few half-size scratch buffers for
2041 * the NTLM auth tokens, so the engine needs roughly 4x this in stack. 1024 covers the NEGOTIATE ->
2042 * SESSION_SETUP -> TREE_CONNECT -> CREATE handshake; raise it if a server's SPNEGO/target-info token
2043 * or your share path is unusually large.
2044 */
2045#ifndef PROTOCORE_SMB_BUF
2046#define PROTOCORE_SMB_BUF 1024
2047#endif
2048
2049/**
2050 * @brief Chunk the core-dump image is streamed out of flash in (PROTOCORE_EXC_COREDUMP_CHUNK).
2051 *
2052 * protocore_exc_coredump_save() copies the partition to a file this many bytes at a time from a stack
2053 * buffer, so a dump of any size costs no heap and never has to fit RAM.
2054 */
2055#ifndef PROTOCORE_EXC_COREDUMP_CHUNK
2056#define PROTOCORE_EXC_COREDUMP_CHUNK 512
2057#endif
2058
2059/**
2060 * @brief Most asset paths a service-worker precache manifest may list (PROTOCORE_DELIVERY_PRECACHE_MAX).
2061 */
2062#ifndef PROTOCORE_DELIVERY_PRECACHE_MAX
2063#define PROTOCORE_DELIVERY_PRECACHE_MAX 16
2064#endif
2065
2066/**
2067 * @brief Buffer the precache manifest JSON is built into (PROTOCORE_DELIVERY_MANIFEST_BUF).
2068 *
2069 * Must hold `{"version":"..","precache":[..]}` for PROTOCORE_DELIVERY_PRECACHE_MAX paths; the manifest
2070 * route answers 500 rather than truncating if it does not fit.
2071 */
2072#ifndef PROTOCORE_DELIVERY_MANIFEST_BUF
2073#define PROTOCORE_DELIVERY_MANIFEST_BUF 512
2074#endif
2075
2076/**
2077 * @brief Channels tracked by the WiFi sniffer's per-channel survey (PROTOCORE_WIFI_SNIFFER_MAX_CHANNELS).
2078 *
2079 * 14 covers the full 2.4 GHz channel plan (1-14); lower it to the channels actually swept to shrink
2080 * the survey table (each entry is 11 bytes).
2081 */
2082#ifndef PROTOCORE_WIFI_SNIFFER_MAX_CHANNELS
2083#define PROTOCORE_WIFI_SNIFFER_MAX_CHANNELS 14
2084#endif
2085
2086/** @brief I2C address of the FDC2214, set by the ADDR pin: 0x2A when it is low, 0x2B when it is high. */
2087#ifndef PROTOCORE_FDC2214_I2C_ADDR
2088#define PROTOCORE_FDC2214_I2C_ADDR 0x2A
2089#endif
2090
2091/** @brief I2C address of the LDC1614, set by the ADDR pin: 0x2A when it is low, 0x2B when it is high. */
2092#ifndef PROTOCORE_LDC1614_I2C_ADDR
2093#define PROTOCORE_LDC1614_I2C_ADDR 0x2A
2094#endif
2095
2096/** @brief I2C address of the VL53L0X. DS11555 gives the device address as 0x52, which is the 8-bit
2097 * form with the R/W bit in it; on a 7-bit API that is 0x29. */
2098#ifndef PROTOCORE_VL53L0X_I2C_ADDR
2099#define PROTOCORE_VL53L0X_I2C_ADDR 0x29
2100#endif
2101
2102/** @brief Number of log lines retained in the ring. */
2103#ifndef PROTOCORE_LOG_LINES
2104#define PROTOCORE_LOG_LINES 32
2105#endif
2106
2107/** @brief Maximum length of one stored log line (bytes, including null). */
2108#ifndef PROTOCORE_LOG_LINE_LEN
2109#define PROTOCORE_LOG_LINE_LEN 96
2110#endif
2111
2112/**
2113 * @brief Compile-time severity floor for the PROTOCORE_LOG* macros (shared/log/log.h).
2114 *
2115 * The values are ordered low -> high and match protocore_log_level's, so a level is usable both in the
2116 * preprocessor (which cannot see a constexpr) and as the runtime argument. PROTOCORE_NONE sits above ERROR
2117 * so that the default discards everything.
2118 */
2119#define PROTOCORE_LOG_LEVEL_DEBUG 0
2120#define PROTOCORE_LOG_LEVEL_INFO 1
2121#define PROTOCORE_LOG_LEVEL_WARN 2
2122#define PROTOCORE_LOG_LEVEL_ERROR 3
2123#define PROTOCORE_LOG_LEVEL_NONE 4
2124
2125/**
2126 * @brief Lowest severity the PROTOCORE_LOG* macros emit code for.
2127 *
2128 * A call below this floor is discarded by the preprocessor: no call, no formatting, and no format
2129 * string left in flash, because the discarded form only names its arguments inside `sizeof` - an
2130 * unevaluated context that still type-checks them. So instrumentation can be left in the source
2131 * permanently and costs exactly nothing in a build that does not want it, which is the point.
2132 *
2133 * Defaults to PROTOCORE_NONE: opt in per build (e.g. -DPROTOCORE_LOG_LEVEL=PROTOCORE_LOG_LEVEL_WARN).
2134 */
2135#ifndef PROTOCORE_LOG_LEVEL
2136#define PROTOCORE_LOG_LEVEL PROTOCORE_LOG_LEVEL_NONE
2137#endif
2138
2139/** @brief Confirm window (ms): a pending image not confirmed within this rolls back. */
2140#ifndef PROTOCORE_OTA_CONFIRM_WINDOW_MS
2141#define PROTOCORE_OTA_CONFIRM_WINDOW_MS 30000
2142#endif
2143
2144/** @brief WiFi modem-sleep mode: 0 = none (max perf), 1 = min modem, 2 = max modem. */
2145#ifndef PROTOCORE_RADIO_WIFI_PS
2146#define PROTOCORE_RADIO_WIFI_PS 0
2147#endif
2148
2149/** @brief Max TX power cap in dBm (2..20); 0 = leave the platform default. */
2150#ifndef PROTOCORE_RADIO_MAX_TX_DBM
2151#define PROTOCORE_RADIO_MAX_TX_DBM 0
2152#endif
2153
2154/** @brief DNS resolve timeout in milliseconds. */
2155/**
2156 * @brief Nameserver the portable resolver asks when nothing has told it otherwise.
2157 *
2158 * The vendor backend takes its servers from the stack (DHCP), so this is the portable one's only
2159 * starting point. A device that learns a server from DHCP or provisioning should hand it over with
2160 * Resolver.server.ip + Resolver.set_server rather than query this one.
2161 */
2162#ifndef PROTOCORE_DNS_SERVER
2163#define PROTOCORE_DNS_SERVER "9.9.9.9"
2164#endif
2165
2166/** @brief Local UDP port the portable resolver asks from and hears the answer on. */
2167#ifndef PROTOCORE_DNS_CLIENT_PORT
2168#define PROTOCORE_DNS_CLIENT_PORT 1153
2169#endif
2170
2171#ifndef PROTOCORE_DNS_TIMEOUT_MS
2172#define PROTOCORE_DNS_TIMEOUT_MS 5000
2173#endif
2174
2175// Ring depth and per-record message length are tunable in audit_log.h
2176// (PROTOCORE_AUDIT_LOG_ENTRIES, PROTOCORE_AUDIT_MSG_LEN); define them before include to
2177// override. The RAM cost is roughly PROTOCORE_AUDIT_LOG_ENTRIES * (PROTOCORE_AUDIT_MSG_LEN
2178// + 41) bytes.
2179
2180/** @brief Max accepted OIDC ID-token length (also sizes the Authorization buffer). */
2181#ifndef PROTOCORE_OIDC_MAX_LEN
2182#define PROTOCORE_OIDC_MAX_LEN 1600
2183#endif
2184
2185/** @brief NamespaceIndex the umati MachineTool nodes live at (default 1). */
2186#ifndef PROTOCORE_UMATI_NS
2187#define PROTOCORE_UMATI_NS 1
2188#endif
2189
2190/** @brief NamespaceIndex the robotics MotionDeviceSystem nodes live at (default 1). */
2191#ifndef PROTOCORE_ROBOTICS_NS
2192#define PROTOCORE_ROBOTICS_NS 1
2193#endif
2194
2195/** @brief Number of Axes the robotics MotionDevice exposes (default 6; must fit PROTOCORE_OPCUA_REF_MAX). */
2196#ifndef PROTOCORE_ROBOTICS_AXES
2197#define PROTOCORE_ROBOTICS_AXES 6
2198#endif
2199
2200/** @brief NamespaceIndex the EUROMAP 77 IMM_MES_Interface nodes live at (default 1). */
2201#ifndef PROTOCORE_EM77_NS
2202#define PROTOCORE_EM77_NS 1
2203#endif
2204
2205// The RX-ring feature floors (streaming needs a full TCP window, SSH/TLS a full first flight) are
2206// resolved by derived_sizing.h, included at the end of this file once every feature
2207// flag is known - that is the sizing layer's job, not this file's.
2208
2209/** @brief Maximum formatted syslog datagram length in bytes (RFC 5424 line). */
2210#ifndef PROTOCORE_SYSLOG_MSG_MAX
2211#define PROTOCORE_SYSLOG_MSG_MAX 256
2212#endif
2213
2214/** @brief Maximum syslog HOSTNAME / APP-NAME field length (including NUL). */
2215#ifndef PROTOCORE_SYSLOG_FIELD_MAX
2216#define PROTOCORE_SYSLOG_FIELD_MAX 32
2217#endif
2218
2219/** @brief Default syslog collector UDP port (RFC 5426 well-known 514; overridable at runtime
2220 * via Syslog.collector.port + Syslog.init and here for a non-standard collector). */
2221#ifndef PROTOCORE_SYSLOG_DEFAULT_PORT
2222#define PROTOCORE_SYSLOG_DEFAULT_PORT 514
2223#endif
2224
2225/** @brief Maximum accepted JWT length in bytes (header.payload.signature). */
2226#ifndef PROTOCORE_JWT_MAX_LEN
2227#define PROTOCORE_JWT_MAX_LEN 512
2228#endif
2229
2230/** @brief Receive buffer (and max response size) for the outbound HTTP client, bytes. */
2231#ifndef PROTOCORE_HTTP_CLIENT_BUF_SIZE
2232#define PROTOCORE_HTTP_CLIENT_BUF_SIZE 2048
2233#endif
2234
2235/** @brief Outbound HTTP client connect/response timeout in milliseconds. */
2236#ifndef PROTOCORE_HTTP_CLIENT_TIMEOUT_MS
2237#define PROTOCORE_HTTP_CLIENT_TIMEOUT_MS 8000
2238#endif
2239
2240/** @brief Max length of one SMTP command / address line (bytes, incl. CRLF). */
2241#ifndef PROTOCORE_SMTP_LINE_MAX
2242#define PROTOCORE_SMTP_LINE_MAX 256
2243#endif
2244
2245/** @brief Max size of the assembled DATA payload (headers + dot-stuffed body), bytes. */
2246#ifndef PROTOCORE_SMTP_MSG_MAX
2247#define PROTOCORE_SMTP_MSG_MAX 2048
2248#endif
2249
2250/** @brief Max size of one (possibly multi-line) server reply held while parsing, bytes. */
2251#ifndef PROTOCORE_SMTP_REPLY_MAX
2252#define PROTOCORE_SMTP_REPLY_MAX 512
2253#endif
2254
2255/** @brief SMTP connect / per-reply timeout in milliseconds. */
2256#ifndef PROTOCORE_SMTP_TIMEOUT_MS
2257#define PROTOCORE_SMTP_TIMEOUT_MS 10000
2258#endif
2259
2260/** @brief Ciphertext receive-ring size for SMTPS, bytes (only used when the message is TLS). */
2261#ifndef PROTOCORE_SMTP_CT_BUF_SIZE
2262#define PROTOCORE_SMTP_CT_BUF_SIZE 4096
2263#endif
2264
2265/**
2266 * @brief MQTT packet buffer size in bytes (bounds one outgoing/incoming packet).
2267 *
2268 * The client borrows twice this from the secure pool's persistent end and splits it: one half is
2269 * the payload the codec assembles into and the receive reassembly, the other is the wire. Must hold
2270 * the largest CONNECT/PUBLISH the client sends and the largest incoming PUBLISH it accepts
2271 * (topic + payload + a few header bytes); larger incoming packets are dropped.
2272 */
2273#ifndef PROTOCORE_MQTT_BUF_SIZE
2274#define PROTOCORE_MQTT_BUF_SIZE 1024
2275#endif
2276
2277/**
2278 * @brief What the whole MQTT connect is given, in milliseconds.
2279 *
2280 * Covers the transport coming up, the TLS handshake for mqtts, and the CONNACK: Mqtt.connect
2281 * returns immediately and Mqtt.loop gives the Network Connection up once this passes. One budget
2282 * rather than one per stage, because a Server slow in any of them is slow to the caller either way.
2283 */
2284#ifndef PROTOCORE_MQTT_CONNECT_MS
2285#define PROTOCORE_MQTT_CONNECT_MS 8000
2286#endif
2287
2288/** @brief Default MQTT keep-alive interval in seconds (PINGREQ cadence / CONNECT field). */
2289#ifndef PROTOCORE_MQTT_KEEPALIVE_S
2290#define PROTOCORE_MQTT_KEEPALIVE_S 30
2291#endif
2292
2293/** @brief Ciphertext receive-ring size for MQTTS (draining ring; must exceed one TCP_MSS). */
2294#ifndef PROTOCORE_MQTT_CT_BUF_SIZE
2295#define PROTOCORE_MQTT_CT_BUF_SIZE 4096
2296#endif
2297
2298/** @brief Maximum inbound MQTT topic length (including NUL) delivered to the callback. */
2299#ifndef PROTOCORE_MQTT_MAX_TOPIC
2300#define PROTOCORE_MQTT_MAX_TOPIC 128
2301#endif
2302
2303/**
2304 * @brief Outbound QoS 1/2 in-flight slots (unacknowledged exchanges awaiting their acknowledgement).
2305 *
2306 * A slot records the packet identifier, how far the exchange has got and when it was last sent - not
2307 * the packet, which stays in the client's wire buffer where a retransmit marks DUP and rewinds the
2308 * worker to the start of it. A publish is refused when all slots are busy. Each slot costs a
2309 * handful of bytes.
2310 */
2311#ifndef PROTOCORE_MQTT_MAX_INFLIGHT
2312#define PROTOCORE_MQTT_MAX_INFLIGHT 4
2313#endif
2314
2315/** @brief Retransmit timeout (ms) for an unacknowledged in-flight QoS 1/2 message. */
2316#ifndef PROTOCORE_MQTT_RETRANSMIT_MS
2317#define PROTOCORE_MQTT_RETRANSMIT_MS 5000
2318#endif
2319
2320/** @brief Inbound QoS 2 packet-id de-duplication ring depth (PUBREC-acknowledged, awaiting PUBREL). */
2321#ifndef PROTOCORE_MQTT_RX_QOS2_SLOTS
2322#define PROTOCORE_MQTT_RX_QOS2_SLOTS 8
2323#endif
2324
2325/** @brief WebSocket client send/receive buffer size in bytes (bounds one frame). */
2326#ifndef PROTOCORE_WS_CLIENT_BUF_SIZE
2327#define PROTOCORE_WS_CLIENT_BUF_SIZE 1024
2328#endif
2329
2330/** @brief Ciphertext receive-ring size for wss:// (draining ring; must exceed one TCP_MSS). */
2331#ifndef PROTOCORE_WS_CLIENT_CT_BUF_SIZE
2332#define PROTOCORE_WS_CLIENT_CT_BUF_SIZE 4096
2333#endif
2334
2335// Everything that dials out goes through the one TCP client rather than carrying a private
2336// copy of the connect-and-drain pattern, so each of these needs it compiled in. This was an
2337// OR-list that set PROTOCORE_NEED_CLIENT, and a feature missing from the list got a stub
2338// whose open() returns -1 - a build that compiled, linked, and never connected.
2339
2340#define PROTOCORE_ENABLE_HTTP_CLIENT_NEEDS_TCP_CLIENT PROTOCORE_ENABLE_TCP_CLIENT
2341#if PROTOCORE_ENABLE_HTTP_CLIENT && !PROTOCORE_ENABLE_HTTP_CLIENT_NEEDS_TCP_CLIENT
2342#error "ProtoCore: PROTOCORE_ENABLE_HTTP_CLIENT needs PROTOCORE_ENABLE_TCP_CLIENT"
2343#endif
2344
2345#define PROTOCORE_ENABLE_MQTT_NEEDS_TCP_CLIENT PROTOCORE_ENABLE_TCP_CLIENT
2346#if PROTOCORE_ENABLE_MQTT && !PROTOCORE_ENABLE_MQTT_NEEDS_TCP_CLIENT
2347#error "ProtoCore: PROTOCORE_ENABLE_MQTT needs PROTOCORE_ENABLE_TCP_CLIENT"
2348#endif
2349
2350#define PROTOCORE_ENABLE_WS_CLIENT_NEEDS_TCP_CLIENT PROTOCORE_ENABLE_TCP_CLIENT
2351#if PROTOCORE_ENABLE_WS_CLIENT && !PROTOCORE_ENABLE_WS_CLIENT_NEEDS_TCP_CLIENT
2352#error "ProtoCore: PROTOCORE_ENABLE_WS_CLIENT needs PROTOCORE_ENABLE_TCP_CLIENT"
2353#endif
2354
2355#define PROTOCORE_ENABLE_RELAY_NEEDS_TCP_CLIENT PROTOCORE_ENABLE_TCP_CLIENT
2356#if PROTOCORE_ENABLE_RELAY && !PROTOCORE_ENABLE_RELAY_NEEDS_TCP_CLIENT
2357#error "ProtoCore: PROTOCORE_ENABLE_RELAY needs PROTOCORE_ENABLE_TCP_CLIENT"
2358#endif
2359
2360#define PROTOCORE_ENABLE_SMTP_NEEDS_TCP_CLIENT PROTOCORE_ENABLE_TCP_CLIENT
2361#if PROTOCORE_ENABLE_SMTP && !PROTOCORE_ENABLE_SMTP_NEEDS_TCP_CLIENT
2362#error "ProtoCore: PROTOCORE_ENABLE_SMTP needs PROTOCORE_ENABLE_TCP_CLIENT"
2363#endif
2364
2365#define PROTOCORE_ENABLE_SMB_NEEDS_TCP_CLIENT PROTOCORE_ENABLE_TCP_CLIENT
2366#if PROTOCORE_ENABLE_SMB && !PROTOCORE_ENABLE_SMB_NEEDS_TCP_CLIENT
2367#error "ProtoCore: PROTOCORE_ENABLE_SMB needs PROTOCORE_ENABLE_TCP_CLIENT"
2368#endif
2369
2370#define PROTOCORE_ENABLE_DNC_NEEDS_TCP_CLIENT PROTOCORE_ENABLE_TCP_CLIENT
2371#if PROTOCORE_ENABLE_DNC && !PROTOCORE_ENABLE_DNC_NEEDS_TCP_CLIENT
2372#error "ProtoCore: PROTOCORE_ENABLE_DNC needs PROTOCORE_ENABLE_TCP_CLIENT"
2373#endif
2374
2375#define PROTOCORE_ENABLE_FTP_SESSION_NEEDS_TCP_CLIENT PROTOCORE_ENABLE_TCP_CLIENT
2376#if PROTOCORE_ENABLE_FTP_SESSION && !PROTOCORE_ENABLE_FTP_SESSION_NEEDS_TCP_CLIENT
2377#error "ProtoCore: PROTOCORE_ENABLE_FTP_SESSION needs PROTOCORE_ENABLE_TCP_CLIENT"
2378#endif
2379
2380#define PROTOCORE_ENABLE_SSH_CLIENT_NEEDS_TCP_CLIENT PROTOCORE_ENABLE_TCP_CLIENT
2381#if PROTOCORE_ENABLE_SSH_CLIENT && !PROTOCORE_ENABLE_SSH_CLIENT_NEEDS_TCP_CLIENT
2382#error "ProtoCore: PROTOCORE_ENABLE_SSH_CLIENT needs PROTOCORE_ENABLE_TCP_CLIENT"
2383#endif
2384
2385// The client dials by name, so it needs the resolver: one owner of the hostname marshal
2386// instead of a private copy per client.
2387
2388// SSH port forwarding dials the forwarded destination, so it is on the same list. Spelled without
2389// the ENABLE_ infix, which is why it is stated here rather than generated with the rest.
2390#define PROTOCORE_SSH_PORT_FORWARD_NEEDS_TCP_CLIENT PROTOCORE_ENABLE_TCP_CLIENT
2391#if PROTOCORE_SSH_PORT_FORWARD && !PROTOCORE_SSH_PORT_FORWARD_NEEDS_TCP_CLIENT
2392#error "ProtoCore: PROTOCORE_SSH_PORT_FORWARD needs PROTOCORE_ENABLE_TCP_CLIENT"
2393#endif
2394
2395#define PROTOCORE_ENABLE_TCP_CLIENT_NEEDS_DNS_RESOLVER PROTOCORE_ENABLE_DNS_RESOLVER
2396#if PROTOCORE_ENABLE_TCP_CLIENT && !PROTOCORE_ENABLE_TCP_CLIENT_NEEDS_DNS_RESOLVER
2397#error "ProtoCore: PROTOCORE_ENABLE_TCP_CLIENT needs PROTOCORE_ENABLE_DNS_RESOLVER"
2398#endif
2399
2400// The datagram transport's rings only move when Session.tick() drains them, so a feature that
2401// binds a UDP port or sends a datagram needs the transport in the image. This was an OR-list
2402// that set PROTOCORE_NEED_UDP, and a feature missing from it filled its rings and stopped,
2403// with nothing on the wire.
2404
2405#define PROTOCORE_ENABLE_COAP_NEEDS_UDP PROTOCORE_ENABLE_UDP
2406#if PROTOCORE_ENABLE_COAP && !PROTOCORE_ENABLE_COAP_NEEDS_UDP
2407#error "ProtoCore: PROTOCORE_ENABLE_COAP needs PROTOCORE_ENABLE_UDP"
2408#endif
2409
2410#define PROTOCORE_ENABLE_DTLS_NEEDS_UDP PROTOCORE_ENABLE_UDP
2411#if PROTOCORE_ENABLE_DTLS && !PROTOCORE_ENABLE_DTLS_NEEDS_UDP
2412#error "ProtoCore: PROTOCORE_ENABLE_DTLS needs PROTOCORE_ENABLE_UDP"
2413#endif
2414
2415#define PROTOCORE_ENABLE_STATSD_NEEDS_UDP PROTOCORE_ENABLE_UDP
2416#if PROTOCORE_ENABLE_STATSD && !PROTOCORE_ENABLE_STATSD_NEEDS_UDP
2417#error "ProtoCore: PROTOCORE_ENABLE_STATSD needs PROTOCORE_ENABLE_UDP"
2418#endif
2419
2420#define PROTOCORE_ENABLE_UDP_TELEMETRY_NEEDS_UDP PROTOCORE_ENABLE_UDP
2421#if PROTOCORE_ENABLE_UDP_TELEMETRY && !PROTOCORE_ENABLE_UDP_TELEMETRY_NEEDS_UDP
2422#error "ProtoCore: PROTOCORE_ENABLE_UDP_TELEMETRY needs PROTOCORE_ENABLE_UDP"
2423#endif
2424
2425#define PROTOCORE_ENABLE_SNMP_NEEDS_UDP PROTOCORE_ENABLE_UDP
2426#if PROTOCORE_ENABLE_SNMP && !PROTOCORE_ENABLE_SNMP_NEEDS_UDP
2427#error "ProtoCore: PROTOCORE_ENABLE_SNMP needs PROTOCORE_ENABLE_UDP"
2428#endif
2429
2430#define PROTOCORE_ENABLE_SNMP_TRAP_NEEDS_UDP PROTOCORE_ENABLE_UDP
2431#if PROTOCORE_ENABLE_SNMP_TRAP && !PROTOCORE_ENABLE_SNMP_TRAP_NEEDS_UDP
2432#error "ProtoCore: PROTOCORE_ENABLE_SNMP_TRAP needs PROTOCORE_ENABLE_UDP"
2433#endif
2434
2435#define PROTOCORE_ENABLE_SNMP_V3_NEEDS_UDP PROTOCORE_ENABLE_UDP
2436#if PROTOCORE_ENABLE_SNMP_V3 && !PROTOCORE_ENABLE_SNMP_V3_NEEDS_UDP
2437#error "ProtoCore: PROTOCORE_ENABLE_SNMP_V3 needs PROTOCORE_ENABLE_UDP"
2438#endif
2439
2440#define PROTOCORE_ENABLE_SYSLOG_NEEDS_UDP PROTOCORE_ENABLE_UDP
2441#if PROTOCORE_ENABLE_SYSLOG && !PROTOCORE_ENABLE_SYSLOG_NEEDS_UDP
2442#error "ProtoCore: PROTOCORE_ENABLE_SYSLOG needs PROTOCORE_ENABLE_UDP"
2443#endif
2444
2445#define PROTOCORE_ENABLE_FLOW_EXPORT_NEEDS_UDP PROTOCORE_ENABLE_UDP
2446#if PROTOCORE_ENABLE_FLOW_EXPORT && !PROTOCORE_ENABLE_FLOW_EXPORT_NEEDS_UDP
2447#error "ProtoCore: PROTOCORE_ENABLE_FLOW_EXPORT needs PROTOCORE_ENABLE_UDP"
2448#endif
2449
2450#define PROTOCORE_ENABLE_PROVISIONING_NEEDS_UDP PROTOCORE_ENABLE_UDP
2451#if PROTOCORE_ENABLE_PROVISIONING && !PROTOCORE_ENABLE_PROVISIONING_NEEDS_UDP
2452#error "ProtoCore: PROTOCORE_ENABLE_PROVISIONING needs PROTOCORE_ENABLE_UDP"
2453#endif
2454
2455#define PROTOCORE_ENABLE_NTP_SERVER_NEEDS_UDP PROTOCORE_ENABLE_UDP
2456#if PROTOCORE_ENABLE_NTP_SERVER && !PROTOCORE_ENABLE_NTP_SERVER_NEEDS_UDP
2457#error "ProtoCore: PROTOCORE_ENABLE_NTP_SERVER needs PROTOCORE_ENABLE_UDP"
2458#endif
2459
2460#define PROTOCORE_ENABLE_DNS_SERVER_NEEDS_UDP PROTOCORE_ENABLE_UDP
2461#if PROTOCORE_ENABLE_DNS_SERVER && !PROTOCORE_ENABLE_DNS_SERVER_NEEDS_UDP
2462#error "ProtoCore: PROTOCORE_ENABLE_DNS_SERVER needs PROTOCORE_ENABLE_UDP"
2463#endif
2464
2465#define PROTOCORE_ENABLE_HTTP3_NEEDS_UDP PROTOCORE_ENABLE_UDP
2466#if PROTOCORE_ENABLE_HTTP3 && !PROTOCORE_ENABLE_HTTP3_NEEDS_UDP
2467#error "ProtoCore: PROTOCORE_ENABLE_HTTP3 needs PROTOCORE_ENABLE_UDP"
2468#endif
2469
2470// ---------------------------------------------------------------------------
2471// Full Authorization-header capture (internal)
2472// ---------------------------------------------------------------------------
2473// Digest auth and JWT bearer tokens both carry an Authorization value far longer
2474// than MAX_VAL_LEN, so the parser captures the whole header into a dedicated
2475// per-request buffer (HttpReq::authorization) when either feature is enabled.
2476
2477/** @brief True when the parser must capture the full Authorization header value. */
2478#if PROTOCORE_ENABLE_AUTH || PROTOCORE_ENABLE_JWT || PROTOCORE_ENABLE_OIDC
2479#define PROTOCORE_CAPTURE_AUTH_HEADER 1
2480#else
2481#define PROTOCORE_CAPTURE_AUTH_HEADER 0
2482#endif
2483
2484/**
2485 * @brief Capacity of HttpReq::authorization (full Authorization header value).
2486 *
2487 * Sized to the largest enabled consumer: a Digest header (DIGEST_AUTH_HDR_MAX), a
2488 * `Bearer <jwt>` HS256 token (PROTOCORE_JWT_MAX_LEN), or a `Bearer <id_token>` OIDC
2489 * RS256 token (PROTOCORE_OIDC_MAX_LEN), each plus the scheme.
2490 */
2491#if PROTOCORE_ENABLE_OIDC
2492#define PROTOCORE_AUTH_HDR_CAP_OIDC (PROTOCORE_OIDC_MAX_LEN + 16)
2493#else
2494#define PROTOCORE_AUTH_HDR_CAP_OIDC 0
2495#endif
2496#if PROTOCORE_ENABLE_JWT
2497#define PROTOCORE_AUTH_HDR_CAP_JWT (PROTOCORE_JWT_MAX_LEN + 16)
2498#else
2499#define PROTOCORE_AUTH_HDR_CAP_JWT 0
2500#endif
2501#define PROTOCORE_AUTH_HDR_CAP_M1 \
2502 (PROTOCORE_AUTH_HDR_CAP_JWT > DIGEST_AUTH_HDR_MAX ? PROTOCORE_AUTH_HDR_CAP_JWT : DIGEST_AUTH_HDR_MAX)
2503#define PROTOCORE_AUTH_HDR_CAP \
2504 (PROTOCORE_AUTH_HDR_CAP_OIDC > PROTOCORE_AUTH_HDR_CAP_M1 ? PROTOCORE_AUTH_HDR_CAP_OIDC : PROTOCORE_AUTH_HDR_CAP_M1)
2505
2506/**
2507 * @brief Stack scratch for protocore_web_terminal_println() line building.
2508 *
2509 * One formatted terminal line must fit in this many bytes (longer is truncated).
2510 * Allocated on the stack only during the call - no persistent RAM cost.
2511 */
2512#ifndef TERM_TX_BUF_SIZE
2513#define TERM_TX_BUF_SIZE 256
2514#endif
2515
2516/**
2517 * @brief Maximum requests served on one keep-alive connection before it is closed.
2518 *
2519 * A fairness bound so a single client cannot hold a connection slot
2520 * indefinitely with a steady request stream. After this many responses the
2521 * server emits `Connection: close` and drops the link; the client simply
2522 * reconnects. Only meaningful when PROTOCORE_ENABLE_KEEPALIVE is set.
2523 */
2524#ifndef PROTOCORE_KEEPALIVE_MAX_REQUESTS
2525#define PROTOCORE_KEEPALIVE_MAX_REQUESTS 100
2526#endif
2527
2528/**
2529 * @brief Per-connection HPACK dynamic-table size in bytes (our decoder; advertised to the peer
2530 * as SETTINGS_HEADER_TABLE_SIZE). RFC 7541's default is 4096; lower it to save per-connection
2531 * RAM (each active HTTP/2 connection holds one table).
2532 */
2533#ifndef PROTOCORE_HPACK_TABLE_BYTES
2534#define PROTOCORE_HPACK_TABLE_BYTES 4096
2535#endif
2536
2537/** @brief Max HPACK dynamic-table entries (>= PROTOCORE_HPACK_TABLE_BYTES / 32, the min entry size). */
2538#ifndef PROTOCORE_HPACK_MAX_ENTRIES
2539#define PROTOCORE_HPACK_MAX_ENTRIES 128
2540#endif
2541
2542/**
2543 * @brief Largest HTTP/2 frame we accept, in bytes (advertised as SETTINGS_MAX_FRAME_SIZE). RFC
2544 * 9113 requires accepting at least 16384; a whole frame is buffered for reassembly, so this
2545 * (plus the HPACK table) sets the per-HTTP/2-connection RAM. Range: [16384, 16777215].
2546 */
2547#ifndef PROTOCORE_H2_MAX_FRAME
2548#define PROTOCORE_H2_MAX_FRAME 16384
2549#endif
2550
2551/** @brief Max concurrent HTTP/2 streams per connection (advertised as MAX_CONCURRENT_STREAMS). */
2552#ifndef PROTOCORE_H2_MAX_STREAMS
2553#define PROTOCORE_H2_MAX_STREAMS 8
2554#endif
2555
2556/**
2557 * @brief Header-block reassembly buffer for HTTP/2 requests that span HEADERS + CONTINUATION
2558 * frames (a single END_HEADERS frame decodes in place and needs no copy). Caps the compressed
2559 * request-header size; a larger block is rejected (RFC 9113 sec 6.10).
2560 */
2561#ifndef PROTOCORE_H2_HDR_BLOCK
2562#define PROTOCORE_H2_HDR_BLOCK 4096
2563#endif
2564
2565/**
2566 * @brief CONTINUATION frames one header block may span (RFC 9113 sec 6.10).
2567 *
2568 * PROTOCORE_H2_HDR_BLOCK bounds the bytes a block may carry, but an empty CONTINUATION adds no bytes, so
2569 * a peer can send them without end and never reach that bound. This bounds the frame count as
2570 * well, which is what makes the block terminate.
2571 */
2572#ifndef PROTOCORE_H2_MAX_CONTINUATION
2573#define PROTOCORE_H2_MAX_CONTINUATION 8
2574#endif
2575
2576/**
2577 * @brief Largest datagram a DTLS handshake flight will put on the wire, before a connection
2578 * overrides it (RFC 9147 sec 4.3).
2579 *
2580 * A handshake message longer than this is split across fragments that each fit one datagram. The
2581 * default is the IPv6 minimum MTU less the worst-case IPv6 and UDP headers, which no path is
2582 * allowed to be smaller than; a connection that knows its own path sets DtlsServerConfig::pmtu.
2583 */
2584#ifndef PROTOCORE_DTLS_PMTU_DEFAULT
2585#define PROTOCORE_DTLS_PMTU_DEFAULT 1232
2586#endif
2587
2588/**
2589 * @brief Place the HTTP/2 connection-engine pool in external PSRAM (ESP32).
2590 *
2591 * Each HTTP/2 connection needs a ~28 KB engine, so the pool (MAX_CONNS of them) does not fit the
2592 * ~122 KB internal DRAM alongside a TLS server - HTTP/2 therefore requires PSRAM. Set this to 1
2593 * on a PSRAM board (S3 / P4 / WROVER) to move the pool to external RAM via `EXT_RAM_BSS_ATTR`.
2594 * Like PROTOCORE_TLS_ARENA_IN_PSRAM it needs a framework built with
2595 * `CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y` (the stock arduino-esp32 core ships it off; see
2596 * tools/psram/README.md). A compile-time guard rejects PROTOCORE_ENABLE_HTTP2 without this on ARDUINO.
2597 */
2598#ifndef PROTOCORE_H2_POOL_IN_PSRAM
2599#define PROTOCORE_H2_POOL_IN_PSRAM 0
2600#endif
2601
2602// Internal request-dispatch slots appended to the connection pool for non-TCP transports.
2603// HTTP/3 runs over QUIC/UDP and has no accept-time TCP slot, but it reuses the same request
2604// pipeline (match_and_execute + send), which is indexed by a connection-pool slot. One reserved
2605// slot at index MAX_CONNS lets an HTTP/3 request run through that pipeline. The TCP accept path only
2606// ever scans [0, MAX_CONNS), and this slot is driven synchronously by the HTTP/3 poll on the worker
2607// thread, so there is no accept race. CONN_POOL_SLOTS sizes conn_pool / http_pool / the per-slot
2608// response-header buffer; every TCP loop still bounds itself with MAX_CONNS.
2609#if PROTOCORE_ENABLE_HTTP3
2610#define PROTOCORE_INTERNAL_SLOTS 1
2611#define PROTOCORE_H3_DISPATCH_SLOT MAX_CONNS ///< reserved conn-pool slot an HTTP/3 request dispatches through
2612#else
2613#define PROTOCORE_INTERNAL_SLOTS 0
2614#endif
2615#define CONN_POOL_SLOTS (MAX_CONNS + PROTOCORE_INTERNAL_SLOTS)
2616
2617/** @brief UDP port the HTTP/3 (QUIC) server binds by default (used by protocore_h3_cert). */
2618#ifndef PROTOCORE_HTTP3_PORT
2619#define PROTOCORE_HTTP3_PORT 443
2620#endif
2621
2622/**
2623 * @brief Maximum bytes of one QUIC/TLS handshake CRYPTO flight (RFC 9001).
2624 *
2625 * The server's second flight - EncryptedExtensions + Certificate + CertificateVerify + Finished -
2626 * is assembled whole before it is fragmented into CRYPTO frames across Handshake packets. The
2627 * Certificate (a DER X.509 chain) dominates the size, so this bounds the certificate the server can
2628 * present. The default fits a single Ed25519 leaf certificate comfortably; raise it for a chain.
2629 */
2630#ifndef PROTOCORE_H3_CRYPTO_BUF
2631#define PROTOCORE_H3_CRYPTO_BUF 2048
2632#endif
2633
2634/**
2635 * @brief Maximum concurrent request streams per HTTP/3 connection.
2636 *
2637 * Bounds the per-connection QUIC stream table (client-initiated bidirectional request streams plus
2638 * the handful of unidirectional control / QPACK streams). Each slot is small; 8 matches the HTTP/2
2639 * default (PROTOCORE_H2_MAX_STREAMS).
2640 */
2641#ifndef PROTOCORE_H3_MAX_STREAMS
2642#define PROTOCORE_H3_MAX_STREAMS 8
2643#endif
2644
2645/** @brief Simultaneous HTTP/3 connections. Each is a QuicConn plus an H3Conn. */
2646#ifndef PROTOCORE_QUIC_MAX_CONNS
2647#define PROTOCORE_QUIC_MAX_CONNS 2
2648#endif
2649
2650/** @brief Datagrams buffered from the lwIP thread until the server poll drains them. */
2651#ifndef PROTOCORE_QUIC_INGEST_RING
2652#define PROTOCORE_QUIC_INGEST_RING 8
2653#endif
2654
2655/** @brief Largest UDP payload the QUIC transport sends or accepts (conservative, under a 1500 MTU). */
2656#ifndef PROTOCORE_QUIC_MAX_DATAGRAM
2657#define PROTOCORE_QUIC_MAX_DATAGRAM 1350
2658#endif
2659
2660// What one HTTP/3 request stream holds: the frames it reassembles and the three pseudo-headers it
2661// captures out of them. Every one is a power of two, so a stream reaches its own bytes with a shift.
2662#ifndef PROTOCORE_H3_STREAM_BUF
2663#define PROTOCORE_H3_STREAM_BUF 2048 ///< per-request-stream reassembly buffer (HEADERS + DATA)
2664#endif
2665#ifndef PROTOCORE_H3_PATH_LEN
2666#define PROTOCORE_H3_PATH_LEN 256 ///< captured :path length cap
2667#endif
2668#ifndef PROTOCORE_H3_AUTHORITY_LEN
2669#define PROTOCORE_H3_AUTHORITY_LEN 128 ///< captured :authority length cap
2670#endif
2671#ifndef PROTOCORE_H3_METHOD_LEN
2672#define PROTOCORE_H3_METHOD_LEN 16 ///< captured :method length cap
2673#endif
2674// What QPACK decodes a field section through, and what a response field section is encoded into.
2675// Both are taken from the plaintext pool's transient end while one call runs.
2676#ifndef PROTOCORE_H3_QPACK_SCRATCH
2677#define PROTOCORE_H3_QPACK_SCRATCH 512
2678#endif
2679#ifndef PROTOCORE_H3_QPACK_BLOCK
2680#define PROTOCORE_H3_QPACK_BLOCK 256
2681#endif
2682// What the QUIC transport under HTTP/3 holds per connection: the bytes owed to each stream, and the
2683// in-order CRYPTO window per packet-number space. Both are powers of two, so a stream and a space
2684// reach their own bytes with a shift.
2685#ifndef PROTOCORE_QUIC_STREAM_TX
2686#define PROTOCORE_QUIC_STREAM_TX 2048 ///< per-stream outbound buffer (drained into STREAM frames)
2687#endif
2688#ifndef PROTOCORE_QUIC_CRYPTO_RX
2689#define PROTOCORE_QUIC_CRYPTO_RX 2048 ///< per-level inbound CRYPTO reassembly window (ClientHello, Finished)
2690#endif
2691#ifndef PROTOCORE_QUIC_MAX_STREAMS
2692#define PROTOCORE_QUIC_MAX_STREAMS PROTOCORE_H3_MAX_STREAMS ///< tracked streams (request + control/QPACK)
2693#endif
2694
2695/**
2696 * @brief Enforce the RFC 7230 §5.4 Host-header requirement (default on).
2697 *
2698 * When 1, an HTTP/1.1 request that lacks a Host header - or carries more than
2699 * one - is rejected with 400 Bad Request. When 0, the Host header is not
2700 * required (useful for constrained clients or test harnesses that feed bare
2701 * request lines). The multiple-Host rule and Content-Length validation are
2702 * always active regardless of this flag.
2703 */
2704#ifndef PROTOCORE_ENFORCE_HOST_HEADER
2705#define PROTOCORE_ENFORCE_HOST_HEADER 1
2706#endif
2707
2708/**
2709 * @brief Allow SSH password authentication (default on).
2710 *
2711 * Set to 0 to harden the SSH server to publickey-only authentication
2712 * (RFC 4252 §7): the "password" method is then refused outright and is not
2713 * advertised in the USERAUTH_FAILURE method list. Publickey auth is always
2714 * available regardless of this flag.
2715 */
2716#ifndef PROTOCORE_SSH_ALLOW_PASSWORD
2717#define PROTOCORE_SSH_ALLOW_PASSWORD 1
2718#endif
2719
2720/**
2721 * @brief Maximum failed SSH authentication attempts per connection.
2722 *
2723 * RFC 4252 §4 permits the server to disconnect after a small bounded number of
2724 * failed USERAUTH_REQUESTs. After this many SSH_MSG_USERAUTH_FAILURE responses
2725 * on one connection the server sends SSH_MSG_DISCONNECT and drops the link.
2726 * (The publickey "would-be-accepted" probe and a SUCCESS do not count.)
2727 */
2728#ifndef SSH_MAX_AUTH_ATTEMPTS
2729#define SSH_MAX_AUTH_ATTEMPTS 6
2730#endif
2731
2732// Minimum spacing between password-change attempts (RFC 4252 sec 8). A change runs the caller's
2733// storage write, so a request inside this window is answered as a failure (busy) rather than run.
2734#ifndef PROTOCORE_SSH_PW_CHANGE_COOLDOWN_MS
2735#define PROTOCORE_SSH_PW_CHANGE_COOLDOWN_MS 60000u
2736#endif
2737
2738/**
2739 * @brief Where the SSH RSA host private key is stored: the NVS namespace and the item in it.
2740 *
2741 * The server reads a DER-encoded PKCS#1/PKCS#8 blob from here at startup. Provisioning writes it
2742 * once per device (docs/SSH.md). Both names are within the 15-character NVS limit.
2743 */
2744#ifndef PROTOCORE_SSH_HOST_KEY_NS
2745#define PROTOCORE_SSH_HOST_KEY_NS "ssh_host_key"
2746#endif
2747#ifndef PROTOCORE_SSH_HOST_KEY_ITEM
2748#define PROTOCORE_SSH_HOST_KEY_ITEM "priv_der"
2749#endif
2750
2751// ---------------------------------------------------------------------------
2752// Listener pool
2753// ---------------------------------------------------------------------------
2754
2755/** @brief Maximum number of simultaneously active listener ports. */
2756#ifndef MAX_LISTENERS
2757#define MAX_LISTENERS 3
2758#endif
2759
2760/**
2761 * @brief Maximum simultaneously bound UDP ports (transport-layer UDP service).
2762 *
2763 * Sizes the fixed pool in udp.cpp. One slot per bound port, e.g. SNMP
2764 * (:161) and the captive-portal DNS responder (:53). Costs only a few pointers
2765 * of BSS each.
2766 */
2767#ifndef PROTOCORE_MAX_UDP_LISTENERS
2768#define PROTOCORE_MAX_UDP_LISTENERS 2
2769#endif
2770
2771/**
2772 * @brief Largest UDP datagram a bound port accepts, in bytes.
2773 *
2774 * Bounds one datagram, both directions: a longer inbound datagram is truncated to this at the
2775 * receive trampoline, and a longer send is refused. Sizes the per-slot staging buffer the drain
2776 * hands the handler. Must hold the largest datagram any UDP service expects (SNMP messages are the
2777 * largest user).
2778 */
2779#ifndef PROTOCORE_UDP_RX_BUF_SIZE
2780#define PROTOCORE_UDP_RX_BUF_SIZE 1472
2781#endif
2782
2783/**
2784 * @brief Per-slot UDP receive ring, in bytes.
2785 *
2786 * Backs one bound port's receive ring. The stack's trampoline frames each datagram into it and the
2787 * drain reads them out, so this is how many bytes of datagram, plus a header each, may wait between
2788 * two poll() calls. A datagram that does not fit the free space is dropped.
2789 */
2790#ifndef PROTOCORE_UDP_RX_RING
2791#define PROTOCORE_UDP_RX_RING 2048
2792#endif
2793
2794/** @brief Max accepted connections per throttle window (see PROTOCORE_ENABLE_ACCEPT_THROTTLE). */
2795#ifndef PROTOCORE_ACCEPT_THROTTLE_MAX
2796#define PROTOCORE_ACCEPT_THROTTLE_MAX 20
2797#endif
2798
2799/** @brief Throttle window length in milliseconds (see PROTOCORE_ENABLE_ACCEPT_THROTTLE). */
2800#ifndef PROTOCORE_ACCEPT_THROTTLE_WINDOW_MS
2801#define PROTOCORE_ACCEPT_THROTTLE_WINDOW_MS 1000
2802#endif
2803
2804/** @brief Number of source IPv4 addresses tracked by the per-IP throttle (BSS bucket table). */
2805#ifndef PROTOCORE_PER_IP_THROTTLE_SLOTS
2806#define PROTOCORE_PER_IP_THROTTLE_SLOTS 16
2807#endif
2808
2809/** @brief Max accepted connections per window from one source IP (see PROTOCORE_ENABLE_PER_IP_THROTTLE). */
2810#ifndef PROTOCORE_PER_IP_THROTTLE_MAX
2811#define PROTOCORE_PER_IP_THROTTLE_MAX 10
2812#endif
2813
2814/** @brief Per-IP throttle window length in milliseconds (see PROTOCORE_ENABLE_PER_IP_THROTTLE). */
2815#ifndef PROTOCORE_PER_IP_THROTTLE_WINDOW_MS
2816#define PROTOCORE_PER_IP_THROTTLE_WINDOW_MS 10000
2817#endif
2818
2819// ---------------------------------------------------------------------------
2820// Source-IP allowlist (accept-time firewall; PROTOCORE_ENABLE_IP_ALLOWLIST)
2821// ---------------------------------------------------------------------------
2822
2823/** @brief Number of CIDR rules the source-IP allowlist can hold (BSS table). */
2824#ifndef PROTOCORE_IP_ALLOWLIST_SLOTS
2825#define PROTOCORE_IP_ALLOWLIST_SLOTS 8
2826#endif
2827
2828// ---------------------------------------------------------------------------
2829// Trusted reverse-proxy forwarded-client resolution (PROTOCORE_ENABLE_FORWARDED_TRUST)
2830// ---------------------------------------------------------------------------
2831
2832/** @brief Number of trusted-upstream CIDR rules the forwarded-client resolver holds (BSS table). */
2833#ifndef PROTOCORE_TRUSTED_PROXY_MAX
2834#define PROTOCORE_TRUSTED_PROXY_MAX 2
2835#endif
2836
2837// ---------------------------------------------------------------------------
2838// Brute-force auth lockout (per-source-IP; PROTOCORE_ENABLE_AUTH_LOCKOUT)
2839// ---------------------------------------------------------------------------
2840
2841/** @brief Number of source IPs the auth lockout tracks (BSS bucket table). */
2842#ifndef PROTOCORE_AUTH_LOCKOUT_SLOTS
2843#define PROTOCORE_AUTH_LOCKOUT_SLOTS 16
2844#endif
2845
2846/** @brief Consecutive failed auths from one IP before it is locked out. */
2847#ifndef PROTOCORE_AUTH_LOCKOUT_THRESHOLD
2848#define PROTOCORE_AUTH_LOCKOUT_THRESHOLD 5
2849#endif
2850
2851/** @brief First lockout duration in ms; doubles on each further failure. */
2852#ifndef PROTOCORE_AUTH_LOCKOUT_BASE_MS
2853#define PROTOCORE_AUTH_LOCKOUT_BASE_MS 1000
2854#endif
2855
2856/** @brief Maximum lockout duration in ms (the exponential backoff cap). */
2857#ifndef PROTOCORE_AUTH_LOCKOUT_MAX_MS
2858#define PROTOCORE_AUTH_LOCKOUT_MAX_MS 300000
2859#endif
2860
2861// ---------------------------------------------------------------------------
2862// CSRF protection (PROTOCORE_ENABLE_CSRF)
2863// ---------------------------------------------------------------------------
2864
2865// ---------------------------------------------------------------------------
2866// Telnet sizing constants (PROTOCORE_ENABLE_TELNET must be 1)
2867// ---------------------------------------------------------------------------
2868
2869/** @brief Maximum simultaneous Telnet connections. */
2870#ifndef MAX_TELNET_CONNS
2871#define MAX_TELNET_CONNS 2
2872#endif
2873
2874/** @brief Stack buffer for one Telnet I/O chunk. */
2875#ifndef TELNET_BUF_SIZE
2876#define TELNET_BUF_SIZE 256
2877#endif
2878
2879// ---------------------------------------------------------------------------
2880// SSH sizing constants (PROTOCORE_ENABLE_SSH must be 1)
2881// ---------------------------------------------------------------------------
2882
2883/** @brief Maximum simultaneous SSH connections. */
2884#ifndef MAX_SSH_CONNS
2885#define MAX_SSH_CONNS 1
2886#endif
2887
2888/**
2889 * @brief One connection's whole span: the wire, the session, the exchange, the packet and the rx
2890 * regions end to end, which ssh.c hands out one slot at a time.
2891 *
2892 * Stated here as a number because the offsets that sum to it are built in
2893 * network_drivers/presentation/ssh/common.h, which this file cannot see. common.h is the translation
2894 * unit that includes both, so it is where this is proved against the real SSH_SLOT_BORROW - the same
2895 * arrangement PROTOCORE_SSH_CPUB_MAX has with the PQC key sizes.
2896 */
2897#ifndef PROTOCORE_SSH_SLOT_BYTES
2898#define PROTOCORE_SSH_SLOT_BYTES 183616u
2899#endif
2900
2901/** @brief Every slot's span together: the bytes ssh.c takes from the secure pool. */
2902#ifndef PROTOCORE_SSH_BORROW
2903#define PROTOCORE_SSH_BORROW ((size_t)MAX_SSH_CONNS * PROTOCORE_SSH_SLOT_BYTES)
2904#endif
2905
2906/**
2907 * @brief SSH TCP port forwarding (`direct-tcpip`, i.e. `ssh -L`). Default off.
2908 *
2909 * When set, the SSH server can open an outbound TCP connection to a client-named
2910 * host:port and bridge bytes between that socket and the SSH channel - the
2911 * `ssh_forward` owner does the I/O via the outbound client transport (protocore_client),
2912 * so it needs `PROTOCORE_CLIENT_CONNS >= PROTOCORE_SSH_FWD_MAX` and a channel pool
2913 * (`PROTOCORE_SSH_MAX_CHANNELS > 1`) to be useful. Forwarding is still opt-in at
2914 * runtime: nothing is forwarded until the application calls `protocore_ssh_forward_begin()`.
2915 * Off = the channel codec refuses every `direct-tcpip` open (no open relay).
2916 */
2917#ifndef PROTOCORE_SSH_PORT_FORWARD
2918#define PROTOCORE_SSH_PORT_FORWARD 0
2919#endif
2920
2921/** @brief Maximum concurrent forwarded TCP connections (must be <= PROTOCORE_CLIENT_CONNS). */
2922#ifndef PROTOCORE_SSH_FWD_MAX
2923#define PROTOCORE_SSH_FWD_MAX 2
2924#endif
2925
2926/** @brief Maximum forward target hostname length including null terminator. */
2927#ifndef PROTOCORE_SSH_FWD_HOST_MAX
2928#define PROTOCORE_SSH_FWD_HOST_MAX 64
2929#endif
2930
2931/** @brief Blocking connect timeout (ms) when opening a forward target. */
2932#ifndef PROTOCORE_SSH_FWD_CONNECT_MS
2933#define PROTOCORE_SSH_FWD_CONNECT_MS 3000
2934#endif
2935
2936/** @brief Max bytes moved per forward channel per poll, target -> client (<= SSH_PKT_BUF_SIZE). */
2937#ifndef PROTOCORE_SSH_FWD_CHUNK
2938#define PROTOCORE_SSH_FWD_CHUNK 1024
2939#endif
2940
2941/**
2942 * @brief Maximum concurrent remote-forward listeners (`ssh -R` / `tcpip-forward`).
2943 *
2944 * Each accepted client that requests remote forwarding can bind up to this many
2945 * ports on the device; each binding consumes one `listener_pool[]` slot, so
2946 * `MAX_LISTENERS` must have that much headroom above the app's own listeners.
2947 * Remote forwarding shares `PROTOCORE_SSH_PORT_FORWARD` (compiled in) and is inert
2948 * until `protocore_ssh_forward_begin()`.
2949 */
2950#ifndef PROTOCORE_SSH_RFWD_MAX
2951#define PROTOCORE_SSH_RFWD_MAX 1
2952#endif
2953
2954/**
2955 * @brief Maximum concurrent bridged connections across all remote forwards.
2956 *
2957 * Each connection accepted on a forwarded port occupies one transport `conn_pool`
2958 * slot plus one SSH channel (so it needs `PROTOCORE_SSH_MAX_CHANNELS` headroom) and one
2959 * entry here while it is bridged back to the client.
2960 */
2961#ifndef PROTOCORE_SSH_RFWD_BRIDGE_MAX
2962#define PROTOCORE_SSH_RFWD_BRIDGE_MAX 2
2963#endif
2964
2965/**
2966 * @brief Packet assembly buffer per SSH connection (bytes).
2967 *
2968 * RFC 4253 sec 6.1: every implementation MUST process an uncompressed payload of 32768 bytes and a
2969 * total packet of 35000. A smaller value rejects a conforming peer's legal packet.
2970 */
2971#ifndef SSH_PKT_BUF_SIZE
2972#define SSH_PKT_BUF_SIZE 2048
2973#endif
2974
2975/** @brief Max concurrent open SFTP handles (files + dirs) per SSH connection. */
2976#ifndef PROTOCORE_SFTP_MAX_HANDLES
2977#define PROTOCORE_SFTP_MAX_HANDLES 4
2978#endif
2979
2980/** @brief SFTP packet-assembly buffer per SFTP channel (bytes); bounds one non-streamed request/response. */
2981#ifndef PROTOCORE_SFTP_PKT_BUF
2982#define PROTOCORE_SFTP_PKT_BUF 2048
2983#endif
2984
2985/**
2986 * @brief Largest PROTOCORE_SSH_FXP_DATA payload returned for one READ (a short read - the client re-requests). Kept
2987 * within one SSH packet (SSH_PKT_BUF_SIZE minus framing), so bump SSH_PKT_BUF_SIZE too for throughput.
2988 */
2989#ifndef PROTOCORE_SFTP_MAX_READ
2990#define PROTOCORE_SFTP_MAX_READ 1024
2991#endif
2992
2993/** @brief Largest absolute path the SFTP/SCP server resolves (mount root + request path). */
2994#ifndef PROTOCORE_FILESYSTEM_PATH_MAX
2995#define PROTOCORE_FILESYSTEM_PATH_MAX 256
2996#endif
2997
2998/**
2999 * @brief Largest serialized SSH_FXP_NAME entry one READDIR emits: filename, `ls -l` longname and
3000 * attributes. Sizes the per-handle stash an entry that did not fit is held in, which is a
3001 * field of SftpHandle in session.h, so it is stated here rather than in the server's own .c.
3002 */
3003#ifndef PROTOCORE_SFTP_ENTRY_MAX
3004#define PROTOCORE_SFTP_ENTRY_MAX (PROTOCORE_FILESYSTEM_PATH_MAX + 320)
3005#endif
3006
3007/**
3008 * @brief Place the per-connection SSH compression state in external PSRAM (ESP32).
3009 *
3010 * Like PROTOCORE_H2_POOL_IN_PSRAM / PROTOCORE_TLS_ARENA_IN_PSRAM: moves the compressor pool
3011 * (MAX_SSH_CONNS of them) to external RAM via `EXT_RAM_BSS_ATTR`. Needs a framework built with
3012 * `CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y` (tools/psram/README.md).
3013 */
3014#ifndef PROTOCORE_SSH_ZLIB_IN_PSRAM
3015#define PROTOCORE_SSH_ZLIB_IN_PSRAM 0
3016#endif
3017
3018/**
3019 * @brief Acknowledge placing the SSH compressor in internal DRAM (no PSRAM).
3020 *
3021 * The per-connection compressor is ~48 KB. With MAX_SSH_CONNS=1 and no TLS server it fits internal
3022 * DRAM on a roomy chip (S3 / P4). Rather than force PSRAM, this mirrors PROTOCORE_TLS_ACK_MULTI_CONN_DRAM:
3023 * set it to 1 to consciously accept the internal-DRAM cost when PROTOCORE_SSH_ZLIB_IN_PSRAM is off. The
3024 * build otherwise fails fast on ARDUINO with guidance (below) instead of a raw linker overflow.
3025 */
3026#ifndef PROTOCORE_SSH_ZLIB_ACK_DRAM
3027#define PROTOCORE_SSH_ZLIB_ACK_DRAM 0
3028#endif
3029
3030/**
3031 * @brief SSH s2c DEFLATE sliding-window size in bytes (max back-reference distance). Power of two,
3032 * 256..32768. Larger = better ratio + more per-connection RAM (the compressor holds a window-sized
3033 * work buffer + a window-sized hash chain). The client always allocates a 32 KB inflate window, so
3034 * any value here interoperates; 8 KB is a good ratio/RAM balance for terminal + command output.
3035 */
3036#ifndef PROTOCORE_SSH_ZLIB_WINDOW
3037#define PROTOCORE_SSH_ZLIB_WINDOW 8192
3038#endif
3039
3040/**
3041 * @brief Largest uncompressed payload the s2c compressor accepts in one call (bytes). Outbound SSH
3042 * payloads are bounded by SSH_PKT_BUF_SIZE; this sizes the compressor's history+input work buffer.
3043 */
3044#ifndef PROTOCORE_SSH_ZLIB_MAX_IN
3045#define PROTOCORE_SSH_ZLIB_MAX_IN 2048
3046#endif
3047
3048/** @brief Maximum SSH username length including null terminator. */
3049#ifndef SSH_MAX_USERNAME_LEN
3050#define SSH_MAX_USERNAME_LEN 32
3051#endif
3052
3053/** @brief Maximum SSH password length including null terminator. */
3054#ifndef SSH_MAX_PASSWORD_LEN
3055#define SSH_MAX_PASSWORD_LEN 64
3056#endif
3057
3058/**
3059 * @brief Size in bytes of the shared per-dispatch scratch arena.
3060 *
3061 * Codec / protocol handlers borrow transient working memory from this single BSS
3062 * arena (see mmgr/plaintext.h) instead of each feature owning a
3063 * dedicated buffer. The session layer empties it before every event dispatch, so
3064 * it only needs to hold the *peak concurrent* scratch of any one dispatch, not
3065 * the sum across features. Tune from the protocore_plaintext_high_water() reading on a real
3066 * workload; an over-budget borrow fails closed (protocore_plaintext_alloc returns NULL).
3067 */
3068// The deepest nest is the SSH receive path with compression on: ssh_recv_ctr_emac holds
3069// SSH_PKT_BUF_SIZE + 64 across ssh_dispatch_payload, which holds SSH_PKT_BUF_SIZE across
3070// protocore_ssh_server_dispatch, which holds a SSH_PKT_BUF_SIZE reply across the switch, under which
3071// protocore_ssh_auth_handle_pubkey holds 2,552 - 8,760 bytes live together.
3072// The transient end: what a request, a response body and a codec work out of while a call runs.
3073#ifndef PROTOCORE_PLAINTEXT_SCRATCH
3074#define PROTOCORE_PLAINTEXT_SCRATCH 10240
3075#endif
3076
3077#if PROTOCORE_ENABLE_HTTP3
3078#define PROTOCORE_PLAINTEXT_WORK_H3CONN \
3079 (PROTOCORE_WORK_H3_CONN + PROTOCORE_WORK_QUIC_CONN + PROTOCORE_QUIC_SERVER_BORROW)
3080#else
3081#define PROTOCORE_PLAINTEXT_WORK_H3CONN 0
3082#endif
3083
3084#if PROTOCORE_ENABLE_EDGE_CACHE
3085#define PROTOCORE_PLAINTEXT_WORK_EDGEPROXY PROTOCORE_EDGE_PROXY_BORROW
3086#else
3087#define PROTOCORE_PLAINTEXT_WORK_EDGEPROXY 0
3088#endif
3089
3090#if PROTOCORE_ENABLE_EUROMAP77
3091#define PROTOCORE_PLAINTEXT_WORK_EUROMAP77 PROTOCORE_EUROMAP77_BORROW
3092#else
3093#define PROTOCORE_PLAINTEXT_WORK_EUROMAP77 0
3094#endif
3095
3096#if PROTOCORE_ENABLE_UMATI
3097#define PROTOCORE_PLAINTEXT_WORK_UMATI PROTOCORE_UMATI_BORROW
3098#else
3099#define PROTOCORE_PLAINTEXT_WORK_UMATI 0
3100#endif
3101
3102#if PROTOCORE_ENABLE_ROBOTICS
3103#define PROTOCORE_PLAINTEXT_WORK_ROBOTICS PROTOCORE_ROBOTICS_BORROW
3104#else
3105#define PROTOCORE_PLAINTEXT_WORK_ROBOTICS 0
3106#endif
3107
3108#if PROTOCORE_ENABLE_SIMATIC
3109#define PROTOCORE_PLAINTEXT_WORK_SIMATIC PROTOCORE_SIMATIC_BORROW
3110#else
3111#define PROTOCORE_PLAINTEXT_WORK_SIMATIC 0
3112#endif
3113
3114#if PROTOCORE_ENABLE_J1939
3115#define PROTOCORE_PLAINTEXT_WORK_J1939 PROTOCORE_J1939_BORROW
3116#else
3117#define PROTOCORE_PLAINTEXT_WORK_J1939 0
3118#endif
3119
3120#if PROTOCORE_ENABLE_MODBUS
3121#define PROTOCORE_PLAINTEXT_WORK_MODBUS PROTOCORE_MODBUS_BORROW
3122#else
3123#define PROTOCORE_PLAINTEXT_WORK_MODBUS 0
3124#endif
3125
3126#if PROTOCORE_ENABLE_ESPNOW
3127#define PROTOCORE_PLAINTEXT_WORK_ESPNOW PROTOCORE_ESPNOW_BORROW
3128#else
3129#define PROTOCORE_PLAINTEXT_WORK_ESPNOW 0
3130#endif
3131
3132#if PROTOCORE_ENABLE_PROMISC
3133#define PROTOCORE_PLAINTEXT_WORK_PROMISC PROTOCORE_PROMISC_BORROW
3134#else
3135#define PROTOCORE_PLAINTEXT_WORK_PROMISC 0
3136#endif
3137
3138#if PROTOCORE_ENABLE_WIFI_SNIFFER
3139#define PROTOCORE_PLAINTEXT_WORK_WIFISNIFF PROTOCORE_WIFI_SNIFFER_BORROW
3140#else
3141#define PROTOCORE_PLAINTEXT_WORK_WIFISNIFF 0
3142#endif
3143
3144#if PROTOCORE_ENABLE_RADIO_POWER
3145#define PROTOCORE_PLAINTEXT_WORK_RADIOPOWER PROTOCORE_RADIO_POWER_BORROW
3146#else
3147#define PROTOCORE_PLAINTEXT_WORK_RADIOPOWER 0
3148#endif
3149
3150#if PROTOCORE_ENABLE_DNS_SERVER
3151#define PROTOCORE_PLAINTEXT_WORK_DNSSERVER PROTOCORE_DNS_SERVER_BORROW
3152#else
3153#define PROTOCORE_PLAINTEXT_WORK_DNSSERVER 0
3154#endif
3155
3156#if PROTOCORE_ENABLE_FORWARD
3157#define PROTOCORE_PLAINTEXT_WORK_FORWARD PROTOCORE_FORWARD_BORROW
3158#else
3159#define PROTOCORE_PLAINTEXT_WORK_FORWARD 0
3160#endif
3161
3162// The session layer's per-protocol handler table, one pointer per registered ProtoHandler. Measured
3163// at 96 bytes for the default PROTO_MAX_HANDLERS of 12, and scales with it. ProtoRegistryNs reads
3164// the same table through the same borrow: the registry holds nothing of its own. No key material,
3165// so the plaintext end.
3166#ifndef PROTOCORE_SESSION_BORROW
3167#define PROTOCORE_SESSION_BORROW ((size_t)PROTO_MAX_HANDLERS * 8u + 32u)
3168#endif
3169
3170#define PROTOCORE_PLAINTEXT_WORK_SESSION PROTOCORE_SESSION_BORROW
3171
3172// The signalling layer's link state and the counters around it. Measured at 28 bytes. No key
3173// material, so the plaintext end.
3174#ifndef PROTOCORE_SIGNALING_BORROW
3175#define PROTOCORE_SIGNALING_BORROW 64u
3176#endif
3177
3178#define PROTOCORE_PLAINTEXT_WORK_SIGNALING PROTOCORE_SIGNALING_BORROW
3179
3180// The trace ring one capture fills. Measured at 176 bytes. No key material, so the plaintext end.
3181#ifndef PROTOCORE_TRACE_CAPTURE_BORROW
3182#define PROTOCORE_TRACE_CAPTURE_BORROW 256u
3183#endif
3184
3185#if PROTOCORE_ENABLE_TRACE_CAPTURE
3186#define PROTOCORE_PLAINTEXT_WORK_TRACECAPTURE PROTOCORE_TRACE_CAPTURE_BORROW
3187#else
3188#define PROTOCORE_PLAINTEXT_WORK_TRACECAPTURE 0
3189#endif
3190
3191// The power manager's current mode and the two flags around it. Measured at 3 bytes. No key material, so the plaintext
3192// end.
3193#ifndef PROTOCORE_POWER_MGMT_BORROW
3194#define PROTOCORE_POWER_MGMT_BORROW 8u
3195#endif
3196
3197#if PROTOCORE_ENABLE_POWER_MGMT
3198#define PROTOCORE_PLAINTEXT_WORK_POWERMGMT PROTOCORE_POWER_MGMT_BORROW
3199#else
3200#define PROTOCORE_PLAINTEXT_WORK_POWERMGMT 0
3201#endif
3202
3203// The guardrail counters one pass trips against. Measured at 8 bytes. No key material, so the plaintext end.
3204#ifndef PROTOCORE_GUARDRAILS_BORROW
3205#define PROTOCORE_GUARDRAILS_BORROW 16u
3206#endif
3207
3208#if PROTOCORE_ENABLE_GUARDRAILS
3209#define PROTOCORE_PLAINTEXT_WORK_GUARDRAILS PROTOCORE_GUARDRAILS_BORROW
3210#else
3211#define PROTOCORE_PLAINTEXT_WORK_GUARDRAILS 0
3212#endif
3213
3214// The failsafe's armed state and what it reverts to. Measured at 208 bytes. No key material, so the plaintext end.
3215#ifndef PROTOCORE_FAILSAFE_BORROW
3216#define PROTOCORE_FAILSAFE_BORROW 256u
3217#endif
3218
3219#if PROTOCORE_ENABLE_FAILSAFE
3220#define PROTOCORE_PLAINTEXT_WORK_FAILSAFE PROTOCORE_FAILSAFE_BORROW
3221#else
3222#define PROTOCORE_PLAINTEXT_WORK_FAILSAFE 0
3223#endif
3224
3225// Every worker's task handle, its deferred-callback queue and that queue's storage, plus the pump
3226// each runs and the flag that stops them. Scales with both maxima: measured at 160 bytes for one
3227// worker and 1136 for eight, at the default queue depth of 8. No key material, so the plaintext end.
3228#ifndef PROTOCORE_WORKER_BORROW
3229#define PROTOCORE_WORKER_BORROW \
3230 ((size_t)PROTOCORE_WORKER_COUNT * ((size_t)PROTOCORE_DEFER_QUEUE_DEPTH * 16u + 64u) + 64u)
3231#endif
3232
3233#if PROTOCORE_ENABLE_PREEMPT_QUEUE
3234#define PROTOCORE_PLAINTEXT_WORK_WORKER PROTOCORE_WORKER_BORROW
3235#else
3236#define PROTOCORE_PLAINTEXT_WORK_WORKER 0
3237#endif
3238
3239// The preemption queue's entries and its head and tail. Measured at 408 bytes. No key material, so the plaintext end.
3240#ifndef PROTOCORE_PREEMPT_QUEUE_BORROW
3241#define PROTOCORE_PREEMPT_QUEUE_BORROW 512u
3242#endif
3243
3244#if PROTOCORE_ENABLE_PREEMPT_QUEUE
3245#define PROTOCORE_PLAINTEXT_WORK_PREEMPTQUEUE PROTOCORE_PREEMPT_QUEUE_BORROW
3246#else
3247#define PROTOCORE_PLAINTEXT_WORK_PREEMPTQUEUE 0
3248#endif
3249
3250// The log ring: PROTOCORE_LOG_LINES lines of PROTOCORE_LOG_LINE_LEN, their severities, and the
3251// trap. Measured at 3120 bytes. No key material, so the plaintext end.
3252#ifndef PROTOCORE_LOGBUF_BORROW
3253#define PROTOCORE_LOGBUF_BORROW 3584u
3254#endif
3255
3256#if PROTOCORE_ENABLE_LOGBUF
3257#define PROTOCORE_PLAINTEXT_WORK_LOGBUF PROTOCORE_LOGBUF_BORROW
3258#else
3259#define PROTOCORE_PLAINTEXT_WORK_LOGBUF 0
3260#endif
3261
3262// The flow exporter's cursor into the datagram it is filling. Measured at 40 bytes. Flow records
3263// carry no key material, so the plaintext end.
3264#ifndef PROTOCORE_FLOW_EXPORT_BORROW
3265#define PROTOCORE_FLOW_EXPORT_BORROW 64u
3266#endif
3267
3268#if PROTOCORE_ENABLE_FLOW_EXPORT
3269#define PROTOCORE_PLAINTEXT_WORK_FLOWEXPORT PROTOCORE_FLOW_EXPORT_BORROW
3270#else
3271#define PROTOCORE_PLAINTEXT_WORK_FLOWEXPORT 0
3272#endif
3273
3274// The syslog sender's collector address, facility and the one line it formats. Measured at 342
3275// bytes. No key material, so the plaintext end.
3276#ifndef PROTOCORE_SYSLOG_BORROW
3277#define PROTOCORE_SYSLOG_BORROW 512u
3278#endif
3279
3280#if PROTOCORE_ENABLE_SYSLOG
3281#define PROTOCORE_PLAINTEXT_WORK_SYSLOG PROTOCORE_SYSLOG_BORROW
3282#else
3283#define PROTOCORE_PLAINTEXT_WORK_SYSLOG 0
3284#endif
3285
3286// The trap sender's request id and the PDU it builds. Measured at 1028 bytes. A v3 trap is signed
3287// and may be encrypted with the USM keys, so the secure end.
3288#ifndef PROTOCORE_SNMP_NOTIFY_BORROW
3289#define PROTOCORE_SNMP_NOTIFY_BORROW 1536u
3290#endif
3291
3292#if PROTOCORE_ENABLE_SNMP_TRAP
3293#define PROTOCORE_SECURE_WORK_SNMPNOTIFY PROTOCORE_SNMP_NOTIFY_BORROW
3294#else
3295#define PROTOCORE_SECURE_WORK_SNMPNOTIFY 0
3296#endif
3297
3298// The HTTP client's receive buffer, target and built request. Measured at 3060 bytes. A request
3299// line carries Authorization headers and a response its bodies, so the secure end.
3300#ifndef PROTOCORE_HTTP_CLIENT_BORROW
3301#define PROTOCORE_HTTP_CLIENT_BORROW 3584u
3302#endif
3303
3304#if PROTOCORE_ENABLE_HTTP_CLIENT
3305#define PROTOCORE_SECURE_WORK_HTTPCLIENT PROTOCORE_HTTP_CLIENT_BORROW
3306#else
3307#define PROTOCORE_SECURE_WORK_HTTPCLIENT 0
3308#endif
3309
3310// The SMTP session's command and reply lines, with the base64 AUTH client response (RFC 4954 sec
3311// 4). Measured at 3104 bytes. That response is a credential, so the secure end.
3312#ifndef PROTOCORE_SMTP_BORROW
3313#define PROTOCORE_SMTP_BORROW 3584u
3314#endif
3315
3316#if PROTOCORE_ENABLE_SMTP
3317#define PROTOCORE_SECURE_WORK_SMTP PROTOCORE_SMTP_BORROW
3318#else
3319#define PROTOCORE_SECURE_WORK_SMTP 0
3320#endif
3321
3322// The WebSocket client's three rings: received octets, the assembled packet and what is queued to
3323// send. Measured at 4144 bytes. Over wss those rings hold the cleartext, so the secure end.
3324#ifndef PROTOCORE_WS_CLIENT_BORROW
3325#define PROTOCORE_WS_CLIENT_BORROW 4608u
3326#endif
3327
3328#if PROTOCORE_ENABLE_WS_CLIENT
3329#define PROTOCORE_SECURE_WORK_WSCLIENT PROTOCORE_WS_CLIENT_BORROW
3330#else
3331#define PROTOCORE_SECURE_WORK_WSCLIENT 0
3332#endif
3333
3334// The USM engine: its engine id and boots, the user, and the auth and privacy keys derived for it
3335// (RFC 3414). Measured at 8680 bytes. Key material, so the secure end.
3336#ifndef PROTOCORE_SNMP_V3_BORROW
3337#define PROTOCORE_SNMP_V3_BORROW 9216u
3338#endif
3339
3340#if PROTOCORE_ENABLE_SNMP_V3
3341#define PROTOCORE_SECURE_WORK_SNMPV3 PROTOCORE_SNMP_V3_BORROW
3342#else
3343#define PROTOCORE_SECURE_WORK_SNMPV3 0
3344#endif
3345
3346// The agent's MIB table, its read and write community strings, and the varbinds one request walks.
3347// Measured at 12512 bytes. A community string is a credential, so the secure end.
3348#ifndef PROTOCORE_SNMP_AGENT_BORROW
3349#define PROTOCORE_SNMP_AGENT_BORROW 13312u
3350#endif
3351
3352#if PROTOCORE_ENABLE_SNMP
3353#define PROTOCORE_SECURE_WORK_SNMPAGENT PROTOCORE_SNMP_AGENT_BORROW
3354#else
3355#define PROTOCORE_SECURE_WORK_SNMPAGENT 0
3356#endif
3357
3358// The OAuth 2.0 request body and the token-endpoint reply. Measured at 3072 bytes. The body carries
3359// the RFC 6749 sec 2.3.1 client password and the reply the issued tokens, so the secure end. Only
3360// the transport calls own it, and those need PROTOCORE_ENABLE_HTTP_CLIENT.
3361#ifndef PROTOCORE_OAUTH2_BORROW
3362#define PROTOCORE_OAUTH2_BORROW 3072u
3363#endif
3364
3365#if PROTOCORE_ENABLE_OAUTH2 && PROTOCORE_ENABLE_HTTP_CLIENT
3366#define PROTOCORE_SECURE_WORK_OAUTH2 PROTOCORE_OAUTH2_BORROW
3367#else
3368#define PROTOCORE_SECURE_WORK_OAUTH2 0
3369#endif
3370
3371// The filesystem's bound roots and the two buffers a path is resolved into. Measured at 2684
3372// bytes. Paths and mount names, so the plaintext end.
3373#ifndef PROTOCORE_FILESYSTEM_BORROW
3374#define PROTOCORE_FILESYSTEM_BORROW 3072u
3375#endif
3376
3377#define PROTOCORE_PLAINTEXT_WORK_FILESYSTEM PROTOCORE_FILESYSTEM_BORROW
3378
3379// The southbound driver table: each driver's name, its point range and the callbacks that reach
3380// it. Measured at 72 bytes. Field device addresses, so the plaintext end.
3381#ifndef PROTOCORE_SOUTHBOUND_BORROW
3382#define PROTOCORE_SOUTHBOUND_BORROW 128u
3383#endif
3384
3385#if PROTOCORE_ENABLE_SOUTHBOUND
3386#define PROTOCORE_PLAINTEXT_WORK_SOUTHBOUND PROTOCORE_SOUTHBOUND_BORROW
3387#else
3388#define PROTOCORE_PLAINTEXT_WORK_SOUTHBOUND 0
3389#endif
3390
3391// The SCP server's bound root, its registration flag and one control line's filename. Upload
3392// paths, so the plaintext end.
3393#ifndef PROTOCORE_SSH_SCP_BORROW
3394#define PROTOCORE_SSH_SCP_BORROW ((size_t)PROTOCORE_FILESYSTEM_PATH_MAX + 16u)
3395#endif
3396
3397#if PROTOCORE_ENABLE_SSH_SCP
3398#define PROTOCORE_PLAINTEXT_WORK_SSHSCP PROTOCORE_SSH_SCP_BORROW
3399#else
3400#define PROTOCORE_PLAINTEXT_WORK_SSHSCP 0
3401#endif
3402
3403/**
3404 * @brief One SSH connection's zlib@openssh.com compressor: both streams and their windows.
3405 *
3406 * Stated here as a number because it sums a deflate window, its hash chain, the fixed Huffman
3407 * tables and a 32 KB inflate context-takeover window, none of which this file can see - they are
3408 * built in ssh/transport/comp/comp.c, which is the translation unit that includes both and is where
3409 * this is proved against the real SshCompCtx. Same arrangement as PROTOCORE_SSH_SLOT_BYTES.
3410 */
3411#ifndef PROTOCORE_SSH_COMP_SLOT_BYTES
3412#define PROTOCORE_SSH_COMP_SLOT_BYTES 81000u
3413#endif
3414
3415/** @brief Every connection's compressor together: the bytes comp.c takes from the plaintext pool. */
3416#ifndef PROTOCORE_SSH_COMP_BORROW
3417#define PROTOCORE_SSH_COMP_BORROW ((size_t)MAX_SSH_CONNS * PROTOCORE_SSH_COMP_SLOT_BYTES)
3418#endif
3419
3420// Compression is a negotiated extra, so the term is the borrow only where the streams are built.
3421#if PROTOCORE_ENABLE_SSH_ZLIB
3422#define PROTOCORE_PLAINTEXT_WORK_SSHCOMP PROTOCORE_SSH_COMP_BORROW
3423#else
3424#define PROTOCORE_PLAINTEXT_WORK_SSHCOMP 0
3425#endif
3426
3427// The SFTP server's bound root and registration flag, its one response-build buffer, the READ
3428// scratch, two request paths, one serialized READDIR entry with its longname and the entry's own
3429// name, and a handle on its way into a HANDLE response. Measured at 5008 bytes for the default
3430// SSH_PKT_BUF_SIZE, PROTOCORE_SFTP_MAX_READ and PROTOCORE_FILESYSTEM_PATH_MAX, and scales with all
3431// three. File bytes and paths, no key material, so the plaintext end.
3432#ifndef PROTOCORE_SSH_SFTP_BORROW
3433#define PROTOCORE_SSH_SFTP_BORROW \
3434 ((size_t)SSH_PKT_BUF_SIZE + PROTOCORE_SFTP_MAX_READ + 5u * PROTOCORE_FILESYSTEM_PATH_MAX + 656u)
3435#endif
3436
3437#if PROTOCORE_ENABLE_SSH_SFTP
3438#define PROTOCORE_PLAINTEXT_WORK_SSHSFTP PROTOCORE_SSH_SFTP_BORROW
3439#else
3440#define PROTOCORE_PLAINTEXT_WORK_SSHSFTP 0
3441#endif
3442
3443// The static file server's bound accessor root. Measured at 4 bytes; the per-slot transfer state
3444// this pages out of is session's, not here. A handle, no key material, so the plaintext end.
3445#ifndef PROTOCORE_FILE_SERVING_BORROW
3446#define PROTOCORE_FILE_SERVING_BORROW 16u
3447#endif
3448
3449#if PROTOCORE_ENABLE_FILE_SERVING
3450#define PROTOCORE_PLAINTEXT_WORK_FILESERVING PROTOCORE_FILE_SERVING_BORROW
3451#else
3452#define PROTOCORE_PLAINTEXT_WORK_FILESERVING 0
3453#endif
3454
3455// The HTTP request parser's streaming-body hooks: the three callbacks an application installs to
3456// take a body as it arrives. Measured at 24 bytes. Function pointers, no key material, so the
3457// plaintext end. The per-slot request table is http_pool[], which is not a borrow.
3458#ifndef PROTOCORE_HTTP_PARSER_BORROW
3459#define PROTOCORE_HTTP_PARSER_BORROW 32u
3460#endif
3461
3462// The parser took a gate when every module did, so the term follows it. It defaults on, so this
3463// changes no build that exists - it is the arm a build that turns it off would otherwise pay for.
3464#if PROTOCORE_ENABLE_HTTP_PARSER
3465#define PROTOCORE_PLAINTEXT_WORK_HTTPPARSER PROTOCORE_HTTP_PARSER_BORROW
3466#else
3467#define PROTOCORE_PLAINTEXT_WORK_HTTPPARSER 0
3468#endif
3469
3470// The adaptive mDNS announcer's live state: its config, the beacon interval, the contention window,
3471// the running frame total the promiscuous sink bumps, and the channel capture is pinned to.
3472// Measured at 80 bytes. Beacon timing, no key material, so the plaintext end.
3473#ifndef PROTOCORE_MDNS_ADAPTIVE_BORROW
3474#define PROTOCORE_MDNS_ADAPTIVE_BORROW 96u
3475#endif
3476
3477#if PROTOCORE_ENABLE_MDNS_ADAPTIVE
3478#define PROTOCORE_PLAINTEXT_WORK_MDNSADAPTIVE PROTOCORE_MDNS_ADAPTIVE_BORROW
3479#else
3480#define PROTOCORE_PLAINTEXT_WORK_MDNSADAPTIVE 0
3481#endif
3482
3483// The upload service's in-flight transfer: the sink the streamed body is handed to and the byte
3484// count the last one carried. Measured at 32 bytes. Upload bookkeeping, no key material, so the
3485// plaintext end.
3486#ifndef PROTOCORE_UPLOAD_SERVICE_BORROW
3487#define PROTOCORE_UPLOAD_SERVICE_BORROW 48u
3488#endif
3489
3490#if PROTOCORE_ENABLE_UPLOAD
3491#define PROTOCORE_PLAINTEXT_WORK_UPLOADSERVICE PROTOCORE_UPLOAD_SERVICE_BORROW
3492#else
3493#define PROTOCORE_PLAINTEXT_WORK_UPLOADSERVICE 0
3494#endif
3495
3496// The SNTP client's session: the epoch the last accepted reply carried, the millisecond it arrived
3497// so the monotonic clock can carry it between syncs, the cookie that reply had to echo, and the
3498// request span held in flight. Measured at 48 bytes. A wall clock and an anti-spoof cookie, no key
3499// material, so the plaintext end.
3500#ifndef PROTOCORE_NTP_SERVICE_BORROW
3501#define PROTOCORE_NTP_SERVICE_BORROW 64u
3502#endif
3503
3504#if PROTOCORE_ENABLE_NTP
3505#define PROTOCORE_PLAINTEXT_WORK_NTPSERVICE PROTOCORE_NTP_SERVICE_BORROW
3506#else
3507#define PROTOCORE_PLAINTEXT_WORK_NTPSERVICE 0
3508#endif
3509
3510// The rendered `Date` header value, held between the render and the caller reading it. One
3511// IMF-fixdate plus its NUL, so it is ::PROTOCORE_HTTP_DATE_MAX and nothing else - the size is a
3512// property of RFC 7231's fixed-width format rather than a tuning choice. A timestamp, no key
3513// material, so the plaintext end. Taken only where a build emits the header at all.
3514#ifndef PROTOCORE_HTTP_CLOCK_BORROW
3515#define PROTOCORE_HTTP_CLOCK_BORROW 32u
3516#endif
3517
3518#if PROTOCORE_ENABLE_HTTP_CLOCK
3519#define PROTOCORE_PLAINTEXT_WORK_HTTPCLOCK PROTOCORE_HTTP_CLOCK_BORROW
3520#else
3521#define PROTOCORE_PLAINTEXT_WORK_HTTPCLOCK 0
3522#endif
3523
3524// The NTP server's advertised stratum and reference id. Measured at 8 bytes. Clock metadata, no
3525// key material, so the plaintext end.
3526#ifndef PROTOCORE_NTP_SERVER_BORROW
3527#define PROTOCORE_NTP_SERVER_BORROW 16u
3528#endif
3529
3530#if PROTOCORE_ENABLE_NTP_SERVER
3531#define PROTOCORE_PLAINTEXT_WORK_NTPSERVER PROTOCORE_NTP_SERVER_BORROW
3532#else
3533#define PROTOCORE_PLAINTEXT_WORK_NTPSERVER 0
3534#endif
3535
3536// The mDNS responder's advertised host name, the services and TXT pairs it answers with, and the
3537// UDP binding it answers on. Measured at 304 bytes. Service names, no key material, so the
3538// plaintext end.
3539#ifndef PROTOCORE_MDNS_SERVICE_BORROW
3540#define PROTOCORE_MDNS_SERVICE_BORROW 320u
3541#endif
3542
3543#if PROTOCORE_ENABLE_MDNS
3544#define PROTOCORE_PLAINTEXT_WORK_MDNSSERVICE PROTOCORE_MDNS_SERVICE_BORROW
3545#else
3546#define PROTOCORE_PLAINTEXT_WORK_MDNSSERVICE 0
3547#endif
3548
3549// The Telnet console's per-slot NVT table, the read scratch a slot's bytes are staged in for the
3550// IAC walk, the command callback and the row a call is bound to. Measured at 1568 bytes for the
3551// default MAX_TELNET_CONNS and RX_BUF_SIZE, and scales with both. Console lines, no key material,
3552// so the plaintext end.
3553#ifndef PROTOCORE_TELNET_BORROW
3554#define PROTOCORE_TELNET_BORROW ((size_t)MAX_TELNET_CONNS * (TELNET_BUF_SIZE + 16u) + (size_t)RX_BUF_SIZE + 32u)
3555#endif
3556
3557#if PROTOCORE_ENABLE_TELNET
3558#define PROTOCORE_PLAINTEXT_WORK_TELNET PROTOCORE_TELNET_BORROW
3559#else
3560#define PROTOCORE_PLAINTEXT_WORK_TELNET 0
3561#endif
3562
3563// The HTTP connection glue's read scratch, where a slot's available bytes are staged for the
3564// parser, plus the per-slot pump the application installs. Measured at 1032 bytes for the default
3565// RX_BUF_SIZE of 1024, and scales with it. Request bytes, not key material, so the plaintext end.
3566#ifndef PROTOCORE_HTTP_CONN_BORROW
3567#define PROTOCORE_HTTP_CONN_BORROW ((size_t)RX_BUF_SIZE + 32u)
3568#endif
3569
3570#define PROTOCORE_PLAINTEXT_WORK_HTTPCONN PROTOCORE_HTTP_CONN_BORROW
3571
3572// The HTTP surface's registered handlers: the not-found handler, and the edge-cache fetch pump when
3573// that capability is built. Measured at 8 bytes, 16 with PROTOCORE_ENABLE_EDGE_CACHE. Function
3574// pointers, no key material, so the plaintext end.
3575#ifndef PROTOCORE_HTTP_BORROW
3576#define PROTOCORE_HTTP_BORROW 32u
3577#endif
3578
3579#define PROTOCORE_PLAINTEXT_WORK_HTTP PROTOCORE_HTTP_BORROW
3580
3581// The SSH network layer's slot-to-stream map: which socket each SSH slot uses, which pool that
3582// handle indexes, the socket each channel bridges, and the one-time init flag. Measured at 20 bytes
3583// for the default MAX_SSH_CONNS of 1, 24 with PROTOCORE_SSH_MAX_CHANNELS raised to 4, and scales
3584// with both. Slot numbers and socket handles, no key material, so the plaintext end.
3585#ifndef PROTOCORE_SSH_NETWORK_BORROW
3586#define PROTOCORE_SSH_NETWORK_BORROW ((size_t)MAX_SSH_CONNS * (4u + 4u * PROTOCORE_SSH_MAX_CHANNELS) + 16u)
3587#endif
3588
3589#if PROTOCORE_ENABLE_SSH || PROTOCORE_ENABLE_SSH_CLIENT
3590#define PROTOCORE_PLAINTEXT_WORK_SSHNETWORK PROTOCORE_SSH_NETWORK_BORROW
3591#else
3592#define PROTOCORE_PLAINTEXT_WORK_SSHNETWORK 0
3593#endif
3594
3595// The SSH listening role's per-slot teardown flag, one octet per connection. Measured at 1 byte for
3596// the default MAX_SSH_CONNS of 1, and scales with it. No key material, so the plaintext end.
3597#ifndef PROTOCORE_SSH_SERVER_BORROW
3598#define PROTOCORE_SSH_SERVER_BORROW ((size_t)MAX_SSH_CONNS + 8u)
3599#endif
3600
3601#if PROTOCORE_ENABLE_SSH
3602#define PROTOCORE_PLAINTEXT_WORK_SSHSERVER PROTOCORE_SSH_SERVER_BORROW
3603#else
3604#define PROTOCORE_PLAINTEXT_WORK_SSHSERVER 0
3605#endif
3606
3607// The RCWL-0516's debounce and hold state and the GPIO pin it samples. Measured at 28 bytes. A pin
3608// level, so the plaintext end.
3609#ifndef PROTOCORE_RCWL0516_BORROW
3610#define PROTOCORE_RCWL0516_BORROW 32u
3611#endif
3612
3613#if PROTOCORE_ENABLE_RCWL0516
3614#define PROTOCORE_PLAINTEXT_WORK_RCWL0516 PROTOCORE_RCWL0516_BORROW
3615#else
3616#define PROTOCORE_PLAINTEXT_WORK_RCWL0516 0
3617#endif
3618
3619// The SEN0192's motion state: the debounced level, its event count and when it last went active.
3620// Measured at 24 bytes. A pin level and a counter, so the plaintext end.
3621#ifndef PROTOCORE_SEN0192_BORROW
3622#define PROTOCORE_SEN0192_BORROW 32u
3623#endif
3624
3625#if PROTOCORE_ENABLE_SEN0192
3626#define PROTOCORE_PLAINTEXT_WORK_SEN0192 PROTOCORE_SEN0192_BORROW
3627#else
3628#define PROTOCORE_PLAINTEXT_WORK_SEN0192 0
3629#endif
3630
3631// The FTP client session: its two socket handles, the step it is on and the reply buffer it reads
3632// control lines into. Measured at 1320 bytes. A path and a reply line, so the plaintext end.
3633#ifndef PROTOCORE_FTP_SESSION_BORROW
3634#define PROTOCORE_FTP_SESSION_BORROW 1536u
3635#endif
3636
3637#if PROTOCORE_ENABLE_FTP_SESSION
3638#define PROTOCORE_PLAINTEXT_WORK_FTPSESSION PROTOCORE_FTP_SESSION_BORROW
3639#else
3640#define PROTOCORE_PLAINTEXT_WORK_FTPSESSION 0
3641#endif
3642
3643// The StatsD client's collector address, its tag string, and the one line it formats at a time.
3644// Measured at 398 bytes. No key material, so the plaintext end.
3645#ifndef PROTOCORE_STATSD_BORROW
3646#define PROTOCORE_STATSD_BORROW 512u
3647#endif
3648
3649#if PROTOCORE_ENABLE_STATSD
3650#define PROTOCORE_PLAINTEXT_WORK_STATSD PROTOCORE_STATSD_BORROW
3651#else
3652#define PROTOCORE_PLAINTEXT_WORK_STATSD 0
3653#endif
3654
3655// One parsed GraphQL document and the execution walking it. Measured at 4520 bytes. No key
3656// material, so the plaintext end.
3657#ifndef PROTOCORE_GRAPHQL_BORROW
3658#define PROTOCORE_GRAPHQL_BORROW 5120u
3659#endif
3660
3661#if PROTOCORE_ENABLE_GRAPHQL
3662#define PROTOCORE_PLAINTEXT_WORK_GRAPHQL PROTOCORE_GRAPHQL_BORROW
3663#else
3664#define PROTOCORE_PLAINTEXT_WORK_GRAPHQL 0
3665#endif
3666
3667// The LwM2M TLV codec's two cursors, one per direction. Measured at 64 bytes. No key material,
3668// so the plaintext end.
3669#ifndef PROTOCORE_LWM2M_TLV_BORROW
3670#define PROTOCORE_LWM2M_TLV_BORROW 128u
3671#endif
3672
3673#if PROTOCORE_ENABLE_LWM2M
3674#define PROTOCORE_PLAINTEXT_WORK_LWM2MTLV PROTOCORE_LWM2M_TLV_BORROW
3675#else
3676#define PROTOCORE_PLAINTEXT_WORK_LWM2MTLV 0
3677#endif
3678
3679// The CoAP server's resource table, the path and query it splits out, and its message buffers.
3680// Measured at 3032 bytes. No key material - DTLS keys live in coaps_server - so the plaintext end.
3681#ifndef PROTOCORE_COAP_BORROW
3682#define PROTOCORE_COAP_BORROW 3584u
3683#endif
3684
3685#if PROTOCORE_ENABLE_COAP
3686#define PROTOCORE_PLAINTEXT_WORK_COAP PROTOCORE_COAP_BORROW
3687#else
3688#define PROTOCORE_PLAINTEXT_WORK_COAP 0
3689#endif
3690
3691// The DTLS CoAP server's connection pool and ingest ring, with its Ed25519 seed and cookie key.
3692// Measured at 33664 bytes: each slot carries a whole DtlsConn, whose ks_store is
3693// PROTOCORE_TLS13_KS_BORROW, so the pool tracks the key schedule's width. Key material, so the
3694// secure end.
3695#ifndef PROTOCORE_COAPS_SERVER_BORROW
3696#define PROTOCORE_COAPS_SERVER_BORROW 34816u
3697#endif
3698
3699#if PROTOCORE_ENABLE_DTLS && PROTOCORE_ENABLE_COAP
3700#define PROTOCORE_SECURE_WORK_COAPSSERVER PROTOCORE_COAPS_SERVER_BORROW
3701#else
3702#define PROTOCORE_SECURE_WORK_COAPSSERVER 0
3703#endif
3704
3705// The MQTT session: its transport slot, keep-alive timers, inflight table and topic. Measured at
3706// 304 bytes. Beside the wire buffers this module already takes from the secure end, and on the
3707// same end because a retained topic and packet ids describe secured traffic.
3708#ifndef PROTOCORE_MQTT_BORROW
3709#define PROTOCORE_MQTT_BORROW 512u
3710#endif
3711
3712#if PROTOCORE_ENABLE_MQTT && PROTOCORE_HAS_NET_STACK
3713#define PROTOCORE_SECURE_WORK_MQTT PROTOCORE_MQTT_BORROW
3714#else
3715#define PROTOCORE_SECURE_WORK_MQTT 0
3716#endif
3717
3718// The protobuf codec's writer and reader rows, one pair per slot. Measured at 224 bytes. No key
3719// material, so the plaintext end.
3720#ifndef PROTOCORE_PROTOBUF_BORROW
3721#define PROTOCORE_PROTOBUF_BORROW 512u
3722#endif
3723
3724#if PROTOCORE_ENABLE_PROTOBUF
3725#define PROTOCORE_PLAINTEXT_WORK_PROTOBUF PROTOCORE_PROTOBUF_BORROW
3726#else
3727#define PROTOCORE_PLAINTEXT_WORK_PROTOBUF 0
3728#endif
3729
3730// The one Sparkplug metric being encoded or decoded. Measured at 256 bytes. No key material, so
3731// the plaintext end.
3732#ifndef PROTOCORE_SPARKPLUG_BORROW
3733#define PROTOCORE_SPARKPLUG_BORROW 512u
3734#endif
3735
3736#if PROTOCORE_ENABLE_SPARKPLUG
3737#define PROTOCORE_PLAINTEXT_WORK_SPARKPLUG PROTOCORE_SPARKPLUG_BORROW
3738#else
3739#define PROTOCORE_PLAINTEXT_WORK_SPARKPLUG 0
3740#endif
3741
3742// The UDP telemetry sender's collector address and the line it is building. Measured at 56 bytes.
3743// No key material, so the plaintext end.
3744#ifndef PROTOCORE_UDP_TELEMETRY_BORROW
3745#define PROTOCORE_UDP_TELEMETRY_BORROW 128u
3746#endif
3747
3748#if PROTOCORE_ENABLE_UDP_TELEMETRY
3749#define PROTOCORE_PLAINTEXT_WORK_UDPTELEMETRY PROTOCORE_UDP_TELEMETRY_BORROW
3750#else
3751#define PROTOCORE_PLAINTEXT_WORK_UDPTELEMETRY 0
3752#endif
3753
3754#define PROTOCORE_PLAINTEXT_WORK_LOG PROTOCORE_LOG_BORROW
3755
3756#if PROTOCORE_ENABLE_DIFFSERV
3757#define PROTOCORE_PLAINTEXT_WORK_DIFFSERV PROTOCORE_DIFFSERV_BORROW
3758#else
3759#define PROTOCORE_PLAINTEXT_WORK_DIFFSERV 0
3760#endif
3761
3762#define PROTOCORE_PLAINTEXT_WORK_UDPCLIENT PROTOCORE_UDP_CLIENT_BORROW
3763
3764#define PROTOCORE_PLAINTEXT_WORK_UDPLISTENER PROTOCORE_UDP_LISTENER_BORROW
3765
3766#define PROTOCORE_PLAINTEXT_WORK_TCPLOWER PROTOCORE_TCP_LOWER_BORROW
3767
3768#define PROTOCORE_PLAINTEXT_WORK_CONNPOOL PROTOCORE_CONN_POOL_BORROW
3769
3770#define PROTOCORE_PLAINTEXT_WORK_TCPLISTENER PROTOCORE_TCP_LISTENER_BORROW
3771
3772#define PROTOCORE_PLAINTEXT_WORK_TCPCLIENT PROTOCORE_TCP_CLIENT_BORROW
3773
3774#if PROTOCORE_ENABLE_HAPPY_EYEBALLS
3775#define PROTOCORE_PLAINTEXT_WORK_HAPPYEYEBALLS PROTOCORE_HAPPY_EYEBALLS_BORROW
3776#else
3777#define PROTOCORE_PLAINTEXT_WORK_HAPPYEYEBALLS 0
3778#endif
3779
3780#define PROTOCORE_PLAINTEXT_WORK_PHYSICAL PROTOCORE_PHYSICAL_BORROW
3781
3782#ifndef PROTOCORE_PLAINTEXT_ARENA_SIZE
3783#define PROTOCORE_PLAINTEXT_ARENA_SIZE \
3784 (PROTOCORE_PLAINTEXT_SCRATCH + PROTOCORE_PLAINTEXT_WORK_H3CONN + PROTOCORE_PLAINTEXT_WORK_EDGEPROXY + \
3785 PROTOCORE_PLAINTEXT_WORK_EUROMAP77 + PROTOCORE_PLAINTEXT_WORK_UMATI + PROTOCORE_PLAINTEXT_WORK_ROBOTICS + \
3786 PROTOCORE_PLAINTEXT_WORK_J1939 + PROTOCORE_PLAINTEXT_WORK_SIMATIC + PROTOCORE_PLAINTEXT_WORK_MODBUS + \
3787 PROTOCORE_PLAINTEXT_WORK_FTPSESSION + PROTOCORE_PLAINTEXT_WORK_ESPNOW + PROTOCORE_PLAINTEXT_WORK_PROMISC + \
3788 PROTOCORE_PLAINTEXT_WORK_WIFISNIFF + PROTOCORE_PLAINTEXT_WORK_RADIOPOWER + PROTOCORE_PLAINTEXT_WORK_DNSSERVER + \
3789 PROTOCORE_PLAINTEXT_WORK_FORWARD + PROTOCORE_PLAINTEXT_WORK_DIFFSERV + PROTOCORE_PLAINTEXT_WORK_UDPCLIENT + \
3790 PROTOCORE_PLAINTEXT_WORK_UDPLISTENER + PROTOCORE_PLAINTEXT_WORK_TCPLOWER + PROTOCORE_PLAINTEXT_WORK_CONNPOOL + \
3791 PROTOCORE_PLAINTEXT_WORK_TCPLISTENER + PROTOCORE_PLAINTEXT_WORK_TCPCLIENT + \
3792 PROTOCORE_PLAINTEXT_WORK_HAPPYEYEBALLS + PROTOCORE_PLAINTEXT_WORK_PHYSICAL + PROTOCORE_PLAINTEXT_WORK_LOG + \
3793 PROTOCORE_PLAINTEXT_WORK_STATSD + PROTOCORE_PLAINTEXT_WORK_FILESYSTEM + PROTOCORE_PLAINTEXT_WORK_SOUTHBOUND + \
3794 PROTOCORE_PLAINTEXT_WORK_SSHSCP + PROTOCORE_PLAINTEXT_WORK_RCWL0516 + PROTOCORE_PLAINTEXT_WORK_SEN0192 + \
3795 PROTOCORE_PLAINTEXT_WORK_GRAPHQL + PROTOCORE_PLAINTEXT_WORK_LWM2MTLV + PROTOCORE_PLAINTEXT_WORK_COAP + \
3796 PROTOCORE_PLAINTEXT_WORK_PROTOBUF + PROTOCORE_PLAINTEXT_WORK_SPARKPLUG + PROTOCORE_PLAINTEXT_WORK_UDPTELEMETRY + \
3797 PROTOCORE_PLAINTEXT_WORK_FLOWEXPORT + PROTOCORE_PLAINTEXT_WORK_SYSLOG + PROTOCORE_PLAINTEXT_WORK_POWERMGMT + \
3798 PROTOCORE_PLAINTEXT_WORK_GUARDRAILS + PROTOCORE_PLAINTEXT_WORK_FAILSAFE + PROTOCORE_PLAINTEXT_WORK_WORKER + \
3799 PROTOCORE_PLAINTEXT_WORK_PREEMPTQUEUE + PROTOCORE_PLAINTEXT_WORK_LOGBUF + PROTOCORE_PLAINTEXT_WORK_SESSION + \
3800 PROTOCORE_PLAINTEXT_WORK_SIGNALING + PROTOCORE_PLAINTEXT_WORK_TRACECAPTURE + PROTOCORE_PLAINTEXT_WORK_SSHSERVER + \
3801 PROTOCORE_PLAINTEXT_WORK_HTTP + PROTOCORE_PLAINTEXT_WORK_SSHNETWORK + PROTOCORE_PLAINTEXT_WORK_HTTPCONN + \
3802 PROTOCORE_PLAINTEXT_WORK_TELNET + PROTOCORE_PLAINTEXT_WORK_MDNSSERVICE + PROTOCORE_PLAINTEXT_WORK_NTPSERVER + \
3803 PROTOCORE_PLAINTEXT_WORK_NTPSERVICE + PROTOCORE_PLAINTEXT_WORK_UPLOADSERVICE + \
3804 PROTOCORE_PLAINTEXT_WORK_MDNSADAPTIVE + PROTOCORE_PLAINTEXT_WORK_HTTPPARSER + PROTOCORE_PLAINTEXT_WORK_SSHSFTP + \
3805 PROTOCORE_PLAINTEXT_WORK_SSHCOMP + PROTOCORE_PLAINTEXT_WORK_MTCONNECT + PROTOCORE_PLAINTEXT_WORK_FILESERVING + \
3806 PROTOCORE_PLAINTEXT_WORK_HTTPCLOCK + 256)
3807#endif
3808
3809/**
3810 * @brief Compile the library's internal debug checks (default 0 = off).
3811 *
3812 * Deliberately NOT keyed on NDEBUG. Whether NDEBUG is defined is a property of whichever toolchain
3813 * happens to build the library - the Arduino ESP32 core does not define it - so keying on it means
3814 * nobody actually chose. This is the switch we control.
3815 *
3816 * What it enables today: the pools' owner tripwire, which records the first execution context to
3817 * touch each slot and asserts every later borrow matches. That catches a borrow crossing tasks - the
3818 * one way the lock-free single-accessor invariant can break - and turns a silent cross-core race into
3819 * an immediate failure.
3820 *
3821 * Measured cost on an ESP32-S3 at 240 MHz: ~52 cycles per pool entry point, and a
3822 * mark + alloc + release touches three of them, so roughly 156 cycles on every borrow. Worth paying
3823 * while chasing a memory bug; not worth shipping.
3824 */
3825#ifndef PROTOCORE_DEBUG_CHECKS
3826#define PROTOCORE_DEBUG_CHECKS 0
3827#endif
3828
3829/**
3830 * @brief Bytes a worker's generator draws before it redraws its seed from the platform.
3831 *
3832 * The generator's own pace, not a caller's: nothing in crypto/rng/rng.h lets a consumer ask for a
3833 * reseed, because a consumer that could ask would set the rate, and the module that asked most often
3834 * would set it for everyone. A draw is answered from the keystream and the seed is redrawn once this
3835 * budget is spent.
3836 *
3837 * Lower spends more platform entropy for a shorter window per seed; higher does the reverse. The
3838 * forward ratchet already makes an earlier draw unrecoverable from the current state, so this bounds
3839 * the other direction - how long one platform draw is relied on - rather than backward secrecy.
3840 */
3841#ifndef PROTOCORE_RAND_RESEED_BYTES
3842#define PROTOCORE_RAND_RESEED_BYTES (1u << 20)
3843#endif
3844
3845// ---------------------------------------------------------------------------
3846// One gate per crypto primitive
3847// ---------------------------------------------------------------------------
3848// Each module under crypto/ is wrapped in exactly one of these, so a build turns a primitive off by
3849// name rather than by knowing which consumer drags it in. Stated here rather than in the module's own
3850// header because the six derived below read the feature flags, which are all resolved above.
3851//
3852// The six that a header used to gate on a consumer keep that consumer's expression verbatim; the rest
3853// were compiled unconditionally before they had a gate, so they stand at 1. Every one is
3854// #ifndef-guarded, so -D on the command line wins.
3855
3856// One IKE SA's crypto: the cookie hash, the prf+ chain, the AUTH MAC and the ECDSA / RSA signature,
3857// which run in sequence. The signature is the largest.
3858#ifndef PROTOCORE_IKE_BORROW
3859#define PROTOCORE_IKE_BORROW PROTOCORE_CRYPTO_BORROW_MAX
3860#endif
3861
3862// The largest working set any single crypto operation takes. An owner that runs several in sequence
3863// out of one region sizes it by this rather than restating the comparison.
3864#ifndef PROTOCORE_CRYPTO_BORROW_MAX
3865#define PROTOCORE_CRYPTO_BORROW_MAX PROTOCORE_HMAC_SHA512_BORROW
3866#endif
3867
3868// QUIC packet keys: the HKDF's bytes, then the packet key and header-protection key it expands into
3869// before each becomes a keyed context.
3870#ifndef PROTOCORE_QUIC_KEYS_BORROW
3871#define PROTOCORE_QUIC_KEYS_BORROW (PROTOCORE_HKDF_BORROW + 32)
3872#endif
3873
3874// ECDSA hashes the message with SHA-256, then the software path draws its nonce from an RFC 6979
3875// HMAC-DRBG. The two run in sequence; the sum is the bound either way.
3876#ifndef PROTOCORE_ECDSA_BORROW
3877#define PROTOCORE_ECDSA_BORROW (PROTOCORE_SHA256_BORROW + PROTOCORE_HMAC_SHA256_BORROW)
3878#endif
3879
3880// The same at SHA-384's width: one HMAC-SHA384, a 48-byte T(i) block, and the same 514-byte HkdfLabel
3881// region, whose cap is the RFC 8446 sec 7.1 field widths and not the hash.
3882#ifndef PROTOCORE_HKDF_SHA384_BORROW
3883#define PROTOCORE_HKDF_SHA384_BORROW (PROTOCORE_HMAC_SHA384_BORROW + 48 + 514)
3884#endif
3885
3886// HKDF drives one HMAC-SHA256 and holds the T(i) block and the HkdfLabel it builds.
3887#ifndef PROTOCORE_HKDF_BORROW
3888#define PROTOCORE_HKDF_BORROW (PROTOCORE_HMAC_SHA256_BORROW + 32 + 514)
3889#endif
3890
3891// The RFC 4253 sec 7.2 KDF runs one exchange-hash digest and accumulates the K1 || K2 chain behind
3892// it. The chain is bounded by SSH_KDF_MAX (128).
3893#ifndef PROTOCORE_SSH_KDF_BORROW
3894#define PROTOCORE_SSH_KDF_BORROW (PROTOCORE_SSH_KEXHASH_BORROW + 128)
3895#endif
3896
3897// The two tables a module holds for the life of the program rather than for the life of a call.
3898// They take the persistent end of the arena, so they are stated here for the same reason every
3899// working set is: the pool is sized off what the build declares, and an undeclared borrow is one
3900// the pool has no room for.
3901#ifndef PROTOCORE_WORK_ROUTE_TABLE
3902#define PROTOCORE_WORK_ROUTE_TABLE (MAX_ROUTES * 104 + 16) // HttpRoute is 88 with every gated id compiled
3903#endif
3904
3905/** @brief The route table's borrow: every entry plus the count. Proved in http_route.c. */
3906#ifndef PROTOCORE_HTTP_ROUTE_BORROW
3907#define PROTOCORE_HTTP_ROUTE_BORROW PROTOCORE_WORK_ROUTE_TABLE
3908#endif
3909
3910// The routes are a gated module and it defaults off, so a build without it reserved the whole table
3911// for something it never compiled. The term is the borrow only where the table is built.
3912#if PROTOCORE_ENABLE_HTTP_ROUTE
3913#define PROTOCORE_SECURE_WORK_ROUTETABLE PROTOCORE_HTTP_ROUTE_BORROW
3914#else
3915#define PROTOCORE_SECURE_WORK_ROUTETABLE 0
3916#endif
3917/**
3918 * @brief The HTTP auth borrow: the credential table, then the SHA-256 bytes behind it.
3919 *
3920 * Both regions of one span. The table lasts the life of the program and the hash scratch does not,
3921 * but the worst case over an entry's whole chain is taken once and never exceeded, so the digest
3922 * nonce and the Basic check run out of the same borrow the table sits in. Proved in http/auth.c.
3923 */
3924#ifndef PROTOCORE_HTTP_AUTH_BORROW
3925#define PROTOCORE_HTTP_AUTH_BORROW ((size_t)PROTOCORE_WORK_AUTH_TABLE + PROTOCORE_SHA256_BORROW)
3926#endif
3927
3928#ifndef PROTOCORE_WORK_AUTH_TABLE
3929#define PROTOCORE_WORK_AUTH_TABLE (MAX_ROUTES * (3 * MAX_AUTH_LEN + 8) + 32) // AuthCred is 3*MAX_AUTH_LEN + 1
3930#endif
3931// The SSH host key on the software RSA backend: the private exponent from the persistent end for the
3932// program's life, plus the PKCS#8 DER borrowed while protocore_ssh_rsa_load_pubkey walks it.
3933#ifndef PROTOCORE_WORK_SSH_HOST_KEY
3934#define PROTOCORE_WORK_SSH_HOST_KEY (256 + 1700 + 16) // PROTOCORE_RSA_KEY_BYTES + SSH_RSA_KEY_DER_MAX + alignment
3935#endif
3936
3937#ifndef PROTOCORE_H2_SERVER_BORROW
3938#define PROTOCORE_H2_SERVER_BORROW ((size_t)MAX_CONNS * 16 + 64) // one pointer + one mask per slot
3939#endif
3940
3941// ---------------------------------------------------------------------------
3942// Static RAM (BSS) usage table
3943// ---------------------------------------------------------------------------
3944//
3945// All library memory is in BSS - allocated at link time, zero-initialized by
3946// the C runtime, never heap-allocated after begin(). The table below shows
3947// the contribution of every feature at its default constant values.
3948//
3949// Sizes are for ESP32 (32-bit pointers, int = 4 B). Where a size depends on
3950// a macro the formula is given so you can compute the impact of any change.
3951//
3952// ┌──────────────────────────────┬──────────────────────────────────────────────────────────────┬──────────┐
3953// │ Symbol / pool │ Size formula │ Default │
3954// ├──────────────────────────────┼──────────────────────────────────────────────────────────────┼──────────┤
3955// │ TRANSPORT LAYER (always on) │ │ │
3956// │ conn_pool[MAX_CONNS] │ MAX_CONNS × (RX_BUF_SIZE + 22) │ 4 168 B │
3957// │ listener_pool[MAX_LISTENERS]│ MAX_LISTENERS × (StaticQueue_t≈48 + EVT_QUEUE_DEPTH×12 + 18)│ 654 B │
3958// │ conn_timeout_ms │ 4 B │ 4 B │
3959// │ TRANSPORT SUBTOTAL │ │ 4 826 B │
3960// ├──────────────────────────────┼──────────────────────────────────────────────────────────────┼──────────┤
3961// │ HTTP PRESENTATION (always on)│ │ │
3962// │ http_pool[MAX_CONNS] │ MAX_CONNS × (MAX_PATH_LEN + MAX_QUERY_LEN │ │
3963// │ │ + MAX_HEADERS×(MAX_KEY_LEN+MAX_VAL_LEN) │ │
3964// │ │ + MAX_QUERY_PARAMS×(QUERY_KEY_LEN+QUERY_VAL_LEN) │ │
3965// │ │ + BODY_BUF_SIZE + 50) │ 6 668 B │
3966// │ HTTP SUBTOTAL │ │ 6 668 B │
3967// ├──────────────────────────────┼──────────────────────────────────────────────────────────────┼──────────┤
3968// │ WEBSOCKET (PROTOCORE_ENABLE_WEBSOCKET=1) │ │
3969// │ ws_pool[MAX_WS_CONNS] │ MAX_WS_CONNS × (WS_FRAME_SIZE + 29) │ 1 082 B │
3970// ├──────────────────────────────┼──────────────────────────────────────────────────────────────┼──────────┤
3971// │ SSE (PROTOCORE_ENABLE_SSE=1) │ │ │
3972// │ protocore_sse_pool[MAX_SSE_CONNS] │ MAX_SSE_CONNS × (MAX_PATH_LEN + 3) │ 134 B │
3973// ├──────────────────────────────┼──────────────────────────────────────────────────────────────┼──────────┤
3974// │ SSH (PROTOCORE_ENABLE_SSH=1) │ │ │
3975// │ ssh_pool[MAX_SSH_CONNS] │ MAX_SSH_CONNS × (SSH_PKT_BUF_SIZE + 22) │ 2 070 B │
3976// │ ssh_keys[MAX_SSH_CONNS] │ MAX_SSH_CONNS × sizeof(SshKeyMat) │ 1187 B │
3977// │ └─ SshKeyMat (all builds) │ 2×aes_key[32] + 2×aes_iv[16] + 2×mac_key[64] │ │
3978// │ │ + 2×chacha_key[64] + 3 flags = 355 B, plus the two keyed │ │
3979// │ │ GCM contexts 2×PROTOCORE_AESGCM_BORROW (832 B on a vendor AEAD). │ │
3980// │ │ The contexts buy ~9,200 cycles per packet - a FIXED cost │ │
3981// │ │ that dominates small interactive traffic (see aesgcm.h). │ │
3982// │ │ CTR still rebuilds its schedule in scratch per packet. │ │
3983// │ ssh_dh[MAX_SSH_CONNS] │ MAX_SSH_CONNS × (3×protocore_bignum[256] + H[32] + 1) │ 801 B │
3984// │ crypto_work[] │ PROTOCORE_CRYPTO_WORK_SIZE (scratch, wiped after each use) │ 2 144 B │
3985// │ SSH SUBTOTAL │ │ 5 370 B │
3986// ├──────────────────────────────┼──────────────────────────────────────────────────────────────┼──────────┤
3987// │ GRAND TOTAL (all features) │ │ ≈18 KB │
3988// └──────────────────────────────┴──────────────────────────────────────────────────────────────┴──────────┘
3989//
3990// ESP32 has 320 KB of SRAM; the library uses ~5–18 KB depending on features.
3991// Stack usage is separate; the largest frame is during SSH DH key exchange
3992// (~256 B for the protocore_bignum private scalar on the call stack before it is
3993// zeroed by ssh_dh_finish()).
3994//
3995// SSH KEY MATERIAL IS NOT IN THE TABLE ABOVE intentionally:
3996// - The RSA host private key is NEVER stored in any static array. It is
3997// loaded from NVS into a local stack frame at sign time, used once, then
3998// explicitly zeroed (volatile memset) before the function returns.
3999// - AES session keys and HMAC keys live in ssh_keys[] (above), which is a
4000// separate BSS symbol from ssh_pool[]. Physical separation means a
4001// buffer overflow in the packet receive path (ssh_pool[].pkt_buf) cannot
4002// reach the key material without crossing a distinct linker symbol - a
4003// significant barrier against heap/BSS spray attacks.
4004// - The DH ephemeral private scalar y lives in ssh_dh[].y and is zeroed
4005// immediately after the shared secret K is derived.
4006// - crypto_work[] is zeroed via mmgr_zero_buf() after every use so that
4007// bignum intermediates (including partial products that contain key
4008// material) do not persist in memory.
4009
4010// ---------------------------------------------------------------------------
4011// Protocol identifier
4012// ---------------------------------------------------------------------------
4013
4014/**
4015 * @brief Application protocol spoken on a listener port or connection slot.
4016 *
4017 * Stored in both Listener::proto and TcpConn::proto. The session layer uses
4018 * this to route events to the correct protocol handler without branching on
4019 * port numbers.
4020 *
4021 * All values are always present regardless of feature flags - the enum is
4022 * part of the listener API. Feature flags gate the implementation, not the
4023 * identifier.
4024 */
4026{
4027 PROTO_NONE = 0, ///< Unassigned slot.
4028 PROTO_HTTP = 1, ///< HTTP/1.1 with optional WS and SSE upgrades.
4029 PROTO_TELNET = 2, ///< Telnet (RFC 854).
4030 PROTO_SSH = 3, ///< SSH (RFC 4253/4252/4254).
4031 PROTO_MODBUS = 4, ///< Modbus TCP slave (Modbus Application Protocol).
4032 PROTO_OPCUA = 5, ///< OPC UA Binary (UA-TCP) server.
4033 PROTO_SSH_RFWD = 6, ///< SSH remote-forward listener (ssh -R): accepts bridge to a forwarded-tcpip channel.
4034 PROTO_RELAY = 7, ///< TCP relay / DNAT (PROTOCORE_ENABLE_RELAY): bridge to an origin protocore_client connection.
4035 PROTO_BRIDGE = 8, ///< address:port -> hardware bus (PROTOCORE_ENABLE_IFACE_BRIDGE): UART/SPI/I2C device server.
4036 PROTO_NTRIP_CASTER = 9, ///< NTRIP caster (PROTOCORE_ENABLE_NTRIP_CASTER): serves RTCM3 corrections to rovers.
4037 PROTO_MESH =
4038 10, ///< Edge-cache sibling link (PROTOCORE_ENABLE_EDGE_MESH): answers a peer's content-addressed query.
4039 PROTO_UDP = 11, ///< A bound datagram port. The slot carries the peer per entry, not per slot.
4041
4042/**
4043 * @brief What an interface is, and the filter that selects one.
4044 *
4045 * One vocabulary for both jobs. A registered interface carries its kind (layer 1 keeps the
4046 * registry); a route or a connection carries the same value as a filter, where PROTOCORE_IF_ANY means
4047 * "no filter". The wifi/eth values are what a connection is stamped with at accept time by
4048 * comparing its local IP to the softAP IP; a bus or radio interface is registered by the
4049 * application and forwarded to like any other.
4050 */
4052{
4053 PROTOCORE_IF_ANY = 0, ///< unspecified kind, and the filter that matches any interface
4054 PROTOCORE_IF_WIFI_STA = 1, ///< station interface (joined to an AP / your LAN)
4055 PROTOCORE_IF_WIFI_AP = 2, ///< softAP interface (clients joined to the device)
4056 PROTOCORE_IF_ETH = 3, ///< wired Ethernet PHY
4057 PROTOCORE_IF_BUS = 4, ///< a bus bridged onto the network (uart, spi, can)
4058 PROTOCORE_IF_RADIO = 5, ///< a non-wifi radio
4060
4061// --- feature dependency guards (centralized; see the BUILD-FLAG DEPENDENCY TREE
4062// near the top of this file). A child feature requires its parent(s). ---
4063
4064/** @brief Number of simultaneous outbound client connections (BSS pool size). */
4065#ifndef PROTOCORE_CLIENT_CONNS
4066#if PROTOCORE_ENABLE_SSH_CLIENT
4067// The reverse-SSH tunnel holds the relay connection plus one local bridge per forwarded channel, so
4068// the pool must cover 1 + PROTOCORE_SSH_CLIENT_MAX_CHANNELS or channels past the pool fail to bridge.
4069#define PROTOCORE_CLIENT_CONNS (1 + PROTOCORE_SSH_CLIENT_MAX_CHANNELS)
4070#else
4071#define PROTOCORE_CLIENT_CONNS 2
4072#endif
4073#endif
4074
4075/**
4076 * @brief Per-connection wire receive ring size (bytes).
4077 *
4078 * Holds plaintext (plain) or ciphertext (TLS). The transport ACKs on consume
4079 * (TcpClient.read reopens the window), so for a large inbound transfer to never
4080 * stall the ring must hold a full TCP receive window: keep PROTOCORE_CLIENT_RX_BUF >=
4081 * TCP_WND (~5.7 KB). The 8192 default clears that and a multi-KB TLS handshake
4082 * flight; a ring below TCP_WND can deadlock a sustained download (the peer would be
4083 * allowed to send more than the ring holds). Must exceed one TCP segment (TCP_MSS).
4084 */
4085#ifndef PROTOCORE_CLIENT_RX_BUF
4086#define PROTOCORE_CLIENT_RX_BUF 8192
4087#endif
4088
4089// -- SSH (network_drivers/presentation/ssh; the codec compiles when the SSH sources are
4090// built, so its knobs are always defined) --
4091/** @brief Initial receive window the SSH server advertises (RFC 4254 §5.1). */
4092#ifndef SSH_CHAN_WINDOW
4093#define SSH_CHAN_WINDOW 32768u
4094#endif
4095/**
4096 * @brief Maximum SSH channel data payload the server advertises it can receive per message.
4097 *
4098 * This is what a peer may put in one SSH_MSG_CHANNEL_DATA, so it MUST fit one inbound SSH packet: the
4099 * transport rejects any packet larger than SSH_PKT_BUF_SIZE, so advertising more than that (minus the
4100 * channel-data + packet framing + MAC + padding) makes a peer that sends a bigger message - e.g. an SFTP
4101 * WRITE - trip the packet-too-large check and drop the connection. Derived from SSH_PKT_BUF_SIZE so it scales
4102 * when that buffer is raised (e.g. for higher SFTP throughput). Interactive shells never approach it.
4103 */
4104#ifndef SSH_CHAN_MAX_PACKET
4105#define SSH_CHAN_MAX_PACKET (SSH_PKT_BUF_SIZE - 64u)
4106#endif
4107/**
4108 * @brief Re-key when either packet sequence number reaches this value.
4109 *
4110 * Two bounds govern one key and the tighter one binds: RFC 4253 sec 9 gives a gigabyte of
4111 * transmitted data, RFC 4344 sec 3.2 gives 2^32 blocks, which at 16 bytes a block is 64 GiB. So the
4112 * gigabyte is what to divide by a packet. A wire packet is the payload buffer plus the compressor's
4113 * worst case (an eighth) plus framing and the largest MAC tag, which is under twice the buffer, so
4114 * twice the buffer is the packet size to divide by. Both are powers of two and so is the quotient:
4115 * the sequence-number check is a compare against a shift, never a divide. 2^30 / (2 * BUF) is
4116 * written as 2^29 / BUF. Far below SSH_SEQ_CLOSE_THRESHOLD, so a re-key always precedes the wrap
4117 * that would repeat the CTR keystream.
4118 */
4119#ifndef SSH_REKEY_PACKET_THRESHOLD
4120#define SSH_REKEY_PACKET_THRESHOLD (0x20000000u / SSH_PKT_BUF_SIZE)
4121#endif
4122/**
4123 * @brief Elapsed-time re-key trigger in milliseconds (RFC 4253 §9: "after each hour"). Default 1 hour.
4124 *
4125 * A server-initiated re-key fires when either this much time or SSH_REKEY_PACKET_THRESHOLD packets have
4126 * passed since the last KEX, whichever comes first. Set to 0 to disable the time trigger (packet-count
4127 * only). Measured with the pluggable clock (protocore_millis()).
4128 */
4129#ifndef SSH_REKEY_TIME_MS
4130#define SSH_REKEY_TIME_MS 3600000u
4131#endif
4132/**
4133 * @brief How long a connection may stay unauthenticated before it is disconnected, milliseconds.
4134 *
4135 * RFC 4252 sec 4: "The server SHOULD have a timeout for authentication and disconnect if the
4136 * authentication has not been accepted within the timeout period. The RECOMMENDED timeout period is
4137 * 10 minutes." Set to 0 to disable. Measured with the pluggable clock (protocore_millis()).
4138 */
4139#ifndef SSH_AUTH_TIMEOUT_MS
4140#define SSH_AUTH_TIMEOUT_MS 600000u
4141#endif
4142/** @brief Max stored user name (RFC 4252 imposes no limit; we cap for BSS). */
4143#ifndef SSH_AUTH_USER_MAX
4144#define SSH_AUTH_USER_MAX 32
4145#endif
4146/**
4147 * @brief Max stored TERM value from a pty-req (RFC 4254 sec 6.2).
4148 *
4149 * TERM is an environment variable's value, so the RFC sets no bound; a longer one is truncated
4150 * rather than refused. "vt100", "xterm", "xterm-256color" and "screen-256color" all fit.
4151 */
4152#ifndef PROTOCORE_SSH_PTY_TERM_MAX
4153#define PROTOCORE_SSH_PTY_TERM_MAX 24
4154#endif
4155/** @brief Max stored password length. */
4156#ifndef SSH_AUTH_PASS_MAX
4157#define SSH_AUTH_PASS_MAX 64
4158#endif
4159/** @brief Max stored public-key algorithm name ("rsa-sha2-512", "ecdsa-sha2-nistp256", RFC 4253 sec 6.6). */
4160#ifndef SSH_AUTH_ALGO_MAX
4161#define SSH_AUTH_ALGO_MAX 20
4162#endif
4163/**
4164 * @brief Max stored size of the CLIENT KEXINIT payload (I_C, for the exchange hash).
4165 *
4166 * A modern OpenSSH client's KEXINIT (post-quantum KEX names + cert host-key algs + EtM
4167 * MACs + ext-info-c) runs well past 1 KB, so this must be large enough to hold it - a
4168 * smaller bound silently rejects real clients at key exchange. The packet layer already
4169 * caps any single packet at SSH_PKT_BUF_SIZE.
4170 */
4171#ifndef SSH_KEXINIT_MAX
4172#define SSH_KEXINIT_MAX 2048
4173#endif
4174
4175#if PROTOCORE_ENABLE_AUDIT_LOG
4176// -- Audit log (server/security/audit_log) --
4177#ifndef PROTOCORE_AUDIT_LOG_ENTRIES
4178#define PROTOCORE_AUDIT_LOG_ENTRIES 32 ///< RAM ring depth (records retained for query/verify).
4179#endif
4180#ifndef PROTOCORE_AUDIT_MSG_LEN
4181#define PROTOCORE_AUDIT_MSG_LEN 48 ///< Max message bytes per record (truncated to fit).
4182#endif
4183/**
4184 * @brief Octets of each record's chain hash: the SHA-256 digest width.
4185 *
4186 * Not a knob. Each record hashes SHA-256(prev_hash || seq || ts || category || msg_len || msg), so
4187 * the width is the digest's and any other value breaks the chain a verify walks.
4188 */
4189#define PROTOCORE_AUDIT_HASH_LEN 32
4190#endif // PROTOCORE_ENABLE_AUDIT_LOG
4191
4192#if PROTOCORE_ENABLE_DEVICENET
4193// -- DeviceNet (services/fieldbus/devicenet) --
4194#ifndef PROTOCORE_DEVICENET_MSG_MAX
4195#define PROTOCORE_DEVICENET_MSG_MAX 256 ///< max reassembled fragmented message
4196#endif
4197#endif // PROTOCORE_ENABLE_DEVICENET
4198
4199#if PROTOCORE_ENABLE_ESPNOW
4200// -- ESP-NOW (services/radio/espnow) --
4201#ifndef PROTOCORE_ESPNOW_MAX_PEERS
4202#define PROTOCORE_ESPNOW_MAX_PEERS 8 ///< Bounded peer registry size.
4203#endif
4204#endif // PROTOCORE_ENABLE_ESPNOW
4205
4206#if PROTOCORE_ENABLE_GRAPHQL
4207// -- GraphQL (services/iot/graphql) --
4208#ifndef PROTOCORE_GQL_MAX_NODES
4209#define PROTOCORE_GQL_MAX_NODES 48 ///< Max fields across the whole query.
4210#endif
4211#ifndef PROTOCORE_GQL_MAX_ARGS
4212#define PROTOCORE_GQL_MAX_ARGS 24 ///< Max arguments across the whole query.
4213#endif
4214#ifndef PROTOCORE_GQL_MAX_DEPTH
4215#define PROTOCORE_GQL_MAX_DEPTH 6 ///< Max selection-set nesting depth.
4216#endif
4217#ifndef PROTOCORE_GQL_NAME_MAX
4218#define PROTOCORE_GQL_NAME_MAX 32 ///< Max field / argument name length.
4219#endif
4220#ifndef PROTOCORE_GQL_PATH_MAX
4221#define PROTOCORE_GQL_PATH_MAX 96 ///< Max dotted path length passed to the resolver.
4222#endif
4223#ifndef PROTOCORE_GQL_STRBUF
4224#define PROTOCORE_GQL_STRBUF 256 ///< Pool for decoded string-argument bytes.
4225#endif
4226#endif // PROTOCORE_ENABLE_GRAPHQL
4227
4228#if PROTOCORE_ENABLE_J1939
4229// -- J1939 (services/j1939; also built when NMEA 2000 is enabled) --
4230#ifndef PROTOCORE_J1939_TP_MAX
4231#define PROTOCORE_J1939_TP_MAX 256 ///< max reassembled TP message (spec allows up to 1785); sized down for RAM
4232#endif
4233#endif // PROTOCORE_ENABLE_J1939
4234
4235#if PROTOCORE_ENABLE_NMEA0183
4236// -- NMEA 0183 (services/timing_position/nmea0183) --
4237#ifndef PROTOCORE_NMEA0183_MAX_FIELDS
4238#define PROTOCORE_NMEA0183_MAX_FIELDS 26 ///< max comma-separated fields (incl. the address field)
4239#endif
4240#endif // PROTOCORE_ENABLE_NMEA0183
4241
4242#if PROTOCORE_ENABLE_UBX
4243// -- UBX (services/timing_position/ubx) --
4244#ifndef PROTOCORE_UBX_MAX_PAYLOAD
4245#define PROTOCORE_UBX_MAX_PAYLOAD \
4246 256 ///< max UBX payload the stream demux buffers (NAV-PVT is 92; longer frames are skipped)
4247#endif
4248#endif // PROTOCORE_ENABLE_UBX
4249
4250#if PROTOCORE_ENABLE_NMEA2000
4251// -- NMEA 2000 (services/timing_position/nmea2000) --
4252#ifndef PROTOCORE_N2K_FP_MAX
4253#define PROTOCORE_N2K_FP_MAX 223 ///< Fast Packet max payload (6 in frame 0 + 31 x 7)
4254#endif
4255#endif // PROTOCORE_ENABLE_NMEA2000
4256
4257#if PROTOCORE_ENABLE_OAUTH2
4258// -- OAuth2 (services/security/oauth2) --
4259#ifndef PROTOCORE_OAUTH2_TOKEN_LEN
4260#define PROTOCORE_OAUTH2_TOKEN_LEN 768 ///< access_token / id_token buffer (JWTs are large).
4261#endif
4262#ifndef PROTOCORE_OAUTH2_RT_LEN
4263#define PROTOCORE_OAUTH2_RT_LEN 256 ///< refresh_token buffer.
4264#endif
4265#ifndef PROTOCORE_OAUTH2_BODY_BUF
4266#define PROTOCORE_OAUTH2_BODY_BUF 1024 ///< token-request body buffer.
4267#endif
4268#ifndef PROTOCORE_OAUTH2_RESP_BUF
4269#define PROTOCORE_OAUTH2_RESP_BUF 2048 ///< token-endpoint response buffer.
4270#endif
4271#endif // PROTOCORE_ENABLE_OAUTH2
4272
4273#if PROTOCORE_ENABLE_OIDC
4274// -- OIDC (services/security/oidc) --
4275// PROTOCORE_OIDC_MAX_LEN is declared unconditionally with PROTOCORE_ENABLE_OIDC above, because PROTOCORE_AUTH_HDR_CAP
4276// sizes the Authorization buffer from it and that runs before this block.
4277#ifndef PROTOCORE_OIDC_SUB_LEN
4278#define PROTOCORE_OIDC_SUB_LEN 64 ///< Captured `sub` claim buffer.
4279#endif
4280#ifndef PROTOCORE_OIDC_EMAIL_LEN
4281#define PROTOCORE_OIDC_EMAIL_LEN 96 ///< Captured `email` claim buffer.
4282#endif
4283#ifndef PROTOCORE_OIDC_KID_LEN
4284#define PROTOCORE_OIDC_KID_LEN 80 ///< Max `kid` length.
4285#endif
4286#ifndef PROTOCORE_OIDC_JWKS_MAX
4287#define PROTOCORE_OIDC_JWKS_MAX 16384 ///< Max JWKS document scanned; exceeds any real multi-key set, bounds the parse.
4288#endif
4289#endif // PROTOCORE_ENABLE_OIDC
4290
4291#if PROTOCORE_ENABLE_PROVISIONING
4292// -- Wi-Fi provisioning credential store (server/core/provisioning_service) --
4293// The NVS namespace and its keys, overridable per deployment (e.g. to avoid an NVS-namespace
4294// collision with the application's own store).
4295#ifndef PROTOCORE_PROV_NVS_NAMESPACE
4296#define PROTOCORE_PROV_NVS_NAMESPACE "wifi_prov" ///< NVS namespace holding the saved credentials.
4297#endif
4298#ifndef PROTOCORE_PROV_KEY_SSID
4299#define PROTOCORE_PROV_KEY_SSID "ssid" ///< NVS key + HTML form field for the SSID.
4300#endif
4301#ifndef PROTOCORE_PROV_KEY_PSK
4302#define PROTOCORE_PROV_KEY_PSK "psk" ///< NVS key + HTML form field for the pre-shared key.
4303#endif
4304#endif // PROTOCORE_ENABLE_PROVISIONING
4305
4306#if PROTOCORE_ENABLE_MNT
4307// -- Mounted storage (services/storage/mnt) --
4308#ifndef PROTOCORE_MNT_RAM_FILES
4309#define PROTOCORE_MNT_RAM_FILES 4 ///< RAM backend: number of files (a directory occupies one).
4310#endif
4311#ifndef PROTOCORE_MNT_RAM_FILE_SIZE
4312#define PROTOCORE_MNT_RAM_FILE_SIZE 1024 ///< RAM backend: max bytes per file.
4313#endif
4314#ifndef PROTOCORE_MNT_MAX_OPEN
4315#define PROTOCORE_MNT_MAX_OPEN 4 ///< Concurrent open handles, files and directory cursors together.
4316#endif
4317#ifndef PROTOCORE_MNT_NAME_MAX
4318#define PROTOCORE_MNT_NAME_MAX 48 ///< Max path length (RAM backend). Not a bound on any caller's buffer.
4319#endif
4320#endif // PROTOCORE_ENABLE_MNT
4321
4322/** @brief SCPI error/event queue depth (entries). The SCPI status model requires a queue; when it
4323 * overflows the tail entry is replaced with -350 "Queue overflow" per the standard. */
4324#ifndef PROTOCORE_SCPI_ERR_QUEUE
4325#define PROTOCORE_SCPI_ERR_QUEUE 8
4326#endif
4327
4328#ifndef PROTOCORE_WAL_PAGE_SIZE
4329#define PROTOCORE_WAL_PAGE_SIZE 32768 // sequential write unit (the measured durable-throughput knee)
4330#endif
4331
4332#ifndef PROTOCORE_WAL_MAX_RECORD
4333#define PROTOCORE_WAL_MAX_RECORD 4096 // largest single record payload
4334#endif
4335
4336#ifndef PROTOCORE_DBM_SLOTS
4337#define PROTOCORE_DBM_SLOTS 256 // max live keys (in-RAM index capacity; open-addressed, keep load < ~0.7)
4338#endif
4339
4340#ifndef PROTOCORE_DBM_KEY_MAX
4341#define PROTOCORE_DBM_KEY_MAX 32 // largest key in bytes
4342#endif
4343
4344#ifndef PROTOCORE_DBM_VAL_MAX
4345#define PROTOCORE_DBM_VAL_MAX 256 // largest value in bytes
4346#endif
4347
4348#ifndef PROTOCORE_DOCSTORE_FIELD_MAX
4349#define PROTOCORE_DOCSTORE_FIELD_MAX 128 // largest string field value a find can compare
4350#endif
4351
4352// PROTOCORE_MESH_MAX_PEERS and PROTOCORE_MESH_MAX_CONNS come from vendor/board_profiles/ (classic floor, raised
4353// per chip/PSRAM).
4354#ifndef PROTOCORE_MESH_QUERY_MS
4355#define PROTOCORE_MESH_QUERY_MS 300 // per-peer query deadline before moving on (miss) / to the origin
4356#endif
4357
4358#ifndef PROTOCORE_MESH_HOST_MAX
4359#define PROTOCORE_MESH_HOST_MAX 64 // largest sibling peer host string
4360#endif
4361
4362#ifndef PROTOCORE_MESH_HDRS_MAX
4363#define PROTOCORE_MESH_HDRS_MAX \
4364 384 // request-header snapshot carried to a peer so it can match Vary variants
4365 // (headers past the cap are dropped -> at worst a safe mesh miss, never wrong content)
4366#endif
4367
4368#endif // PROTOCORE_BUFFER_SIZING_H
enum PROTO_ENUM_PACKED protocore_if_kind
What an interface is, and the filter that selects one.
PROTO_ENUM_PACKED
Application protocol spoken on a listener port or connection slot.
@ PROTO_SSH_RFWD
SSH remote-forward listener (ssh -R): accepts bridge to a forwarded-tcpip channel.
@ PROTO_NONE
Unassigned slot.
@ PROTO_NTRIP_CASTER
NTRIP caster (PROTOCORE_ENABLE_NTRIP_CASTER): serves RTCM3 corrections to rovers.
@ PROTOCORE_IF_BUS
a bus bridged onto the network (uart, spi, can)
@ PROTO_TELNET
Telnet (RFC 854).
@ PROTO_MESH
Edge-cache sibling link (PROTOCORE_ENABLE_EDGE_MESH): answers a peer's content-addressed query.
@ PROTO_UDP
A bound datagram port. The slot carries the peer per entry, not per slot.
@ PROTOCORE_IF_RADIO
a non-wifi radio
@ PROTOCORE_IF_WIFI_STA
station interface (joined to an AP / your LAN)
@ PROTO_BRIDGE
address:port -> hardware bus (PROTOCORE_ENABLE_IFACE_BRIDGE): UART/SPI/I2C device server.
@ PROTO_HTTP
HTTP/1.1 with optional WS and SSE upgrades.
@ PROTO_RELAY
TCP relay / DNAT (PROTOCORE_ENABLE_RELAY): bridge to an origin protocore_client connection.
@ PROTO_SSH
SSH (RFC 4253/4252/4254).
@ PROTOCORE_IF_WIFI_AP
softAP interface (clients joined to the device)
@ PROTOCORE_IF_ANY
unspecified kind, and the filter that matches any interface
@ PROTO_OPCUA
OPC UA Binary (UA-TCP) server.
@ PROTO_MODBUS
Modbus TCP slave (Modbus Application Protocol).
@ PROTOCORE_IF_ETH
wired Ethernet PHY
enum PROTO_ENUM_PACKED ProtoConn
Application protocol spoken on a listener port or connection slot.