wavelength-web
Browser SDK factory, configuration, and passkey ceremony API for Wavelength web apps.
@lightninglabs/wavelength-web is the browser/wasm transport for the Wavelength SDK: it
runs the embedded daemon as WebAssembly, either in a dedicated Web Worker (the
default) or on the page’s main thread, and exposes it as a standard
WavelengthClient. createWebWalletEngine()
wraps that client in a WalletEngine
in one call, which is the entry point most apps want. It also ships the
browser (WebAuthn/PRF) passkey ceremony.
Configuration
defaultConfig
functionReturns a ready-to-use RuntimeConfig for a network, preloaded with the
canonical public REST gateway endpoints the web transport dials, merged with
any overrides. This is a shallow merge of top-level RuntimeConfig fields.
Pass overrides to set dataDir or point at your own infrastructure.
function defaultConfig( network: PresetNetwork, overrides?: Partial<RuntimeConfig>,): RuntimeConfignetworkPresetNetwork- The Bitcoin network to run against; mainnet and regtest are excluded because they have no preset. Use signet for development and testing.
overridesPartial<RuntimeConfig>- Optional. Fields that override the network preset's defaults. Default: {}.
RuntimeConfigA complete configuration object for client.start() or the engine factory’s config.
import { defaultConfig } from '@lightninglabs/wavelength-web';
const config = defaultConfig('signet', { dataDir: 'my-wallet' });'mainnet' and 'regtest' are not accepted: mainnet has no public
deployment yet, and regtest’s local ports vary per development environment.
Build their RuntimeConfig by hand, mainnet with your own gateway URLs and
allowMainnet, regtest with local URLs and the insecure-transport flags.
On web, arkServerAddress and swapServerAddress are REST URLs, and
walletEsploraUrl is an HTTP Esplora endpoint. The web transport rejects
arkServerTlsCertPath. It accepts swapServerTlsCertPath only when
disableSwaps: true, which suppresses every swap field.
The per-network endpoint values are on the
wavelength-core reference.
Creating a client
createWebClient
functionCreates and returns a WavelengthClient that runs the embedded daemon in the
browser. Call once at application startup, then wrap it with
createWalletEngine from @lightninglabs/wavelength-core (or use
createWebWalletEngine below to do both in one call)
or call start(defaultConfig(network)) directly. Defaults to the Web Worker
transport (WorkerWavelengthClient); pass runtimeThread: 'main' to run on the page’s main thread instead.
function createWebClient( options?: WebClientOptions,): WavelengthClientoptionsWebClientOptions- Optional browser-specific settings. Defaults to worker-thread mode with page-relative runtime assets.
WavelengthClientA live client instance. Call ready() to wait for the WASM runtime to load, then start(config) to boot the daemon.
WebClientOptions
typeBrowser-specific options for createWebClient. The config passed to
client.start() is separate: use defaultConfig(network) from
@lightninglabs/wavelength-web.
type WebClientOptions = { workerURL?: string; runtimeBaseUrl?: string; runtimeThread?: RuntimeThread; runtimeCache?: boolean; debug?: boolean; onPerformance?: WavelengthPerformanceListener; runtimeIntegrity?: boolean;};workerURLstring- Overrides the worker entry point. By default the worker client spawns the worker its bundler emits from new URL("../wavewalletdk-worker.js", import.meta.url); pass this to point at a custom-hosted copy instead. runtimeBaseUrl is still forwarded to the worker even when this is set. Keep a custom copy in sync with the installed SDK version: an older copy predating runtime integrity ignores the digest table this client sends it and performs no verification at all, silently. Default: bundler-emitted worker URL.
runtimeBaseUrlstring- Base URL the daemon runtime binaries (wavewalletdk.wasm.gz, wasm_exec.js, sqlite-*.js) are resolved against. Unset resolves relative to the document: worker mode computes the document base URL and forwards it to the worker so it resolves page-relative just like main-thread mode. Default: unset (page-relative).
runtimeThreadRuntimeThread- Which thread runs the WASM runtime. 'main' produces a MainThreadWavelengthClient that blocks rendering while busy; use it only when Worker support is unavailable. Default: 'worker'.
debugboolean- Logs every RPC request and response payload to the console. Payloads can include addresses and amounts, so treat this as a development-only aid and never enable it in a shipped app. Main-thread mode logs facade calls directly; worker mode forwards the flag into the worker so it can log there too. Default: false.
runtimeCacheboolean- Keeps a copy of the daemon runtime in Cache Storage so a returning visitor reads it from disk instead of downloading it again. Set false to take every runtime binary from the network, which is what you want while iterating on a local daemon build: rebuilding at the same RUNTIME_MANIFEST_VERSION produces new bytes under a bucket name that has not changed, and DevTools' "Disable cache" governs only the HTTP cache, so it will not help. Turning it off deletes nothing: an existing bucket is left as it is and simply not read or written, so turning it back on resumes from it. Default: true.
onPerformanceWavelengthPerformanceListener- Receives structured timing samples for runtime readiness and wasm loading. Instrumentation stays off unless this is supplied: with it omitted no timing is measured and no samples are delivered. A listener that throws cannot break wallet work. Pass the same callback to createWebWalletEngine to also cover wallet RPCs, info adoption and sync polling. Default: unset (disabled).
runtimeIntegrityboolean- Verifies the SHA-256 digest of the wasm binary and the bootstrap scripts (wasm_exec.js, sqlite-bridge.js) against the digests pinned for RUNTIME_MANIFEST_VERSION (RUNTIME_ASSET_DIGESTS) before executing them. Set false only when running a runtime built from source (for example a local wavelength checkout), whose bytes cannot match the pinned release; disabling logs a one-time console warning so the switch is never silently left off in production. Default: true.
RuntimeThread
typeSelects which thread the wasm runtime runs on: 'worker' (default) in a
dedicated Web Worker that keeps the UI thread free, or 'main' on the page’s
main thread as an escape hatch (for example, environments without Worker
support).
type RuntimeThread = 'main' | 'worker';Creating an engine
createWebWalletEngine
functionCreates a WalletEngine over the browser/wasm transport: the one-call setup for a web app. It builds the client with createWebClient() internally, so you only need this one factory. Pass the result to WavelengthProvider from wavelength-react, or drive it directly without React.
function createWebWalletEngine( options?: WebWalletEngineOptions,): WalletEngineoptionsWebWalletEngineOptions- Optional. The same browser-specific settings as createWebClient, plus the engine's config and autoStart.
WalletEngineA live engine driving a freshly built web client. See WalletEngine for its full behavior, including the background processes it runs automatically.
WebWalletEngineOptions
The browser-specific settings from WebClientOptions combined with WalletEngineOptions’s config/autoStart union (minus client, which this factory builds for you): the types require a config when autoStart is enabled, so autoStart: true without config is a compile error.
type WebWalletEngineOptions = WebClientOptions & ( | { config: RuntimeConfig; autoStart: true } | { config?: RuntimeConfig; autoStart?: false } );workerURLstring- Same as WebClientOptions.workerURL.
runtimeBaseUrlstring- Same as WebClientOptions.runtimeBaseUrl.
runtimeThreadRuntimeThread- Same as WebClientOptions.runtimeThread.
debugboolean- Same as WebClientOptions.debug.
runtimeCacheboolean- Same as WebClientOptions.runtimeCache.
onPerformanceWavelengthPerformanceListener- Same as WebClientOptions.onPerformance, and forwarded to the engine as well, so one listener covers runtime loading plus wallet RPCs, info adoption and sync polling.
runtimeIntegrityboolean- Same as WebClientOptions.runtimeIntegrity.
configRuntimeConfig- Required when autoStart is true. Default runtime config used by autoStart and by the engine's start() when called with no argument.
autoStarttrue | false- Optional. Start the runtime automatically once it is ready. Setting it to true requires config.
import { createWebWalletEngine, defaultConfig,} from '@lightninglabs/wavelength-web';import { WavelengthProvider } from '@lightninglabs/wavelength-react';
const engine = createWebWalletEngine({ runtimeBaseUrl: 'https://your-host/wavewalletdk/', config: defaultConfig('signet'), autoStart: true,});
export function Root() { return <WavelengthProvider engine={engine}>{/* your app */}</WavelengthProvider>;}Transports
MainThreadWavelengthClient
classRuns the wasm runtime on the page’s main thread. It implements
WavelengthClient directly and is
the escape hatch for environments without Web Worker support (or where
main-thread execution is preferred for another reason). Unlike worker mode it
blocks rendering while the runtime is busy.
class MainThreadWavelengthClient implements WavelengthClient { constructor(options?: WebClientOptions);}optionsWebClientOptions- Same options accepted by createWebClient. runtimeThread is ignored here since the constructor already picks the main thread.
Prefer createWebClient({ runtimeThread: 'main' }) over constructing this
class directly; only reach for the class import when you need to branch
construction logic yourself (for example, feature-detecting Worker support at
runtime).
WorkerWavelengthClient is the default transport returned by createWebClient()
(when runtimeThread is unset or 'worker'). It is not exported directly,
select it via createWebClient(). Worker mode requires a daemon that stores
the encrypted seed in OPFS rather than localStorage (which is window-only
and unavailable inside a Worker); the daemon shipped with this package already
does this. See the browser support guide for
which browsers provide OPFS and the other APIs worker mode depends on.
Runtime assets and hosting
RUNTIME_ASSETS
constThe canonical set of daemon runtime binaries the wasm client loads at
runtime, resolved against runtimeBaseUrl. RUNTIME_ASSET_FILES is just
Object.values(RUNTIME_ASSETS). This set is built by wavelength (make wasm-wallet) and version-locked to the embedded wavelength daemon, so the
files must be hosted side by side at one base URL.
const RUNTIME_ASSETS: { wasm: string; wasmGz: string; wasmExec: string; sqliteBridge: string; sqliteWorker: string; sqlite: string; sqliteWasm: string; sqliteOpfsProxy: string;};The values are fixed filenames, not just string-typed placeholders:
| Key | Filename |
|---|---|
wasm |
wavewalletdk.wasm |
wasmGz |
wavewalletdk.wasm.gz |
wasmExec |
wasm_exec.js |
sqliteBridge |
sqlite-bridge.js |
sqliteWorker |
sqlite-worker.js |
sqlite |
sqlite3.js |
sqliteWasm |
sqlite3.wasm |
sqliteOpfsProxy |
sqlite3-opfs-async-proxy.js |
RUNTIME_ASSET_FILES
constFlat list of every runtime binary that must be hosted together at
runtimeBaseUrl: wavewalletdk.wasm, wavewalletdk.wasm.gz, wasm_exec.js,
sqlite-bridge.js, sqlite-worker.js, sqlite3.js, sqlite3.wasm,
sqlite3-opfs-async-proxy.js (that is, Object.values(RUNTIME_ASSETS)). Host
these files and point runtimeBaseUrl at them before calling
client.ready().
const RUNTIME_ASSET_FILES: readonly string[];RUNTIME_ASSET_DIGESTS
constThe SHA-256 digest (SRI format, sha256-<base64>) of every file in the
runtime asset set published for RUNTIME_MANIFEST_VERSION, keyed by
filename. Generated by pnpm gen:runtime-digests from a staged asset set;
the web transport verifies the wasm binary and the bootstrap scripts it
executes against this table before running them, and the full table is
exported so integrators can also verify their hosted set at deploy time.
const RUNTIME_ASSET_DIGESTS: Readonly<Record<string, string>>;RUNTIME_MANIFEST_VERSION
constThe daemon build this SDK release is paired with: the generated types, the
client methods, and the runtime asset set all come from this one daemon
revision. Hosted asset sets live in a directory named after this value, so a
version bump changes every asset URL and browsers can never serve a stale
cached runtime. Defined in (and re-exported from)
@lightninglabs/wavelength-core.
const RUNTIME_MANIFEST_VERSION: string;Asset resolution and loading
runtimeBaseUrl controls where each file in RUNTIME_ASSETS is fetched
from:
- Trailing slash: a base URL is normalized by appending a trailing slash
if missing before resolving each asset name against it, so
https://assets.example.com/wavewalletdkandhttps://assets.example.com/wavewalletdk/behave the same. - Unset base: with no
runtimeBaseUrl, the bare filename is used and resolves relative to the document. In worker mode the worker cannot resolve page-relative paths itself, so the client computes the document’s base URL (document.baseURI) on the main thread and forwards it to the worker as part of its$initmessage, keeping worker mode and main-thread mode page-relative in the same way. - Compressed-first wasm loading: the client fetches the gzip-compressed
wavewalletdk.wasm.gzbinary first and falls back to the uncompressedwavewalletdk.wasmif that asset cannot be loaded at all, or the browser has noDecompressionStreamto inflate it. - The body decides, not the headers: both assets go through one loader,
which reads the first four bytes and branches on the magic number
(
1f 8bfor gzip,00 61 73 6dfor wasm) rather than onContent-TypeorContent-Encoding. That matters because neither header is trustworthy: a host may label the compressed fileapplication/gzip, or serve it already inflated, andContent-Encodingis not a CORS-safelisted response header, so cross-origin it is often invisible even when the transport did decode the body. Reading the bytes means every one of those configurations lands on the same path. It also means you do not have to configure anything for the compressed asset to work. - Integrity verification before execution: before running any of it, the
client verifies the fetched bytes of the wasm binary and its two bootstrap
scripts (
wasm_exec.jsandsqlite-bridge.js) against the digests pinned inRUNTIME_ASSET_DIGESTSforRUNTIME_MANIFEST_VERSION. The bootstrap scripts are fetched, verified, and then executed from a blob URL (a<script src={blobURL}>on the main thread,importScripts(blobURL)in the worker); the wasm bytes are verified and then passed straight toWebAssembly.instantiate, buffered rather than streamed, since the bytes must be complete before they can be hashed. A digest mismatch throwsasset_integrity_failedand does not fall back to another loading path, since retrying would only refetch the same tampered or stale content from the same origin. A cache hit (seeruntimeCacheabove) is re-verified the same way before it is instantiated, so a Cache Storage entry can never become a bypass. SetruntimeIntegrity: falseto skip this check, for example against a runtime you built yourself. See the hosting runtime assets guide for the full verification story, including the files this check does not cover. - Worker entry resolution: the worker script itself (unlike the daemon
binaries above) ships inside
@lightninglabs/wavelength-weband is emitted by your bundler vianew URL('../wavewalletdk-worker.js', import.meta.url), so it versions with the SDK and needs no separate hosting.runtimeBaseUrlis still forwarded to the worker (via its$initmessage) even when you override the worker entry point withworkerURL, since the two settings are independent:workerURLonly changes where the worker script itself loads from.
See the hosting runtime assets guide for a complete self-hosting walkthrough.
Passkeys (browser)
webPasskeyCeremony
constBrowser (WebAuthn/PRF) implementation of the
PasskeyCeremony contract,
built from the three standalone functions below with instrumentation left off.
Pass it to useWalletPasskey from wavelength-react, or call the functions
directly. It is a stable module-level value, so it is safe to pass straight to
a hook dependency list.
const webPasskeyCeremony: PasskeyCeremony;createWebPasskeyCeremony
functionBuilds a browser PasskeyCeremony
that reports ceremony timing. Use this instead of webPasskeyCeremony when you
want passkey samples on the same sink as runtime and wallet timing; with no
options it behaves identically.
Call it once and keep the result, rather than calling it inside a render: each call returns a new object, and hooks that take a ceremony compare it by identity.
function createWebPasskeyCeremony( options?: { onPerformance?: WavelengthPerformanceListener },): PasskeyCeremonyoptions.onPerformanceWavelengthPerformanceListener- Receives a sample per ceremony, tagged with the outcome so cancellations are distinguishable from errors. Phases are registerCeremony and assertCeremony; note that one registerPasskeyWallet call emits both when the browser omits the PRF result from create() and registration falls back to an assertion. Omit it to keep instrumentation disabled. Default: unset (disabled).
PasskeyCeremonyA ceremony forwarding the listener into registerPasskeyWallet and assertPasskeyPrf.
supportsPasskeyPrf
functionReports whether a user-verifying platform authenticator is available, which is a prerequisite for WebAuthn PRF.
function supportsPasskeyPrf(): Promise<boolean>;Promise<boolean>true when a platform authenticator that can perform user verification is available.
registerPasskeyWallet
functionCreates a new platform passkey and returns its PRF output and credential id.
Some browsers do not surface the PRF result from create(), so this falls
back to an assertion scoped to the just-created credential to read it
reliably without prompting a chooser.
function registerPasskeyWallet( appName: string, onPerformance?: WavelengthPerformanceListener,): Promise<PasskeyAssertion>;appNamestring- Used as both the WebAuthn relying-party name (rp.name) and the account display name shown by the platform passkey UI.
onPerformanceWavelengthPerformanceListener- Optional. Receives a registerCeremony sample tagged with the outcome, so a cancellation is distinguishable from an error. When the browser omits the PRF result from create() this falls back to an assertion, which emits its own assertCeremony sample, so a single call can produce two. Omit it to keep instrumentation disabled. Default: unset (disabled).
Promise<PasskeyAssertion>The new credential’s PRF output and credential id. Rejects if registration is cancelled.
assertPasskeyPrf
functionAuthenticates with a passkey and returns its PRF output plus the credential id used.
function assertPasskeyPrf( allowCredentialId?: string, onPerformance?: WavelengthPerformanceListener,): Promise<PasskeyAssertion>;allowCredentialIdstring- Optional. When set, the assertion is scoped to that one credential (base64url id from a prior PasskeyAssertion.credentialId) so the OS unlocks it directly without a chooser. When omitted, the assertion is discoverable (an empty allowCredentials list) so a synced passkey can be offered even on a device that has never seen this wallet.
onPerformanceWavelengthPerformanceListener- Optional. Receives one assertCeremony sample for this call, tagged with the outcome so a cancellation is distinguishable from an error. Omit it to keep instrumentation disabled. Default: unset (disabled).
Promise<PasskeyAssertion>The asserted credential’s PRF output and credential id. Rejects if authentication is cancelled.
PasskeyAssertion
typeResult of a passkey registration or assertion ceremony. Defined in
@lightninglabs/wavelength-core
and surfaced here through the re-export (see the callout at the top of this
page); shown in full because it is the return type of the two functions
above.
type PasskeyAssertion = { prfOutput: string; credentialId: string;};prfOutputstring- The WebAuthn PRF extension output, as a lower-case hex string.
credentialIdstring- The base64url-encoded WebAuthn credential id. Safe to persist and pass back into assertPasskeyPrf(allowCredentialId) to scope a later assertion to this credential.
Reproducibility. Both functions evaluate the PRF extension against a
fixed salt, SHA-256("wavewalletdk-passkey:v1"), a fixed namespace chosen
client-side. Using the same fixed salt on every
device/platform is what makes the derived wallet reproducible: asserting the
same passkey anywhere yields the same prfOutput, and therefore the same
derived wallet.
Discoverable vs. scoped assertions. assertPasskeyPrf() called with no
argument performs a discoverable assertion (empty allowCredentials),
letting the platform’s passkey chooser surface any matching synced passkey,
including on a device that has never seen this wallet before. Passing
allowCredentialId instead performs a scoped assertion limited to that one
credential, which lets the OS unlock it directly without showing a chooser.
Re-exported from wavelength-core
Types like SendRequest and ReceiveRequest referenced by
WavelengthClient methods
(send, prepareSend, receive, and so on) originate in
@lightninglabs/wavelength-core and are available
here only because wavelength-web re-exports the entire core package (see the
callout at the top of this page). The .d.ts filename shown in each
signature below is illustrative of the shape, not of which package physically
declares the type.
SendRequest
typeParameters accepted by prepareSend() and send(). SendRequest is a
discriminated union: provide invoice for a Lightning send, or
onchainAddress for an on-chain send. The two branches are mutually
exclusive; sweepAll exists only on the on-chain branch.
type SendRequest = | { invoice: string; amountSat?: number; note?: string; maxFeeSat?: number; } | { onchainAddress: string; amountSat?: number; sweepAll?: boolean; note?: string; maxFeeSat?: number; };invoicestring- BOLT11 lightning invoice to pay. Mutually exclusive with onchainAddress.
onchainAddressstring- Bech32 Bitcoin address for an on-chain send. Mutually exclusive with invoice.
amountSatnumber- Required at runtime for on-chain sends unless sweepAll is true; the TypeScript type does not enforce this.
notestring- Optional memo stored with the activity entry.
maxFeeSatnumber- Optional fee ceiling for the send.
sweepAllboolean- Drain every live VTXO to onchainAddress. Only present on the on-chain branch.
ReceiveRequest
typeParameters accepted by receive().
type ReceiveRequest = { amountSat: number; memo?: string;};amountSatnumber- Invoice amount in satoshis.
memostring- Optional description embedded in the invoice.
See also
- wavelength-core reference for
WavelengthClient,WalletEngine,RuntimeConfig,WalletInfo,Balance,Entry, and every other re-exported type. - wavelength-react reference for
WavelengthProviderand the hooks that consume an engine created here. - Hosting runtime assets for a full self-hosting walkthrough of the files listed above.