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:
await loadWasm(...)
- construct
new SigbashClient(...)
await client.getKey(keyId)
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:
- Creates an updateable date-only signet key.
- Spawns a fresh Node process.
- 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.
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:
This makes the documented future policy-bump flow unreliable. The important use case is creating an updateable policy-gated key, saving its
bip328Xpubin a descriptor, then later bumping the policy date without changing the xpub/descriptor.Environment
@sigbash/sdk0.6.1v22.22.3https://www.sigbash.com/sigbash.wasmExpected Behavior
In a fresh Node process:
await loadWasm(...)new SigbashClient(...)await client.getKey(keyId)await client.updatePolicy(keyId, JSON.stringify(newPolicy))Expected result:
policyRootchangesbip328Xpubremains unchangedpolicyUpdateCountincreasesActual Behavior
Fresh-process update fails:
Against a clean upstream
0.6.1source build, the stack line was:Same-Process Control Test
A create-then-update flow in the same Node process succeeded:
Example successful result:
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:
Same-process control against the same clean source build:
Minimal Reproduction
Save reproduce-update-policy-fresh-session.mjs and run it in a project with
@sigbash/sdkinstalled:Same-process control:
The script:
updatePolicy().The child process fails with
SeedManager not initialized.Additional Probe
I also probed the direct WASM
updatePolicyexport 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: trueas 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.