Skip to content

Review todo: cloud Low lists (store, AI, MCP, device plane, DB/ops) - #472

Merged
mariusandra merged 1 commit into
mainfrom
review-todo-cloud-lows
Sep 10, 2026
Merged

Review todo: cloud Low lists (store, AI, MCP, device plane, DB/ops)#472
mariusandra merged 1 commit into
mainfrom
review-todo-cloud-lows

Conversation

@mariusandra

Copy link
Copy Markdown
Collaborator

Ships the five "Cloud — …" Low sections of docs/review-todo.md (scene store/images/render, the AI, MCP server, device plane, database/ops/deployment) — 32 items in one PR, five fork agents in one shared worktree.

Scene store, images, render

  • Per-owner render concurrency (1 in flight, 2 waiting per account or client key) on top of the global 2-slot / 8-deep queue → 429 render_concurrency_limit, retry-after on 429/503.
  • POST /api/scenes/render and the lint route run the same Origin/CSRF gate as the other mutating cookie routes; API-token bearers unchanged.
  • unzipBounded (src/lib/zip-bounded.ts): parses the central directory itself and inflates selected entries through fflate's streaming Inflate in 8 KB slices, checking the actual inflated total. Replaces all 8 unzipSync call sites. Test proves a 4 MB stream with a 100-byte header is refused under a 1 MB ceiling (fflate's sync path silently truncated it before).
  • Transparency verdict cached by content sha256, not byte length.
  • Store scene delete: typed-name dialog explaining what goes and pointing at yank, instead of window.confirm.
  • Yank re-projects description/tags/category/frameos_version/risk_flags from the version now served (legacy versions without a recorded listing leave the text alone).

The AI

  • get_frame returns last_state / last_metrics inside the untrusted_data frame.
  • Deleting or evicting a chat stops its running turn ("The chat was deleted."); appendChatMessage returns false on a missing chat row instead of surfacing the FK error.
  • Chats get a title from the first user message (≤ 60 chars); message_count follows the retained window after trimming.
  • startTurn's run receives the registered turn instead of closing over the const before it is initialised.
  • Relay maxDuration = 900 (= the turn ceiling); both chat clients resume on a 16-min budget with backoff and a 30-consecutive-unreachable cap.

MCP server

  • Hints for invalid_frame, scene_pulled, insufficient_scope, daily_cap_reached (402), ai_disabled, invalid_openai_key, model_budget_exhausted, model_failed; too_many_scenes distinguishes the convert cap from the frame's 20-scene cap.
  • Nim→JS (scene-convert): - -x-(-x); a let shadowing an argument or earlier binding gets a fresh name instead of a TDZ read.
  • repository.json cache forgotten after every scene-changing tool; one fresh re-read on a miss.
  • frame_logs sends limit / search / since; GET /api/frames/{id}/logs applies them (ILIKE with wildcard escaping, bounded).
  • The anonymous convert route answers 502 model_failed for a bad caller-supplied key; only signed-in callers see invalid_openai_key.

Device plane

  • Replayed enrollment answers the link's current scopes.
  • First enrollment is race-safe: on conflict do nothing on linked_client_id, the loser re-registers against the winner (three concurrent enrollments → one frame, all 200).
  • Log broadcast uses the rows that survived the cull (RETURNING + in-transaction survivor select).
  • Read-only API tokens never enqueue device commands from GETs (/asset, /image, /assets, /scene_images/{scene}) → 403 read_only_token when only the device could answer.
  • /asset answers 503 frame_offline + Retry-After: 60 immediately for disconnected or sleeping frames (still queues the fetch).
  • One normalizeAssetPath (control chars, .., backslashes) on both paths.
  • Backup quota checked and spent in one transaction under a per-account advisory lock (499 rows + 3 concurrent saves → 200/403/403).
  • /command set_current_scene runs the scene id through deviceSceneIdForFrame like the event route.

Database, ops, deployment

  • grant-superadmin.sh takes an account id or a verified identity email; unverified-only matches are refused.
  • Job tokens are minted with a TTL (default 90 days); POST /api/admin/billing/nightly reports token_expires_at, the nightly script warns for the last 14 days. Runbook updated.
  • db-cleanup.sh prunes expired/used password_reset_tokens and email_verification_tokens; integration test runs the real script.
  • 0053_fk_delete_indexes.sql: 11 indexes on ON DELETE FK columns that lacked one. schema.ts now declares every SQL-only index and three UNIQUE constraints it mis-declared as unique indexes; schema-migrations.test.ts diffs drizzle/*.sql against the schema both ways.
  • frameos-cloud-update --rollback is a stack (/opt/frameos-cloud.history); rehearsal step 6 extended and passing in Docker.
  • Restore drill asserts on frame tables; frame-hub unit in deployment.md gets Requires=/After=postgresql.service.

Verification

  • tsc, eslint, vitest green in auth-web, frame-hub, db, ledger, mcp, scene-convert, auth-client; cloud-frontend and frontend tsc clean.
  • auth-web integration suite (34 files / 435 tests) and frame-hub integration (55) green on Postgres. One pre-existing order-dependent assertion in ai-save-scene (a global store_images count) is now a before/after delta.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Sd9p5eSSzqxRqUj6GwCWEo

Ships the five "Cloud — …" Low sections of docs/review-todo.md in one PR.

Scene store, images, render
- Per-owner render concurrency (1 in flight, 2 waiting per account/client
  key) on top of the global 2-slot/8-deep queue; 429 render_concurrency_limit.
- POST /api/scenes/render and the lint route run the same Origin/CSRF gate
  as the other mutating cookie routes (API-token bearers unchanged).
- unzipBounded: streaming inflate that bounds the bytes actually produced,
  replacing every unzipSync call site; the declared-size check stays as the
  cheap pre-check.
- Transparency verdict cached by content hash, not byte length.
- Store scene delete is a typed-name dialog that points at yanking.
- Yank re-projects description/tags/category from the version now served.

The AI
- get_frame wraps device-written last_state/last_metrics as untrusted.
- Deleting or evicting a chat stops its running turn cleanly; appends after
  the row is gone return false instead of an FK error.
- Chats get a title from the first user message; message_count follows the
  retained window after trimming.
- The turn handle is passed into run() rather than closed over before init.
- Relay maxDuration = the 15-min turn ceiling; both clients resume on a
  16-min budget with backoff and a consecutive-unreachable cap.

MCP server
- Hints for invalid_frame, scene_pulled, insufficient_scope, the AI 402 and
  friends; too_many_scenes distinguishes the convert cap from the frame cap.
- Nim→JS: `- -x` emits `-(-x)`; a shadowing `let` gets a fresh name.
- repository.json cache is forgotten after every scene-changing tool and
  re-read once on a miss.
- frame_logs filters (limit/search/since) server-side.
- The anonymous convert route no longer distinguishes invalid_openai_key.

Device plane
- Replayed enrollment answers the link's current scopes.
- First enrollment is race-safe (on conflict do nothing + re-register).
- Log broadcast uses the rows that survived the cull.
- Read-only tokens never enqueue device commands from GETs (403 read_only_token).
- /asset answers 503 frame_offline immediately for offline/sleeping frames.
- One normalizeAssetPath (control chars, backslashes) on both paths.
- Backup quota checked and spent under a per-account advisory lock.
- /command set_current_scene translates store uuids like the event route.

Database, ops, deployment
- grant-superadmin.sh matches account id or a verified identity email only.
- Job tokens are minted with a TTL; the nightly route reports expiry.
- db-cleanup.sh prunes password-reset and email-verification tokens.
- 0053: indexes on every ON DELETE FK column that lacked one; schema.ts now
  declares every SQL-only index/unique constraint, guarded by a test that
  diffs drizzle/*.sql against the schema.
- frameos-cloud-update --rollback is a stack (/opt/frameos-cloud.history).
- Restore drill asserts on frame tables; frame-hub unit orders after postgres.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sd9p5eSSzqxRqUj6GwCWEo
@github-actions

Copy link
Copy Markdown
Contributor

ESP32 firmware size

Built from 906a713 by this run; compared with the latest release v2026.9.12.

Image This PR v2026.9.12 Δ
esp32-s3 app (OTA image) 3,161,408
87.7% of 3520K slot, 433 KB free
3,160,192 +1,216 (+0.0%)
esp32-s3 merged flash image 3,226,944 3,225,728 +1,216 (+0.0%)
esp32-s3-32mb app (OTA image) 3,161,408
76.6% of 4032K slot, 945 KB free
3,160,192 +1,216 (+0.0%)
esp32-s3-32mb merged flash image 3,292,480 3,291,264 +1,216 (+0.0%)

Breakdown by subsystem — esp32-s3

Subsystem This PR v2026.9.12 Δ
FrameOS core (Nim) 404,596 404,507 +89 (+0.0%)
QuickJS 356,332 356,332 ±0
FrameOS apps (Nim) 318,368 317,959 +409 (+0.1%)
pixie 301,065 301,065 ±0
Wi-Fi stack 295,300 295,304 -4 (-0.0%)
Nim stdlib 248,937 248,937 ±0
ESP-IDF misc 178,065 178,057 +8 (+0.0%)
Embedded font 146,358 146,358 ±0
String pool (attributed to efuse) 132,705 132,605 +100 (+0.1%)
mbedTLS + certificates 132,539 132,539 ±0
lwIP / HTTP / WebSocket 126,640 126,640 ±0
fos_* firmware shell (C) 116,897 116,298 +599 (+0.5%)
libc / libm / newlib 116,259 116,259 ±0
Storage (SPIFFS / FatFS / SD / NVS) 94,136 94,136 ±0
Nim packages (chrono, zippy, chroma, qrgen, ...) 72,031 72,031 ±0
Display drivers (C) 64,690 64,690 ±0
Crypto (monocypher) 35,149 35,149 ±0
Total mapped flash 3,140,067 3,138,866 +1,201 (+0.0%)
Inside each subsystem

FrameOS core (Nim) — 404,596

Part This PR v2026.9.12 Δ
frameos/js_runtime 102,117 102,114 +3 (+0.0%)
frameos/interpreter.nim 97,344 97,352 -8 (-0.0%)
frameos/utils 89,644 89,574 +70 (+0.1%)
embedded_main.nim 18,351 18,351 ±0
lib/tz.nim 16,875 16,875 ±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,893 +20 (+0.3%)
frameos/values.nim 6,901 6,901 ±0
frameos/apps.nim 6,046 6,046 ±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,493 2,493 ±0
fos_netguard.c.obj 2,092 2,088 +4 (+0.2%)
frameos/runtime_diagnostics.nim 763 763 ±0
fos_version.c.obj 324 324 ±0
frameos/cloud 255 255 ±0
frameos/ids.nim 133 133 ±0
frameos/channels.nim 118 118 ±0
frameos/hal 40 40 ±0

QuickJS — 356,332

Part This PR v2026.9.12 Δ
quickjs.c.obj 283,817 283,817 ±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) — 318,368

Part This PR v2026.9.12 Δ
apps/data/icalJson 50,265 50,265 ±0
apps/render/calendar 30,666 30,666 ±0
apps/render/chart 21,541 21,541 ±0
apps/data/immich 17,542 17,538 +4 (+0.0%)
apps/data/wikicommons 17,110 17,110 ±0
apps/data/openaiImage 13,926 13,926 ±0
apps/data/beRecycle 11,166 10,749 +417 (+3.9%)
apps/data/unsplash 11,056 11,056 ±0
apps/data/weather 10,210 10,210 ±0
apps/render/split 9,648 9,648 ±0
apps/data/localImage 9,186 9,190 -4 (-0.0%)
apps/data/eventsToAgenda 9,084 9,084 ±0
apps/data/googlePhotos 9,068 9,068 ±0
apps/apps.nim 8,636 8,636 ±0
apps/render/zoomPan 8,552 8,552 ±0
apps/data/openaiText 7,353 7,353 ±0
apps/data/haSensor 7,291 7,291 ±0
apps/render/text 5,423 5,423 ±0
apps/data/xmlToJson 5,420 5,420 ±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,333 3,333 ±0
apps/render/gradient 3,173 3,173 ±0
apps/data/qr 3,118 3,118 ±0
apps/data/downloadUrl 3,065 3,065 ±0
apps/data/frameOSGallery 3,006 3,006 ±0
apps/render/color 2,804 2,804 ±0
apps/data/newImage 2,652 2,652 ±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,843 -8 (-0.4%)
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 — 301,065

Part This PR v2026.9.12 Δ
pixie/fontformats/opentype.nim 55,507 55,507 ±0
pixie/fileformats/webp.nim 37,525 37,525 ±0
pixie/fileformats/svg.nim 37,203 37,203 ±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,787 15,787 ±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,540 6,540 ±0
pixie/fileformats/gif.nim 5,929 5,929 ±0
pixie/inflatestream.nim 5,325 5,325 ±0
pixie.nim 5,248 5,248 ±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,686 2,686 ±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.12 Δ
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,784 -4 (-0.1%)
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 — 248,937

Part This PR v2026.9.12 Δ
pure/collections/tables.nim 70,556 70,556 ±0
system.nim 46,665 46,665 ±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,473 8,473 ±0
pure/parsexml.nim 5,975 5,975 ±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,332 1,332 ±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.12 Δ
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,656 +4 (+0.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,253 +4 (+0.1%)
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.12 Δ
assets/fonts.nim 146,358 146,358 ±0

String pool (attributed to efuse) — 132,705

Part This PR v2026.9.12 Δ
esp_efuse_utility.c.obj 131,659 131,559 +100 (+0.1%)
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.12 Δ
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,640

Part This PR v2026.9.12 Δ
liblwip.a 78,301 78,301 ±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.12 Δ
fos_http.c.obj 22,202 22,034 +168 (+0.8%)
fos_cloud.c.obj 16,975 17,935 -960 (-5.4%)
fos_console.c.obj 13,601 13,501 +100 (+0.7%)
fos_client.c.obj 10,499 10,483 +16 (+0.2%)
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,775 -736 (-12.7%)
fos_config.c.obj 4,298 5,755 -1,457 (-25.3%)
fos_assets.c.obj 3,124 3,282 -158 (-4.8%)
fos_wifi.c.obj 3,201 3,201 ±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,339 +12 (+0.5%)
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,673 -6 (-0.4%)
fos_config_parse.c.obj 1,467 +1,467
fos_url_guard.c.obj 1,397 +1,397
fos_tz.c.obj 1,381 1,381 ±0
fos_battery.c.obj 1,140 1,140 ±0
fos_minisig.c.obj 497 +497
fos_framebuffer.c.obj 483 483 ±0
fos_assets_path.c.obj 259 +259
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.12 Δ
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,136

Part This PR v2026.9.12 Δ
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,745 13,745 ±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,031

Part This PR v2026.9.12 Δ
QRgen 3.1.0 22,036 22,036 ±0
chrono 0.3.1 15,514 15,514 ±0
zippy 0.10.19 15,272 15,272 ±0
chroma 1.0.0 14,916 14,916 ±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,690

Part This PR v2026.9.12 Δ
(everything else) 21,249 21,249 ±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,556 1,556 ±0
EPD_5in79.c.obj 1,478 1,478 ±0
EPD_2in7.c.obj 1,289 1,289 ±0
EPD_4in2.c.obj 1,255 1,255 ±0
EPD_7in3e.c.obj 1,208 1,208 ±0
DEV_Debug.c.obj 1,206 1,206 ±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,485 23,485 ±0

Crypto (monocypher) — 35,149

Part This PR v2026.9.12 Δ
monocypher.c.obj 31,920 31,920 ±0
monocypher-ed25519.c.obj 3,229 3,229 ±0
Biggest movers (per object)
Object Δ This PR Baseline
fos_config.c.obj -1,457 4,298 5,755
fos_cloud.c.obj -960 16,975 17,935
fos_ota.c.obj -736 5,039 5,775
apps/data/beRecycle/app.nim +417 9,680 9,263
fos_http.c.obj +168 22,202 22,034
fos_console.c.obj +100 13,601 13,501
esp_efuse_utility.c.obj +100 131,659 131,559
frameos/utils/status_screen.nim +24 17,947 17,923
frameos/types.nim +20 7,913 7,893
frameos/utils/font.nim +16 5,583 5,567
fos_client.c.obj +16 10,499 10,483
frameos/utils/image.nim -12 34,362 34,374
frameos/interpreter.nim -8 97,344 97,352
tlsf.c.obj +4 4,284 4,280
apps/data/immich/app.nim +4 15,995 15,991
Largest 30 objects
Object Subsystem This PR v2026.9.12 Δ
quickjs.c.obj QuickJS 283,817 283,817 ±0
assets/fonts.nim Embedded font 146,358 146,358 ±0
esp_efuse_utility.c.obj String pool (attributed to efuse) 131,659 131,559 +100 (+0.1%)
frameos/interpreter.nim FrameOS core (Nim) 97,344 97,352 -8 (-0.0%)
nim/lib/pure/collections/tables.nim Nim stdlib 70,556 70,556 ±0
frameos/js_runtime/app_runtime.nim FrameOS core (Nim) 61,505 61,502 +3 (+0.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,665 46,665 ±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,203 37,203 ±0
frameos/utils/image.nim FrameOS core (Nim) 34,362 34,374 -12 (-0.0%)
pkgs/pixie-6.1.0/pixie/paths.nim pixie 32,570 32,570 ±0
monocypher.c.obj Crypto (monocypher) 31,920 31,920 ±0
frameos/js_runtime/runtime.nim FrameOS core (Nim) 30,094 30,094 ±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,618 23,618 ±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,034 +168 (+0.8%)
apps/render/chart/app.nim FrameOS apps (Nim) 18,940 18,940 ±0
embedded_main.nim FrameOS core (Nim) 18,351 18,351 ±0
frameos/utils/status_screen.nim FrameOS core (Nim) 17,947 17,923 +24 (+0.1%)
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 17,935 -960 (-5.4%)
lib/tz.nim FrameOS core (Nim) 16,875 16,875 ±0
apps/data/immich/app.nim FrameOS apps (Nim) 15,995 15,991 +4 (+0.0%)
pkgs/pixie-6.1.0/pixie/images.nim pixie 15,787 15,787 ±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.

@mariusandra
mariusandra merged commit ad18ffe into main Sep 10, 2026
33 checks passed
@mariusandra
mariusandra deleted the review-todo-cloud-lows branch September 10, 2026 11:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant