filter: enforce userspace three-color policers#1410
Conversation
|
@copilot review |
There was a problem hiding this comment.
Pull request overview
Adds a userspace runtime for RFC 2697/2698 three-color policers: snapshots compile into stable name-sorted runtime handles linked from filter terms, TX-selection meters them on live, cached, and missing-neighbor-retry paths, red drops are enforced for then discard, and green/yellow/red/drop counters are surfaced via Rust status, Go ProcessStatus, CLI status formatting, and Prometheus. The firewall three-color-policer userspace capability gate is removed.
Changes:
- Compile three-color policer snapshots into
Arc<ThreeColorPolicerRuntime>keyed by stable IDs and wire them into TX-selection filter evaluation (live and cached/flow-cache). - Plumb new
ThreeColorPolicerStatusthrough Rustprotocol.rs, Go protocol, status formatter, and Prometheus collector with per-color and drop counters. - Remove the capability gate, box the flow key on
PendingNeighPacketso neighbor-retry can re-meter, and update README/docs/plan to reflect the new scope and remaining hardening.
Reviewed changes
Copilot reviewed 33 out of 33 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| userspace-dp/src/filter/mod.rs | Add ThreeColorPolicerRuntime, counters, and FilterState fields/helpers; widen TxSelectionFilterResult and cached result with policer-drop bit and runtime handles. |
| userspace-dp/src/filter/compiler.rs | New parse_filter_state_with_three_color; name-sorted runtime IDs; term linking by policer name. |
| userspace-dp/src/filter/engine.rs | New _runtime_counted evaluators and apply_cached_three_color_policers; metering with hardcoded PacketColor::Green. |
| userspace-dp/src/filter/policer.rs | Expose mode_name/color_blind accessors on policer state. |
| userspace-dp/src/filter/tests.rs | Tests for stable runtime IDs, miss-path counters, and cached metering. |
| userspace-dp/src/filter/README.md | Reword foundation → runtime; list new wiring and remaining limitations. |
| userspace-dp/src/afxdp/tx/cos_classify.rs | Add drop field to CoSTxSelection; new resolve_cos_tx_selection_at; ingress-only-for-policer ingress evaluation. |
| userspace-dp/src/afxdp/tx/dispatch.rs | Pass now_ns and recycle ingress on policer drop. |
| userspace-dp/src/afxdp/tx/mod.rs | Re-export resolve_cos_tx_selection_at. |
| userspace-dp/src/afxdp/poll_descriptor.rs | Run cached three-color policers in flow-cache hit path; clone tx_selection per hit. |
| userspace-dp/src/afxdp/neighbor_dispatch.rs | Use boxed flow key + now_ns to meter policers on retry. |
| userspace-dp/src/afxdp/types/mod.rs | Box flow_key on PendingNeighPacket; update size assert to 232 B. |
| userspace-dp/src/afxdp/mod.rs | Update comment for 232 B per-entry footprint. |
| userspace-dp/src/afxdp/forward_request.rs | Thread now_ns and drop-on-policer through live request build. |
| userspace-dp/src/afxdp/forwarding_build.rs | Plumb three-color snapshots and extend tx_selection_enabled. |
| userspace-dp/src/afxdp/flow_cache.rs | Add cached policer runtime list to CachedTxSelectionDescriptor. |
| userspace-dp/src/afxdp/coordinator/status.rs | Expose three_color_policer_counters(). |
| userspace-dp/src/afxdp/{frame,umem,tests}.rs | Test fixture updates for new params/fields. |
| userspace-dp/src/protocol.rs | ThreeColorPolicerStatus DTO and ProcessStatus field. |
| userspace-dp/src/server/{helpers,lifecycle}.rs | Refresh and seed the new status slice. |
| pkg/dataplane/userspace/protocol.go | Add Go ThreeColorPolicerStatus and ProcessStatus field. |
| pkg/dataplane/userspace/protocol_test.go | Round-trip JSON test. |
| pkg/dataplane/userspace/statusfmt.go | Format three-color policer table. |
| pkg/dataplane/userspace/statusfmt_test.go | Format coverage test. |
| pkg/dataplane/userspace/manager.go | Remove three-color capability gate. |
| pkg/dataplane/userspace/manager_test.go | Flip gate tests to admit/no-gate-closed. |
| pkg/api/metrics.go | Define and emit 4 Prometheus desc for three-color policers. |
| pkg/api/metrics_test.go | Test 8 emitted metrics for one policer. |
| docs/userspace-dataplane-gaps.md | Update capability/gap/tracking tables for #1375. |
| docs/pr/1373-retire-ebpf-dataplane/plan-1375-three-color-policers.md | Update plan status, tests list, and remaining work. |
| let mut cached_tx_selection = | ||
| cached_descriptor.tx_selection.clone(); | ||
| let policer_action = | ||
| crate::filter::apply_cached_three_color_policers( | ||
| &cached_tx_selection.three_color_policers, | ||
| now_ns, | ||
| meta.pkt_len as u64, | ||
| ); | ||
| if policer_action.drop { | ||
| binding.scratch.scratch_recycle.push(desc.addr); | ||
| continue; | ||
| } | ||
| cached_tx_selection.dscp_rewrite = policer_action | ||
| .dscp_rewrite | ||
| .or(cached_tx_selection.dscp_rewrite); |
| #[inline] | ||
| fn apply_term_three_color_policer( | ||
| term: &FilterTerm, | ||
| now_ns: Option<u64>, | ||
| packet_bytes: u64, | ||
| ) -> ThreeColorPolicerAction { | ||
| let Some(runtime) = term.three_color_policer.as_ref() else { | ||
| return ThreeColorPolicerAction::default(); | ||
| }; | ||
| let Some(now_ns) = now_ns else { | ||
| return ThreeColorPolicerAction::default(); | ||
| }; | ||
| let decision = runtime.meter(now_ns, packet_bytes, PacketColor::Green); | ||
| ThreeColorPolicerAction { | ||
| dscp_rewrite: decision.dscp_rewrite, | ||
| drop: decision.drop, | ||
| } | ||
| } | ||
|
|
||
| pub(crate) fn apply_cached_three_color_policers( | ||
| policers: &[Arc<ThreeColorPolicerRuntime>], | ||
| now_ns: u64, | ||
| packet_bytes: u64, | ||
| ) -> ThreeColorPolicerAction { | ||
| let mut action = ThreeColorPolicerAction::default(); | ||
| for policer in policers { | ||
| let decision = policer.meter(now_ns, packet_bytes, PacketColor::Green); | ||
| action.dscp_rewrite = action.dscp_rewrite.or(decision.dscp_rewrite); | ||
| action.drop |= decision.drop; | ||
| } | ||
| action | ||
| } |
Round-1 Gemini review on
|
Round-1 Codex r1 review on
|
|
@copilot review |
Addressed in df03dae and 4986fd5. I removed flow-cache fast-path TX-selection clone churn, switched tunnel/ICMP/embedded-ICMP CoS evaluation to
Addressed in df03dae and 4986fd5. The pending-neighbor packet path already carries inline
Reviewed and patched on 4986fd5 (plus df03dae). Follow-up fixes are in for fast-path clone avoidance and policer bypass hardening on tunnel/ICMP/injected paths with timestamped CoS evaluation and |
Round-2 Gemini r2 review on
|
Round-2 dual-review consolidated synthesis on
|
| Reviewer | Verdict |
|---|---|
| Codex | MERGE-NEEDS-MAJOR (3 findings — pending-forward double-metering, local ICMP flow regression, inject lacks flow) |
| Gemini Pro 3 | MERGE-READY |
Net: MERGE-NEEDS-MAJOR. Codex caught issues Gemini missed.
r1 wins (both reviewers confirm)
- ✓ Box::new hot-path heap alloc removed (Gemini r1 MAJOR 1)
- ✓ tunnel/icmp/inject pass
now_ns+ honorcos.drop(Gemini r1 MAJOR 2 fixed across callers) - ✓ Color-aware semantics handled (Gemini r1 MAJOR 3)
Codex MAJOR 1 — Pending-forward double-metering
build_live_forward_request_from_frame() applies resolve_cos_tx_selection_at() and drops on cos.drop. But if the successful selection has cos_queue_id == None AND dscp_rewrite == None, the request carries no proof selection ran.
enqueue_pending_forwards() treats the same None/None shape as UNRESOLVED → calls resolve_pending_forward_cos_tx_selection() AGAIN.
Worked failure: ordinary forwarded packets + r2 flow-cache fallback descriptor when policer has no CoS queue or DSCP rewrite → packet metered TWICE.
Refs: forward_request.rs:119-135, tx/dispatch.rs:110-118, poll_descriptor.rs:195-199.
Fix: add an explicit selection_ran marker so None/None is distinguished from "not yet evaluated".
Codex MAJOR 2 — Local ICMP three-color policer bypass
afxdp/icmp.rs:20-58: function already receives a reliable &SessionFlow, but r2 names it _flow and instead reparses only UserspaceDpMeta. If flow was parsed from frame (which parse_session_flow_from_bytes() supports) and metadata tuple fields are absent, the local ICMP response bypasses the three-color policer.
Codex MAJOR 3 — Inject path can't enforce policers
InjectPacketRequest has no source/destination tuple fields (userspace-dp/src/protocol.rs:2003-2021). inject_test_packet() builds metadata with zero flow addresses, so parse_session_flow_from_meta(meta) returns None. resolve_cos_tx_selection_at() falls back to default queue/no drop.
Carryover from Codex r1 finding (non-TCP/UDP/ICMP and now inject paths bypass policer).
New root cause: None/None overloaded meaning
Codex identifies: r2 uses None/None as BOTH "selection already ran and produced no queue/rewrite" AND "selection has not run yet". Not a valid production state marker once policer-only configs are admitted.
Tests gap
Existing tests cover helper-level metering and one build_live_forward_request metadata-flow case. Don't drive:
- Production
build_live_forward_request_from_frame()→enqueue_pending_forwards()path - Flow-cache fallback with policer-only
None/None - Local ICMP using frame-derived flow with empty metadata tuple
Plan doc
README still says three-color policers are gated while gap doc says bounded slice is implemented. Contradiction.
Recommendation
Block on:
- Add explicit
selection_ranmarker toPreparedTxRequest/ forwarding state soNone/Nonedoesn't double-meter - Local ICMP: use the passed
&SessionFlow(rename_flow→flow) instead of reparsing meta - Add flow tuple to
InjectPacketRequestOR explicitly gate policer enforcement off for injected packets with documented "control packets bypass policer" semantics - Reconcile README/gap-doc contradiction
Codex task-mpa7x8iz-7pi1ow; Gemini task-mpa7xwbx-2b9iw3. Not merging — author's decision.
Round-3 Codex review on
|
|
@copilot review |
Addressed on 20b1f7b and dec92e0. Pending-forward double-metering is fixed with explicit
The three open r2 MAJORs are closed in 20b1f7b, with docs consistency follow-up in dec92e0.
Reviewed at dec92e0 (with core runtime fixes already in 20b1f7b). No additional blocking correctness or security issues found in the three-color follow-up scope: pending-forward CoS resolution state, local ICMP flow-key usage, and inject emit-on-wire tuple stamping are all present and validated. No UI changes (screenshot not applicable). |
Claude r4 review on
|
Round-4 triple-review synthesis on
|
| Reviewer | Verdict |
|---|---|
| Claude | |
| Codex | MERGE-NEEDS-MAJOR |
| Gemini Pro 3 | MERGE-NEEDS-MAJOR |
Self-correction
My r4 review said the inject 5-tuple MAJOR was closed via the stamp_injected_packet_tuple synthesis. That was wrong. Codex + Gemini both caught it independently. The wire contract is still broken — both Rust InjectPacketRequest (protocol.rs:2002) and Go InjectPacketRequest (protocol.go:1026) lack src_ip, dst_ip, src_port, dst_port. The stamp helper synthesizes a tuple from egress.primary_v4/v6 + dst + slot as u16 + port 0 + forced ICMP/ICMPv6 — that's inventing a tuple, not preserving one. A caller cannot specify a 5-tuple to be metered.
Acknowledging the miss directly: the "defensible design" framing in my r4 review skipped checking the Go side and missed that BuildInjectPacketRequest at pkg/dataplane/userspace/inject.go:33 hardcodes Protocol: 6 while the Rust emit path forces ICMP. The two paths disagree on what gets injected.
MAJOR persists — inject 5-tuple contract
Codex:
"
dec92e0b:userspace-dp/src/protocol.rs:2002still definesInjectPacketRequestwith onlyslot, length, addr family, protocol, generations,metadata_valid,destination_ip, andemit_on_wire. Nosrc_ip,dst_ip,src_port, ordst_port. Go matches that hole atdec92e0b:pkg/dataplane/userspace/protocol.go:1026."
"Worse, Go still only exposes
destination-ipandemit-on-wireinpkg/dataplane/userspace/inject.go:10, whileBuildInjectPacketRequeststill hardcodesProtocol: 6atpkg/dataplane/userspace/inject.go:33. The Rust emit path then meters/sends ICMP anyway."
Gemini: confirmed same — wire untouched, Rust-side synthesis is workaround.
Closed (both reviewers agree)
A. Double-meter: marker cos_tx_selection_resolved: bool added at types/tx.rs:52, set in forward_request.rs:151 + icmp.rs:89, checked via extracted helper pending_forward_needs_cos_tx_selection at tx/dispatch.rs:1186-1196. Old None/None ambiguity gone.
B. ICMP _flow: renamed to flow, parse_session_flow_from_meta(meta) removed, Some(&flow.forward_key) passed directly, flow_key: Some(flow.forward_key.clone()) on emitted TxRequest.
Tests
- Double-meter:
pending_forward_cos_resolution_uses_resolved_bit_not_empty_outputs(tx/dispatch_tests.rs:106-127) pins the resolved-bit predicate ✓ - ICMP flow:
build_local_time_exceeded_request_meters_icmp_flow_key(afxdp/tests.rs:1121-1191) ✓ - Inject: only
stamp_injected_packet_tuple_builds_ipv4_icmp_flow_key(coordinator/tests.rs:13-89) — Codex: "only prove the new synthetic ICMP tuple helper parses. They do not pin Rust/Go protocol fields, CLI parsing, JSON round trip, or preservation of a caller-provided 5-tuple."
tunnel.rs adjacent fix
Codex+Gemini both noted tunnel.rs:232-237 is a related-but-adjacent fix (local tunnel TX now preserves session key + addr_family + protocol instead of zeros). Sensible hardening, scope-tangent.
README claim
README.md:88,100-102 claims bounded three-color policer admission. Codex: "too broad while inject emit-on-wire still cannot carry a requested 5-tuple. Either fix the inject wire contract in Rust and Go, or explicitly carve inject out of the admission claim."
Recommendation
Block on:
- Extend
InjectPacketRequest(Rust protocol.rs AND Go protocol.go AND CLI inject.go) with optional 5-tuple fields. Default to current synthesis behavior when caller doesn't provide; respect caller-provided tuple when present. - README: scope-down the policer admission claim to exclude inject path, OR document that inject is intentionally not policed.
- Inject test should exercise wire-level round-trip with a caller-provided tuple.
A+B are real wins this round. C is the third consecutive round this MAJOR has been raised. Author's call on the wire-contract design.
Codex task: task-mpaaihwo-c6aztl. Gemini task: task-mpaaiy0m-bcmf1d. Not merging — author's decision.
|
@copilot review |
|
Fix pushed for the r5 MAJOR: remote CLI/API silent downgrade.\n\nCommit: e972093\n\nWhat changed:\n- Added helper-capability and tuple-metadata validation before emit_on_wire helper IPC.\n- Remote callers now fail closed instead of silently downgrading to non-tuple inject semantics when the helper or request is stale.\n- Added regression tests for missing helper tuple protocol and legacy request metadata.\n\nValidation:\n- go test ./pkg/dataplane/userspace\n- git diff --check\n\n@copilot review |
Reviewed on e972093. The manager-side fail-closed guard is in place ( |
| cos_flow.as_ref().map(|flow| &flow.forward_key), | ||
| now_ns, | ||
| ); | ||
| if cos.drop { |
| } | ||
| if self.first.is_none() { | ||
| self.first = Some(runtime); | ||
| } else if self.second.is_none() { | ||
| self.second = Some(runtime); | ||
| } | ||
| } |
| if req.tuple_metadata_version != INJECT_PACKET_TUPLE_PROTOCOL_VERSION { | ||
| return Err(format!( | ||
| "emit-on-wire requires tuple metadata version {} (got {})", |
Claude r6 review on
|
Round-6 triple-review synthesis on
|
| Reviewer | Verdict |
|---|---|
| Claude | MERGE-NEEDS-MAJOR |
| Codex | MERGE-NEEDS-MAJOR |
| Gemini Pro 3 | MERGE-NEEDS-MAJOR |
All three converge: remote CLI silent downgrade still persists. Claude's r6 analysis (manager.go validation unreachable from remote CLI) verified by both reviewers with worked reproduction.
What r6 closed
validateInjectPacketRequestForHelper(req, status)inpkg/dataplane/userspace/inject.go:71+rejectsEmitOnWire=truerequests with missing/stale tuple metadata- Manager.InjectPacket calls validator before helper IPC
- Defense-in-depth for local CLI and programmatic callers
- Tests pin the backend fail-closed contract
What r6 did NOT close — remote CLI silent downgrade persists
Codex:
"At
e9720936,cmd/cli/request.go:174-180is still:target = extra["destination-ip"].emit-on-wire,source-ip,source-port,destination-port, andprotocolare still dropped.""At
pkg/grpcapi/server_diag.go:848-852, the server still reconstructs onlyextra = {"destination-ip": req.Target}.""So remote CLI
emit-on-wire true ...becomes a plain non-emit inject request.req.EmitOnWireis false, the new userspace validator returns nil, and the operator still gets silent downgrade."
Gemini (verified by direct quote + worked reproduction):
- Operator:
request chassis cluster data-plane userspace inject-packet slot 7 valid destination-ip 172.16.80.200 emit-on-wire true source-ip 172.16.80.8 source-port 7 destination-port 7 protocol icmpcmd/cli/request.goparsesextra["emit-on-wire"] = "true"but drops it, only setstarget = "172.16.80.200".- gRPC server reconstructs
extra = {"destination-ip": "172.16.80.200"}.BuildInjectPacketRequestsetsreq.EmitOnWire = false.validateInjectPacketRequestForHelperhitsif !req.EmitOnWire { return nil }early-exit.- Operator gets silent non-emit injection. Commit fails to achieve its stated goal.
Recommendation
Block on:
- Extend
SystemActionRequest.Target(or change to structured args) to carryemit-on-wire,source-ip,source-port,destination-port,protocol. - Mirror reconstruction in
pkg/grpcapi/server_diag.goso remote-CLI extras propagate toBuildInjectPacketRequest. - Add a remote-CLI integration test that types the new args and asserts either successful emit OR a clear error (NOT a silent non-emit injection).
OR: change remote CLI to reject emit-on-wire at parse time with an explicit "not supported via remote CLI" error, until the transport is extended. That would also close the silent-downgrade gap and is faster than the full transport fix.
Either approach closes the MAJOR. The current r6 (defense-in-depth at manager.go) is real progress but unreachable from the path operators actually use.
This is now round-3 of this same MAJOR (raised in r4, r5, r6). The fix needs to land at the right layer.
Codex task: task-mpad7d88-38m4po. Gemini task: task-mpad7rew-3quff7. Not merging — author's decision.
|
Pushed Fixes:
Validation:
|
Claude r7 review on
|
Round-7 triple-review synthesis on
|
| Reviewer | Verdict |
|---|---|
| Claude | MERGE-READY |
| Codex | MERGE-READY |
| Gemini Pro 3 | MERGE-READY |
All three converge. Round-4 (r4/r5/r6/r7) MAJOR is closed.
Codex confirmation
"r7 closes the silent downgrade for the r7 CLI → r7 server path."
"EncodeInjectPacketTarget serializes the entire
extra map[string]stringas prefixed JSON, so destination-ip, emit-on-wire, source-ip, source-port, destination-port, and protocol all ride in Target if present.""DecodeInjectPacketTarget restores the JSON map, and non-prefixed legacy targets become map[destination-ip]=target."
"system_action_test.go:127-170 encodes full extras, calls SystemAction, and verifies the provider receives an InjectPacketRequest with EmitOnWire, tuple metadata, family/protocol, source/destination IP, and both ports."
"Format is stable enough:
xpf-inject-extra:+ JSON object. IPv6:is safe inside JSON strings."
Gemini confirmation
All five hostile checks (A–E) verified with quoted evidence:
- CLI uses
EncodeInjectPacketTarget(extra)✓ - Server uses
DecodeInjectPacketTarget(req.Target)✓ - JSON marshalling handles IPv6 colons natively ✓
TestSystemActionUserspaceInjectDecodesEmitOnWireTargetExtrasend-to-end test exists ✓- Backward compat for legacy bare destination-ip ✓
Validation chain now reachable
Codex traces the full r7 path:
"
cmd/cli/request.go:180encodes extras → gRPCSystemActionRequest.Target→server_diag.go:848decodes extras →BuildInjectPacketRequestsetsEmitOnWireand callspopulateInjectPacketTuple. The first fail-closed gate fires there if helper tuple protocol is missing (inject.go:149-153), returningInvalidArgumentbeforeInjectPacket. If it gets as far as the userspace manager, the r6 pre-IPC gate still fires atmanager.go:1570viavalidateInjectPacketRequestForHelper."
So the r6 fail-closed gate (which was unreachable from remote CLI in r6) is now reachable AND there's an additional fail-closed gate at the build step. Defense-in-depth: two gates, both visible to the operator as gRPC errors.
Codex caveat (non-blocking)
"Caveat: a pre-r7 CLI that already dropped
emit-on-wirecannot be rescued by the new server, because the emit fields are gone before transmission."
This is correct and acceptable — old CLI binaries can't emit-on-wire. The fix requires both ends to be r7. Worth noting in deployment notes.
Recommendation
Merge-ready. Closes the round-3 MAJOR raised in r4, r5, r6. A four-round saga concluded.
Codex task: task-mpaljt4t-1f7xfj. Gemini task: task-mpalk6d3-iu3d09. Not merging — author's decision.
50e2df4 to
f992cee
Compare
| #[inline] | ||
| pub(crate) fn push(&mut self, runtime: Arc<ThreeColorPolicerRuntime>) { | ||
| if self | ||
| .first | ||
| .as_ref() | ||
| .is_some_and(|existing| existing.id == runtime.id) | ||
| || self | ||
| .second | ||
| .as_ref() | ||
| .is_some_and(|existing| existing.id == runtime.id) | ||
| { | ||
| return; | ||
| } | ||
| if self.first.is_none() { | ||
| self.first = Some(runtime); | ||
| } else if self.second.is_none() { | ||
| self.second = Some(runtime); | ||
| } | ||
| } | ||
|
|
||
| #[inline] | ||
| pub(crate) fn extend(&mut self, other: Self) { | ||
| if let Some(runtime) = other.first { | ||
| self.push(runtime); | ||
| } | ||
| if let Some(runtime) = other.second { | ||
| self.push(runtime); | ||
| } | ||
| } |
| now_ns, | ||
| ); | ||
| if cos.drop { | ||
| return Ok(()); |
Agent-Logs-Url: https://github.com/psaab/xpf/sessions/c323a8b5-3faa-4ca0-a061-95df2c59ed09 Co-authored-by: psaab <196946+psaab@users.noreply.github.com>
Agent-Logs-Url: https://github.com/psaab/xpf/sessions/c323a8b5-3faa-4ca0-a061-95df2c59ed09 Co-authored-by: psaab <196946+psaab@users.noreply.github.com>
Agent-Logs-Url: https://github.com/psaab/xpf/sessions/fa2df8f5-5bad-4297-837d-7040894e62a5 Co-authored-by: psaab <196946+psaab@users.noreply.github.com>
f992cee to
9b73589
Compare
Enforces userspace three-color policers (#1410) in filter. - filter: tighten three-color runtime slice - userspace-dp: harden three-color policer enforcement paths - userspace-dp: avoid unnecessary cached tx descriptor build - userspace-dp: fix three-color pending forward paths - docs: align three-color policer admission wording - userspace: carry inject tuple metadata on wire Touches userspace-dp/src/afxdp, userspace-dp/src/filter, pkg/dataplane/userspace, and userspace-dp/src/protocol.rs across documentation, tests, configuration schema, userspace dataplane, dataplane integration, and operator/API output. The largest file deltas are userspace-dp/src/afxdp/frame/tests.rs, userspace-dp/src/afxdp/tx/cos_classify.rs, and userspace-dp/src/filter/tests.rs. The diff is 2861 additions and 305 deletions across 49 files.
Summary
Refs #1375.
Adds a bounded runtime/counter slice for userspace RFC 2697/2698 three-color policers:
then discardThis does not claim complete #1375 retirement. Remaining hardening/evidence:
Validation
cargo checkcargo test three_colorgo test ./pkg/dataplane/userspace ./pkg/apigit diff --check