Run a WASM Node
Run a Fiber node directly in your browser app
TL;DR
Use @nervosnetwork/fiber-js to run a Fiber node inside a browser app. Install the package, serve a Fiber YAML config, create a Fiber instance on a cross-origin isolated page, and call start(); application code should not depend on the internal fiber-wasm package directly.
Interactive Quick Start
Run a real Fiber node in this browser
Start Fiber WASM, complete a real WSS peer handshake, derive this browser's Testnet CKB address, open a channel, and send a keysend payment. The lab is isolated from the docs page because Fiber WASM requires SharedArrayBuffer.
Open Browser Node Lab →Try It Before You Integrate It
The lab runs the node locally in your browser; it is not a UI that remotely controls a hosted Fiber node. Browsers cannot open raw TCP sockets, so the WASM node connects outbound to a native public node through WSS:
Your browser Fiber Testnet
┌──────────────────────┐ ┌──────────────────────────┐
│ Fiber WASM node │ WSS │ Public native node │
│ IndexedDB state │ ───────► │ Payment channel peer │
│ Browser-local key │ │ Fiber network │
└──────────────────────┘ └──────────────────────────┘The interactive flow uses fiber-testnet-public-bottle from Network Resources. It verifies each stage with live SDK state:
- Start the WASM node and wait until
listPeers()confirms the WSS handshake. - Display the browser node's CKB address and query its Testnet balance.
- Fund that address and open a real Testnet channel with the public node.
- Enter an amount and send an invoice-free keysend payment after the channel reaches
Ready.
Testnet identity and funding
The lab stores one Testnet identity in the current browser so the funding address survives refreshes. Different browser profiles receive different addresses, and clearing site data creates a new one. Do not send Mainnet funds to the lab address. The public node's current channel threshold is listed in Network Resources, and additional CKB is required for transaction fees.
What is fiber-js?
fiber-js is a JavaScript/TypeScript wrapper around the Fiber WebAssembly (WASM) node. It runs in browser-based applications and provides common Fiber node operations without running a separate backend service.
| Native Node | WASM Node | |
|---|---|---|
| Runtime | Native binary | Browser |
| Deployment | Server or local machine | Embedded in web app |
| Public IP required | Yes | No, use outbound /ws/ or /wss/ peers |
| Storage | File system (SQLite/RocksDB) | IndexedDB (via WASM worker) |
| Use case | Production node operators | Browser wallets, web games, client-side dApps |
Prerequisites
You need a JavaScript development environment before using fiber-js:
- Node.js with
npm - A browser-oriented build tool or framework, such as Vite, Next.js, Webpack 5, or an equivalent ESM toolchain
The examples below use npm. If your project already uses pnpm, use the equivalent pnpm command.
Installation
npm install @nervosnetwork/fiber-js
# or
pnpm add @nervosnetwork/fiber-jsPrepare a YAML config from the Fiber source tree. For local docs development with fiber checked out next to fiber-docs:
mkdir -p public/fiber-config
cp ../fiber/config/testnet/config.yml public/fiber-config/testnet.ymlYou can also download the config from the Fiber repository and serve it from any static path in your app.
Browser requirements
new Fiber() creates SharedArrayBuffer objects immediately. Only create a Fiber instance on a cross-origin isolated page, and make sure your deployment serves WASM and worker assets with the required CORS and isolation headers.
Quick Start
import { Fiber, randomSecretKey } from "@nervosnetwork/fiber-js";
if (!crossOriginIsolated) {
throw new Error("fiber-js requires a cross-origin isolated page.");
}
// 1. Create the Fiber WASM node wrapper.
const fiber = new Fiber();
// 2. Load the same YAML config format used by fnn.
const network = "testnet";
const configPath = `/fiber-config/${network}.yml`;
const config = await fetch(configPath).then((response) => {
if (!response.ok) {
throw new Error(`Failed to load Fiber config: ${configPath}`);
}
return response.text();
});
// 3. Start the node. Persist these keys in real applications.
const fiberKeyPair = randomSecretKey();
const ckbSecretKey = randomSecretKey();
const databasePrefix = `${network}:demo`;
await fiber.start(
config,
fiberKeyPair,
ckbSecretKey,
undefined,
"info",
databasePrefix,
);
// 4. Query node state.
const info = await fiber.nodeInfo();
console.log("Node started:", info);
console.log("Connected peers:", await fiber.listPeers());Expected Output: The node initializes, connects to configured
/ws/or/wss/peers, and starts syncing.nodeInfo()returns your node's pubkey, version, and chain info.
Common Workflows
Open a Channel and Send a Payment
Replace peerPubkey and peerAddress with values for the same target peer. openChannel() requires the CKB secret key passed to start() to control spendable CKB. Browser wallet integrations usually use openChannelWithExternalFunding() instead.
const toRpcHex = (value: bigint): `0x${string}` =>
`0x${value.toString(16)}` as `0x${string}`;
const ckb = (amount: bigint): `0x${string}` => toRpcHex(amount * 100_000_000n);
const randomHash = (): `0x${string}` => {
const bytes = crypto.getRandomValues(new Uint8Array(32));
return `0x${Array.from(bytes)
.map((byte) => byte.toString(16).padStart(2, "0"))
.join("")}` as `0x${string}`;
};
const peerPubkey = "<peer_pubkey>";
const peerAddress = "<peer_wss_multiaddr>";
// Connect to a browser-reachable peer.
await fiber.connectPeer({
address: peerAddress,
save: true,
});
// Open a channel. The amount is a CKB RPC hex string in shannons.
await fiber.openChannel({
pubkey: peerPubkey,
funding_amount: ckb(499n),
public: true,
});
// Create an invoice on the receiving node.
const invoice = await fiber.newInvoice({
amount: ckb(1n),
currency: "Fibt",
description: "Test payment",
payment_preimage: randomHash(),
});
// Send the payment.
await fiber.sendPayment({ invoice: invoice.invoice_address });Keysend (No Invoice)
await fiber.sendPayment({
target_pubkey: "<recipient_pubkey>",
amount: ckb(1n),
keysend: true,
});Error Handling
try {
const result = await fiber.sendPayment({ invoice: "fibt1..." });
} catch (err) {
console.error("Payment failed:", err);
}Common errors:
fiber-js requires a cross-origin isolated page: serve the page with isolation headers before callingnew Fiber().- Fiber is not started:
await fiber.start(...)before invoking node methods. - Connection failures: browser nodes usually need remote
/ws/or/wss/peer addresses, not plain/tcp/addresses. - Configuration errors: check that the loaded YAML config matches the target network and includes browser-reachable bootnodes.
API Reference
@nervosnetwork/fiber-js exports the Fiber class. Its methods are camelCase wrappers around Fiber RPC commands, and async methods return promises that reject on command errors.
| Area | Key Methods |
|---|---|
| Lifecycle | start, stop, invokeCommand |
| Channel | openChannel, openChannelWithExternalFunding, submitSignedFundingTx, listChannels, shutdownChannel, updateChannel |
| Payment | sendPayment, getPayment, buildRouter, sendPaymentWithRouter |
| Invoice | newInvoice, parseInvoice, getInvoice, cancelInvoice |
| Peer | connectPeer, disconnectPeer, listPeers |
| Graph | graphNodes, graphChannels |
| Node | nodeInfo |
For the full API reference see Build -> js.
Next Steps
- Open Browser Node Lab — run the complete browser flow
- Basic Transfer — learn the channel and payment flow
- JavaScript Overview — browser runtime, security headers, and API details
- Build a Game with Fiber — end-to-end example with Fiber payments