Runtime
Architecture and Implementation Blueprint for a Portable WebAssembly Transformer Runtime
Report summary
The optimal architecture for a portable, deterministic CPU runtime designed to execute compact transformer models in the browser is a zero-dependency, statically compiled WebAssembly (WASM) module generated from a pure Rust execution core. The primary directive of the TinyRustLM ecosystem is to perf
Key topics
- Runtime
- AI
- TypeScript
- Rust
- Privacy
- Semantic Systems
- Research Archive
- Strategy
Research provenance
For citation, use the report title and canonical URL. Archival presence does not establish authorship or promote report statements into portfolio evidence.
This page renders the archived Markdown as safe, formatted HTML. It is background research and does not become a portfolio claim without evidence review.
Full report
On this page
1. Executive Runtime Architecture and Assumptions
The optimal architecture for a portable, deterministic CPU runtime designed to execute compact transformer models in the browser is a zero-dependency, statically compiled WebAssembly (WASM) module generated from a pure Rust execution core. The primary directive of the TinyRustLM ecosystem is to perform local conversational inference strictly on the client device. This operational model mathematically guarantees privacy, as neither prompts nor model weights are ever transmitted to external servers. The architectural philosophy is anchored by a single, normative scalar oracle implemented in standard Rust. The scalar implementation serves not merely as a fallback, but as the absolute numerical reference that defines the behavioral contract for all accelerated execution paths. Any target acceleration, whether it utilizes Single Instruction, Multiple Data (SIMD) paradigms or multi-threading, must share well-defined behavior with this native Rust scalar core. Acceleration paths that alter the mathematical semantics of the raw conversation beyond meticulously declared numerical tolerances must be categorically classified as failed paths. Optimization must preserve model semantics perfectly. The target execution environments span native Windows and Linux architectures, alongside browser-based WebAssembly contexts. The transformer models admitted into this runtime are restricted to dense, decoder-only architectures. These architectures incorporate standard modern components including Root Mean Square Normalization (RMSNorm), Rotary Position Encoding (RoPE), Grouped-Query Attention (GQA) or Multi-Head Attention (MHA), gated Feed-Forward Networks (FFNs), tied or untied embeddings, and quantized linear layers supporting multiple context lengths. To satisfy strict size limits, auditability requirements, and security constraints, the runtime and packer must prefer zero third-party Rust crates. Browser glue logic must remain minimal, strictly typed, and testable. TinyRustLM is currently in a pre-publication phase, demanding a single clean runtime architecture where weaker, unpublished experimental paths are aggressively removed post-verification to prevent technical debt and semantic divergence.
2. Standards-Status and Browser-Capability Matrix
The WebAssembly specification underwent a significant evolution culminating in the WebAssembly 3.0 standard, which was formally recognized and widely integrated into mainstream browser engines by late 2025 and early 20261. However, deploying high-performance computing workloads to a diverse browser ecosystem requires uncompromising capability negotiation. Runtime selection must be truthful; cross-compiling a module simply proves that the byte stream was built, not that a specific browser or physical target successfully executed those accelerated instructions. The following capability taxonomy dictates the negotiation logic for baseline features and advanced proposals:
| Feature | Specification Status | Browser Availability (2026) | Detection Method | Build Requirement | Security / Header Req. | Deterministic Fallback |
|---|---|---|---|---|---|---|
| Baseline WASM | W3C Core 1.0 / 2.0 | Universal | WebAssembly.validate on MVP bytes | wasm32-unknown-unknown | None | N/A (Normative Baseline) |
| SIMD128 | W3C Core 2.0 | Universal | Compile-time JS validate | \-C target-feature=+simd128 | None | Scalar execution |
| Relaxed SIMD | W3C Core 3.0 | Chrome, Firefox (Safari partial) | validate with relaxed opcodes | \-C target-feature=+relaxed-simd | None | Deterministic SIMD128 |
| Threads & Atomics | W3C Core 2.0 | Universal (if headers present) | typeof SharedArrayBuffer \!== 'undefined' | \-C target-feature=+atomics | COEP: credentialless, COOP: same-origin | Single-threaded execution |
| Bulk Memory | W3C Core 2.0 | Universal | Default in modern rustc | \-C target-feature=+bulk-memory | None | Loop-based memory copy |
| Memory64 | W3C Core 3.0 | Chrome, Firefox, Safari (Recent) | validate with address: "i64" | wasm64-unknown-unknown | None | WASM32 (4GB hard limit) |
| Exception Handling | W3C Core 3.0 | Wide adoption (exnref) | validate with exnref | \-C panic=unwind | None | \-C panic=abort |
| Tail Calls | W3C Core 3.0 | Universal | validate with return\_call | \-C target-feature=+tail-call | None | Standard loop/call stack |
| WebGPU Interop | W3C Draft | Chrome, Edge (Safari emerging) | navigator.gpu | N/A (Host managed) | Secure Context (HTTPS) | Pure CPU execution |
The WebAssembly Relaxed SIMD proposal introduces inherently non-deterministic instructions, such as fused multiply-add operations, where the exact rounding behavior or NaN propagation depends heavily on the underlying host hardware (e.g., discrepancies between x86\_64 and ARM64 silicon)3. Because conversational LLMs demand strict reproducibility to prevent divergent generation paths, the runtime must mandate deterministic lowering. When running outside the browser (e.g., via Wasmtime), the relaxed\_simd\_deterministic flag must be explicitly enforced3. Within the browser, if deterministic guarantees cannot be strictly verified for relaxed instructions, the runtime must gracefully fall back to the standardized SIMD128 pathways to preserve numerical fidelity. Furthermore, browser security models strictly dictate that SharedArrayBuffer—the foundational requirement for WebAssembly threads—is entirely inaccessible unless the hosting environment guarantees cross-origin isolation8. The server delivering the application must explicitly provide the Cross-Origin-Embedder-Policy: credentialless (or require-corp) and Cross-Origin-Opener-Policy: same-origin HTTP headers10. Feature detection must actively verify self.crossOriginIsolated. The runtime must never claim multi-threaded capabilities if this isolation is absent, seamlessly dropping down to the single-threaded baseline without generating errors.
3. Module Packaging and Feature-Selection Decision
The deployment and packaging mechanism for the WebAssembly binaries dramatically impacts network load times, browser caching efficiency, and the efficacy of Dead Code Elimination (DCE). Evaluating the design space requires comparing a universal module with runtime dispatch, componentized modules, and multiple feature-specific modules. A universal module bundles all execution paths—scalar, SIMD, and multi-threaded—into a single large binary, relying on dynamic runtime dispatch to select the appropriate kernel. While this simplifies deployment, it severely inflates the download size and disrupts browser caching. Furthermore, compilers like rustc and the LLVM backend struggle to effectively inline operations across CPU-feature branches in WebAssembly, leading to suboptimal register allocation and retained dead code. Componentized modules, utilizing the evolving WebAssembly Component Model (WASI Preview 2 and 3), offer sophisticated polyglot composition1. However, for a pure browser-based execution environment aimed at zero-dependency minimal footprints, componentization introduces unnecessary abstraction layers and polyfill overhead. The optimal strategy is the deployment of multiple feature-specific modules. The host application serves a lightweight JavaScript preflight script that actively probes the browser's capabilities by passing minimal byte arrays to WebAssembly.validate. Based on the probe results (e.g., the presence of SharedArrayBuffer and successful validation of SIMD128 opcodes), the preflight logic conditionally fetches exactly one highly optimized binary: core\_scalar.wasm, core\_simd.wasm, or core\_simd\_threads.wasm. This approach guarantees that if a specific feature path is executed, it has been mathematically verified and stripped of all irrelevant dead code. It mitigates the critical security and analytical risk of claiming a hardware feature path that did not actually execute, as the loaded binary literally does not contain the fallback paths. The test matrix is simplified because each binary represents a single, verifiable execution state.
4. Typed Host/WASM API and Ownership Contract
To eliminate serialization overhead and minimize JavaScript glue code, the boundary between the JavaScript host and the WebAssembly module must be strictly defined via flat memory pointers and exact lengths. The WebAssembly module exclusively owns and manages its linear memory. JavaScript must never arbitrarily allocate or mutate memory directly within the WASM heap; it must request an allocation, write the required data, and subsequently transfer ownership back to the WASM runtime. The following typed functions define the clean module boundary:
| Function Signature | Description | Result Code / Ownership Contract |
|---|---|---|
| preflight\_check() \-\> i32 | Validates internal state before execution. | Returns 0 for success. |
| alloc(len: i32) \-\> i32 | Allocates a contiguous buffer of len bytes. | Returns absolute memory offset. Rust retains internal allocator state. |
| free(ptr: i32, len: i32) | Frees a previously allocated buffer. | Host relinquishes pointer. Pointer becomes strictly invalid. |
| streaming\_hash(ptr: i32, len: i32) | Accumulates bytes into a running SHA/BLAKE hash. | Returns void. Used for model validation during streaming instantiation. |
| validate\_model() \-\> i32 | Verifies the fully loaded model against expected hash. | Returns status code (0 \= OK, 1 \= Corrupt). |
| init\_session(model\_id: i32) \-\> i32 | Allocates KV cache and conversational state. | Returns a versioned session\_id. |
| tokenize(ptr: i32, len: i32) \-\> i64 | Converts a UTF-8 string to a token sequence. | Returns packed i64: (token\_ptr \<\< 32\) |
| prefill(sess: i32, tok\_ptr: i32, len: i32) \-\> i32 | Ingests a prompt and computes initial logits. | Returns status code. Consumes tokens. |
| decode(sess: i32) \-\> i64 | Generates a single token autoregressively. | Returns packed i64: (token\_id \<\< 32\) |
| cancel(sess: i32) | Flags an asynchronous abort for long-running tasks. | Modifies shared atomic state. Host retains session until freed. |
| get\_error(sess: i32) \-\> i64 | Retrieves diagnostic error codes and messages. | Returns packed i64 pointing to error string. |
To prevent the catastrophic failure mode of stale handles, the session\_id must incorporate a generation counter. If the JavaScript host attempts to invoke decode on a session that has already been deallocated, the generation mismatch safely traps the execution, returning an explicit ERR\_STALE\_HANDLE code rather than corrupting memory or inducing undefined behavior.
5. Memory, Allocator, and Resource Formulas
WebAssembly's 32-bit linear memory space theoretically supports up to 4 GB (65,536 pages of 64 KiB)12. The Memory64 proposal extends this to a 64-bit address space, allowing massive allocations2. However, relying universally on Memory64 is prohibited, and practical browser constraints on WASM32 memory are significantly tighter than the theoretical limits. Mobile browsers, particularly iOS Safari (WebKit), strictly constrain worker memory footprints, frequently issuing silent Out-Of-Memory (OOM) kills when a module attempts to grow beyond \~384 MB, or when utilizing shared: true without meticulously pre-allocated limits14. The architecture must eschew one unmanaged contiguous arena in favor of highly deterministic, typed sub-arenas managed by a custom static bump-allocator written in Rust. Because standard malloc implementations induce fragmentation, and relying on dynamic memory.grow during inference guarantees latency spikes and platform-specific OOM crashes, all memory required for the model weights, KV cache, and activations must be reserved precisely during load\_model and init\_session. The total peak memory consumption is formulated as: [Figure omitted from source export]
1. Static Weights ([Figure omitted from source export]): For a symmetrically quantized Q4\_0 model, each block of 32 weights occupies 18 bytes (16 bytes payload \+ 2 bytes FP16 scale)17. A 1.5 billion parameter model requires [Figure omitted from source export]. This fits within desktop WASM limits but exceeds iOS Safari's threshold, mandating smaller model variants (\< 500M parameters) for broad mobile compatibility.
2. Dequantization Metadata ([Figure omitted from source export]): Stored inline within the blocks, contributing to the 4.5 bits-per-weight metric.
3. KV Cache ([Figure omitted from source export]): The persistent state for autoregressive decoding.
[Figure omitted from source export]
Where [Figure omitted from source export] is the number of layers, [Figure omitted from source export] is the number of KV heads, [Figure omitted from source export] is the head dimension, [Figure omitted from source export] is the maximum context length, and [Figure omitted from source export] is the bytes per element (2 bytes for FP16). For a configuration of 24 layers, 8 heads, 64 dimension, 2048 context, and FP16 precision, [Figure omitted from source export].
4. Activations ([Figure omitted from source export]): Memory for intermediate tensors. The peak is achieved during the prefill phase, scaling as [Figure omitted from source export] (assuming FP32 intermediate precision).
5. Worker Stacks and Thread-Local Scratch ([Figure omitted from source export]): When threading is enabled, each Web Worker necessitates its own isolated stack space (defaulting to 1 MB in wasm-ld via \_\_heap\_base and \_\_data\_end offsets)19. Furthermore, local scratch pads are required to accumulate partial dot products to prevent severe false sharing across cache lines.
To guarantee stability, the Rust linker flags must set \-Clink-arg=--max-memory=... to define a hard upper bound. The JavaScript instantiation sequence must explicitly pass { initial: calculated\_pages, maximum: calculated\_pages }7 to the WebAssembly.Memory constructor to ensure the browser allocates the exact physical backing store required up front, bypassing incremental growth failures entirely. JavaScript views (e.g., Float32Array) must be carefully managed; if memory were to grow, all existing views would be instantly invalidated, but our static allocation strategy completely eliminates this class of bugs.
6. Scalar Transformer Reference Algorithms
The scalar reference implementation forms the bedrock of the numerical conformance strategy. It is the absolute mathematical ground truth against which all optimized kernels are evaluated. The transformer execution graph dictates a strict progression: embedding lookup, RMSNorm, Q/K/V projections, Rotary Position Encoding (RoPE), Grouped-Query Attention (GQA), stable softmax, output projection, gated FFN, residual additions, final layer norm, and logits generation, terminating in tied or untied output embeddings. Every operator requires a precise baseline algorithm:
[Figure omitted from source export] The accumulator for the sum of squares must be executed strictly left-to-right. Using checked integer operations for index bounds ensures out-of-bounds reads trap deterministically rather than silently reading adjacent memory.
- Embedding Lookup & Q/K/V Projections: Standard matrix multiplications where accumulators must strictly utilize IEEE-754 f32 precision. Computations must proceed in a deterministic loop order (row-major traversal) to guarantee exact floating-point rounding parity, as floating-point addition is not strictly associative.
- RMSNorm:
- Rotary Position Encoding (RoPE): The scalar algorithm must explicitly define the dimension split geometry (e.g., interleaved odds/evens vs. sequential halves) and apply the complex exponential rotations using standard f32 trigonometric functions (or precomputed sinusoidal caches) without hardware-specific fast-math approximations.
- Stable Softmax: The raw attention scores ([Figure omitted from source export]) frequently produce large positive values. The scalar reference must compute stable softmax as [Figure omitted from source export]. The maximum subtraction perfectly bounds the input to [Figure omitted from source export], entirely preventing f32 infinity overflows that propagate NaN cascades.
- Quantized Tail Handling: Matrix dimensions are not perpetually clean multiples of quantization block sizes (e.g., an odd hidden dimension parsed against a 32-element Q4\_0 block). The scalar reference explicitly defines tail handling by iterating over the unaligned remainder element-by-element, ensuring bounds are never violated and padding is mathematically zeroed.
Empty tensor cases and one-element edge cases must gracefully bypass operations or yield identity matrices to provide reference outputs suitable for generating continuous Golden Vectors in testing environments.
7. SIMD Kernel Designs and Tail Invariants
WebAssembly 128-bit SIMD (v128) provides instructions uniquely suited for accelerating quantized inference. Symmetrically quantized blocks, specifically Q8\_0 and Q4\_0, must be unpacked, scaled, and accumulated maximally within the 128-bit registers17. A Q4\_0 block encapsulates 32 weights compressed into 16 bytes (4 bits per weight), augmented by a single 2-byte FP16 scaling factor. This architectural layout yields 18 bytes per block22. The SIMD128 dot-product decomposition for a Q4\_0 weight vector and an FP32 activation vector is designed as follows:
1. Unpacking and Scale Broadcast: Load the 2-byte FP16 scale, promote it to FP32, and broadcast it horizontally across an f32x4 register.
2. Nibble Extraction: Load 16 bytes of the Q4\_0 weights (containing 32 independent values). Utilize i8x16.bitmask and bitwise shift operations to extract the high and low 4-bit nibbles into separate vectors23.
3. Re-centering: Subtract the integer constant 8 from the extracted values to shift the unsigned domain \[0, 15\] back to the symmetric signed domain \[-8, 7\].
4. Dot-Product Decomposition: Leverage the specialized dot\_i16x8\_s instruction, which efficiently multiplies corresponding 16-bit lanes and adds adjacent pairs together, widening the result into 32-bit accumulators24.
5. Floating-Point Scaling: Convert the accumulated 32-bit integers to FP32 and multiply by the broadcasted FP32 scale.
6. Horizontal Reduction: Execute horizontal additions across the f32x4 vectors to collapse the SIMD lanes into a single scalar sum.
Tail Invariants: SIMD loops operate optimally in strides of 128 bits. When matrix dimensions misalign with these boundaries, the SIMD kernel must halt at the largest divisible boundary. The remaining elements—the tail—are strictly delegated to the exact scalar algorithm defined in Section 6\. The scalar accumulated result is ultimately added to the SIMD horizontal reduction. This hybrid approach guarantees bounds safety and absolute scalar equivalence without requiring complex masked loads or zero-padding overhead.
8. Thread-Pool, Scheduling, Prefill, and Decode Design
WebAssembly threading requires meticulous orchestration. The runtime utilizes SharedArrayBuffer as the foundational shared memory primitive, which in turn mandates strict cross-origin isolation8. During the initial preflight phase, the JavaScript host spawns a fixed pool of Web Workers, typically matching navigator.hardwareConcurrency \- 1\. Each worker initializes by instantiating the exact same core\_simd\_threads.wasm module, bound to a single, globally shared WebAssembly.Memory instance. Thread-pool lifetime is continuous; workers remain active throughout the session to avoid the catastrophic latency of repeatedly spawning and destroying threads. Scheduling distinguishes strictly between the Prefill and Decode phases due to their distinct bottlenecks:
- Prefill (Compute-Bound): Ingesting the prompt entails processing input shapes of [Figure omitted from source export]. The primary operations are massive matrix-matrix multiplications. The runtime schedules this by partitioning the work along the layer and row dimensions (e.g., distributing specific attention heads or FFN rows to different workers). Cache-aware tiling minimizes register spilling.
- Decode (Memory-Bound): Autoregressive generation involves input shapes of [Figure omitted from source export]. The operations collapse to matrix-vector multiplications, where the architectural bottleneck is strictly memory bandwidth. The scheduler partitions the massive weight matrices by rows. Each worker computes a partial dot product over its assigned rows, followed by a highly deterministic atomic reduction across the thread pool.
Synchronization is managed entirely within Rust utilizing lightweight spin-locks and atomic condition variables (core::arch::wasm32::memory\_atomic\_wait32). To eliminate false sharing—where independent threads invalidate each other's CPU cache lines—all thread-local scratch buffers and atomic counters are strictly padded to 64-byte alignments. For long-running prefill operations spanning thousands of tokens, the scheduler implements interruption points. Processing is batched (e.g., chunks of 128 tokens) and yields control back to the event loop. This prevents the browser's "Page Unresponsive" watchdog from triggering and keeps the UI fluid, while checking a shared cancellation atomic flag to cleanly abort if the user clears the chat.
9. Cache Identity and Deterministic Sampling Contract
Autoregressive caching and generative sampling are the final determinants of the model's output quality. The runtime boundary establishes an exact-prefix cache interaction model. Reusable state (KV cache) is strictly bound to a composite identity comprising the model hash, active adapters, tokenizer version, chat template configuration, prompt tokens, positional policy, and runtime numerical mode. Any perturbation to this identity triggers an immediate invalidation of the associated cache blocks. Deterministic sampling is a non-negotiable requirement. The runtime cannot rely on the JavaScript Math.random() function, nor any external entropy sources, as they destroy generative reproducibility. The runtime integrates a PCG32 (Permuted Congruential Generator) implemented entirely in Rust27. PCG32 is selected for its minimal 64-bit state, zero external dependencies, exceptional statistical quality, and guaranteed stream reproducibility29. The PRNG state is serialized alongside the session, allowing conversations to be paused and resumed with bit-exact continuation. The sampling pipeline strictly manages logits in f32 precision. When the configuration dictates a Temperature of 0.0, the pipeline bypasses the PRNG entirely, identifying the argmax token. For Top-K and Top-P (Nucleus) sampling, logits must be sorted. Rust's f32 does not implement the standard Ord trait because IEEE-754 NaN values are mathematically incomparable32. To ensure stable sorting, the pipeline utilizes f32::total\_cmp, which enforces a strict total ordering (properly handling NaNs, \+0.0, and \-0.0). During Top-P cumulative summation, if multiple tokens share the exact same probability bounding the threshold, ties are deterministically broken utilizing the original token ID index, guaranteeing bit-exact invariance regardless of platform.
10. Compiler, Linker, Reproducibility, and Instruction Verification
Translating the Rust core into an implementation-grade WebAssembly binary requires aggressive compiler and linker controls. The baseline target is wasm32-unknown-unknown33. The wasm32-wasi target is explicitly rejected, as the runtime requires no OS imports or POSIX emulations33. The rustc configuration mandates:
- \-C opt-level=3 for maximum execution speed, or z if network transfer constraints dominate.
- \-C lto=fat to enable aggressive cross-crate inlining and comprehensive Dead Code Elimination.
- \-C panic=abort to eliminate massive stack unwinding infrastructure from the binary, a requirement given the absence of legacy exception handling35.
- Overflow checks are disabled in release builds to maximize throughput, as all tensor indexing is mathematically proven via bounds constraints in the scalar reference.
- \-C target-feature=+simd128,+bulk-memory dynamically applied during the SIMD-specific build pipeline.
Post-processing utilizes wasm-opt \-O3 \--strip-debug to execute Binaryen optimization passes, reducing binary size and stripping DWARF symbols and source-map data to protect proprietary implementation details36. To mathematically prove that the intended instructions exist in the final module—verifying that the compiler did not silently fall back to scalar polyfills—the continuous integration (CI) pipeline leverages the wasmparser Rust crate38. A custom verification script parses the emitted wasm32-unknown-unknown binary, statically asserting the presence of v128.load, i8x16.bitmask, and dot\_i16x8\_s opcodes. If these are absent, the build fails instantly.
11. Numerical Conformance Hierarchy and Golden Vectors
Numerical conformance is enforced via a strict, multi-tiered hierarchy to ensure refactoring or hardware disparities never silently corrupt the model.
1. Bit-Exact Invariant: Applied to the PCG32 PRNG output, token ID tracking, and integer arithmetic (e.g., RoPE index calculations). Outputs must hash identically across all platforms.
2. ULP-Bounded Invariant: Floating-point reductions, particularly the inner loop of horizontal SIMD dot products, may exhibit microscopic variations due to the non-associativity of IEEE-754 addition when vectorized differently by hardware. These pathways must match a known Golden Vector within 2 Units in the Last Place (ULP).
3. Raw-Output Invariant (Next-Token / Top-K): The final selected token IDs generated at Temperature 0.0 must be 100% identical between the native Rust target and the WebAssembly target for a continuous 1,000-token sequence.
4. Semantic-Regression Bounded: Perplexity drift and semantic evaluation are explicitly reserved for validating the quality of different quantization algorithms (e.g., Q8\_0 versus Q4\_0), and are never used to excuse divergence between the scalar oracle and an accelerated kernel.
Golden Vectors are established by compiling the scalar oracle as a native ELF/Mach-O CLI binary, feeding it deterministic random noise, and capturing the intermediate tensor states. These vectors are embedded in the CI pipeline to gate every pull request.
12. Measurement Protocol and Cross-Platform Matrix
Performance measurements must eschew "benchmark theater." Favorable microbenchmarks of single matrix multiplications do not reflect the reality of conversational LLM deployment. Measurements are captured directly in the browser using high-resolution timers (performance.now()), strictly isolating distinct phases:
- Compile & Initialization: Wall time to fetch, compile via WebAssembly.instantiateStreaming, and allocate memory.
- Cold / Warm Load: Distinguishing between cache misses and hits.
- Time To First Token (TTFT): Measures the prefill phase. Heavily compute-bound, representing prompt processing latency.
- Steady Decode: Inter-token latency measured over 128 continuous tokens, capturing memory-bandwidth efficiency.
The cross-platform test matrix spans:
- Chromium (V8) on Windows x64: Leveraging TurboFan tiering for optimal sustained throughput41. Tests Universal, SIMD, and Thread paths.
- Firefox (SpiderMonkey) on Linux x64: Strong baseline for COOP/COEP cross-origin isolation verification.
- Safari (WebKit) on macOS/iOS ARM64: Crucial for identifying memory pressure ceilings and OOM limits (\~384MB)14.
- Physical Linux ARM64: Validating cache line geometries and thermal state limitations (downclocking during prolonged decode loops).
13. Fault Injection, Error Taxonomy, and Public-Safe Receipts
The runtime must safely trap and report systemic errors rather than silently generating corrupted text or entering infinite loops. Startup self-tests operate prior to accepting full model weights; the runtime processes small, deterministic vectors to prove scalar operations, SIMD parity, PRNG stability, and thread synchronization. If any test fails, an error is generated from the following taxonomy:
- ERR\_UNSUPPORTED\_FEATURE (0x01): SIMD requested, but environment failed validation.
- ERR\_OOM\_ALLOC (0x02): Bump allocator exhausted capacity during init\_session.
- ERR\_STALE\_HANDLE (0x03): Host accessed an invalidated generation ID.
- ERR\_SAB\_MISSING (0x04): Threaded module instantiated without SharedArrayBuffer present.
- ERR\_MATH\_DIVERGENCE (0x05): Startup self-test failed ULP conformance.
Upon successful initialization, the runtime returns a public-safe, versioned capability receipt to the UI.
JSON { "version": "1.0.0-pre", "wasm\_hash": "e3b0c442...", "features\_enabled": \["simd128", "bulk-memory"\], "coop\_coep\_status": "isolated", "worker\_count": 4, "memory\_mode": "static\_arena", "self\_test": "pass" }
This receipt explicitly avoids collecting stable hardware fingerprinting data (e.g., exact CPU microarchitecture or physical core counts beyond the configured worker pool) that the product does not require, preserving user privacy. Failure injection routines in the test suite simulate incorrect server headers, Web Worker creation failures, shared-memory allocation denials, partial module compilation, out-of-bounds access attempts, and browser page discards to ensure the runtime recovers or terminates gracefully.
14. TDD Sequence and Clean Replacement Plan
Development executes a strict Test-Driven Development (TDD) sequence:
1. Phase 1: The Oracle. Implement the pure scalar f32 transformer in native Rust. Establish Golden Vectors.
2. Phase 2: The WASM Port. Cross-compile the scalar core to wasm32-unknown-unknown. Run via a headless browser test harness. Assert absolute vector matches.
3. Phase 3: SIMD Implementation. Develop SIMD128 kernels for Q4\_0 and Q8\_0 dot products independently. Assert ULP-bounded equality to the Oracle.
4. Phase 4: Threading. Implement row-partitioning. Assert exact byte parity with the single-threaded SIMD execution.
5. Phase 5: Clean Replacement. Once the SIMD and Thread paths achieve a 100% pass rate against the Golden Vectors, aggressively delete any superseded, experimental, or diverging code paths. No legacy scalar/SIMD compatibility shims remain; there is only one normative operation contract.
15. Unknowns Requiring Local Source, Browser, Model, or Hardware Execution
Because no access is granted to private TinyRustLM implementations, specific models, or hardware traces, the following parameters remain unknowns requiring local verification by the engineering team:
1. JIT Compilation Latency Dynamics: V8 executes WebAssembly via a tiered pipeline, starting with Liftoff (fast compilation, slower execution) and aggressively upgrading hot loops via TurboFan (slower compilation, maximum execution speed)41. The exact duration required for TurboFan to fully optimize the decoding loop for TinyRustLM impacts early TTFT and must be profiled locally.
2. Mobile Safari Worker Garbage Collection: Safari (WebKit) has historically exhibited memory leakage when aggressively terminating Web Workers that reference SharedArrayBuffer allocations42. The stability of prolonged conversation sessions over multiple loads and frees on physical iOS hardware requires direct observation.
3. Cache Line False Sharing: The physical layout of the atomic locks utilized by the thread pool must be traced on physical ARM64 chips to confirm that 64-byte alignment sufficiently prevents false sharing across modern heterogeneous cores.
16. Annotated Primary-Source Bibliography
- \[cite: 33, 34\]: Rust Target Support (wasm32-unknown-unknown). Establishes the core baseline target, definitively decoupled from Emscripten, utilizing mvp CPU flags unless explicitly augmented by specific LLVM features.
- \[cite: 1, 2, 43\]: WebAssembly 3.0 Specification Release (2025/2026). Details the formal rollout of Memory64, Garbage Collection, Tail Calls, and Exception Handling across mainstream browsers.
- \[cite: 3, 5, 7, 44\]: Relaxed SIMD Proposal and Determinism. Outlines non-deterministic performance optimizations and the specific Wasmtime flags (relaxed\_simd\_deterministic) required to enforce strict reproducibility.
- \[cite: 8, 9, 10, 11, 25\]: MDN & Security Standards on COOP/COEP. Mandates the absolute requirement of credentialless and same-origin HTTP headers to unlock SharedArrayBuffer for Web Workers.
- \[cite: 27, 28, 29, 30, 31\]: PCG32 Deterministic PRNG. Justifies the use of the Permuted Congruential Generator for zero-dependency, mathematically reproducible random sampling natively in Rust.
- \[cite: 37, 41, 45\]: V8 WASM Compilation Pipeline & Binaryen. Describes V8's Liftoff to TurboFan tiering and the \-O3 and \--strip-debug passes invoked by wasm-opt.
- \[cite: 17, 18, 22\]: GGML Quantization Formats. Documents the explicit memory layouts and sub-block architecture for Q8\_0 and Q4\_0 block formats (yielding 4.5 and 8.5 bits-per-weight respectively).
- \[cite: 12, 19, 20, 21, 46, 47, 48\]: WASM Linear Memory & Rust Export Constraints. Documents the reliance on \_\_heap\_base and \-Clink-arg=--max-memory= for raw, static memory control.
- \[cite: 38, 39, 40\]: Wasmparser & Tooling. Provides evidence for automated binary validation of specific instructions within CI pipelines.
- \[cite: 32\]: Rust Floating Point Comparators. Details the IEEE-754 NaN handling complexities driving the necessity of f32::total\_cmp for deterministic top-p sorting.
- \[cite: 14, 15, 16, 42\]: iOS Safari Memory Constraints. Captures the highly constrained OOM thresholds (\~384MB) plaguing WebKit when allocating WASM memory buffers or spawning complex workers.
- \[cite: 49, 50, 51\]: Tail Call Proposal. Explains the exact stack unwinding behavior of return\_call and its role in WebAssembly execution semantics.
Works cited
1. State of WebAssembly 2026 | The Dev Newsletter, https://devnewsletter.com/p/state-of-webassembly-2026/
2. WebAssembly 3.0 Is Official: Nine Features That Change What Wasm Can Do | byteiota, https://byteiota.com/webassembly-30-spec-release/
3. config.rs \- source \- Wasmtime, https://docs.wasmtime.dev/api/src/wasmtime/config.rs.html
4. WebAssembly Relaxed SIMD · Issue \#651 · mozilla/standards-positions \- GitHub, https://github.com/mozilla/standards-positions/issues/651
5. Implications of hw discovery · Issue \#11 · WebAssembly/relaxed-simd \- GitHub, https://github.com/WebAssembly/relaxed-simd/issues/11
6. lib.rs \- source \- Wasmtime, https://docs.wasmtime.dev/api/src/wasmtime\_cli\_flags/lib.rs.html
7. Deterministic Wasm Execution \- Wasmtime, https://docs.wasmtime.dev/examples-deterministic-wasm-execution.html
8. Security | 2024 | The Web Almanac by HTTP Archive, https://almanac.httparchive.org/en/2024/security
9. aero/docs/adr/0002-cross-origin-isolation.md at main · wilsonzlin, https://github.com/wilsonzlin/aero/blob/main/docs/adr/0002-cross-origin-isolation.md
10. I just wanted to compress a confidential document. Instead I built 60 client-side file tools., https://dev.to/nengil/i-just-wanted-to-compress-a-confidential-document-instead-i-built-60-client-side-file-tools-5454
11. @sqlite.org/sqlite-wasm TypeScript type hints · GitHub, https://gist.github.com/jbaiter/d4a2a22c6c15571062f25d1dfea73218
12. WebAssembly.Memory() constructor \- MDN Web Docs \- Mozilla, https://developer.mozilla.org/en-US/docs/WebAssembly/Reference/JavaScript\_interface/Memory/Memory
13. Wasm 3.0 Completed \- WebAssembly, https://webassembly.org/news/2025-09-17-wasm-3.0/
14. Fix: Unity WebGL Build Crashing on Safari iOS | Bugnet Blog, https://bugnet.io/blog/how-to-fix-unity-webgl-build-crashing-on-safari-ios
15. WebGL memory increment issue and crash on iOS \- Unity Discussions, https://discussions.unity.com/t/webgl-memory-increment-issue-and-crash-on-ios/894771
16. 255103 – REGRESSION (iOS 16.4): WASM Out Of Memory with shared=true \- WebKit Bugzilla, https://bugs.webkit.org/show\_bug.cgi?id=255103
17. A system programmer's guide to LLM inference \- Xiangpeng's blog, https://blog.xiangpeng.systems/posts/how-to-llm-inference/
18. The Complete Guide to LLM Quantization with vLLM: Benchmarks & Best Practices, https://jarvislabs.ai/blog/vllm-quantization-complete-guide-benchmarks
19. Why WebAssembly came to the Backend (Wasm in the wild part 3\) \- Jakob's Blog, https://www.jakobmeier.ch/wasm-road-2
20. What should the memory layout look like for wasm modules? · Issue \#81 · rustwasm/team \- GitHub, https://github.com/rustwasm/team/issues/81
21. how to reduce the initial memory size? · Issue \#1345 \- GitHub, https://github.com/wasm-bindgen/wasm-bindgen/issues/1345
22. Rotate, Then Round: The Geometry of KV-Cache Compression | by Tejaswi kashyap, https://medium.com/@tejaswi\_kashyap/rotate-then-round-the-geometry-of-kv-cache-compression-962bc22a1698
23. Implementing the WebAssembly bitmask operations on the 64-bit Arm architecture, https://developer.arm.com/community/arm-community-blogs/b/architectures-and-processors-blog/posts/webassembly-bitmask-operations
24. dot\_i16x8\_s: Wasm SIMD arithmetic instruction \- WebAssembly \- MDN Web Docs, https://developer.mozilla.org/en-US/docs/WebAssembly/Reference/SIMD/arithmetic/dot\_i16x8\_s
25. Cross-Origin-Opener-Policy (COOP) header \- HTTP \- MDN Web Docs, https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cross-Origin-Opener-Policy
26. Pthreads support \- Emscripten 6.0.5-git (dev) documentation, https://emscripten.org/docs/porting/pthreads.html
27. rand\_pcg \- Rust \- Docs.rs, https://docs.rs/rand\_pcg/latest/rand\_pcg/
28. Pseudo-random numbers/PCG32 \- Rosetta Code, https://rosettacode.org/wiki/Pseudo-random\_numbers/PCG32
29. Zero-dependency random number generation in Rust \- Orhun's Blog, https://blog.orhun.dev/zero-deps-random-in-rust/
30. Reversible random number generation, http://robotics.ucsd.edu/ReversibleRNG.pdf
31. PCG32: The Perfect PRNG for Roguelikes \- Steve Landey, https://steveasleep.com/pcg32-the-perfect-prng-for-roguelikes.html
32. total\_cmp on f32 and f64 and Ord : r/rust \- Reddit, https://www.reddit.com/r/rust/comments/yl3tov/total\_cmp\_on\_f32\_and\_f64\_and\_ord/
33. Zig and WASM \- Hacker News, https://news.ycombinator.com/item?id=32083090
34. wasm32v1-none \- The rustc book \- Rust Documentation, https://doc.rust-lang.org/rustc/platform-support/wasm32v1-none.html
35. Version 1.90 (2025-09-18) \- Google Git, https://chromium.googlesource.com/external/github.com/rust-lang/rust/+/ce6daf3d5a5bffb2a00264197f92dc31608df0da/RELEASES.md
36. How to remove debug symbols from a WebAssembly file \- Stack Overflow, https://stackoverflow.com/questions/62260790/how-to-remove-debug-symbols-from-a-webassembly-file
37. wasm-opt(1) — binaryen — Debian experimental, https://manpages.debian.org/experimental/binaryen/wasm-opt.1.en.html
38. wasmparser \- Rust \- Docs.rs, https://docs.rs/wasmparser
39. GitHub \- bytecodealliance/wasm-tools: CLI and Rust libraries for low-level manipulation of WebAssembly modules, https://github.com/bytecodealliance/wasm-tools
40. wasm-tools \- crates.io: Rust Package Registry, https://crates.io/crates/wasm-tools/1.0.54
41. WebAssembly compilation pipeline \- V8.dev, https://v8.dev/docs/wasm-compilation-pipeline
42. 250569 – Terminated worker memory leak \- WebKit Bugzilla, https://bugs.webkit.org/show\_bug.cgi?id=250569