Skip to content

4.1 IpcMessage Protocol

Wiki Keeper (Grok Bot) edited this page Oct 26, 2026 · 3 revisions

IpcMessage Protocol

Relevant source files

The following files were used as context for generating this wiki page:

The IpcMessage protocol serves as the top-level Inter-Process Communication (IPC) envelope for the corpus-ipc framework. Renamed from RuntimeMessage and SpineMessage per downstream migrations, it facilitates bidirectional communication between Rust-based simulation components (a stimulus producer and an SNN consumer) and external compute engines. The protocol handles spiking data, dense embeddings, continuous stimuli, neuromodulators, training feedback like gradients and eligibility traces, and control signals.

Sources: src/models/message.rs:13-57

The IpcMessage Envelope and Serde Tagging

The core envelope is the IpcMessage tagged enum, defined in src/models/message.rs. It derives Serialize, Deserialize, Debug, Clone, and PartialEq. Variant names (Spikes, Embeddings, Stimuli, Neuromodulators, Loss, ConfigUpdate, GradientUpdate, EligibilityTraces, TrainingComplete, Shutdown, Ping) act as serde identifiers and must remain stable across versions.

src/models/message.rs:32-57

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub enum IpcMessage {
    // Input messages
    Spikes(SpikeBatch),
    Embeddings(EmbeddingBatch),
    Stimuli(StimulusBatch),
    Neuromodulators(NeuromodulatorSnapshot),
    Loss(#[serde(deserialize_with = "de_loss")] f32),
    ConfigUpdate(ConfigPayload),

    // Output messages
    GradientUpdate(GradientBatch),
    EligibilityTraces(TraceBatch),
    TrainingComplete,

    // Control
    Shutdown,
    Ping,
}

Variant-name stability is enforced via integration tests in src/models/compat.rs, ensuring JSON serialization retains the exact variant keys and round-trips correctly.

src/models/compat.rs:181-193

#[test]
fn ipc_message_envelopes_keep_variant_names() {
    let spikes = serde_json::to_value(IpcMessage::Spikes(sample_spike_batch())).unwrap();
    let traces = serde_json::to_value(IpcMessage::EligibilityTraces(sample_trace_batch())).unwrap();
    assert!(spikes.get("Spikes").is_some());
    assert!(traces.get("EligibilityTraces").is_some());
    let decoded_spikes: IpcMessage = serde_json::from_value(spikes).unwrap();
    let decoded_traces: IpcMessage = serde_json::from_value(traces).unwrap();
    assert_eq!(decoded_spikes, IpcMessage::Spikes(sample_spike_batch()));
    assert_eq!(
        decoded_traces,
        IpcMessage::EligibilityTraces(sample_trace_batch())
    );
}

Sources: src/models/message.rs:32-57, src/models/compat.rs:181-193

Message Taxonomy

Messages within the envelope are grouped into three primary categories based on direction and role:

Category Variants Primary Purpose
Input Messages Spikes, Embeddings, Stimuli, Neuromodulators, Loss, ConfigUpdate Transmits sensor events, continuous streams, loss metrics, and configuration changes from Rust to external engines.
Output Messages GradientUpdate, EligibilityTraces, TrainingComplete Returns gradients, eligibility traces, and training completion status from external engines to Rust.
Control Messages Shutdown, Ping Manages connection lifecycle and latency monitoring.

Sources: src/models/message.rs:34-57

Session and Batch Correlation

To maintain correlation and traceability across the asynchronous IPC boundary, batch-oriented payloads wrapped by IpcMessage variants utilize session_id and batch_id fields.

graph TD
    subgraph "CodeEntitySpace"
        IM["IpcMessage [src/models/message.rs:33]"]
        SB["SpikeBatch [src/models/message.rs:8]"]
        GB["GradientBatch [src/models/message.rs:49]"]
        TB["TraceBatch [src/models/message.rs:51]"]
    end

    subgraph "NaturalLanguageSpace"
        SID["Session Correlation (session_id)"]
        BID["Batch Correlation (batch_id)"]
    end

    IM -->|"Spikes"| SB
    IM -->|"GradientUpdate"| GB
    IM -->|"EligibilityTraces"| TB

    SB -->|"Contains"| SID
    SB -->|"Contains"| BID
    GB -->|"Contains"| BID
Loading

Sources: src/models/message.rs:33-57, src/models/compat.rs:7-18

Protocol Validation Integration

IpcMessage implements the Validate trait via validate_with(limits: ProtocolLimits), routing validation checks down to the inner batch types or primitive fields (such as finite-value checks on Loss).

src/models/message.rs:59-73

impl Validate for IpcMessage {
    fn validate_with(&self, limits: ProtocolLimits) -> Result<(), ValidationError> {
        match self {
            Self::Spikes(batch) => batch.validate_with(limits),
            Self::Embeddings(batch) => batch.validate_with(limits),
            Self::Stimuli(batch) => batch.validate_with(limits),
            Self::Neuromodulators(snapshot) => snapshot.validate_with(limits),
            Self::ConfigUpdate(payload) => payload.validate_with(limits),
            Self::GradientUpdate(batch) => batch.validate_with(limits),
            Self::EligibilityTraces(batch) => batch.validate_with(limits),
            Self::Loss(value) => check_finite("Loss", *value),
            Self::TrainingComplete | Self::Shutdown | Self::Ping => Ok(()),
        }
    }
}

Sources: src/models/message.rs:59-73

Communication Flow and Data Architecture

The following diagram bridges natural language descriptions of the messaging architecture to concrete code entities in src/models/message.rs.

Title: "IpcMessage Processing and Validation Flow"

sequenceDiagram
    participant Relay as "producer / client"
    participant parser as "decode_ipc_message_json [src/lib.rs]"
    participant msg as "IpcMessage [src/models/message.rs:33]"
    participant val as "Validate::validate_with [src/models/message.rs:60]"

    Relay->>parser: "JSON Payload"
    parser->>msg: "Deserialize into IpcMessage Variant"
    msg->>val: "Invoke validate_with(limits)"
    alt Valid Payload
        val-->>msg: "Ok(())"
    else Invalid Payload / Non-Finite Loss
        val-->>msg: "Err(ValidationError)"
    end
Loading

Sources: src/models/message.rs:33-73, src/models/message.rs:5-11

Clone this wiki locally