---
title: "Hosting runtime assets"
description: "How the Wavelength SDK loads its WebAssembly runtime, and how to obtain and host the asset set for your deployment."
canonical: https://wavelength.lightning.engineering/web/get-started/hosting-runtime-assets/
---

> Docs index: https://wavelength.lightning.engineering/llms.txt

# Hosting runtime assets

The web transport loads its WebAssembly runtime from a set of files that you host yourself and point `runtimeBaseUrl` at. You always provide the assets. The worker entry (`wavewalletdk-worker.js`) ships inside `@lightninglabs/wavelength-web` and is emitted by your bundler; only the daemon binaries below need to be hosted.

```ts
import { createWebClient } from '@lightninglabs/wavelength-web';

const client = createWebClient({
  runtimeBaseUrl: 'https://your-host/wavewalletdk/<version>/',
});
```

If `runtimeBaseUrl` is unset, assets resolve relative to the page URL in both worker and main-thread mode, so you can also serve the runtime files alongside your app.

## The asset set

The runtime is a fixed set of files built from [wavelength](https://github.com/lightninglabs/wavelength). Import `RUNTIME_ASSET_FILES` to see every filename that must be served together at one base URL. `RUNTIME_MANIFEST_VERSION` identifies the daemon build the SDK is paired with:

```ts
import {
  RUNTIME_ASSET_FILES,
  RUNTIME_MANIFEST_VERSION,
} from '@lightninglabs/wavelength-web';

console.log(RUNTIME_ASSET_FILES);
// wavewalletdk.wasm, wavewalletdk.wasm.gz, wasm_exec.js, sqlite-bridge.js,
// sqlite-worker.js, sqlite-node-vfs.js, sqlite3.js, sqlite3.wasm,
// sqlite3-opfs-async-proxy.js
```

## Get the asset set

There are two ways to obtain the files, both producing the set for the pinned `RUNTIME_MANIFEST_VERSION`:

**Download from the wavelength release.** The matching asset set is attached to the paired [wavelength release](https://github.com/lightninglabs/wavelength/releases) as `Wavewalletdk.wasm.tar.gz`. It holds every file above, flat with no enclosing directory, so it unpacks straight into the directory you serve them from:

```bash
BASE=https://github.com/lightninglabs/wavelength/releases/download
curl -fsSLO "$BASE/<version>/Wavewalletdk.wasm.tar.gz"

mkdir -p "./public/wavewalletdk/<version>"
tar -xzf Wavewalletdk.wasm.tar.gz -C "./public/wavewalletdk/<version>"
```

**Build from a wavelength checkout.** From a checkout of `wavelength`, build the WASM wallet target and copy the output into your app’s static folder:

```bash
make -C /path/to/wavelength wasm-wallet
mkdir -p "./public/wavewalletdk/<version>"
for f in wavewalletdk.wasm wavewalletdk.wasm.gz wasm_exec.js sqlite-bridge.js \
         sqlite-worker.js sqlite-node-vfs.js sqlite3.js sqlite3.wasm \
         sqlite3-opfs-async-proxy.js; do
  cp "/path/to/wavelength/bin/wasm/$f" "./public/wavewalletdk/<version>/"
done
```

The monorepo demo wraps the same build in `pnpm --filter web-wallet-demo run wasm:local`, which builds from a sibling `wavelength` checkout and stages files into `apps/web-wallet-demo/public/runtime/<version>/`, the versioned path the demo’s `runtimeBaseUrl` points at. After `vite build`, those files land in `dist/` and are served from the app origin.

A runtime built this way will not match the pinned digests described in [Integrity verification](#integrity-verification) below; point `createWebClient()` at it with `runtimeIntegrity: false` as covered there.

## Host the assets

Host all of them under a single path and set `runtimeBaseUrl` to that directory. Prefer a path that includes `RUNTIME_MANIFEST_VERSION` (for example `/wavewalletdk/<version>/`): every asset set then gets a unique URL, so browsers pick up new assets on an SDK upgrade instead of serving stale cached copies, and you can cache the files aggressively. Trailing slashes are optional; the client normalizes the base URL.

**Version lock.** Runtime assets are version-locked to the embedded daemon inside `@lightninglabs/wavelength-web`. When you upgrade the SDK, obtain and redeploy the matching WASM bundle. Mismatched versions can fail at load time or produce subtle runtime errors.

**Compression.** The client fetches `wavewalletdk.wasm.gz` first and falls back to the uncompressed `wavewalletdk.wasm` if it cannot be used (for example when the browser has no `DecompressionStream` to inflate it). Host both files so every supported browser can load the runtime.

No particular *compression* headers are required. The client reads the file’s first bytes and branches on the magic number rather than on `Content-Type` or `Content-Encoding`, so a body your host serves compressed, one it has already inflated, and one labelled `application/gzip` all load the same way. Compress it however your host makes easiest.

Other headers still matter: a cross-origin `runtimeBaseUrl` needs `Cross-Origin-Resource-Policy: cross-origin` on these assets, covered in [cross-origin isolation](/web/get-started/cross-origin-isolation/).

Do still set long-lived cache headers, but note that the browser will not keep a module this large in its HTTP cache regardless of what you send. The SDK therefore keeps its own copy in Cache Storage so returning visitors do not re-download it; see [Data & persistence](/web/runtime/data-and-persistence/#the-runtime-cache) for what that stores and when it is pruned. Cached bytes are re-verified against the pinned digest on every read, exactly like a fresh fetch, so a cache entry can never become a bypass for the checks below.

## Integrity verification

Before executing the wasm binary and the two bootstrap scripts the transport executes (`wasm_exec.js` and `sqlite-bridge.js`), the web transport verifies their fetched bytes against SHA-256 digests pinned in the npm package for the paired `RUNTIME_MANIFEST_VERSION`. Hosting a mismatched version’s assets fails loudly with `asset_integrity_failed` instead of silently running stale or tampered code. `wavewalletdk.wasm.gz` is covered too: the client verifies its decompressed bytes against the same digest entry as `wavewalletdk.wasm`, since decompressing it yields identical content.

The remaining files in the asset set (the sqlite worker chain: `sqlite-worker.js`, `sqlite-node-vfs.js`, `sqlite3.js`, `sqlite3.wasm`, `sqlite3-opfs-async-proxy.js`) are not verified at runtime. Cover them by checking your deployed set at deploy time instead: `RUNTIME_ASSET_DIGESTS` exports the digest for every file in the set, keyed by filename in the same SRI format (`sha256-<base64>`) the client checks against.

```bash
f=./public/wavewalletdk/<version>/wavewalletdk.wasm
echo "sha256-$(openssl dgst -sha256 -binary "$f" | openssl base64 -A)"
```

**Escape hatch for source-built runtimes.** A runtime you build yourself from a `wavelength` checkout will not match the pinned digests, since those pin a specific published release. Set `runtimeIntegrity: false` on `createWebClient()` or `createWebWalletEngine()` to skip verification; doing so logs a one-time console warning so the switch is never silently left off in production.

**Hosting requirements.** Serve the runtime assets same-origin where possible. If your app sets a Content-Security-Policy, `script-src` must include `blob:` alongside `'self'`: the SDK executes its verified bootstrap scripts from blob URLs, and a policy without `blob:` breaks the SDK outright. Hosting the assets cross-origin additionally requires CORS headers on the asset host, since the client fetches them before verifying their bytes.
