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.
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. 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:
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, sqlite3.js, sqlite3.wasm, sqlite3-opfs-async-proxy.jsGet 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 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:
BASE=https://github.com/lightninglabs/wavelength/releases/downloadcurl -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:
# Build the WASM runtime into wavelength/bin/wasm/make -C /path/to/wavelength wasm-wallet
# Copy every runtime binary into a directory named after# RUNTIME_MANIFEST_VERSION (example: Vite public/)mkdir -p "./public/wavewalletdk/<version>"for f in wavewalletdk.wasm wavewalletdk.wasm.gz wasm_exec.js sqlite-bridge.js \ sqlite-worker.js sqlite3.js sqlite3.wasm sqlite3-opfs-async-proxy.js; do cp "/path/to/wavelength/bin/wasm/$f" "./public/wavewalletdk/<version>/"doneThe 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 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.
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 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, 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.
f=./public/wavewalletdk/<version>/wavewalletdk.wasmecho "sha256-$(openssl dgst -sha256 -binary "$f" | openssl base64 -A)"# compare the output against RUNTIME_ASSET_DIGESTS['wavewalletdk.wasm']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.