Opening a Workspace
Workspace.open() in its OpenOptions and positional forms, automatic create-vs-open with folder validation, and the three-tier path resolution (WATIVE_WORKSPACE_PATH, ./.wative2, ~/.wative2).
Workspace.open() is the single entry point for both creating a new workspace and opening an existing one — there is no separate create step. It auto-selects based on what it finds at the target location, resolves a default path when you give none, and returns either an unlocked or a locked workspace depending on whether you pass a password.
import { Workspace } from "wative-core";
// Options-object form
const ws = await Workspace.open({ path: "./my-wallet", password: "wsp-pwd" });OpenOptions object vs positional
There are two co-existing call shapes.
Options object — the recommended form, taking an OpenOptions bag:
const ws = await Workspace.open({ path: "./my-wallet", password: "wsp-pwd" });| Field | Type | Notes |
|---|---|---|
provider | Provider | Custom storage backend. Mutually exclusive with path. |
path | string | Root path for the default provider. Mutually exclusive with provider. Omit both for the default location. |
password | string | Workspace password. Omit to return a locked workspace and call unlock(pwd) later. |
Passing both provider and path throws a WativeError with code === "PARAMETER_ERROR".
Positional — open(providerOrRoot?, password?), where the first argument is a Provider instance, a root-path string, or omitted:
const ws = await Workspace.open("./my-wallet", "wsp-pwd");A third positional create argument is accepted for backward compatibility but is deprecated and ignored — create-vs-open is now automatic.
The backend resolves as:
- a
Providerinstance — used as-is, and the caller keeps ownership of it, OR - a
stringroot path — wrapped in the default provider for the host, OR - omitted — resolved via the three-tier default-path strategy below.
A Provider must be imported from the same entry point as Workspace. Each entry point (wative-core, wative-core/node, …) is a self-contained bundle with its own class identities, so a provider from one handed to a Workspace from another is refused with PARAMETER_ERROR. Import both from one place, e.g. import { Workspace, HybridProvider } from "wative-core".
Create-vs-open decision
When a password is supplied, open() inspects the target's structural state before unlocking and picks the safe action:
| Target | Action |
|---|---|
| An existing wative workspace | Open it and verify the password. A wrong password throws BAD_PASSWORD. |
| Empty, or does not exist | Create a new workspace there. |
| A non-empty location that is not a wative workspace | Refuse with PARAMETER_ERROR rather than scaffolding a workspace into unrelated data. |
That last row is the folder-validation guard: it stops a mistyped path from writing workspace files into a directory that already holds something else.
// First call: directory is empty → creates the workspace.
let ws = await Workspace.open({ path: "./my-wallet", password: "wsp-pwd" });
await ws.lock();
// Second call: same directory is now a workspace → opens it.
ws = await Workspace.open({ path: "./my-wallet", password: "wsp-pwd" });If the provider is already held by another live Workspace, open() refuses with UNSUPPORTED_OP — two workspaces on one container would overwrite each other's records. Call close() on the first workspace, or construct a separate provider.
Three-tier path resolution
When you call open() with no path and no provider, the default location is resolved on a filesystem host in this order:
WATIVE_WORKSPACE_PATH — if this environment variable is set and non-empty (trimmed), it always wins. A leading ~ / ~/ is expanded by the default provider.<cwd>/.wative2 — the .wative2 directory under the current working directory, preferred when it already exists and is not a symlink.<home>/.wative2 — the .wative2 directory under the user's home directory, the last-resort fallback and where a fresh machine lands.A symlinked <cwd>/.wative2 is deliberately skipped — resolution falls through to the home tier rather than following the link. This defends against attacker-controlled redirection on shared systems where another user can write to the working directory but not to your home directory. To use a symlinked path intentionally, set WATIVE_WORKSPACE_PATH or pass the path explicitly.
Default-path resolution requires a Node.js runtime — it reads process.env, process.cwd(), and the home directory. In a non-Node runtime you must pass an explicit path or provider; otherwise open() throws PARAMETER_ERROR.
Unlocked vs locked open
Whether a password is present decides what state the returned workspace is in.
// No password → a locked workspace; unlock it later.
const ws = await Workspace.open({ path: "./my-wallet" });
ws.locked; // true
await ws.unlock("wsp-pwd");
ws.locked; // falseA no-password open() never hydrates and never inspects the container's contents — it just returns a locked handle. A password, if supplied, must be a non-empty string or open() throws PARAMETER_ERROR. See the Workspace lifecycle for what unlock, lock, and close do next.