Encrypted Noise Sessions
Relevant source files
The following files were used as context for generating this wiki page:
Encrypted Noise Sessions provide a secure, authenticated communication channel between an ESP32 device and a backend Virtual Machine (VM). This system utilizes the Noise Protocol Framework to encrypt data transmitted over a WebSocket transport. Every setup creates a fresh encrypted session to protect device tokens and user data.
Sources: board-port/esp32/README.md:158-162, board-port/esp32/main/noise_control.cpp:46-48
Session Lifecycle and Architecture
The Noise session operates within a dedicated FreeRTOS task named noise_ctrl. This task manages the transition from raw TCP/TLS connection to a fully established Noise cryptographic state. The architecture separates control JSON messages from optional high-bandwidth tunnel data.
Connection Sequence
Establishment of a session follows a specific multi-step handshake:
- TLS Connection: The device connects to the host (default
hatch.metaaivm.com) on port 443 viaesp_tls. - WebSocket Upgrade: The device sends an HTTP GET request to
/v1/noisewith anAuthorizationbearer token. - Noise Handshake: Once the WebSocket is established, the device and server perform a three-message Noise handshake (Initiator -> Responder -> Initiator).
- Stream Multiplexing: After the handshake, the device opens the
POST /link-controlstream (Stream ID 1).
Sources: board-port/esp32/main/noise_control.cpp:882-956, board-port/esp32/main/noise_control.cpp:465-498
The sequence shows the transition from standard protocols to the proprietary Noise encrypted layer.
Sources: board-port/esp32/main/noise_control.cpp:465-498, board-port/esp32/main/noise_control.cpp:525-566
Messaging and Stream Multiplexing
The session supports multiple logical streams over a single encrypted connection. Streams are identified by specific IDs and handle different types of traffic.
Core Stream IDs
| Stream ID | Name | Description |
|---|---|---|
| 1 | CTRL_STREAM_ID | Carries device registration, commands, and heartbeats. |
| 2 | TUNNEL_STREAM_ID | Optional L3 tunnel for home network access. |
| 3 | IDENTITY_STREAM_ID | One-shot request for the agent's name. |
| 16+ | REQ_STREAM_BASE | Extra daemon requests used for Muse audio/chat features. |
Sources: board-port/esp32/main/noise_control.cpp:49-53, board-port/esp32/main/noise_control.cpp:635-637
Control Message Framing
Control messages use a length-prefixed JSON format. The system sends data in BodyChunks. The first chunk of a message includes a 4-byte little-endian length prefix. Large messages, such as discovery results, are split across multiple chunks to fit memory constraints.
Sources: board-port/esp32/main/noise_control.cpp:568-617
Command Execution and Handling
The backend invokes functionality on the device via the link.invoke method. Commands are received as JSON objects over Stream 1 and dispatched to appropriate handlers.
Key Commands and Handlers
- device.register: Sent by the device immediately after session establishment to advertise capabilities and metadata like platform, version, and supported commands.
- device.health: Reports uptime, heap memory (Internal and PSRAM), and battery metrics.
- device.ota: Initiates an Over-The-Air firmware update using a provided HTTPS URL.
- display.draw_url: Downloads and renders an image to the device display.
Sources: board-port/esp32/main/noise_control.cpp:737-832, board-port/esp32/main/app.c:1347-1365
Logic flow for processing inbound messages within the Noise session loop.
Sources: board-port/esp32/main/noise_control.cpp:1134-1180, board-port/esp32/main/noise_control.cpp:1003-1070
Memory and Buffer Management
Noise sessions are memory-intensive. The system adjusts buffer sizes based on hardware capabilities, particularly the presence of PSRAM and display modules.
Configuration Constants
| Constant | Value (Small Session) | Value (Standard) | Description |
|---|---|---|---|
SVC_FRAME_SCRATCH | 17 KB | 12 KB | Scratch space for inbound service frames. |
WS_BUF_SIZE | 7 KB | 64 KB (PSRAM) | WebSocket send/receive buffer. |
CTRL_BODY_CHUNK_MAX | 4 KB | 8 KB | Maximum payload for one BodyChunk. |
NOISE_CTRL_STACK | 12,288 Bytes | 12,288 Bytes | Dedicated task stack size. |
Sources: board-port/esp32/main/noise_control.cpp:78-103
On boards with limited RAM, the system uses SMALL_CONTROL_SESSION settings to avoid memory exhaustion during TLS handshakes or large JSON parsing.
Sources: board-port/esp32/main/noise_control.cpp:70-76
Reconnection and Reliability
The system implements a backoff strategy to manage connection failures. Reconnection delays increase after successive failures to avoid hammering the backend.
- Minimum Backoff: 1,000 ms.
- Maximum Backoff: 15,000 ms.
- Auth Failure Backoff: 60,000 ms (triggered by rejected bearer tokens).
- Stable Session Threshold: 30,000 ms (sessions lasting longer reset the backoff).
Sources: board-port/esp32/main/noise_control.cpp:57-65, board-port/esp32/main/noise_control.cpp:1425-1447
WebSocket Heartbeats
To prevent middlebox idle-timeout resets, the device sends a WebSocket ping (opcode 0x9) every 60 seconds. The peer must answer with a pong to keep the transport alive. This is separate from the application-level link.heartbeat JSON message.
Sources: board-port/esp32/main/noise_control.cpp:328-334, board-port/esp32/main/noise_control.cpp:1292-1304
Summary
Encrypted Noise Sessions provide the secure foundation for Muse Gadget communication. By wrapping the Noise Protocol Framework in a WebSocket transport, the system achieves reliable and encrypted bi-directional messaging. The implementation manages resource constraints on ESP32 hardware while ensuring high availability through sophisticated reconnection logic and heartbeat mechanisms.
Sources: board-port/esp32/main/noise_control.cpp:1316-1335, board-port/esp32/AGENTS.md:32-37