[nu-demo] hero_os WASM shell is 211 MB debug — needs release + wasm-opt + brotli/gzip sidecars #140

Closed
opened 2026-04-23 23:17:02 +00:00 by mik-tf · 1 comment
Owner

Symptom

First-load of the Hero OS shell on a fresh browser is 2-3 minutes on a home connection — the WASM blob is 211 MB uncompressed over the wire. Live demo impact: noticeable pause before anything renders.

Root cause

Current make build-wasm target produces a debug-profile WASM with no wasm-opt and no compression. Axum's ServeDir in hero_os_ui serves the raw .wasm with no Content-Encoding: br/gzip sidecar negotiation.

Demo workaround (applied 2026-04-23)

None applied at runtime on the demo VM. A compression-analysis agent produced a ready-to-apply patch set (sitting in the repo-local workspace), but we did not apply it during the nu-demo bring-up.

Proper fix

Three changes, combined drop 211 MB -> ~5-8 MB first-load (~40x reduction):
(a) Build WASM via dx build --web --release so Dioxus.toml's wasm-opt -Oz pass runs. Expect ~20 MB uncompressed.
(b) Add a Makefile post-build step that produces sidecars next to the .wasm:

brotli -q 11 -k <file>.wasm    # → <file>.wasm.br
gzip -9 -k <file>.wasm         # → <file>.wasm.gz

(c) In hero_os_ui, configure Axum ServeDir.precompressed_br().precompressed_gzip() so it serves the sidecar on matching Accept-Encoding.

This also establishes the pattern for every other _ui service in the Hero stack — they should all adopt the same sidecar + precompressed-serving recipe.

Filed 2026-04-23 nu-shell demo bring-up. Signed-off-by: mik-tf

## Symptom First-load of the Hero OS shell on a fresh browser is 2-3 minutes on a home connection — the WASM blob is 211 MB uncompressed over the wire. Live demo impact: noticeable pause before anything renders. ## Root cause Current `make build-wasm` target produces a debug-profile WASM with no `wasm-opt` and no compression. Axum's `ServeDir` in `hero_os_ui` serves the raw `.wasm` with no `Content-Encoding: br`/`gzip` sidecar negotiation. ## Demo workaround (applied 2026-04-23) None applied at runtime on the demo VM. A compression-analysis agent produced a ready-to-apply patch set (sitting in the repo-local workspace), but we did not apply it during the nu-demo bring-up. ## Proper fix Three changes, combined drop 211 MB -> ~5-8 MB first-load (~40x reduction): (a) Build WASM via `dx build --web --release` so Dioxus.toml's wasm-opt `-Oz` pass runs. Expect ~20 MB uncompressed. (b) Add a Makefile post-build step that produces sidecars next to the `.wasm`: ``` brotli -q 11 -k <file>.wasm # → <file>.wasm.br gzip -9 -k <file>.wasm # → <file>.wasm.gz ``` (c) In `hero_os_ui`, configure Axum `ServeDir.precompressed_br().precompressed_gzip()` so it serves the sidecar on matching `Accept-Encoding`. This also establishes the pattern for every other `_ui` service in the Hero stack — they should all adopt the same sidecar + precompressed-serving recipe. Filed 2026-04-23 nu-shell demo bring-up. Signed-off-by: mik-tf
Author
Owner

Compression landed + verified live on herodemo

hero_os c27d9f6: adds a compress-assets Makefile target and wires install-assets-release to call it automatically. Builds .br + .gz sidecars for every .wasm / .js / .html / .css / .json / .svg over 1 KiB.

Before / after on herodemo

Asset Raw Brotli (q11) Gzip (q9)
hero_os_app_bg.wasm (live, today) 9.06 MB 1.75 MB 2.73 MB
hero_os_app_bg.wasm (debug, broken) 215.5 MB
index.html 2.6 KB 0.83 KB 1.07 KB
livekit.js 6.4 KB 1.52 KB 1.86 KB

The 215 MB debug bundle was a misstep earlier in the session (make build-wasm builds debug). Release mode (make build) drops the WASM to 9 MB before compression; brotli q11 sidecar drops the wire payload to 1.75 MB — matches the historical good-state target.

Server-side wiring

hero_os_uis static handler already negotiates Accept-Encoding. Verified on herodemo via internal hero_router (port 9990, bypassing nginx auth):

$ curl -I -H Accept-Encoding: br,gzip http://10.1.2.2:9990/hero_os/ui/assets/hero_os_app_bg-<hash>.wasm
HTTP/1.1 200 OK
content-type: application/wasm
content-encoding: br
cache-control: public, max-age=31536000, immutable
accept-ranges: bytes

Why it wont regress

  • install-assets-release now invokes compress-assets automatically — operators cant accidentally skip it.
  • compress-assets is idempotent and tolerates missing tools (warns about brotli/gzip being absent rather than failing the build).
  • Brotli q11 is build-time cost amortised across every page load forever.

Closing.

Signed-off-by: mik-tf

## Compression landed + verified live on herodemo hero_os [`c27d9f6`](https://forge.ourworld.tf/lhumina_code/hero_os/commit/c27d9f6): adds a `compress-assets` Makefile target and wires `install-assets-release` to call it automatically. Builds .br + .gz sidecars for every .wasm / .js / .html / .css / .json / .svg over 1 KiB. ## Before / after on herodemo | Asset | Raw | Brotli (q11) | Gzip (q9) | |---|---|---|---| | `hero_os_app_bg.wasm` (live, today) | 9.06 MB | **1.75 MB** | 2.73 MB | | `hero_os_app_bg.wasm` (debug, broken) | 215.5 MB | — | — | | `index.html` | 2.6 KB | 0.83 KB | 1.07 KB | | `livekit.js` | 6.4 KB | 1.52 KB | 1.86 KB | The 215 MB debug bundle was a misstep earlier in the session (`make build-wasm` builds debug). Release mode (`make build`) drops the WASM to 9 MB before compression; brotli q11 sidecar drops the wire payload to **1.75 MB** — matches the historical good-state target. ## Server-side wiring hero_os_uis static handler already negotiates `Accept-Encoding`. Verified on herodemo via internal hero_router (port 9990, bypassing nginx auth): ``` $ curl -I -H Accept-Encoding: br,gzip http://10.1.2.2:9990/hero_os/ui/assets/hero_os_app_bg-<hash>.wasm HTTP/1.1 200 OK content-type: application/wasm content-encoding: br cache-control: public, max-age=31536000, immutable accept-ranges: bytes ``` ## Why it wont regress * `install-assets-release` now invokes `compress-assets` automatically — operators cant accidentally skip it. * `compress-assets` is idempotent and tolerates missing tools (warns about brotli/gzip being absent rather than failing the build). * Brotli q11 is build-time cost amortised across every page load forever. Closing. Signed-off-by: mik-tf
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/home#140
No description provided.