Getting StartedNode & Browser

Node & Browser

Running the same API in both runtimes: the side-effect import "wative-core/node" plus filesystem storage in Node versus IndexedDB and the required Buffer polyfill in the browser.

wative-core runs in Node and in the browser with the same API. The domain objects — Workspace, Account, Wallet, Address — behave identically; what changes is where records are stored and a small amount of one-time setup. This page collects everything that differs so you can set up either runtime correctly.

What differs

NodeBrowser
Extra importimport "wative-core/node"none
Storageencrypted files on diskIndexedDB
Where it livesWorkspace.open({ path }), or the default locationIdbProvider.create(name)
Extra setupnoneBuffer polyfill

Everything else — creating accounts, deriving wallets, signing, transactions — is written once and runs in both.

Node: the side-effect import

In Node you import the package for its side effect once, anywhere before you open a workspace:

server.ts
import { Workspace } from "wative-core";
import "wative-core/node"; // registers the filesystem backends

const ws = await Workspace.open({ password: "your-workspace-password" });

Importing wative-core/node registers the filesystem backends with the core, which is what lets Workspace.open() accept a path — or no argument at all — and write encrypted files to disk. Without that import, opening a workspace in Node throws, and the error tells you to add it.

wative-core/node also re-exports the Node-only classes HybridProvider, HybridProviderV3, and FileSink; for convenience these are re-exported from the package root in Node as well. See Node providers.

Browser: storage and setup

The browser build stores records in IndexedDB in the current origin. There is no side-effect import — you build an IdbProvider and pass it to Workspace.open().

Add the Buffer polyfill

The Solana libraries read a global Buffer, which browsers do not provide. Install it:

pnpm add buffer
npm i buffer

Then, once, before importing wative-core:

polyfill.ts
import { Buffer } from "buffer";
globalThis.Buffer = Buffer;

Open with an IdbProvider

app.ts
import { Workspace, IdbProvider } from "wative-core";

const provider = await IdbProvider.create("my-dapp");
const ws = await Workspace.open({ provider, password: "your-workspace-password" });

Take the provider from the same entry point

Each entry point of the package is a separate bundle with its own class identities and module state. A provider built by one is not recognized by a Workspace from another, so import both from the same specifier:

// Right — one entry point.
import { Workspace, HybridProvider } from "wative-core";
const ws = await Workspace.open(new HybridProvider("~/wallets"), password);
// Wrong — rejected with a PARAMETER_ERROR that names the fix.
import { Workspace } from "wative-core";
import { HybridProvider } from "wative-core/node";

A bare import "wative-core/node" for its side effects is unaffected by this — the rule is only about handing a provider instance across entry points.

Content-Security-Policy and WebAssembly

Key derivation runs on a bundled WebAssembly build by default. In a browser with a Content-Security-Policy, grant 'wasm-unsafe-eval' in script-src, or derivation silently degrades to a pure-JS path that produces identical keys roughly 17x slower. The degradation is announced once on the console, and you can detect it programmatically:

import { argon2BackendInfo } from "wative-core";

argon2BackendInfo();
// -> { backend: "wasm" | "noble" | "unresolved", wasm, reason?, overrides }

argon2BackendInfo() is a pure read that resolves nothing, so before the first unlock it honestly answers "unresolved".

Runtime support

Where to go next

Last updated on