Skip to content

Releases: saschadaemgen/SimpleGo

v0.2.0-beta - Queue Rotation + Multi-Server + Web Installer

26 Mar 20:30
a37370a

Choose a tag to compare

Release Notes: v0.2.0-beta

Release Date: March 26, 2026
Previous Version: v0.1.17-alpha (February 28, 2026)
Codename: Queue Rotation + Multi-Server + Web Installer


Highlights

  • Queue Rotation - Live server migration without disconnection. Four consecutive rotations verified with bidirectional chat, delivery receipts, and post-quantum encryption on 512 KB SRAM.
  • Multi-Server Infrastructure - 21 preset SMP relay servers from three operators (SimpleX Chat, Flux, SimpleGo). Single active server with radio-button selection. Server switch triggers live Queue Rotation.
  • Web Serial Installer - Flash SimpleGo directly from the browser via WebSerial. Open and Vault mode selection, eFuse status check, erase flash, reboot. No CLI tools required.
  • 5 Encryption Layers - Post-quantum key exchange (sntrup761) now active and verified. Hybrid X448 + sntrup761 KEM from the first message.
  • TLS Fingerprint Verification (SEC-07) - Server identity verified via SHA-256 key hash pinning at all four TLS connection points (main server, peer server, rotation server, reconnect).
  • eFuse Vault Mode - Hardware-secured NVS encryption with HMAC key burned into ESP32-S3 eFuse BLOCK_KEY1. Device generates its own key on first boot. Irreversible.
  • Delivery Receipts - Two-checkmark system. Sent, delivered, failed, pending states stored in SD card history (History v2 format).
  • 8 Queue Rotation Bugs Fixed - Consecutive rotations work reliably. Root causes identified and surgically fixed via DIAG-based byte-level analysis.

New Features

Queue Rotation (Sessions 49-50)

Live migration of message queues between SMP servers. The 7-phase protocol (QADD, QKEY, KEY, QUSE, Live-Switch, QTEST, Cleanup) is fully implemented in C.

Phase What happens
QADD Create new queue on target server, send replacement info to peer
QKEY Receive peer's new E2E key for the replacement queue
KEY Secure the new queue with peer's sender key
QUSE Activate the new queue, peer switches to sending there
Live-Switch Overwrite credentials in RAM and NVS, reconnect to new server
QTEST Verify bidirectional communication on new queue
Cleanup Clear rotation state, resume normal operation

Queue Rotation uses a second TLS connection to the target server. SRAM budget confirmed: ~1,500 bytes per additional TLS connection. Maximum 2 simultaneous TLS connections safe on ESP32-S3.

Multi-Server Infrastructure (Session 49)

Detail Value
Total preset servers 21
SimpleX Chat servers 14 (Storage + Proxy)
Flux servers 6 (Storage + Proxy)
SimpleGo server 1 (smp.simplego.dev)
Maximum servers 32 (including custom)
Selection model Radio-button (one active at a time)
Persistence NVS with version-checked blob serialization
Server switch Live Queue Rotation (no reboot)

Web Serial Installer

Browser-based firmware installer at simplego.dev/installer.

Feature Details
Browser Chrome 89+ or Edge 89+ (WebSerial API)
Modes Open Mode and Vault Mode selection
Flash method Merged binary at offset 0x0, eraseAll enabled
Baud rate 921,600 with compression
eFuse check Reads ESP32-S3 eFuse register (EFUSE_RD_REPEAT_DATA1) to detect HMAC_UP
Additional tools Erase Flash (with confirmation), Reboot Device, Check Security
Flash time ~60-90 seconds for ~1.9 MB binary

Delivery Receipts (Session 43)

History v2 format adds 1-byte delivery status after direction byte.

Status Value Meaning
SENT 0 Message sent, no confirmation yet
DELIVERED 1 Peer acknowledged receipt
FAILED 2 Send failed
PENDING 3 Queued for sending

Old v1 history files remain v1 forever (no migration). New files use HISTORY_VERSION 0x02.

TLS Fingerprint Verification - SEC-07 (Session 49)

Server identity verified at four connection points:

  1. Main server connection (boot + reconnect)
  2. Peer server connection (sending messages)
  3. Rotation server connection (Queue Rotation target)
  4. Re-subscription after network loss

SHA-256 hash of server's DH public key compared against stored key_hash from preset or NVS. Connection rejected on mismatch.


Bugs Fixed

Queue Rotation Bugs (Session 50) - 8 Fixes

Fix Problem Solution
A s_complete_logged function-local static, never reset File-static variable + smp_tasks_reset_rotation_guard() API
B Auth/DH backup overwritten every rotation Conditional backup: if (!has_peer_auth) - only originals saved
C rq->snd_id set to Reply Queue snd_id instead of Main Queue rq->snd_id = rd->new_snd_id
D CQ E2E peer cache never invalidated Reset in rotation_start() + after Phase 1b
Phase 1b CQ pipeline needs new E2E keys after rotation our_queue.e2e_private = rd->new_e2e_private + peer key from QKEY
Cache Phase 1b writes after cache fill Second smp_tasks_reset_rotation_guard() call after Phase 1b
Queue 2 App processes only Queue 1 from QADD Confirmed via Haskell NonEmpty head pattern - no code change needed
DIAG Root cause found via byte-level comparison Stale peer key (82488db3) used instead of new key (8fca4b2e)

Other Fixes

Fix Description
SEC-03 memset replaced with mbedtls_platform_zeroize in smp_storage.c (CWE-14)
SMP server fingerprint Updated smp.simplego.dev fingerprint after certificate renewal
LVGL memory Screen lifecycle fix - LVGL pool stable at 31% (was 86%)
subscribe_all Removed redundant calls - O(N*M) scaling was root cause of Bug #30
smp_app_run Refactored from 530 to 118 lines, 5 static helpers extracted
smp_handshake 74 debug statements removed, 1281 to 1207 lines

Architecture Changes

FreeRTOS Task Architecture

Task Core Stack Responsibility
network_task 0 16 KB PSRAM TLS read/write, subscriptions, PINGs
smp_app_task 0 64 KB SRAM Protocol logic, NVS, Ratchet, Rotation
lvgl_task 1 8 KB SRAM UI rendering
wifi_manager 0 4 KB PSRAM WiFi connection management

New Files

File Purpose
smp_servers.c/h Multi-server management, 21 presets, NVS persistence
smp_rotation.c/h Queue Rotation protocol (QADD/QKEY/KEY/QUSE/QTEST)
ui_settings_info.c Info panel with eFuse status, server management, timezone
ui_settings_bright.c Backlight control tab
ui_settings_wifi.c WiFi management tab

New Patterns

Pattern Description
Conditional backup if (!has_peer_auth) - peer credentials saved only on first rotation
Double cache invalidation Static caches reset at rotation_start() AND after Phase 1b
DIAG-based debugging Hex dump of actual keys in decrypt path for byte-level comparison
Lazy TLS Rotation TLS opened only when needed (NEW, KEY), closed immediately
Live-Switch Credentials overwritten in RAM + NVS, reconnect without reboot

Protocol Knowledge

Documented during implementation and verified against Haskell reference:

Topic Detail
X3DH 4-DH scheme (not 3 like Signal). HKDF-SHA512, salt=32 zeros, info="SimpleXX3DH", output=96 bytes
Ratchet AES-256-GCM. Message Key via HKDF-SHA256 info="SimpleXMK". Chain Key via "SimpleXCK". 12-byte counter nonce.
Header 2346 bytes fixed (56B X448 key + 4B pn + 4B ns + padding). Encrypted with header key.
E2E Layer 2 Non-standard XSalsa20: HSalsa20(key, zeros[16]) instead of HSalsa20(key, nonce[0:16])
X448 Haskell cryptonite outputs reversed byte order. SimpleGo reverses in smp_x448.c.
Zstd Level 3 (default). Always active, including first connection request.
SMP version Client range v1-v4 for QADD. App negotiates v7 for main connection.
corrId encoding 0x30 = Maybe Nothing (pre-shared key). 0x31 = Maybe Just (inline SPKI key).
QADD App processes only Queue 1 (Haskell NonEmpty head pattern). Queue 2 ignored.
findQ Compares (SMPServer, SenderId) tuples. keyHash NOT compared by sameSrvAddr.

Codebase Statistics

Metric Value
Source files 47+
Lines of C 22,000+
Encryption layers 5
Maximum contacts 128
Preset SMP servers 21
SMP block size 16,384 bytes
Ratchet states in PSRAM 128 (permanently resident)
SD card encryption AES-256-GCM with HKDF per-contact key

Build Information

Detail Value
ESP-IDF 5.5.2
Target ESP32-S3
Flash mode DIO, 80 MHz
Flash size 16 MB
App offset 0x110000
PSRAM 8 MB OPI
Partition Custom (OTA_0 3MB, OTA_1 3MB, NVS 128KB, NVS Keys 256KB)

Merged Binary Creation

python -m esptool --chip esp32s3 merge_bin \
  -o simplego-tdeck-plus-v0.2.0-beta-open.bin \
  --flash_mode dio --flash_freq 80m --flash_size 16MB \
  0x0 build/bootloader/bootloader.bin \
  0x8000 build/partition_table/partition-table.bin \
  0x110000 build/simplex_client.bin

Security Status

ID Status Description
SEC-01 Open Decrypted messages in PSRAM never zeroed
SEC-02 Open NVS keys plaintext without Vault mode
SEC-03 Closed mbedtls_platform_zeroize in smp_storage.c
SEC-04 Open No memory wipe on screen lock
SEC-05 Deferred HKDF info parameter (resolved by eFuse binding)
SEC-06 Deferred Post-quantum toggle per contact...
Read more