ProtoCore v0.0.1
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
Interop harness matrix

Which protocol features are exercised against something other than ourselves, and by what.

A round-trip through our own codec proves self-consistency, not conformance. This matrix tracks the ways a feature is judged by something other than itself:

  1. Interop peer - a real third-party implementation in the harness (test/servers/peers/, driven by test/servers/interop.py), so the wire format is validated by something that did not read our source. This is the strongest evidence a wire format is right, and the reason the matrix exists.
  2. Throughput bench - host ns/op + on-device µs/op, recorded in FEATURE_PERFORMANCE.md (device numbers via the rig /bench CCOUNT harness).
  3. Advanced attack - targeted, protocol-aware exploits fired at a live rig over the network (pentesting/pc_pentest.py), with the determinism and liveness oracles.
  4. Adversarial fuzz - host-level hostile input in the native_pentest env, run separately from the main suite because it is heavy.

Attack and fuzz are not interchangeable: one proves the device survives a knowledgeable opponent on the wire, the other proves the parsers survive malformed input at all. A feature is fully covered when it has all four.

Rows without a peer are not untested - they have host tests with pinned spec vectors. They are untested by a third party, which is precisely the gap this tracks.

Related: AUDIT.md records the per-standard conformance verdicts this evidence backs; STANDARDS.md maps each feature to its authoritative spec.

Status at a glance

Dimension Count Source of truth
Interop peers 39 test/servers/peers/*_peer.py
Throughput benches 207 performance_benching/**/platformio.ini
Advanced attacks 79 @attack(...) in pentesting/pc_pentest.py
Adversarial fuzz 78 native_pentest (test/test_pentest/)
Service modules 197 src/services/<group>/<module>/

Interop peers cover 39 of 197 service modules. Not every module needs a third-party peer - many are pure codecs with pinned spec vectors, and some need hardware the project does not have - but the ratio is the honest measure of how much is judged by something other than ourselves.

Peers: amqp, coap, dns, euromap77, fanuc_j519, focas, ftp, graphql, grpcweb, h2, h3, http, jwt, modbus, mqtt, mtconnect, nats, ntp, opcua, openadr, redis, robotics, sep2, simatic, smb, smtp, snmp, sparkplug, sse, ssh, statsd, stomp, sunspec, syslog, tls, wamp, webdav, ws, xmpp

Legend

OK covered · ~ partial/basic · - gap · n/a no external peer (device-internal or sensor)

Protocol matrix (interop-relevant features)

Protocol / feature Interop Bench Adv. attack Next / notes
HTTP/1.1 OK OK OK desync matrix, chunk abuse, obs-fold, CRLF, oversized, slowloris
WebSocket OK OK OK RFC 6455 peer; permessage-deflate host+device bench (inflate 196us); frame-abuse + ping-flood + deflate-bomb (1014x defused) - all held
Server-Sent Events OK OK OK WHATWG EventSource peer; pc_sse_format host+device bench; exhaustion+churn attacks (found+fixed a real DoS: SSE slot leak)
HTTP/2 OK OK OK DONE (device-as-server on PSRAM). Interop http2 peer 7/7 via httpx/h2 (ALPN h2, HEADERS/DATA+HPACK, multiplexed streams). Bench: cold ~452 ms, warm ~51 ms/req (19.5/s), ~1-2 concurrent streams (FEATURE_PERFORMANCE.md). Attack pc_h2_abuse HELD (rapid-reset CVE-2023-44487 + CONTINUATION-flood/HPACK-bomb/PING-flood, 0 findings). Bring-up fixed a core-locking tcp_write HW bug (BUGS.md). Pools in PSRAM (s_h2@0x3c0e0000 + s_pool@0x3c0fcf30, DRAM 18%)
HTTP/3 / QUIC OK OK OK DONE end-to-end on the PSRAM board (Ed25519 cert + pc_h3_cert() + begin, s_qpool@0x3c0e0000, UDP:443). Interop http3 peer 5/5 via aioquic (QUIC handshake X25519+Ed25519+AES-128-GCM, GET / -> 200 over QPACK+DATA, multiplexed 2nd stream). Bench cold ~940 ms, ~2 req/conn. Attack pc_quic_abuse HELD (raw-UDP: Initial-flood 300, malformed long/short-header fuzz, anti-amplification/reflection 0.00x - the server never answers a forged Initial; 0 findings). Bring-up fixed two HW bugs (Ed25519 stack v5.95.11, frame parser v5.95.12, BUGS.md)
TLS OK OK OK device-as-server on a slim HTTP+TLS rig (DRAM, 48 KB static arena, no PSRAM/no malloc). tls_peer 7/7 (TLS 1.2 ECDHE-ECDSA-AES256-GCM-SHA384, cert CN esp32-pc, GET-over-TLS 200); tls_server_abuse held (downgrade refused, weak/NULL/EXPORT/RC4 refused, 7 malformed handshakes survived); handshake ~913 ms median. Found+fixed 2 HW bugs: tcpip_thread self-deadlock on close_notify, and RX ring < 1.5 KB ClientHello. tls_session_abuse HELD (session/record layer: resumption OFF - full handshakes only, so no ticket-replay surface; malformed/duplicate ALPN offer handled without a crash; 24 half-open handshakes survived; engine recovers). 0-RTT n/a (device is TLS 1.2, not 1.3)
Basic/Digest auth n/a OK OK auth_bypass (null-trunc fixed) + **digest_nonce_replay** (RFC 7616 SHA-256): forged nonce (bad MAC), tampered response digest, and wrong password all rejected (3/3 held on HW); the stateless keyed-MAC nonce lets an identical captured response replay within the ~5 min lifetime - reported INFO (CWE-294, bounded tradeoff, keep the lifetime short + use TLS). Targets /secure-digest on the rig; sets alive_override (invalid attempts perturb the throttle)
JWT (HS256 bearer) OK OK OK real PyJWT peer (6/6 HW: valid accepted, wrong-secret rejected, expired rejected by exp) via rig /jwt/verify (Authorization: Bearer, not query - MAX_QUERY_LEN truncates); pc_jwt_verify_hs256 host+device bench (141us, HMAC-SHA256, heaviest auth op); pc_jwt_forgery (14/14 forgeries rejected: alg=none/HS384/RS256/None-confusion, wrong-secret, sig strip/bitflip/truncate, extra dots, oversized) - a positive alg-confusion-immunity proof
CoAP OK OK OK aiocoap peer (3/3 HW on rig); pc_coap_server_process host+device bench; 3 advanced attacks (option-abuse, block bomb, observe flood) + fuzz - all held
MQTT OK OK OK broker peer; mqtt codec host+device bench; mqtt_malicious_broker attack (8/8 HW: bad CONNACK, topic-len lie, remlen 268MB, 5-byte remlen, truncated, giant topic - all held) via the rig /mqtt/probe device-as-client trigger
Modbus OK OK OK pymodbus peer (6/6 HW on rig); pc_modbus_process_adu host+device bench; 2 advanced attacks (illegal-requests -> 6/6 proper exceptions, MBAP abuse) + fuzz - all held
SNMP OK OK OK net-snmp peer (3/3 HW on rig); pc_snmp_agent_process host+device bench; 3 advanced attacks (GETBULK amplification x9.7 bounded, BER abuse, varbind flood) + fuzz - all held
OPC-UA OK OK OK asyncua peer (2/2 HW on rig: connect/session/browse); handshake+DataValue host bench + device op; 3 advanced attacks (HELLO buffer abuse->8192 bounded, UACP abuse, chunk flood) + fuzz - all held
SMB2 client OK OK OK ALL THREE DONE (2026-07-11). Slim rig_s3_smb env + main_smb.cpp /smb/probe device-as-SMB2-client endpoint (NEGOTIATE -> NTLMv2 SESSION_SETUP -> TREE_CONNECT -> CREATE -> READ -> CLOSE via pc_client_*, DRAM 26.8%). Interop smb_peer.py 7/7 HW vs real samba (dperson/samba :4445): the device read content FNV-1a matches an independent smbclient read exactly. Attack pc_smb2_malicious_server 10/10 HELD (fake server: 16 MB / 4 GB / zero direct-TCP frame, truncated + slow-drip, bad ProtocolId, < 64 B header, NT-status error, OOB SecurityBuffer, accept-then-close) - the de-framer + NEGOTIATE parser survived, heap stable, 0 findings (auto-selected via --diag: /diag now advertises the protocol flags)
SSH OK OK OK All three DONE (2026-07-11). Interop: ssh peer 6/6 vs the reference OpenSSH client (curve25519-sha256 KEX, ssh-ed25519 host key, chacha20-poly1305, NEWKEYS, RFC 4252 password auth, RFC 4254 channel echo; aes256-ctr E&M + ETM also verified). Bench: performance_benching/bench_ssh.cpp host + S3 CCOUNT (FEATURE_PERFORMANCE.md) - X25519 ~150.8 ms, ed25519_sign ~547.9 ms, chacha 1 KB 705 us / 1.5 MB/s; handshake ~0.85 s of crypto (software field arithmetic). Attacks: 5 stdlib-only raw-socket SSH-2 attacks (ssh_banner_abuse, ssh_kexinit_abuse, ssh_packet_abuse, ssh_kexdh_abuse, ssh_msgtype_abuse) covering the whole pre-encryption handshake (banner/version, KEXINIT limits + no-mutual-alg + length-lie, packet-length/padding underflow, wrong-length Q_C, out-of-order message types) with an SSH-native liveness oracle - all HELD. **ssh_msgtype_abuse FOUND + FIXED a real bug (BUGS.md):** SERVICE_REQUEST had no phase guard, so a client could jump DH_INIT -> AUTH in cleartext, skipping KEX; guarded to SSH_PHASE_SERVICE + regression test. Two HW bugs total found on SSH bring-up (emit-wiring v5.95.13 + this). Rig rig_s3_ssh
FTP client OK OK OK pyftpdlib peer (11/11 HW: USER/PASS/TYPE/PASV/STOR + SIZE, upload verified server-side) via rig /ftp/probe device-as-client; pc_ftp_parse_reply host+device bench; pc_ftp_malicious_server (9 personalities: oversized/unterminated reply, infinite multiline, malformed 227, PASV bounce, code confusion - all held)
WebDAV OK OK OK stdlib RFC 4918 client peer (17/17 HW); ms_entry host+device bench; 5 attacks (Depth:inf, XXE, traversal, PROPPATCH bomb, LOCK flood)
Redis (RESP) OK OK OK redis_peer device-as-client vs a real redis-server (6/6 HW: PING/SET/GET + the SET verified server-side) via rig /redis/probe; RESP codec host+device bench; redis_resp_fuzz (10/11 malformed replies bounded)
NATS OK OK OK stdlib client peer vs a real nats-server (7/7 HW: INFO/CONNECT/SUB/PUB + the PUB delivered to an independent subscriber through the broker) via rig /nats/probe device-as-client; pc_nats_build_pub + pc_nats_parse host+device bench (6.55us); pc_nats_malicious_server (10 personalities: unterminated INFO, MSG len-lie, truncated, bad tokens, neg size, over-buffer line, -ERR flood, unknown verb, no-CRLF, mid-close) - held
STOMP OK OK OK stdlib spec STOMP 1.2 broker peer (7/7 HW: CONNECT/SUBSCRIBE/SEND + the SEND captured server-side) via rig /stomp/probe device-as-client; pc_stomp_build_frame + pc_stomp_parse_frame host+device bench (9.64us); pc_stomp_malicious_broker (10 personalities: content-length lie/overflow/nondigit/mismatch, header flood, no-colon, over-buffer frame, no-NUL body, unterminated CONNECTED, heartbeat flood) - held

| StatsD | OK | OK | OK | stdlib UDP collector peer (5/5 HW: name:value | type validated) via rig /statsd/probe device-as-client; pc_statsd_format host+device bench (4.38us); pc_statsd_injection (newline metric-forging, :/| confusion, control bytes, 2KB oversized -> bounded 51B) - held; newline forging noted INFO (CWE-117, StatsD packs
-separated metrics) | | XMPP | OK | - | - | Interop DONE (2026-07-12, device-as-client). xmpp peer 11/11 HW: the rig GET /xmpp/probe dials a self-contained RFC 6120 c2s server and runs the full client dialogue with the xmpp.h stanza codec - stream open (‘version='1.0’,to=domain), SASL PLAIN<auth>(the server b64-decodes + verifies the credentials), stream restart, resource-bind<iq>(the server replies the full JID),<presence>, and a<message><body>the server captures + validates. Independent-impl proof of the stream/message/presence/iq builders. (prosody / ejabberd are the production references.) Bench + attack next. | | AMQP | OK | - | - | **Interop DONE (2026-07-12, device-as-client).**amqppeer **12/12 HW**: the rigGET /amqp/probedials a self-contained AMQP 0-9-1 broker and runs the full client dialogue with the amqp.h frame/method codec - protocol header, Connection.Start-Ok (SASL PLAIN, the broker decodes + verifies the credentials), Tune-Ok, Connection.Open, Channel.Open, and a Basic.Publish + content-header + body the broker captures + validates (routing key pc.q). Independent-impl proof of the frame builder + method encoder/parser; the method-arg encoders live in the rig (per amqp.h). (RabbitMQ is the production reference.) Bench + attack next. | | WAMP | OK | - | - | **Interop DONE (2026-07-12, device-as-client over WebSocket).**wamppeer **12/12 HW**: the rigGET /wamp/probedoes a WS client handshake offering thewamp.2.jsonsubprotocol (needed a small library feature - asubprotocolarg onws_client_build_handshake, v-bump, native-tested), then runs the WAMP client dialogue with the wamp.h codec against a stdlib WAMP-over-WebSocket router - HELLO/WELCOME, SUBSCRIBE/SUBSCRIBED, PUBLISH (acknowledge+exclude_me=false)/PUBLISHED + the routed EVENT, CALL/RESULT. Independent-impl proof of the client builders + the router-reply parser over a real WebSocket. (crossbar / autobahn are the production references.) Bench + attack next. | | DDS | - | - | - | RTPS; a real DDS peer (CycloneDDS) | | DNP3 / IEC-60870 / MMS+GOOSE / S7comm | - | ~ | - | industrial SCADA - **all implemented codecs** (src/services + native tests), NOT roadmap. **DNP3 bench DONE host+device** (performance_benching/bench_dnp3.cppcrc16 215 ns / build/parse ~550 ns host; rig/benchpc_dnp3_parse_frame**~15.6 us / 3753 cyc device** - the table-less CRC dominates). **DNP3 attack DONE:**pc_dnp3_frame_fuzzHELD (rig/dnp3/parseendpoint; 12 malformed frames - bad start word, LEN under/over-run, header + block CRC corruption, truncated header/block, all-0xFF, 2 KB blob - all rejected, a valid frame still accepted, deterministic). So DNP3 is **bench OK + attack OK**; interop needs the DNP3 **application layer** (function codes/objects) built on the data-link codec to exchange with opendnp3/lib60870/etc - feature work, not just wiring. **S7comm bench DONE host+device** (performance_benching/bench_s7comm.cpp+ rig/benchpc_s7_parse_header**~0.54 us / 129 cyc**) **+ attack DONE** (s7comm_frame_fuzzHELD: 11 malformed PDUs - bad proto-id/ROSCTR, length lies, truncation - rejected, valid parses; deterministic warm). DNP3+BACnet+S7comm+IEC-60870 now all bench+attack. **IEC-60870-5-104 bench DONE host+device** (performance_benching/bench_iec60870.cpp: build_i 8.3 ns / parse 5.8 ns / asdu_parse_header 5.0 ns host; rig/benchpc_iec104_parse**~0.47 us / 112 cyc device**) **+ attack DONE** (pc_iec104_frame_fuzzHELD on HW via rig/iec104/parse: 14/15 malformed APDUs - bad start octet, length under/over-run, truncated APCI, degenerate I/S/U control, short chained ASDU, all-0xFF - rejected/sliced without over-read, valid I-frame still parses; 15th oversized blob refused by the 256 B body cap; heap flat after warmup). **MMS (IEC 61850) host bench added** (performance_benching/bench_mms.cpp: read_request build 101 ns / read_response build 53 ns / parse 8.5 ns - the nested-BER ObjectName encode dominates the request; parse is a trivial TLV walk). **GOOSE (IEC 61850) host bench added** (performance_benching/bench_goose.cpp: goose_pdu build 131 ns / goose_frame build 135 ns) - GOOSE is publish-only (no parser -> NO fuzz-attack surface, bench-only like NTS; interop needs raw-L2 multicast + an Ethernet PHY). **EtherNet/IP host bench added** (performance_benching/bench_enip.cpp: pc_eip_build 7.5 ns / pc_eip_parse 4.9 ns / register_session 2.9 ns - fixed 24-octet encapsulation header, fast fixed-field class; has a real parser so device bench + enip_frame_fuzz are the next increments). **PROFINET DCP host bench added** (performance_benching/bench_profinet.cpp: dcp_header 4.9 ns / parse_header 4.0 ns / dcp_walk 7.5 ns - fixed 10-octet header + TLV blocks; dcp_walk is the fuzz target, device bench + profinet_dcp_fuzz next; interop needs raw-L2 + a PHY). Same shape for the rest of the family (codec + parser-fuzz coverable now; full interop needs the app/role layer) | | EtherNet/IP / Profinet / Profibus / CANopen / DeviceNet / CC-Link / Sercos / Powerlink / Interbus / FINS / Melsec | - | ~(some) | - | industrial fieldbus family; large interop gap | | BACnet | - | - | - | building automation (ASHRAE 135, BVLC+NPDU codec). **Bench DONE host+device** (performance_benching/bench_bacnet.cpp+ rig/benchbacnet_npdu_parse**~0.42 us / 101 cyc** - no CRC, ~37x lighter than DNP3). **Attack DONE:**bacnet_frame_fuzzHELD (rig/bacnet/parse; 12 malformed BVLC/NPDU datagrams - bad type/version, length lies, DLEN/SLEN over-runs, truncation - all rejected, a valid one still parses; deterministic warm). BACnet is bench OK + attack OK; interop needs the APDU/application layer (bacnet-stack peer) | | Sparkplug B | OK | - | - | **Interop DONE (2026-07-12, device-as-client over a real MQTT broker).**sparkplugpeer **9/9 HW**: starts mosquitto, subscribesspBv1.0/#, triggers the rigGET /sparkplug/probe(the device dials the broker and publishes an NBIRTH tospBv1.0/pc/NBIRTH/rig, built by the sparkplug + protobuf codecs), and decodes the TahuPayloadprotobuf with an INDEPENDENT stdlib decoder - asserting the topic + the metric names/datatypes/values (float 23.5, uint32, string). A genuine broker hop (mosquitto logspc-rig-spbconnecting + publishing). Bench + attack next. | | SunSpec | OK | - | - | **Interop DONE (2026-07-12, device-as-server over Modbus TCP).**sunspecpeer **6/6 HW**: the rig seeds a SunSpec device-information map (SunS marker + Common model ID 1, 66-reg body + end model) into its Modbus holding registers at base 100 (clear of the plain-Modbus interop's low regs, PC_MODBUS_HOLDING_REGS=192), and **pysunspec2** (the reference SunSpec Alliance client) scans the model chain over Modbus TCP and reads the Common model - asserting Mn/Md/SN/Vr. Validates the SunSpec codec + register layout against the reference client, reusing the Modbus slave. Bench + attack next. | | SEP2 (IEEE 2030.5) | OK | - | - | **Interop DONE (2026-07-12, device-as-server).**sep2peer **18/18 HW**: the rig serves the sep2.h resource documents asapplication/sep+xml- DeviceCapability at /dcap, EndDevice at /edev, DERControl at /derc. An independent spec consumer (stdlib XML) walks DeviceCapability -> the linked resources and validates theurn:ieee:std:2030.5:nsnamespace, the resource vocabulary, the href graph (EndDeviceListLink resolves), and the values (sFDI/lFDI, the DER interval, opModFixedW=-1500 W). Served over plain HTTP; production 2030.5 mandates TLS + client certs (the official sep.xsd / certified 2030.5 clients are the references). Bench + attack next. | | OpenADR 3.0 | OK | - | - | **Interop DONE (2026-07-12, device-as-server / VEN).**openadrpeer **17/17 HW**: the rig serves the openadr.h JSON objects over HTTP - an EVENT at /openadr/event (programID + eventName + an intervals array of typed payload points) and a REPORT at /openadr/report (a resource READING answering an event). An independent OpenADR 3.0 consumer (stdlib json) validates the 3.0 object model - objectType, programID, the intervals/interval/payloads/values nesting, the resources array - and the numeric values. Served over plain HTTP; production 3.0 rides HTTPS + OAuth2 (the OpenADR 3.0 OpenAPI / openleadr are the references). Bench + attack next. | | GraphQL | OK | - | - | **Interop DONE (2026-07-12, device-as-server).**graphqlpeer **10/10 HW** on the full rigPOST /graphql(pc_graphql_execute+ a device resolver): the reference **graphql-core** parses/validates each query and derives the requested field shape, and the device's{"data":...}response mirrors it exactly (the subset property:{ device { name } }returns onlyname), resolves live scalars (uptime/heap), flows an argument to the leaf (sensor(id: 7) { value }-> 71), handles a named operation, and fails closed on a malformed query (errors + 400). Bench + attack (query-complexity bomb) next. | | gRPC-web | - | - | - | web-API; grpcurl / a gRPC-web client (HTTP/1.1 length-prefixed frames + protobuf body) | | SMTP client | OK | OK | OK | aiosmtpd peer (6/6 HW: EHLO/MAIL/RCPT/DATA + message verified server-side) via rig /smtp/probe device-as-client; smtp_run full-dialogue host+device bench (56.5us); smtp_malicious_server (10 personalities: oversized/unterminated greeting, multiline flood, huge continuation, wrong codes per stage, mid-dialog close - all held) | | syslog | OK | OK | OK | stdlib UDP collector peer (7/7 HW: PRI/VERSION/HOSTNAME/APP-NAME/MSG validated as RFC 5424) via rig /syslog/probe device-as-client; pc_syslog_format host+device bench (15.4us); pc_syslog_injection (CRLF/LF forging, NUL truncate, control bytes, 2KB oversized -> bounded 80B) - held; CR/LF passthrough noted INFO (CWE-117, spec-permitted over UDP) | | NTP server | OK | OK | OK | real ntplib client peer (6/6 HW: mode-4, origin echo, stratum 2, LOCL ref-id, plausible epoch) on UDP/123; pc_ntp_server_build_response host+device bench (1.37us); pc_ntp_server_abuse (amplification matrix -> max 1.00x = NOT a reflector, 64/64 mode/version combos, malformed/oversized) - held. Rig seeds a synthetic time source. NTP _client_ = esp-idf SNTP wrapper (no library codec). **NTS (RFC 8915) framing bench added, host + device** (performance_benching/bench_nts.cpp+ rig/benchnts_ke_parse: host ke_parse 18.9 ns, **device ~1.1 us / 270 cyc**). NTS is **codec-only** (record/EF build + parse helpers; no running NTS-KE endpoint - zero callers outsideservices/timing_position/nts/, not in the session dispatcher), so interop + attack are **n/a** like the other pure codecs; parser robustness against truncated/malformed records is covered bynative_nts. **NTS coverage COMPLETE for what applies.** | | DNS server | OK | OK | OK | real dnspython client peer (11/11 HW: 3 A records + AA flag + NXDOMAIN) on UDP/53; pc_dns_server_build_response host+device bench (1.75us); pc_dns_server_abuse (open-resolver check -> False, amplification matrix -> max 1.64x, compression-pointer loop + 14 malformed/opcode cases) - held. Authoritative-only, NOT an open reflector | | mDNS | n/a | n/a | n/a | **esp-idf wrapper, not a library wire codec.**mdns_service.cppis a thin shim over the ESP-IDFmdnscomponent (mdns_init/mdns_hostname_set/mdns_service_add) - the multicast responder + wire protocol are esp-idf's, so interop/attack/bench of the wire would test esp-idf, not this library (same status as the NTP _client_ = esp-idf SNTP wrapper). The library's OWN mDNS code is the adaptive-beacon backoff (mdns_adaptive: contention-driven interval doubling/halving), a control-plane timing calc (not a per-packet hot op) covered bynative_mdns_adaptive`. Covered for what applies. | | MTConnect | OK | OK | OK | stdlib probe/current/sample peer (9/9 HW on rig); streams-doc bench; mtconnect_param_abuse (from/count max-u64/u32, malformed, oversized -> bounded 793B, held) | | Zigbee / Z-Wave / LoRa / Sigfox / Wi-SUN / Thread / ESP-NOW | - | - | - | RF; needs radios (HW plan) - interop is device-to-device |

Priorities (tractable next, easiest reference peer first)

  1. SSE interop peer + connection-exhaustion attack DONE (2026-07-11) - WHATWG EventSource peer (HW 8/8), pc_sse_format host+device bench, exhaustion+churn attacks. The exhaustion attack found a real permanent-DoS bug (SSE pc_sse_pool slot leak wedging the whole server), root-caused over JTAG and fixed (http_release_upgrade_bindings); see docs/BUGS.md. 1b. WebDAV peer + bench + attacks DONE (2026-07-11) - stdlib RFC 4918 client peer (17/17 HW, full OPTIONS/PROPFIND/PUT/GET/MKCOL/COPY/MOVE/DELETE round trip), pc_webdav_ms_entry host+device bench, 5 advanced attacks (Depth:infinity amplification, XXE + billion-laughs, Destination/URI traversal, PROPPATCH bomb, LOCK flood) - all held on the rig (WEBDAV+LittleFS enabled on the rig firmware). Audited clean. 1c. Redis peer + bench + attacks DONE (2026-07-11) - device-as-Redis-client vs a real redis-server (6/6 HW, SET verified server-side), RESP host+device bench, redis_resp_fuzz. 1d. FTP peer + bench + attacks DONE (2026-07-11) - device-as-FTP-client vs a real pyftpdlib server (11/11 HW, USER/PASS/TYPE/PASV/STOR + SIZE, upload verified server-side), pc_ftp_parse_reply host+device bench, pc_ftp_malicious_server (9 personalities: oversized/unterminated reply, infinite multiline, malformed 227, PASV bounce, reply-code confusion) - all held. Rig RAM 56.5% (FTP codec is tiny). 1e. SMTP peer + bench + attacks DONE (2026-07-11) - device-as-SMTP-client vs a real aiosmtpd server (6/6 HW, EHLO/MAIL/RCPT/DATA, the alert email verified server-side), smtp_run full-dialogue host+device bench (56.5us), smtp_malicious_server (10 personalities: oversized/unterminated greeting, multiline flood, huge continuation, wrong codes at each stage, mid-dialog close) - all held. 1f. syslog peer + bench + attacks DONE (2026-07-11) - device-as-syslog-client (UDP) vs a stdlib RFC 5424 collector (7/7 HW, PRI/VERSION/HOSTNAME/APP-NAME/MSG validated), pc_syslog_format host+device bench (15.4us), pc_syslog_injection (CRLF/LF log-forging, NUL truncate, control bytes, 2 KB oversized -> bounded)
    • held. Found+fixed a rig regression: MAX_ROUTES (16) had silently dropped the overflow routes since the FTP tick; raised to 28. CR/LF passthrough is INFO (CWE-117, spec-permitted over UDP - the caller sanitizes). 1g. NTP server peer + bench + attacks DONE (2026-07-11) - device-as-server (UDP/123) vs a real ntplib client (6/6 HW: mode-4, origin echo, stratum, LOCL, plausible epoch), pc_ntp_server_build_response host+device bench (1.37us), pc_ntp_server_abuse (amplification matrix -> max 1.00x = NOT a reflector, 64/64 mode/version combos, malformed/oversized) - held. Rig seeds a synthetic time source; needed -DPC_MAX_UDP_LISTENERS=4 (the default 2 was full with CoAP + SNMP). 1h. DNS server peer + bench + attacks DONE (2026-07-11) - device-as-server (UDP/53) vs a real dnspython client (11/11 HW: 3 A records + AA + NXDOMAIN), pc_dns_server_build_response host+device bench (1.75us), pc_dns_server_abuse (open-resolver=False, amplification max 1.64x, compression-pointer loop + 14 malformed/opcode cases) - held. Authoritative-only + rejects question compression pointers = no reflector, no pointer-loop DoS. Bumped -DPC_MAX_UDP_LISTENERS=6 (CoAP+SNMP+NTP+DNS). 1i. NATS peer + bench + attacks DONE (2026-07-11) - device-as-NATS-client vs a real nats-server (7/7 HW: INFO/CONNECT/SUB/PUB + the PUB delivered to an independent subscriber through the broker), pc_nats_build_pub/pc_nats_parse host+device bench (6.55us), pc_nats_malicious_server (10 malformed INFO/MSG personalities) - held. First of the AMQP/STOMP/NATS/WAMP/XMPP messaging family. nats-server on the RPi at 0.0.0.0:4222 (stop the systemd unit that binds 127.0.0.1 first). 1j. STOMP peer + bench + attacks DONE (2026-07-11) - device-as-STOMP-client vs a stdlib spec STOMP 1.2 broker (7/7 HW: CONNECT/SUBSCRIBE/SEND + the SEND captured server-side), pc_stomp_build_frame/ pc_stomp_parse_frame host+device bench (9.64us), pc_stomp_malicious_broker (10 malformed-frame personalities: content-length lie/overflow/mismatch, header flood, over-buffer, no-NUL, unterminated) - held. (coilmq is broken on Python 3.13 - removed pkg_resources - so the peer runs an in-thread stdlib broker.) 1k. statsd peer + bench + attacks DONE (2026-07-11) - device-as-StatsD-client (UDP) vs a stdlib collector (5/5 HW: name:value|type validated), pc_statsd_format host+device bench (4.38us), pc_statsd_injection (newline metric-forging, :/| confusion, 2 KB oversized -> bounded 51B) - held. Newline forging is INFO (CWE-117; StatsD packs \n-separated metrics, so a compliant collector forges - caller sanitizes the name). 1l. JWT peer + bench + attacks DONE (2026-07-11) - device-as-server HS256 bearer-auth vs a real PyJWT client (6/6 HW: valid accepted, wrong-secret rejected, expired rejected by exp), pc_jwt_verify_hs256 host+device bench (141us), pc_jwt_forgery (14/14 forgeries rejected: alg=none, alg-confusion, wrong-secret, sig strip/bitflip/truncate, oversized) - a positive alg-confusion-immunity proof. The token rides the Authorization: Bearer header (the query is capped by MAX_QUERY_LEN).
  2. PSRAM boards now attached (2026-07-11) - the two rig ESP32-S3s have PSRAM, so the PSRAM-gated features are testable with a PSRAM rig env: TLS (device-as-server bench + downgrade/renegotiation/0-RTT/ALPN attacks), HTTP/2 (rapid-reset CVE-2023-44487 + CONTINUATION flood CVE-2024-27316 + HPACK bomb), HTTP/3 / QUIC (initial flood + amplification + QPACK bomb). Build a rig_s3_psram env (board_build with PSRAM + the PC_ENABLE_TLS/HTTP2/HTTP3 flags) - this is the highest-value remaining work.
  3. Messaging family - ALL THREE interop DONE (2026-07-12): ~~XMPP (xmpp, prosody)~~ device-as-client /xmpp/probe vs a stdlib RFC 6120 c2s server (11/11 HW: stream + SASL PLAIN + bind + message); ~~AMQP (amqp, RabbitMQ)~~ /amqp/probe vs a stdlib AMQP 0-9-1 broker (12/12 HW: header + Start-Ok SASL PLAIN + Tune-Ok + Open + Channel.Open + Basic.Publish); ~~WAMP (wamp, crossbar)~~ /wamp/probe vs a stdlib WAMP-over-WebSocket router (12/12 HW: WS handshake w/ wamp.2.json subprotocol + HELLO/SUBSCRIBE/PUBLISH/CALL round trips; added a subprotocol arg to ws_client_build_handshake). Next for all three: bench + attack.
  4. SSH interop against real OpenSSH - the server is now HW-verified end to end (a fatal emit-wiring bug was found + fixed 2026-07-11, BUGS.md), so the remaining work is an automated OpenSSH peer in test/servers/peers/, a KEX/cipher throughput bench, and advanced SSH attacks - no longer a bring-up blocker.
  5. CCOUNT device benches for the remaining hot paths (request parse, response build, CoAP/MQTT codec) through the rig /bench endpoint.

How each dimension is extended

  • Interop: add test/servers/peers/<proto>_peer.py (NAME/HELP/add_args/run), register in interop.py _MODULES; see the existing peers for the shape.
  • Bench: pure host op -> performance_benching/*.cpp (host ns/op); device µs/op -> add the op to the rig /bench endpoint (pentesting/rig_firmware) and read it over HTTP; record both in FEATURE_PERFORMANCE.md.
  • Attack: add an @attack(...) to pentesting/pc_pentest.py gated on the feature flag; keep the determinism + liveness oracles and --authorized; live-fire against the rig.