Skip to content

fix(web): HTTPS receive buffer holds a whole TLS record; document the feature - #84

Merged
angeloINTJ merged 1 commit into
mainfrom
fix/https-server-rx
Aug 19, 2026
Merged

fix(web): HTTPS receive buffer holds a whole TLS record; document the feature#84
angeloINTJ merged 1 commit into
mainfrom
fix/https-server-rx

Conversation

@angeloINTJ

Copy link
Copy Markdown
Owner

First full hardware test of the HTTPS transport (shipped in v2.2.6-beta, never exercised end-to-end) found that every upload past ~4 KB killed the TLS connection mid-body — clean bisection: 3.5 KB passes, 5 KB dies in 0.5 s. Language packs (30 KB) and OTA images could not cross an encrypted session, while downloads worked, because our own transmit records are small.

Cause and fix

TLS record framing: a record must fit the receive buffer whole; the max-fragment-length extension is offered by clients only, and stock OpenSSL/browsers never offer it — they ship 16 KB records for any large body. The server's RX buffer was 4,096 B. It is now 16,709 B (BR_SSL_BUFSIZE_INPUT, one full record), allocated per accepted connection, not at boot (post-boot largest free block: 33.6 KB — one TLS client fits).

Measured on the rig (image 9ae86012, over OTA)

Check Result
32,045 B es-ES pack upload over TLS HTTP 200 in 1.9 s (dead connection before)
120 KB up / down, md5 4.8 s up, 2.6 s down, checksums match
/api/lang over TLS byte-identical to HTTP baseline
Handshake 0.5–0.7 s, TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384
Cookie Secure + HttpOnly
Heap while one TLS connection is alive free ~15.2 KB, largest block ~7–10 KB (transitory, per connection)
Fallback (invalid key + reboot) back on HTTP:80 in 26 s — the off switch works

Known and documented (not fixed — buffer economics): one TLS client at a time; a second simultaneous connection is dropped without a response, and connections opened in a tight burst right after another closes can be reset.

Docs (the v2.2.6 feature never reached them)

  • MANUAL.md §6: new Serving the UI over HTTPS section — openssl one-liner, Files-page provisioning, the 443 default, fallback contract, limitations.
  • SECURITY.md: attack-surface entry covers the HTTPS mode.
  • README comparison row mentions optional HTTPS in all three languages.

… feature

The server's 4096 B receive buffer killed every upload past ~4 KB over TLS
(bisected on hardware: 3.5 KB passes, 5 KB dies mid-body): a TLS record must
fit the receive buffer whole, the max-fragment-length extension is offered by
clients only, and stock OpenSSL/browsers never offer it — they ship 16 KB
records for any large body. Language packs and OTA images were impossible to
upload over HTTPS while downloads worked fine, because our own transmit
records are small.

The receive buffer is now 16,709 B (BR_SSL_BUFSIZE_INPUT, one full record).
It is allocated per accepted connection, not at boot; the post-boot largest
free block measures 33.6 KB, so one TLS client fits with room. Under
long-uptime fragmentation the accept can fail and that connection drops —
the listener keeps running.

Documented while at it, since the v2.2.6-beta feature never reached the
manual: MANUAL §6 gains "Serving the UI over HTTPS" (openssl one-liner,
Files-page provisioning, the 443 default, fallback = the off switch, and the
one-TLS-client-at-a-time limit), SECURITY.md's attack surface covers the
HTTPS mode, and the README comparison row mentions it in all three languages.
@angeloINTJ
angeloINTJ merged commit 8664978 into main Aug 19, 2026
@github-actions github-actions Bot added documentation Improvements or additions to documentation security Security, authentication, and threat model code Firmware source changes (src/, WebUI.h) labels Aug 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

code Firmware source changes (src/, WebUI.h) documentation Improvements or additions to documentation security Security, authentication, and threat model

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant