Native HTTPS on Linux frames: TLS in the runtime, Caddy gone - #481
Conversation
Every Linux frame — Raspberry Pi OS and the three Buildroot images alike — now serves HTTPS from inside the runtime's own HTTP server, from the same per-frame certificate the backend mints. No Caddy process any more, neither for the API (tls_proxy.nim, deleted) nor for the hotspot portal (setup_proxy.nim, deleted). ESP32 firmware already did this natively; Linux frames match. Plan and removal list: docs/native-https.md. Runtime - mummy is now the FrameOS/mummy fork (pinned by commit like pixie): TLS listeners with OpenSSL inside the epoll loop, newTlsConfig() loading the PEM material from memory (the private key never touches the file system), addListener/removeListener from any thread while serving, Request.secure. OpenSSL is already linked on every image (hub_client wss, http_client, logger), so no base image rebuild. - server/listeners.nim plans the sockets from the config alone: plain framePort (on loopback with exposeOnlyPort, on bindHost when set) plus TLS on httpsProxy.port when enabled and certificate material is present; otherwise tls:default_cert as before. A certificate the runtime cannot load or a port it cannot bind logs tls:config_error / tls:start_error and the frame stays reachable over plain HTTP; the plain listener failing is fatal, as it always was. - server/hotspot_listener.nim: while the hotspot is up and the plain listener is not on every interface, a plain listener on 10.42.0.1:<first free port from 8000> (every interface when that address is not up yet, which is what the Caddy setup proxy always bound), removed with the AP. hotspotSetupPort reads it for the QR code and the captive-portal redirect. - Request.secure feeds the same-origin guard, the Secure cookie flag and the cloud link's recorded origin alongside X-Forwarded-Proto, so an owner's own reverse proxy keeps working. - mummy's own log lines reach the frame logger (http:error, http:info; http:debug with the per-handshake timing only when debug is on). - frameos.service.unprivileged carries CAP_NET_BIND_SERVICE next to CAP_SYS_TTY_CONFIG for frames configured on a port below 1024. Backend, frontend, deploy - The three Buildroot gates from bde54b2 are gone (force-off in ensure_buildroot_frame_defaults, the 400, the disabled switch). A Buildroot frame keeps its HTTPS setting; a new one gets HTTPS on by default like every other frame. - The caddy apt package is no longer planned or installed. - Upgrades from a Caddy-era release: the caddy.service probe in the full deploy plan is version-gated on the deploy baseline's device-reported frameos_version — frame_may_still_run_caddy() is true only for an unknown version or one at or below LAST_CADDY_FRAMEOS_VERSION (2026.9.13, the last release that shipped Caddy). The first full deploy past it disables a leftover caddy.service; a frame already reporting something newer is never asked again. The setup scripts keep their disable line for one release. - Copy: "HTTPS proxy via Caddy" -> "HTTPS API", the section is "HTTPS" on every platform. docs/api-triality.md is unchanged (the https_proxy shape stays). Tests - Fork: tests/test_tls.nim (GET/POST, plain and TLS side by side, a 4 MB response, keep-alive, WebSocket over TLS, a client stalled mid-handshake, plain text on the TLS port, listeners added/removed while serving). - Runtime: test_listeners (planning), test_hotspot_listener and test_tls_listener (a TLS listener next to the harness's plain one through the real router: Secure cookie without a proxy header, the origin guard's 443 default, a 200 KB POST, removal while serving). The harness's stopServer now runs GC_fullCollect() first: the router handler closure is built on the test thread and freed on mummy's serving thread, and ORC's cycle roots are per thread — it only passed before by allocation-churn luck. - Backend: test_frame_may_still_run_caddy, test_post_deploy_plan_probes_caddy_only_for_caddy_era_frames, test_api_frame_update_buildroot_keeps_https_proxy. Bench on uus2w, Cloud-5, Cloud-W and one upgraded Raspbian Pi is still to do; see the doc's Bench section. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018eY8yWLDyHKssZ31wnkB54
- mummy fork at 522e28f9: the extra OpenSSL symbols are bound through the wrapper's dynlib patterns (stock Nim loads libssl at run time, so a plain importc left SSL_get_version undefined at link time on every Ubuntu job), and listener changes ride the responseQueued event instead of a fourth SelectEvent. - frame_may_still_run_caddy: the last-Caddy version tuple is asserted non-None before the comparison; mypy baseline refreshed (five entries went away with the removed Buildroot guard, the test fakes gained the usual arg-type entries). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018eY8yWLDyHKssZ31wnkB54
destroy() skips events newServer never created (the Windows fuzz job's ephemeral-port exhaustion used to segfault there instead of raising). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018eY8yWLDyHKssZ31wnkB54
ESP32 firmware sizeBuilt from 4cff633 by this run; compared with the latest release v2026.9.13.
Breakdown by subsystem —
|
| Subsystem | This PR | v2026.9.13 | Δ |
|---|---|---|---|
| FrameOS core (Nim) | 404,620 | 404,620 | ±0 |
| QuickJS | 356,328 | 356,328 | ±0 |
| FrameOS apps (Nim) | 316,786 | 316,786 | ±0 |
| pixie | 300,949 | 300,949 | ±0 |
| Wi-Fi stack | 295,300 | 295,300 | ±0 |
| Nim stdlib | 249,026 | 249,026 | ±0 |
| ESP-IDF misc | 178,065 | 178,065 | ±0 |
| Embedded font | 146,358 | 146,358 | ±0 |
| String pool (attributed to efuse) | 132,857 | 132,857 | ±0 |
| mbedTLS + certificates | 132,539 | 132,539 | ±0 |
| lwIP / HTTP / WebSocket | 126,644 | 126,644 | ±0 |
| fos_* firmware shell (C) | 116,897 | 116,897 | ±0 |
| libc / libm / newlib | 116,259 | 116,259 | ±0 |
| Storage (SPIFFS / FatFS / SD / NVS) | 94,132 | 94,132 | ±0 |
| Nim packages (chrono, zippy, chroma, qrgen, ...) | 72,043 | 72,043 | ±0 |
| Display drivers (C) | 64,934 | 64,934 | ±0 |
| Crypto (monocypher) | 35,149 | 35,149 | ±0 |
| Total mapped flash | 3,138,886 | 3,138,886 | ±0 |
Inside each subsystem
FrameOS core (Nim) — 404,620
| Part | This PR | v2026.9.13 | Δ |
|---|---|---|---|
frameos/js_runtime |
102,097 | 102,097 | ±0 |
frameos/interpreter.nim |
97,276 | 97,276 | ±0 |
frameos/utils |
89,928 | 89,928 | ±0 |
embedded_main.nim |
18,319 | 18,319 | ±0 |
lib/tz.nim |
16,751 | 16,751 | ±0 |
embedded_runtime.nim |
15,056 | 15,056 | ±0 |
frameos_nim_glue.c.obj |
9,814 | 9,814 | ±0 |
frameos/planner.nim |
9,605 | 9,605 | ±0 |
frameos/types.nim |
7,913 | 7,913 | ±0 |
frameos/values.nim |
6,873 | 6,873 | ±0 |
frameos/apps.nim |
6,054 | 6,054 | ±0 |
frameos/spool.nim |
5,678 | 5,678 | ±0 |
embedded_scene.nim |
4,945 | 4,945 | ±0 |
frameos/app_config.nim |
2,871 | 2,871 | ±0 |
frameos/reboot_reason.nim |
2,628 | 2,628 | ±0 |
frameos/node_config.nim |
2,590 | 2,590 | ±0 |
frameos/app_capabilities.nim |
2,489 | 2,489 | ±0 |
fos_netguard.c.obj |
2,092 | 2,092 | ±0 |
frameos/runtime_diagnostics.nim |
763 | 763 | ±0 |
fos_version.c.obj |
324 | 324 | ±0 |
frameos/cloud |
255 | 255 | ±0 |
frameos/ids.nim |
137 | 137 | ±0 |
frameos/channels.nim |
122 | 122 | ±0 |
frameos/hal |
40 | 40 | ±0 |
QuickJS — 356,328
| Part | This PR | v2026.9.13 | Δ |
|---|---|---|---|
quickjs.c.obj |
283,813 | 283,813 | ±0 |
libunicode.c.obj |
50,174 | 50,174 | ±0 |
libregexp.c.obj |
13,995 | 13,995 | ±0 |
dtoa.c.obj |
5,681 | 5,681 | ±0 |
cutils.c.obj |
2,141 | 2,141 | ±0 |
fos_qjs_glue.c.obj |
449 | 449 | ±0 |
fos_quickjs_tz.c.obj |
75 | 75 | ±0 |
FrameOS apps (Nim) — 316,786
| Part | This PR | v2026.9.13 | Δ |
|---|---|---|---|
apps/data/icalJson |
50,265 | 50,265 | ±0 |
apps/render/calendar |
30,707 | 30,707 | ±0 |
apps/render/chart |
21,541 | 21,541 | ±0 |
apps/data/immich |
17,695 | 17,695 | ±0 |
apps/data/wikicommons |
17,110 | 17,110 | ±0 |
apps/data/openaiImage |
13,970 | 13,970 | ±0 |
apps/data/unsplash |
11,084 | 11,084 | ±0 |
apps/data/weather |
10,193 | 10,193 | ±0 |
apps/data/beRecycle |
9,918 | 9,918 | ±0 |
apps/render/split |
9,648 | 9,648 | ±0 |
apps/data/localImage |
9,202 | 9,202 | ±0 |
apps/data/eventsToAgenda |
9,060 | 9,060 | ±0 |
apps/data/googlePhotos |
9,044 | 9,044 | ±0 |
apps/apps.nim |
8,636 | 8,636 | ±0 |
apps/render/zoomPan |
8,544 | 8,544 | ±0 |
apps/data/haSensor |
7,287 | 7,287 | ±0 |
apps/data/openaiText |
6,762 | 6,762 | ±0 |
apps/data/xmlToJson |
5,420 | 5,420 | ±0 |
apps/render/text |
5,419 | 5,419 | ±0 |
apps/render/svg |
5,176 | 5,176 | ±0 |
apps/render/image |
4,510 | 4,510 | ±0 |
apps/data/rotateImage |
3,457 | 3,457 | ±0 |
apps/logic/setAsState |
3,441 | 3,441 | ±0 |
apps/data/downloadImage |
3,345 | 3,345 | ±0 |
apps/render/gradient |
3,173 | 3,173 | ±0 |
apps/data/qr |
3,118 | 3,118 | ±0 |
apps/data/frameOSGallery |
3,046 | 3,046 | ±0 |
apps/data/downloadUrl |
3,045 | 3,045 | ±0 |
apps/render/color |
2,804 | 2,804 | ±0 |
apps/data/newImage |
2,676 | 2,676 | ±0 |
apps/data/resizeImage |
2,550 | 2,550 | ±0 |
apps/data/log |
2,283 | 2,283 | ±0 |
apps/render/opacity |
2,272 | 2,272 | ±0 |
apps/data/prettyJson |
2,213 | 2,213 | ±0 |
apps/data/parseJson |
1,844 | 1,844 | ±0 |
apps/data/clock |
1,835 | 1,835 | ±0 |
apps/logic/nextSleepDuration |
1,639 | 1,639 | ±0 |
apps/logic/ifElse |
1,476 | 1,476 | ±0 |
apps/logic/breakIfRendering |
1,378 | 1,378 | ±0 |
pixie — 300,949
| Part | This PR | v2026.9.13 | Δ |
|---|---|---|---|
pixie/fontformats/opentype.nim |
55,507 | 55,507 | ±0 |
pixie/fileformats/webp.nim |
37,525 | 37,525 | ±0 |
pixie/fileformats/svg.nim |
37,143 | 37,143 | ±0 |
pixie/paths.nim |
32,570 | 32,570 | ±0 |
pixie/fileformats/jpeg.nim |
25,338 | 25,338 | ±0 |
pixie/fileformats/png.nim |
22,849 | 22,849 | ±0 |
pixie/images.nim |
15,783 | 15,783 | ±0 |
pixie/fonts.nim |
11,019 | 11,019 | ±0 |
pixie/paints.nim |
9,889 | 9,889 | ±0 |
pixie/fileformats/bmp.nim |
8,234 | 8,234 | ±0 |
pixie/common.nim |
7,416 | 7,416 | ±0 |
pixie/blends.nim |
6,520 | 6,520 | ±0 |
pixie/fileformats/gif.nim |
5,925 | 5,925 | ±0 |
pixie/inflatestream.nim |
5,325 | 5,325 | ±0 |
pixie.nim |
5,224 | 5,224 | ±0 |
pixie/fileformats/webp_vp8_tables.nim |
3,881 | 3,881 | ±0 |
pixie/fileformats/ppm.nim |
3,681 | 3,681 | ±0 |
pixie/contexts.nim |
3,038 | 3,038 | ±0 |
pixie/fileformats/qoi.nim |
2,682 | 2,682 | ±0 |
pixie/internal.nim |
754 | 754 | ±0 |
pixie/rgb565.nim |
381 | 381 | ±0 |
pixie/fontformats/svgfont.nim |
215 | 215 | ±0 |
pixie/decodebudget.nim |
50 | 50 | ±0 |
Wi-Fi stack — 295,300
| Part | This PR | v2026.9.13 | Δ |
|---|---|---|---|
libnet80211.a |
136,168 | 136,168 | ±0 |
libpp.a |
62,154 | 62,154 | ±0 |
libwpa_supplicant.a |
54,900 | 54,900 | ±0 |
libphy.a |
34,176 | 34,176 | ±0 |
libesp_wifi.a |
4,780 | 4,780 | ±0 |
libesp_phy.a |
2,541 | 2,541 | ±0 |
libesp_coex.a |
291 | 291 | ±0 |
libcore.a |
287 | 287 | ±0 |
libespnow.a |
3 | 3 | ±0 |
Nim stdlib — 249,026
| Part | This PR | v2026.9.13 | Δ |
|---|---|---|---|
pure/collections/tables.nim |
70,556 | 70,556 | ±0 |
system.nim |
46,659 | 46,659 | ±0 |
pure/unicode.nim |
17,405 | 17,405 | ±0 |
pure/times.nim |
16,984 | 16,984 | ±0 |
pure/json.nim |
13,414 | 13,414 | ±0 |
std/private/dragonbox.nim |
10,351 | 10,351 | ±0 |
pure/strutils.nim |
8,520 | 8,520 | ±0 |
pure/parsexml.nim |
5,999 | 5,999 | ±0 |
pure/algorithm.nim |
5,368 | 5,368 | ±0 |
pure/base64.nim |
5,302 | 5,302 | ±0 |
pure/hashes.nim |
4,114 | 4,114 | ±0 |
pure/xmlparser.nim |
3,567 | 3,567 | ±0 |
pure/parsejson.nim |
3,553 | 3,553 | ±0 |
std/formatfloat.nim |
3,508 | 3,508 | ±0 |
pure/uri.nim |
3,225 | 3,225 | ±0 |
pure/collections/sets.nim |
3,211 | 3,211 | ±0 |
pure/streams.nim |
2,820 | 2,820 | ±0 |
pure/strformat.nim |
2,641 | 2,641 | ±0 |
std/syncio.nim |
2,355 | 2,355 | ±0 |
pure/xmltree.nim |
2,051 | 2,051 | ±0 |
pure/lexbase.nim |
1,805 | 1,805 | ±0 |
pure/parseutils.nim |
1,757 | 1,757 | ±0 |
system/exceptions.nim |
1,598 | 1,598 | ±0 |
pure/strtabs.nim |
1,568 | 1,568 | ±0 |
pure/pathnorm.nim |
1,356 | 1,356 | ±0 |
pure/options.nim |
1,118 | 1,118 | ±0 |
std/private/ospaths2.nim |
1,032 | 1,032 | ±0 |
pure/collections/lists.nim |
983 | 983 | ±0 |
system/dollars.nim |
891 | 891 | ±0 |
pure/random.nim |
835 | 835 | ±0 |
std/private/digitsutils.nim |
651 | 651 | ±0 |
pure/math.nim |
650 | 650 | ±0 |
std/oserrors.nim |
637 | 637 | ±0 |
std/private/osdirs.nim |
580 | 580 | ±0 |
std/monotimes.nim |
401 | 401 | ±0 |
std/private/oscommon.nim |
329 | 329 | ±0 |
std/envvars.nim |
250 | 250 | ±0 |
std/private/decode_helpers.nim |
240 | 240 | ±0 |
std/private/ossymlinks.nim |
187 | 187 | ±0 |
pure/bitops.nim |
162 | 162 | ±0 |
pure/os.nim |
99 | 99 | ±0 |
std/private/osfiles.nim |
96 | 96 | ±0 |
std/assertions.nim |
87 | 87 | ±0 |
core/macros.nim |
69 | 69 | ±0 |
system/iterators.nim |
33 | 33 | ±0 |
system/ctypes.nim |
9 | 9 | ±0 |
ESP-IDF misc — 178,065
| Part | This PR | v2026.9.13 | Δ |
|---|---|---|---|
libesp_hw_support.a |
37,650 | 37,650 | ±0 |
libhal.a |
21,464 | 21,464 | ±0 |
libfreertos.a |
17,194 | 17,194 | ±0 |
libesp_system.a |
13,780 | 13,780 | ±0 |
libesp_driver_uart.a |
11,627 | 11,627 | ±0 |
libheap.a |
8,660 | 8,660 | ±0 |
libesp_driver_i2c.a |
8,062 | 8,062 | ±0 |
libesp_driver_spi.a |
7,756 | 7,756 | ±0 |
libconsole.a |
5,733 | 5,733 | ±0 |
libjson.a |
4,940 | 4,940 | ±0 |
libesp_driver_gpio.a |
3,890 | 3,890 | ±0 |
libbootloader_support.a |
3,813 | 3,813 | ±0 |
libxtensa.a |
3,691 | 3,691 | ±0 |
libesp_driver_usb_serial_jtag.a |
3,408 | 3,408 | ±0 |
libesp_ringbuf.a |
3,322 | 3,322 | ±0 |
libesp_event.a |
3,257 | 3,257 | ±0 |
libesp_mm.a |
2,451 | 2,451 | ±0 |
libapp_update.a |
2,422 | 2,422 | ±0 |
libesp_psram.a |
2,329 | 2,329 | ±0 |
libesp_timer.a |
2,326 | 2,326 | ±0 |
libsoc.a |
2,315 | 2,315 | ±0 |
libesp_adc.a |
2,289 | 2,289 | ±0 |
liblog.a |
1,375 | 1,375 | ±0 |
libpthread.a |
1,365 | 1,365 | ±0 |
libesp_security.a |
1,050 | 1,050 | ±0 |
libesp_rom.a |
902 | 902 | ±0 |
libesp_app_format.a |
507 | 507 | ±0 |
libxt_hal.a |
437 | 437 | ±0 |
libesp_common.a |
26 | 26 | ±0 |
libesp_pm.a |
24 | 24 | ±0 |
Embedded font — 146,358
| Part | This PR | v2026.9.13 | Δ |
|---|---|---|---|
assets/fonts.nim |
146,358 | 146,358 | ±0 |
String pool (attributed to efuse) — 132,857
| Part | This PR | v2026.9.13 | Δ |
|---|---|---|---|
esp_efuse_utility.c.obj |
131,811 | 131,811 | ±0 |
esp_efuse_rtc_calib.c.obj |
566 | 566 | ±0 |
esp_efuse_table.c.obj |
268 | 268 | ±0 |
esp_efuse_api.c.obj |
151 | 151 | ±0 |
esp_efuse_startup.c.obj |
61 | 61 | ±0 |
mbedTLS + certificates — 132,539
| Part | This PR | v2026.9.13 | Δ |
|---|---|---|---|
libmbedcrypto.a |
71,391 | 71,391 | ±0 |
libmbedtls.a |
45,803 | 45,803 | ±0 |
libmbedx509.a |
7,084 | 7,084 | ±0 |
libesp-tls.a |
6,709 | 6,709 | ±0 |
libesp_https_server.a |
1,552 | 1,552 | ±0 |
lwIP / HTTP / WebSocket — 126,644
| Part | This PR | v2026.9.13 | Δ |
|---|---|---|---|
liblwip.a |
78,305 | 78,305 | ±0 |
libhttp_parser.a |
10,350 | 10,350 | ±0 |
libesp_http_server.a |
10,016 | 10,016 | ±0 |
libtcp_transport.a |
8,237 | 8,237 | ±0 |
libespressif__esp_websocket_client.a |
6,826 | 6,826 | ±0 |
libesp_http_client.a |
6,666 | 6,666 | ±0 |
libesp_netif.a |
6,244 | 6,244 | ±0 |
fos_ firmware shell (C)* — 116,897
| Part | This PR | v2026.9.13 | Δ |
|---|---|---|---|
fos_http.c.obj |
22,202 | 22,202 | ±0 |
fos_cloud.c.obj |
16,975 | 16,975 | ±0 |
fos_console.c.obj |
13,601 | 13,601 | ±0 |
fos_client.c.obj |
10,499 | 10,499 | ±0 |
fos_scenes.c.obj |
7,551 | 7,551 | ±0 |
fos_settings.c.obj |
6,511 | 6,511 | ±0 |
fos_ota.c.obj |
5,039 | 5,039 | ±0 |
fos_config.c.obj |
4,298 | 4,298 | ±0 |
fos_wifi.c.obj |
3,201 | 3,201 | ±0 |
fos_assets.c.obj |
3,124 | 3,124 | ±0 |
fos_cloud_contract.c.obj |
2,896 | 2,896 | ±0 |
fos_sd_probe.c.obj |
2,753 | 2,753 | ±0 |
fos_assets_sd.c.obj |
2,480 | 2,480 | ±0 |
fos_schedule.c.obj |
2,351 | 2,351 | ±0 |
fos_buttons.c.obj |
2,139 | 2,139 | ±0 |
fos_status_screen.c.obj |
2,049 | 2,049 | ±0 |
main.c.obj |
1,667 | 1,667 | ±0 |
fos_config_parse.c.obj |
1,467 | 1,467 | ±0 |
fos_url_guard.c.obj |
1,397 | 1,397 | ±0 |
fos_tz.c.obj |
1,381 | 1,381 | ±0 |
fos_battery.c.obj |
1,140 | 1,140 | ±0 |
fos_minisig.c.obj |
497 | 497 | ±0 |
fos_framebuffer.c.obj |
483 | 483 | ±0 |
fos_assets_path.c.obj |
259 | 259 | ±0 |
fos_power.c.obj |
241 | 241 | ±0 |
fos_battery_filter.c.obj |
214 | 214 | ±0 |
fos_wake.c.obj |
184 | 184 | ±0 |
fos_board.c.obj |
179 | 179 | ±0 |
fos_json_guard.c.obj |
119 | 119 | ±0 |
libc / libm / newlib — 116,259
| Part | This PR | v2026.9.13 | Δ |
|---|---|---|---|
libc.a |
76,467 | 76,467 | ±0 |
libm.a |
31,563 | 31,563 | ±0 |
libnewlib.a |
6,609 | 6,609 | ±0 |
libstdc++.a |
1,476 | 1,476 | ±0 |
libgcc.a |
98 | 98 | ±0 |
libcxx.a |
46 | 46 | ±0 |
Storage (SPIFFS / FatFS / SD / NVS) — 94,132
| Part | This PR | v2026.9.13 | Δ |
|---|---|---|---|
libspiffs.a |
20,380 | 20,380 | ±0 |
libfatfs.a |
20,133 | 20,133 | ±0 |
libspi_flash.a |
14,168 | 14,168 | ±0 |
libnvs_flash.a |
13,741 | 13,741 | ±0 |
libsdmmc.a |
13,013 | 13,013 | ±0 |
libvfs.a |
6,196 | 6,196 | ±0 |
libesp_driver_sdspi.a |
3,858 | 3,858 | ±0 |
libesp_partition.a |
1,960 | 1,960 | ±0 |
libesp_vfs_console.a |
678 | 678 | ±0 |
libnvs_sec_provider.a |
5 | 5 | ±0 |
Nim packages (chrono, zippy, chroma, qrgen, ...) — 72,043
| Part | This PR | v2026.9.13 | Δ |
|---|---|---|---|
QRgen 3.1.0 |
22,036 | 22,036 | ±0 |
chrono 0.3.1 |
15,510 | 15,510 | ±0 |
zippy 0.10.19 |
15,280 | 15,280 | ±0 |
chroma 1.0.0 |
14,924 | 14,924 | ±0 |
checksums 0.2.1 |
3,371 | 3,371 | ±0 |
flatty 0.3.4 |
533 | 533 | ±0 |
jsony 1.1.5 |
272 | 272 | ±0 |
bumpy 1.1.3 |
117 | 117 | ±0 |
Display drivers (C) — 64,934
| Part | This PR | v2026.9.13 | Δ |
|---|---|---|---|
(everything else) |
21,290 | 21,290 | ±0 |
frameos_panel_table.c.obj |
6,685 | 6,685 | ±0 |
EPD_13in3e.c.obj |
1,792 | 1,792 | ±0 |
EPD_3in7.c.obj |
1,564 | 1,564 | ±0 |
EPD_5in79.c.obj |
1,481 | 1,481 | ±0 |
DEV_Debug.c.obj |
1,309 | 1,309 | ±0 |
EPD_2in7.c.obj |
1,292 | 1,292 | ±0 |
EPD_4in2.c.obj |
1,262 | 1,262 | ±0 |
EPD_7in3e.c.obj |
1,208 | 1,208 | ±0 |
EPD_4in2_V2.c.obj |
1,184 | 1,184 | ±0 |
DEV_Config_esp.c.obj |
1,180 | 1,180 | ±0 |
EPD_4in26.c.obj |
1,123 | 1,123 | ±0 |
| 29 smaller rows | 23,564 | 23,564 | ±0 |
Crypto (monocypher) — 35,149
| Part | This PR | v2026.9.13 | Δ |
|---|---|---|---|
monocypher.c.obj |
31,916 | 31,916 | ±0 |
monocypher-ed25519.c.obj |
3,233 | 3,233 | ±0 |
Largest 30 objects
| Object | Subsystem | This PR | v2026.9.13 | Δ |
|---|---|---|---|---|
quickjs.c.obj |
QuickJS | 283,813 | 283,813 | ±0 |
assets/fonts.nim |
Embedded font | 146,358 | 146,358 | ±0 |
esp_efuse_utility.c.obj |
String pool (attributed to efuse) | 131,811 | 131,811 | ±0 |
frameos/interpreter.nim |
FrameOS core (Nim) | 97,276 | 97,276 | ±0 |
nim/lib/pure/collections/tables.nim |
Nim stdlib | 70,556 | 70,556 | ±0 |
frameos/js_runtime/app_runtime.nim |
FrameOS core (Nim) | 61,493 | 61,493 | ±0 |
pkgs/pixie-6.1.0/pixie/fontformats/opentype.nim |
pixie | 55,507 | 55,507 | ±0 |
libunicode.c.obj |
QuickJS | 50,174 | 50,174 | ±0 |
nim/lib/system.nim |
Nim stdlib | 46,659 | 46,659 | ±0 |
apps/data/icalJson/ical.nim |
FrameOS apps (Nim) | 44,568 | 44,568 | ±0 |
pkgs/pixie-6.1.0/pixie/fileformats/webp.nim |
pixie | 37,525 | 37,525 | ±0 |
pkgs/pixie-6.1.0/pixie/fileformats/svg.nim |
pixie | 37,143 | 37,143 | ±0 |
frameos/utils/image.nim |
FrameOS core (Nim) | 34,490 | 34,490 | ±0 |
pkgs/pixie-6.1.0/pixie/paths.nim |
pixie | 32,570 | 32,570 | ±0 |
monocypher.c.obj |
Crypto (monocypher) | 31,916 | 31,916 | ±0 |
frameos/js_runtime/runtime.nim |
FrameOS core (Nim) | 30,086 | 30,086 | ±0 |
pkgs/pixie-6.1.0/pixie/fileformats/jpeg.nim |
pixie | 25,338 | 25,338 | ±0 |
apps/render/calendar/app.nim |
FrameOS apps (Nim) | 23,659 | 23,659 | ±0 |
pkgs/pixie-6.1.0/pixie/fileformats/png.nim |
pixie | 22,849 | 22,849 | ±0 |
fos_http.c.obj |
fos_* firmware shell (C) | 22,202 | 22,202 | ±0 |
apps/render/chart/app.nim |
FrameOS apps (Nim) | 18,940 | 18,940 | ±0 |
embedded_main.nim |
FrameOS core (Nim) | 18,319 | 18,319 | ±0 |
frameos/utils/status_screen.nim |
FrameOS core (Nim) | 18,111 | 18,111 | ±0 |
x509_crt_bundle.S.obj |
mbedTLS + certificates | 17,928 | 17,928 | ±0 |
nim/lib/pure/unicode.nim |
Nim stdlib | 17,405 | 17,405 | ±0 |
nim/lib/pure/times.nim |
Nim stdlib | 16,984 | 16,984 | ±0 |
fos_cloud.c.obj |
fos_* firmware shell (C) | 16,975 | 16,975 | ±0 |
lib/tz.nim |
FrameOS core (Nim) | 16,751 | 16,751 | ±0 |
apps/data/immich/app.nim |
FrameOS apps (Nim) | 16,148 | 16,148 | ±0 |
pkgs/pixie-6.1.0/pixie/images.nim |
pixie | 15,783 | 15,783 | ±0 |
Flash = .text + .rodata from the linker map via esp_idf_size; "Inside each subsystem" splits every bucket one level down (the Nim core by directory, apps by app, packages by package, ESP-IDF by archive) over all objects, not just the largest. "String pool (attributed to efuse)" is the linker's merged string-literal pool for the whole image, not efuse code — see docs/esp32-image-size.md.
Every Linux frame — Raspberry Pi OS and the three Buildroot images alike — now serves HTTPS from inside the runtime's own HTTP server, from the same per-frame certificate the backend mints. No Caddy process any more, neither for the API (
tls_proxy.nim, deleted) nor for the hotspot portal (setup_proxy.nim, deleted). ESP32 firmware already did this natively; Linux frames match. Plan, decisions and removal list:docs/native-https.md.Depends on the FrameOS/mummy fork: FrameOS/mummy#1 (
frameos.nimblepins its commit like pixie;nimble.lockupdated by hand for that one entry).Runtime
newTlsConfig()loading the PEM material from memory (the private key never touches the file system),addListener/removeListenerfrom any thread while serving,Request.secure. OpenSSL is already linked on every image (hub_client wss, http_client, logger), so no base image rebuild.server/listeners.nimplans the sockets from the config alone: plainframePort(on loopback withexposeOnlyPort, onbindHostwhen set) plus TLS onhttpsProxy.portwhen enabled and certificate material is present; otherwisetls:default_certas before. A certificate the runtime cannot load or a port it cannot bind logstls:config_error/tls:start_errorand the frame stays reachable over plain HTTP; the plain listener failing is fatal, as it always was.server/hotspot_listener.nim: while the hotspot is up and the plain listener is not on every interface, a plain listener on10.42.0.1:<first free port from 8000>(every interface when that address is not up yet, which is what the Caddy setup proxy always bound), removed with the AP.Request.securefeeds the same-origin guard, theSecurecookie flag and the cloud link's recorded origin alongsideX-Forwarded-Proto, so an owner's own reverse proxy keeps working.http:error,http:info;http:debugwith per-handshake timing only whendebugis on).frameos.service.unprivilegedcarriesCAP_NET_BIND_SERVICEnext toCAP_SYS_TTY_CONFIG.Backend, frontend, deploy
caddyapt package is no longer planned or installed.caddy.serviceprobe in the full deploy plan is version-gated on the deploy baseline's device-reportedframeos_version.frame_may_still_run_caddy()is true only for an unknown version or one at or belowLAST_CADDY_FRAMEOS_VERSION = "2026.9.13"(the last release that shipped Caddy, cut this morning). The first full deploy past it disables a leftovercaddy.service; a frame already reporting something newer is never asked again. The setup scripts keep theirsystemctl disable --now caddy.serviceline for one release, then both go.docs/api-triality.mdis unchanged (thehttps_proxyshape stays).Tests
tests/test_tls.nim— GET/POST, plain and TLS side by side, a 4 MB response, keep-alive, WebSocket over TLS, a client stalled mid-handshake, plain text on the TLS port, listeners added/removed while serving. Upstream suite green with and without-d:ssl.test_listeners(planning),test_hotspot_listener,test_tls_listener(a TLS listener next to the harness's plain one through the real router:Securecookie without a proxy header, the origin guard's 443 default, a 200 KB POST, removal while serving). The harness'sstopServernow runsGC_fullCollect()first: the router handler closure is built on the test thread and freed on mummy's serving thread, and ORC's cycle roots are per thread — it only passed before by allocation-churn luck.test_frame_may_still_run_caddy,test_post_deploy_plan_probes_caddy_only_for_caddy_era_frames,test_api_frame_update_buildroot_keeps_https_proxy;app/tasks,test_frames.py, frame sync andapp/utilssuites green (802 passed).tscand prettier clean.Still to do
Bench on uus2w (uid 990), Cloud-5, Cloud-W (ARMv6, handshake ms with
debugon) and one Raspbian Pi upgraded from 2026.9.13 (expect the plan to disablecaddy.serviceonce, then never probe again) — see the doc's Bench section.🤖 Generated with Claude Code
https://claude.ai/code/session_018eY8yWLDyHKssZ31wnkB54