@fased/fased 0.1.68 → 0.1.69

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +20 -22
  3. package/config/sat-runtime.env +6 -2
  4. package/config/signer-protocol-v2.json +6 -0
  5. package/dist/agents/tools/wallet-action-tool.js +2 -0
  6. package/dist/agents/tools/wallet-tool.js +5 -3
  7. package/dist/brand.js +1 -1
  8. package/dist/build-info.json +3 -3
  9. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  10. package/dist/cli/program/register.wallet.js +88 -5
  11. package/dist/commands/wallet.js +842 -57
  12. package/dist/config/sat-runtime-ids.js +6 -1
  13. package/dist/control-ui/assets/{app-DyPTS0oM.js → app-BLFLoFPk.js} +8 -8
  14. package/dist/control-ui/assets/{federation-DgA9e-Pn.js → federation-SGMAnM_8.js} +2 -2
  15. package/dist/control-ui/assets/{index-Cu13yYHr.js → index-S88kp5ZQ.js} +3 -3
  16. package/dist/control-ui/assets/{mining-Dg-3ll0F.js → mining-ClI8RW4u.js} +2 -2
  17. package/dist/control-ui/assets/{wallet-ByKBcYhm.js → wallet-CRIhDQbW.js} +201 -116
  18. package/dist/control-ui/index.html +1 -1
  19. package/dist/control-ui/version.json +1 -1
  20. package/dist/gateway/server-http.js +46 -9
  21. package/dist/mining/mainnet-sync.js +67 -5
  22. package/dist/operator/operator-readiness.js +1 -1
  23. package/dist/plugin-sdk/{audio-preflight-_FVQjR1r.js → audio-preflight-CS7Q_7-R.js} +2 -2
  24. package/dist/plugin-sdk/channel-plugin-common.d.ts +2 -2
  25. package/dist/plugin-sdk/{chunk-items-LH4uKSnh.js → chunk-items-BLBZNORg.js} +1 -1
  26. package/dist/plugin-sdk/command-status.js +2 -2
  27. package/dist/plugin-sdk/{config-schema-DjU_uD-6.js → config-schema-BiBOUW89.js} +255 -47
  28. package/dist/plugin-sdk/device-pair.d.ts +1 -1
  29. package/dist/plugin-sdk/discord.d.ts +4 -4
  30. package/dist/plugin-sdk/index.d.ts +16 -16
  31. package/dist/plugin-sdk/index.js +2 -2
  32. package/dist/plugin-sdk/{monitor-DUMMbG4h.js → monitor-WXdn4mR-.js} +4 -4
  33. package/dist/plugin-sdk/{monitor-DXIM16Hj.js → monitor-xzVAPbg2.js} +6 -6
  34. package/dist/plugin-sdk/{openresponses-http-C8lKwta1.js → openresponses-http-DnI1LFgq.js} +2 -2
  35. package/dist/plugin-sdk/{pairing-message-mKO92L-M.d.ts → pairing-message-CiTcdD6m.d.ts} +1 -1
  36. package/dist/plugin-sdk/provider-web-search-config-contract.d.ts +1 -1
  37. package/dist/plugin-sdk/{pw-ai-DJ_gTc4M.js → pw-ai-DZYLDC-S.js} +1 -1
  38. package/dist/plugin-sdk/{runtime-dependency-nup8niQL.js → runtime-dependency-DczkvPxn.js} +9 -9
  39. package/dist/plugin-sdk/sat-runtime.d.ts +89 -10
  40. package/dist/plugin-sdk/sat-runtime.js +2 -2
  41. package/dist/plugin-sdk/{slack-B-nOOLM0.js → slack-CkIRVDUK.js} +4 -4
  42. package/dist/plugin-sdk/slack.d.ts +4 -4
  43. package/dist/plugin-sdk/{sticker-cache-BLiId8Em.js → sticker-cache-Dpap5ce_.js} +2 -2
  44. package/dist/plugin-sdk/{telegram-actions-1X9A32Dn.js → telegram-actions-Cewdga8H.js} +3 -3
  45. package/dist/plugin-sdk/telegram.d.ts +4 -4
  46. package/dist/plugin-sdk/{tmp-fased-dir-BkpWlGUe.d.ts → tmp-fased-dir-BSEHc8MQ.d.ts} +2 -2
  47. package/dist/plugin-sdk/{types-DtCOO_hW.d.ts → types-BakzIzGN.d.ts} +1 -1
  48. package/dist/plugin-sdk/{types-B-zL9gSv.d.ts → types-CzF0YQqK.d.ts} +2 -2
  49. package/dist/plugin-sdk/{web-BvjNyYBz.js → web-DGWi7i3B.js} +2 -2
  50. package/dist/plugin-sdk/whatsapp.d.ts +9 -9
  51. package/dist/plugin-sdk/{zod-schema.providers-core-BCDlgtls.d.ts → zod-schema.providers-core-CIzCjXH9.d.ts} +63 -63
  52. package/dist/wallet/local-socket-signer-lifecycle.js +79 -3
  53. package/dist/wallet/local-socket-signer-protocol.js +70 -10
  54. package/dist/wallet/mining-wallet-retirement.js +256 -0
  55. package/dist/wallet/providers/local-socket-signer-adapter.js +2 -0
  56. package/dist/wallet/signer-protocol-v2.generated.js +6 -0
  57. package/dist/wallet/wallet-agent-selection.js +6 -10
  58. package/dist/wallet/wallet-provider-registry.js +70 -29
  59. package/dist/wallet/wallet-send-approvals.js +6 -2
  60. package/dist/wallet/wallet-status.js +56 -1
  61. package/dist/wizard/onboarding.js +32 -30
  62. package/dist/wizard/onboarding.wallet.js +10 -1
  63. package/extensions/acpx/package.json +1 -1
  64. package/extensions/bluebubbles/package.json +1 -1
  65. package/extensions/copilot-proxy/package.json +1 -1
  66. package/extensions/diagnostics-otel/package.json +1 -1
  67. package/extensions/fased-federation/package.json +1 -1
  68. package/extensions/google-gemini-cli-auth/package.json +1 -1
  69. package/extensions/imessage/package.json +1 -1
  70. package/extensions/irc/package.json +1 -1
  71. package/extensions/line/package.json +1 -1
  72. package/extensions/llm-task/package.json +1 -1
  73. package/extensions/lobster/package.json +1 -1
  74. package/extensions/matrix/CHANGELOG.md +6 -0
  75. package/extensions/matrix/package.json +1 -1
  76. package/extensions/mattermost/package.json +1 -1
  77. package/extensions/memory-core/package.json +2 -2
  78. package/extensions/memory-lancedb/package.json +1 -1
  79. package/extensions/minimax-portal-auth/package.json +1 -1
  80. package/extensions/msteams/CHANGELOG.md +6 -0
  81. package/extensions/msteams/package.json +1 -1
  82. package/extensions/nextcloud-talk/package.json +1 -1
  83. package/extensions/nostr/CHANGELOG.md +6 -0
  84. package/extensions/nostr/package.json +1 -1
  85. package/extensions/open-prose/package.json +1 -1
  86. package/extensions/sat-mining/package.json +1 -1
  87. package/extensions/signal/package.json +1 -1
  88. package/extensions/synology-chat/package.json +1 -1
  89. package/extensions/tlon/package.json +1 -1
  90. package/extensions/twitch/CHANGELOG.md +6 -0
  91. package/extensions/twitch/package.json +1 -1
  92. package/extensions/voice-call/CHANGELOG.md +6 -0
  93. package/extensions/voice-call/package.json +1 -1
  94. package/extensions/zalo/CHANGELOG.md +6 -0
  95. package/extensions/zalo/package.json +1 -1
  96. package/extensions/zalouser/CHANGELOG.md +6 -0
  97. package/extensions/zalouser/package.json +1 -1
  98. package/install.sh +276 -103
  99. package/package.json +4 -3
  100. package/scripts/install-fased-signerd.sh +4 -8
  101. package/scripts/install-runtime-profile.sh +27 -0
  102. package/scripts/start-managed.sh +13 -1
  103. package/scripts/fased-signer-wallet-import-hosting.sh +0 -78
@@ -3,15 +3,16 @@ import { loadConfig, writeConfigFile } from "../config/io.js";
3
3
  import "../config/config.js";
4
4
  import { hasLegacyEmbeddedKeystoreConfig, hasLegacyEmbeddedKeystoreMaterialHint, throwLegacyEmbeddedKeystoreMigrationRequired } from "../wallet/legacy-embedded-keystore.js";
5
5
  import { ensureWalletStateDir, isLocalSignerExternallyManaged, resolveLocalSignerControlSocketPath, resolveLocalSignerMasterKeyPath, resolveLocalSignerSidecarPaths, resolveLocalSignerSocketPath, resolveLocalSignerStateDbPath } from "../wallet/wallet-runtime-config.js";
6
- import { normalizeWalletUserRole, readWalletProviderRegistry, resolveWalletUserRole, setDefaultWallet, setNamedWalletRole, setWalletProviderEnabled, upsertNamedWallet } from "../wallet/wallet-provider-registry.js";
6
+ import { normalizeWalletUserRole, readWalletProviderRegistry, replaceRetiredMiningWallet, resolveWalletUserRole, setDefaultWallet, setNamedWalletRole, setWalletProviderEnabled, upsertNamedWallet, writeWalletProviderRegistry } from "../wallet/wallet-provider-registry.js";
7
7
  import { buildWalletProviderCapabilityMatrix } from "../wallet/wallet-provider-capabilities.js";
8
8
  import { redactWalletDiagnosticText } from "../wallet/wallet-redaction.js";
9
9
  import { callLocalSocketSigner, probeLocalSocketSignerHealth, requireLocalSocketSignerPath } from "../wallet/providers/local-socket-signer-adapter.js";
10
10
  import { readWalletProviderSecretStatus, saveWalletProviderSecret } from "../wallet/wallet-secrets-store.js";
11
11
  import { createWalletProviderAdapter, resolveWalletProviderId } from "../wallet/wallet-provider-resolver.js";
12
- import { readWalletStatusSnapshot, resolveWalletConfigForRuntime } from "../wallet/wallet-status.js";
13
- import { createLockedSignerOwnedWallet } from "../wallet/local-socket-signer-lifecycle.js";
12
+ import { activateSignerOwnedRoleBaseline, createRoleReadySignerOwnedWallet, readSignerOwnedWallet, readSignerOwnedWalletReadiness } from "../wallet/local-socket-signer-lifecycle.js";
14
13
  import { normalizeNativeSignerWalletId } from "../wallet/native-signer-wallet-id.js";
14
+ import { readWalletStatusSnapshot, resolveWalletConfigForRuntime } from "../wallet/wallet-status.js";
15
+ import { buildMiningRetirementEvidence, verifyMiningRecoveryPackage, writeMiningRetirementReceipt } from "../wallet/mining-wallet-retirement.js";
15
16
  import { configureSignerOwnedWalletNetwork } from "../wallet/signer-network-admin.js";
16
17
  import { listWalletInboundEvents, pollWalletInboundEvents, reconcileWalletInboundEvents } from "../wallet/wallet-inbound-events.js";
17
18
  import { buildWalletCanaryReport, runWalletProviderCanaryReport } from "../wallet/wallet-canary.js";
@@ -411,7 +412,14 @@ async function createSignerOwnedWalletForSetup(params) {
411
412
  }
412
413
  let result;
413
414
  try {
414
- result = await createLockedSignerOwnedWallet({
415
+ result = hosted && params.env.FASED_GATEWAY_SERVICE !== "1" ? invokeNativeSignerWalletCreate({
416
+ signerBinPath: "/opt/fased/signer/fased-signerd",
417
+ operatorSocketPath: "/run/fased-signerd/operator.sock",
418
+ walletId: expectedSignerWalletId,
419
+ role: params.role,
420
+ allowExisting: Boolean(params.options.force),
421
+ env: mergedEnv
422
+ }) : await createRoleReadySignerOwnedWallet({
415
423
  socketPath,
416
424
  walletId,
417
425
  role: params.role,
@@ -419,7 +427,7 @@ async function createSignerOwnedWalletForSetup(params) {
419
427
  });
420
428
  } catch (error) {
421
429
  if (hosted) {
422
- throw new Error(`root-managed hosted signer wallet creation failed: ${error instanceof Error ? error.message : String(error)}. Repair only from the provider root console with the exact tagged, attested Hosting release; never run the app checkout with sudo.`, { cause: error });
430
+ throw new Error(`hosted signer wallet creation failed: ${error instanceof Error ? error.message : String(error)}. Confirm the operator socket and exact installed signer release are healthy, then retry the same wallet ID.`, { cause: error });
423
431
  }
424
432
  throw error;
425
433
  }
@@ -431,7 +439,16 @@ async function createSignerOwnedWalletForSetup(params) {
431
439
  if (signerIdCollision) {
432
440
  throw new Error(`native signer wallet ID ${signerWalletId} is already registered as ${signerIdCollision.id}; choose a distinct wallet ID`);
433
441
  }
434
- const network = await configureSignerOwnedWalletNetwork({
442
+ const operatorLifecycle = hosted && params.env.FASED_GATEWAY_SERVICE !== "1";
443
+ const network = operatorLifecycle ? invokeNativeSignerNetworkSetPrimary({
444
+ signerBinPath: "/opt/fased/signer/fased-signerd",
445
+ socketFlag: "--operator-socket",
446
+ socketPath: "/run/fased-signerd/operator.sock",
447
+ walletId: signerWalletId,
448
+ primaryRpcUrl: params.rpcUrl,
449
+ expectedVersion: 0,
450
+ env: mergedEnv
451
+ }) : await configureSignerOwnedWalletNetwork({
435
452
  walletId: signerWalletId,
436
453
  primaryRpcUrl: params.rpcUrl,
437
454
  env: mergedEnv,
@@ -439,6 +456,19 @@ async function createSignerOwnedWalletForSetup(params) {
439
456
  });
440
457
  cfg = setConfigEnvVar(cfg, rpcEnvKeyFor(params.chain, walletId), params.rpcUrl);
441
458
  await writeConfigFile(cfg);
459
+ const readiness = operatorLifecycle ? invokeNativeSignerWalletReadiness({
460
+ signerBinPath: "/opt/fased/signer/fased-signerd",
461
+ socketFlag: "--operator-socket",
462
+ socketPath: "/run/fased-signerd/operator.sock",
463
+ walletId: signerWalletId,
464
+ env: mergedEnv
465
+ }) : await readSignerOwnedWalletReadiness({
466
+ socketPath,
467
+ walletId: signerWalletId
468
+ });
469
+ if (!readiness.ready || readiness.walletId !== signerWalletId || readiness.publicKey !== result.wallet.publicKey || readiness.role !== params.role || readiness.baselineVersion !== 1 || readiness.policyHash !== result.policy.hash || readiness.policyVersion !== result.policy.version || readiness.networkHash !== network.hash || readiness.networkVersion !== network.version) {
470
+ throw new Error(`Setup incomplete: signer readiness does not match the created ${params.role} wallet lifecycle`);
471
+ }
442
472
  const wallet = upsertNamedWallet({
443
473
  walletId,
444
474
  name: params.options.walletName || "Wallet",
@@ -451,19 +481,16 @@ async function createSignerOwnedWalletForSetup(params) {
451
481
  signerWalletId,
452
482
  policyHash: result.policy.hash,
453
483
  policyVersion: result.policy.version,
454
- policyState: "locked",
484
+ baselineVersion: readiness.baselineVersion,
485
+ policyState: "ready",
455
486
  networkHash: network.hash,
456
487
  networkVersion: network.version,
457
- networkReady: network.ready
488
+ networkReady: network.ready,
489
+ operationLane: readiness.operationLane,
490
+ roleReady: readiness.ready
458
491
  },
459
492
  env: params.env
460
493
  });
461
- if (params.role === "agent") {
462
- setDefaultWallet({
463
- walletId: wallet.id,
464
- env: params.env
465
- });
466
- }
467
494
  if (params.role === "mining") {
468
495
  const currentEntry = cfg.plugins?.entries?.["sat-mining"];
469
496
  const currentSatConfig = currentEntry?.config && typeof currentEntry.config === "object" && !Array.isArray(currentEntry.config) ? currentEntry.config : {};
@@ -496,15 +523,18 @@ async function createSignerOwnedWalletForSetup(params) {
496
523
  address: result.wallet.publicKey,
497
524
  policyHash: result.policy.hash,
498
525
  policyVersion: result.policy.version,
499
- policyState: "locked",
526
+ baselineVersion: readiness.baselineVersion,
527
+ policyState: "ready",
500
528
  networkVersion: network.version,
501
- networkReady: network.ready
529
+ networkReady: network.ready,
530
+ operationLane: readiness.operationLane,
531
+ roleReady: readiness.ready
502
532
  }, null, 2));
503
533
  } else {
504
534
  params.runtime.log(`Signer wallet ID: ${signerWalletId}`);
505
535
  params.runtime.log(`${params.chain.toUpperCase()} address: ${result.wallet.publicKey}`);
506
536
  if (!params.options.noSignerHints) {
507
- params.runtime.log("Signer-owned wallet created with a deny-all policy. Configure explicit operations, destinations, assets, and positive caps before sending.");
537
+ params.runtime.log(`Role baseline active: ${params.role} v${readiness.baselineVersion} (${readiness.operationLane}).`);
508
538
  }
509
539
  }
510
540
  return result;
@@ -551,26 +581,23 @@ function parseNativeSignerImportResult(params) {
551
581
  throw new Error("native signer import returned an invalid result");
552
582
  }
553
583
  const result = value;
554
- if (result.wallet?.walletId !== params.walletId || typeof result.wallet.publicKey !== "string" || !result.wallet.publicKey.trim() || !Number.isSafeInteger(result.wallet.version) || result.policy?.walletId !== params.walletId || result.policy.role !== params.role || result.policy.version !== 1 || result.policy.operations?.length !== 0 || result.policy.programs?.length !== 0 || result.policy.assets?.length !== 0 || typeof result.policy.hash !== "string" || !/^sha256:[0-9a-f]{64}$/.test(result.policy.hash)) {
555
- throw new Error("native signer import did not return the exact deny-all wallet result");
584
+ if (result.wallet?.walletId !== params.walletId || typeof result.wallet.publicKey !== "string" || !result.wallet.publicKey.trim() || !Number.isSafeInteger(result.wallet.version) || result.policy?.walletId !== params.walletId || result.policy.role !== params.role || result.policy.version !== 1 || result.policy.baselineVersion !== 1 || !result.policy.operations?.length || !result.policy.programs?.length || !result.policy.assets?.length || typeof result.policy.hash !== "string" || !/^sha256:[0-9a-f]{64}$/.test(result.policy.hash)) {
585
+ throw new Error("native signer import did not return an active signer-owned role baseline");
556
586
  }
557
587
  return result;
558
588
  }
559
589
  function invokeNativeSignerWalletImport(params) {
560
- if (params.hosted) {
561
- throw new Error("Hosting private-key import is a signer-owner operation. Run /usr/local/sbin/fased-signer-wallet-import from the provider root console, then rerun wallet setup with Create and the same wallet ID to register the existing signer wallet.");
562
- }
563
590
  const input = requireOwnerOnlySignerImportFile(params.importFile);
564
591
  const command = params.signerBinPath;
565
592
  const args = [
566
593
  "admin",
567
594
  "wallet",
568
595
  "import",
569
- "--control-socket",
596
+ params.hosted ? "--operator-socket" : "--control-socket",
570
597
  params.controlSocketPath,
571
598
  "--wallet-id",
572
599
  params.walletId,
573
- "--locked-role",
600
+ "--baseline-role",
574
601
  params.role
575
602
  ];
576
603
  try {
@@ -605,21 +632,294 @@ function invokeNativeSignerWalletImport(params) {
605
632
  fs.closeSync(input.fd);
606
633
  }
607
634
  }
608
- function invokeNativeSignerRecoveryImport(params) {
609
- if (params.hosted) {
610
- throw new Error("Hosting recovery import is a signer-owner operation. Run it from the provider root console; the Gateway must not receive the recovery password.");
635
+ function nativeSignerLifecycleEnv(env) {
636
+ return {
637
+ HOME: env.HOME,
638
+ LANG: env.LANG || "C.UTF-8",
639
+ PATH: "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
640
+ };
641
+ }
642
+ function invokeNativeSignerWalletCreate(params) {
643
+ const child = spawnSync(params.signerBinPath, [
644
+ "admin",
645
+ "wallet",
646
+ "create",
647
+ "--operator-socket",
648
+ params.operatorSocketPath,
649
+ "--wallet-id",
650
+ params.walletId,
651
+ "--baseline-role",
652
+ params.role,
653
+ ...params.allowExisting ? ["--allow-existing"] : []
654
+ ], {
655
+ env: nativeSignerLifecycleEnv(params.env),
656
+ stdio: [
657
+ "ignore",
658
+ "pipe",
659
+ "pipe"
660
+ ],
661
+ encoding: "utf8",
662
+ maxBuffer: 256 * 1024,
663
+ timeout: 3e4
664
+ });
665
+ if (child.error) {
666
+ throw child.error;
667
+ }
668
+ if (child.status !== 0) {
669
+ throw new Error(redactWalletDiagnosticText(String(child.stderr || "native signer create failed").trim()));
670
+ }
671
+ return parseNativeSignerImportResult({
672
+ stdout: String(child.stdout ?? ""),
673
+ walletId: params.walletId,
674
+ role: params.role
675
+ });
676
+ }
677
+ function invokeNativeSignerNetworkSetPrimary(params) {
678
+ const input = JSON.stringify({ primaryRpcUrl: params.primaryRpcUrl });
679
+ const child = spawnSync(params.signerBinPath, [
680
+ "admin",
681
+ "network",
682
+ "set-primary",
683
+ params.socketFlag,
684
+ params.socketPath,
685
+ "--wallet-id",
686
+ params.walletId,
687
+ "--expected-version",
688
+ String(params.expectedVersion)
689
+ ], {
690
+ env: nativeSignerLifecycleEnv(params.env),
691
+ input,
692
+ stdio: [
693
+ "pipe",
694
+ "pipe",
695
+ "pipe"
696
+ ],
697
+ encoding: "utf8",
698
+ maxBuffer: 256 * 1024,
699
+ timeout: 6e4
700
+ });
701
+ if (child.error) {
702
+ throw child.error;
703
+ }
704
+ if (child.status !== 0) {
705
+ throw new Error(redactWalletDiagnosticText(String(child.stderr || "native signer RPC update failed").trim()));
706
+ }
707
+ let result;
708
+ try {
709
+ result = JSON.parse(String(child.stdout ?? ""));
710
+ } catch {
711
+ throw new Error("native signer RPC update returned invalid JSON");
712
+ }
713
+ const record = result;
714
+ if (!record || record.walletId !== params.walletId || record.configured !== true || record.ready !== true || !Number.isSafeInteger(record.version) || typeof record.hash !== "string" || !/^hmac-sha256:[0-9a-f]{64}$/.test(record.hash)) {
715
+ throw new Error("native signer RPC update returned an invalid readiness summary");
716
+ }
717
+ return {
718
+ walletId: String(record.walletId),
719
+ configured: true,
720
+ ready: true,
721
+ version: Number(record.version),
722
+ hash: String(record.hash)
723
+ };
724
+ }
725
+ function invokeNativeSignerWalletReadiness(params) {
726
+ const child = spawnSync(params.signerBinPath, [
727
+ "admin",
728
+ "wallet",
729
+ "readiness",
730
+ params.socketFlag,
731
+ params.socketPath,
732
+ "--wallet-id",
733
+ params.walletId
734
+ ], {
735
+ env: nativeSignerLifecycleEnv(params.env),
736
+ stdio: [
737
+ "ignore",
738
+ "pipe",
739
+ "pipe"
740
+ ],
741
+ encoding: "utf8",
742
+ maxBuffer: 256 * 1024,
743
+ timeout: 3e4
744
+ });
745
+ if (child.error) {
746
+ throw child.error;
611
747
  }
748
+ if (child.status !== 0) {
749
+ throw new Error(redactWalletDiagnosticText(String(child.stderr || "native signer readiness failed").trim()));
750
+ }
751
+ let result;
752
+ try {
753
+ result = JSON.parse(String(child.stdout ?? ""));
754
+ } catch {
755
+ throw new Error("native signer readiness returned invalid JSON");
756
+ }
757
+ const readiness = result;
758
+ if (!readiness || readiness.walletId !== params.walletId || typeof readiness.publicKey !== "string" || !Number.isSafeInteger(readiness.walletVersion) || typeof readiness.role !== "string" || !Number.isSafeInteger(readiness.policyVersion) || !Number.isSafeInteger(readiness.networkVersion) || typeof readiness.ready !== "boolean") {
759
+ throw new Error("native signer readiness returned an invalid result");
760
+ }
761
+ return readiness;
762
+ }
763
+ function invokeNativeSignerJSON(params) {
764
+ const child = spawnSync(params.signerBinPath, params.args, {
765
+ env: nativeSignerLifecycleEnv(params.env),
766
+ input: params.input,
767
+ stdio: [
768
+ params.input === undefined ? "ignore" : "pipe",
769
+ "pipe",
770
+ "pipe"
771
+ ],
772
+ encoding: "utf8",
773
+ maxBuffer: 512 * 1024,
774
+ timeout: 12e4
775
+ });
776
+ if (child.error) {
777
+ throw child.error;
778
+ }
779
+ if (child.status !== 0) {
780
+ throw new Error(redactWalletDiagnosticText(String(child.stderr || `${params.label} failed`).trim()));
781
+ }
782
+ let parsed;
783
+ try {
784
+ parsed = JSON.parse(String(child.stdout ?? ""));
785
+ } catch {
786
+ throw new Error(`${params.label} returned invalid JSON`);
787
+ }
788
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
789
+ throw new Error(`${params.label} returned an invalid result`);
790
+ }
791
+ return parsed;
792
+ }
793
+ function invokeNativeSignerBalance(params) {
794
+ const result = invokeNativeSignerJSON({
795
+ signerBinPath: params.signerBinPath,
796
+ args: [
797
+ "admin",
798
+ "wallet",
799
+ "balance",
800
+ params.socketFlag,
801
+ params.socketPath,
802
+ "--wallet-id",
803
+ params.walletId
804
+ ],
805
+ env: params.env,
806
+ label: "native signer balance lookup"
807
+ });
808
+ const balance = typeof result.balance === "string" ? result.balance : "";
809
+ if (result.address !== params.publicKey || result.chain !== "solana" || result.unit !== "lamports" || !/^\d+$/u.test(balance)) {
810
+ throw new Error("native signer balance lookup returned an invalid result");
811
+ }
812
+ return balance;
813
+ }
814
+ function parseNativeSignerRotation(value) {
815
+ if (typeof value.rotationId !== "string" || !/^sha256:[0-9a-f]{64}$/u.test(value.rotationId) || typeof value.sourceWalletId !== "string" || typeof value.sourcePublicKey !== "string" || typeof value.successorWalletId !== "string" || typeof value.successorPublicKey !== "string" || value.state !== "prepared" && value.state !== "committed" || !Number.isSafeInteger(value.version)) {
816
+ throw new Error("native signer rotation returned an invalid result");
817
+ }
818
+ return value;
819
+ }
820
+ function invokeNativeSignerRotationCreate(params) {
821
+ return parseNativeSignerRotation(invokeNativeSignerJSON({
822
+ signerBinPath: params.signerBinPath,
823
+ args: [
824
+ "admin",
825
+ "wallet",
826
+ "rotate-successor",
827
+ params.socketFlag,
828
+ params.socketPath,
829
+ "--wallet-id",
830
+ params.sourceWalletId,
831
+ "--successor-wallet-id",
832
+ params.successorWalletId,
833
+ "--expected-source-public-key",
834
+ params.sourcePublicKey,
835
+ "--expected-source-wallet-version",
836
+ String(params.sourceWalletVersion),
837
+ "--expected-source-policy-version",
838
+ String(params.sourcePolicyVersion)
839
+ ],
840
+ env: params.env,
841
+ label: "native signer Mining successor preparation"
842
+ }));
843
+ }
844
+ function invokeNativeSignerRotationStatus(params) {
845
+ try {
846
+ return parseNativeSignerRotation(invokeNativeSignerJSON({
847
+ signerBinPath: params.signerBinPath,
848
+ args: [
849
+ "admin",
850
+ "wallet",
851
+ "rotation-status",
852
+ params.socketFlag,
853
+ params.socketPath,
854
+ "--wallet-id",
855
+ params.sourceWalletId
856
+ ],
857
+ env: params.env,
858
+ label: "native signer Mining rotation status"
859
+ }));
860
+ } catch (error) {
861
+ const message = error instanceof Error ? error.message : String(error);
862
+ if (/rotation.*not found/iu.test(message)) {
863
+ return null;
864
+ }
865
+ throw error;
866
+ }
867
+ }
868
+ function invokeNativeSignerRotationCommit(params) {
869
+ const rotation = params.rotation;
870
+ return parseNativeSignerRotation(invokeNativeSignerJSON({
871
+ signerBinPath: params.signerBinPath,
872
+ args: [
873
+ "admin",
874
+ "wallet",
875
+ "rotation-commit",
876
+ params.socketFlag,
877
+ params.socketPath,
878
+ "--wallet-id",
879
+ rotation.sourceWalletId,
880
+ "--successor-wallet-id",
881
+ rotation.successorWalletId,
882
+ "--rotation-id",
883
+ rotation.rotationId,
884
+ "--expected-source-public-key",
885
+ rotation.sourcePublicKey,
886
+ "--expected-successor-public-key",
887
+ rotation.successorPublicKey,
888
+ "--expected-source-wallet-version",
889
+ String(rotation.prepareExpectedSourceWalletVersion),
890
+ "--expected-source-policy-version",
891
+ String(rotation.prepareExpectedSourcePolicyVersion),
892
+ "--expected-successor-wallet-version",
893
+ "1",
894
+ "--expected-successor-policy-version",
895
+ "1",
896
+ "--expected-rotation-version",
897
+ "1",
898
+ "--expected-successor-network-version",
899
+ String(params.successorNetworkVersion),
900
+ "--expected-successor-network-hash",
901
+ params.successorNetworkHash
902
+ ],
903
+ input: JSON.stringify({
904
+ recoveryPackageHash: params.recoveryPackageHash,
905
+ safetyEvidence: params.safetyEvidence
906
+ }),
907
+ env: params.env,
908
+ label: "native signer Mining retirement commit"
909
+ }));
910
+ }
911
+ function invokeNativeSignerRecoveryImport(params) {
612
912
  const input = requireOwnerOnlySignerImportFile(params.recoveryFile);
613
913
  try {
614
914
  const child = spawnSync(params.signerBinPath, [
615
915
  "admin",
616
916
  "wallet",
617
917
  "recovery-import",
618
- "--control-socket",
918
+ params.hosted ? "--operator-socket" : "--control-socket",
619
919
  params.controlSocketPath,
620
920
  "--wallet-id",
621
921
  params.walletId,
622
- "--locked-role",
922
+ "--baseline-role",
623
923
  params.role,
624
924
  "--recovery-file",
625
925
  input.path
@@ -678,7 +978,7 @@ async function importSignerOwnedWalletForSetup(params) {
678
978
  throw new Error(`native signer wallet ID ${signerWalletId} is already registered as ${collision.id}; choose a distinct wallet ID`);
679
979
  }
680
980
  const signerBinPath = hosted ? "/opt/fased/signer/fased-signerd" : resolveSignerdBinaryPath(mergedEnv);
681
- const controlSocketPath = hosted ? "/run/fased-signerd/control.sock" : resolveLocalSignerControlSocketPath(mergedEnv);
981
+ const controlSocketPath = hosted ? "/run/fased-signerd/operator.sock" : resolveLocalSignerControlSocketPath(mergedEnv);
682
982
  if (!hosted) {
683
983
  writeLocalSignerEnvFile({
684
984
  config: cfg,
@@ -706,7 +1006,15 @@ async function importSignerOwnedWalletForSetup(params) {
706
1006
  importFile: params.importFile,
707
1007
  env: mergedEnv
708
1008
  });
709
- const network = await configureSignerOwnedWalletNetwork({
1009
+ const network = hosted ? invokeNativeSignerNetworkSetPrimary({
1010
+ signerBinPath,
1011
+ socketFlag: "--operator-socket",
1012
+ socketPath: controlSocketPath,
1013
+ walletId: signerWalletId,
1014
+ primaryRpcUrl: params.rpcUrl,
1015
+ expectedVersion: 0,
1016
+ env: mergedEnv
1017
+ }) : await configureSignerOwnedWalletNetwork({
710
1018
  walletId: signerWalletId,
711
1019
  primaryRpcUrl: params.rpcUrl,
712
1020
  env: mergedEnv,
@@ -714,6 +1022,19 @@ async function importSignerOwnedWalletForSetup(params) {
714
1022
  });
715
1023
  cfg = setConfigEnvVar(cfg, rpcEnvKeyFor(params.chain, params.walletId), params.rpcUrl);
716
1024
  await writeConfigFile(cfg);
1025
+ const readiness = hosted ? invokeNativeSignerWalletReadiness({
1026
+ signerBinPath,
1027
+ socketFlag: "--operator-socket",
1028
+ socketPath: controlSocketPath,
1029
+ walletId: signerWalletId,
1030
+ env: mergedEnv
1031
+ }) : await readSignerOwnedWalletReadiness({
1032
+ socketPath,
1033
+ walletId: signerWalletId
1034
+ });
1035
+ if (!readiness.ready || readiness.publicKey !== result.wallet.publicKey || readiness.role !== params.role || readiness.baselineVersion !== 1 || readiness.policyHash !== result.policy.hash || readiness.policyVersion !== result.policy.version || readiness.networkHash !== network.hash || readiness.networkVersion !== network.version) {
1036
+ throw new Error(`Setup incomplete: signer readiness does not match the imported ${params.role} wallet lifecycle`);
1037
+ }
717
1038
  const wallet = upsertNamedWallet({
718
1039
  walletId: params.walletId,
719
1040
  name: params.options.walletName || "Wallet",
@@ -726,19 +1047,16 @@ async function importSignerOwnedWalletForSetup(params) {
726
1047
  signerWalletId,
727
1048
  policyHash: result.policy.hash,
728
1049
  policyVersion: result.policy.version,
729
- policyState: "locked",
1050
+ baselineVersion: readiness.baselineVersion,
1051
+ policyState: "ready",
730
1052
  networkHash: network.hash,
731
1053
  networkVersion: network.version,
732
- networkReady: network.ready
1054
+ networkReady: network.ready,
1055
+ operationLane: readiness.operationLane,
1056
+ roleReady: readiness.ready
733
1057
  },
734
1058
  env: params.env
735
1059
  });
736
- if (params.role === "agent") {
737
- setDefaultWallet({
738
- walletId: wallet.id,
739
- env: params.env
740
- });
741
- }
742
1060
  if (params.role === "mining") {
743
1061
  const currentEntry = cfg.plugins?.entries?.["sat-mining"];
744
1062
  const currentConfig = currentEntry?.config && typeof currentEntry.config === "object" && !Array.isArray(currentEntry.config) ? currentEntry.config : {};
@@ -770,14 +1088,17 @@ async function importSignerOwnedWalletForSetup(params) {
770
1088
  signerWalletId,
771
1089
  role: params.role,
772
1090
  address: result.wallet.publicKey,
773
- policyState: "locked",
1091
+ baselineVersion: readiness.baselineVersion,
1092
+ policyState: "ready",
774
1093
  networkVersion: network.version,
775
- networkReady: network.ready
1094
+ networkReady: network.ready,
1095
+ operationLane: readiness.operationLane,
1096
+ roleReady: readiness.ready
776
1097
  }, null, 2));
777
1098
  } else {
778
1099
  params.runtime.log(`Imported ${params.role} wallet ${wallet.id} into fased-signerd.`);
779
1100
  params.runtime.log(`SOLANA address: ${result.wallet.publicKey}`);
780
- params.runtime.log(`Readiness: key=yes RPC=${network.ready ? "yes" : "no"} policy=receive-only`);
1101
+ params.runtime.log(`Readiness: ${readiness.ready ? "ready" : "setup incomplete"} (${readiness.operationLane})`);
781
1102
  }
782
1103
  return result;
783
1104
  }
@@ -1035,6 +1356,71 @@ async function walletSetupCommand(runtime = defaultRuntime, options = {}) {
1035
1356
  return;
1036
1357
  }
1037
1358
  }
1359
+ async function walletRpcSetCommand(runtime = defaultRuntime, options) {
1360
+ const walletId = options.walletId.trim();
1361
+ const rpcUrl = options.rpcUrl.trim();
1362
+ if (!walletId) {
1363
+ throw new Error("--wallet-id is required");
1364
+ }
1365
+ if (!rpcUrl) {
1366
+ throw new Error("--rpc-url is required");
1367
+ }
1368
+ const cfg = loadConfig();
1369
+ const effectiveEnv = {
1370
+ ...process.env,
1371
+ ...cfg.env?.vars
1372
+ };
1373
+ const registry = readWalletProviderRegistry(effectiveEnv);
1374
+ const wallet = registry.wallets.find((entry) => entry.id === walletId);
1375
+ if (!wallet || wallet.providerId !== "local-socket-signer") {
1376
+ throw new Error(`native signer wallet not found: ${walletId}`);
1377
+ }
1378
+ const signerWalletId = typeof wallet.metadata?.signerWalletId === "string" && wallet.metadata.signerWalletId.trim() ? wallet.metadata.signerWalletId.trim() : normalizeNativeSignerWalletId(wallet.id);
1379
+ const hosted = String(effectiveEnv.FASED_HOST_PROFILE ?? "").trim().toLowerCase() === "hosting";
1380
+ const signerBinPath = hosted ? "/opt/fased/signer/fased-signerd" : resolveSignerdBinaryPath(effectiveEnv);
1381
+ const socketFlag = hosted ? "--operator-socket" : "--control-socket";
1382
+ const socketPath = hosted ? "/run/fased-signerd/operator.sock" : resolveLocalSignerControlSocketPath(effectiveEnv);
1383
+ const current = hosted ? invokeNativeSignerWalletReadiness({
1384
+ signerBinPath,
1385
+ socketFlag,
1386
+ socketPath,
1387
+ walletId: signerWalletId,
1388
+ env: effectiveEnv
1389
+ }) : await readSignerOwnedWalletReadiness({
1390
+ socketPath: resolveLocalSignerSocketPath(effectiveEnv),
1391
+ walletId: signerWalletId
1392
+ });
1393
+ const network = invokeNativeSignerNetworkSetPrimary({
1394
+ signerBinPath,
1395
+ socketFlag,
1396
+ socketPath,
1397
+ walletId: signerWalletId,
1398
+ primaryRpcUrl: rpcUrl,
1399
+ expectedVersion: current.networkVersion,
1400
+ env: effectiveEnv
1401
+ });
1402
+ const nextConfig = setConfigEnvVar(cfg, rpcEnvKeyFor("solana", walletId), rpcUrl);
1403
+ await writeConfigFile(nextConfig);
1404
+ registry.wallets = registry.wallets.map((entry) => entry.id === wallet.id ? {
1405
+ ...entry,
1406
+ metadata: {
1407
+ ...entry.metadata,
1408
+ networkHash: network.hash,
1409
+ networkVersion: network.version,
1410
+ networkReady: network.ready
1411
+ },
1412
+ updatedAt: new Date().toISOString()
1413
+ } : entry);
1414
+ writeWalletProviderRegistry(registry, effectiveEnv);
1415
+ if (options.json) {
1416
+ runtime.log(JSON.stringify({
1417
+ ok: true,
1418
+ network
1419
+ }, null, 2));
1420
+ return;
1421
+ }
1422
+ runtime.log(`Primary Solana RPC verified for ${wallet.name} (${wallet.id}); signer network v${network.version} is ready.`);
1423
+ }
1038
1424
  async function walletRecoveryExportCommand(runtime = defaultRuntime, options) {
1039
1425
  const walletId = options.walletId.trim();
1040
1426
  const output = options.output.trim();
@@ -1058,18 +1444,15 @@ async function walletRecoveryExportCommand(runtime = defaultRuntime, options) {
1058
1444
  throw new Error(`native signer wallet ${walletId} has no verified Solana public address`);
1059
1445
  }
1060
1446
  const hosted = String(effectiveEnv.FASED_HOST_PROFILE ?? "").trim().toLowerCase() === "hosting";
1061
- if (hosted) {
1062
- throw new Error("Hosting recovery export remains root/signer-owner only so a compromised Gateway cannot choose the encryption password and export a wallet. Use the provider root console and fased-signerd admin wallet recovery-export until a signer-owned owner-authorization ceremony is configured.");
1063
- }
1064
1447
  const signerWalletId = typeof wallet.metadata?.signerWalletId === "string" && wallet.metadata.signerWalletId.trim() ? wallet.metadata.signerWalletId.trim() : normalizeNativeSignerWalletId(wallet.id);
1065
- const signerBinPath = resolveSignerdBinaryPath(effectiveEnv);
1066
- const controlSocketPath = resolveLocalSignerControlSocketPath(effectiveEnv);
1448
+ const signerBinPath = hosted ? "/opt/fased/signer/fased-signerd" : resolveSignerdBinaryPath(effectiveEnv);
1449
+ const controlSocketPath = hosted ? "/run/fased-signerd/operator.sock" : resolveLocalSignerControlSocketPath(effectiveEnv);
1067
1450
  runtime.log(`Creating encrypted recovery package for ${wallet.id}. The signer will ask for and confirm the recovery password without echoing it.`);
1068
1451
  const child = spawnSync(signerBinPath, [
1069
1452
  "admin",
1070
1453
  "wallet",
1071
1454
  "recovery-export",
1072
- "--control-socket",
1455
+ hosted ? "--operator-socket" : "--control-socket",
1073
1456
  controlSocketPath,
1074
1457
  "--wallet-id",
1075
1458
  signerWalletId,
@@ -1109,6 +1492,270 @@ async function walletRecoveryImportCommand(runtime = defaultRuntime, options) {
1109
1492
  noSignerHints: true
1110
1493
  });
1111
1494
  }
1495
+ async function walletRetireCommand(runtime = defaultRuntime, options) {
1496
+ const walletId = options.walletId.trim();
1497
+ const successorWalletId = options.successorWalletId.trim();
1498
+ const successorWalletName = options.successorWalletName.trim();
1499
+ const rpcUrl = options.rpcUrl.trim();
1500
+ if (!walletId || !successorWalletId || !successorWalletName || !rpcUrl) {
1501
+ throw new Error("--wallet-id, --successor-wallet-id, --successor-wallet-name, and --rpc-url are required");
1502
+ }
1503
+ if (walletId === successorWalletId) {
1504
+ throw new Error("Mining successor wallet id must differ from the retired wallet id");
1505
+ }
1506
+ if (!/^[a-zA-Z0-9_-]+$/u.test(successorWalletId)) {
1507
+ throw new Error("--successor-wallet-id must contain only letters, numbers, hyphens, or underscores");
1508
+ }
1509
+ const cfg = loadConfig();
1510
+ const effectiveEnv = {
1511
+ ...process.env,
1512
+ ...cfg.env?.vars
1513
+ };
1514
+ const registry = readWalletProviderRegistry(effectiveEnv);
1515
+ const source = registry.wallets.find((entry) => entry.id === walletId);
1516
+ if (!source || source.providerId !== "local-socket-signer" || resolveWalletUserRole(source) !== "mining") {
1517
+ throw new Error(`active signer-owned Mining wallet not found: ${walletId}`);
1518
+ }
1519
+ const configuredMiningWalletId = resolveConfiguredMiningWalletId(cfg);
1520
+ if (configuredMiningWalletId !== walletId && configuredMiningWalletId !== successorWalletId) {
1521
+ throw new Error(`${walletId} is not the active singleton Mining wallet`);
1522
+ }
1523
+ if (registry.wallets.some((entry) => entry.id === successorWalletId)) {
1524
+ throw new Error(`successor wallet id is already registered: ${successorWalletId}`);
1525
+ }
1526
+ const sourcePublicKey = source.addresses?.solana?.trim() ?? "";
1527
+ if (!sourcePublicKey) {
1528
+ throw new Error("source Mining wallet has no verified Solana address");
1529
+ }
1530
+ const sourceSignerWalletId = typeof source.metadata?.signerWalletId === "string" && source.metadata.signerWalletId.trim() ? source.metadata.signerWalletId.trim() : normalizeNativeSignerWalletId(source.id);
1531
+ const successorSignerWalletId = normalizeNativeSignerWalletId(successorWalletId);
1532
+ if (sourceSignerWalletId === successorSignerWalletId) {
1533
+ throw new Error("Mining successor signer wallet id must be distinct");
1534
+ }
1535
+ const recovery = verifyMiningRecoveryPackage({
1536
+ recoveryFile: options.recoveryFile,
1537
+ walletId: sourceSignerWalletId,
1538
+ publicKey: sourcePublicKey
1539
+ });
1540
+ const hosted = String(effectiveEnv.FASED_HOST_PROFILE ?? "").trim().toLowerCase() === "hosting";
1541
+ const signerBinPath = hosted ? "/opt/fased/signer/fased-signerd" : resolveSignerdBinaryPath(effectiveEnv);
1542
+ const socketFlag = hosted ? "--operator-socket" : "--control-socket";
1543
+ const socketPath = hosted ? "/run/fased-signerd/operator.sock" : resolveLocalSignerControlSocketPath(effectiveEnv);
1544
+ let rotation = invokeNativeSignerRotationStatus({
1545
+ signerBinPath,
1546
+ socketFlag,
1547
+ socketPath,
1548
+ sourceWalletId: sourceSignerWalletId,
1549
+ env: effectiveEnv
1550
+ });
1551
+ if (rotation && rotation.successorWalletId !== successorSignerWalletId) {
1552
+ throw new Error(`source wallet is already bound to immutable successor ${rotation.successorWalletId}`);
1553
+ }
1554
+ if (!rotation && configuredMiningWalletId !== walletId) {
1555
+ throw new Error("Mining configuration changed before the signer prepared a successor");
1556
+ }
1557
+ let evidence;
1558
+ if (!rotation || rotation.state !== "committed") {
1559
+ const sourceReadiness = invokeNativeSignerWalletReadiness({
1560
+ signerBinPath,
1561
+ socketFlag,
1562
+ socketPath,
1563
+ walletId: sourceSignerWalletId,
1564
+ env: effectiveEnv
1565
+ });
1566
+ if (sourceReadiness.publicKey !== sourcePublicKey || sourceReadiness.role !== "mining" || !sourceReadiness.keyReady || !sourceReadiness.policyReady || !sourceReadiness.networkReady) {
1567
+ throw new Error("source Mining wallet is not live and role-ready in the signer");
1568
+ }
1569
+ const signerSOL = invokeNativeSignerBalance({
1570
+ signerBinPath,
1571
+ socketFlag,
1572
+ socketPath,
1573
+ walletId: sourceSignerWalletId,
1574
+ publicKey: sourcePublicKey,
1575
+ env: effectiveEnv
1576
+ });
1577
+ evidence = buildMiningRetirementEvidence({
1578
+ walletId,
1579
+ signerWalletId: sourceSignerWalletId,
1580
+ publicKey: sourcePublicKey,
1581
+ signerSolBalanceLamports: signerSOL,
1582
+ liveStatus: options.liveMiningStatus,
1583
+ env: effectiveEnv
1584
+ });
1585
+ rotation ??= invokeNativeSignerRotationCreate({
1586
+ signerBinPath,
1587
+ socketFlag,
1588
+ socketPath,
1589
+ sourceWalletId: sourceSignerWalletId,
1590
+ successorWalletId: successorSignerWalletId,
1591
+ sourcePublicKey,
1592
+ sourceWalletVersion: Number(sourceReadiness.walletVersion),
1593
+ sourcePolicyVersion: sourceReadiness.policyVersion,
1594
+ env: effectiveEnv
1595
+ });
1596
+ if (rotation.role !== "mining" || rotation.sourcePublicKey !== sourcePublicKey || rotation.successorPublicKey === sourcePublicKey) {
1597
+ throw new Error("signer prepared an invalid Mining successor binding");
1598
+ }
1599
+ let successorReadiness = invokeNativeSignerWalletReadiness({
1600
+ signerBinPath,
1601
+ socketFlag,
1602
+ socketPath,
1603
+ walletId: successorSignerWalletId,
1604
+ env: effectiveEnv
1605
+ });
1606
+ let successorNetwork = {
1607
+ version: successorReadiness.networkVersion,
1608
+ hash: successorReadiness.networkHash ?? "",
1609
+ ready: successorReadiness.networkReady
1610
+ };
1611
+ if (successorNetwork.version === 0) {
1612
+ successorNetwork = invokeNativeSignerNetworkSetPrimary({
1613
+ signerBinPath,
1614
+ socketFlag,
1615
+ socketPath,
1616
+ walletId: successorSignerWalletId,
1617
+ primaryRpcUrl: rpcUrl,
1618
+ expectedVersion: 0,
1619
+ env: effectiveEnv
1620
+ });
1621
+ }
1622
+ if (!successorNetwork.ready || !successorNetwork.hash) {
1623
+ throw new Error("Mining successor RPC is not verified and ready");
1624
+ }
1625
+ rotation = invokeNativeSignerRotationCommit({
1626
+ signerBinPath,
1627
+ socketFlag,
1628
+ socketPath,
1629
+ rotation,
1630
+ successorNetworkVersion: successorNetwork.version,
1631
+ successorNetworkHash: successorNetwork.hash,
1632
+ recoveryPackageHash: recovery.packageHash,
1633
+ safetyEvidence: evidence,
1634
+ env: effectiveEnv
1635
+ });
1636
+ successorReadiness = invokeNativeSignerWalletReadiness({
1637
+ signerBinPath,
1638
+ socketFlag,
1639
+ socketPath,
1640
+ walletId: successorSignerWalletId,
1641
+ env: effectiveEnv
1642
+ });
1643
+ if (rotation.state !== "committed" || successorReadiness.publicKey !== rotation.successorPublicKey || successorReadiness.role !== "mining" || successorReadiness.operationLane !== "mining-typed-sat" || !successorReadiness.ready || successorReadiness.policyHash !== rotation.successorActivatedPolicyHash) {
1644
+ throw new Error("Mining successor did not become authoritatively role-ready after retirement");
1645
+ }
1646
+ }
1647
+ if (rotation.state !== "committed" || rotation.recoveryPackageHash !== recovery.packageHash || !rotation.sourceRetiredPolicyHash || !rotation.successorActivatedPolicyHash || !rotation.safetyEvidenceHash) {
1648
+ throw new Error("signer retirement acknowledgement is incomplete");
1649
+ }
1650
+ const successorReadiness = invokeNativeSignerWalletReadiness({
1651
+ signerBinPath,
1652
+ socketFlag,
1653
+ socketPath,
1654
+ walletId: successorSignerWalletId,
1655
+ env: effectiveEnv
1656
+ });
1657
+ if (successorReadiness.publicKey !== rotation.successorPublicKey || successorReadiness.role !== "mining" || !successorReadiness.ready) {
1658
+ throw new Error("committed Mining successor is not live and ready");
1659
+ }
1660
+ const receipt = {
1661
+ version: 1,
1662
+ kind: "fased-mining-wallet-retirement",
1663
+ rotationId: rotation.rotationId,
1664
+ committedAt: rotation.committedAt,
1665
+ sourceWalletId: walletId,
1666
+ sourceSignerWalletId,
1667
+ sourcePublicKey,
1668
+ sourceRetiredPolicyVersion: rotation.sourceRetiredPolicyVersion,
1669
+ sourceRetiredPolicyHash: rotation.sourceRetiredPolicyHash,
1670
+ successorWalletId,
1671
+ successorSignerWalletId,
1672
+ successorPublicKey: rotation.successorPublicKey,
1673
+ successorPolicyVersion: successorReadiness.policyVersion,
1674
+ successorPolicyHash: successorReadiness.policyHash,
1675
+ successorNetworkVersion: successorReadiness.networkVersion,
1676
+ successorNetworkHash: successorReadiness.networkHash,
1677
+ recoveryPackageHash: rotation.recoveryPackageHash,
1678
+ safetyEvidenceHash: rotation.safetyEvidenceHash,
1679
+ balances: evidence ?? rotation.safetyEvidence ? {
1680
+ solBalanceLamports: (evidence ?? rotation.safetyEvidence).solBalanceLamports,
1681
+ satBalanceRaw: (evidence ?? rotation.safetyEvidence).satBalanceRaw
1682
+ } : undefined
1683
+ };
1684
+ const receiptPath = writeMiningRetirementReceipt({
1685
+ sourceWalletId: walletId,
1686
+ receipt,
1687
+ env: effectiveEnv
1688
+ });
1689
+ const currentEntry = cfg.plugins?.entries?.["sat-mining"];
1690
+ const currentPluginConfig = currentEntry?.config && typeof currentEntry.config === "object" && !Array.isArray(currentEntry.config) ? currentEntry.config : {};
1691
+ await writeConfigFile({
1692
+ ...cfg,
1693
+ plugins: {
1694
+ ...cfg.plugins,
1695
+ entries: {
1696
+ ...cfg.plugins?.entries,
1697
+ "sat-mining": {
1698
+ ...currentEntry,
1699
+ enabled: true,
1700
+ config: {
1701
+ ...currentPluginConfig,
1702
+ walletId: successorWalletId
1703
+ }
1704
+ }
1705
+ }
1706
+ }
1707
+ });
1708
+ replaceRetiredMiningWallet({
1709
+ sourceWalletId: walletId,
1710
+ signerAcknowledgement: {
1711
+ rotationId: rotation.rotationId,
1712
+ sourceRetiredPolicyHash: String(rotation.sourceRetiredPolicyHash),
1713
+ successorPublicKey: rotation.successorPublicKey,
1714
+ successorPolicyHash: successorReadiness.policyHash
1715
+ },
1716
+ successor: {
1717
+ id: successorWalletId,
1718
+ name: successorWalletName,
1719
+ providerId: "local-socket-signer",
1720
+ addresses: { solana: rotation.successorPublicKey },
1721
+ metadata: {
1722
+ role: "mining",
1723
+ purpose: "mining",
1724
+ keyAuthority: "signer-owned-v2",
1725
+ signerWalletId: successorSignerWalletId,
1726
+ policyHash: successorReadiness.policyHash,
1727
+ policyVersion: successorReadiness.policyVersion,
1728
+ baselineVersion: successorReadiness.baselineVersion,
1729
+ policyState: "ready",
1730
+ networkHash: successorReadiness.networkHash,
1731
+ networkVersion: successorReadiness.networkVersion,
1732
+ networkReady: true,
1733
+ operationLane: successorReadiness.operationLane,
1734
+ roleReady: true,
1735
+ predecessorWalletId: walletId,
1736
+ rotationId: rotation.rotationId
1737
+ }
1738
+ },
1739
+ env: effectiveEnv
1740
+ });
1741
+ const result = {
1742
+ ok: true,
1743
+ retiredWalletId: walletId,
1744
+ retiredAddress: sourcePublicKey,
1745
+ successorWalletId,
1746
+ successorAddress: rotation.successorPublicKey,
1747
+ rotationId: rotation.rotationId,
1748
+ receiptPath
1749
+ };
1750
+ if (options.json) {
1751
+ runtime.log(JSON.stringify(result, null, 2));
1752
+ } else {
1753
+ runtime.log(`Retired Mining wallet ${walletId} (${sourcePublicKey}).`);
1754
+ runtime.log(`Active Mining successor: ${successorWalletId} (${rotation.successorPublicKey}).`);
1755
+ runtime.log(`Audit receipt: ${receiptPath}`);
1756
+ }
1757
+ return result;
1758
+ }
1112
1759
  async function walletRawExportCommand(runtime = defaultRuntime, options) {
1113
1760
  const walletId = options.walletId.trim();
1114
1761
  const output = options.output.trim();
@@ -1134,17 +1781,15 @@ async function walletRawExportCommand(runtime = defaultRuntime, options) {
1134
1781
  if (!publicKey) {
1135
1782
  throw new Error(`native signer wallet ${walletId} has no verified Solana public address`);
1136
1783
  }
1137
- if (String(effectiveEnv.FASED_HOST_PROFILE ?? "").trim().toLowerCase() === "hosting") {
1138
- throw new Error("Hosting raw-key export remains signer-owner only. Run the native signer admin command from the provider root console so the Gateway cannot export custody material.");
1139
- }
1784
+ const hosted = String(effectiveEnv.FASED_HOST_PROFILE ?? "").trim().toLowerCase() === "hosting";
1140
1785
  const signerWalletId = typeof wallet.metadata?.signerWalletId === "string" && wallet.metadata.signerWalletId.trim() ? wallet.metadata.signerWalletId.trim() : normalizeNativeSignerWalletId(wallet.id);
1141
1786
  runtime.log("WARNING: raw private-key export reduces signer custody protection.");
1142
- const child = spawnSync(resolveSignerdBinaryPath(effectiveEnv), [
1787
+ const child = spawnSync(hosted ? "/opt/fased/signer/fased-signerd" : resolveSignerdBinaryPath(effectiveEnv), [
1143
1788
  "admin",
1144
1789
  "wallet",
1145
1790
  "export-raw",
1146
- "--control-socket",
1147
- resolveLocalSignerControlSocketPath(effectiveEnv),
1791
+ hosted ? "--operator-socket" : "--control-socket",
1792
+ hosted ? "/run/fased-signerd/operator.sock" : resolveLocalSignerControlSocketPath(effectiveEnv),
1148
1793
  "--wallet-id",
1149
1794
  signerWalletId,
1150
1795
  "--expected-public-key",
@@ -1217,10 +1862,56 @@ async function walletRoleSetCommand(runtime = defaultRuntime, options) {
1217
1862
  }, null, 2));
1218
1863
  return;
1219
1864
  }
1220
- runtime.log(`${wallet?.name ?? walletId} (${walletId}) set to ${role === "agent" ? "Agent wallet" : "Vault wallet"}${primary ? " and primary Agent fallback" : ""}.`);
1865
+ runtime.log(`${wallet?.name ?? walletId} (${walletId}) set to ${role === "agent" ? "Agent wallet" : "Vault wallet"}${primary ? " and Default Agent wallet fallback" : ""}.`);
1221
1866
  }
1222
1867
  async function walletStatusCommand(runtime = defaultRuntime, options = {}) {
1223
- const status = await readWalletStatusSnapshot();
1868
+ const config = loadConfig();
1869
+ const effectiveEnv = {
1870
+ ...process.env,
1871
+ ...config.env?.vars
1872
+ };
1873
+ const hostedOperator = String(effectiveEnv.FASED_HOST_PROFILE ?? "").trim().toLowerCase() === "hosting" && effectiveEnv.FASED_GATEWAY_SERVICE !== "1";
1874
+ if (hostedOperator) {
1875
+ const registered = readWalletProviderRegistry(effectiveEnv).wallets.filter((wallet) => wallet.providerId === "local-socket-signer" && (!options.walletId || wallet.id === options.walletId));
1876
+ if (options.walletId && registered.length === 0) {
1877
+ throw new Error(`native signer wallet not found: ${options.walletId}`);
1878
+ }
1879
+ const wallets = registered.map((wallet) => {
1880
+ const signerWalletId = typeof wallet.metadata?.signerWalletId === "string" && wallet.metadata.signerWalletId.trim() ? wallet.metadata.signerWalletId.trim() : normalizeNativeSignerWalletId(wallet.id);
1881
+ return {
1882
+ id: wallet.id,
1883
+ name: wallet.name,
1884
+ signer: invokeNativeSignerWalletReadiness({
1885
+ signerBinPath: "/opt/fased/signer/fased-signerd",
1886
+ socketFlag: "--operator-socket",
1887
+ socketPath: "/run/fased-signerd/operator.sock",
1888
+ walletId: signerWalletId,
1889
+ env: effectiveEnv
1890
+ })
1891
+ };
1892
+ });
1893
+ if (options.json) {
1894
+ runtime.log(JSON.stringify({
1895
+ ok: true,
1896
+ status: {
1897
+ mode: "hosting-operator",
1898
+ wallets
1899
+ }
1900
+ }, null, 2));
1901
+ return;
1902
+ }
1903
+ if (wallets.length === 0) {
1904
+ runtime.log("No signer-owned wallets are registered.");
1905
+ return;
1906
+ }
1907
+ for (const wallet of wallets) {
1908
+ runtime.log(`Wallet ${wallet.name} (${wallet.id}): ${wallet.signer.ready ? "ready" : "setup incomplete"}`);
1909
+ runtime.log(` signer=${wallet.signer.walletId} role=${wallet.signer.role} lane=${wallet.signer.operationLane} baseline=v${wallet.signer.baselineVersion}`);
1910
+ runtime.log(` key=${String(wallet.signer.keyReady)} policy=v${wallet.signer.policyVersion} ${wallet.signer.policyHash} network=v${wallet.signer.networkVersion} ${wallet.signer.networkHash ?? "unconfigured"}`);
1911
+ }
1912
+ return;
1913
+ }
1914
+ const status = await readWalletStatusSnapshot({ walletId: options.walletId });
1224
1915
  if (options.json) {
1225
1916
  runtime.log(JSON.stringify({
1226
1917
  ok: true,
@@ -1248,10 +1939,104 @@ async function walletStatusCommand(runtime = defaultRuntime, options = {}) {
1248
1939
  if (status.addresses?.solana) {
1249
1940
  runtime.log(`Address: ${status.addresses.solana}`);
1250
1941
  }
1942
+ for (const wallet of status.wallets ?? []) {
1943
+ const signer = wallet.readiness.signer;
1944
+ runtime.log(`Wallet ${wallet.name} (${wallet.id}): ${wallet.readiness.ready ? "ready" : "setup incomplete"}`);
1945
+ if (signer) {
1946
+ runtime.log(` signer=${signer.walletId} role=${signer.role} lane=${signer.operationLane} baseline=v${signer.baselineVersion}`);
1947
+ runtime.log(` key=${String(signer.keyReady)} policy=v${signer.policyVersion} ${signer.policyHash} network=v${signer.networkVersion} ${signer.networkHash ?? "unconfigured"}`);
1948
+ }
1949
+ if (wallet.readiness.error) {
1950
+ runtime.log(` warning=${wallet.readiness.error}`);
1951
+ }
1952
+ }
1251
1953
  if (status.error) {
1252
1954
  runtime.log(`Status warning: ${status.error}`);
1253
1955
  }
1254
1956
  }
1957
+ async function walletPolicyActivateRoleBaselineCommand(runtime = defaultRuntime, options) {
1958
+ if (!options.confirm) {
1959
+ throw new Error("Activate role baseline requires --confirm after reviewing the selected role");
1960
+ }
1961
+ const role = normalizeWalletUserRole(options.role);
1962
+ if (!role) {
1963
+ throw new Error("role must be one of: agent, mining, vault");
1964
+ }
1965
+ const registry = readWalletProviderRegistry(process.env);
1966
+ const wallet = registry.wallets.find((entry) => entry.id === options.walletId.trim());
1967
+ if (!wallet || wallet.providerId !== "local-socket-signer") {
1968
+ throw new Error(`registered signer-owned wallet was not found: ${options.walletId}`);
1969
+ }
1970
+ const registeredRole = resolveWalletUserRole(wallet);
1971
+ if (registeredRole && registeredRole !== role) {
1972
+ throw new Error(`wallet ${wallet.id} has immutable role=${registeredRole}; refusing requested role=${role}`);
1973
+ }
1974
+ const signerWalletId = typeof wallet.metadata?.signerWalletId === "string" && wallet.metadata.signerWalletId.trim() ? wallet.metadata.signerWalletId.trim() : normalizeNativeSignerWalletId(wallet.id);
1975
+ const socketPath = requireLocalSocketSignerPath(process.env);
1976
+ const current = await readSignerOwnedWallet({
1977
+ socketPath,
1978
+ walletId: signerWalletId
1979
+ });
1980
+ if (current.wallet.publicKey !== wallet.addresses?.solana) {
1981
+ throw new Error("registered wallet address does not match the signer-owned wallet");
1982
+ }
1983
+ if (current.policy.role !== role) {
1984
+ throw new Error(`signer-owned wallet ${signerWalletId} has immutable role=${current.policy.role}, not ${role}`);
1985
+ }
1986
+ let policy = current.policy;
1987
+ if (policy.baselineVersion === undefined || policy.baselineVersion === 0) {
1988
+ policy = await activateSignerOwnedRoleBaseline({
1989
+ socketPath,
1990
+ walletId: signerWalletId,
1991
+ role,
1992
+ expectedPolicyVersion: policy.version
1993
+ });
1994
+ } else if (policy.baselineVersion !== 1) {
1995
+ throw new Error(`signer-owned wallet ${signerWalletId} uses unsupported baseline version ${policy.baselineVersion}`);
1996
+ }
1997
+ const readiness = await readSignerOwnedWalletReadiness({
1998
+ socketPath,
1999
+ walletId: signerWalletId
2000
+ });
2001
+ if (readiness.publicKey !== current.wallet.publicKey || readiness.role !== role || readiness.policyHash !== policy.hash || readiness.policyVersion !== policy.version || readiness.baselineVersion !== 1) {
2002
+ throw new Error("Setup incomplete: activated role baseline does not match live signer state");
2003
+ }
2004
+ upsertNamedWallet({
2005
+ walletId: wallet.id,
2006
+ name: wallet.name,
2007
+ providerId: wallet.providerId,
2008
+ addresses: wallet.addresses,
2009
+ metadata: {
2010
+ ...wallet.metadata,
2011
+ role,
2012
+ purpose: role,
2013
+ signerWalletId,
2014
+ policyHash: readiness.policyHash,
2015
+ policyVersion: readiness.policyVersion,
2016
+ baselineVersion: readiness.baselineVersion,
2017
+ policyState: readiness.policyReady ? "ready" : "setup-incomplete",
2018
+ networkHash: readiness.networkHash,
2019
+ networkVersion: readiness.networkVersion,
2020
+ networkReady: readiness.networkReady,
2021
+ operationLane: readiness.operationLane,
2022
+ roleReady: readiness.ready
2023
+ },
2024
+ env: process.env
2025
+ });
2026
+ const payload = {
2027
+ ok: true,
2028
+ walletId: wallet.id,
2029
+ signerWalletId,
2030
+ role,
2031
+ readiness
2032
+ };
2033
+ if (options.json) {
2034
+ runtime.log(JSON.stringify(payload, null, 2));
2035
+ return;
2036
+ }
2037
+ runtime.log(`Activated ${role} role baseline v${readiness.baselineVersion} for ${wallet.name} (${wallet.id}).`);
2038
+ runtime.log(`Readiness: ${readiness.ready ? "ready" : "setup incomplete"} (${readiness.operationLane}).`);
2039
+ }
1255
2040
  async function walletKeystoreInitCommand(runtime = defaultRuntime, options = {}) {
1256
2041
  void runtime;
1257
2042
  void options;
@@ -1936,4 +2721,4 @@ async function walletInboundReconcileCommand(runtime = defaultRuntime, options =
1936
2721
  }
1937
2722
 
1938
2723
  //#endregion
1939
- export { collectWalletSignerDoctorReport, walletCanaryCommand, walletInboundListCommand, walletInboundPollCommand, walletInboundReconcileCommand, walletLegacyMigrationFinalizeCommand, walletLimitOrdersConfigureCommand, walletMigrateCommand, walletPolicyProfileApplyCommand, walletProviderConfigureCommand, walletRawExportCommand, walletRecoveryExportCommand, walletRecoveryImportCommand, walletRoleSetCommand, walletRotateKeysCommand, walletSetupCommand, walletSignerDoctorCommand, walletSignerServeCommand, walletStatusCommand };
2724
+ export { collectWalletSignerDoctorReport, walletCanaryCommand, walletInboundListCommand, walletInboundPollCommand, walletInboundReconcileCommand, walletLegacyMigrationFinalizeCommand, walletLimitOrdersConfigureCommand, walletMigrateCommand, walletPolicyActivateRoleBaselineCommand, walletPolicyProfileApplyCommand, walletProviderConfigureCommand, walletRawExportCommand, walletRecoveryExportCommand, walletRecoveryImportCommand, walletRetireCommand, walletRoleSetCommand, walletRotateKeysCommand, walletRpcSetCommand, walletSetupCommand, walletSignerDoctorCommand, walletSignerServeCommand, walletStatusCommand };