#AI#TypeScript#DevOps

Browser WebAssembly Model Asset Governance: Digest Verification, Cache ABI, and Safe Rollback

A client-side model is composed of Wasm, JS glue, weights, and a tokenizer; any cached skew can silently produce wrong results. This article provides digest verification, an ABI manifest, isolation-header decisions, and a rehearsed rollback plan.

Key Points

  • Topic Focus: AI、TypeScript、DevOps
  • Suitable for: indie hackers, full-stack engineers, AI application developers
  • Core value: quickly understand background, practical paths, common pitfalls, and engineering advice
Author @admin·Published July 26, 2026·Updated July 26, 2026

A browser-side model is composed of .wasm, JS glue, weight shards, a tokenizer, and configuration. If any object on the CDN updates slowly, the page may end up using the wrong vocabulary or the wrong operator implementation. WebAssembly Basics explains how JS and Wasm cooperate; this article focuses on the integrity boundaries of download, caching, and rollback.

One manifest binds all artifacts

The build pipeline generates an immutable manifest for each release, recording at minimum the release ID, the byte count and SHA-384 of each file, the runtimeAbi, the model format, the tokenizer digest, the JS glue version, the required Wasm features, and the build toolchain. URLs use a content digest or a release ID; pointing /model/latest.bin at mutable content is forbidden.

json
{
  "release": "browser-ai-2026.07.18.3",
  "runtimeAbi": "ort-web-v4",
  "features": ["simd"],
  "assets": [
    {"url": "/ai/4f2a/runtime.wasm", "bytes": 918244,
     "integrity": "sha384-BASE64_DIGEST"},
    {"url": "/ai/4f2a/model-01.bin", "bytes": 67108864,
     "integrity": "sha384-BASE64_DIGEST"}
  ]
}

The digest only proves that the bytes match the manifest; it does not prove the publisher is trusted. The manifest must ship with a trusted bundle or be signed by a trusted key; if an attacker can replace both the file and the manifest at once, the hash is meaningless. This is consistent with the chain-of-evidence principle in AI Supply Chain Security.

The loader uses fetch(url, { integrity }) and checks the final URL, the bytes actually read, and the response type. When integrity is non-empty, Fetch reads and verifies the entire body first before handing the Response to the caller; therefore this SRI path cannot let Wasm compilation proceed in parallel with the network download. The .wasm must still return Content-Type: application/wasm; WebAssembly.instantiateStreaming can avoid the application layer first constructing an arrayBuffer(), but it cannot recover network overlap that is already lost. The cold-start budget and peak memory must be measured against full asset verification, and a wrong MIME type fails outright at pre-release. Weight shards likewise cannot be handed to the inference engine until verification completes, and the browser matrix must also verify SRI, redirects, and cross-origin response behavior.

The cache namespace must include the ABI

Cache Storage is isolated per release, for example ai-assets:browser-ai-2026.07.18.3. When installing a new version, first write to a staging namespace, verify the digest and size item by item, and only mark the release as ready once everything passes; a half-set of resources can never become the active version. Web Caching Strategies can decide freshness, but the correctness of model assets cannot depend on TTL or filename guessing.

On a cache hit, still re-check the byte count and digest against the trusted manifest; you cannot trust a single piece of ready metadata. verify/use must bind to the same Response snapshot: the full scheme reads once into controlled bytes and consumes those bytes directly after verification; you cannot verify and then call cache.match() a second time, or a concurrent replacement creates a TOCTOU. The tee of Response.clone() may also buffer the entire body for the slow branch, so it cannot be treated as a zero-cost option. If large shards use a content-addressed chunked digest, the chunk size, order, and final root digest all go into the manifest; each verified chunk is written to staging before consumption, and on failure the entire namespace is isolated.

Before loading, compare the manifest's runtimeAbi, the JS glue declaration, and the model format. Even with identical export names, skew can arise from changes in memory layout, dtype, operator set, or tokenizer. The loader should return stable errors such as ASSET_DIGEST_MISMATCH, ABI_UNSUPPORTED, and WASM_MIME_INVALID; it cannot swallow the error and keep using arbitrary old caches.

An old namespace is kept for at least one observation window and is subject to the overall byte budget. Cleanup only deletes complete releases that are "inactive and not in the rollback set"; per-file LRU is not allowed, because it creates unrecoverable mixed versions. Offline startup may only select a manifest that is already ready and compatible with the current loader; otherwise it shows a clear offline-unavailable state.

Threading capability and cross-origin isolation are decided separately

Single-threaded Wasm, SIMD, and ordinary Workers do not require cross-origin isolation just because "AI is used." Only when the runtime actually depends on SharedArrayBuffer or Wasm threads do you configure Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp (or a verified credentialless), and check crossOriginIsolated at startup.

These response headers change opener, iframe, and cross-origin resource loading; they cannot be treated as performance switches. Models, Workers, fonts, and telemetry endpoints must satisfy CORS or CORP; you can audit them together with CORS and CSP. The matrix tests isolated and non-isolated modes separately: when headers are missing it selects the published single-threaded assets, and without a fallback package it refuses to start.

The threaded and single-threaded versions each have their own manifest, digest, and benchmark results. The manifest records the full target browser version, CPU architecture, thread count, SIMD/threads probe results, and peak memory. Concurrent computation should still sit inside a controlled Worker protocol to avoid leaving a runtime behind after the UI cancels; task isolation can reference Web Workers Concurrency.

Use corruption and skew tests to gate rollback

The release gate proactively injects: flipping one byte of the Wasm, truncating weights, returning an HTML error page, faking application/wasm, letting the CDN return old glue, mixing a new model with an old tokenizer, losing the network mid-cache-write, concurrently replacing the cache after digest verification, misconfiguring COEP, and the browser not supporting threads. The network waterfall must also prove that, with integrity enabled, the Response is delivered only after full verification, and records cold-start and peak memory. Each case should produce the expected error code before the first inference, and staging must not be marked ready.

Rollback is not clearing the cache and retrying. The control plane points the active release back at the previous verified manifest while restoring the matching loader bundle and threading policy; the client stops enqueuing the new version, waits for in-flight tasks to finish or terminates them per protocol, and then rebuilds the runtime from the retained namespace. The new assets that have already landed continue to be kept isolated for forensics; the old loader cannot be allowed to guess a new ABI. If response headers must also be restored, window communication and embedding relationships are verified separately.

Monitoring aggregates digest failures, MIME errors, ABI rejections, cache hits, initialization time, and inference verification samples per release, without recording user input. Only after the new version completes cold start, offline start, corruption recovery, and a real rollback rehearsal in the approved browsers is traffic expanded. Reliable client-side inference is not "Wasm can instantiate"; it is that every byte is verifiable, every ABI is matchable, every capability assumption is detectable, and the previous version is always recoverable.

Further reading

Related

Related Content