Skip to content

updatePolicy() fails in a fresh Node process with SeedManager not initialized #1

Description

@billerickson

client.updatePolicy() works when called in the same Node process that just created the key, but fails when called later from a fresh Node process against the same updateable key.

The failing fresh-session path returns:

SigbashSDKError: SeedManager not initialized

This makes the documented future policy-bump flow unreliable. The important use case is creating an updateable policy-gated key, saving its bip328Xpub in a descriptor, then later bumping the policy date without changing the xpub/descriptor.

Environment

  • SDK: @sigbash/sdk 0.6.1
  • Node: v22.22.3
  • Network tested: signet
  • WASM loaded from: https://www.sigbash.com/sigbash.wasm
  • OS tested: macOS
  • Also reproduced against a clean upstream source build, not only a locally modified checkout.

Expected Behavior

In a fresh Node process:

  1. await loadWasm(...)
  2. construct new SigbashClient(...)
  3. await client.getKey(keyId)
  4. await client.updatePolicy(keyId, JSON.stringify(newPolicy))

Expected result:

  • policy update succeeds
  • policyRoot changes
  • bip328Xpub remains unchanged
  • policyUpdateCount increases

Actual Behavior

Fresh-process update fails:

SigbashSDKError: SeedManager not initialized
    at SigbashClient.updatePolicy (.../dist/index.mjs:4504:19)

Against a clean upstream 0.6.1 source build, the stack line was:

SigbashSDKError: SeedManager not initialized
    at SigbashClient.updatePolicy (.../dist/index.mjs:4489:19)

Same-Process Control Test

A create-then-update flow in the same Node process succeeded:

updateableConfirmed: true
xpubUnchanged: true
policyRootChanged: true
policyUpdateCount: 1

Example successful result:

initialUnlockUnix: 1798761600
updatedUnlockUnix: 1830297600
xpubUnchanged: true
policyRootChanged: true

So the updateable-key mechanism appears to preserve the xpub correctly; the failure seems specific to restoring enough WASM/SeedManager state in a later process.

Clean Upstream Output

Fresh-session failure against a clean source build:

SDK version: 0.6.1
Creating signet updateable key at keyIndex=0
[Sigbash WASM] Initializing...
[Sigbash WASM] Exports registered. Module ready.
{
  "createdKeyId": "0",
  "keyIndex": 0,
  "policyRoot": "92f98792d2dbbdd985ca257ecdbbd9608c7c4676389ae554e2d2a82c738b96b3",
  "bip328Xpub": "[ae014264]tpubD6NzVbkrYhZ4XgHkCEtfpuZPJDLaLPxu5ZBEtAbub9GcUX1mTS2t3eCnBYi2UmkBu8634Cr9ndCVFCRRp6YxSDjdc5rWGhdHSvVGTYpMHxR"
}

Spawning a fresh Node process to update the policy...
[Sigbash WASM] Initializing...
[Sigbash WASM] Exports registered. Module ready.

SigbashSDKError: SeedManager not initialized

Same-process control against the same clean source build:

SDK version: 0.6.1
Creating signet updateable key at keyIndex=1
[Sigbash WASM] Initializing...
[Sigbash WASM] Exports registered. Module ready.
{
  "createdKeyId": "1",
  "keyIndex": 1,
  "policyRoot": "92f98792d2dbbdd985ca257ecdbbd9608c7c4676389ae554e2d2a82c738b96b3",
  "bip328Xpub": "[cee13b9e]tpubD6NzVbkrYhZ4XgHkCEtfpuZPJDLaLPxu5ZBEtAbub9GcUX1mTS2t3eCnBZT6AcWwsxyjK1P4Z9dqJes6q5XC9hY3EgGaUZTqDaH8izqG4dD"
}

Updating in the same Node process...
{
  "mode": "same-process-control",
  "keyId": "1",
  "xpubUnchanged": true,
  "policyRootChanged": true,
  "after": {
    "policyRoot": "10049c2b8270eb9c4fa3e53041817752a5eee76fde22bc52f9b66b78b772a461",
    "bip328Xpub": "[cee13b9e]tpubD6NzVbkrYhZ4XgHkCEtfpuZPJDLaLPxu5ZBEtAbub9GcUX1mTS2t3eCnBZT6AcWwsxyjK1P4Z9dqJes6q5XC9hY3EgGaUZTqDaH8izqG4dD",
    "policyUpdateCount": 1
  }
}

Minimal Reproduction

Save reproduce-update-policy-fresh-session.mjs and run it in a project with @sigbash/sdk installed:

node reproduce-update-policy-fresh-session.mjs --network=signet --key-index=<unused-index>

Same-process control:

node reproduce-update-policy-fresh-session.mjs --network=signet --key-index=<unused-index> --same-process-control

The script:

  1. Creates an updateable date-only signet key.
  2. Spawns a fresh Node process.
  3. The child process loads WASM, fetches the key, and calls updatePolicy().

The child process fails with SeedManager not initialized.

Additional Probe

I also probed the direct WASM updatePolicy export in a fresh process after fetching the key KMC and compiling the new policy with the same HKDF-derived seed used by the SDK.

Observed direct-WASM results:

{
  "updatePolicyLength": 0,
  "compilePolicyLength": 0,
  "attempts": [
    {
      "label": "sdk-json-raw-policy",
      "ok": false,
      "result": { "error": "SeedManager not initialized" }
    },
    {
      "label": "sdk-json-compiled-policy",
      "ok": false,
      "result": { "error": "SeedManager not initialized" }
    },
    {
      "label": "positional-raw-policy",
      "ok": false,
      "result": { "error": "kmc_json is required" }
    },
    {
      "label": "positional-compiled-policy",
      "ok": false,
      "result": { "error": "kmc_json is required" }
    }
  ]
}

This suggests the SDK JSON calling convention is correct, but the fresh-session WASM update path does not initialize or restore the SeedManager state required by updatePolicy().

Why This Matters

The docs describe updateable: true as the way to update policy without changing the on-chain address, aggregate key, bip328Xpub, or descriptor. That is exactly the behavior needed for long-lived wallets whose policy date may need to be bumped.

The same-process test confirms the xpub remains stable, but the real operational flow is a future fresh-session update.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions