Skip to content

Latest commit

 

History

History
547 lines (475 loc) · 33.4 KB

wasm.md

File metadata and controls

547 lines (475 loc) · 33.4 KB

WebAssembly in OctoSense

English | 简体中文

WebAssembly turns up in four places in OctoSense. One is designed to run an app's own code: an app's own functions, which standard builds run on macOS, Windows, Linux, Android and OpenHarmony, with limited support. The plug-ins of two engines, photocraft behind photo and vectorcraft behind vector, are closed: both services refuse every plug-in command. The other two are a makepad internal and a browser build that does not compile. For each, this page says whose module runs, what it can reach, which builds have it and how it was checked. This page describes the current source tree. Desktop 0.1.0-rc.2 and Home 0.1.0-beta.2 contain ADR 0011 core modules. ADR 0014 shared components and the declaration-only changes below passed final-source acceptance at 8b09e05d on macOS and OnePlus 6. Desktop RC5 at 83d356c2 (11 October 2026) is the first release in which a component's HTTPS request works (#471: RC4 panicked while choosing a TLS provider on the first wasi:http request) and the first with the native charts an app can require through charts.d3@1 (charts). On it, Repo Radar became the first component app installed from the official public catalog: its public-store acceptance passed 93 native checks covering live GitHub requests from the component, native charts and component-owned state across a restart. Desktop RC6 keeps all of this and adds sockets. Desktop RC4 at 9266b008 brought the component runtime; its archive evidence records the actual package, signing and acceptance boundaries. The isolated phone fixtures did not update installed Home. Source, test and release status are separate. See capabilities and execution boundaries.

Where Whose module Runtime Builds State
An app's own functions: the wasm service (ADR 0011) The admitted app's fns/*.wasm or its exact shared-component pins Wasmtime 49, compiled by Cranelift (crates/wasm-host); components get WASI 0.2 from wasmtime-wasi Every standard desktop and Home build on macOS, Windows, Linux, Android and OpenHarmony (feature wasm-functions; on OpenHarmony in Pulley, Wasmtime's interpreter); none for iOS Core modules shipped in desktop rc.2 and Home beta.2. Desktop RC4 includes components and shared components; archive/source evidence is recorded below. Isolated OnePlus 6 fixtures did not upgrade Home
Engine plug-ins: photocraft behind photo, vectorcraft behind vector (ADR 0013) None: both services refuse every plugin.* command (#398, #405) wasmi 2, an interpreter Linked into every build with App Hub Closed: no caller can install or run a plug-in
Splash's math compiler (makepad) Generated by makepad from Splash code makepad-stitch, an interpreter Linked into every build Not used: OctoSense links nothing that calls it
The shell in a browser The shell itself, built for wasm32-unknown-unknown The browser None Does not build

An app's own functions: the wasm service

ADR 0011 (Accepted, with limited support) lets an app ship Rust functions compiled to WebAssembly in its bundle, and the shell runs them for that app only. They are for light computing beside a script: parsing, ranking, scheduling, diffing, a format conversion. Splash's compute kernels stay the answer for numeric work.

The module

A module is core WebAssembly at fns/<name>.wasm, at most 8 per bundle, with no WASI. A file there may instead be a component, which has both.

  • It exports memory, octo_alloc(len) -> ptr and octo_free(ptr, len).
  • Every other export of type (i32, i32) -> i64 is a function: it takes its input at (ptr, len) and returns (out_ptr << 32) | out_len. The output starts with a status byte, 0 for success and 1 for an error, whose text follows.
  • Its only import may be octo.log(ptr, len). A module that imports anything else is refused when it loads.
  • It has one memory and at most one table.

The guest crate apps/wasmlab/guest/octosense-guest writes all of this: export! for fn(&[u8]) -> Result<Vec<u8>, String>, export_json! for serde types, and log. Build the crate as a cdylib for wasm32-unknown-unknown. Builds for wasm32-wasip1 and wasm-bindgen output import functions the shell does not provide, so they do not load; a build for wasm32-wasip2 is a component, the other kind it runs. App developers start from App Flow's Run your own Rust code.

Components

ADR 0014 (Accepted; desktop implementation released in RC4, with working component HTTPS since RC5) adds a second kind of fns/<name>.wasm: a WebAssembly component, built from an ordinary Rust crate with cargo build --target wasm32-wasip2. The service tells the two kinds apart by the file's preamble, and a bundle may carry both.

  • No glue. Every function the component exports is wasm.<function>, named in snake_case: save-html is wasm.save_html, and either spelling is accepted. Arguments are an object keyed by parameter name, an array in parameter order or, for one parameter, the value itself. A record comes back as an object, an enum as its name, list<u8> as base64 and a result<T, E> as its value or the request's error. wasm.functions lists every export with its WIT signature; a function that takes or returns a resource is listed as skipped, with the reason.
  • State. Each component's instance lives on in the app's worker between calls, so it can keep a parsed document or a cache. A trap or the deadline spends it and the next call gets a new one; an update, a changed grant, a withdrawal, the last owning app isolate closing, or the worker's exit (a minute without calls) ends it.
  • What it reaches. Clocks and random numbers. Its stdout and stderr become the app's log lines, and it gets no environment, arguments or stdin. A component that imports anything beyond wasi:cli, wasi:clocks, wasi:filesystem, wasi:http, wasi:io, wasi:random, wasi:sockets and octosense:host is refused when it loads.
  • Files. The app's available storage folder, the one its script's fs.* sees, is the component's /. Account-scoped apps need their active account. Nothing else on the device is visible. Missing storage or a signed-out account leaves no preopen; omitting the storage declaration does not.
  • The storage quota. A call may add what is left of the app's quota when the call starts. A write past it fails inside the component: ftruncate reports a full disk, and an ordinary write an I/O error, because wasi-libc reports any failed stream write as one. The request's error then ends with "a write was refused: the storage budget is used up". Rewriting, truncating and deleting give bytes back, so a component can free space.
  • The network. Its wasi:http requests and wasi:sockets TCP, UDP and DNS operations reach the network as the device can: an app's network declarations (net, network.hosts) are shown when it is installed and not enforced while it runs. The ruling of 8 October 2026 removed the per-app runtime gates; the OS and the host's API surface are the boundary. The pinned Splash runtime also no longer enforces the manifest host list. A component that imports wasi:http or wasi:sockets gets 10 s a call instead of 2 s. HTTP timeouts and blocking socket/stream/poll waits end with the call; cancellation is observed at most every 10 ms while waiting in the host. TCP bind/listen/accept and UDP bind/send/receive are supported. Privileged raw IP sockets (SOCK_RAW) are not part of this API. An app shares a 32-handle budget for live TCP/UDP sockets and DNS lookup streams across its components. Each component has at most 512 host resources, including streams and pollables. Closing a handle refunds its slot; a trap or cancellation immediately drops the instance's resources. A cancelled DNS waiter stops promptly. An OS resolver operation already running may finish in the background; it retains its budget slot until then. At most eight native resolvers run across the process, with no waiting queue.
  • Host services. Through octosense:host (crates/wasm-host/wit/octosense-host.wit), request(service, args) uses the admitted app's identity, dispatched on the UI thread. Declaration lists do not gate the call. Each service still checks its account, consent, review and resource boundaries. A component never gains a sheet, prompt or trusted user input, so only methods a background surface may call are available. wasm.* is refused, since the app's worker is busy with the call, and the call's deadline bounds the wait.

Socket compatibility: desktop RC6 includes TCP, UDP and DNS support on macOS, Windows and Linux. Desktop RC5 rejects socket imports. This desktop release does not update Android Home. Socket bundles must keep host-api-v1 and wasm-components-v1 in requires and declare host_api.required: {"wasm.sockets": 1}. The SDK adds this automatically when it detects socket imports, and App Hub verifies it for bundled and shared components. wasm.sockets is a runtime compatibility marker, not a callable JSON method or permission. Older hosts can still read the catalog and report this individual app as incompatible. Rust crates must still build for wasm32-wasip2; native TLS backends, process creation and threads do not become portable just because sockets work. Rust's WASI UDP set_read_timeout is unsupported; use nonblocking receive with a deadline, or rely on the host call deadline.

ADR 0014 records what this costs. On an M-series Mac, the 313 KiB test component compiles in about 40 ms and loads from the cache in about 4 ms; its Markdown function runs about 2.6 times slower than native Rust, close to the same code as a module. In Pulley it runs about 32 times slower than that.

Shared components

An app can also call a component that App Hub publishes on its own (App Hub ADR 0003, OctoSense ADR 0014 phase 4). The manifest pins it under components by alias, id, exact version and BLAKE3 digest, and requires wasm-shared-components-v1. The wasm service loads each pinned component beside the app's fns/ and routes its functions as wasm.<alias>.<function>: with {"as": "md", "id": "org.example.markdown", …}, wasm.md.to_html calls the component's to-html. The app gets an instance of its own, under its own account and storage boundaries, as for a component in its fns/. It may carry no fns/ at all and call only shared components. wasm.functions lists their functions with the alias, and names the component each comes from ("shared": {"alias", "id", "version", "blake3"}).

The service asks App Hub for the files on every load (octosense_appstore::components::resolved_in). A system app's are in its bundle at components/<blake3>.wasm. An installed app's are in the shared, read-only store at <apps root>/.components/<blake3>.wasm. Each is checked against the verified catalog, which must still offer it at that version, and against its digest. Nothing is fetched there: App Hub's installers fetch an app's components before the app itself. A missing, changed or withdrawn component stops the app's launch.

wasm_service::tests::a_pinned_shared_component_answers_as_alias_and_function is a focused test with a stand-in resolver. The separate real GitHub acceptance uses actual publisher attestations and an admin-attested private dry-run catalog, then installs two apps through the real Store and calls their shared components through Splash tools. Both macOS and OnePlus 6 passed 28/28 native assertions in the earlier c5f0c5c1 run; their drivers passed 12/12 and 13/13 checks respectively. At final source 8b09e05d, the full macOS desktop passed 11/11 App Hub/component rehearsal checks, and OnePlus 6 again passed 28/28 native assertions plus 13/13 driver checks. Final evidence keeps those runs separate. The public catalog and installed Home were unchanged. The Mac rehearsal used a development binary, not a released archive; neither run used a live model.

How a module reaches a device

  • A store app. App Hub's current gate (app contract 1.11) admits up to 8 .wasm files at fns/<name>.wasm (name [a-z0-9_-], at most 64 characters). It distinguishes core modules from components and checks component imports and required ABIs. It verifies exact shared-component versions, digests and publishing proofs. Omitting wasm, storage or net is not an admission refusal. Keep those declarations accurate for store disclosure; the runtime still validates code when it loads. The rules are in App Hub's publishing reference (the functions finding, required ABIs and wasm usage disclosure).
  • A system app. It is packed into the shell like any other, by digest, from the packaging's system-app list. desktop/system-apps-wasm-lab.json and phone/system-apps-wasm-lab.json are the standard lists plus wasmlab.

The service

The wasm host service (crates/shell/src/wasm_service.rs) is in every standard desktop and Home build on macOS, Windows, Linux, Android and OpenHarmony: the shell's wasm-functions feature, on by default in both packages (wasm-lab is its former name). iOS leaves the runtime out. A build without the service answers no service answers "wasm" on this device. It answers two kinds of request:

  • wasm.functions: the functions the app's modules export, how each module loaded (compiled, or from the cache, and how long it took) and how each function has run.
  • wasm.<function>: one call. For a module, a string argument goes in as its text and anything else as JSON; JSON output comes back as data, anything else as {"text": …}. A component's arguments and result are typed (Components). The function's own error, a trap or the deadline is the request's error.

The code always comes from the calling app's own admitted, digest-checked bundle, never from an argument, and no app reaches another app's functions. The service rechecks bundle admission and app/profile identity before loading or executing code. Capability declarations do not authorize execution.

Every call to a module gets a fresh instance: memory, globals, tables and log lines from one call never reach the next, including after a success or the function's own error. A component keeps one instance between calls instead (Components). Only compiled code is reused, in the app's worker and in a disk cache (wasm-cache in the host directory, keyed by the module's SHA-256 and the engine's compatibility hash). The service checks admission again before a call runs and before its answer is delivered. An update, a changed grant or a signed withdrawal discards the answer and the compiled code. Compiling cannot be interrupted, but an expired or cancelled request never goes on to run.

Limit Value
One call, start function included 2 s; 10 s with HTTP/socket imports (checked every 10 ms)
Linear memory 256 MiB
Table elements 16,384
Wasm stack 512 KiB
Live sockets and DNS lookup streams 32 per app, shared across components
Host resource table 512 entries per component
Native DNS work 8 resolvers per process; slots retained until native completion
Module 8 MiB
Input or output of one call 16 MiB in the runtime; 8 MiB of serialized input in the service
Log 64 lines a call, 1 KiB each
Modules per app 8
Apps running functions at once 4 workers, one per app
Queued requests 4 per app
Buffered input, all apps 32 MiB
One request, queueing and loading included 10 s
An idle worker exits after 5 s, or after 60 s while it holds a component's instance

A full queue or no free worker fails the request at once instead of making it wait.

When App Hub installs or updates an app, the shell compiles its functions into the cache in the background (wasm_service::warm): one app at a time, on a thread of its own, from the admitted bundle. When that work has finished and the cache is promptly readable, the app's first call uses the compiled code. Cache reads wait at most 50 ms, with at most two readers in flight per process. A slow or unavailable cache falls back to compiling the verified source without rewriting that cache entry. The request deadline remains 10 s; source compilation still takes time. See the runtime cache notes.

Agent tools

An app's tools.json may map a tool to one of its functions with "host_method": "wasm.<function>". The tool must be offered by its app; a wasm declaration is not an execution grant. Unlike shared host methods it has no minimum risk and needs no "private_data": true: a module sees only the arguments it is given, and a component also its own app's folder, as the app's script does. Neither reaches shared data. Wasm Lab maps wasmlab.find_slots, wasmlab.rank and wasmlab.diff to wasm.find_slots, wasm.fuzzy_rank and wasm.text_diff, all read.

Platforms

Platform State
macOS Runs. Validated in a hidden desktop shell (ADR 0011). Wasmtime catches traps with Mach exception ports, not signals.
Linux Runs. The runtime and service tests pass on x86_64, with traps caught by signals, and Wasm Lab ran in a headless desktop (8 October 2026).
Android Runs in Home's default build. Needs libc 0.2.190 or later, which crates/wasm-host requires: with 0.2.189 every trap ended the process. Validated on a Redmi Note 12 (ADR 0011) and by the phone acceptance on a OnePlus 6. The runtime adds about 7.5 MiB of code; compiling a 433 KiB module takes about 0.4 s on a Snapdragon 685, and loading it from the cache 5–11 ms.
Windows Included (ADR 0014). Windows CI at a8e170d4 passed 42/42 runtime tests and checked the shell with the service; see the RC4 source distinction. A desktop GUI run on Windows is unverified.
iOS Not included and not planned for now (ADR 0014). No iOS build with the service has been compiled.
OpenHarmony Included, in Pulley, Wasmtime's interpreter, until OpenHarmony's code-generation policy is known: Cranelift compiles to Pulley's bytecode and nothing runs as native code. Home's OpenHarmony release build compiled with the service (9 October 2026, cargo-makepad makepad ohos … deveco -p octosense-home --release); no device has run it (unverified). Pulley is about 32 times slower than Cranelift (ADR 0014).

ADR 0011 records earlier cache-open measurements on a managed Mac. The full desktop rehearsal also observed file-open stalls lasting about 10 s; its stack sample identifies the file-open wait, not its cause. The bounded cache reader prevents this optional cache wait from consuming the whole request deadline.

Wasm Lab

Wasm Lab (os.wasmlab, apps/wasmlab/) is the system app that shows the service. It is a demo: standard builds do not ship it. Its functions (apps/wasmlab/guest/functions) are Markdown to HTML (pulldown-cmark), free slots around busy times, fuzzy ranking (strsim), a line diff (similar), and rogue, which loops forever, allocates without end, panics or recurses without end on request. Its screen calls each one through host.request and shows what came back, the round trip and wasm.functions.

Build a desktop with it, then start it with Wasm Lab open, hidden and driven over the remote control surface (/help lists the routes; end with /quit):

OCTOSENSE_SYSTEM_APPS=$PWD/desktop/system-apps-wasm-lab.json cargo build --locked -p octosense
MAKEPAD_HIDE_WINDOWS=1 target/debug/octosense --remote=47631 --test-action launch-wasmlab

On the Linux build host (8 October 2026; a default debug build, hidden, under a headless Weston), the module compiled in 98 ms on first use and loaded from the cache in 4 ms after that, also after a restart. Each misbehaving mode ended in an error and the shell kept running: the endless loop at its deadline after 2,039 ms, endless allocation at the memory cap after 340 ms, and the panic and runaway recursion after 37 ms each, as the script timed them.

When it opens, Wasm Lab makes its four calls first and asks for wasm.functions once they have answered: the service queues at most four requests per app. wasm.functions counts calls since the app's worker started; a worker exits after five idle seconds, and the counts start again with the next one.

The checked-in module rebuilds byte for byte: with rustc 1.97.1, apps/wasmlab/guest/build.sh writes the same bundle/fns/wasmlab.wasm (SHA-256 f4a69c32…). It needs the target once: rustup target add wasm32-unknown-unknown.

Tests

cargo test --locked -p octosense-wasm-host
cd phone && cargo test --locked --features mobile-apps -p octosense-shell wasm_service::tests
  • crates/wasm-host/tests/runtime.rs runs a hand-written guest: input and output, guest errors, the deadline, the memory, table and stack caps, traps, the import rule, per-instance state, threads, the size limit, the cache and its concurrent writers, cancellation.
  • crates/wasm-host/tests/guest.rs runs Wasm Lab's module and compares every function with the same Rust run natively, then every rogue mode.
  • crates/wasm-host/tests/component.rs runs four components built from crates/wasm-host/tests/component-guest: an unmodified crate (pulldown-cmark) called with JSON, records and snake_case names, state kept on one instance, clocks, randomness and bytes, files only in the app folder (also read-only), the storage budget, the deadline and the memory cap; fetch, whose wasi:http reaches a local server without a manifest host allowlist, and whose request to a server that never answers ends at the deadline; hostcall, which calls host services through the embedder; and a component importing ordinary sockets.
  • crates/wasm-host/tests/component_sockets.rs runs real TCP/UDP echo, localhost DNS, listener/accept, shared socket limits, close/refund cycles, blocked-read cancellation and UDP deadlines. Unit tests also cover stalled stream destruction and abandoned native resolver work.
  • The service tests cover the tool mapping, bounded input, a fresh instance per call, revocation after an update, a grant change or a withdrawal, cancellation and the queue bounds, and the app's own functions answering its tools and its script. For a component they cover typed calls, its instance's state, the app's storage folder and its quota, an app without available storage area, a component shipped as a system app through App Hub's admission, network calls without a manifest allowlist, host calls under the app's identity and service boundaries, and an installed app's first call loading from the cache.
  • cargo run --release -p octosense-wasm-host --example measure_component prints a component's timings against a module and native Rust.
  • OCTOSENSE_WASM_PULLEY=1 cargo test --locked -p octosense-wasm-host --features pulley runs the same tests in Pulley, as OpenHarmony runs them; phone.yml runs it too.

The test components rebuild byte for byte: with rustc 1.97.1, crates/wasm-host/tests/component-guest/build.sh writes the same notes.component.wasm (SHA-256 963c965b…), netprobe.component.wasm (faf1cb5d…), fetch.component.wasm (6eaf6ac8…) and hostcall.component.wasm (d39f2a8e…). It needs the target once: rustup target add wasm32-wasip2.

phone.yml runs the first command, and the service's tests as part of Home's (mobile-apps includes wasm-functions); tools/ci-local.sh --linux-host --offload runs both on the Linux build host. Every CI build of the desktop and Home includes the service, and tools/check-shell-graph.sh checks that its runtime is linked exactly on macOS, Linux and Android.

Calling a function from the command line

crates/wasm-host/examples/wasm_call.rs calls one function of a .wasm file with the same runtime and limits as the wasm service, with no shell and no device. Run from this repository:

cargo run -q -p octosense-wasm-host --example wasm_call -- \
    crates/wasm-host/tests/fixtures/notes.component.wasm to-html '"# Hi"'

It prints "<h1>Hi</h1>\n". For a component, the arguments are the JSON a script passes to wasm.<function>: a bare value for one parameter, an object by name, or an array in order. The answer prints as JSON, and a record prints as an object. For a core module, the arguments are the function's input as text: wasm_call apps/wasmlab/bundle/fns/wasmlab.wasm md_to_html '# Hello *core*' prints <h1>Hello <em>core</em></h1>.

--storage DIR gives a component DIR as its app's storage folder. Its wasi:http requests go out as in the shell. Its octosense:host calls fail with no host services in wasm_call: <service> needs a shell. Each call runs in a fresh instance, where the shell keeps a component's instance between calls.

Phone acceptance

tools/fixtures/wasm-phone-lab holds a synthetic signed store app, org.octosense.samples.wasmprobe, whose module (state.wasm, from state.wat) remembers its last input if its instance is ever reused. crates/wasm-phone-smoke packages it in a test APK that installs it through the normal signed store and calls it through a real script tool, and tools/test-wasm-phone.py drives that on an assigned device. Its receipt records 22 of 22 checks passed on a OnePlus 6 with Android 15, including a fresh instance after a success, a guest error, a trap and the deadline. Its source, e67ce63e, records the service and runtime tested at that time. A live model, performance and a release APK were not part of it. state.wasm rebuilds byte for byte with the fixture README's encode_phone_fixture command.

Not done yet

  • A Home release with ADR 0014 components and declaration-only behavior. Desktop RC4 includes them; the isolated phone acceptance did not upgrade Home beta.2, which ships core modules only.
  • CPU fairness between apps, and a bound on the disk cache, which nothing evicts.
  • Compiling system apps' functions ahead: an installed app's are compiled at install, a system app's at its first call.
  • OpenHarmony component execution on a device (unverified); Android shared-component execution passed on the OnePlus 6.
  • Shared-component publication in the official public catalog. The private, genuinely attested dry-run catalog passed the development-host and final Mac RC4 archive flows; its synthetic entries were not added to the public catalog.
  • Bytes are slow through JSON: a 1 MiB list<u8> costs about 18 ms there and back, as base64.

Engine plug-ins: the photo and vector services

Two engines of ADR 0013 host their own WebAssembly plug-ins, in wasmi, a pure-Rust interpreter, each in a registry that belongs to the process, not to a call:

  • photocraft, the raster engine behind the photo service (apps/photo/host-service): filter plug-ins with no imports, 32 MiB per module, 512 MiB of memory per instance, 50 million instructions per call and 60 s per run.
  • vectorcraft, the vector engine behind the vector service (apps/vector/host-service): object filters and live effects with no imports, a fuel budget, a memory cap and a fresh instance per run.

effectcraft, behind effect, has WebAssembly plug-ins too, behind an optional feature; OctoSense does not link its plug-in crate. vectorcraft's desktop UI crate has a second install command, ui.installPlugin; OctoSense does not link that crate either.

Closed since #398. photo.run refuses every plugin.* command before the engine sees it, and photo.commands leaves them out. Before #398 a caller could install a plug-in from base64 data; it went into photocraft's process-wide registry, outlived the call and served every later caller, under limits far above the wasm service's. A scratch program that calls the service as a script's host.request does showed both, on the Linux build host (8 October 2026). Before #398, os.photos installed photocraft's example plug-in, the next call listed it, and another system app, os.notes, ran it on a document. With #398, each of those calls was refused. Since the command doors (#418, ADR 0013) the refusal is the allowlist gate's, which classes every plugin.* id code: photo.run: `plugin.install` is classed code: it installs or runs code, or runs other commands, so the door never runs it.

Closed in vector by #405. vector.run refused files, documents, the app.* group and path-like arguments, but not plugin.*, and vectorcraft's engine installs a plug-in from base64 dataBase64. The same scratch program showed it on main before #405: os.notes installed vectorcraft's example plug-in, the next call listed it, another system app (os.maps) saw it, and vector.commands offered plugin.install. Now vector.run refuses every plugin.* id before the engine sees it (since #418 by the allowlist gate: vector.run: `plugin.install` is classed code: it installs or runs code, or runs other commands, so the door never runs it), and vector.commands leaves them out. An effect plug-in named to a built-in effect command (effect.apply {effect: "plugin.<id>"}) is refused too.

Both engines still link wasmi, and both services answer system apps (os.*) only.

The UI thread. The photo service, like the other engine services, does its work inside the request. App Hub dispatches a script's host.request on the UI thread, and calls the service while it holds the lock on the service registry (services::dispatch). So a long photo.run holds up the UI and every other app's requests until it returns (#399).

Splash math and compute kernels

makepad's script engine can compile a pure-math Splash function, such as a signed distance field, into a WebAssembly module it generates itself, and run it in makepad-stitch, its own interpreter, as the reference that matches the Splash interpreter bit for bit (.sources/makepad/platform/script/src/math_aot/). No app supplies that module. In OctoSense only makepad's CSG library would call it, and OctoSense does not link that library, so the code is present and unused. ADR 0011 measured stitch for apps' functions (2,942 CoreMark against Wasmtime's 44,534) and did not choose it.

makepad's compute kernels also have a WebAssembly backend, but it runs only when makepad itself is compiled for wasm32 (a browser). Native builds use their native backends.

The shell in a browser

The shell keeps the wasm32 paths of makepad's window manager, its "web superbuild": no child processes (crates/shell/src/host.rs), every native app hosted in process (tools/native_apps.py gives each app a wasm hosting of module or none in crates/shell/src/native_apps.rs, and refuses process there), and in-process service links for the assistant (crates/shell/src/pane_links.rs). App Hub reports such a host's platform as web.

No build, CI job or release produces it, and it does not compile. This check on the browser target fails:

cargo check --locked --keep-going --target wasm32-unknown-unknown -p octosense-shell --no-default-features

getrandom 0.2 and 0.3, uuid, fs2, ring and aws-lc-sys fail to build for that target. Even without default features the shell links octosense-ai-host and App Hub's crates, which bring in most of them (credential encryption, sigstore verification, TLS, file locking); the shell's own uuid lacks a randomness source there. Treat these paths as unmaintained.

See also

  • ADR 0011: the decision, the runtimes measured and the phone measurements.
  • ADR 0012: host API discovery adds no WebAssembly loading.
  • ADR 0013: the engines behind photo and the other craft services.
  • App Hub's publishing reference: wasm usage disclosure, required ABIs, the functions finding and wasm.<function> tools. Current 1.11 policy does not require a matching capability-family declaration to admit or execute a public call.