@fased/fased 0.1.67 → 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 (187) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +20 -22
  3. package/config/sat-runtime.env +6 -2
  4. package/config/signer-protocol-v2.json +10 -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 +119 -5
  11. package/dist/commands/wallet.js +1286 -74
  12. package/dist/config/sat-runtime-ids.js +6 -1
  13. package/dist/control-ui/assets/{app-De8gTtZU.js → app-BLFLoFPk.js} +1526 -1526
  14. package/dist/control-ui/assets/{federation-DgA9e-Pn.js → federation-SGMAnM_8.js} +2 -2
  15. package/dist/control-ui/assets/{index-DYYBRBOS.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-DvStR_AT.js → wallet-CRIhDQbW.js} +325 -156
  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 +394 -55
  21. package/dist/mining/mainnet-sync.js +67 -5
  22. package/dist/operator/operator-readiness.js +1 -1
  23. package/dist/plugin-sdk/{actions-DRu135h0.js → actions-B-Vwj64K.js} +3 -3
  24. package/dist/plugin-sdk/{allow-list-DyAxs4yw.js → allow-list-Bf45HfoP.js} +1 -1
  25. package/dist/plugin-sdk/{audio-preflight-Dq_rJtqH.js → audio-preflight-CS7Q_7-R.js} +26 -25
  26. package/dist/plugin-sdk/{audit-C5d2z5U1.js → audit-CUULWG22.js} +1 -1
  27. package/dist/plugin-sdk/{channel-access-configure-DaI5wevh.js → channel-access-configure-BoUsC-Ys.js} +1 -1
  28. package/dist/plugin-sdk/{channel-adapters.discord-BS4S4Svh.js → channel-adapters.discord-DSiR4X2Y.js} +1 -1
  29. package/dist/plugin-sdk/channel-plugin-common.d.ts +2 -2
  30. package/dist/plugin-sdk/channel-plugin-common.js +1 -1
  31. package/dist/plugin-sdk/{chunk-CyoyqpbG.js → chunk-CBpl-AiQ.js} +1 -1
  32. package/dist/plugin-sdk/{chunk-items-Dunoq4Er.js → chunk-items-BLBZNORg.js} +1 -1
  33. package/dist/plugin-sdk/command-status.js +26 -25
  34. package/dist/plugin-sdk/{commands-registry-BfqdEWdF.js → commands-registry-G5KXSi_y.js} +1 -1
  35. package/dist/plugin-sdk/{config-schema--vl89M7d.js → config-schema-BiBOUW89.js} +431 -125
  36. package/dist/plugin-sdk/device-pair.d.ts +1 -1
  37. package/dist/plugin-sdk/{diagnostic-DLL_zekv.js → diagnostic-sYs6Agr-.js} +1 -1
  38. package/dist/plugin-sdk/{directory-live-CSuYxdwk.js → directory-live-BXMpuYAF.js} +1 -1
  39. package/dist/plugin-sdk/{directory-live-Dw7ZgC9_.js → directory-live-Rn1BzPof.js} +1 -1
  40. package/dist/plugin-sdk/discord.d.ts +4 -4
  41. package/dist/plugin-sdk/discord.js +20 -20
  42. package/dist/plugin-sdk/{draft-stream-controls-BqchW1vi.js → draft-stream-controls-CMNdKSx-.js} +1 -1
  43. package/dist/plugin-sdk/{guilds-CDhRIX57.js → guilds-Dkwl0yHv.js} +1 -1
  44. package/dist/plugin-sdk/{handle-action-hvwapMKg.js → handle-action-CRdM6B-8.js} +15 -15
  45. package/dist/plugin-sdk/index.d.ts +15 -15
  46. package/dist/plugin-sdk/index.js +28 -27
  47. package/dist/plugin-sdk/{login-Ca583Okc.js → login-D7WnK8-P.js} +1 -1
  48. package/dist/plugin-sdk/{login-qr-CGciBSNP.js → login-qr-CRueOKZK.js} +2 -114
  49. package/dist/plugin-sdk/{markdown-tables-DCOXEUfZ.js → markdown-tables-d7GlUs5R.js} +1 -1
  50. package/dist/plugin-sdk/{media-CAdQF5TG.js → media-BAoaxCrt.js} +1 -1
  51. package/dist/plugin-sdk/{monitor-DrTy_4CL.js → monitor-WXdn4mR-.js} +34 -33
  52. package/dist/plugin-sdk/{monitor-CV1sr0p2.js → monitor-xzVAPbg2.js} +44 -43
  53. package/dist/plugin-sdk/{openresponses-http-BAjgURZa.js → openresponses-http-DnI1LFgq.js} +26 -25
  54. package/dist/plugin-sdk/{outbound-attachment-QqTnIhZL.js → outbound-attachment-CyXM3cAv.js} +1 -1
  55. package/dist/plugin-sdk/{outbound-BgVDdHxx.js → outbound-zB6llMGW.js} +6 -6
  56. package/dist/plugin-sdk/{pairing-message-BjIJYVvp.d.ts → pairing-message-CiTcdD6m.d.ts} +1 -1
  57. package/dist/plugin-sdk/{pairing-store-BChahgI2.js → pairing-store-B6J2C0vP.js} +1 -1
  58. package/dist/plugin-sdk/{pi-model-discovery-runtime-DxTMz_XF.js → pi-model-discovery-runtime-6JzLw9Gk.js} +1 -1
  59. package/dist/plugin-sdk/{probe-CJb5uR8B.js → probe-BM7Oe40M.js} +2 -2
  60. package/dist/plugin-sdk/{probe-l_whvGLY.js → probe-CMPksimh.js} +1 -1
  61. package/dist/plugin-sdk/{probe-BFroeK3g.js → probe-oVp12D6L.js} +1 -1
  62. package/dist/plugin-sdk/provider-web-search-config-contract.d.ts +1 -1
  63. package/dist/plugin-sdk/{pw-ai-DlxqX6Es.js → pw-ai-DZYLDC-S.js} +1 -1
  64. package/dist/plugin-sdk/qr-image-B4MlMG4N.js +115 -0
  65. package/dist/plugin-sdk/{replies-ChYo5ZQM.js → replies-CY-V0Zob.js} +3 -3
  66. package/dist/plugin-sdk/{resolve-channels-87LZrFk_.js → resolve-channels-1HtDcMqh.js} +2 -2
  67. package/dist/plugin-sdk/{resolve-channels-_-nV-EK2.js → resolve-channels-CU72Qh2j.js} +1 -1
  68. package/dist/plugin-sdk/{resolve-outbound-target-D-KGgHJZ.js → resolve-outbound-target-nQSBVYz9.js} +2 -2
  69. package/dist/plugin-sdk/{resolve-route-C8tvQm8Z.js → resolve-route-Dufnu2cT.js} +1 -1
  70. package/dist/plugin-sdk/{resolve-users-DN9VQBwv.js → resolve-users-Bk_ss2vC.js} +1 -1
  71. package/dist/plugin-sdk/{resolve-users-V6dHh_2B.js → resolve-users-IevCOk4W.js} +2 -2
  72. package/dist/plugin-sdk/{runtime-2B9_WKoI.js → runtime-CXSQwMaR.js} +1 -1
  73. package/dist/plugin-sdk/{runtime-dependency-nup8niQL.js → runtime-dependency-DczkvPxn.js} +9 -9
  74. package/dist/plugin-sdk/sat-runtime.d.ts +105 -10
  75. package/dist/plugin-sdk/sat-runtime.js +26 -25
  76. package/dist/plugin-sdk/{send-DCO441U7.js → send-BXBS_mRK.js} +8 -8
  77. package/dist/plugin-sdk/{send-CFzGKJXo.js → send-D-HfKQZm.js} +4 -4
  78. package/dist/plugin-sdk/{send-B6KkGKZD.js → send-D1j12ZgX.js} +5 -5
  79. package/dist/plugin-sdk/{send-q9HXrSwK.js → send-DWKm6yay.js} +10 -10
  80. package/dist/plugin-sdk/{send-gDoGA-7e.js → send-Di1Jit3L.js} +7 -7
  81. package/dist/plugin-sdk/{skill-commands-FKEPLS7T.js → skill-commands-iJtvpqgb.js} +2 -2
  82. package/dist/plugin-sdk/{slack-DSC6T2KW.js → slack-CkIRVDUK.js} +45 -44
  83. package/dist/plugin-sdk/{slack-actions-DhbMcp8M.js → slack-actions-BzUEszJa.js} +10 -10
  84. package/dist/plugin-sdk/slack.d.ts +4 -4
  85. package/dist/plugin-sdk/slack.js +6 -6
  86. package/dist/plugin-sdk/{sticker-cache-C0l_5IKg.js → sticker-cache-Dpap5ce_.js} +2 -2
  87. package/dist/plugin-sdk/{tables-B3jr0Pvq.js → tables-_1W36GII.js} +2 -2
  88. package/dist/plugin-sdk/{targets-CtSbTxYX.js → targets-DDbNnLnB.js} +2 -2
  89. package/dist/plugin-sdk/{telegram-actions-BwGBIVDE.js → telegram-actions-Cewdga8H.js} +30 -29
  90. package/dist/plugin-sdk/telegram.d.ts +4 -4
  91. package/dist/plugin-sdk/telegram.js +3 -3
  92. package/dist/plugin-sdk/{thread-bindings.lifecycle-QbkNl-m6.js → thread-bindings.lifecycle-Y9T3CQGf.js} +3 -3
  93. package/dist/plugin-sdk/{thread-bindings.state-D9hg_M1c.js → thread-bindings.state-D2oOWDpv.js} +1 -1
  94. package/dist/plugin-sdk/{tmp-fased-dir-DXnGKKgi.d.ts → tmp-fased-dir-BSEHc8MQ.d.ts} +8 -2
  95. package/dist/plugin-sdk/{types-eG9aYqXF.d.ts → types-BakzIzGN.d.ts} +1 -1
  96. package/dist/plugin-sdk/{types-Cegc86EP.d.ts → types-CzF0YQqK.d.ts} +3 -3
  97. package/dist/plugin-sdk/{web-C_AH5PyB.js → web-DGWi7i3B.js} +33 -32
  98. package/dist/plugin-sdk/{whatsapp-actions-Bj_rOncr.js → whatsapp-actions-Blu9-eX-.js} +9 -9
  99. package/dist/plugin-sdk/{whatsapp-heartbeat-B1WHkJIg.js → whatsapp-heartbeat-DjmQM7hq.js} +2 -2
  100. package/dist/plugin-sdk/whatsapp.d.ts +9 -9
  101. package/dist/plugin-sdk/whatsapp.js +7 -7
  102. package/dist/plugin-sdk/{wsl-J-HscWxS.js → wsl-CTvIWvZz.js} +1 -1
  103. package/dist/plugin-sdk/{zod-schema.providers-core-FaYR6KdD.d.ts → zod-schema.providers-core-CIzCjXH9.d.ts} +61 -61
  104. package/dist/wallet/local-socket-signer-lifecycle.js +107 -3
  105. package/dist/wallet/local-socket-signer-protocol.js +97 -10
  106. package/dist/wallet/mining-wallet-retirement.js +256 -0
  107. package/dist/wallet/providers/local-socket-signer-adapter.js +10 -0
  108. package/dist/wallet/signer-network-admin.js +11 -118
  109. package/dist/wallet/signer-protocol-v2.generated.js +10 -0
  110. package/dist/wallet/solana-assets.js +9 -3
  111. package/dist/wallet/solana-rpc-read-fetch.js +65 -0
  112. package/dist/wallet/wallet-agent-selection.js +6 -10
  113. package/dist/wallet/wallet-provider-registry.js +70 -29
  114. package/dist/wallet/wallet-send-approvals.js +6 -2
  115. package/dist/wallet/wallet-status.js +63 -1
  116. package/dist/web/login-qr.js +1 -1
  117. package/dist/wizard/onboarding.js +86 -84
  118. package/dist/wizard/onboarding.wallet.js +10 -1
  119. package/extensions/acpx/package.json +1 -1
  120. package/extensions/bluebubbles/package.json +1 -1
  121. package/extensions/copilot-proxy/package.json +1 -1
  122. package/extensions/diagnostics-otel/package.json +1 -1
  123. package/extensions/fased-federation/package.json +1 -1
  124. package/extensions/google-gemini-cli-auth/package.json +1 -1
  125. package/extensions/imessage/package.json +1 -1
  126. package/extensions/irc/package.json +1 -1
  127. package/extensions/line/package.json +1 -1
  128. package/extensions/llm-task/package.json +1 -1
  129. package/extensions/lobster/package.json +1 -1
  130. package/extensions/matrix/CHANGELOG.md +12 -0
  131. package/extensions/matrix/package.json +1 -1
  132. package/extensions/mattermost/package.json +1 -1
  133. package/extensions/memory-core/package.json +2 -2
  134. package/extensions/memory-lancedb/package.json +1 -1
  135. package/extensions/minimax-portal-auth/package.json +1 -1
  136. package/extensions/msteams/CHANGELOG.md +12 -0
  137. package/extensions/msteams/package.json +1 -1
  138. package/extensions/nextcloud-talk/package.json +1 -1
  139. package/extensions/nostr/CHANGELOG.md +12 -0
  140. package/extensions/nostr/package.json +1 -1
  141. package/extensions/open-prose/package.json +1 -1
  142. package/extensions/sat-mining/package.json +1 -1
  143. package/extensions/signal/package.json +1 -1
  144. package/extensions/synology-chat/package.json +1 -1
  145. package/extensions/tlon/package.json +1 -1
  146. package/extensions/twitch/CHANGELOG.md +12 -0
  147. package/extensions/twitch/package.json +1 -1
  148. package/extensions/voice-call/CHANGELOG.md +12 -0
  149. package/extensions/voice-call/package.json +1 -1
  150. package/extensions/zalo/CHANGELOG.md +12 -0
  151. package/extensions/zalo/package.json +1 -1
  152. package/extensions/zalouser/CHANGELOG.md +12 -0
  153. package/extensions/zalouser/package.json +1 -1
  154. package/install.sh +286 -107
  155. package/package.json +4 -2
  156. package/scripts/fased-managed-updater.mjs +10 -2
  157. package/scripts/install-fased-signerd.sh +4 -8
  158. package/scripts/install-managed-runtime.mjs +10 -7
  159. package/scripts/install-runtime-profile.sh +27 -0
  160. package/scripts/start-managed.sh +13 -1
  161. /package/dist/plugin-sdk/{active-listener-B_NMtgPL.js → active-listener-p9Ooc1qj.js} +0 -0
  162. /package/dist/plugin-sdk/{blocks-input-Bg-8S2pl.js → blocks-input-DiZaqaN7.js} +0 -0
  163. /package/dist/plugin-sdk/{channel-access-CcFRdRPO.js → channel-access-B0aZBSd2.js} +0 -0
  164. /package/dist/plugin-sdk/{channel-activity-DBDMQ3yI.js → channel-activity-F07DjroV.js} +0 -0
  165. /package/dist/plugin-sdk/{channel-core-CFRs8LPy.js → channel-core-DoF4X04L.js} +0 -0
  166. /package/dist/plugin-sdk/{client-pI228AVT.js → client-DLY-kWF_.js} +0 -0
  167. /package/dist/plugin-sdk/{command-poll-backoff-yJU83wEq.js → command-poll-backoff-NAePk3eL.js} +0 -0
  168. /package/dist/plugin-sdk/{date-time-uUevHscU.js → date-time-ChY_tHoG.js} +0 -0
  169. /package/dist/plugin-sdk/{draft-stream-loop-CrOPfV3F.js → draft-stream-loop-DMBwjjd1.js} +0 -0
  170. /package/dist/plugin-sdk/{fetch-BtmoPIlf.js → fetch-B0v6OTUZ.js} +0 -0
  171. /package/dist/plugin-sdk/{filesystem-manager-DoI8IBNF.js → filesystem-manager-BzuIJ9K9.js} +0 -0
  172. /package/dist/plugin-sdk/{gateway-registry-DZDsqicG.js → gateway-registry-Bgl2GFgy.js} +0 -0
  173. /package/dist/plugin-sdk/{inbound-context-DrAcXE0y.js → inbound-context-CQnfFFQ9.js} +0 -0
  174. /package/dist/plugin-sdk/{pi-model-discovery-BX_JLcEJ.js → pi-model-discovery-BHNVyRny.js} +0 -0
  175. /package/dist/plugin-sdk/{polls-DKQXVfJp.js → polls-DXd67TD8.js} +0 -0
  176. /package/dist/plugin-sdk/{proxy-DWpYcsvR.js → proxy-CLRUdShp.js} +0 -0
  177. /package/dist/plugin-sdk/{qmd-manager-CgRaTxUq.js → qmd-manager-B8Vnu0ND.js} +0 -0
  178. /package/dist/plugin-sdk/{render-e7fENCYH.js → render-DrBfVs0u.js} +0 -0
  179. /package/dist/plugin-sdk/{reply-reference-Bqk-7HXE.js → reply-reference-BnAk5Wjb.js} +0 -0
  180. /package/dist/plugin-sdk/{retry-policy-C6SjKjQA.js → retry-policy-CXh1owkc.js} +0 -0
  181. /package/dist/plugin-sdk/{sandbox-paths-Dx2lUqCu.js → sandbox-paths-Bzh1CuZc.js} +0 -0
  182. /package/dist/plugin-sdk/{session-DHD4ZKyG.js → session-iuGR_rdi.js} +0 -0
  183. /package/dist/plugin-sdk/{shared-CBgp_GBw.js → shared-D1AD2Ejn.js} +0 -0
  184. /package/dist/plugin-sdk/{target-errors-Ckufe179.js → target-errors-DfS1sLR9.js} +0 -0
  185. /package/dist/plugin-sdk/{tool-loop-detection-Bp8bPJLs.js → tool-loop-detection-sDZl27w7.js} +0 -0
  186. /package/dist/plugin-sdk/{ui-Be8cLHIS.js → ui-GQDYm_jw.js} +0 -0
  187. /package/dist/plugin-sdk/{with-timeout-ckWB8rTo.js → with-timeout-CVV8tytk.js} +0 -0
@@ -11,6 +11,7 @@ import { resolveLocalSignerSocketPath, resolveWalletRuntimeConfig } from "../wal
11
11
  import { WALLET_PROVIDER_IDS, checkNamedWalletDeletionSafety, deleteNamedWallet, normalizeWalletUserRole, readWalletProviderRegistry, resolveWalletSelection, resolveWalletUserRole, setAgentWalletAssignment, setDefaultWallet, setNamedWalletRole, setWalletProviderEnabled, upsertNamedWallet } from "../wallet/wallet-provider-registry.js";
12
12
  import { resolveAgentPublicOrigin, resolveFederationBaseUrl, resolveFederationBondWalletId, resolveFederationHandle } from "../federation/runtime.js";
13
13
  import { isValidSolanaAddress } from "../wallet/solana-address.js";
14
+ import { fetchPinnedSolanaRpcRead } from "../wallet/solana-rpc-read-fetch.js";
14
15
  import { fetchSolanaMintInfoViaRpc, fetchSolanaNativeBalanceViaRpc, fetchSolanaTokenBalanceViaRpc, fetchSolanaWalletAssetsViaRpc, invalidateSolanaAssetRpcCaches, summarizeSolanaAssetRpcMetrics } from "../wallet/solana-assets.js";
15
16
  import { tryResolveSatRuntimeIds } from "../config/sat-runtime-ids.js";
16
17
  import { commitWalletPolicyConfigUpdate, prepareWalletPolicyConfigUpdate, resolveWalletPolicyConfig, resolveWalletRecurringTransferPolicy } from "../wallet/wallet-policy.js";
@@ -28,19 +29,22 @@ import { normalizeCanvasScopedUrl } from "./canvas-capability.js";
28
29
  import { A2UI_PATH, CANVAS_HOST_PATH, CANVAS_WS_PATH, handleA2uiHttpRequest } from "../canvas-host/a2ui.js";
29
30
  import { resolveFederationBondWallet } from "../wallet/solana-bond-signing.js";
30
31
  import { createAndSubmitFederationBondProof, loadPersistedFederationBondProof, submitFederationBondProof } from "../federation/auto-connect.js";
32
+ import { resolveNativeSignerWalletId } from "../wallet/native-signer-wallet-id.js";
31
33
  import { beginWalletApprovalAssertion, beginWalletPasskeyRegistration, consumeWalletApprovalGrant, createWalletApprovalChallenge, finishWalletApprovalAssertion, finishWalletPasskeyRegistration, listWalletPasskeys, readWalletApprovalAuthSnapshot, removeWalletPasskey, resolveWalletApprovalAuthMode, resolveWalletApprovalChallengeTtlSeconds, resolveWalletApprovalGrantTtlSeconds } from "../wallet/wallet-approval-auth.js";
32
34
  import { readWalletStatusSnapshot } from "../wallet/wallet-status.js";
33
35
  import { inspectSatBondPosition, inspectSatBondStakingDistributor, inspectSatBondStakingPosition, inspectSatChainSlot, invalidateSatReadCaches } from "../extensions/sat-mining/src/rpc-read.js";
34
36
  import { digestSatSubmissionIntent } from "../extensions/sat-mining/src/submission-ledger.js";
35
37
  import { runWithSatSubmissionWorkflow, submitSatCancelBondUnlock, submitSatClaimBondStakingRewards, submitSatFinalizeBondUnlock, submitSatIncreaseBondPosition, submitSatOpenBondPosition, submitSatRequestBondUnlock, submitSatSyncBondStakingPosition, submitSatSyncBondStakingRewards } from "../extensions/sat-mining/src/solana-submit.js";
36
38
  import { resolveAgentAvatar } from "../agents/identity-avatar.js";
37
- import { resolveNativeSignerWalletId } from "../wallet/native-signer-wallet-id.js";
39
+ import { configureSignerOwnedWalletNetwork } from "../wallet/signer-network-admin.js";
38
40
  import { listWalletInboundEvents, pollWalletInboundEvents, reconcileWalletInboundEvents, recordWalletInboundWebhookEvent } from "../wallet/wallet-inbound-events.js";
39
41
  import { walletSetupCommand } from "../commands/wallet.js";
40
42
  import { getDiagnosticStabilitySnapshot } from "../logging/diagnostic-stability.js";
41
43
  import { resolveManagedFederationPublicUrl } from "../managed/federation.js";
44
+ import { lockSignerOwnedWalletForArchive } from "../wallet/local-socket-signer-archive.js";
42
45
  import { LocalSignerPolicyAdminRequiredError, buildLocalSignerPolicyTightening, localSignerPolicyState } from "../wallet/local-socket-signer-policy.js";
43
46
  import { executeWalletStandardReview, prepareWalletStandardReview, readWalletStandardReviewTxHash } from "../wallet/wallet-standard-review.js";
47
+ import { renderQrPngBase64 } from "../web/qr-image.js";
44
48
  import { createA2aHandler } from "./a2a-http.js";
45
49
  import { CONTROL_UI_BOOT_CHECK_PATH, resolveControlUiBootCheck } from "./control-ui-boot-check.js";
46
50
  import { normalizePublicHost, resolveControlUiPublicHost } from "./control-ui-login.js";
@@ -100,12 +104,19 @@ function parseCookies(raw) {
100
104
  }
101
105
  return cookies;
102
106
  }
103
- function isLocalHostName(host) {
107
+ function normalizeHostName(host) {
104
108
  const raw = host.trim().toLowerCase();
105
- const normalized = raw.startsWith("[") ? (() => {
109
+ return raw.startsWith("[") ? (() => {
106
110
  const end = raw.indexOf("]");
107
111
  return end > 0 ? raw.slice(1, end) : raw;
108
112
  })() : raw.replace(/:\d+$/, "");
113
+ }
114
+ function isLiteralLoopbackHostName(host) {
115
+ const normalized = normalizeHostName(host);
116
+ return normalized === "localhost" || normalized === "127.0.0.1" || normalized === "::1";
117
+ }
118
+ function isLocalHostName(host) {
119
+ const normalized = normalizeHostName(host);
109
120
  return normalized === "localhost" || normalized === "127.0.0.1" || normalized === "::1" || normalized.endsWith(".local");
110
121
  }
111
122
  function isLoopbackBrowserOrigin(origin) {
@@ -114,7 +125,18 @@ function isLoopbackBrowserOrigin(origin) {
114
125
  if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
115
126
  return false;
116
127
  }
117
- return isLocalHostName(parsed.host);
128
+ return isLiteralLoopbackHostName(parsed.host);
129
+ } catch {
130
+ return false;
131
+ }
132
+ }
133
+ function isSameBrowserOriginHost(origin, host) {
134
+ try {
135
+ const parsed = new URL(origin);
136
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
137
+ return false;
138
+ }
139
+ return parsed.host.toLowerCase() === host.trim().toLowerCase();
118
140
  } catch {
119
141
  return false;
120
142
  }
@@ -132,7 +154,7 @@ function applyLoopbackCorsIfAllowed(req, res, host) {
132
154
  if (!origin) {
133
155
  return false;
134
156
  }
135
- if (!isLocalHostName(host) || !isLoopbackBrowserOrigin(origin)) {
157
+ if (!isLocalHostName(host) || !isLoopbackBrowserOrigin(origin) && !isSameBrowserOriginHost(origin, host)) {
136
158
  return false;
137
159
  }
138
160
  res.setHeader("Access-Control-Allow-Origin", origin);
@@ -659,7 +681,7 @@ async function readWalletSettingsSignerPolicy(params) {
659
681
  ...asset,
660
682
  destinations: [...asset.destinations]
661
683
  })),
662
- ...state === "locked" ? { guidance: "Signer policy is deny-all. Install an owner-reviewed policy through the native signer control socket; the Gateway can only tighten it." } : {}
684
+ ...state === "locked" ? { guidance: `This existing wallet is deny-all. Review its immutable role, then run: fased wallet policy activate-role-baseline --wallet-id ${walletId} --role ${policy.role} --confirm` } : {}
663
685
  };
664
686
  } catch {
665
687
  return {
@@ -935,20 +957,20 @@ async function fetchSolanaLamportsViaRpc(params) {
935
957
  if (!rpcUrl || !address) {
936
958
  return null;
937
959
  }
938
- const controller = new AbortController();
939
- const timeout = setTimeout(() => controller.abort(), Math.max(250, params.timeoutMs));
960
+ let release;
940
961
  try {
941
- const response = await fetch(rpcUrl, {
942
- method: "POST",
943
- headers: { "content-type": "application/json" },
962
+ const guarded = await fetchPinnedSolanaRpcRead({
963
+ rpcUrl,
964
+ timeoutMs: params.timeoutMs,
944
965
  body: JSON.stringify({
945
966
  jsonrpc: "2.0",
946
967
  id: "wallet-balance-fallback",
947
968
  method: "getBalance",
948
969
  params: [address]
949
- }),
950
- signal: controller.signal
970
+ })
951
971
  });
972
+ release = guarded.release;
973
+ const response = guarded.response;
952
974
  if (!response.ok) {
953
975
  recordWalletGatewayRpcMethod("direct.getBalance", "failure");
954
976
  return null;
@@ -962,7 +984,7 @@ async function fetchSolanaLamportsViaRpc(params) {
962
984
  recordWalletGatewayRpcMethod("direct.getBalance", "failure");
963
985
  return null;
964
986
  } finally {
965
- clearTimeout(timeout);
987
+ await release?.();
966
988
  }
967
989
  }
968
990
  function resolveWalletProbeTimeoutMs(env) {
@@ -1004,11 +1026,18 @@ function resolveMiningAgentWalletConflict(walletId) {
1004
1026
  return null;
1005
1027
  }
1006
1028
  const activeMiningWalletId = readSatMiningWalletIdFromConfig(loadConfig());
1007
- if (activeMiningWalletId === normalizedWalletId) {
1008
- return null;
1029
+ if (activeMiningWalletId && activeMiningWalletId !== normalizedWalletId) {
1030
+ return `SAT Mining already uses ${activeMiningWalletId}. Archive that singleton wallet before attaching a replacement.`;
1009
1031
  }
1010
1032
  const registry = readWalletProviderRegistry(process.env);
1011
1033
  const wallet = registry.wallets.find((entry) => entry.id === normalizedWalletId);
1034
+ const otherMiningWallet = registry.wallets.find((entry) => entry.id !== normalizedWalletId && resolveWalletUserRole(entry) === "mining");
1035
+ if (otherMiningWallet) {
1036
+ return `SAT Mining already has the singleton wallet ${otherMiningWallet.id}. Archive it before attaching a replacement.`;
1037
+ }
1038
+ if (!wallet) {
1039
+ return "SAT Mining requires an existing dedicated Mining wallet.";
1040
+ }
1012
1041
  const purpose = resolveWalletUserRole(wallet);
1013
1042
  if (normalizedWalletId === registry.defaultWalletId || purpose === "agent") {
1014
1043
  return "SAT Mining must use a dedicated Mining wallet. Create a new Mining wallet instead of reusing an Agent wallet.";
@@ -1288,9 +1317,9 @@ async function checkSolanaAtaReadiness(address, env) {
1288
1317
  }
1289
1318
  try {
1290
1319
  const USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
1291
- const response = await fetch(rpcUrl, {
1292
- method: "POST",
1293
- headers: { "Content-Type": "application/json" },
1320
+ const guarded = await fetchPinnedSolanaRpcRead({
1321
+ rpcUrl,
1322
+ timeoutMs: 1e4,
1294
1323
  body: JSON.stringify({
1295
1324
  jsonrpc: "2.0",
1296
1325
  id: 1,
@@ -1302,15 +1331,19 @@ async function checkSolanaAtaReadiness(address, env) {
1302
1331
  ]
1303
1332
  })
1304
1333
  });
1305
- if (!response.ok) {
1306
- recordWalletGatewayRpcMethod("direct.getTokenAccountsByOwner", "failure");
1307
- return false;
1334
+ try {
1335
+ if (!guarded.response.ok) {
1336
+ recordWalletGatewayRpcMethod("direct.getTokenAccountsByOwner", "failure");
1337
+ return false;
1338
+ }
1339
+ const payload = await guarded.response.json();
1340
+ const accounts = payload?.result?.value;
1341
+ const ok = Array.isArray(accounts);
1342
+ recordWalletGatewayRpcMethod("direct.getTokenAccountsByOwner", ok ? "success" : "failure");
1343
+ return ok && accounts.length > 0;
1344
+ } finally {
1345
+ await guarded.release();
1308
1346
  }
1309
- const payload = await response.json();
1310
- const accounts = payload?.result?.value;
1311
- const ok = Array.isArray(accounts);
1312
- recordWalletGatewayRpcMethod("direct.getTokenAccountsByOwner", ok ? "success" : "failure");
1313
- return ok && accounts.length > 0;
1314
1347
  } catch {
1315
1348
  recordWalletGatewayRpcMethod("direct.getTokenAccountsByOwner", "failure");
1316
1349
  return false;
@@ -3065,7 +3098,20 @@ function createGatewayHttpServer(opts) {
3065
3098
  const directLocalRequest = !tailscaleProxyConfigured && isDirectLoopbackRequest(req, trustedProxies);
3066
3099
  const ensureWalletApiAuthorized = async () => {
3067
3100
  if (directLocalRequest) {
3068
- return true;
3101
+ const requestOrigin = String(getHeader(req, "origin") ?? "").trim();
3102
+ if (!requestOrigin || isLoopbackBrowserOrigin(requestOrigin)) {
3103
+ return true;
3104
+ }
3105
+ if (!loopbackCorsApplied) {
3106
+ sendLoginResponse(403, {
3107
+ ok: false,
3108
+ error: {
3109
+ code: "forbidden_origin",
3110
+ message: "cross-origin browser requests are not allowed on the loopback wallet API"
3111
+ }
3112
+ });
3113
+ return false;
3114
+ }
3069
3115
  }
3070
3116
  let authorized = false;
3071
3117
  const bearer = getBearerToken(req);
@@ -4476,24 +4522,24 @@ function createGatewayHttpServer(opts) {
4476
4522
  const requestedWalletId = typeof payload.walletId === "string" ? payload.walletId.trim() : "";
4477
4523
  const roleProvided = Object.prototype.hasOwnProperty.call(payload, "role");
4478
4524
  const normalizedRole = roleProvided ? normalizeWalletUserRole(payload.role) : undefined;
4479
- const requestedRole = normalizedRole === "agent" || normalizedRole === "vault" ? normalizedRole : undefined;
4525
+ const requestedRole = normalizedRole === "agent" || normalizedRole === "mining" || normalizedRole === "vault" ? normalizedRole : undefined;
4480
4526
  if (roleProvided && !requestedRole) {
4481
4527
  sendLoginResponse(400, {
4482
4528
  ok: false,
4483
4529
  error: {
4484
4530
  code: "invalid_wallet_role",
4485
- message: "role must be agent or vault"
4531
+ message: "role must be agent, mining, or vault"
4486
4532
  }
4487
4533
  });
4488
4534
  return;
4489
4535
  }
4490
4536
  const activeMiningWalletId = readSatMiningWalletIdFromConfig(loadConfig());
4491
- if (requestedRole && requestedWalletId && requestedWalletId === activeMiningWalletId) {
4537
+ if (requestedRole === "mining" && activeMiningWalletId && requestedWalletId !== activeMiningWalletId) {
4492
4538
  sendLoginResponse(409, {
4493
4539
  ok: false,
4494
4540
  error: {
4495
4541
  code: "wallet_in_use",
4496
- message: "walletId is the singleton SAT mining wallet; delete and recreate @wallet:mining before changing this wallet role"
4542
+ message: `Mining already uses the singleton wallet ${activeMiningWalletId}; use the reviewed Replace/Archive flow instead of creating a second Mining wallet`
4497
4543
  }
4498
4544
  });
4499
4545
  return;
@@ -4581,6 +4627,16 @@ function createGatewayHttpServer(opts) {
4581
4627
  });
4582
4628
  return;
4583
4629
  }
4630
+ if (!requestedRole) {
4631
+ sendLoginResponse(400, {
4632
+ ok: false,
4633
+ error: {
4634
+ code: "invalid_wallet_role",
4635
+ message: "choose agent, mining, or vault; the native signer never infers a role"
4636
+ }
4637
+ });
4638
+ return;
4639
+ }
4584
4640
  const chain = inferLocalSignerCreateChain({
4585
4641
  payloadChain: payload.chain,
4586
4642
  walletId: requestedWalletId,
@@ -4597,18 +4653,26 @@ function createGatewayHttpServer(opts) {
4597
4653
  });
4598
4654
  return;
4599
4655
  }
4656
+ const rpcUrl = typeof payload.rpcUrl === "string" ? payload.rpcUrl.trim() : "";
4657
+ if (!rpcUrl) {
4658
+ sendLoginResponse(400, {
4659
+ ok: false,
4660
+ error: {
4661
+ code: "invalid_request",
4662
+ message: "one primary Solana RPC URL is required for native signer wallet creation"
4663
+ }
4664
+ });
4665
+ return;
4666
+ }
4600
4667
  try {
4601
- const effectiveEnv = {
4602
- ...process.env,
4603
- ...cfg.env?.vars
4604
- };
4605
4668
  await walletSetupCommand(silentWalletSetupRuntime, {
4606
4669
  mode: "local-signer-create",
4607
4670
  chain,
4608
4671
  walletId: requestedWalletId,
4609
4672
  walletName,
4610
- rpcUrl: resolveWalletRpcUrlFromEnv(effectiveEnv, chain, requestedWalletId),
4673
+ rpcUrl,
4611
4674
  role: requestedRole,
4675
+ force: true,
4612
4676
  nonInteractive: true,
4613
4677
  noSignerHints: true,
4614
4678
  noDoctor: true
@@ -4738,13 +4802,15 @@ function createGatewayHttpServer(opts) {
4738
4802
  const name = typeof payload.name === "string" && payload.name.trim() ? payload.name.trim() : undefined;
4739
4803
  const roleProvided = Object.prototype.hasOwnProperty.call(payload, "role");
4740
4804
  const normalizedRole = roleProvided ? normalizeWalletUserRole(payload.role) : undefined;
4741
- const requestedRole = normalizedRole === "agent" || normalizedRole === "vault" ? normalizedRole : undefined;
4742
- if (!walletId || !name && !roleProvided) {
4805
+ const requestedRole = normalizedRole === "agent" || normalizedRole === "mining" || normalizedRole === "vault" ? normalizedRole : undefined;
4806
+ const rpcUrl = typeof payload.rpcUrl === "string" ? payload.rpcUrl.trim() : "";
4807
+ const rpcProvided = Object.prototype.hasOwnProperty.call(payload, "rpcUrl");
4808
+ if (!walletId || !name && !roleProvided && !rpcProvided) {
4743
4809
  sendLoginResponse(400, {
4744
4810
  ok: false,
4745
4811
  error: {
4746
4812
  code: "invalid_request",
4747
- message: "walletId and name or role are required"
4813
+ message: "walletId and role or rpcUrl are required"
4748
4814
  }
4749
4815
  });
4750
4816
  return;
@@ -4754,7 +4820,7 @@ function createGatewayHttpServer(opts) {
4754
4820
  ok: false,
4755
4821
  error: {
4756
4822
  code: "invalid_wallet_role",
4757
- message: "role must be agent or vault"
4823
+ message: "role must be agent, mining, or vault"
4758
4824
  }
4759
4825
  });
4760
4826
  return;
@@ -4781,13 +4847,117 @@ function createGatewayHttpServer(opts) {
4781
4847
  });
4782
4848
  return;
4783
4849
  }
4850
+ if (rpcProvided) {
4851
+ if (!rpcUrl) {
4852
+ sendLoginResponse(400, {
4853
+ ok: false,
4854
+ error: {
4855
+ code: "invalid_request",
4856
+ message: "rpcUrl must be a non-empty primary Solana RPC URL"
4857
+ }
4858
+ });
4859
+ return;
4860
+ }
4861
+ if (existing.providerId !== "local-socket-signer") {
4862
+ sendLoginResponse(409, {
4863
+ ok: false,
4864
+ error: {
4865
+ code: "invalid_provider",
4866
+ message: "primary RPC editing is available only for native signer wallets"
4867
+ }
4868
+ });
4869
+ return;
4870
+ }
4871
+ const cfg = loadConfig();
4872
+ if (!ensureWalletApprovalAuthorized({
4873
+ operation: "wallet.network",
4874
+ cfg
4875
+ })) {
4876
+ return;
4877
+ }
4878
+ try {
4879
+ const effectiveEnv = {
4880
+ ...process.env,
4881
+ ...cfg.env?.vars
4882
+ };
4883
+ const signerWalletId = resolveNativeSignerWalletId(existing);
4884
+ const network = await configureSignerOwnedWalletNetwork({
4885
+ walletId: signerWalletId,
4886
+ primaryRpcUrl: rpcUrl,
4887
+ env: effectiveEnv,
4888
+ socketPath: resolveLocalSignerSocketPath(effectiveEnv)
4889
+ });
4890
+ const suffix = normalizeWalletIdForEnvSuffix(walletId)?.toUpperCase();
4891
+ const rpcKey = suffix ? `FASED_WALLET_SOLANA_RPC_URL__${suffix}` : "FASED_WALLET_SOLANA_RPC_URL";
4892
+ const configUpdate = await updateWalletConfig({
4893
+ env: effectiveEnv,
4894
+ mutate: (next) => {
4895
+ next.env = {
4896
+ ...next.env,
4897
+ vars: {
4898
+ ...next.env?.vars,
4899
+ [rpcKey]: rpcUrl
4900
+ }
4901
+ };
4902
+ }
4903
+ });
4904
+ if (!configUpdate.ok) {
4905
+ throw new Error(`signer accepted network version ${network.version}, but app configuration persistence failed: ${configUpdate.message}`);
4906
+ }
4907
+ upsertNamedWallet({
4908
+ walletId: existing.id,
4909
+ name: existing.name,
4910
+ providerId: existing.providerId,
4911
+ addresses: existing.addresses,
4912
+ metadata: {
4913
+ ...existing.metadata,
4914
+ networkHash: network.hash,
4915
+ networkVersion: network.version,
4916
+ networkReady: network.ready
4917
+ },
4918
+ env: process.env
4919
+ });
4920
+ appendWalletAuditEntry({
4921
+ action: "wallet_rpc_updated",
4922
+ actor: "control-ui",
4923
+ details: {
4924
+ walletId,
4925
+ networkVersion: network.version,
4926
+ networkReady: network.ready
4927
+ },
4928
+ env: process.env
4929
+ });
4930
+ } catch (err) {
4931
+ sendLoginResponse(502, {
4932
+ ok: false,
4933
+ error: {
4934
+ code: "wallet_rpc_update_failed",
4935
+ message: err instanceof Error ? err.message : String(err)
4936
+ }
4937
+ });
4938
+ return;
4939
+ }
4940
+ }
4784
4941
  const activeMiningWalletId = readSatMiningWalletIdFromConfig(loadConfig());
4785
- if (requestedRole && walletId === activeMiningWalletId) {
4942
+ if (requestedRole === "mining") {
4943
+ const miningConflict = resolveMiningAgentWalletConflict(walletId);
4944
+ if (miningConflict) {
4945
+ sendLoginResponse(409, {
4946
+ ok: false,
4947
+ error: {
4948
+ code: "wallet_in_use",
4949
+ message: miningConflict
4950
+ }
4951
+ });
4952
+ return;
4953
+ }
4954
+ }
4955
+ if (requestedRole && requestedRole !== "mining" && walletId === activeMiningWalletId) {
4786
4956
  sendLoginResponse(409, {
4787
4957
  ok: false,
4788
4958
  error: {
4789
4959
  code: "wallet_in_use",
4790
- message: "walletId is the singleton SAT mining wallet; delete and recreate @wallet:mining before changing this wallet role"
4960
+ message: "walletId is the singleton SAT Mining wallet; use the guarded Archive/Replace flow before creating a wallet with a different role"
4791
4961
  }
4792
4962
  });
4793
4963
  return;
@@ -4806,7 +4976,7 @@ function createGatewayHttpServer(opts) {
4806
4976
  ok: false,
4807
4977
  error: {
4808
4978
  code: "signer_wallet_role_immutable",
4809
- message: `The native signer owns this wallet as role=${signerPolicy.role}; the Gateway cannot change it to ${requestedRole}. ` + "Create a new signer-owned wallet with the required locked role through Local onboarding or an authenticated Hosting administrator session."
4979
+ message: `The native signer owns this wallet as role=${signerPolicy.role}; the Gateway cannot change it to ${requestedRole}. ` + "Create a new signer-owned wallet with the required locked role, then use the guarded Archive/Replace flow for the old wallet."
4810
4980
  }
4811
4981
  });
4812
4982
  return;
@@ -4863,6 +5033,8 @@ function createGatewayHttpServer(opts) {
4863
5033
  }
4864
5034
  const payload = body.value ?? {};
4865
5035
  const walletId = typeof payload.walletId === "string" ? payload.walletId.trim() : "";
5036
+ const archiveRequested = payload.archive === true;
5037
+ const archiveConfirmation = typeof payload.confirmWalletId === "string" ? payload.confirmWalletId.trim() : "";
4866
5038
  if (!walletId) {
4867
5039
  sendLoginResponse(400, {
4868
5040
  ok: false,
@@ -4873,13 +5045,23 @@ function createGatewayHttpServer(opts) {
4873
5045
  });
4874
5046
  return;
4875
5047
  }
5048
+ if (archiveRequested && archiveConfirmation !== walletId) {
5049
+ sendLoginResponse(400, {
5050
+ ok: false,
5051
+ error: {
5052
+ code: "archive_confirmation_required",
5053
+ message: "confirmWalletId must exactly match walletId"
5054
+ }
5055
+ });
5056
+ return;
5057
+ }
4876
5058
  const activeMiningWalletId = readSatMiningWalletIdFromConfig(loadConfig());
4877
- if (activeMiningWalletId === walletId) {
5059
+ if (activeMiningWalletId === walletId && !archiveRequested) {
4878
5060
  sendLoginResponse(409, {
4879
5061
  ok: false,
4880
5062
  error: {
4881
5063
  code: "wallet_in_use",
4882
- message: "walletId is the singleton SAT mining wallet; use onboarding or CLI guarded delete for @wallet:mining instead of this API"
5064
+ message: "walletId is the singleton SAT Mining wallet; request the guarded Archive flow instead of ordinary delete"
4883
5065
  }
4884
5066
  });
4885
5067
  return;
@@ -4896,6 +5078,16 @@ function createGatewayHttpServer(opts) {
4896
5078
  });
4897
5079
  return;
4898
5080
  }
5081
+ if (resolveWalletUserRole(existing) === "mining") {
5082
+ sendLoginResponse(409, {
5083
+ ok: false,
5084
+ error: {
5085
+ code: "mining_retirement_required",
5086
+ message: "Mining wallets cannot be deleted directly; use Retire and replace Mining wallet so signer acknowledgement precedes registry detachment"
5087
+ }
5088
+ });
5089
+ return;
5090
+ }
4899
5091
  const deletionSafety = checkNamedWalletDeletionSafety({
4900
5092
  walletId,
4901
5093
  env: process.env
@@ -4911,6 +5103,88 @@ function createGatewayHttpServer(opts) {
4911
5103
  });
4912
5104
  return;
4913
5105
  }
5106
+ if (existing.providerId === "local-socket-signer" && archiveRequested) {
5107
+ const cfg = loadConfig();
5108
+ if (!ensureWalletApprovalAuthorized({
5109
+ operation: "wallet.archive",
5110
+ cfg
5111
+ })) {
5112
+ return;
5113
+ }
5114
+ let archivedPolicy;
5115
+ try {
5116
+ const effectiveEnv = {
5117
+ ...process.env,
5118
+ ...cfg.env?.vars
5119
+ };
5120
+ archivedPolicy = await lockSignerOwnedWalletForArchive({
5121
+ wallet: existing,
5122
+ socketPath: resolveLocalSignerSocketPath(effectiveEnv)
5123
+ });
5124
+ const federationWalletId = resolveFederationBondDefaultWalletId(cfg);
5125
+ const suffix = normalizeWalletIdForEnvSuffix(walletId)?.toUpperCase();
5126
+ const rpcKey = suffix ? `FASED_WALLET_SOLANA_RPC_URL__${suffix}` : "FASED_WALLET_SOLANA_RPC_URL";
5127
+ const updated = await updateWalletConfig({
5128
+ env: effectiveEnv,
5129
+ mutate: (next) => {
5130
+ if (next.env?.vars) {
5131
+ delete next.env.vars[rpcKey];
5132
+ }
5133
+ const currentEntry = next.plugins?.entries?.["sat-mining"];
5134
+ const currentSatConfig = currentEntry?.config && typeof currentEntry.config === "object" && !Array.isArray(currentEntry.config) ? { ...currentEntry.config } : {};
5135
+ if (currentSatConfig.walletId === walletId) {
5136
+ delete currentSatConfig.walletId;
5137
+ next.plugins = {
5138
+ ...next.plugins,
5139
+ entries: {
5140
+ ...next.plugins?.entries,
5141
+ "sat-mining": {
5142
+ enabled: true,
5143
+ ...currentEntry,
5144
+ config: currentSatConfig
5145
+ }
5146
+ }
5147
+ };
5148
+ }
5149
+ if (federationWalletId === walletId) {
5150
+ applyFederationBondWalletConfig(next, null);
5151
+ }
5152
+ }
5153
+ });
5154
+ if (!updated.ok) {
5155
+ throw new Error(`signer locked the wallet, but attachment cleanup failed: ${updated.message}`);
5156
+ }
5157
+ } catch (err) {
5158
+ sendLoginResponse(502, {
5159
+ ok: false,
5160
+ error: {
5161
+ code: "wallet_archive_failed",
5162
+ message: err instanceof Error ? err.message : String(err)
5163
+ }
5164
+ });
5165
+ return;
5166
+ }
5167
+ const removed = deleteNamedWallet({
5168
+ walletId,
5169
+ env: process.env
5170
+ });
5171
+ appendWalletAuditEntry({
5172
+ action: "wallet_archived",
5173
+ actor: "control-ui",
5174
+ details: {
5175
+ walletId,
5176
+ signerWalletId: archivedPolicy.walletId,
5177
+ denyAllPolicyVersion: archivedPolicy.version
5178
+ },
5179
+ env: process.env
5180
+ });
5181
+ sendLoginResponse(200, {
5182
+ ok: true,
5183
+ removed: removed.removed,
5184
+ archived: true
5185
+ });
5186
+ return;
5187
+ }
4914
5188
  if (existing.providerId === "local-socket-signer" || existing.providerId === "embedded-keystore" || existing.metadata?.selfHosted === true) {
4915
5189
  sendLoginResponse(409, {
4916
5190
  ok: false,
@@ -4943,6 +5217,44 @@ function createGatewayHttpServer(opts) {
4943
5217
  });
4944
5218
  return;
4945
5219
  }
5220
+ if (requestPath === "/api/wallet/receive-qr") {
5221
+ if (!await ensureWalletApiAuthorized()) {
5222
+ return;
5223
+ }
5224
+ if (req.method !== "GET") {
5225
+ res.statusCode = 405;
5226
+ res.setHeader("Allow", "GET");
5227
+ sendLoginResponse(405, {
5228
+ ok: false,
5229
+ error: {
5230
+ code: "method_not_allowed",
5231
+ message: "method must be GET"
5232
+ }
5233
+ });
5234
+ return;
5235
+ }
5236
+ const walletId = parsedUrl.searchParams.get("walletId")?.trim() ?? "";
5237
+ const wallet = readWalletProviderRegistry(process.env).wallets.find((entry) => entry.id === walletId);
5238
+ const address = wallet?.addresses?.solana?.trim() ?? "";
5239
+ if (!walletId || !address || !isValidSolanaAddress(address)) {
5240
+ sendLoginResponse(404, {
5241
+ ok: false,
5242
+ error: {
5243
+ code: "wallet_address_not_found",
5244
+ message: "wallet address not found"
5245
+ }
5246
+ });
5247
+ return;
5248
+ }
5249
+ const png = Buffer.from(await renderQrPngBase64(`solana:${address}`), "base64");
5250
+ res.statusCode = 200;
5251
+ res.setHeader("Content-Type", "image/png");
5252
+ res.setHeader("Content-Length", String(png.byteLength));
5253
+ res.setHeader("Cache-Control", "private, no-store");
5254
+ res.setHeader("X-Content-Type-Options", "nosniff");
5255
+ res.end(png);
5256
+ return;
5257
+ }
4946
5258
  if (requestPath === "/api/wallet/assignments") {
4947
5259
  if (!await ensureWalletApiAuthorized()) {
4948
5260
  return;
@@ -4987,7 +5299,7 @@ function createGatewayHttpServer(opts) {
4987
5299
  ok: false,
4988
5300
  error: {
4989
5301
  code: "wallet_in_use",
4990
- message: "walletId is the singleton SAT mining wallet; delete and recreate @wallet:mining before making it the primary Agent wallet"
5302
+ message: "walletId is the singleton SAT Mining wallet; Archive/Replace Mining with an Agent wallet before making it the Default Agent wallet fallback"
4991
5303
  }
4992
5304
  });
4993
5305
  return;
@@ -4996,12 +5308,12 @@ function createGatewayHttpServer(opts) {
4996
5308
  const registry = readWalletProviderRegistry(process.env);
4997
5309
  const wallet = registry.wallets.find((entry) => entry.id === nextDefaultWalletId);
4998
5310
  const role = resolveWalletUserRole(wallet) ?? (registry.defaultWalletId === nextDefaultWalletId ? "agent" : undefined);
4999
- if (role && role !== "agent") {
5311
+ if (role !== "agent") {
5000
5312
  sendLoginResponse(409, {
5001
5313
  ok: false,
5002
5314
  error: {
5003
5315
  code: "wallet_purpose_locked",
5004
- message: "only Agent wallets can become the primary Agent fallback; create a new Agent wallet instead"
5316
+ message: "only an explicit Agent wallet can become the Default Agent wallet fallback; create or select an Agent wallet instead"
5005
5317
  }
5006
5318
  });
5007
5319
  return;
@@ -5014,9 +5326,34 @@ function createGatewayHttpServer(opts) {
5014
5326
  }
5015
5327
  const agentId = typeof payload.agentId === "string" ? payload.agentId.trim() : "";
5016
5328
  if (agentId) {
5329
+ const assignedWalletId = typeof payload.walletId === "string" ? payload.walletId.trim() : "";
5330
+ if (assignedWalletId) {
5331
+ const registry = readWalletProviderRegistry(process.env);
5332
+ const wallet = registry.wallets.find((entry) => entry.id === assignedWalletId);
5333
+ if (!wallet) {
5334
+ sendLoginResponse(404, {
5335
+ ok: false,
5336
+ error: {
5337
+ code: "wallet_not_found",
5338
+ message: "walletId does not exist"
5339
+ }
5340
+ });
5341
+ return;
5342
+ }
5343
+ if (resolveWalletUserRole(wallet) !== "agent") {
5344
+ sendLoginResponse(409, {
5345
+ ok: false,
5346
+ error: {
5347
+ code: "wallet_purpose_locked",
5348
+ message: "only an explicit Agent wallet can be assigned to an Agent"
5349
+ }
5350
+ });
5351
+ return;
5352
+ }
5353
+ }
5017
5354
  setAgentWalletAssignment({
5018
5355
  agentId,
5019
- walletId: typeof payload.walletId === "string" ? payload.walletId.trim() : undefined,
5356
+ walletId: assignedWalletId || undefined,
5020
5357
  env: process.env
5021
5358
  });
5022
5359
  }
@@ -5533,10 +5870,12 @@ function createGatewayHttpServer(opts) {
5533
5870
  }
5534
5871
  } };
5535
5872
  statusPayload.wallets = registry.wallets.map((wallet) => {
5873
+ const liveWallet = snapshot.wallets?.find((entry) => entry.id === wallet.id);
5536
5874
  const solana = findWalletChainEntry(snapshotAny.chainWallets?.solana, wallet.id);
5537
- const readiness = {
5875
+ const readiness = liveWallet?.readiness ?? {
5538
5876
  keystore: Boolean(solana?.decryptReady ?? false),
5539
- rpc: Boolean(solana?.rpcConfigured)
5877
+ rpc: Boolean(solana?.rpcConfigured),
5878
+ ready: false
5540
5879
  };
5541
5880
  return {
5542
5881
  id: wallet.id,
@@ -5548,7 +5887,7 @@ function createGatewayHttpServer(opts) {
5548
5887
  readiness,
5549
5888
  chains: wallet.addresses?.solana ? ["solana"] : [],
5550
5889
  rpcConfigured: readiness.rpc,
5551
- health: readiness.keystore ? "ok" : "degraded"
5890
+ health: readiness.ready ? "ok" : "degraded"
5552
5891
  };
5553
5892
  });
5554
5893
  if (configuredProviderId === "local-socket-signer") {
@@ -7129,7 +7468,7 @@ function createGatewayHttpServer(opts) {
7129
7468
  });
7130
7469
  return;
7131
7470
  }
7132
- if (operation !== "wallet.approve" && operation !== "wallet.policy" && operation !== "wallet.settings" && operation !== "wallet.provider-credentials" && operation !== "wallet.passkey-enroll" && operation !== "wallet.passkey-remove" && operation !== "wallet.reset" && operation !== "wallet.rotate" && operation !== "wallet.execution-mode" && operation !== "wallet.send" && operation !== "mining.capital" && operation !== "mining.policy") {
7471
+ if (operation !== "wallet.approve" && operation !== "wallet.policy" && operation !== "wallet.settings" && operation !== "wallet.provider-credentials" && operation !== "wallet.network" && operation !== "wallet.archive" && operation !== "wallet.passkey-enroll" && operation !== "wallet.passkey-remove" && operation !== "wallet.reset" && operation !== "wallet.rotate" && operation !== "wallet.execution-mode" && operation !== "wallet.send" && operation !== "mining.capital" && operation !== "mining.policy") {
7133
7472
  sendLoginResponse(400, {
7134
7473
  ok: false,
7135
7474
  error: {
@@ -7263,7 +7602,7 @@ function createGatewayHttpServer(opts) {
7263
7602
  const payloadRaw = body.value ?? {};
7264
7603
  const requestId = typeof payloadRaw.requestId === "string" ? payloadRaw.requestId.trim() || undefined : undefined;
7265
7604
  const operation = typeof payloadRaw.operation === "string" ? payloadRaw.operation.trim() || undefined : undefined;
7266
- if (operation && operation !== "wallet.approve" && operation !== "wallet.policy" && operation !== "wallet.settings" && operation !== "wallet.provider-credentials" && operation !== "wallet.passkey-enroll" && operation !== "wallet.passkey-remove" && operation !== "wallet.reset" && operation !== "wallet.rotate" && operation !== "wallet.execution-mode" && operation !== "wallet.send" && operation !== "mining.capital" && operation !== "mining.policy") {
7605
+ if (operation && operation !== "wallet.approve" && operation !== "wallet.policy" && operation !== "wallet.settings" && operation !== "wallet.provider-credentials" && operation !== "wallet.network" && operation !== "wallet.archive" && operation !== "wallet.passkey-enroll" && operation !== "wallet.passkey-remove" && operation !== "wallet.reset" && operation !== "wallet.rotate" && operation !== "wallet.execution-mode" && operation !== "wallet.send" && operation !== "mining.capital" && operation !== "mining.policy") {
7267
7606
  sendLoginResponse(400, {
7268
7607
  ok: false,
7269
7608
  error: {