Interface control document
1. Identification
| Item | Value |
|---|---|
| Document | ICD-CMMENTALISTA-001 rev 1 |
| Product | Mentalista module (HEAT.Mentalista in the module registry) |
| Product versions covered | v2.0.1 |
| Peer | Mentalista’s own web application, running in an automated Chrome session logged into Mentalista’s cloud backend - see Section 2 |
| Host | HEAT Capture, HEAT.Capture.ModuleSDK 2.0.5 |
| Data model | HEAT ProtoLib 0.37.0 (bundled with HEAT.Capture.ModuleSDK 2.0.5) |
| Date | 2026-09-01 |
| Owner | VRAI Simulation |
This document is incomplete in one respect: no unit is stated, by the peer or by this module, for the four prediction values in Section 4.5. See that section for the exact field names; there is no further source to resolve this from.
2. Scope
This document defines the WebSocket-based protocol the Mentalista module exposes: HEAT Capture
hosts the listener, and a client connects to it, not the other way around. It covers the
listener’s connection handling, the connect command the module sends, and the four event types
it recognises in response.
The client this module actually talks to is not the EEG headset. In this deployment the headset
connects to Mentalista’s own web application, running in an automated Chrome session (driven via
ChromeDriver) that is logged into Mentalista’s cloud backend; that backend performs the
prediction inference, and the browser session relays the resulting prediction events down this
WebSocket. This module has no visibility into, and no direct interface with, the headset or
Mentalista’s backend - its entire contract is with whatever client connects to its listener and
speaks the protocol in Section 4.
The module does not process raw sensor data - only prediction events become HEAT data. It does
not authenticate a connecting client and does not encrypt the connection.
3. Interface inventory
| ID | Name | Direction | Transport | Peer |
|---|---|---|---|---|
| IF-CMMENTALISTA-01 | Prediction WebSocket | Bidirectional | WebSocket over HTTP, no TLS | Mentalista’s browser-based client |
4. IF-CMMENTALISTA-01 Prediction WebSocket
4.1 Transport and addressing
| Item | Value |
|---|---|
| Protocol | WebSocket, upgraded from an HTTP request accepted by a .NET HttpListener |
| Host and port | Host:Port, default localhost:5555; see Configure |
| Path | Any path under the listener’s root is accepted; the module does not route by path |
| TLS | None. Plain http:///ws://. |
| Non-WebSocket requests | Answered with HTTP 400 and closed |
4.2 Session and connection lifecycle
The listener exists only while a HEAT Capture session is running: it is created when the session starts and torn down when it ends. This prevents new connections after capture ends. In this release, capture end does not close WebSockets that were already upgraded; they remain active until the peer closes or their receive loop fails. Do not rely on listener teardown to bound the connection’s lifetime.
The module accepts one WebSocket upgrade per incoming request and starts a receive loop for it immediately; it does not track or limit concurrent client connections. Connecting a second client while a first remains connected is not a supported or tested configuration - the module’s connection-state fields are shared across every accepted connection, not scoped per client.
If ConnectOnStartup is enabled, the module waits ConnectInitialDelayMs after the WebSocket
opens, then sends the connect command (4.4). If it is disabled, the module considers the
connection established immediately with no command exchange.
The peer closes the session with a standard WebSocket close frame; the module acknowledges it and returns to listening for the next connection. Any receive-loop exception (including malformed JSON, 4.7) ends that connection’s receive loop without an application-level close handshake.
4.3 Authentication
None. Any client able to reach Host:Port and complete a WebSocket upgrade is accepted.
4.4 Messages
| Message | Direction | Purpose |
|---|---|---|
connect | Product to peer | Requests the peer connect to a specific device; see below |
ready | Peer to product | Announces the connected device’s name |
prediction | Peer to product | Carries one set of biometric prediction values |
commandSuccess | Peer to product | Acknowledges a command (used here only for connect) |
commandError | Peer to product | Reports a failed command; drives the connect retry loop |
connect command payload, sent by the module:
{ "commandName": "connect", "payload": { "type": "M0" } }type is "M0" when TargetDevice is Bluetooth (the first paired Bluetooth device matching
that filter), or "S06" when TargetDevice is Synthetic (a development test device). Per the
module’s own source attribution, M0/S06 targeting and resolution is defined by the
Mentalista SDK on the peer side, not by this module.
If the peer responds commandError naming connect, and fewer than ConnectRetryCount retries
have already been sent, the module waits (ConnectRetryDelayMs, doubling each retry up to
MaxConnectRetryDelayMs) and resends connect. A commandError for any other command name, or
for connect after ConnectRetryCount is exhausted, is logged only - the module does not retry
further and does not close the connection.
Any event name other than the five above is received and silently ignored.
4.5 Data elements
prediction payload, at payload.predictions:
| Field | Type | Unit | Optionality |
|---|---|---|---|
emotion.valence | float | Not stated by the peer or this module | Present when the backend’s prediction model produced a value |
emotion.arousal | float | Not stated by the peer or this module | Present when the backend’s prediction model produced a value |
attention.attention_index | float | Not stated by the peer or this module | Present when the backend’s prediction model produced a value |
cognitiveLoad.cognitive_index | float | Not stated by the peer or this module | Present when the backend’s prediction model produced a value |
timestamp (event envelope, not under predictions) | integer, milliseconds | Epoch not stated by the peer | Required - treated as 0 if absent |
ready payload: payload.name (string, optional) - the device name, used as the published
entity name when EntityName is not configured.
See Data produced for how these map onto
WearableBioSensorComponent.
4.6 Timing
Event-driven; the module never polls the peer. With SendAtNativeRate enabled (default), each
prediction event is published as its own SimulationFrame immediately. Disabled, HEAT
Capture’s own per-tick poll publishes the most recently received prediction, once, then clears
it - a tick with no new prediction since the last one publishes nothing.
4.7 Error behaviour
| Condition | Observable | Module behaviour |
|---|---|---|
| Malformed JSON on the WebSocket | Error logged, receive loop ends | Connection is not actively closed by the module; the peer must reconnect |
commandError for connect, retries remaining | Warning logged, retry sent after backoff | Retries as described in 4.4 |
commandError for connect, retries exhausted | Warning logged: “Connect retry limit reached” | No further retries this connection |
prediction with none of the four recognised paths | Nothing logged | No frame published for that event |
| Non-WebSocket HTTP request | HTTP 400 | Request closed |
| Listener fails to bind (for example, port already in use) | Error logged, capture start fails | Module reports it did not start |
5. Data model reference
Output is HEAT ProtoLib SimulationFrame, from HEAT.ProtoLib.CSharp 0.37.0 as bundled with
HEAT.Capture.ModuleSDK 2.0.5. This module populates only WearableBioSensorComponent, via a
list of BiometricReading values. See
HEAT ProtoLib’s message reference for the component’s fields.
The peer’s own JSON event schema is not a VRAI-published specification; per the module’s source
comments, its connect/M0/S06 semantics come from the Mentalista vendor’s own SDK
documentation, communicated directly to the module’s authors rather than published. This document
names only the message and field subset this module actually reads.
6. Units, frames and conventions
| Concept | Convention |
|---|---|
| Prediction values | No unit stated by the peer or this module - see Section 1 |
| Time base | The peer’s own timestamp field, used directly as the published capture time in milliseconds; its epoch is not stated by the peer |
| Coordinate frame | Not applicable - this interface carries no spatial data |
7. Configuration affecting the interface
| Setting | Interface effect |
|---|---|
Host, Port | Listener address. Changing Port moves outside the firewall scope this module’s capability declares - see Section 9 |
ConnectOnStartup, TargetDevice, ConnectRetryCount, ConnectInitialDelayMs, ConnectRetryDelayMs, MaxConnectRetryDelayMs | The connect command exchange in 4.4 |
EntityId, EntityName | Identity of the published entity |
SendAtNativeRate | Publish timing, 4.6 |
See Configure for the full settings reference.
8. Compatibility and versioning
Built against HEAT.Capture.ModuleSDK 2.0.5. The peer protocol carries no version field; this
module does not detect or reject a peer speaking an incompatible or newer version of the
Mentalista protocol - an unrecognised field is silently ignored, but a restructured payload (for
example, a renamed predictions key) would stop producing readings with no error.
9. Security considerations
There is no authentication: any client that can reach Host:Port and complete a WebSocket
upgrade is accepted and can send a prediction event that becomes captured data, or trigger the
connect retry logic. There is no transport encryption (no TLS).
This module declares the network.listen capability for tcp/5555 specifically. HEAT Capture’s
host-side firewall scoping is built from that declaration - changing Port in
Configure without a matching firewall change moves the listener
outside its granted scope. Treat the network between the HEAT Capture workstation and the peer
as the security boundary; do not expose this listener beyond an isolated capture network.
New WebSocket upgrades are limited to an active capture session. Existing upgraded connections are not closed automatically when capture ends, so network isolation must remain in place until each peer disconnects.
10. Conformance
- Start a HEAT Capture session with this module enabled. Confirm the module’s debug view reports
listenerActive: true. - Connect a WebSocket client to
ws://Host:Port/(its configuredHostandPort). Confirm the debug view reportssocketConnected: true. - With
ConnectOnStartupenabled, confirm the client receives aconnectcommand withinConnectInitialDelayMsof connecting, and that replying withcommandSuccessforconnectflips the debug view’s connection state toconnected. - Send a
predictionevent with all four recognised paths populated. Confirm aWearableBioSensorComponentwith four readings appears in HEAT Capture’s captured data within one capture tick, and that the reading names match Section 4.5 exactly. - Reply to a
connectcommand withcommandErrornamingconnect. Confirm a retry is sent after approximatelyConnectRetryDelayMs, and that retries stop afterConnectRetryCountattempts.
11. Revision history
| Rev | Date | Product version | Change |
|---|---|---|---|
| 1 | 2026-09-01 | v2.0.1 | Initial issue. |