@fased/fased 0.1.62 → 0.1.64

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 (341) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +38 -30
  3. package/config/dependency-ownership.json +1 -1
  4. package/config/signer-policies/README.md +49 -0
  5. package/config/signer-policies/agent.json.template +32 -0
  6. package/config/signer-policies/mining.json.template +69 -0
  7. package/config/signer-policies/network.json.template +5 -0
  8. package/config/signer-policies/vault.json.template +70 -0
  9. package/config/signer-protocol-v2.json +76 -0
  10. package/config/typescript-noemit-baseline.json +680 -0
  11. package/dist/agents/auth-profiles/store.js +267 -31
  12. package/dist/agents/bash-tools.exec.js +10 -4
  13. package/dist/agents/current-model-catalog.js +3 -3
  14. package/dist/agents/memory-search.js +7 -1
  15. package/dist/agents/model-catalog-access.js +1 -2
  16. package/dist/agents/model-catalog-snapshot.js +1 -1
  17. package/dist/agents/model-catalog-source.js +1 -1
  18. package/dist/agents/model-metadata.js +1 -1
  19. package/dist/agents/model-selection.js +2 -28
  20. package/dist/agents/models-config.providers.js +6 -6
  21. package/dist/agents/pi-embedded-runner/compact.js +15 -7
  22. package/dist/agents/pi-embedded-runner/model.js +1 -1
  23. package/dist/agents/pi-embedded-runner/run/attempt.js +24 -13
  24. package/dist/agents/pi-embedded-runner/run.js +2 -0
  25. package/dist/agents/pi-embedded-runner/tool-split.js +40 -2
  26. package/dist/agents/pi-tool-definition-adapter.js +16 -1
  27. package/dist/agents/pi-tools.js +2 -1
  28. package/dist/agents/sandbox/config.js +1 -1
  29. package/dist/agents/sandbox/context.js +27 -2
  30. package/dist/agents/skills/env-overrides.js +38 -28
  31. package/dist/agents/skills/trust.js +93 -0
  32. package/dist/agents/skills/workspace.js +128 -0
  33. package/dist/agents/skills-clawhub.js +36 -11
  34. package/dist/agents/skills-marketplace-scan.js +49 -0
  35. package/dist/agents/tools/marketplace-offer-draft-tool.js +1 -1
  36. package/dist/agents/tools/mining-tool.js +1 -1
  37. package/dist/agents/tools/session-status-tool.js +1 -1
  38. package/dist/agents/tools/wallet-action-tool.js +172 -36
  39. package/dist/agents/tools/wallet-skill-policy.js +65 -23
  40. package/dist/agents/tools/wallet-tool.js +7 -11
  41. package/dist/auto-reply/reply/directive-handling.fast-lane.js +2 -1
  42. package/dist/auto-reply/reply/directive-handling.impl.js +2 -1
  43. package/dist/auto-reply/reply/directive-handling.model.js +2 -1
  44. package/dist/auto-reply/reply/directive-handling.persist.js +2 -2
  45. package/dist/auto-reply/reply/get-reply-directives-apply.js +2 -0
  46. package/dist/auto-reply/reply/get-reply-inline-actions.js +25 -1
  47. package/dist/auto-reply/reply/model-selection.js +7 -4
  48. package/dist/auto-reply/reply/session-reset-model.js +4 -4
  49. package/dist/brand.js +1 -1
  50. package/dist/build-info.json +3 -3
  51. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  52. package/dist/channel-web.js +2 -2
  53. package/dist/cli/gateway-cli/run.js +8 -0
  54. package/dist/cli/program/command-registry.js +1 -1
  55. package/dist/cli/program/register.onboard.js +2 -3
  56. package/dist/cli/program/register.wallet.js +16 -123
  57. package/dist/cli/providers-cli.js +1 -1
  58. package/dist/cli/sat-cli.js +129 -2
  59. package/dist/cli/update-cli/update-command.js +260 -11
  60. package/dist/commands/auth-choice.apply.api-providers.js +3 -3
  61. package/dist/commands/doctor-config-flow.js +1 -1
  62. package/dist/commands/doctor-legacy-config.js +2 -13
  63. package/dist/commands/model-picker.js +6 -6
  64. package/dist/commands/models/auth.js +1 -1
  65. package/dist/commands/onboard-auth.config-gateways.js +1 -1
  66. package/dist/commands/onboard-auth.credentials.js +32 -8
  67. package/dist/commands/onboard-non-interactive/local/wallet-config.js +28 -27
  68. package/dist/commands/onboard-non-interactive/local.js +5 -1
  69. package/dist/commands/wallet.js +568 -1911
  70. package/dist/config/config.js +2 -2
  71. package/dist/config/io.js +64 -2
  72. package/dist/config/schema.help.js +8 -7
  73. package/dist/config/schema.labels.js +5 -4
  74. package/dist/config/zod-schema.agent-runtime.js +1 -0
  75. package/dist/config/zod-schema.js +5 -0
  76. package/dist/control-ui/assets/{app-DLil9VO1.js → app-bR-fO5ty.js} +1511 -1555
  77. package/dist/control-ui/assets/{config-CU5Zi8Dh.js → config-LeImukCW.js} +10 -10
  78. package/dist/control-ui/assets/{federation-D10Q4eP_.js → federation-DgA9e-Pn.js} +364 -422
  79. package/dist/control-ui/assets/{index-DdE_NIXo.js → index-ckCxHwjy.js} +3 -3
  80. package/dist/control-ui/assets/{mining-BdhMn18r.js → mining-BgHFh0ei.js} +134 -114
  81. package/dist/control-ui/assets/mining-commit-DFpB_4JN.js +2 -0
  82. package/dist/control-ui/assets/{open-external-url-DwSiwMPl.js → open-external-url-ulF1B-sA.js} +3 -3
  83. package/dist/control-ui/assets/{providers-QMn4w2_f.js → providers-CIx9fxc6.js} +2 -2
  84. package/dist/control-ui/assets/{wallet-C2g294QT.js → wallet-DvStR_AT.js} +259 -377
  85. package/dist/control-ui/index.html +1 -1
  86. package/dist/control-ui/version.json +1 -1
  87. package/dist/daemon/systemd-system.js +56 -50
  88. package/dist/extensions/sat-mining/src/protocol-contract.js +43 -3
  89. package/dist/extensions/sat-mining/src/rpc-read.js +235 -67
  90. package/dist/extensions/sat-mining/src/sat-bond-layout.js +5 -2
  91. package/dist/extensions/sat-mining/src/signer-codec-manifest.js +406 -0
  92. package/dist/extensions/sat-mining/src/solana-submit.js +1038 -397
  93. package/dist/extensions/sat-mining/src/submission-ledger.js +349 -0
  94. package/dist/federation/auto-connect.js +98 -8
  95. package/dist/federation/marketplace-capability-adapter.js +33 -7
  96. package/dist/federation/marketplace-delivery-outbox.js +196 -0
  97. package/dist/federation/marketplace-delivery.js +351 -215
  98. package/dist/federation/marketplace-direct-payment.js +272 -141
  99. package/dist/federation/marketplace-escrow.js +607 -249
  100. package/dist/federation/marketplace-settlement-ledger.js +352 -0
  101. package/dist/federation/offers.js +9 -7
  102. package/dist/federation/peer-auth-v2.js +529 -0
  103. package/dist/gateway/a2a-http.js +425 -96
  104. package/dist/gateway/a2a-settlement.js +259 -153
  105. package/dist/gateway/a2a-task-store.js +524 -0
  106. package/dist/gateway/auth.js +38 -2
  107. package/dist/gateway/config-reload.js +46 -33
  108. package/dist/gateway/durable-capacity.js +31 -0
  109. package/dist/gateway/federation-http.js +540 -135
  110. package/dist/gateway/federation-marketplace-run-store.js +397 -0
  111. package/dist/gateway/federation-marketplace.js +535 -230
  112. package/dist/gateway/mutating-admin-rpc-rate-limit.js +5 -0
  113. package/dist/gateway/security-path.js +1 -1
  114. package/dist/gateway/server-http.js +1084 -1205
  115. package/dist/gateway/server-methods/models.js +1 -1
  116. package/dist/gateway/server-methods/secrets.js +40 -0
  117. package/dist/gateway/server-methods.js +1 -2
  118. package/dist/gateway/server-reload-handlers.js +196 -66
  119. package/dist/gateway/server-runtime-config.js +1 -1
  120. package/dist/gateway/server-secrets-runtime.js +288 -0
  121. package/dist/gateway/server.impl.js +73 -42
  122. package/dist/gateway/sessions-patch.js +3 -5
  123. package/dist/index.js +4 -2
  124. package/dist/infra/fasedhub.js +64 -1
  125. package/dist/infra/local-source-paired-update.js +538 -0
  126. package/dist/infra/restart.js +41 -10
  127. package/dist/infra/update-runner.js +96 -0
  128. package/dist/light-update-status.js +11 -11
  129. package/dist/memory/backend-config.js +2 -1
  130. package/dist/memory/session-files.js +16 -2
  131. package/dist/plugin-sdk/{accounts-DxblobYM.js → accounts-Brn3-uWN.js} +1 -1
  132. package/dist/plugin-sdk/{actions-D4UNimWa.js → actions-DRu135h0.js} +2 -2
  133. package/dist/plugin-sdk/{active-listener-CFuy106s.js → active-listener-B_NMtgPL.js} +1 -1
  134. package/dist/plugin-sdk/{audio-preflight-Bwi2Oq03.js → audio-preflight-BYxAt775.js} +27 -27
  135. package/dist/plugin-sdk/{audit-Cs0K7LBp.js → audit-C5d2z5U1.js} +1 -1
  136. package/dist/plugin-sdk/{channel-access-D5aHoTY8.js → channel-access-CcFRdRPO.js} +1 -1
  137. package/dist/plugin-sdk/{channel-access-configure-Dvch9mMQ.js → channel-access-configure-DaI5wevh.js} +1 -1
  138. package/dist/plugin-sdk/channel-plugin-common.d.ts +3 -3
  139. package/dist/plugin-sdk/channel-plugin-common.js +4 -4
  140. package/dist/plugin-sdk/{chunk-B0FUGc5A.js → chunk-CyoyqpbG.js} +1 -1
  141. package/dist/plugin-sdk/{chunk-items-DzT4L4Ty.js → chunk-items-DIRHG2gJ.js} +1 -1
  142. package/dist/plugin-sdk/command-status.d.ts +2 -2
  143. package/dist/plugin-sdk/command-status.js +27 -27
  144. package/dist/plugin-sdk/{commands-registry-DmDRhRAU.js → commands-registry-BfqdEWdF.js} +2 -2
  145. package/dist/plugin-sdk/{common-CfUOowOp.js → common-HCcXU-NR.js} +1 -1
  146. package/dist/plugin-sdk/{config-mxULv0fQ.js → config-DlWVeLFB.js} +7113 -6877
  147. package/dist/plugin-sdk/{config-schema-BrkmtFxB.js → config-schema-BOT7kHb6.js} +6779 -3696
  148. package/dist/plugin-sdk/device-pair.d.ts +1 -1
  149. package/dist/plugin-sdk/discord.d.ts +6 -6
  150. package/dist/plugin-sdk/discord.js +19 -19
  151. package/dist/plugin-sdk/file-lock.js +35 -4
  152. package/dist/plugin-sdk/{filesystem-manager-ZoHU8oA7.js → filesystem-manager-DoI8IBNF.js} +1 -1
  153. package/dist/plugin-sdk/{group-mentions-BHRff6j8.d.ts → group-mentions-q3hUTCt7.d.ts} +1 -1
  154. package/dist/plugin-sdk/{handle-action-DbVCF9HZ.js → handle-action-hvwapMKg.js} +13 -13
  155. package/dist/plugin-sdk/{helpers-BXbdgs5H.js → helpers-DMBU4oTo.js} +1 -1
  156. package/dist/plugin-sdk/{image-ops-D7jPxaph.js → image-ops-DQi0n_Tt.js} +1 -1
  157. package/dist/plugin-sdk/index.d.ts +21 -39
  158. package/dist/plugin-sdk/index.js +29 -29
  159. package/dist/plugin-sdk/{login-BRxww6nv.js → login-Ca583Okc.js} +3 -3
  160. package/dist/plugin-sdk/{login-qr-DJXsk9XH.js → login-qr-CGciBSNP.js} +3 -3
  161. package/dist/plugin-sdk/{manager-Y2S_5IlI.js → manager-BegWxNYX.js} +4 -4
  162. package/dist/plugin-sdk/{markdown-tables-DHLFQqaF.js → markdown-tables-DCOXEUfZ.js} +1 -1
  163. package/dist/plugin-sdk/{media-Czo4carg.js → media-CAdQF5TG.js} +47 -47
  164. package/dist/plugin-sdk/{memory-search-DTjIMw8J.js → memory-search-CrFPOeTH.js} +4 -1
  165. package/dist/plugin-sdk/{monitor-M_ta2lMc.js → monitor-BCFDaqJ-.js} +31 -31
  166. package/dist/plugin-sdk/{monitor-wYVncM9w.js → monitor-BgbjRiPM.js} +32 -32
  167. package/dist/plugin-sdk/{openresponses-http-B2GmeSt4.js → openresponses-http-DFf87SMM.js} +27 -27
  168. package/dist/plugin-sdk/{optional-runtime-dependency-DdMbxS24.js → optional-runtime-dependency-0dznm3ZO.js} +1 -1
  169. package/dist/plugin-sdk/{outbound-Db8mRN6P.js → outbound-BgVDdHxx.js} +6 -6
  170. package/dist/plugin-sdk/{outbound-attachment-BqFjcypb.js → outbound-attachment-QqTnIhZL.js} +2 -2
  171. package/dist/plugin-sdk/{pairing-message-B3bHYGaw.d.ts → pairing-message-CS-9BHug.d.ts} +2 -2
  172. package/dist/plugin-sdk/{pairing-message-B3GC_7CD.js → pairing-message-D0CoLHRJ.js} +1 -1
  173. package/dist/plugin-sdk/{pairing-store-CQikgIbw.js → pairing-store-BChahgI2.js} +2 -2
  174. package/dist/plugin-sdk/{pi-model-discovery-CY3m5zJP.js → pi-model-discovery-BX_JLcEJ.js} +1 -1
  175. package/dist/plugin-sdk/{pi-model-discovery-runtime-DSh1T2pn.js → pi-model-discovery-runtime-DxTMz_XF.js} +2 -2
  176. package/dist/plugin-sdk/provider-web-search-config-contract.d.ts +2 -2
  177. package/dist/plugin-sdk/{pw-ai-AFUqn-tQ.js → pw-ai-natjh3rm.js} +3 -3
  178. package/dist/plugin-sdk/{qmd-manager-DSyx77KY.js → qmd-manager-CgRaTxUq.js} +1 -1
  179. package/dist/plugin-sdk/{query-expansion-CAI2kKLQ.js → query-expansion-BG3FgzF7.js} +14 -3
  180. package/dist/plugin-sdk/{replies-Fzh9PhXl.js → replies-ChYo5ZQM.js} +2 -2
  181. package/dist/plugin-sdk/{resolve-outbound-target-KpF3co6T.js → resolve-outbound-target-D-KGgHJZ.js} +1 -1
  182. package/dist/plugin-sdk/{resolve-route-CDl74O19.js → resolve-route-C8tvQm8Z.js} +1 -1
  183. package/dist/plugin-sdk/{retry-policy-D4uiqUOG.js → retry-policy-C6SjKjQA.js} +1 -1
  184. package/dist/plugin-sdk/{runtime-Bb6KRnCC.js → runtime-2B9_WKoI.js} +8 -3
  185. package/dist/plugin-sdk/{runtime-dependency-DLDmB94f.js → runtime-dependency-DczkvPxn.js} +2 -2
  186. package/dist/plugin-sdk/sat-runtime.d.ts +549 -309
  187. package/dist/plugin-sdk/sat-runtime.js +29 -29
  188. package/dist/plugin-sdk/{send-C4k4JuJN.js → send-B6KkGKZD.js} +6 -6
  189. package/dist/plugin-sdk/{send-D-uDKGIq.js → send-CFzGKJXo.js} +6 -6
  190. package/dist/plugin-sdk/{send-Du8ykP1k.js → send-DCO441U7.js} +8 -8
  191. package/dist/plugin-sdk/{send-5nFktR1n.js → send-gDoGA-7e.js} +5 -5
  192. package/dist/plugin-sdk/{send-Bfzf11VY.js → send-q9HXrSwK.js} +7 -7
  193. package/dist/plugin-sdk/{session-CS-Td9EM.js → session-DHD4ZKyG.js} +2 -2
  194. package/dist/plugin-sdk/{skill-commands-CFAlXWHk.js → skill-commands-FKEPLS7T.js} +201 -26
  195. package/dist/plugin-sdk/{slack-CxE1mfzj.js → slack-BUTt8cxK.js} +39 -39
  196. package/dist/plugin-sdk/{slack-actions-BcMkNv1Z.js → slack-actions-DhbMcp8M.js} +11 -11
  197. package/dist/plugin-sdk/slack.d.ts +6 -6
  198. package/dist/plugin-sdk/slack.js +9 -9
  199. package/dist/plugin-sdk/{sticker-cache-CGzGWu00.js → sticker-cache-D0wHVqjt.js} +3 -3
  200. package/dist/plugin-sdk/{tables-bFXyRpkx.js → tables-B3jr0Pvq.js} +1 -1
  201. package/dist/plugin-sdk/{targets-DH7a9lKB.js → targets-CtSbTxYX.js} +1 -1
  202. package/dist/plugin-sdk/{telegram-actions-DaEcYQ8r.js → telegram-actions-CC_qN4v0.js} +30 -30
  203. package/dist/plugin-sdk/telegram.d.ts +6 -6
  204. package/dist/plugin-sdk/telegram.js +8 -8
  205. package/dist/plugin-sdk/{thread-bindings.lifecycle-CgOvvdw8.js → thread-bindings.lifecycle-QbkNl-m6.js} +3 -3
  206. package/dist/plugin-sdk/{thread-bindings.state-CQpk44Hq.js → thread-bindings.state-D9hg_M1c.js} +2 -2
  207. package/dist/plugin-sdk/tmp-fased-dir-xOTGlzBE.d.ts +486 -0
  208. package/dist/plugin-sdk/{types-vcJrC_uv.d.ts → types-C3avNytw.d.ts} +3 -4
  209. package/dist/plugin-sdk/{types-DyhXnYUr.d.ts → types-D0kDG8l0.d.ts} +3 -2
  210. package/dist/plugin-sdk/{types-DFAW-n1J.d.ts → types-DWfbSAaM.d.ts} +2 -2
  211. package/dist/plugin-sdk/{types.plugin-_fbu40Re.d.ts → types.plugin-BcSBq42x.d.ts} +8 -3
  212. package/dist/plugin-sdk/{web-CInY-lCv.js → web-BXsJ1vqe.js} +32 -32
  213. package/dist/plugin-sdk/{whatsapp-actions-BtSc2CBv.js → whatsapp-actions-Bj_rOncr.js} +13 -13
  214. package/dist/plugin-sdk/{whatsapp-heartbeat-Bg9TuYzl.js → whatsapp-heartbeat-B1WHkJIg.js} +2 -2
  215. package/dist/plugin-sdk/whatsapp.d.ts +13 -13
  216. package/dist/plugin-sdk/whatsapp.js +12 -12
  217. package/dist/plugin-sdk/{wsl-C4DGFkOd.js → wsl-J-HscWxS.js} +3 -3
  218. package/dist/plugin-sdk/{zod-schema.providers-core-BEbNizPe.d.ts → zod-schema.providers-core-CIzCjXH9.d.ts} +61 -61
  219. package/dist/providers/refresh.js +1 -1
  220. package/dist/providers/registry.js +1 -1
  221. package/dist/secrets/apply.js +1 -1
  222. package/dist/secrets/runtime.js +1 -1
  223. package/dist/wallet/external-submission-ledger.js +376 -0
  224. package/dist/wallet/legacy-embedded-keystore.js +40 -0
  225. package/dist/wallet/local-socket-signer-archive.js +30 -0
  226. package/dist/wallet/local-socket-signer-lifecycle.js +97 -0
  227. package/dist/wallet/local-socket-signer-policy.js +166 -0
  228. package/dist/wallet/local-socket-signer-protocol.js +561 -118
  229. package/dist/wallet/native-signer-wallet-id.js +25 -0
  230. package/dist/wallet/providers/alchemy-adapter.js +3 -0
  231. package/dist/wallet/providers/local-socket-signer-adapter.js +380 -105
  232. package/dist/wallet/providers/turnkey-adapter.js +292 -34
  233. package/dist/wallet/providers/wallet-standard-adapter.js +100 -0
  234. package/dist/wallet/signer-network-admin.js +184 -0
  235. package/dist/wallet/signer-protocol-v2.generated.js +90 -0
  236. package/dist/wallet/solana-bond-signing.js +157 -69
  237. package/dist/wallet/solana-reviewed-transaction.js +375 -0
  238. package/dist/wallet/solana-swap.js +520 -64
  239. package/dist/wallet/solana-transaction-inspection.js +106 -4
  240. package/dist/wallet/solana-trigger.js +658 -320
  241. package/dist/wallet/turnkey-reviewed-state.js +370 -0
  242. package/dist/wallet/wallet-approval-auth.js +17 -28
  243. package/dist/wallet/wallet-atomic-state.js +53 -0
  244. package/dist/wallet/wallet-canary.js +0 -1
  245. package/dist/wallet/wallet-inbound-events.js +16 -11
  246. package/dist/wallet/wallet-policy-simulation.js +1 -1
  247. package/dist/wallet/wallet-policy.js +90 -36
  248. package/dist/wallet/wallet-provider-capabilities.js +28 -4
  249. package/dist/wallet/wallet-provider-registry.js +57 -44
  250. package/dist/wallet/wallet-provider-resolver.js +62 -63
  251. package/dist/wallet/wallet-runtime-config.js +28 -41
  252. package/dist/wallet/wallet-secrets-store.js +22 -0
  253. package/dist/wallet/wallet-send-approvals.js +1891 -589
  254. package/dist/wallet/wallet-send-execution-ledger.js +292 -0
  255. package/dist/wallet/wallet-settlement-links.js +14 -13
  256. package/dist/wallet/wallet-standard-review.js +353 -0
  257. package/dist/wallet/wallet-status.js +0 -9
  258. package/dist/wallet/wallet-task-ledger.js +16 -2
  259. package/dist/web/auto-reply/heartbeat-runner.js +1 -1
  260. package/dist/web/auto-reply/monitor.js +2 -2
  261. package/dist/wizard/host-security-capability.js +14 -23
  262. package/dist/wizard/onboarding.finalize.js +63 -184
  263. package/dist/wizard/onboarding.host-security.js +117 -1367
  264. package/dist/wizard/onboarding.js +172 -217
  265. package/dist/wizard/onboarding.wallet.js +232 -785
  266. package/extensions/acpx/package.json +1 -1
  267. package/extensions/bluebubbles/package.json +1 -1
  268. package/extensions/copilot-proxy/package.json +1 -1
  269. package/extensions/diagnostics-otel/package.json +1 -1
  270. package/extensions/fased-federation/package.json +1 -1
  271. package/extensions/google-gemini-cli-auth/package.json +1 -1
  272. package/extensions/imessage/package.json +1 -1
  273. package/extensions/irc/package.json +1 -1
  274. package/extensions/line/package.json +1 -1
  275. package/extensions/llm-task/package.json +1 -1
  276. package/extensions/lobster/package.json +1 -1
  277. package/extensions/matrix/CHANGELOG.md +12 -0
  278. package/extensions/matrix/package.json +1 -1
  279. package/extensions/mattermost/package.json +1 -1
  280. package/extensions/memory-core/package.json +2 -2
  281. package/extensions/memory-lancedb/package.json +1 -1
  282. package/extensions/minimax-portal-auth/package.json +1 -1
  283. package/extensions/msteams/CHANGELOG.md +12 -0
  284. package/extensions/msteams/package.json +1 -1
  285. package/extensions/nextcloud-talk/package.json +1 -1
  286. package/extensions/nostr/CHANGELOG.md +12 -0
  287. package/extensions/nostr/package.json +1 -1
  288. package/extensions/open-prose/package.json +1 -1
  289. package/extensions/sat-mining/index.ts +498 -153
  290. package/extensions/sat-mining/package.json +1 -1
  291. package/extensions/sat-mining/signer-codec-schema.v1.json +374 -0
  292. package/extensions/sat-mining/src/audit-store.ts +50 -5
  293. package/extensions/sat-mining/src/claim-service.ts +85 -14
  294. package/extensions/sat-mining/src/config.ts +9 -0
  295. package/extensions/sat-mining/src/cycle-progress.ts +16 -8
  296. package/extensions/sat-mining/src/epoch-service.ts +9 -5
  297. package/extensions/sat-mining/src/gateway-runner.ts +21 -1
  298. package/extensions/sat-mining/src/protocol-contract.ts +45 -1
  299. package/extensions/sat-mining/src/recovery-service.ts +312 -0
  300. package/extensions/sat-mining/src/round-watcher.ts +391 -81
  301. package/extensions/sat-mining/src/rpc-read.ts +330 -89
  302. package/extensions/sat-mining/src/runtime.ts +14 -0
  303. package/extensions/sat-mining/src/sat-bond-layout.ts +6 -0
  304. package/extensions/sat-mining/src/signer-codec-manifest.ts +429 -0
  305. package/extensions/sat-mining/src/solana-submit.ts +1199 -428
  306. package/extensions/sat-mining/src/submission-ledger.ts +433 -0
  307. package/extensions/signal/package.json +1 -1
  308. package/extensions/synology-chat/package.json +1 -1
  309. package/extensions/tlon/package.json +1 -1
  310. package/extensions/twitch/CHANGELOG.md +12 -0
  311. package/extensions/twitch/package.json +1 -1
  312. package/extensions/voice-call/CHANGELOG.md +12 -0
  313. package/extensions/voice-call/package.json +1 -1
  314. package/extensions/zalo/CHANGELOG.md +12 -0
  315. package/extensions/zalo/package.json +1 -1
  316. package/extensions/zalouser/CHANGELOG.md +12 -0
  317. package/extensions/zalouser/package.json +1 -1
  318. package/install.sh +4955 -0
  319. package/package.json +23 -5
  320. package/scripts/fased-host-updater.mjs +1421 -0
  321. package/scripts/fased-host-updaterctl.mjs +186 -0
  322. package/scripts/fased-managed-service.sh +36 -0
  323. package/scripts/fased-managed-updater.mjs +3065 -156
  324. package/scripts/fased-signer-enroll-hosting.sh +275 -0
  325. package/scripts/fased-signer-network-hosting.sh +143 -0
  326. package/scripts/fased-signer-owner-policy.mjs +1430 -0
  327. package/scripts/fased-signer-policy-hosting.sh +50 -0
  328. package/scripts/fased-signer-policy-local.sh +26 -0
  329. package/scripts/hosted-release-manifest.mjs +185 -0
  330. package/scripts/install-fased-signerd.sh +183 -108
  331. package/scripts/install-managed-runtime.mjs +212 -10
  332. package/scripts/managed-runtime-layout.mjs +75 -3
  333. package/scripts/migrate-hosted-signer-v2.mjs +58 -0
  334. package/scripts/start-managed.sh +169 -360
  335. package/dist/control-ui/assets/mining-commit-R-6nu87p.js +0 -2
  336. package/dist/plugin-sdk/tmp-fased-dir-CS6zsYEE.d.ts +0 -77
  337. package/dist/wallet/local-socket-signer-broker.js +0 -224
  338. package/dist/wallet/local-socket-signer-custody.js +0 -42
  339. package/dist/wallet/providers/embedded-keystore-adapter.js +0 -402
  340. package/dist/wallet/providers/privy-adapter.js +0 -80
  341. package/dist/wallet/wallet-custody.js +0 -1836
@@ -1,16 +1,16 @@
1
+ import { resolveGatewayPort } from "../config/paths.js";
1
2
  import { VERSION } from "../version.js";
2
3
  import { loadConfig } from "../config/io.js";
3
4
  import "../config/config.js";
4
- import { ensureWalletStateDir, resolveLocalSignerBackendSocketPath, resolveLocalSignerMaterialRootDir, resolveLocalSignerRunAsUser, resolveLocalSignerSidecarPaths, resolveLocalSignerSocketPath, resolveWalletRuntimeConfig } from "../wallet/wallet-runtime-config.js";
5
+ import { hasLegacyEmbeddedKeystoreMaterialHint, throwLegacyEmbeddedKeystoreMigrationRequired } from "../wallet/legacy-embedded-keystore.js";
6
+ import { ensureWalletStateDir, isLocalSignerExternallyManaged, resolveLocalSignerControlSocketPath, resolveLocalSignerMasterKeyPath, resolveLocalSignerMaterialRootDir, resolveLocalSignerSidecarPaths, resolveLocalSignerSocketPath, resolveLocalSignerStateDbPath } from "../wallet/wallet-runtime-config.js";
5
7
  import { WALLET_PROVIDER_IDS, readWalletProviderRegistry, setWalletProvidersEnabled } from "../wallet/wallet-provider-registry.js";
6
- import { applyWalletPolicyConfig, resolveWalletRoleForId } from "../wallet/wallet-policy.js";
7
8
  import { callLocalSocketSigner } from "../wallet/providers/local-socket-signer-adapter.js";
8
9
  import { resolveWalletProviderId } from "../wallet/wallet-provider-resolver.js";
9
10
  import { fileURLToPath } from "node:url";
10
11
  import syncFs from "node:fs";
11
12
  import path from "node:path";
12
13
  import { execSync, spawn, spawnSync } from "node:child_process";
13
- import { randomBytes } from "node:crypto";
14
14
  import net from "node:net";
15
15
 
16
16
  //#region src/wizard/onboarding.wallet.ts
@@ -21,32 +21,10 @@ function resolveSignerdBinaryPath(env = process.env) {
21
21
  }
22
22
  return path.join(env.HOME ?? "/root", ".fased/bin/fased-signerd");
23
23
  }
24
- function resolveAppOwnedSignerdStagingPath(env = process.env) {
25
- return path.join(env.HOME ?? "/root", ".fased/bin/fased-signerd");
26
- }
27
24
  const INSTALL_SCRIPT_RELURLS = ["../../scripts/install-fased-signerd.sh", "../scripts/install-fased-signerd.sh"];
28
25
  const BUILD_SCRIPT_RELURLS = ["../../scripts/build-fased-signerd.sh", "../scripts/build-fased-signerd.sh"];
29
- const BROKER_CLI_RELPATHS = ["./index.js", "../../dist/index.js"];
30
26
  const DEFAULT_SIGNER_RELEASE_DOWNLOAD_BASE = "https://github.com/fased-ai/fased/releases/download";
31
- const SIGNER_MAINTENANCE_HELPER = "/usr/local/sbin/fased-signer-maintenance";
32
- const SIGNER_ISOLATION_HELPER = "/usr/local/sbin/fased-signer-isolation";
33
- function runCommand(params) {
34
- const child = spawnSync(params.command, params.args, {
35
- env: params.env,
36
- encoding: "utf8",
37
- stdio: params.capture ? [
38
- "ignore",
39
- "pipe",
40
- "pipe"
41
- ] : "ignore"
42
- });
43
- if (child.status !== 0) {
44
- const stderr = typeof child.stderr === "string" ? child.stderr.trim() : "";
45
- const stdout = typeof child.stdout === "string" ? child.stdout.trim() : "";
46
- throw new Error(stderr || stdout || `${params.command} exited with status ${child.status ?? -1}`);
47
- }
48
- return typeof child.stdout === "string" ? child.stdout : "";
49
- }
27
+ const LOCAL_SIGNER_ENROLLMENT_ORIGIN = "http://localhost:18791";
50
28
  function isSignerdRunning(socketPath) {
51
29
  try {
52
30
  syncFs.statSync(socketPath);
@@ -98,28 +76,19 @@ async function waitForSignerdHealthy(socketPath, deadlineMs) {
98
76
  return isSignerdRunning(socketPath) && await isSignerdHealthy(socketPath);
99
77
  }
100
78
  function resolveSignerdLogPath(params) {
101
- return params.runAsUser ? path.join(path.dirname(params.appSocketPath), "local-signer.log") : path.join(params.materialDir, "local-signer.log");
79
+ return path.join(params.materialDir, "local-signer.log");
102
80
  }
103
81
  function buildSignerdReadinessError(params) {
104
- const signerLogPath = resolveSignerdLogPath({
105
- appSocketPath: params.appSocketPath,
106
- materialDir: params.materialDir,
107
- runAsUser: params.runAsUser
108
- });
109
- const brokerLogPath = path.join(path.dirname(params.appSocketPath), "local-signer-broker.log");
82
+ const signerLogPath = resolveSignerdLogPath({ materialDir: params.materialDir });
110
83
  const logDetails = [];
111
84
  const signerLogTail = readFileTail(signerLogPath);
112
85
  if (signerLogTail) {
113
86
  logDetails.push(`Signer log tail (${signerLogPath}):\n${signerLogTail}`);
114
87
  }
115
- const brokerLogTail = params.backendSocketPath !== params.appSocketPath ? readFileTail(brokerLogPath) : undefined;
116
- if (brokerLogTail) {
117
- logDetails.push(`Broker log tail (${brokerLogPath}):\n${brokerLogTail}`);
118
- }
119
88
  return new Error([
120
89
  `fased-signerd did not become ready within ${Math.round(params.readyTimeoutMs / 1e3)}s.`,
121
- `Check logs: ${signerLogPath}${params.backendSocketPath !== params.appSocketPath ? `, ${brokerLogPath}` : ""}`,
122
- `Run manually: ${params.binPath} -socket ${params.backendSocketPath}`,
90
+ `Check logs: ${signerLogPath}`,
91
+ `Run manually: ${params.binPath} -socket ${params.socketPath}`,
123
92
  ...logDetails
124
93
  ].join("\n"));
125
94
  }
@@ -175,15 +144,6 @@ function resolveBuildScript() {
175
144
  }
176
145
  return null;
177
146
  }
178
- function resolveBrokerCliPath() {
179
- for (const rel of BROKER_CLI_RELPATHS) {
180
- const candidate = fileURLToPath(new URL(rel, import.meta.url));
181
- if (syncFs.existsSync(candidate)) {
182
- return candidate;
183
- }
184
- }
185
- return null;
186
- }
187
147
  function resolveLocalSignerdReleaseBaseUrl(installScriptPath) {
188
148
  const repoRoot = path.resolve(path.dirname(installScriptPath), "..");
189
149
  const releaseDir = path.join(repoRoot, "dist-native", "release");
@@ -276,27 +236,6 @@ function copySignerdBinaryToTarget(params) {
276
236
  if (!syncFs.existsSync(sourcePath)) {
277
237
  throw new Error(`fased-signerd build completed but binary was not found at ${sourcePath}.`);
278
238
  }
279
- if (params.runAsUser) {
280
- if (runSignerIsolationHelper(params.runAsUser, [
281
- "install-binary",
282
- sourcePath,
283
- targetPath
284
- ]) === undefined) {
285
- runCommand({
286
- command: "sudo",
287
- args: [
288
- "bash",
289
- "-lc",
290
- [
291
- "set -euo pipefail",
292
- `install -d -m 755 -o ${JSON.stringify(params.runAsUser)} -g ${JSON.stringify(params.runAsUser)} ${JSON.stringify(path.dirname(targetPath))}`,
293
- `install -m 755 -o ${JSON.stringify(params.runAsUser)} -g ${JSON.stringify(params.runAsUser)} ${JSON.stringify(sourcePath)} ${JSON.stringify(targetPath)}`
294
- ].join("; ")
295
- ]
296
- });
297
- }
298
- return;
299
- }
300
239
  syncFs.mkdirSync(path.dirname(targetPath), { recursive: true });
301
240
  syncFs.copyFileSync(sourcePath, targetPath);
302
241
  syncFs.chmodSync(targetPath, 493);
@@ -323,8 +262,7 @@ function buildSignerdBinaryFromSource(binPath) {
323
262
  const builtPath = path.resolve(path.dirname(script), "..", "dist-native", "fased-signerd");
324
263
  copySignerdBinaryToTarget({
325
264
  sourcePath: builtPath,
326
- targetPath: binPath,
327
- runAsUser: resolveLocalSignerRunAsUser(process.env)
265
+ targetPath: binPath
328
266
  });
329
267
  return true;
330
268
  }
@@ -336,8 +274,10 @@ function resolveSignerdAssetInstallEnv(installScript) {
336
274
  const localReleaseBaseUrl = resolveLocalSignerdReleaseBaseUrl(installScript);
337
275
  if (localReleaseBaseUrl) {
338
276
  env.FASED_LOCAL_SIGNER_BASE_URL = localReleaseBaseUrl;
339
- env.FASED_LOCAL_SIGNER_VERSION = "";
277
+ env.FASED_LOCAL_SIGNER_VERSION = `v${VERSION.trim().replace(/^v/, "")}`;
340
278
  env.FASED_LOCAL_SIGNER_LATEST_TAG = "";
279
+ env.FASED_LOCAL_SIGNER_ALLOW_UNATTESTED = "1";
280
+ env.FASED_LOCAL_SIGNER_FLAT_RELEASE = "1";
341
281
  return env;
342
282
  }
343
283
  const normalizedVersion = VERSION.trim().replace(/^v/, "");
@@ -360,9 +300,7 @@ function installSignerdBinaryFromAsset(binPath) {
360
300
  if (!script) {
361
301
  return false;
362
302
  }
363
- const runAsUser = resolveLocalSignerRunAsUser(process.env);
364
- const stagingPath = resolveAppOwnedSignerdStagingPath(process.env);
365
- const installDir = path.dirname(runAsUser ? stagingPath : binPath);
303
+ const installDir = path.dirname(binPath);
366
304
  const verboseInstall = String(process.env.FASED_INSTALL_VERBOSE ?? "").trim() === "1" || String(process.env.FASED_ONBOARD_VERBOSE ?? "").trim() === "1";
367
305
  runScriptOrThrow({
368
306
  script,
@@ -373,30 +311,10 @@ function installSignerdBinaryFromAsset(binPath) {
373
311
  verbose: verboseInstall,
374
312
  fallbackError: "fased-signerd asset install failed"
375
313
  });
376
- const installedPath = runAsUser ? stagingPath : binPath;
314
+ const installedPath = binPath;
377
315
  if (!syncFs.existsSync(installedPath)) {
378
316
  throw new Error(`fased-signerd install script ran but binary not found at ${binPath}.\n` + `Check install script output above for errors.`);
379
317
  }
380
- if (runAsUser && installedPath !== binPath) {
381
- if (runSignerIsolationHelper(runAsUser, [
382
- "install-binary",
383
- installedPath,
384
- binPath
385
- ]) === undefined) {
386
- runCommand({
387
- command: "sudo",
388
- args: [
389
- "bash",
390
- "-lc",
391
- [
392
- "set -euo pipefail",
393
- `install -d -m 755 -o ${JSON.stringify(runAsUser)} -g ${JSON.stringify(runAsUser)} ${JSON.stringify(path.dirname(binPath))}`,
394
- `install -m 755 -o ${JSON.stringify(runAsUser)} -g ${JSON.stringify(runAsUser)} ${JSON.stringify(installedPath)} ${JSON.stringify(binPath)}`
395
- ].join("; ")
396
- ]
397
- });
398
- }
399
- }
400
318
  if (!syncFs.existsSync(binPath)) {
401
319
  throw new Error(`fased-signerd install script completed but target binary not found at ${binPath}.\n` + `Check install script output above for errors.`);
402
320
  }
@@ -410,77 +328,26 @@ function installSignerdBinary(binPath) {
410
328
  }
411
329
  throw new Error("FASED_BUILD_NATIVE_SIGNER_FROM_SOURCE=1 requires a source checkout and Go >= 1.25.7.");
412
330
  }
413
- let assetError;
414
331
  try {
415
332
  if (installSignerdBinaryFromAsset(binPath)) {
416
333
  return;
417
334
  }
418
335
  } catch (error) {
419
- assetError = error;
336
+ const detail = error instanceof Error ? error.message : String(error);
337
+ throw new Error([
338
+ `Verified fased-signerd installation failed for ${process.platform}/${process.arch}.`,
339
+ detail,
340
+ "Go is not required for the official prebuilt signer.",
341
+ "Fased will not replace a failed official asset or attestation with an unverified local build.",
342
+ "Developers may explicitly set FASED_BUILD_NATIVE_SIGNER_FROM_SOURCE=1 in a trusted source checkout."
343
+ ].join("\n"), { cause: error });
420
344
  }
421
- let sourceError;
422
- try {
423
- if (buildSignerdBinaryFromSource(binPath)) {
424
- return;
425
- }
426
- } catch (error) {
427
- sourceError = error;
428
- }
429
- const assetDetail = assetError instanceof Error ? assetError.message : undefined;
430
- const sourceDetail = sourceError instanceof Error ? sourceError.message : undefined;
431
345
  throw new Error([
432
- `Automatic fased-signerd installation failed for ${process.platform}/${process.arch}.`,
433
- `Fased ${VERSION} normally downloads its matching prebuilt signer and verifies its SHA-256 checksum; Go is not required.`,
434
- assetDetail ? `Asset install: ${assetDetail}` : "The packaged signer installer was not found.",
435
- sourceDetail ? `Source fallback: ${sourceDetail}` : undefined,
436
- "Retry after checking GitHub release access, or set FASED_WALLET_LOCAL_SIGNER_BIN to a trusted existing binary."
437
- ].filter((line) => Boolean(line)).join("\n"));
438
- }
439
- function hasScopedWalletEnvValue(env, prefix) {
440
- return Object.entries(env).some(([key, value]) => key.startsWith(`${prefix}__`) && typeof value === "string" && value.trim().length > 0);
441
- }
442
- function hasLocalSignerMaterialEnv(env) {
443
- if (String(env.FASED_WALLET_LOCAL_SIGNER_SOCKET ?? "").trim() || String(env.FASED_WALLET_LOCAL_SIGNER_BACKEND_SOCKET ?? "").trim() || String(env.FASED_WALLET_SIGNER_STATE_DIR ?? "").trim() || String(env.FASED_WALLET_PASSPHRASE_FILE ?? "").trim()) {
444
- return true;
445
- }
446
- return Object.entries(env).some(([key, rawValue]) => {
447
- if (typeof rawValue !== "string" || rawValue.trim().length === 0) {
448
- return false;
449
- }
450
- return key === "FASED_WALLET_SOLANA_KEYSTORE_PATH" || key.startsWith("FASED_WALLET_SOLANA_KEYSTORE_PATH__");
451
- });
452
- }
453
- function normalizeWalletIdForEnvSuffix(walletId) {
454
- const normalized = walletId.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
455
- return normalized || undefined;
456
- }
457
- function normalizeWalletIdForFilename(walletId) {
458
- const normalized = walletId.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
459
- return normalized || undefined;
460
- }
461
- function inferScopedSignerKeystoreEnv(materialDir, env) {
462
- const inferred = {};
463
- try {
464
- const registry = readWalletProviderRegistry(env);
465
- for (const wallet of registry.wallets) {
466
- if (wallet.providerId !== "local-socket-signer") {
467
- continue;
468
- }
469
- const envSuffix = normalizeWalletIdForEnvSuffix(wallet.id)?.toUpperCase();
470
- const fileSuffix = normalizeWalletIdForFilename(wallet.id);
471
- if (!envSuffix || !fileSuffix) {
472
- continue;
473
- }
474
- const solanaKey = `FASED_WALLET_SOLANA_KEYSTORE_PATH__${envSuffix}`;
475
- if (!String(env[solanaKey] ?? "").trim()) {
476
- const candidate = path.join(materialDir, `keystore-solana-${fileSuffix}.v1.enc`);
477
- if (syncFs.existsSync(candidate)) {
478
- inferred[solanaKey] = candidate;
479
- }
480
- }
481
- }
482
- } catch {}
483
- return inferred;
346
+ `Verified fased-signerd installation is unavailable for ${process.platform}/${process.arch}.`,
347
+ `Fased ${VERSION} requires its matching prebuilt signer, SHA-256 checksum, and release attestation; Go is not required.`,
348
+ "The packaged signer installer was not found.",
349
+ "Developers may explicitly set FASED_BUILD_NATIVE_SIGNER_FROM_SOURCE=1 in a trusted source checkout."
350
+ ].join("\n"));
484
351
  }
485
352
  function shouldSyncLocalSocketSignerFromConfig(params) {
486
353
  const cfg = params?.config ?? loadConfig();
@@ -494,9 +361,6 @@ function shouldSyncLocalSocketSignerFromConfig(params) {
494
361
  if (resolveWalletProviderId(cfg, mergedEnv) === "local-socket-signer") {
495
362
  return true;
496
363
  }
497
- if (hasLocalSignerMaterialEnv(mergedEnv)) {
498
- return true;
499
- }
500
364
  try {
501
365
  const registry = readWalletProviderRegistry(mergedEnv);
502
366
  return registry.wallets.some((wallet) => wallet.providerId === "local-socket-signer");
@@ -504,142 +368,100 @@ function shouldSyncLocalSocketSignerFromConfig(params) {
504
368
  return false;
505
369
  }
506
370
  }
507
- function resolveLocalSignerPassphraseSource(materialDir, env) {
508
- const configuredFile = String(env.FASED_WALLET_PASSPHRASE_FILE ?? "").trim();
509
- const candidates = [configuredFile ? path.resolve(configuredFile) : "", path.join(materialDir, "passphrase")].filter(Boolean);
510
- for (const candidate of candidates) {
511
- try {
512
- const value = syncFs.readFileSync(candidate, "utf8").trim();
513
- if (value) {
514
- return {
515
- kind: "file",
516
- path: candidate,
517
- value
518
- };
519
- }
520
- } catch {}
521
- }
522
- const explicit = String(env.FASED_WALLET_PASSPHRASE ?? "").trim();
523
- if (explicit) {
524
- return {
525
- kind: "value",
526
- value: explicit
527
- };
528
- }
529
- return { kind: "none" };
530
- }
531
371
  function resolveSignerChildEnv(materialDir, env = process.env, cfg = loadConfig()) {
372
+ void materialDir;
532
373
  const configEnv = cfg.env?.vars ?? {};
533
374
  const mergedEnv = {
534
375
  ...env,
535
376
  ...configEnv
536
377
  };
537
- const inferredScopedEnv = inferScopedSignerKeystoreEnv(materialDir, mergedEnv);
538
- const signerEnv = {
539
- ...mergedEnv,
540
- ...inferredScopedEnv
541
- };
542
- const hasScopedSolKeystore = hasScopedWalletEnvValue(signerEnv, "FASED_WALLET_SOLANA_KEYSTORE_PATH");
543
- const childBaseEnv = { ...signerEnv };
544
- if (hasScopedSolKeystore) {
545
- delete childBaseEnv.FASED_WALLET_SOLANA_KEYSTORE_PATH;
546
- }
547
- const explicitSolPath = String(signerEnv.FASED_WALLET_SOLANA_KEYSTORE_PATH ?? "").trim();
548
- const passphraseSource = resolveLocalSignerPassphraseSource(materialDir, signerEnv);
549
- const isolatedPassphraseFile = resolveLocalSignerRunAsUser(signerEnv) ? path.resolve(String(signerEnv.FASED_WALLET_PASSPHRASE_FILE ?? "").trim() || path.join(materialDir, "passphrase")) : "";
550
- const solPath = hasScopedSolKeystore ? "" : explicitSolPath || path.join(materialDir, "keystore-solana.v1.enc");
551
- const chains = resolveSignerChainsEnvValue(cfg, signerEnv);
552
- const chainSet = new Set(chains.split(",").map((entry) => entry.trim().toLowerCase()).filter(Boolean));
553
- return {
554
- ...childBaseEnv,
555
- FASED_WALLET_CHAINS: chains,
556
- ...chainSet.has("solana") && solPath ? { FASED_WALLET_SOLANA_KEYSTORE_PATH: solPath } : {},
557
- ...passphraseSource.kind === "file" ? {
558
- FASED_WALLET_PASSPHRASE_FILE: passphraseSource.path,
559
- FASED_WALLET_PASSPHRASE: undefined
560
- } : isolatedPassphraseFile ? {
561
- FASED_WALLET_PASSPHRASE_FILE: isolatedPassphraseFile,
562
- FASED_WALLET_PASSPHRASE: undefined
563
- } : passphraseSource.kind === "value" ? { FASED_WALLET_PASSPHRASE: passphraseSource.value } : {}
564
- };
565
- }
566
- const LOCAL_SIGNER_EXPORTABLE_PREFIXES = [
567
- "FASED_WALLET_SOLANA_KEYSTORE_PATH",
568
- "FASED_WALLET_SOLANA_RPC_URL",
569
- "FASED_WALLET_RPC_URL",
570
- "FASED_WALLET_EMBEDDED_KEYSTORE_RPC_URL",
571
- "FASED_WALLET_CUSTODY_MODE",
572
- "FASED_WALLET_CUSTODY_WALLETS",
573
- "FASED_WALLET_CUSTODY_PASSKEY_CEREMONY",
574
- "FASED_WALLET_CUSTODY_EPHEMERAL_RECONSTRUCTION",
575
- "FASED_WALLET_CUSTODY_PHASE2_COMPLETE"
576
- ];
577
- function collectLocalSignerExportEnv(childEnv) {
578
- const out = {};
579
- for (const [key, rawValue] of Object.entries(childEnv)) {
580
- if (!LOCAL_SIGNER_EXPORTABLE_PREFIXES.some((prefix) => key === prefix || key.startsWith(`${prefix}__`))) {
581
- continue;
378
+ const childEnv = {};
379
+ const inheritedKeys = [
380
+ "PATH",
381
+ "HOME",
382
+ "TMPDIR",
383
+ "LANG",
384
+ "LC_ALL",
385
+ "TZ",
386
+ "SSL_CERT_FILE",
387
+ "SSL_CERT_DIR",
388
+ "HTTP_PROXY",
389
+ "HTTPS_PROXY",
390
+ "NO_PROXY",
391
+ "http_proxy",
392
+ "https_proxy",
393
+ "no_proxy"
394
+ ];
395
+ for (const key of inheritedKeys) {
396
+ const value = String(mergedEnv[key] ?? "").trim();
397
+ if (value) {
398
+ childEnv[key] = value;
582
399
  }
400
+ }
401
+ for (const [key, rawValue] of Object.entries(mergedEnv)) {
583
402
  const value = String(rawValue ?? "").trim();
584
- if (!value) {
585
- continue;
403
+ const isSignerRuntimeConfig = key.startsWith("FASED_WALLET_LOCAL_SIGNER_RATE_") || key === "FASED_WALLET_LOCAL_SIGNER_AUDIT_MAX_BYTES" || key === "FASED_WALLET_SOLANA_CONFIRM_TIMEOUT_MS" || key === "FASED_WALLET_SOLANA_WRITE_RPC_TIMEOUT_MS";
404
+ if (value && isSignerRuntimeConfig) {
405
+ childEnv[key] = value;
586
406
  }
587
- out[key] = value;
588
407
  }
589
- return out;
590
- }
591
- function walletPolicyEnvSuffix(walletId) {
592
- return walletId.trim().toUpperCase().replace(/[^A-Z0-9]+/g, "_").replace(/^_+|_+$/g, "");
593
- }
594
- function addLocalSignerPolicyEnv(out, params) {
595
- const key = (name) => `${name}${params.prefix}`;
596
- out[key("FASED_WALLET_LOCAL_SIGNER_ROLE")] = params.role;
597
- out[key("FASED_WALLET_LOCAL_SIGNER_DIRECT_SIGNING")] = params.policy.directSigning ? "1" : "0";
598
- out[key("FASED_WALLET_LOCAL_SIGNER_CAPS_ENABLED")] = params.policy.capsEnabled ? "1" : "0";
599
- out[key("FASED_WALLET_LOCAL_SIGNER_SOLANA_MAX_PER_TX")] = params.policy.solana.caps.maxPerTx.toString();
600
- out[key("FASED_WALLET_LOCAL_SIGNER_SOLANA_MAX_DAILY")] = params.policy.solana.caps.maxDaily.toString();
601
- if (params.policy.solana.allowPrograms.length > 0) {
602
- out[key("FASED_WALLET_LOCAL_SIGNER_SOLANA_ALLOW_PROGRAMS")] = params.policy.solana.allowPrograms.join(",");
408
+ const webAuthn = resolveLocalSignerWebAuthnConfig(cfg, mergedEnv);
409
+ if (webAuthn) {
410
+ childEnv.FASED_WALLET_WEBAUTHN_RP_ID = webAuthn.rpId;
411
+ childEnv.FASED_WALLET_WEBAUTHN_ORIGINS = webAuthn.origins;
603
412
  }
413
+ childEnv.FASED_WALLET_CHAINS = resolveSignerChainsEnvValue(cfg, mergedEnv);
414
+ return childEnv;
604
415
  }
605
- function collectLocalSignerPolicyExportEnv(cfg, env) {
606
- const out = {};
607
- const registry = readWalletProviderRegistry(env);
608
- const localSignerWallets = registry.wallets.filter((wallet) => wallet.providerId === "local-socket-signer" && wallet.id.trim());
609
- if (localSignerWallets.length === 0) {
610
- return out;
611
- }
612
- const baseRuntime = resolveWalletRuntimeConfig(cfg, env);
613
- for (const wallet of localSignerWallets) {
614
- const role = resolveWalletRoleForId({
615
- walletId: wallet.id,
616
- cfg,
617
- env
618
- });
619
- const runtime = applyWalletPolicyConfig({
620
- config: baseRuntime,
621
- cfg,
622
- env,
623
- walletId: wallet.id
624
- });
625
- const suffix = walletPolicyEnvSuffix(wallet.id);
626
- if (!suffix) {
627
- continue;
416
+ function resolveLocalSignerWebAuthnConfig(cfg, env = process.env) {
417
+ if (String(env.FASED_HOST_PROFILE ?? "").trim().toLowerCase() === "hosting") {
418
+ return undefined;
419
+ }
420
+ const explicitRpId = String(env.FASED_WALLET_WEBAUTHN_RP_ID ?? "").trim().toLowerCase().replace(/\.$/, "");
421
+ const explicitOrigins = String(env.FASED_WALLET_WEBAUTHN_ORIGINS ?? "").trim();
422
+ if (Boolean(explicitRpId) !== Boolean(explicitOrigins)) {
423
+ throw new Error("Local signer WebAuthn requires both FASED_WALLET_WEBAUTHN_RP_ID and FASED_WALLET_WEBAUTHN_ORIGINS");
424
+ }
425
+ if (explicitRpId && explicitOrigins) {
426
+ if (explicitRpId.length > 253 || !/^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/.test(explicitRpId) || explicitRpId.includes("..")) {
427
+ throw new Error("Local signer WebAuthn RP ID must be a valid exact hostname");
628
428
  }
629
- addLocalSignerPolicyEnv(out, {
630
- prefix: `__${suffix}`,
631
- role,
632
- policy: runtime.policy
429
+ const normalizedOrigins = explicitOrigins.split(",").map((rawOrigin) => {
430
+ const origin = rawOrigin.trim();
431
+ let parsed;
432
+ try {
433
+ parsed = new URL(origin);
434
+ } catch {
435
+ throw new Error("Local signer WebAuthn contains an invalid origin");
436
+ }
437
+ const hostname = parsed.hostname.toLowerCase().replace(/\.$/, "");
438
+ const loopback = hostname === "localhost" || hostname.endsWith(".localhost");
439
+ const hostMatchesRp = hostname === explicitRpId || hostname.endsWith(`.${explicitRpId}`);
440
+ if (parsed.username || parsed.password || parsed.pathname && parsed.pathname !== "/" || parsed.search || parsed.hash || !hostMatchesRp || parsed.protocol !== "https:" && !(parsed.protocol === "http:" && loopback)) {
441
+ throw new Error("Local signer WebAuthn origins must exactly match the RP ID and use HTTPS, except localhost HTTP");
442
+ }
443
+ return parsed.origin.toLowerCase();
633
444
  });
634
- if (registry.defaultWalletId === wallet.id || localSignerWallets.length === 1) {
635
- addLocalSignerPolicyEnv(out, {
636
- prefix: "",
637
- role,
638
- policy: runtime.policy
639
- });
445
+ const uniqueOrigins = [...new Set([...normalizedOrigins, ...explicitRpId === "localhost" ? [LOCAL_SIGNER_ENROLLMENT_ORIGIN] : []])].toSorted();
446
+ if (uniqueOrigins.length === 0) {
447
+ throw new Error("Local signer WebAuthn requires at least one exact origin");
640
448
  }
449
+ return {
450
+ rpId: explicitRpId,
451
+ origins: uniqueOrigins.join(",")
452
+ };
641
453
  }
642
- return out;
454
+ const port = resolveGatewayPort(cfg, env);
455
+ return {
456
+ rpId: "localhost",
457
+ origins: [`http://localhost:${port}`, LOCAL_SIGNER_ENROLLMENT_ORIGIN].toSorted().join(",")
458
+ };
459
+ }
460
+ function quoteSignerEnvValue(value) {
461
+ if (value.includes("\0") || /[\r\n]/.test(value)) {
462
+ throw new Error("Signer environment values must be single-line strings");
463
+ }
464
+ return `"${value.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"").replaceAll("$", "\\$").replaceAll("`", "\\`")}"`;
643
465
  }
644
466
  function renderLocalSignerEnvFile(params) {
645
467
  const cfg = params?.config ?? loadConfig();
@@ -650,22 +472,22 @@ function renderLocalSignerEnvFile(params) {
650
472
  const walletRoot = ensureWalletStateDir(mergedEnv).rootDir;
651
473
  const materialDir = resolveLocalSignerMaterialRootDir(mergedEnv);
652
474
  const socketPath = resolveLocalSignerSocketPath(mergedEnv);
653
- const backendSocketPath = resolveLocalSignerBackendSocketPath(mergedEnv);
475
+ const controlSocketPath = resolveLocalSignerControlSocketPath(mergedEnv);
476
+ const stateDbPath = resolveLocalSignerStateDbPath(mergedEnv);
477
+ const masterKeyPath = resolveLocalSignerMasterKeyPath(mergedEnv);
654
478
  const signerBinPath = resolveSignerdBinaryPath(mergedEnv);
655
479
  const childEnv = resolveSignerChildEnv(materialDir, mergedEnv, cfg);
656
- const exportLines = Object.entries({
657
- ...collectLocalSignerExportEnv(childEnv),
658
- ...collectLocalSignerPolicyExportEnv(cfg, mergedEnv)
659
- }).toSorted(([a], [b]) => a.localeCompare(b)).map(([key, value]) => `export ${key}="${value.replaceAll("\"", "\\\"")}"`);
480
+ const webAuthn = resolveLocalSignerWebAuthnConfig(cfg, mergedEnv);
660
481
  const lines = [
661
- `export FASED_WALLET_LOCAL_SIGNER_SOCKET="${socketPath}"`,
662
- ...backendSocketPath !== socketPath ? [`export FASED_WALLET_LOCAL_SIGNER_BACKEND_SOCKET="${backendSocketPath}"`] : [],
663
- ...materialDir !== walletRoot ? [`export FASED_WALLET_SIGNER_STATE_DIR="${materialDir}"`] : [],
664
- `export FASED_WALLET_CHAINS="${String(childEnv.FASED_WALLET_CHAINS ?? "solana").trim() || "solana"}"`,
665
- ...String(childEnv.FASED_WALLET_PASSPHRASE ?? "").trim() ? [`export FASED_WALLET_PASSPHRASE="${String(childEnv.FASED_WALLET_PASSPHRASE ?? "").trim().replaceAll("\"", "\\\"")}"`] : String(childEnv.FASED_WALLET_PASSPHRASE_FILE ?? "").trim() ? [`export FASED_WALLET_PASSPHRASE_FILE="${String(childEnv.FASED_WALLET_PASSPHRASE_FILE ?? "").trim()}"`] : [],
666
- ...exportLines,
482
+ `export FASED_WALLET_LOCAL_SIGNER_SOCKET=${quoteSignerEnvValue(socketPath)}`,
483
+ `export FASED_WALLET_LOCAL_SIGNER_CONTROL_SOCKET=${quoteSignerEnvValue(controlSocketPath)}`,
484
+ `export FASED_WALLET_LOCAL_SIGNER_STATE_DB=${quoteSignerEnvValue(stateDbPath)}`,
485
+ `export FASED_WALLET_LOCAL_SIGNER_MASTER_KEY=${quoteSignerEnvValue(masterKeyPath)}`,
486
+ ...materialDir !== walletRoot ? [`export FASED_WALLET_SIGNER_STATE_DIR=${quoteSignerEnvValue(materialDir)}`] : [],
487
+ `export FASED_WALLET_CHAINS=${quoteSignerEnvValue(String(childEnv.FASED_WALLET_CHAINS ?? "solana").trim() || "solana")}`,
488
+ ...webAuthn ? [`export FASED_WALLET_WEBAUTHN_RP_ID=${quoteSignerEnvValue(webAuthn.rpId)}`, `export FASED_WALLET_WEBAUTHN_ORIGINS=${quoteSignerEnvValue(webAuthn.origins)}`] : [],
667
489
  "",
668
- `"${signerBinPath}" --socket "\${FASED_WALLET_LOCAL_SIGNER_BACKEND_SOCKET:-$FASED_WALLET_LOCAL_SIGNER_SOCKET}"`
490
+ `${quoteSignerEnvValue(signerBinPath)} --socket "$FASED_WALLET_LOCAL_SIGNER_SOCKET" --control-socket "$FASED_WALLET_LOCAL_SIGNER_CONTROL_SOCKET" --state-db "$FASED_WALLET_LOCAL_SIGNER_STATE_DB" --master-key "$FASED_WALLET_LOCAL_SIGNER_MASTER_KEY"`
669
491
  ];
670
492
  return `${lines.join("\n")}\n`;
671
493
  }
@@ -692,246 +514,24 @@ function writeLocalSignerEnvFile(params) {
692
514
  } catch {}
693
515
  return signerEnvPath;
694
516
  }
695
- function resolveCurrentUnixUser() {
696
- const explicit = String(process.env.USER ?? process.env.LOGNAME ?? "").trim();
697
- if (explicit) {
698
- return explicit;
699
- }
700
- try {
701
- return execSync("id -un", { encoding: "utf8" }).trim() || "app";
702
- } catch {
703
- return "app";
704
- }
705
- }
706
- function runSignerIsolationHelper(runAsUser, args, opts) {
707
- const helperArgs = signerMaintenanceHelperArgs(runAsUser, args);
708
- if (!helperArgs) {
709
- return undefined;
710
- }
711
- try {
712
- return runCommand({
713
- command: "sudo",
714
- args: [
715
- "-n",
716
- "-E",
717
- ...helperArgs
718
- ],
719
- capture: opts?.capture
720
- });
721
- } catch (err) {
722
- if (err instanceof Error) {
723
- throw err;
724
- }
725
- throw new Error(String(err), { cause: err });
726
- }
727
- }
728
- function startSignerIsolationHelperBackground(runAsUser, args, logPath, env = process.env) {
729
- const helperArgs = signerMaintenanceHelperArgs(runAsUser, args);
730
- if (!helperArgs) {
731
- return false;
732
- }
733
- const out = syncFs.openSync(logPath, "a", 384);
734
- const child = spawn("sudo", [
735
- "-n",
736
- "-E",
737
- ...helperArgs
738
- ], {
739
- detached: true,
740
- stdio: [
741
- "ignore",
742
- out,
743
- out
744
- ],
745
- env: {
746
- ...process.env,
747
- ...env
748
- }
749
- });
750
- child.unref();
751
- return true;
752
- }
753
- function signerMaintenanceHelperArgs(runAsUser, args) {
754
- if (syncFs.existsSync(SIGNER_MAINTENANCE_HELPER)) {
755
- return [SIGNER_MAINTENANCE_HELPER, ...args];
756
- }
757
- if (syncFs.existsSync(SIGNER_ISOLATION_HELPER)) {
758
- return [
759
- SIGNER_ISOLATION_HELPER,
760
- resolveCurrentUnixUser(),
761
- runAsUser,
762
- ...args
763
- ];
764
- }
765
- return undefined;
766
- }
767
- function ensureIsolatedSignerPaths(materialDir, backendSocketPath, appSocketPath, runAsUser) {
768
- const helperResult = runSignerIsolationHelper(runAsUser, [
769
- "prepare",
770
- materialDir,
771
- backendSocketPath,
772
- appSocketPath,
773
- resolveSignerdBinaryPath(process.env)
774
- ]);
775
- if (helperResult !== undefined) {
776
- return;
777
- }
778
- const appUser = resolveCurrentUnixUser();
779
- const signerWalletDir = path.dirname(materialDir);
780
- const signerRootDir = path.dirname(signerWalletDir);
781
- const commands = [`install -d -m 700 -o ${JSON.stringify(runAsUser)} -g ${JSON.stringify(runAsUser)} ${JSON.stringify(materialDir)}`, `install -d -m 700 -o ${JSON.stringify(runAsUser)} -g ${JSON.stringify(runAsUser)} ${JSON.stringify(path.dirname(backendSocketPath))}`];
782
- if (appSocketPath !== backendSocketPath) {
783
- commands.unshift(`install -d -m 710 -o ${JSON.stringify(runAsUser)} -g ${JSON.stringify(appUser)} ${JSON.stringify(signerRootDir)}`, `install -d -m 710 -o ${JSON.stringify(runAsUser)} -g ${JSON.stringify(appUser)} ${JSON.stringify(signerWalletDir)}`, `install -d -m 2770 -o ${JSON.stringify(runAsUser)} -g ${JSON.stringify(appUser)} ${JSON.stringify(path.dirname(appSocketPath))}`);
784
- }
785
- runCommand({
786
- command: "sudo",
787
- args: [
788
- "bash",
789
- "-lc",
790
- ["set -euo pipefail", ...commands].join("; ")
791
- ]
792
- });
793
- }
794
- function ensureLocalSignerPassphrase() {
795
- const materialDir = resolveLocalSignerMaterialRootDir(process.env);
796
- const runAsUser = resolveLocalSignerRunAsUser(process.env);
797
- const appSocketPath = resolveLocalSignerSocketPath(process.env);
798
- const backendSocketPath = resolveLocalSignerBackendSocketPath(process.env);
799
- if (runAsUser) {
800
- ensureIsolatedSignerPaths(materialDir, backendSocketPath, appSocketPath, runAsUser);
801
- }
802
- const existing = resolveLocalSignerPassphraseSource(materialDir, process.env);
803
- if (existing.kind === "file") {
804
- process.env.FASED_WALLET_PASSPHRASE_FILE = existing.path;
805
- delete process.env.FASED_WALLET_PASSPHRASE;
806
- return existing.value;
807
- }
808
- if (runAsUser) {
809
- const generated = existing.kind === "value" ? existing.value : randomBytes(24).toString("base64url");
810
- const stagingDir = ensureWalletStateDir(process.env).rootDir;
811
- const stagingPath = path.join(stagingDir, `.local-signer-passphrase-${process.pid}-${Date.now()}`);
812
- syncFs.mkdirSync(path.dirname(stagingPath), {
813
- recursive: true,
814
- mode: 448
815
- });
816
- syncFs.writeFileSync(stagingPath, `${generated}\n`, {
817
- encoding: "utf8",
818
- mode: 384
819
- });
820
- try {
821
- syncFs.chmodSync(stagingPath, 384);
822
- } catch {}
823
- let passphrasePath = path.join(materialDir, "passphrase");
824
- try {
825
- const helperResult = runSignerIsolationHelper(runAsUser, [
826
- "install-passphrase",
827
- stagingPath,
828
- materialDir
829
- ], { capture: true });
830
- if (helperResult === undefined) {
831
- runCommand({
832
- command: "sudo",
833
- args: [
834
- "bash",
835
- "-lc",
836
- [
837
- "set -euo pipefail",
838
- `test ! -e ${JSON.stringify(passphrasePath)}`,
839
- `install -d -m 700 -o ${JSON.stringify(runAsUser)} -g ${JSON.stringify(runAsUser)} ${JSON.stringify(materialDir)}`,
840
- `install -m 600 -o ${JSON.stringify(runAsUser)} -g ${JSON.stringify(runAsUser)} ${JSON.stringify(stagingPath)} ${JSON.stringify(passphrasePath)}`,
841
- `rm -f ${JSON.stringify(stagingPath)}`
842
- ].join("; ")
843
- ]
844
- });
845
- } else {
846
- passphrasePath = helperResult.trim() || passphrasePath;
847
- }
848
- process.env.FASED_WALLET_PASSPHRASE = generated;
849
- } catch (err) {
850
- try {
851
- syncFs.rmSync(stagingPath, { force: true });
852
- } catch {}
853
- const detail = err instanceof Error ? err.message : String(err);
854
- if (!detail.includes("passphrase already exists")) {
855
- throw err;
856
- }
857
- delete process.env.FASED_WALLET_PASSPHRASE;
858
- }
859
- process.env.FASED_WALLET_PASSPHRASE_FILE = passphrasePath;
860
- return process.env.FASED_WALLET_PASSPHRASE ?? "";
861
- }
862
- if (existing.kind === "value") {
863
- process.env.FASED_WALLET_PASSPHRASE = existing.value;
864
- delete process.env.FASED_WALLET_PASSPHRASE_FILE;
865
- return existing.value;
866
- }
867
- const generated = randomBytes(24).toString("base64url");
868
- const passphraseFile = path.join(materialDir, "passphrase");
869
- syncFs.mkdirSync(path.dirname(passphraseFile), {
870
- recursive: true,
871
- mode: 448
872
- });
873
- syncFs.writeFileSync(passphraseFile, `${generated}\n`, {
874
- encoding: "utf8",
875
- mode: 384
876
- });
877
- try {
878
- syncFs.chmodSync(passphraseFile, 384);
879
- } catch {}
880
- process.env.FASED_WALLET_PASSPHRASE_FILE = passphraseFile;
881
- delete process.env.FASED_WALLET_PASSPHRASE;
882
- return generated;
883
- }
884
517
  function startSignerdBackground(binPath, socketPath, materialDir, env = process.env) {
885
- const runAsUser = resolveLocalSignerRunAsUser(env);
886
518
  const childEnv = resolveSignerChildEnv(materialDir, env);
887
- const appSocketPath = resolveLocalSignerSocketPath(env);
888
- const backendSocketPath = resolveLocalSignerBackendSocketPath(env);
519
+ const controlSocketPath = resolveLocalSignerControlSocketPath(env);
520
+ const stateDbPath = resolveLocalSignerStateDbPath(env);
521
+ const masterKeyPath = resolveLocalSignerMasterKeyPath(env);
889
522
  const { pidPath, auditPath } = resolveLocalSignerSidecarPaths(socketPath);
890
- const logPath = resolveSignerdLogPath({
891
- appSocketPath,
892
- materialDir,
893
- runAsUser
894
- });
895
- if (runAsUser && process.getuid?.() === 0) {
896
- throw new Error("signer isolation should not be started directly as root");
897
- }
898
- if (runAsUser) {
899
- ensureIsolatedSignerPaths(materialDir, backendSocketPath, appSocketPath, runAsUser);
900
- const envArgs = Object.entries(childEnv).filter(([, value]) => typeof value === "string" && value.trim()).map(([key, value]) => `${key}=${value}`);
901
- try {
902
- if (!startSignerIsolationHelperBackground(runAsUser, [
903
- "start-signerd",
904
- binPath,
905
- socketPath,
906
- pidPath,
907
- auditPath
908
- ], logPath, childEnv)) {
909
- execSync([
910
- "sudo",
911
- "-u",
912
- runAsUser,
913
- "-H",
914
- "env",
915
- ...envArgs.map((value) => JSON.stringify(value)),
916
- "bash",
917
- "-lc",
918
- JSON.stringify([
919
- "set -euo pipefail",
920
- "umask 077",
921
- `${JSON.stringify(binPath)} -socket ${JSON.stringify(socketPath)} -pid-file ${JSON.stringify(pidPath)} -audit-log ${JSON.stringify(auditPath)} >> ${JSON.stringify(logPath)} 2>&1 &`
922
- ].join("; "))
923
- ].join(" "), { stdio: "ignore" });
924
- }
925
- return;
926
- } catch (err) {
927
- throw new Error(`failed to launch isolated signer as ${runAsUser}: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
928
- }
929
- }
523
+ const logPath = resolveSignerdLogPath({ materialDir });
930
524
  const out = syncFs.openSync(logPath, "a", 384);
931
525
  const err = syncFs.openSync(logPath, "a", 384);
932
526
  const child = spawn(binPath, [
933
527
  "-socket",
934
528
  socketPath,
529
+ "-control-socket",
530
+ controlSocketPath,
531
+ "-state-db",
532
+ stateDbPath,
533
+ "-master-key",
534
+ masterKeyPath,
935
535
  "-pid-file",
936
536
  pidPath,
937
537
  "-audit-log",
@@ -947,61 +547,10 @@ function startSignerdBackground(binPath, socketPath, materialDir, env = process.
947
547
  });
948
548
  child.unref();
949
549
  }
950
- function stopProcessBySocket(socketPath, runAsUser) {
550
+ function stopProcessBySocket(socketPath) {
951
551
  const { pidPath } = resolveLocalSignerSidecarPaths(socketPath);
952
552
  const legacyPidPath = `${socketPath}.pid`;
953
553
  const pidPaths = Array.from(new Set([pidPath, legacyPidPath]));
954
- if (runAsUser) {
955
- let stoppedWithHelper = false;
956
- for (const candidatePidPath of pidPaths) {
957
- const helperResult = runSignerIsolationHelper(runAsUser, [
958
- "stop",
959
- socketPath,
960
- candidatePidPath
961
- ]);
962
- if (helperResult !== undefined) {
963
- stoppedWithHelper = true;
964
- }
965
- }
966
- if (stoppedWithHelper) {
967
- return;
968
- }
969
- for (const candidatePidPath of pidPaths) {
970
- try {
971
- const rawPid = runCommand({
972
- command: "sudo",
973
- args: [
974
- "bash",
975
- "-lc",
976
- `if [ -f ${JSON.stringify(candidatePidPath)} ]; then cat ${JSON.stringify(candidatePidPath)}; fi`
977
- ],
978
- capture: true
979
- }).trim();
980
- const pid = Number.parseInt(rawPid, 10);
981
- if (Number.isFinite(pid) && pid > 0) {
982
- runCommand({
983
- command: "sudo",
984
- args: [
985
- "bash",
986
- "-lc",
987
- `cmd=$(ps -p ${pid} -o command= 2>/dev/null || true); case "$cmd" in *fased-signerd*|*local-socket-signer-broker*) kill ${pid} >/dev/null 2>&1 || true ;; esac`
988
- ]
989
- });
990
- }
991
- } catch {}
992
- try {
993
- runCommand({
994
- command: "sudo",
995
- args: [
996
- "bash",
997
- "-lc",
998
- `rm -f ${JSON.stringify(socketPath)} ${JSON.stringify(candidatePidPath)}`
999
- ]
1000
- });
1001
- } catch {}
1002
- }
1003
- return;
1004
- }
1005
554
  for (const candidatePidPath of pidPaths) {
1006
555
  try {
1007
556
  const pid = Number.parseInt(syncFs.readFileSync(candidatePidPath, "utf8").trim(), 10);
@@ -1037,116 +586,32 @@ function readProcessCommand(pid) {
1037
586
  }
1038
587
  function isLocalSignerProcessCommand(command) {
1039
588
  const lower = command.toLowerCase();
1040
- return lower.includes("fased-signerd") || lower.includes("local-socket-signer-broker");
1041
- }
1042
- function startSignerBrokerBackground(appSocketPath, backendSocketPath, materialDir, env = process.env) {
1043
- const brokerCli = resolveBrokerCliPath();
1044
- if (!brokerCli) {
1045
- const tried = BROKER_CLI_RELPATHS.map((rel) => fileURLToPath(new URL(rel, import.meta.url)));
1046
- throw new Error(`wallet signer broker CLI not built. Tried: ${tried.join(", ")}`);
1047
- }
1048
- const logPath = path.join(path.dirname(appSocketPath), "local-signer-broker.log");
1049
- const runAsUser = resolveLocalSignerRunAsUser(env);
1050
- const { pidPath, auditPath } = resolveLocalSignerSidecarPaths(appSocketPath);
1051
- if (runAsUser) {
1052
- ensureIsolatedSignerPaths(materialDir, backendSocketPath, appSocketPath, runAsUser);
1053
- const envArgs = Object.entries({
1054
- ...env,
1055
- FASED_WALLET_LOCAL_SIGNER_SOCKET: appSocketPath,
1056
- FASED_WALLET_LOCAL_SIGNER_BACKEND_SOCKET: backendSocketPath
1057
- }).filter(([, value]) => typeof value === "string" && value.trim()).map(([key, value]) => `${key}=${value}`);
1058
- try {
1059
- if (!startSignerIsolationHelperBackground(runAsUser, [
1060
- "start-broker",
1061
- process.execPath,
1062
- brokerCli,
1063
- appSocketPath,
1064
- backendSocketPath,
1065
- pidPath,
1066
- auditPath
1067
- ], logPath, {
1068
- ...env,
1069
- FASED_WALLET_LOCAL_SIGNER_SOCKET: appSocketPath,
1070
- FASED_WALLET_LOCAL_SIGNER_BACKEND_SOCKET: backendSocketPath
1071
- })) {
1072
- execSync([
1073
- "sudo",
1074
- "-u",
1075
- runAsUser,
1076
- "-H",
1077
- "env",
1078
- ...envArgs.map((value) => JSON.stringify(value)),
1079
- "bash",
1080
- "-lc",
1081
- JSON.stringify([
1082
- "set -euo pipefail",
1083
- "umask 007",
1084
- `${JSON.stringify(process.execPath)} ${JSON.stringify(brokerCli)} wallet signer broker --socket ${JSON.stringify(appSocketPath)} --backend-socket ${JSON.stringify(backendSocketPath)} --pid-file ${JSON.stringify(pidPath)} --audit-log ${JSON.stringify(auditPath)} >> ${JSON.stringify(logPath)} 2>&1 &`
1085
- ].join("; "))
1086
- ].join(" "), { stdio: "ignore" });
1087
- }
1088
- return;
1089
- } catch (err) {
1090
- throw new Error(`failed to launch isolated signer broker as ${runAsUser}: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
1091
- }
1092
- }
1093
- const out = syncFs.openSync(logPath, "a", 384);
1094
- const err = syncFs.openSync(logPath, "a", 384);
1095
- const child = spawn(process.execPath, [
1096
- brokerCli,
1097
- "wallet",
1098
- "signer",
1099
- "broker",
1100
- "--socket",
1101
- appSocketPath,
1102
- "--backend-socket",
1103
- backendSocketPath,
1104
- "--pid-file",
1105
- pidPath,
1106
- "--audit-log",
1107
- auditPath
1108
- ], {
1109
- detached: true,
1110
- stdio: [
1111
- "ignore",
1112
- out,
1113
- err
1114
- ],
1115
- env: {
1116
- ...env,
1117
- FASED_WALLET_LOCAL_SIGNER_SOCKET: appSocketPath,
1118
- FASED_WALLET_LOCAL_SIGNER_BACKEND_SOCKET: backendSocketPath
1119
- }
1120
- });
1121
- child.unref();
589
+ return lower.includes("fased-signerd");
1122
590
  }
1123
591
  async function restartLocalSocketSigner(walletDir, env = process.env) {
1124
592
  void walletDir;
1125
593
  const socketPath = resolveLocalSignerSocketPath(env);
1126
- const backendSocketPath = resolveLocalSignerBackendSocketPath(env);
1127
594
  const binPath = resolveSignerdBinaryPath(env);
1128
595
  const materialDir = resolveLocalSignerMaterialRootDir(env);
1129
- const runAsUser = resolveLocalSignerRunAsUser(env);
596
+ if (isLocalSignerExternallyManaged(env)) {
597
+ const readyTimeoutMs = 8e3;
598
+ if (await waitForSignerdHealthy(socketPath, readyTimeoutMs) && (await readSignerdHealth(socketPath)).ok) {
599
+ return;
600
+ }
601
+ throw new Error(`externally managed fased-signerd is unavailable at ${socketPath}; start or repair the Docker fased-signerd service before retrying`);
602
+ }
1130
603
  if (!syncFs.existsSync(binPath)) {
1131
604
  return;
1132
605
  }
1133
- stopProcessBySocket(socketPath, runAsUser);
1134
- if (backendSocketPath !== socketPath) {
1135
- stopProcessBySocket(backendSocketPath, runAsUser);
1136
- }
1137
- startSignerdBackground(binPath, backendSocketPath, materialDir, env);
1138
- if (backendSocketPath !== socketPath) {
1139
- startSignerBrokerBackground(socketPath, backendSocketPath, materialDir, env);
1140
- }
1141
- const readyTimeoutMs = runAsUser ? 2e4 : 8e3;
606
+ stopProcessBySocket(socketPath);
607
+ startSignerdBackground(binPath, socketPath, materialDir, env);
608
+ const readyTimeoutMs = 8e3;
1142
609
  if (!await waitForSignerdHealthy(socketPath, readyTimeoutMs)) {
1143
610
  throw buildSignerdReadinessError({
1144
- appSocketPath: socketPath,
1145
- backendSocketPath,
611
+ socketPath,
1146
612
  binPath,
1147
613
  materialDir,
1148
- readyTimeoutMs,
1149
- runAsUser
614
+ readyTimeoutMs
1150
615
  });
1151
616
  }
1152
617
  }
@@ -1170,7 +635,8 @@ async function syncLocalSocketSignerFromConfig(params) {
1170
635
  env: mergedEnv
1171
636
  });
1172
637
  const binPath = resolveSignerdBinaryPath(mergedEnv);
1173
- if (!syncFs.existsSync(binPath)) {
638
+ const externallyManaged = isLocalSignerExternallyManaged(mergedEnv);
639
+ if (!externallyManaged && !syncFs.existsSync(binPath)) {
1174
640
  installSignerdBinary(binPath);
1175
641
  }
1176
642
  if (params?.restart === false) {
@@ -1183,45 +649,10 @@ async function syncLocalSocketSignerFromConfig(params) {
1183
649
  await restartLocalSocketSigner(undefined, mergedEnv);
1184
650
  return {
1185
651
  performed: true,
1186
- restarted: true,
652
+ restarted: !externallyManaged,
1187
653
  signerEnvPath
1188
654
  };
1189
655
  }
1190
- function migrateLocalSignerKeystoreToMaterialDir(params) {
1191
- const runAsUser = resolveLocalSignerRunAsUser(process.env);
1192
- const materialDir = resolveLocalSignerMaterialRootDir(process.env);
1193
- const sourcePath = path.resolve(params.keystorePath);
1194
- if (!runAsUser) {
1195
- return sourcePath;
1196
- }
1197
- const targetPath = path.join(materialDir, path.basename(sourcePath));
1198
- if (targetPath === sourcePath) {
1199
- return targetPath;
1200
- }
1201
- ensureIsolatedSignerPaths(materialDir, resolveLocalSignerBackendSocketPath(process.env), resolveLocalSignerSocketPath(process.env), runAsUser);
1202
- if (syncFs.existsSync(targetPath) && !params.force) {
1203
- throw new Error(`Keystore already exists: ${targetPath}`);
1204
- }
1205
- if (runSignerIsolationHelper(runAsUser, [
1206
- "copy-keystore",
1207
- sourcePath,
1208
- targetPath
1209
- ]) === undefined) {
1210
- runCommand({
1211
- command: "sudo",
1212
- args: [
1213
- "bash",
1214
- "-lc",
1215
- [
1216
- "set -euo pipefail",
1217
- `install -D -m 600 -o ${JSON.stringify(runAsUser)} -g ${JSON.stringify(runAsUser)} ${JSON.stringify(sourcePath)} ${JSON.stringify(targetPath)}`,
1218
- `rm -f ${JSON.stringify(sourcePath)}`
1219
- ].join("; ")
1220
- ]
1221
- });
1222
- }
1223
- return targetPath;
1224
- }
1225
656
  const DEFAULT_WALLET_CHAINS = ["solana"];
1226
657
  const DEFAULT_WALLET_RUNTIME_HOST = "127.0.0.1";
1227
658
  const DEFAULT_WALLET_RUNTIME_PORT = 19444;
@@ -1248,6 +679,7 @@ function normalizeProviderId(value) {
1248
679
  case "local-socket-signer":
1249
680
  case "alchemy":
1250
681
  case "turnkey":
682
+ case "wallet-standard":
1251
683
  case "privy": return value;
1252
684
  default: return null;
1253
685
  }
@@ -1258,6 +690,7 @@ function providerLabel(providerId) {
1258
690
  case "local-socket-signer": return "Local native signer";
1259
691
  case "alchemy": return "Alchemy";
1260
692
  case "turnkey": return "Turnkey";
693
+ case "wallet-standard": return "Hardware / Wallet Standard";
1261
694
  case "privy": return "Privy";
1262
695
  default: return providerId;
1263
696
  }
@@ -1285,27 +718,15 @@ function applyWalletConfig(base, runtimeConfig) {
1285
718
  };
1286
719
  }
1287
720
  function hasConfiguredWalletMaterial(params) {
1288
- if (params.registry.wallets.length > 0) {
1289
- return true;
1290
- }
1291
- const mergedEnv = {
1292
- ...params.env,
1293
- ...params.config.env?.vars
1294
- };
1295
- if (String(mergedEnv.FASED_WALLET_SOLANA_KEYSTORE_PATH ?? "").trim() || hasScopedWalletEnvValue(mergedEnv, "FASED_WALLET_SOLANA_KEYSTORE_PATH")) {
1296
- return true;
1297
- }
1298
- try {
1299
- const walletDir = ensureWalletStateDir(mergedEnv).rootDir;
1300
- return syncFs.readdirSync(walletDir).some((entry) => /^keystore-solana(?:-[A-Za-z0-9_-]+)?\.v1\.enc$/.test(entry) || /^keystore-evm(?:-[A-Za-z0-9_-]+)?\.v1\.enc$/.test(entry));
1301
- } catch {
1302
- return false;
1303
- }
721
+ return params.registry.wallets.some((wallet) => wallet.providerId === "local-socket-signer");
1304
722
  }
1305
723
  const LOCAL_SIGNER_ENV_KEYS = [
1306
724
  "FASED_WALLET_LOCAL_SIGNER_SOCKET",
1307
725
  "FASED_WALLET_LOCAL_SIGNER_BACKEND_SOCKET",
726
+ "FASED_WALLET_LOCAL_SIGNER_CONTROL_SOCKET",
1308
727
  "FASED_WALLET_SIGNER_STATE_DIR",
728
+ "FASED_WALLET_LOCAL_SIGNER_STATE_DB",
729
+ "FASED_WALLET_LOCAL_SIGNER_MASTER_KEY",
1309
730
  "FASED_WALLET_LOCAL_SIGNER_RUN_AS_USER",
1310
731
  "FASED_WALLET_LOCAL_SIGNER_BIN",
1311
732
  "FASED_WALLET_PASSPHRASE_FILE"
@@ -1328,16 +749,21 @@ function clearLocalSignerEnv(base) {
1328
749
  }
1329
750
  async function configureWalletForOnboarding(params) {
1330
751
  const { flow, prompter } = params;
752
+ if (params.nextConfig.wallet?.provider?.id === "embedded-keystore") {
753
+ throwLegacyEmbeddedKeystoreMigrationRequired("legacy wallet configuration detected");
754
+ }
1331
755
  const current = params.nextConfig.wallet?.runtime;
1332
756
  const registry = readWalletProviderRegistry(process.env);
757
+ if (registry.providers["embedded-keystore"]?.enabled || registry.wallets.some((wallet) => wallet.providerId === "embedded-keystore") || hasLegacyEmbeddedKeystoreMaterialHint({
758
+ ...process.env,
759
+ ...params.nextConfig.env?.vars
760
+ })) {
761
+ throwLegacyEmbeddedKeystoreMigrationRequired("legacy wallet material detected");
762
+ }
1333
763
  const currentEnabledProviders = WALLET_PROVIDER_IDS.filter((providerId) => registry.providers[providerId]?.enabled);
1334
764
  const onboardingProviderOptions = WALLET_PROVIDER_IDS.filter((providerId) => ["local-socket-signer"].includes(providerId));
1335
765
  const currentChains = normalizeChains(current?.chains);
1336
- const configuredWalletMaterial = hasConfiguredWalletMaterial({
1337
- config: params.nextConfig,
1338
- registry,
1339
- env: process.env
1340
- });
766
+ const configuredWalletMaterial = hasConfiguredWalletMaterial({ registry });
1341
767
  const defaultEnabled = current?.enabled === false ? false : Boolean(current?.enabled && configuredWalletMaterial);
1342
768
  const enabled = params.forceEnable === true ? true : flow === "quickstart" ? defaultEnabled : await prompter.confirm({
1343
769
  message: "Enable wallet integration?",
@@ -1391,12 +817,43 @@ async function configureWalletForOnboarding(params) {
1391
817
  if (defaultProvider === "local-socket-signer") {
1392
818
  const quietSignerNotes = flow === "quickstart";
1393
819
  const socketPath = resolveLocalSignerSocketPath(process.env);
1394
- const backendSocketPath = resolveLocalSignerBackendSocketPath(process.env);
1395
820
  const materialDir = resolveLocalSignerMaterialRootDir(process.env);
1396
821
  const binPath = resolveSignerdBinaryPath(process.env);
1397
- const runAsUser = resolveLocalSignerRunAsUser(process.env);
1398
- ensureLocalSignerPassphrase();
1399
- if (!syncFs.existsSync(binPath)) {
822
+ const hostedSigner = params.hostProfile === "hosting";
823
+ const externallyManagedSigner = !hostedSigner && isLocalSignerExternallyManaged(process.env);
824
+ if (hostedSigner) {
825
+ const expectedSocket = "/run/fased-signerd/app.sock";
826
+ if (socketPath !== expectedSocket) {
827
+ throw new Error("Hosting wallet setup must use only the root-managed signer app socket at /run/fased-signerd/app.sock.");
828
+ }
829
+ let result;
830
+ try {
831
+ result = await callLocalSocketSigner(expectedSocket, { op: "v2.capabilities" });
832
+ } catch (error) {
833
+ throw new Error([
834
+ "The root-managed hosted wallet signer is unavailable or incompatible.",
835
+ `Signer socket: ${expectedSocket}`,
836
+ "Repair only from the provider root console with the exact tagged, attested Hosting release; never run the app checkout with sudo.",
837
+ `Detail: ${error instanceof Error ? error.message : String(error)}`
838
+ ].join("\n"), { cause: error });
839
+ }
840
+ const protocol = result.capabilities?.protocol;
841
+ const features = new Set(result.capabilities?.features ?? []);
842
+ const missingFeatures = [
843
+ "failClosedPolicies",
844
+ "signerOwnedKeys",
845
+ "atomicIdempotency"
846
+ ].filter((feature) => !features.has(feature));
847
+ if (result.ready !== true || protocol?.current !== 2 || (protocol.min ?? Number.POSITIVE_INFINITY) > 2 || (protocol.max ?? Number.NEGATIVE_INFINITY) < 2 || missingFeatures.length > 0) {
848
+ throw new Error(`The root-managed hosted signer did not acknowledge the required signer-v2 boundary${missingFeatures.length > 0 ? ` (missing: ${missingFeatures.join(", ")})` : ""}. Repair from the provider root console with the exact tagged, attested Hosting release; never run the app checkout with sudo.`);
849
+ }
850
+ process.env.FASED_WALLET_LOCAL_SIGNER_SOCKET = expectedSocket;
851
+ delete process.env.FASED_WALLET_LOCAL_SIGNER_BACKEND_SOCKET;
852
+ delete process.env.FASED_WALLET_LOCAL_SIGNER_RUN_AS_USER;
853
+ if (!quietSignerNotes) {
854
+ await prompter.note([`Root-managed fased-signerd is ready at: ${expectedSocket}`, "Wallets start locked with deny-all policy until owner enrollment is completed."].join("\n"), "Hosted wallet signer");
855
+ }
856
+ } else if (!externallyManagedSigner && !syncFs.existsSync(binPath)) {
1400
857
  const signerInstallProgress = quietSignerNotes ? prompter.progress("Installing local wallet signer…") : undefined;
1401
858
  if (!quietSignerNotes) {
1402
859
  await prompter.note([`fased-signerd not found at: ${binPath}`, `Downloading the checksum-verified signer for Fased ${VERSION}. Go is not required for a normal install.`].join("\n"), "Local socket signer");
@@ -1415,57 +872,47 @@ async function configureWalletForOnboarding(params) {
1415
872
  await prompter.note(`fased-signerd installed at: ${binPath}`, "Local socket signer");
1416
873
  }
1417
874
  }
1418
- const socketExists = isSignerdRunning(socketPath);
1419
- const socketHealthy = socketExists ? await isSignerdHealthy(socketPath) : false;
1420
- const desiredSignerChains = normalizeSignerChains(String(resolveSignerChildEnv(materialDir, process.env).FASED_WALLET_CHAINS ?? "").split(","));
1421
- const healthDetails = socketExists && socketHealthy ? await readSignerdHealth(socketPath) : {
1422
- ok: false,
1423
- chains: []
1424
- };
1425
- const missingSignerChains = desiredSignerChains.filter((chain) => !healthDetails.chains.includes(chain));
1426
- if (socketExists && socketHealthy && missingSignerChains.length === 0) {
1427
- process.env.FASED_WALLET_LOCAL_SIGNER_SOCKET = socketPath;
1428
- if (backendSocketPath !== socketPath) {
1429
- process.env.FASED_WALLET_LOCAL_SIGNER_BACKEND_SOCKET = backendSocketPath;
1430
- }
1431
- if (!quietSignerNotes) {
1432
- await prompter.note(`fased-signerd already running at: ${socketPath}`, "Local socket signer");
1433
- }
1434
- } else {
1435
- if (socketExists && (!socketHealthy || missingSignerChains.length > 0)) {
1436
- stopProcessBySocket(socketPath, runAsUser);
1437
- }
1438
- if (backendSocketPath !== socketPath) {
1439
- stopProcessBySocket(backendSocketPath, runAsUser);
1440
- }
1441
- startSignerdBackground(binPath, backendSocketPath, materialDir);
1442
- if (backendSocketPath !== socketPath) {
1443
- startSignerBrokerBackground(socketPath, backendSocketPath, materialDir);
1444
- }
1445
- const readyTimeoutMs = runAsUser ? 2e4 : 8e3;
1446
- if (await waitForSignerdHealthy(socketPath, readyTimeoutMs)) {
1447
- if (!quietSignerNotes) {
1448
- await prompter.note([`fased-signerd started at: ${socketPath}`, "Keys are isolated in the Go signer process — not accessible to Node agent."].join("\n"), "Local socket signer");
1449
- }
875
+ if (!hostedSigner) {
876
+ const socketExists = isSignerdRunning(socketPath);
877
+ const socketHealthy = socketExists ? await isSignerdHealthy(socketPath) : false;
878
+ const desiredSignerChains = normalizeSignerChains(String(resolveSignerChildEnv(materialDir, process.env).FASED_WALLET_CHAINS ?? "").split(","));
879
+ const healthDetails = socketExists && socketHealthy ? await readSignerdHealth(socketPath) : {
880
+ ok: false,
881
+ chains: []
882
+ };
883
+ const missingSignerChains = desiredSignerChains.filter((chain) => !healthDetails.chains.includes(chain));
884
+ if (socketExists && socketHealthy && missingSignerChains.length === 0) {
1450
885
  process.env.FASED_WALLET_LOCAL_SIGNER_SOCKET = socketPath;
1451
- if (backendSocketPath !== socketPath) {
1452
- process.env.FASED_WALLET_LOCAL_SIGNER_BACKEND_SOCKET = backendSocketPath;
886
+ if (!quietSignerNotes) {
887
+ await prompter.note(`fased-signerd already running at: ${socketPath}`, "Local socket signer");
1453
888
  }
889
+ } else if (externallyManagedSigner) {
890
+ throw new Error([`The externally managed native signer is unavailable or incompatible at ${socketPath}.`, "Start or repair the Docker fased-signerd service, wait for it to become healthy, and retry wallet setup."].join("\n"));
1454
891
  } else {
1455
- throw buildSignerdReadinessError({
1456
- appSocketPath: socketPath,
1457
- backendSocketPath,
1458
- binPath,
1459
- materialDir,
1460
- readyTimeoutMs,
1461
- runAsUser
1462
- });
892
+ if (socketExists && (!socketHealthy || missingSignerChains.length > 0)) {
893
+ stopProcessBySocket(socketPath);
894
+ }
895
+ startSignerdBackground(binPath, socketPath, materialDir);
896
+ const readyTimeoutMs = 8e3;
897
+ if (await waitForSignerdHealthy(socketPath, readyTimeoutMs)) {
898
+ if (!quietSignerNotes) {
899
+ await prompter.note([`fased-signerd started at: ${socketPath}`, "Normal Fased code sends signing requests to Go and does not receive plaintext keys. Local shares one OS account and is not a hard compromise boundary; Hosting isolates the signer account."].join("\n"), "Local socket signer");
900
+ }
901
+ process.env.FASED_WALLET_LOCAL_SIGNER_SOCKET = socketPath;
902
+ } else {
903
+ throw buildSignerdReadinessError({
904
+ socketPath,
905
+ binPath,
906
+ materialDir,
907
+ readyTimeoutMs
908
+ });
909
+ }
1463
910
  }
1464
911
  }
1465
912
  }
1466
- const automationSigning = flow === "quickstart" ? current?.policy?.directSigning ?? true : await prompter.confirm({
1467
- message: "Allow automated wallet execution from approved Agent tools?",
1468
- initialValue: current?.policy?.directSigning ?? true
913
+ const automationSigning = flow === "quickstart" ? current?.policy?.directSigning ?? false : await prompter.confirm({
914
+ message: "Allow typed automated wallet execution after signer policy and caps are set?",
915
+ initialValue: current?.policy?.directSigning ?? false
1469
916
  });
1470
917
  const toolAccessMode = flow === "quickstart" ? current?.toolAccess?.mode ?? "owner-only" : await prompter.select({
1471
918
  message: "Wallet tool access scope",
@@ -1491,7 +938,7 @@ async function configureWalletForOnboarding(params) {
1491
938
  initialValue: (current?.toolAccess?.allowAgents ?? []).join(",")
1492
939
  })) : [];
1493
940
  const solanaAllowPrograms = flow === "quickstart" ? (current?.policy?.solana?.allowPrograms ?? []).map((item) => item.trim()).filter(Boolean) : splitCsvList(await prompter.text({
1494
- message: "Solana program allowlist (comma separated, blank=any)",
941
+ message: "Solana program/token allowlist (comma separated, blank=deny all)",
1495
942
  initialValue: (current?.policy?.solana?.allowPrograms ?? []).join(",")
1496
943
  }));
1497
944
  const solanaMaxPerTx = flow === "quickstart" ? current?.policy?.solana?.maxPerTx?.trim() || DEFAULT_SOLANA_MAX_PER_TX : (await prompter.text({
@@ -1543,4 +990,4 @@ async function configureWalletForOnboarding(params) {
1543
990
  }
1544
991
 
1545
992
  //#endregion
1546
- export { configureWalletForOnboarding, installSignerdBinary, migrateLocalSignerKeystoreToMaterialDir, resolveSignerdBinaryPath, restartLocalSocketSigner, syncLocalSocketSignerFromConfig };
993
+ export { configureWalletForOnboarding, installSignerdBinary, resolveSignerdBinaryPath, restartLocalSocketSigner, syncLocalSocketSignerFromConfig, writeLocalSignerEnvFile };