WorkspaceWorkspace

Workspace

The Workspace class — the top-level encrypted container holding accounts, networks, assets, config and a logger — with open/unlock/lock/close and the locked/closed lifecycle.

A Workspace is the top-level encrypted container. Everything else in the library hangs off it: the accounts it holds, the networks they transact on, the imported assets, the workspace-wide config, and a logger. You obtain one with Workspace.open() and drive it through a small session lifecycle — unlock, lock, close.

open-and-use.ts
import { Workspace } from "wative-core";

const ws = await Workspace.open({ path: "./my-wallet", password: "wsp-pwd" });

const account = await ws.accounts.create("Trading Desk", "wsp-pwd", MNEMONIC);
console.log(ws.accounts.length); // 1

await ws.lock();

Lifecycle

A workspace is always in one of three states, exposed by two boolean getters:

GetterTypeMeaning
ws.lockedbooleantrue when there is no live session. A freshly opened no-password workspace is locked; so is one after lock().
ws.closedbooleantrue after close(). Terminal — the backing store has been released and cannot be reopened. Every closed workspace also reads as locked, so code that only checks locked still behaves.

The transitions:

OpenWorkspace.open(...) returns an unlocked workspace when a password is supplied, or a locked one when it is omitted.
Unlockawait ws.unlock(password) starts a live session on a locked (but not closed) workspace.
Lockawait ws.lock() ends the session and wipes in-memory secrets. The workspace can be unlocked again.
Closeawait ws.close() ends the session and releases the provider's store. The workspace is finished.

ws.accounts, ws.networks, ws.logger

These are the primary handles on a live workspace. Each is a getter that requires an unlocked session — reading any of them while locked throws WORKSPACE_LOCKED.

AccessorTypeNotes
ws.accountsAccountCollectionThe account registry. See Accounts.
ws.networksNetworkCollectionBuilt-in plus user-added networks. See Networks.
ws.loggerLoggerWorkspace-scoped logger, materialized on first read after unlock.
ws.configWorkspaceConfigA frozen snapshot of workspace settings — see Workspace Config.
ws.rootDescriptorstringA human-readable descriptor of where this workspace lives.
ws.damagedAccountSlugsReadonlyArray<string>Slugs of account records present on disk that could not be opened (read-only).

ws.accounts and ws.networks are read-only accessors over private backing fields. There is no setter: ws.accounts = [] throws a TypeError in strict mode rather than silently replacing the collection.

The logger auto-persists its configuration. Mutating it — setLevel, setSinks — writes the new LoggerConfig back through the provider, so it survives a lock/reopen cycle:

logger-persists.ts
await ws.logger.setLevel("debug");

await ws.lock();
const reopened = await Workspace.open({ path: "./my-wallet", password: "wsp-pwd" });
reopened.logger.config.minLevel; // "debug"

Stop emission entirely with ws.logger.setSinks([]).

lock() vs close()

Both end the current session; the difference is what happens to the storage backend.

lock()close()
Wipes in-memory secrets (password, mnemonics, keys)yesyes
Closes logger sinksyesyes
Releases the provider claimyesyes
Reopenable with unlock(password)yesno
Calls Provider.close() (releases the store)noyes
State afterwardlocked === trueclosed === true

close() is idempotent and single-flight — concurrent calls share one teardown and all resolve when it is done. Calling lock() on an already-closed workspace is a no-op, not an error.

Re-read handles after unlock

lock() replaces the account and network collections with fresh, empty ones and wipes the previous session's state. Any reference you captured before locking is now stale, and the same is true across an idle-lock followed by unlock() — hydration builds fresh instances. Always re-read the handle from the workspace after a session boundary rather than reusing a cached reference.

reopen-and-reread.ts
let ws = await Workspace.open({ path: "./my-wallet", password: "wsp-pwd" });
const hd = await ws.accounts.create("Desk One", "wsp-pwd", MNEMONIC);

await ws.lock();
ws = await Workspace.open({ path: "./my-wallet", password: "wsp-pwd" });

// Re-read the account off the reopened workspace — `hd` above is stale.
const reopened = ws.accounts.bySlug(hd.slug);

What a stale handle does if you do reuse it is deliberately loud, never silent:

  • A cached ws.networks collection refuses mutations with UNSUPPORTED_OP ("this networks collection is no longer live"). Re-read ws.networks.
  • A cached account or address handle refuses operations that need key material with ACCOUNT_LOCKED, even while the reopened workspace is unlocked and holds a fresh twin.

Last updated on