* Add temporal graph evolution & RuVector integration research GOAP Agent 8 output: 1,528-line SOTA research document covering temporal graph models (TGN, JODIE, DyRep), RuVector graph memory design, mincut trajectory tracking with Kalman filtering, event detection pipelines, compressed temporal storage, cross-room transition graphs, and a 5-phase integration roadmap. Part of RF Topological Sensing research swarm (10 agents). https://claude.ai/code/session_01DGUAowNScGVp88bK2eiuRv * Add transformer architectures for graph sensing research GOAP Agent 4 output: 896-line SOTA document covering Graph Transformers (Graphormer, SAN, GPS, TokenGT), Temporal Graph Transformers (TGN, TGAT, DyRep), ViT for RF spectrograms, transformer-based mincut prediction, positional encoding for RF graphs, foundation models for RF sensing, and efficient edge deployment with INT8 quantization. Part of RF Topological Sensing research swarm (10 agents). https://claude.ai/code/session_01DGUAowNScGVp88bK2eiuRv * Add attention mechanisms for RF sensing research GOAP Agent 3 output: 1,110-line document covering GAT for RF graphs, self-attention for CSI sequences, cross-attention multi-link fusion, attention-weighted differentiable mincut, spatial node attention, antenna-level subcarrier attention, and efficient attention variants (linear, sparse, LSH, S4/Mamba). 8 ASCII architecture diagrams. Part of RF Topological Sensing research swarm (10 agents). https://claude.ai/code/session_01DGUAowNScGVp88bK2eiuRv * Add sublinear mincut algorithms research GOAP Agent 5 output: 698-line document covering classical mincut complexity, sublinear approximation (sampling, sparsifiers), dynamic mincut with lazy recomputation hybrid, streaming sketch algorithms, Benczur-Karger sparsification, local partitioning (PageRank-guided cuts), randomized methods reliability analysis, and Rust implementation with const-generic RfGraph, zero-alloc Stoer-Wagner, SIMD batch updates. Part of RF Topological Sensing research swarm (10 agents). https://claude.ai/code/session_01DGUAowNScGVp88bK2eiuRv * Add CSI edge weight computation research GOAP Agent 2 output: ~700-line document covering CSI feature extraction, coherence metrics (cross-correlation, mutual information, phasor coherence), multipath stability scoring (MUSIC, ESPRIT, ISTA), temporal windowing (EMA, Welford, Kalman), noise robustness (phase noise, AGC, clock drift), edge weight normalization, and implementation architecture showing 32KB memory for 120 edges within ESP32-S3 capability. Part of RF Topological Sensing research swarm (10 agents). https://claude.ai/code/session_01DGUAowNScGVp88bK2eiuRv * Add contrastive learning for RF coherence research GOAP Agent 7 output: 1,226-line document covering SimCLR/MoCo/BYOL for CSI, AETHER-Topo dual-head extension, coherence boundary detection with multi-scale analysis, delta-driven updates (2-12x efficiency), self-supervised pre-training protocol, triplet networks for 5-state edge classification, and MERIDIAN cross-environment transfer with EWC continual learning. Part of RF Topological Sensing research swarm (12 agents). https://claude.ai/code/session_01DGUAowNScGVp88bK2eiuRv * Add resolution and spatial granularity analysis research GOAP Agent 9 output: 1,383-line document covering Fresnel zone analysis, node density vs resolution (16-node/5m room → 30-60cm), Cramer-Rao lower bounds with Fisher Information Matrix, graph cut resolution theory, multi-frequency enhancement (6cm coherent dual-band limit), RF tomography comparison, experimental validation protocols, and resolution scaling laws (8.8cm theoretical limit). Part of RF Topological Sensing research swarm (12 agents). https://claude.ai/code/session_01DGUAowNScGVp88bK2eiuRv * Add RF graph theory and minimum cut foundations research GOAP Agent 1 output: Graph-theoretic foundations covering max-flow/min-cut for RF (Ford-Fulkerson, Stoer-Wagner, Karger), RF as dynamic graph with CSI coherence weights, topological change detection via Fiedler vector and Cheeger inequality, dynamic graph algorithms, comparison to classical RF sensing, formal mathematical framework, and 9 open research questions. Part of RF Topological Sensing research swarm (12 agents). https://claude.ai/code/session_01DGUAowNScGVp88bK2eiuRv * Add ESP32 mesh hardware constraints research GOAP Agent 6 output: ESP32 CSI capabilities (52/114 subcarriers), 16-node mesh topology with 120 edges, TDM synchronized sensing (3ms slots), computational budget (Stoer-Wagner uses 0.07% of one core), channel hopping, power analysis (0.44W/node), dual-core firmware architecture, and edge vs server computing with 100x data reduction on-device. Part of RF Topological Sensing research swarm (12 agents). https://claude.ai/code/session_01DGUAowNScGVp88bK2eiuRv * Add system architecture and prototype design research GOAP Agent 10 output: End-to-end architecture with pipeline diagrams, existing crate integration mapping, new rf_topology module design (DDD aggregate roots), 100ms latency budget breakdown, 3-phase prototype plan (4-node POC → 16-node room → 72-node multi-room), benchmark design with 8 metrics, ADR-044 draft, and Rust trait definitions (EdgeWeightComputer, TopologyGraph, MinCutSolver, BoundaryInterpolator). Part of RF Topological Sensing research swarm (12 agents). https://claude.ai/code/session_01DGUAowNScGVp88bK2eiuRv * Add quantum sensing and quantum biomedical research documents Agent 11: Quantum-level sensors (729 lines) — NV centers, SQUIDs, Rydberg atoms, quantum illumination, quantum graph theory (walks, spectral, QAOA), hybrid classical-quantum architecture, quantum ML (VQC, kernels, reservoir computing), NISQ applications (D-Wave, VQE), hardware roadmap. Agent 12: Quantum biomedical sensing (827 lines) — whole body biomagnetic mapping, neural field imaging without electrodes, circulation sensing, cellular EM signaling, non-contact diagnostics, coherence-based diagnostics (disease as coherence breakdown), neural interfaces, multimodal observatory, room-scale ambient health monitoring, graph-based biomedical analysis. Part of RF Topological Sensing research swarm (12 agents). https://claude.ai/code/session_01DGUAowNScGVp88bK2eiuRv * Add research index synthesizing all 12 documents (14,322 lines) Master index for RF Topological Sensing research compendium covering: graph theory foundations, CSI edge weights, attention mechanisms, transformers, sublinear algorithms, ESP32 hardware, contrastive learning, temporal graphs, resolution analysis, system architecture, quantum sensors, and quantum biomedical sensing. Includes key findings, proposed ADRs (044, 045), and 5-phase implementation roadmap. https://claude.ai/code/session_01DGUAowNScGVp88bK2eiuRv * Add SOTA neural decoding landscape and 10 application domains research - Doc 21: Comprehensive SOTA map (2023-2026) of brain sensors, decoders, and visualization systems with RuVector/mincut positioning analysis - Doc 22: Ten application domains for brain state observatory including disease detection, BCI, cognitive monitoring, mental health diagnostics, neurofeedback, dream reconstruction, cognitive research, HCI, wearables, and brain network digital twins with strategic roadmap https://claude.ai/code/session_01DGUAowNScGVp88bK2eiuRv * Add NV diamond neural magnetometry research document (13/22) Comprehensive 600+ line document covering NV center physics, neural magnetic field sources, sensor architecture, SQUID comparison, signal processing pipeline, RuVector integration, and development roadmap. https://claude.ai/code/session_01DGUAowNScGVp88bK2eiuRv * Add ruv-neural workspace Cargo.toml with 12 crate definitions Workspace structure for the rUv Neural brain topology analysis system. 12 mix-and-match crates with shared dependencies including RuVector integration, petgraph, rustfft, and WASM/ESP32 support. https://claude.ai/code/session_01DGUAowNScGVp88bK2eiuRv * Add ruv-neural crate ecosystem — 12 mix-and-match crates (WIP) Initial implementation of the rUv Neural brain topology analysis system: - ruv-neural-core: Core types, traits, errors, RVF format (compiles) - ruv-neural-sensor: NV diamond, OPM, EEG sensor interfaces (in progress) - ruv-neural-signal: DSP, filtering, spectral, connectivity (in progress) - ruv-neural-graph: Brain connectivity graph construction (in progress) - ruv-neural-mincut: Dynamic minimum cut topology analysis (in progress) - ruv-neural-embed: RuVector graph embeddings (in progress) - ruv-neural-memory: Persistent neural state memory + HNSW (compiles) - ruv-neural-decoder: Cognitive state classification + BCI (in progress) - ruv-neural-esp32: ESP32 edge sensor integration (compiles) - ruv-neural-wasm: WebAssembly browser bindings (in progress) - ruv-neural-viz: Visualization + ASCII rendering (in progress) - ruv-neural-cli: CLI tool (in progress) Agents still writing remaining modules. Next: fix compilation, tests, push. https://claude.ai/code/session_01DGUAowNScGVp88bK2eiuRv * Fix ruv-neural crate compilation: all 12 crates build and 1200+ tests pass - Fix node2vec.rs type inference error (Vec<_> → Vec<Vec<f64>>) - Fix artifact.rs with full filter-based detection implementations - Fix signal crate ConnectivityMetric re-export and trait method names - Fix embed crate EmbeddingGenerator trait implementations - Complete spectral, topology, and node2vec embedders with tests - Complete preprocessing pipeline with sequential stage processing - All workspace crates compile cleanly, 0 test failures https://claude.ai/code/session_01DGUAowNScGVp88bK2eiuRv * Add ruv-neural-cli README https://claude.ai/code/session_01DGUAowNScGVp88bK2eiuRv * fix: convert desktop icons from RGB to RGBA for Tauri build Tauri's generate_context!() macro requires RGBA PNG icons. All 5 icon files (32x32.png, 128x128.png, 128x128@2x.png, icon.icns, icon.ico) were RGB-only, causing a proc macro panic on Linux builds. Fixes #200 Co-Authored-By: claude-flow <ruv@ruv.net> * Add Subcarrier Manifold and Vitals Oracle modules for 3D visualizations - Implemented Subcarrier Manifold to visualize amplitude data as a 3D surface with height and age attributes. - Created Vitals Oracle to represent vital signs using toroidal rings and particle trails, incorporating breathing and heart rate dynamics. - Both modules utilize Three.js for rendering and include custom shaders for visual effects. * feat: complete ruv-neural implementation — physics models, security, witness verification Replace all stubs/mocks with production physics-based signal models: - NV Diamond: ODMR Lorentzian dip, 1/f pink noise (Voss-McCartney), brain oscillations - OPM: SERF-mode, 50/60Hz powerline harmonics, full cross-talk compensation via Gaussian elimination with partial pivoting - EEG: 5 frequency bands, eye blink artifacts (Fp1/Fp2), muscle artifacts, impedance-based thermal noise floor - ESP32 ADC: ring-buffer reader with calibration signal generator, i16 clamp Security hardening (SEC-001 through SEC-005): - RVF bounded allocation (16MB metadata, 256MB payload) - sample_rate validation (>0, finite) - Signal NaN/Inf rejection - ADC resolution_bits overflow clamp - HNSW HashSet visited tracking + bounds checks Performance optimizations (PERF-001 through PERF-005): - 67x fewer FFTs via pre-computed analytic signals - VecDeque O(1) eviction in memory store - Thread-local FFT planner caching - BrainGraph::validate() for edge/weight integrity - Eigenvalue convergence early termination Ed25519 witness verification system: - 41 capability attestations across all 12 crates - SHA-256 digest + Ed25519 signature - CLI commands: `witness --output` and `witness --verify` README: ethics warning, hardware parts list (AliExpress), assembly instructions Co-Authored-By: claude-flow <ruv@ruv.net> * docs: add crates.io badges and install instructions to ruv-neural README Add version badges linking to each published crate on crates.io, cargo add instructions, and crate search link in the Crate Map table. Co-Authored-By: claude-flow <ruv@ruv.net> --------- Co-authored-by: Claude <noreply@anthropic.com>
29 KiB
ruv-neural Crate System: Security and Performance Review
Date: 2026-03-09 Version: 0.1.0 Scope: All 12 workspace crates in the ruv-neural system Status: Implementation checklist for v0.1 and v0.2 milestones
Table of Contents
Crate Inventory
| Crate | Status | Lines (approx) | Role |
|---|---|---|---|
ruv-neural-core |
Implemented | ~500 | Types, traits, error types, RVF format |
ruv-neural-sensor |
Implemented | ~170 | Sensor data acquisition, calibration, quality |
ruv-neural-signal |
Implemented | ~450 | Filtering, spectral analysis, Hilbert, connectivity |
ruv-neural-graph |
Stub | ~2 | Graph construction from signals |
ruv-neural-mincut |
Implemented | ~700 | Stoer-Wagner, spectral cut, Cheeger, dynamic tracking |
ruv-neural-embed |
Implemented | ~350 | Spectral, topology, node2vec embeddings |
ruv-neural-memory |
Implemented | ~425 | Embedding store, HNSW index |
ruv-neural-decoder |
Implemented (lib) | ~25 | KNN, threshold, transition decoders |
ruv-neural-esp32 |
Implemented | ~265 | ADC interface, sensor readout |
ruv-neural-wasm |
Stub | ~2 | WebAssembly bindings |
ruv-neural-viz |
Implemented (lib) | ~20 | Visualization, ASCII rendering, export |
ruv-neural-cli |
Stub | ~2 | CLI binary |
Security Review
Input Validation
All public APIs must validate their inputs at system boundaries. This section catalogs each validation requirement and its current status.
Sensor Data Validation
| Check | Required In | Status | Notes |
|---|---|---|---|
sample_rate_hz > 0 |
MultiChannelTimeSeries::new |
MISSING | Constructor accepts sample_rate_hz without validating it is positive and finite. Division by zero in duration_s() if zero. |
num_channels > 0 |
MultiChannelTimeSeries::new |
PASS | Returns error if data.len() == 0. |
| Channel lengths equal | MultiChannelTimeSeries::new |
PASS | Validates all channels have the same length. |
| Non-NaN/Inf values | All signal processing | MISSING | No validation that input signals contain only finite f64 values. NaN propagation through FFT, PLV, and connectivity metrics produces silent garbage. |
num_samples > 0 |
AdcReader::read_samples |
PASS | Returns error if num_samples == 0. |
| Channel count > 0 | AdcReader::read_samples |
PASS | Returns error if no channels configured. |
| Channel index bounds | AdcReader::load_buffer |
PASS | Returns ChannelOutOfRange error. |
sensitivity > 0 |
SensorChannel |
MISSING | sensitivity_ft_sqrt_hz is a public field with no validation on construction. |
sample_rate > 0 |
SensorChannel |
MISSING | sample_rate_hz is a public field with no validation. |
Recommendation: Add a SensorChannel::new() constructor that validates sensitivity_ft_sqrt_hz > 0, sample_rate_hz > 0, and that the orientation vector is a unit normal. Add sample_rate_hz > 0 and sample_rate_hz.is_finite() checks to MultiChannelTimeSeries::new. Add a validate_finite() utility for signal data.
Graph Construction Validation
| Check | Required In | Status | Notes |
|---|---|---|---|
Edge indices < num_nodes |
BrainGraph::adjacency_matrix |
PARTIAL | Silently skips out-of-bounds edges rather than reporting an error. This masks data corruption. |
| Edge weight is finite | BrainGraph |
MISSING | BrainEdge.weight is not validated. NaN/Inf weights propagate silently through Stoer-Wagner and spectral analysis. |
num_nodes >= 2 |
stoer_wagner_mincut |
PASS | Returns proper error. |
num_nodes >= 2 |
fiedler_decomposition |
PASS | Returns proper error. |
num_nodes >= 2 |
SpectralEmbedder::embed |
PASS | Returns proper error. |
num_nodes >= 2 |
cheeger_constant |
PASS | Returns proper error. |
| Self-loops | BrainGraph |
MISSING | No validation that source != target on edges. Self-loops could inflate degree calculations. |
Recommendation: Add a BrainGraph::validate() method that checks all edge indices are within bounds, weights are finite, and no self-loops exist. Call it from stoer_wagner_mincut, spectral_bisection, and SpectralEmbedder::embed. Consider making adjacency_matrix() return Result with an error for out-of-bounds edges instead of silently ignoring them.
RVF Format Validation
| Check | Required In | Status | Notes |
|---|---|---|---|
| Magic bytes | RvfHeader::validate |
PASS | Validates against RVF_MAGIC. |
| Version | RvfHeader::validate |
PASS | Rejects unknown versions. |
| Header length | RvfHeader::from_bytes |
PASS | Checks bytes.len() < 22. |
| Data type tag | RvfDataType::from_tag |
PASS | Returns error for unknown tags. |
metadata_json_len overflow |
RvfFile::read_from |
CONCERN | metadata_json_len is cast from u32 to usize and used to allocate a Vec. A malicious file with metadata_json_len = u32::MAX (~4 GB) would cause an OOM allocation. |
| Payload length | RvfFile::read_from |
CONCERN | read_to_end reads unbounded data into memory. A malicious file could exhaust memory. |
| JSON validity | RvfFile::read_from |
PASS | Uses serde_json::from_slice which returns an error on invalid JSON. |
num_entries vs actual data |
RvfFile::read_from |
MISSING | The header declares num_entries and embedding_dim, but these are never cross-checked against the actual payload size. |
Recommendation: Add maximum size limits for metadata_json_len (e.g., 16 MB) and total payload size. Validate that num_entries * entry_size_for_type <= data.len() after reading. Use Read::take() to cap reads.
Embedding Validation
| Check | Required In | Status | Notes |
|---|---|---|---|
| Non-empty vector | NeuralEmbedding::new (core) |
PASS | Returns error for empty vectors. |
| Non-empty vector | NeuralEmbedding::new (embed) |
PASS | Returns error for empty vectors. |
| Dimension match | cosine_similarity, euclidean_distance |
PASS | Returns DimensionMismatch error. |
| Zero-norm handling | cosine_similarity |
PASS | Returns 0.0 for zero-norm vectors. |
| NaN/Inf in vector | NeuralEmbedding::new |
MISSING | No check for non-finite values in the embedding vector. |
Memory Store Validation
| Check | Required In | Status | Notes |
|---|---|---|---|
| Capacity > 0 | NeuralMemoryStore::new |
MISSING | Capacity 0 is accepted, producing a store that evicts on every insertion. |
| k > 0 | query_nearest |
MISSING | k=0 produces an empty result silently (acceptable but undocumented). |
| Dimension consistency | NeuralMemoryStore::store |
MISSING | No check that all stored embeddings have the same dimensionality. Mixed dimensions cause silent errors in query_nearest. |
JSON Parsing
| Check | Status | Notes |
|---|---|---|
| Uses serde derive | PASS | All types use #[derive(Serialize, Deserialize)]. No manual parsing anywhere. |
No unsafe JSON parsing |
PASS | Standard serde_json throughout. |
Memory Safety
| Check | Status | Notes |
|---|---|---|
No unsafe code |
PASS | Zero unsafe blocks across all crates. |
| Vec instead of raw pointers | PASS | All data structures use Vec, HashMap, BinaryHeap. |
| ndarray for matrix ops | NOT USED | Despite being listed in workspace.dependencies, matrix operations use Vec<Vec<f64>> throughout. This is bounds-checked but less efficient. |
| No C FFI | PASS | No FFI calls. ESP32 code uses pure Rust types. |
No std::mem::transmute |
PASS | None found. |
No std::ptr usage |
PASS | None found. |
| Bounds checking on slices | PASS | Uses .get(), iterator methods, and Rust's built-in bounds checks. |
| Integer overflow | CONCERN | max_raw_value() in adc.rs casts (1u32 << resolution_bits) - 1 to i16. If resolution_bits > 15, this overflows silently. Currently only 12 or 16 are intended, but 16 produces i16::MAX wrapping. |
Recommendation: Add a validation check on resolution_bits in AdcConfig (must be <= 15 for i16 representation, or switch to u16/i32). Consider migrating Vec<Vec<f64>> matrix representations to ndarray::Array2<f64> for better cache performance and built-in bounds checking.
Data Privacy
Neural data is among the most sensitive personal data categories. This section covers data handling practices.
| Check | Status | Notes |
|---|---|---|
| No PII in log messages | NEEDS AUDIT | The crate uses tracing in workspace dependencies but currently has no tracing::info! or tracing::debug! calls with data fields. As logging is added, ensure neural data values, subject IDs, and session IDs are never logged at INFO level or below. |
| No neural data in error messages | PASS | Error messages contain structural information (dimensions, indices, version numbers) but not raw signal values or embeddings. |
subject_id handling |
CONCERN | EmbeddingMetadata.subject_id is stored as plaintext Option<String>. This is PII that is included in serialized embeddings (serde), HNSW indices, and RVF files. |
session_id handling |
CONCERN | Same concern as subject_id. |
| Memory store encryption | NOT IMPLEMENTED | NeuralMemoryStore holds embeddings in plaintext Vec<f64>. No encryption-at-rest. |
| Memory zeroization on drop | NOT IMPLEMENTED | Embedding data is not zeroed when dropped. Sensitive neural data persists in deallocated memory. |
| WASM data boundary | STUB | WASM crate is not yet implemented. When implemented, must ensure no neural data is sent to external services without explicit user consent. |
| RVF file privacy | CONCERN | RvfFile serializes metadata as JSON, which may contain subject_id. No option to strip or anonymize metadata before export. |
Recommendations:
- Implement a
Redactabletrait for types that may contain PII, providingredact()andanonymize()methods. - Use the
zeroizecrate to zero sensitive data on drop forNeuralEmbedding,NeuralMemoryStore, andMultiChannelTimeSeries. - Add a
strip_pii()method toRvfFilethat removes or hashes identifiers before export. - Document privacy responsibilities in each crate's module documentation.
- For v0.2: Add optional encryption-at-rest for
NeuralMemoryStoreusingringoraes-gcm.
Network Security (ESP32)
| Check | Status | Notes |
|---|---|---|
| Node ID authentication | NOT IMPLEMENTED | ESP32 crate (ruv-neural-esp32) is currently a local ADC reader with no network protocol. When TDM protocol is added, node IDs must be authenticated. |
| CRC32 integrity | NOT IMPLEMENTED | No data packet framing or integrity checks exist yet. |
| TLS encryption | NOT IMPLEMENTED | v0.1 has no network layer. Planned for v0.2. |
| Packet size limits | NOT IMPLEMENTED | No packet protocol exists yet. |
| Buffer overflow prevention | PARTIAL | AdcReader uses a fixed-size ring buffer (4096 samples), which prevents unbounded growth. However, load_buffer silently truncates data that exceeds buffer size rather than reporting it. |
| DMA configuration | N/A | dma_enabled is a configuration flag only; actual DMA is not implemented in std mode. |
Recommendations for v0.2 TDM Protocol:
- Authenticate node IDs using a pre-shared key or challenge-response.
- Add CRC32 or CRC32-C to every data packet.
- Set maximum packet size to 1460 bytes (single WiFi frame MTU).
- Use DTLS or TLS 1.3 for encryption when available.
- Rate-limit incoming packets per node to prevent flooding.
- Validate all fields in received packets before processing.
Supply Chain
| Check | Status | Notes |
|---|---|---|
| Minimal dependencies | PASS | Core dependencies: thiserror, serde, serde_json, num-complex, rustfft, rand. All are well-maintained, widely-used crates. |
| No proc macros except serde | PASS | Only serde's derive macros and thiserror's derive macro are used. clap's derive is CLI-only. |
| All deps from crates.io | PASS | No git dependencies or path dependencies outside the workspace. |
| Workspace-managed versions | PASS | All dependency versions are declared in [workspace.dependencies]. |
petgraph usage |
UNUSED | Listed in workspace dependencies but not imported by any crate. Remove to reduce supply chain surface. |
tokio usage |
UNUSED | Listed in workspace dependencies but not imported by any crate. Remove unless async is planned. |
ruvector-* crates |
UNUSED | Five RuVector crates listed but not imported by any workspace member. Remove unused dependencies. |
Cargo.lock |
PRESENT | Cargo.lock is committed, ensuring reproducible builds. |
Recommendation: Run cargo deny check to audit for known vulnerabilities. Remove unused workspace dependencies (petgraph, tokio, ruvector-* crates) to minimize attack surface. Add cargo audit to CI.
Findings from Code Audit
SEC-001: RVF Unbounded Allocation (Severity: Medium)
Location: ruv-neural-core/src/rvf.rs, line 193
let mut meta_bytes = vec![0u8; header.metadata_json_len as usize];
A crafted RVF file with metadata_json_len = 0xFFFFFFFF allocates 4 GB. Similarly, read_to_end on line 201 reads unbounded data.
Fix: Add maximum size constants and validate before allocating:
const MAX_METADATA_LEN: u32 = 16 * 1024 * 1024; // 16 MB
const MAX_PAYLOAD_LEN: usize = 256 * 1024 * 1024; // 256 MB
if header.metadata_json_len > MAX_METADATA_LEN {
return Err(RuvNeuralError::Serialization(
format!("metadata_json_len {} exceeds maximum {}", header.metadata_json_len, MAX_METADATA_LEN)
));
}
SEC-002: Missing Sample Rate Validation (Severity: Medium)
Location: ruv-neural-core/src/signal.rs, MultiChannelTimeSeries::new
The sample_rate_hz parameter is not validated. A value of 0.0 causes division by zero in duration_s(). A negative or NaN value causes incorrect spectral analysis throughout the pipeline.
Fix: Add validation in the constructor:
if sample_rate_hz <= 0.0 || !sample_rate_hz.is_finite() {
return Err(RuvNeuralError::Signal(
format!("sample_rate_hz must be positive and finite, got {}", sample_rate_hz)
));
}
SEC-003: NaN Propagation in Signal Processing (Severity: Low)
Location: ruv-neural-signal/src/connectivity.rs, all functions
If either input signal contains NaN, the Hilbert transform produces NaN outputs, which propagate silently through PLV, coherence, and all connectivity metrics. The result is a brain graph with NaN edge weights, which causes undefined behavior in Stoer-Wagner (infinite loops or wrong results).
Fix: Add a validate_signal helper and call it at entry points:
fn validate_signal(signal: &[f64]) -> Result<()> {
if signal.iter().any(|x| !x.is_finite()) {
return Err(RuvNeuralError::Signal("Signal contains NaN or Inf values".into()));
}
Ok(())
}
SEC-004: Integer Overflow in ADC (Severity: Low)
Location: ruv-neural-esp32/src/adc.rs, AdcConfig::max_raw_value
pub fn max_raw_value(&self) -> i16 {
((1u32 << self.resolution_bits) - 1) as i16
}
For resolution_bits = 16, this computes 65535 as i16 = -1, which causes incorrect voltage conversion (division by -1 flips sign).
Fix: Change return type to u16 or i32, or validate resolution_bits <= 15.
SEC-005: HNSW Visited Array Allocation (Severity: Low)
Location: ruv-neural-memory/src/hnsw.rs, search_layer, line 261
let mut visited = vec![false; self.embeddings.len()];
This allocates a visited array proportional to the total number of embeddings on every search call. For large indices (100K+ embeddings), this causes unnecessary allocation pressure. More critically, if entry is >= self.embeddings.len(), the indexing on line 262 panics.
Fix: Use a HashSet<usize> instead of a boolean array for sparse visitation. Add bounds check on entry.
Performance Review
Computational Complexity
| Operation | Complexity | Target Latency | Current Status |
|---|---|---|---|
| FFT (1024 points) | O(N log N) | <1 ms | Implemented via rustfft (SIMD-optimized). Meets target. |
| Hilbert transform | O(N log N) | <1 ms | Two FFTs (forward + inverse). Meets target for N <= 4096. |
| PLV (channel pair) | O(N) + 2x FFT | <0.5 ms | Calls hilbert_transform twice. Meets target for N <= 2048. |
| Coherence (channel pair) | O(N) + 2x FFT | <0.5 ms | Same as PLV. |
| Connectivity matrix (68 regions) | O(N^2 x M) | <10 ms | M = samples per channel, N = 68: 2,278 Hilbert pairs. May exceed target for long windows. |
| Stoer-Wagner mincut (68 nodes) | O(V^3) | <5 ms | 68^3 = ~314K operations. Meets target. |
| Spectral embedding (68 nodes) | O(V^2 x k x iterations) | <3 ms | With k=8, iterations=100: 68^2 x 8 x 100 = ~37M ops. May be tight. |
| Fiedler decomposition | O(V^2 x iterations) | <2 ms | 1000 iterations x 68^2 = ~4.6M ops. Meets target. |
| Cheeger constant (exact, n<=16) | O(2^n x n^2) | <5 ms | Exponential but capped at n=16: 65K x 256 = ~16M ops. Meets target. |
| HNSW insert | O(log N x ef x M) | <1 ms | ef=200, M=16: ~3200 distance computations per insert. Meets target. |
| HNSW search (10K embeddings) | O(log N x ef) | <1 ms | ef=50: ~50-200 distance computations. Meets target. |
| Brute-force NN (10K embeddings) | O(N x d) | <5 ms | d=256, N=10K: 2.56M f64 ops. Acceptable but HNSW preferred. |
| Full pipeline (68 regions) | - | <50 ms | Sum of above stages. Should meet target. |
Memory Usage
| Component | Calculation | Size |
|---|---|---|
| 64-channel x 1000 Hz x 8 bytes x 1s | 64 x 1000 x 8 | 512 KB per second |
| Brain graph adjacency (68 nodes) | 68^2 x 8 bytes | ~37 KB |
| Brain graph adjacency (400 nodes) | 400^2 x 8 bytes | ~1.25 MB |
| Single embedding (256-d) | 256 x 8 bytes | 2 KB |
| Memory store (10K embeddings, 256-d) | 10K x 2 KB | ~20 MB |
| HNSW index (10K, M=16, 256-d) | 10K x (2KB + 16 x 16 bytes) | ~22.5 MB |
| Stoer-Wagner working memory (68 nodes) | 2 x 68^2 x 8 + 68 x vec overhead | ~75 KB |
| Spectral embedder (68 nodes, k=8) | k x 68 x 8 + Laplacian 68^2 x 8 | ~41 KB |
| RVF file in memory | header + metadata + payload | Variable, unbounded (see SEC-001) |
Optimization Opportunities
Immediate (v0.1)
-
Eliminate redundant Hilbert transforms in connectivity matrix
compute_all_pairscallshilbert_transformtwice per channel pair.- For 68 channels, this means 68 x 67 = 4,556 Hilbert transforms instead of 68.
- Fix: Pre-compute analytic signals for all channels, then compute metrics pairwise.
- Expected speedup: ~67x for connectivity matrix computation.
-
Replace Vec<Vec> with flat Vec for adjacency matrices
- Current
Vec<Vec<f64>>has poor cache locality due to heap-allocated inner Vecs. - Fix: Use
Vec<f64>with manual row-major indexing, or migrate tondarray::Array2<f64>. - Expected speedup: 2-4x for matrix-heavy operations (Stoer-Wagner, Laplacian).
- Current
-
Avoid Vec::remove(0) in eviction
NeuralMemoryStore::evict_oldestcallsself.embeddings.remove(0), which is O(n).- Fix: Use a
VecDequeor circular buffer. - Expected speedup: O(1) eviction instead of O(n).
-
Pre-allocate FFT planner
compute_psd,compute_stft, andhilbert_transformeach create a newFftPlannerper call.- Fix: Cache the planner or use a thread-local planner.
- Expected speedup: Eliminates repeated plan computation.
Medium-term (v0.2)
-
Rayon for parallel channel processing
compute_all_pairsiterates channel pairs sequentially.- Fix: Use
rayon::par_iterfor the outer loop. - Expected speedup: Linear with core count for connectivity computation.
-
SIMD for distance computations in HNSW
- Euclidean distance in
HnswIndex::distanceuses scalar iteration. - Fix: Use
packed_simd2or auto-vectorization hints. - Expected speedup: 4-8x for 256-d vectors on AVX2.
- Euclidean distance in
-
Sparse graph representation
- Dense adjacency matrix wastes memory for sparse brain graphs.
- For Schaefer400, storing all 160K entries when only ~10K edges exist is wasteful.
- Fix: Use compressed sparse row (CSR) format or
petgraph's sparse graph.
-
Quantized embeddings for WASM
- f64 embeddings are unnecessarily precise for browser-based applications.
- Fix: Support f32 embeddings in WASM builds, halving memory and transfer size.
Long-term (v0.3+)
-
Streaming signal processing
- Current design loads entire time windows into memory.
- Fix: Implement ring-buffer based streaming for real-time operation.
-
GPU acceleration for large-scale spectral analysis
- For Schaefer400 atlas, eigendecomposition of 400x400 matrices benefits from GPU.
- Fix: Optional
wgpuorvulkanobackend for matrix operations.
ESP32 Constraints
| Resource | Limit | Current Usage | Status |
|---|---|---|---|
| SRAM | 520 KB | Ring buffer: 4096 x channels x 2 bytes = 8 KB (1 channel) | OK |
| SRAM (multi-channel) | 520 KB | 4096 x 16 x 2 = 128 KB (16 channels) | TIGHT |
| CPU | 240 MHz dual-core | ADC sampling + data transmission | OK for 1 kHz |
| Flash | 4 MB | Binary size with release profile | Needs measurement |
| WiFi throughput | ~1 Mbps sustained | 64 ch x 1000 Hz x 2 bytes = 128 KB/s = 1 Mbps | AT LIMIT |
Recommendations:
- Use fixed-point arithmetic (i16 or Q15) instead of f64 on ESP32.
- Implement delta encoding or simple compression for data packets.
- Limit on-device processing to ADC readout and basic quality checks.
- Move all signal processing (FFT, connectivity, graph construction) to the host.
- Profile binary size with
cargo bloatto ensure it fits in 4 MB flash. - Consider reducing ring buffer size for multi-channel configurations.
Benchmarking Recommendations
Per-Crate Microbenchmarks (criterion)
# Add to each crate's Cargo.toml
[[bench]]
name = "benchmarks"
harness = false
[dev-dependencies]
criterion = { workspace = true }
| Crate | Benchmark | Input Size | Metric |
|---|---|---|---|
ruv-neural-signal |
bench_hilbert_transform |
256, 512, 1024, 2048, 4096 samples | ns/op |
ruv-neural-signal |
bench_compute_psd |
1024, 4096 samples | ns/op |
ruv-neural-signal |
bench_plv_pair |
1024 samples | ns/op |
ruv-neural-signal |
bench_connectivity_matrix |
16, 32, 68 channels x 1024 samples | ms/op |
ruv-neural-mincut |
bench_stoer_wagner |
10, 20, 50, 68, 100 nodes | us/op |
ruv-neural-mincut |
bench_spectral_bisection |
10, 20, 50, 68, 100 nodes | us/op |
ruv-neural-mincut |
bench_cheeger_constant |
8, 12, 16 nodes (exact), 32, 68 (approx) | us/op |
ruv-neural-embed |
bench_spectral_embed |
20, 50, 68, 100 nodes | us/op |
ruv-neural-memory |
bench_brute_force_nn |
100, 1K, 10K embeddings x 256-d | us/op |
ruv-neural-memory |
bench_hnsw_insert |
1K, 10K embeddings x 256-d | us/op |
ruv-neural-memory |
bench_hnsw_search |
1K, 10K embeddings, k=10, ef=50 | us/op |
ruv-neural-esp32 |
bench_adc_read |
100, 1000 samples x 1-16 channels | us/op |
Full Pipeline Profiling
# Generate a flamegraph of the full pipeline
cargo flamegraph --bench full_pipeline -- --bench
# Memory profiling with DHAT
cargo test --features dhat-heap -- --test full_pipeline
WASM Performance
// When ruv-neural-wasm is implemented, measure with:
performance.mark('embed-start');
const embedding = ruv_neural.embed(graphData);
performance.mark('embed-end');
performance.measure('embed', 'embed-start', 'embed-end');
ESP32 Hardware Timing
// Use esp-idf-hal's timer for hardware-level benchmarks
let start = esp_idf_hal::timer::now();
let samples = reader.read_samples(1000)?;
let elapsed_us = esp_idf_hal::timer::now() - start;
Performance Findings from Code Audit
PERF-001: Redundant Hilbert Transforms (Severity: High)
Location: ruv-neural-signal/src/connectivity.rs, compute_all_pairs
Each call to phase_locking_value, coherence, imaginary_coherence, or amplitude_envelope_correlation independently calls hilbert_transform on both input signals. In compute_all_pairs with 68 channels, each channel's analytic signal is computed 67 times.
Impact: For 68 channels x 1024 samples, this means 4,556 FFTs instead of 68. Estimated waste: ~98.5% of FFT compute in the connectivity matrix.
Fix: Pre-compute all analytic signals, then pass slices to pairwise metrics:
pub fn compute_all_pairs_optimized(channels: &[Vec<f64>], metric: &ConnectivityMetric) -> Vec<Vec<f64>> {
let analytics: Vec<Vec<Complex<f64>>> = channels.iter()
.map(|ch| hilbert_transform(ch))
.collect();
// ... use pre-computed analytics for all pair computations
}
PERF-002: O(n) Eviction in Memory Store (Severity: Medium)
Location: ruv-neural-memory/src/store.rs, evict_oldest
fn evict_oldest(&mut self) {
self.embeddings.remove(0); // O(n) shift
self.rebuild_index(); // O(n) rebuild
}
For a store with 10K embeddings, every insertion at capacity triggers an O(n) shift and full index rebuild.
Fix: Use VecDeque<NeuralEmbedding> and maintain the index incrementally.
PERF-003: FFT Planner Re-creation (Severity: Medium)
Location: ruv-neural-signal/src/spectral.rs (lines 12-13), hilbert.rs (lines 25-27)
A new FftPlanner is created on every function call. rustfft caches FFT plans internally in the planner, but creating a new planner discards the cache.
Fix: Use a thread-local or static planner:
thread_local! {
static FFT_PLANNER: RefCell<FftPlanner<f64>> = RefCell::new(FftPlanner::new());
}
PERF-004: Dense Adjacency for Sparse Graphs (Severity: Low)
Location: ruv-neural-core/src/graph.rs, adjacency_matrix
Always allocates an N x N matrix even when the graph has far fewer edges. For Schaefer400 with ~5K edges, this allocates 1.25 MB for a matrix that is ~97% zeros.
Fix: Return a sparse representation for large graphs, or provide both adjacency_matrix() and sparse_adjacency().
PERF-005: Power Iteration Convergence Not Checked (Severity: Low)
Location: ruv-neural-mincut/src/spectral_cut.rs, largest_eigenvalue
Runs a fixed 200 iterations regardless of convergence. Many graphs converge in 20-50 iterations.
Fix: Add early termination when eigenvalue change < epsilon:
if (eigenvalue - prev_eigenvalue).abs() < 1e-12 {
break;
}
Note: fiedler_decomposition already has this check, but largest_eigenvalue does not.
Action Items
Critical (Must fix before v0.1 release)
- SEC-001: Add maximum size limits to RVF deserialization
- SEC-002: Validate
sample_rate_hz > 0andis_finite()inMultiChannelTimeSeries::new - SEC-004: Fix integer overflow in
AdcConfig::max_raw_value - PERF-001: Pre-compute Hilbert transforms in
compute_all_pairs
Important (Should fix before v0.1 release)
- SEC-003: Add NaN/Inf validation for signal data at pipeline entry points
- SEC-005: Add bounds check on HNSW entry point index
- PERF-002: Replace
Vec::remove(0)withVecDequein memory store - PERF-003: Cache FFT planner across calls
- Add
BrainGraph::validate()for edge index bounds and weight finiteness - Add dimension consistency check to
NeuralMemoryStore::store - Remove unused workspace dependencies (
petgraph,tokio,ruvector-*)
Recommended (Fix in v0.2)
- Implement
zeroize-on-drop forNeuralEmbeddingandNeuralMemoryStore - Add
strip_pii()toRvfFile - Migrate
Vec<Vec<f64>>matrices tondarray::Array2<f64> - Add Rayon parallelism for connectivity matrix computation
- Add criterion benchmarks for all crates
- Implement TDM protocol with CRC32 and node authentication
- Add
cargo denyandcargo auditto CI - Profile and optimize binary size for ESP32
Future (v0.3+)
- Encryption-at-rest for
NeuralMemoryStore - DTLS/TLS for ESP32 network protocol
- Sparse graph representation for large atlases
- f32 quantized embeddings for WASM
- Streaming signal processing pipeline
- GPU backend for large-scale spectral analysis
This document should be reviewed and updated after each milestone. All security findings should be verified as resolved before the corresponding release.