@fased/fased 0.1.61 → 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 (514) hide show
  1. package/CHANGELOG.md +43 -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/acp/client.js +2 -2
  12. package/dist/acp/secret-file.js +2 -2
  13. package/dist/agents/agent-scope.js +2 -2
  14. package/dist/agents/apply-patch.js +8 -8
  15. package/dist/agents/auth-profiles/paths.js +2 -2
  16. package/dist/agents/auth-profiles/store.js +269 -33
  17. package/dist/agents/bash-tools.exec.js +10 -4
  18. package/dist/agents/bash-tools.shared.js +2 -2
  19. package/dist/agents/cli-credentials.js +4 -4
  20. package/dist/agents/current-model-catalog.js +3 -3
  21. package/dist/agents/docs-path.js +3 -3
  22. package/dist/agents/embedded-pi-mcp.js +4 -4
  23. package/dist/agents/identity-avatar.js +3 -3
  24. package/dist/agents/identity-file.js +2 -2
  25. package/dist/agents/memory-search.js +7 -1
  26. package/dist/agents/model-catalog-access.js +1 -2
  27. package/dist/agents/model-catalog-snapshot.js +1 -1
  28. package/dist/agents/model-catalog-source.js +1 -1
  29. package/dist/agents/model-metadata.js +1 -1
  30. package/dist/agents/model-selection.js +2 -28
  31. package/dist/agents/models-config.providers.js +6 -6
  32. package/dist/agents/openai-codex-app-server.js +5 -5
  33. package/dist/agents/pi-embedded-runner/compact.js +15 -7
  34. package/dist/agents/pi-embedded-runner/model.js +1 -1
  35. package/dist/agents/pi-embedded-runner/run/attempt.js +24 -13
  36. package/dist/agents/pi-embedded-runner/run.js +2 -0
  37. package/dist/agents/pi-embedded-runner/tool-split.js +40 -2
  38. package/dist/agents/pi-embedded-subscribe.raw-stream.js +3 -3
  39. package/dist/agents/pi-extensions/compaction-safeguard.js +3 -3
  40. package/dist/agents/pi-model-discovery.js +6 -6
  41. package/dist/agents/pi-project-settings.js +3 -3
  42. package/dist/agents/pi-tool-definition-adapter.js +16 -1
  43. package/dist/agents/pi-tools.js +2 -1
  44. package/dist/agents/sandbox/config.js +1 -1
  45. package/dist/agents/sandbox/context.js +27 -2
  46. package/dist/agents/sandbox/fs-bridge.js +2 -2
  47. package/dist/agents/session-dirs.js +4 -4
  48. package/dist/agents/session-write-lock.js +13 -13
  49. package/dist/agents/shell-utils.js +5 -5
  50. package/dist/agents/skills/bundled-dir.js +4 -4
  51. package/dist/agents/skills/env-overrides.js +38 -28
  52. package/dist/agents/skills/local-loader.js +6 -6
  53. package/dist/agents/skills/plugin-skills.js +2 -2
  54. package/dist/agents/skills/trust.js +93 -0
  55. package/dist/agents/skills/workspace.js +141 -13
  56. package/dist/agents/skills-clawhub.js +36 -11
  57. package/dist/agents/skills-install-download.js +7 -7
  58. package/dist/agents/skills-install-plan.js +2 -2
  59. package/dist/agents/skills-install.js +2 -2
  60. package/dist/agents/skills-marketplace-scan.js +49 -0
  61. package/dist/agents/skills-status.js +2 -2
  62. package/dist/agents/subagent-depth.js +2 -2
  63. package/dist/agents/system-prompt-params.js +2 -2
  64. package/dist/agents/tools/marketplace-offer-draft-tool.js +1 -1
  65. package/dist/agents/tools/mining-tool.js +1 -1
  66. package/dist/agents/tools/session-status-tool.js +1 -1
  67. package/dist/agents/tools/wallet-action-tool.js +172 -36
  68. package/dist/agents/tools/wallet-skill-policy.js +65 -23
  69. package/dist/agents/tools/wallet-tool.js +7 -11
  70. package/dist/agents/workspace.js +22 -22
  71. package/dist/auto-reply/reply/agent-runner-execution.js +2 -2
  72. package/dist/auto-reply/reply/agent-runner.js +2 -2
  73. package/dist/auto-reply/reply/commands-export-session.js +7 -7
  74. package/dist/auto-reply/reply/directive-handling.fast-lane.js +2 -1
  75. package/dist/auto-reply/reply/directive-handling.impl.js +2 -1
  76. package/dist/auto-reply/reply/directive-handling.model.js +2 -1
  77. package/dist/auto-reply/reply/directive-handling.persist.js +2 -2
  78. package/dist/auto-reply/reply/get-reply-directives-apply.js +2 -0
  79. package/dist/auto-reply/reply/get-reply-inline-actions.js +25 -1
  80. package/dist/auto-reply/reply/model-selection.js +7 -4
  81. package/dist/auto-reply/reply/post-compaction-audit.js +3 -3
  82. package/dist/auto-reply/reply/post-compaction-context.js +3 -3
  83. package/dist/auto-reply/reply/session-reset-model.js +4 -4
  84. package/dist/auto-reply/reply/session.js +3 -3
  85. package/dist/auto-reply/skill-commands.js +3 -3
  86. package/dist/auto-reply/status.js +6 -6
  87. package/dist/brand.js +1 -1
  88. package/dist/browser/chrome.executables.js +3 -3
  89. package/dist/browser/chrome.js +3 -3
  90. package/dist/browser/chrome.profile-decoration.js +6 -6
  91. package/dist/browser/profiles-service.js +2 -2
  92. package/dist/browser/server-context.js +2 -2
  93. package/dist/browser/trash.js +4 -4
  94. package/dist/build-info.json +3 -3
  95. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  96. package/dist/canvas-host/server.js +4 -4
  97. package/dist/capabilities/catalog.js +3 -3
  98. package/dist/channel-web.js +2 -2
  99. package/dist/channels/plugins/catalog.js +3 -3
  100. package/dist/cli/browser-cli-extension.js +6 -6
  101. package/dist/cli/command-format.js +2 -2
  102. package/dist/cli/cron-cli/register.cron-simple.js +2 -2
  103. package/dist/cli/dns-cli.js +9 -9
  104. package/dist/cli/gateway-cli/dev.js +5 -5
  105. package/dist/cli/gateway-cli/run.js +10 -2
  106. package/dist/cli/hooks-cli.js +5 -5
  107. package/dist/cli/lightweight/gateway-runtime-probe.js +2 -2
  108. package/dist/cli/memory-cli.js +7 -7
  109. package/dist/cli/nodes-media-utils.js +2 -2
  110. package/dist/cli/plugins-cli.js +2 -2
  111. package/dist/cli/program/command-registry.js +1 -1
  112. package/dist/cli/program/register.onboard.js +2 -3
  113. package/dist/cli/program/register.wallet.js +16 -123
  114. package/dist/cli/providers-cli.js +1 -1
  115. package/dist/cli/sat-cli.js +129 -2
  116. package/dist/cli/secrets-cli.js +3 -3
  117. package/dist/cli/update-cli/update-command.js +260 -11
  118. package/dist/cli/windows-argv.js +2 -2
  119. package/dist/commands/auth-choice.apply.api-providers.js +3 -3
  120. package/dist/commands/dashboard-link.js +3 -3
  121. package/dist/commands/doctor-config-flow.js +1 -1
  122. package/dist/commands/doctor-install.js +5 -5
  123. package/dist/commands/doctor-legacy-config.js +2 -13
  124. package/dist/commands/doctor-memory-search.js +2 -2
  125. package/dist/commands/doctor-platform-notes.js +2 -2
  126. package/dist/commands/doctor-sandbox.js +2 -2
  127. package/dist/commands/doctor-state-integrity.js +23 -23
  128. package/dist/commands/doctor-workspace.js +3 -3
  129. package/dist/commands/doctor.js +3 -3
  130. package/dist/commands/managed-up.js +2 -2
  131. package/dist/commands/model-picker.js +6 -6
  132. package/dist/commands/models/auth.js +1 -1
  133. package/dist/commands/onboard-auth.config-gateways.js +1 -1
  134. package/dist/commands/onboard-auth.credentials.js +35 -11
  135. package/dist/commands/onboard-non-interactive/local/wallet-config.js +28 -27
  136. package/dist/commands/onboard-non-interactive/local.js +5 -1
  137. package/dist/commands/onboarding/plugin-install.js +2 -2
  138. package/dist/commands/sessions-cleanup.js +2 -2
  139. package/dist/commands/signal-install.js +6 -6
  140. package/dist/commands/status-all/channels.js +2 -2
  141. package/dist/commands/wallet.js +570 -1913
  142. package/dist/config/cache-utils.js +2 -2
  143. package/dist/config/config.js +2 -2
  144. package/dist/config/includes.js +5 -5
  145. package/dist/config/io.js +66 -4
  146. package/dist/config/paths.js +3 -3
  147. package/dist/config/sat-runtime-ids.js +3 -3
  148. package/dist/config/schema.help.js +8 -7
  149. package/dist/config/schema.labels.js +5 -4
  150. package/dist/config/sessions/disk-budget.js +6 -6
  151. package/dist/config/sessions/paths.js +2 -2
  152. package/dist/config/sessions/store.js +17 -17
  153. package/dist/config/sessions/transcript.js +4 -4
  154. package/dist/config/talk.js +2 -2
  155. package/dist/config/zod-schema.agent-runtime.js +1 -0
  156. package/dist/config/zod-schema.js +5 -0
  157. package/dist/control-ui/assets/{app-BXA4gPvN.js → app-bR-fO5ty.js} +1511 -1555
  158. package/dist/control-ui/assets/{config-CU5Zi8Dh.js → config-LeImukCW.js} +10 -10
  159. package/dist/control-ui/assets/{federation-D10Q4eP_.js → federation-DgA9e-Pn.js} +364 -422
  160. package/dist/control-ui/assets/{index-D2QbJhVg.js → index-ckCxHwjy.js} +3 -3
  161. package/dist/control-ui/assets/{mining-BdhMn18r.js → mining-BgHFh0ei.js} +134 -114
  162. package/dist/control-ui/assets/mining-commit-DFpB_4JN.js +2 -0
  163. package/dist/control-ui/assets/{open-external-url-DwSiwMPl.js → open-external-url-ulF1B-sA.js} +3 -3
  164. package/dist/control-ui/assets/{providers-QMn4w2_f.js → providers-CIx9fxc6.js} +2 -2
  165. package/dist/control-ui/assets/{wallet-C2g294QT.js → wallet-DvStR_AT.js} +259 -377
  166. package/dist/control-ui/index.html +1 -1
  167. package/dist/control-ui/version.json +1 -1
  168. package/dist/cron/service/store.js +2 -2
  169. package/dist/cron/store.js +6 -6
  170. package/dist/daemon/systemd-system.js +60 -54
  171. package/dist/discord/monitor/thread-bindings.state.js +2 -2
  172. package/dist/extensions/sat-mining/src/protocol-contract.js +43 -3
  173. package/dist/extensions/sat-mining/src/rpc-read.js +235 -67
  174. package/dist/extensions/sat-mining/src/sat-bond-layout.js +14 -11
  175. package/dist/extensions/sat-mining/src/signer-codec-manifest.js +406 -0
  176. package/dist/extensions/sat-mining/src/solana-submit.js +1038 -397
  177. package/dist/extensions/sat-mining/src/submission-ledger.js +349 -0
  178. package/dist/federation/auto-connect.js +98 -8
  179. package/dist/federation/marketplace-capability-adapter.js +33 -7
  180. package/dist/federation/marketplace-delivery-outbox.js +196 -0
  181. package/dist/federation/marketplace-delivery.js +351 -215
  182. package/dist/federation/marketplace-direct-payment.js +272 -141
  183. package/dist/federation/marketplace-escrow.js +607 -249
  184. package/dist/federation/marketplace-settlement-ledger.js +352 -0
  185. package/dist/federation/offers.js +9 -7
  186. package/dist/federation/peer-auth-v2.js +529 -0
  187. package/dist/gateway/a2a-http.js +425 -96
  188. package/dist/gateway/a2a-settlement.js +259 -153
  189. package/dist/gateway/a2a-task-store.js +524 -0
  190. package/dist/gateway/auth.js +38 -2
  191. package/dist/gateway/config-reload.js +46 -33
  192. package/dist/gateway/control-ui-boot-check.js +6 -6
  193. package/dist/gateway/control-ui-login.js +7 -7
  194. package/dist/gateway/control-ui.js +9 -9
  195. package/dist/gateway/durable-capacity.js +31 -0
  196. package/dist/gateway/federation-http.js +540 -135
  197. package/dist/gateway/federation-marketplace-run-store.js +397 -0
  198. package/dist/gateway/federation-marketplace.js +535 -230
  199. package/dist/gateway/hooks-mapping.js +4 -4
  200. package/dist/gateway/mutating-admin-rpc-rate-limit.js +5 -0
  201. package/dist/gateway/security-path.js +1 -1
  202. package/dist/gateway/server-discovery.js +2 -2
  203. package/dist/gateway/server-http.js +1088 -1209
  204. package/dist/gateway/server-methods/chat.js +3 -3
  205. package/dist/gateway/server-methods/models.js +1 -1
  206. package/dist/gateway/server-methods/secrets.js +40 -0
  207. package/dist/gateway/server-methods/sessions.js +6 -6
  208. package/dist/gateway/server-methods/usage.js +2 -2
  209. package/dist/gateway/server-methods.js +1 -2
  210. package/dist/gateway/server-reload-handlers.js +196 -66
  211. package/dist/gateway/server-runtime-config.js +1 -1
  212. package/dist/gateway/server-secrets-runtime.js +288 -0
  213. package/dist/gateway/server.impl.js +73 -42
  214. package/dist/gateway/session-compaction-checkpoints.js +4 -4
  215. package/dist/gateway/session-utils.fs.js +24 -24
  216. package/dist/gateway/session-utils.js +6 -6
  217. package/dist/gateway/sessions-patch.js +3 -5
  218. package/dist/hooks/bundled-dir.js +4 -4
  219. package/dist/hooks/gmail-setup-utils.js +6 -6
  220. package/dist/hooks/import-url.js +2 -2
  221. package/dist/hooks/loader.js +4 -4
  222. package/dist/hooks/workspace.js +7 -7
  223. package/dist/index.js +4 -2
  224. package/dist/infra/archive.js +17 -17
  225. package/dist/infra/boundary-file-read.js +2 -2
  226. package/dist/infra/boundary-path.js +15 -15
  227. package/dist/infra/brew.js +2 -2
  228. package/dist/infra/control-ui-assets.js +14 -14
  229. package/dist/infra/device-auth-store.js +8 -8
  230. package/dist/infra/device-identity.js +8 -8
  231. package/dist/infra/dotenv.js +2 -2
  232. package/dist/infra/exec-approvals.js +8 -8
  233. package/dist/infra/exec-command-resolution.js +4 -4
  234. package/dist/infra/exec-safe-bin-trust.js +2 -2
  235. package/dist/infra/fased-root.js +5 -5
  236. package/dist/infra/fasedhub.js +64 -1
  237. package/dist/infra/fs-safe.js +14 -14
  238. package/dist/infra/gateway-lock.js +11 -11
  239. package/dist/infra/git-commit.js +5 -5
  240. package/dist/infra/git-root.js +4 -4
  241. package/dist/infra/hosted-runtime-artifact.js +3 -3
  242. package/dist/infra/is-main.js +2 -2
  243. package/dist/infra/json-file.js +7 -7
  244. package/dist/infra/local-source-paired-update.js +538 -0
  245. package/dist/infra/managed-runtime-bootstrap.js +6 -6
  246. package/dist/infra/outbound/delivery-queue.js +16 -16
  247. package/dist/infra/path-env.js +3 -3
  248. package/dist/infra/ports-lsof.js +4 -4
  249. package/dist/infra/provider-usage.auth.js +3 -3
  250. package/dist/infra/restart.js +41 -10
  251. package/dist/infra/safe-open-sync.js +2 -2
  252. package/dist/infra/session-cost-usage.js +9 -9
  253. package/dist/infra/shell-env.js +2 -2
  254. package/dist/infra/state-migrations.fs.js +6 -6
  255. package/dist/infra/state-migrations.js +23 -23
  256. package/dist/infra/tmp-fased-dir.js +6 -6
  257. package/dist/infra/update-runner.js +96 -0
  258. package/dist/infra/widearea-dns.js +3 -3
  259. package/dist/infra/wsl.js +2 -2
  260. package/dist/light-update-status.js +11 -11
  261. package/dist/line/accounts.js +2 -2
  262. package/dist/line/download.js +2 -2
  263. package/dist/logging/config.js +3 -3
  264. package/dist/logging/logger.js +7 -7
  265. package/dist/managed/federation.js +3 -3
  266. package/dist/managed/tunnel.js +3 -3
  267. package/dist/media/store.js +13 -13
  268. package/dist/media-understanding/runner.js +3 -3
  269. package/dist/memory/backend-config.js +2 -1
  270. package/dist/memory/embeddings.js +2 -2
  271. package/dist/memory/internal.js +10 -10
  272. package/dist/memory/manager-sync-ops.js +7 -7
  273. package/dist/memory/repair-executor.js +17 -17
  274. package/dist/memory/session-files.js +16 -2
  275. package/dist/node-host/invoke-system-run-plan.js +8 -8
  276. package/dist/node-host/invoke.js +2 -2
  277. package/dist/node-host/runner.js +3 -3
  278. package/dist/pairing/pairing-store.js +3 -3
  279. package/dist/plugin-sdk/{accounts-DxblobYM.js → accounts-Brn3-uWN.js} +1 -1
  280. package/dist/plugin-sdk/{actions-D4UNimWa.js → actions-DRu135h0.js} +2 -2
  281. package/dist/plugin-sdk/{active-listener-CFuy106s.js → active-listener-B_NMtgPL.js} +1 -1
  282. package/dist/plugin-sdk/{audio-preflight-Bv0hUOWL.js → audio-preflight-BYxAt775.js} +27 -27
  283. package/dist/plugin-sdk/{audit-Cs0K7LBp.js → audit-C5d2z5U1.js} +1 -1
  284. package/dist/plugin-sdk/{channel-access-D5aHoTY8.js → channel-access-CcFRdRPO.js} +1 -1
  285. package/dist/plugin-sdk/{channel-access-configure-Dvch9mMQ.js → channel-access-configure-DaI5wevh.js} +1 -1
  286. package/dist/plugin-sdk/channel-plugin-common.d.ts +3 -3
  287. package/dist/plugin-sdk/channel-plugin-common.js +4 -4
  288. package/dist/plugin-sdk/{chunk-B0FUGc5A.js → chunk-CyoyqpbG.js} +1 -1
  289. package/dist/plugin-sdk/{chunk-items-Bk--NU3E.js → chunk-items-DIRHG2gJ.js} +1 -1
  290. package/dist/plugin-sdk/command-status.d.ts +2 -2
  291. package/dist/plugin-sdk/command-status.js +27 -27
  292. package/dist/plugin-sdk/{commands-registry-DmDRhRAU.js → commands-registry-BfqdEWdF.js} +2 -2
  293. package/dist/plugin-sdk/{common-CfUOowOp.js → common-HCcXU-NR.js} +1 -1
  294. package/dist/plugin-sdk/{config-mxULv0fQ.js → config-DlWVeLFB.js} +7113 -6877
  295. package/dist/plugin-sdk/{config-schema-CGW6J3Pv.js → config-schema-BOT7kHb6.js} +6779 -3696
  296. package/dist/plugin-sdk/device-pair.d.ts +1 -1
  297. package/dist/plugin-sdk/discord.d.ts +5 -5
  298. package/dist/plugin-sdk/discord.js +19 -19
  299. package/dist/plugin-sdk/file-lock.js +35 -4
  300. package/dist/plugin-sdk/{filesystem-manager-ZoHU8oA7.js → filesystem-manager-DoI8IBNF.js} +1 -1
  301. package/dist/plugin-sdk/{group-mentions-BHRff6j8.d.ts → group-mentions-q3hUTCt7.d.ts} +1 -1
  302. package/dist/plugin-sdk/{handle-action-DbVCF9HZ.js → handle-action-hvwapMKg.js} +13 -13
  303. package/dist/plugin-sdk/{helpers-BXbdgs5H.js → helpers-DMBU4oTo.js} +1 -1
  304. package/dist/plugin-sdk/{image-ops-D7jPxaph.js → image-ops-DQi0n_Tt.js} +1 -1
  305. package/dist/plugin-sdk/index.d.ts +9 -27
  306. package/dist/plugin-sdk/index.js +29 -29
  307. package/dist/plugin-sdk/json-store.js +6 -6
  308. package/dist/plugin-sdk/{login-BRxww6nv.js → login-Ca583Okc.js} +3 -3
  309. package/dist/plugin-sdk/{login-qr-DJXsk9XH.js → login-qr-CGciBSNP.js} +3 -3
  310. package/dist/plugin-sdk/{manager-Y2S_5IlI.js → manager-BegWxNYX.js} +4 -4
  311. package/dist/plugin-sdk/{markdown-tables-DHLFQqaF.js → markdown-tables-DCOXEUfZ.js} +1 -1
  312. package/dist/plugin-sdk/{media-Czo4carg.js → media-CAdQF5TG.js} +47 -47
  313. package/dist/plugin-sdk/{memory-search-DTjIMw8J.js → memory-search-CrFPOeTH.js} +4 -1
  314. package/dist/plugin-sdk/{monitor-B9Cmt_Je.js → monitor-BCFDaqJ-.js} +31 -31
  315. package/dist/plugin-sdk/{monitor-B71iwAgC.js → monitor-BgbjRiPM.js} +32 -32
  316. package/dist/plugin-sdk/{openresponses-http-Du4GMmwo.js → openresponses-http-DFf87SMM.js} +27 -27
  317. package/dist/plugin-sdk/{optional-runtime-dependency-DdMbxS24.js → optional-runtime-dependency-0dznm3ZO.js} +1 -1
  318. package/dist/plugin-sdk/{outbound-Db8mRN6P.js → outbound-BgVDdHxx.js} +6 -6
  319. package/dist/plugin-sdk/{outbound-attachment-BqFjcypb.js → outbound-attachment-QqTnIhZL.js} +2 -2
  320. package/dist/plugin-sdk/{pairing-message-BMkkS038.d.ts → pairing-message-CS-9BHug.d.ts} +2 -2
  321. package/dist/plugin-sdk/{pairing-message-B3GC_7CD.js → pairing-message-D0CoLHRJ.js} +1 -1
  322. package/dist/plugin-sdk/{pairing-store-CQikgIbw.js → pairing-store-BChahgI2.js} +2 -2
  323. package/dist/plugin-sdk/{pi-model-discovery-CY3m5zJP.js → pi-model-discovery-BX_JLcEJ.js} +1 -1
  324. package/dist/plugin-sdk/{pi-model-discovery-runtime-DSh1T2pn.js → pi-model-discovery-runtime-DxTMz_XF.js} +2 -2
  325. package/dist/plugin-sdk/provider-web-search-config-contract.d.ts +2 -2
  326. package/dist/plugin-sdk/{pw-ai-AFUqn-tQ.js → pw-ai-natjh3rm.js} +3 -3
  327. package/dist/plugin-sdk/{qmd-manager-DSyx77KY.js → qmd-manager-CgRaTxUq.js} +1 -1
  328. package/dist/plugin-sdk/{query-expansion-CAI2kKLQ.js → query-expansion-BG3FgzF7.js} +14 -3
  329. package/dist/plugin-sdk/{replies-Fzh9PhXl.js → replies-ChYo5ZQM.js} +2 -2
  330. package/dist/plugin-sdk/{resolve-outbound-target-KpF3co6T.js → resolve-outbound-target-D-KGgHJZ.js} +1 -1
  331. package/dist/plugin-sdk/{resolve-route-CDl74O19.js → resolve-route-C8tvQm8Z.js} +1 -1
  332. package/dist/plugin-sdk/{retry-policy-D4uiqUOG.js → retry-policy-C6SjKjQA.js} +1 -1
  333. package/dist/plugin-sdk/{runtime-Bb6KRnCC.js → runtime-2B9_WKoI.js} +8 -3
  334. package/dist/plugin-sdk/{runtime-dependency-DLDmB94f.js → runtime-dependency-DczkvPxn.js} +2 -2
  335. package/dist/plugin-sdk/sat-runtime.d.ts +549 -309
  336. package/dist/plugin-sdk/sat-runtime.js +29 -29
  337. package/dist/plugin-sdk/{send-C4k4JuJN.js → send-B6KkGKZD.js} +6 -6
  338. package/dist/plugin-sdk/{send-D-uDKGIq.js → send-CFzGKJXo.js} +6 -6
  339. package/dist/plugin-sdk/{send-Du8ykP1k.js → send-DCO441U7.js} +8 -8
  340. package/dist/plugin-sdk/{send-5nFktR1n.js → send-gDoGA-7e.js} +5 -5
  341. package/dist/plugin-sdk/{send-Bfzf11VY.js → send-q9HXrSwK.js} +7 -7
  342. package/dist/plugin-sdk/{session-CS-Td9EM.js → session-DHD4ZKyG.js} +2 -2
  343. package/dist/plugin-sdk/{skill-commands-CFAlXWHk.js → skill-commands-FKEPLS7T.js} +201 -26
  344. package/dist/plugin-sdk/{slack-D_bbE6Yd.js → slack-BUTt8cxK.js} +39 -39
  345. package/dist/plugin-sdk/{slack-actions-BcMkNv1Z.js → slack-actions-DhbMcp8M.js} +11 -11
  346. package/dist/plugin-sdk/slack.d.ts +5 -5
  347. package/dist/plugin-sdk/slack.js +9 -9
  348. package/dist/plugin-sdk/{sticker-cache-Dqo9nRhi.js → sticker-cache-D0wHVqjt.js} +3 -3
  349. package/dist/plugin-sdk/{tables-bFXyRpkx.js → tables-B3jr0Pvq.js} +1 -1
  350. package/dist/plugin-sdk/{targets-DH7a9lKB.js → targets-CtSbTxYX.js} +1 -1
  351. package/dist/plugin-sdk/{telegram-actions-Cnag7JTH.js → telegram-actions-CC_qN4v0.js} +30 -30
  352. package/dist/plugin-sdk/telegram.d.ts +5 -5
  353. package/dist/plugin-sdk/telegram.js +8 -8
  354. package/dist/plugin-sdk/{thread-bindings.lifecycle-CgOvvdw8.js → thread-bindings.lifecycle-QbkNl-m6.js} +3 -3
  355. package/dist/plugin-sdk/{thread-bindings.state-CQpk44Hq.js → thread-bindings.state-D9hg_M1c.js} +2 -2
  356. package/dist/plugin-sdk/tmp-fased-dir-xOTGlzBE.d.ts +486 -0
  357. package/dist/plugin-sdk/{types-CLIvQaVg.d.ts → types-C3avNytw.d.ts} +3 -4
  358. package/dist/plugin-sdk/{types-DyhXnYUr.d.ts → types-D0kDG8l0.d.ts} +3 -2
  359. package/dist/plugin-sdk/{types-BYDL6dQj.d.ts → types-DWfbSAaM.d.ts} +2 -2
  360. package/dist/plugin-sdk/{types.plugin-_fbu40Re.d.ts → types.plugin-BcSBq42x.d.ts} +8 -3
  361. package/dist/plugin-sdk/{web-BInKQkc4.js → web-BXsJ1vqe.js} +32 -32
  362. package/dist/plugin-sdk/{whatsapp-actions-BtSc2CBv.js → whatsapp-actions-Bj_rOncr.js} +13 -13
  363. package/dist/plugin-sdk/{whatsapp-heartbeat-Bg9TuYzl.js → whatsapp-heartbeat-B1WHkJIg.js} +2 -2
  364. package/dist/plugin-sdk/whatsapp.d.ts +5 -5
  365. package/dist/plugin-sdk/whatsapp.js +12 -12
  366. package/dist/plugin-sdk/{wsl-C4DGFkOd.js → wsl-J-HscWxS.js} +3 -3
  367. package/dist/plugins/bundled-dir.js +3 -3
  368. package/dist/plugins/discovery.js +11 -11
  369. package/dist/plugins/loader.js +13 -13
  370. package/dist/plugins/manifest-registry.js +2 -2
  371. package/dist/plugins/manifest.js +4 -4
  372. package/dist/plugins/path-safety.js +3 -3
  373. package/dist/plugins/status-cache.js +9 -9
  374. package/dist/plugins/update.js +3 -3
  375. package/dist/plugins/web-search-install-catalog.js +3 -3
  376. package/dist/providers/refresh.js +1 -1
  377. package/dist/providers/registry.js +1 -1
  378. package/dist/secrets/apply.js +17 -17
  379. package/dist/secrets/audit.js +11 -11
  380. package/dist/secrets/runtime.js +1 -1
  381. package/dist/secrets/shared.js +9 -9
  382. package/dist/security/scan-paths.js +2 -2
  383. package/dist/shared/config-eval.js +2 -2
  384. package/dist/shared/pid-alive.js +2 -2
  385. package/dist/tasks/standing-orders.js +5 -5
  386. package/dist/tasks/task-flow-registry.js +5 -5
  387. package/dist/tasks/task-registry.js +5 -5
  388. package/dist/tasks/workflow-definitions.js +6 -6
  389. package/dist/telegram/token.js +5 -5
  390. package/dist/utils.js +5 -5
  391. package/dist/wallet/external-submission-ledger.js +376 -0
  392. package/dist/wallet/legacy-embedded-keystore.js +40 -0
  393. package/dist/wallet/local-socket-signer-archive.js +30 -0
  394. package/dist/wallet/local-socket-signer-lifecycle.js +97 -0
  395. package/dist/wallet/local-socket-signer-policy.js +166 -0
  396. package/dist/wallet/local-socket-signer-protocol.js +561 -118
  397. package/dist/wallet/native-signer-wallet-id.js +25 -0
  398. package/dist/wallet/providers/alchemy-adapter.js +3 -0
  399. package/dist/wallet/providers/local-socket-signer-adapter.js +382 -107
  400. package/dist/wallet/providers/turnkey-adapter.js +292 -34
  401. package/dist/wallet/providers/wallet-standard-adapter.js +100 -0
  402. package/dist/wallet/signer-network-admin.js +184 -0
  403. package/dist/wallet/signer-protocol-v2.generated.js +90 -0
  404. package/dist/wallet/solana-bond-signing.js +157 -69
  405. package/dist/wallet/solana-reviewed-transaction.js +375 -0
  406. package/dist/wallet/solana-swap.js +520 -64
  407. package/dist/wallet/solana-transaction-inspection.js +106 -4
  408. package/dist/wallet/solana-trigger.js +658 -320
  409. package/dist/wallet/turnkey-reviewed-state.js +370 -0
  410. package/dist/wallet/wallet-approval-auth.js +20 -31
  411. package/dist/wallet/wallet-atomic-state.js +53 -0
  412. package/dist/wallet/wallet-audit-log.js +5 -5
  413. package/dist/wallet/wallet-canary.js +0 -1
  414. package/dist/wallet/wallet-inbound-events.js +19 -14
  415. package/dist/wallet/wallet-observability.js +5 -5
  416. package/dist/wallet/wallet-policy-simulation.js +1 -1
  417. package/dist/wallet/wallet-policy.js +95 -41
  418. package/dist/wallet/wallet-provider-capabilities.js +28 -4
  419. package/dist/wallet/wallet-provider-registry.js +64 -51
  420. package/dist/wallet/wallet-provider-resolver.js +62 -63
  421. package/dist/wallet/wallet-runtime-config.js +31 -44
  422. package/dist/wallet/wallet-secrets-store.js +44 -22
  423. package/dist/wallet/wallet-send-approvals.js +1894 -592
  424. package/dist/wallet/wallet-send-execution-ledger.js +292 -0
  425. package/dist/wallet/wallet-settlement-links.js +17 -16
  426. package/dist/wallet/wallet-standard-review.js +353 -0
  427. package/dist/wallet/wallet-status.js +0 -9
  428. package/dist/wallet/wallet-task-ledger.js +16 -2
  429. package/dist/web/accounts.js +3 -3
  430. package/dist/web/auth-store.js +18 -18
  431. package/dist/web/auto-reply/heartbeat-runner.js +1 -1
  432. package/dist/web/auto-reply/monitor.js +2 -2
  433. package/dist/web/session.js +4 -4
  434. package/dist/wizard/host-security-capability.js +15 -24
  435. package/dist/wizard/onboarding.finalize.js +63 -184
  436. package/dist/wizard/onboarding.host-security.js +118 -1368
  437. package/dist/wizard/onboarding.js +180 -242
  438. package/dist/wizard/onboarding.wallet.js +323 -829
  439. package/extensions/acpx/package.json +1 -1
  440. package/extensions/bluebubbles/package.json +1 -1
  441. package/extensions/copilot-proxy/package.json +1 -1
  442. package/extensions/diagnostics-otel/package.json +1 -1
  443. package/extensions/fased-federation/package.json +1 -1
  444. package/extensions/google-gemini-cli-auth/package.json +1 -1
  445. package/extensions/imessage/package.json +1 -1
  446. package/extensions/irc/package.json +1 -1
  447. package/extensions/line/package.json +1 -1
  448. package/extensions/llm-task/package.json +1 -1
  449. package/extensions/lobster/package.json +1 -1
  450. package/extensions/matrix/CHANGELOG.md +18 -0
  451. package/extensions/matrix/package.json +1 -1
  452. package/extensions/mattermost/package.json +1 -1
  453. package/extensions/memory-core/package.json +2 -2
  454. package/extensions/memory-lancedb/package.json +1 -1
  455. package/extensions/minimax-portal-auth/package.json +1 -1
  456. package/extensions/msteams/CHANGELOG.md +18 -0
  457. package/extensions/msteams/package.json +1 -1
  458. package/extensions/nextcloud-talk/package.json +1 -1
  459. package/extensions/nostr/CHANGELOG.md +18 -0
  460. package/extensions/nostr/package.json +1 -1
  461. package/extensions/open-prose/package.json +1 -1
  462. package/extensions/sat-mining/index.ts +498 -153
  463. package/extensions/sat-mining/package.json +1 -1
  464. package/extensions/sat-mining/signer-codec-schema.v1.json +374 -0
  465. package/extensions/sat-mining/src/audit-store.ts +50 -5
  466. package/extensions/sat-mining/src/claim-service.ts +85 -14
  467. package/extensions/sat-mining/src/config.ts +9 -0
  468. package/extensions/sat-mining/src/cycle-progress.ts +16 -8
  469. package/extensions/sat-mining/src/epoch-service.ts +9 -5
  470. package/extensions/sat-mining/src/gateway-runner.ts +21 -1
  471. package/extensions/sat-mining/src/protocol-contract.ts +45 -1
  472. package/extensions/sat-mining/src/recovery-service.ts +312 -0
  473. package/extensions/sat-mining/src/round-watcher.ts +391 -81
  474. package/extensions/sat-mining/src/rpc-read.ts +330 -89
  475. package/extensions/sat-mining/src/runtime.ts +14 -0
  476. package/extensions/sat-mining/src/sat-bond-layout.ts +6 -0
  477. package/extensions/sat-mining/src/signer-codec-manifest.ts +429 -0
  478. package/extensions/sat-mining/src/solana-submit.ts +1199 -428
  479. package/extensions/sat-mining/src/submission-ledger.ts +433 -0
  480. package/extensions/signal/package.json +1 -1
  481. package/extensions/synology-chat/package.json +1 -1
  482. package/extensions/tlon/package.json +1 -1
  483. package/extensions/twitch/CHANGELOG.md +18 -0
  484. package/extensions/twitch/package.json +1 -1
  485. package/extensions/voice-call/CHANGELOG.md +18 -0
  486. package/extensions/voice-call/package.json +1 -1
  487. package/extensions/zalo/CHANGELOG.md +18 -0
  488. package/extensions/zalo/package.json +1 -1
  489. package/extensions/zalouser/CHANGELOG.md +18 -0
  490. package/extensions/zalouser/package.json +1 -1
  491. package/install.sh +4955 -0
  492. package/package.json +24 -5
  493. package/scripts/fased-host-updater.mjs +1421 -0
  494. package/scripts/fased-host-updaterctl.mjs +186 -0
  495. package/scripts/fased-managed-service.sh +36 -0
  496. package/scripts/fased-managed-updater.mjs +3065 -156
  497. package/scripts/fased-signer-enroll-hosting.sh +275 -0
  498. package/scripts/fased-signer-network-hosting.sh +143 -0
  499. package/scripts/fased-signer-owner-policy.mjs +1430 -0
  500. package/scripts/fased-signer-policy-hosting.sh +50 -0
  501. package/scripts/fased-signer-policy-local.sh +26 -0
  502. package/scripts/hosted-release-manifest.mjs +185 -0
  503. package/scripts/install-fased-signerd.sh +217 -0
  504. package/scripts/install-managed-runtime.mjs +212 -10
  505. package/scripts/managed-runtime-layout.mjs +75 -3
  506. package/scripts/migrate-hosted-signer-v2.mjs +58 -0
  507. package/scripts/start-managed.sh +169 -360
  508. package/dist/control-ui/assets/mining-commit-R-6nu87p.js +0 -2
  509. package/dist/plugin-sdk/tmp-fased-dir-BdFQvrvw.d.ts +0 -77
  510. package/dist/wallet/local-socket-signer-broker.js +0 -224
  511. package/dist/wallet/local-socket-signer-custody.js +0 -42
  512. package/dist/wallet/providers/embedded-keystore-adapter.js +0 -402
  513. package/dist/wallet/providers/privy-adapter.js +0 -80
  514. package/dist/wallet/wallet-custody.js +0 -1836
@@ -1,15 +1,16 @@
1
+ import { resolveGatewayPort } from "../config/paths.js";
2
+ import { VERSION } from "../version.js";
1
3
  import { loadConfig } from "../config/io.js";
2
4
  import "../config/config.js";
3
- 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";
4
7
  import { WALLET_PROVIDER_IDS, readWalletProviderRegistry, setWalletProvidersEnabled } from "../wallet/wallet-provider-registry.js";
5
- import { applyWalletPolicyConfig, resolveWalletRoleForId } from "../wallet/wallet-policy.js";
6
8
  import { callLocalSocketSigner } from "../wallet/providers/local-socket-signer-adapter.js";
7
9
  import { resolveWalletProviderId } from "../wallet/wallet-provider-resolver.js";
8
10
  import { fileURLToPath } from "node:url";
9
- import fs from "node:fs";
11
+ import syncFs from "node:fs";
10
12
  import path from "node:path";
11
13
  import { execSync, spawn, spawnSync } from "node:child_process";
12
- import { randomBytes } from "node:crypto";
13
14
  import net from "node:net";
14
15
 
15
16
  //#region src/wizard/onboarding.wallet.ts
@@ -20,35 +21,13 @@ function resolveSignerdBinaryPath(env = process.env) {
20
21
  }
21
22
  return path.join(env.HOME ?? "/root", ".fased/bin/fased-signerd");
22
23
  }
23
- function resolveAppOwnedSignerdStagingPath(env = process.env) {
24
- return path.join(env.HOME ?? "/root", ".fased/bin/fased-signerd");
25
- }
26
- const INSTALL_SCRIPT_RELPATHS = ["scripts/install-fased-signerd.sh", "../scripts/install-fased-signerd.sh"];
27
- const BUILD_SCRIPT_RELPATHS = ["scripts/build-fased-signerd.sh", "../scripts/build-fased-signerd.sh"];
28
- const BROKER_CLI_RELPATHS = ["./index.js", "../../dist/index.js"];
24
+ const INSTALL_SCRIPT_RELURLS = ["../../scripts/install-fased-signerd.sh", "../scripts/install-fased-signerd.sh"];
25
+ const BUILD_SCRIPT_RELURLS = ["../../scripts/build-fased-signerd.sh", "../scripts/build-fased-signerd.sh"];
29
26
  const DEFAULT_SIGNER_RELEASE_DOWNLOAD_BASE = "https://github.com/fased-ai/fased/releases/download";
30
- const SIGNER_MAINTENANCE_HELPER = "/usr/local/sbin/fased-signer-maintenance";
31
- const SIGNER_ISOLATION_HELPER = "/usr/local/sbin/fased-signer-isolation";
32
- function runCommand(params) {
33
- const child = spawnSync(params.command, params.args, {
34
- env: params.env,
35
- encoding: "utf8",
36
- stdio: params.capture ? [
37
- "ignore",
38
- "pipe",
39
- "pipe"
40
- ] : "ignore"
41
- });
42
- if (child.status !== 0) {
43
- const stderr = typeof child.stderr === "string" ? child.stderr.trim() : "";
44
- const stdout = typeof child.stdout === "string" ? child.stdout.trim() : "";
45
- throw new Error(stderr || stdout || `${params.command} exited with status ${child.status ?? -1}`);
46
- }
47
- return typeof child.stdout === "string" ? child.stdout : "";
48
- }
27
+ const LOCAL_SIGNER_ENROLLMENT_ORIGIN = "http://localhost:18791";
49
28
  function isSignerdRunning(socketPath) {
50
29
  try {
51
- fs.statSync(socketPath);
30
+ syncFs.statSync(socketPath);
52
31
  return true;
53
32
  } catch {
54
33
  return false;
@@ -77,7 +56,7 @@ async function sleep(ms) {
77
56
  }
78
57
  function readFileTail(filePath, maxLines = 20) {
79
58
  try {
80
- const text = fs.readFileSync(filePath, "utf8").trim();
59
+ const text = syncFs.readFileSync(filePath, "utf8").trim();
81
60
  if (!text) {
82
61
  return undefined;
83
62
  }
@@ -97,28 +76,19 @@ async function waitForSignerdHealthy(socketPath, deadlineMs) {
97
76
  return isSignerdRunning(socketPath) && await isSignerdHealthy(socketPath);
98
77
  }
99
78
  function resolveSignerdLogPath(params) {
100
- 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");
101
80
  }
102
81
  function buildSignerdReadinessError(params) {
103
- const signerLogPath = resolveSignerdLogPath({
104
- appSocketPath: params.appSocketPath,
105
- materialDir: params.materialDir,
106
- runAsUser: params.runAsUser
107
- });
108
- const brokerLogPath = path.join(path.dirname(params.appSocketPath), "local-signer-broker.log");
82
+ const signerLogPath = resolveSignerdLogPath({ materialDir: params.materialDir });
109
83
  const logDetails = [];
110
84
  const signerLogTail = readFileTail(signerLogPath);
111
85
  if (signerLogTail) {
112
86
  logDetails.push(`Signer log tail (${signerLogPath}):\n${signerLogTail}`);
113
87
  }
114
- const brokerLogTail = params.backendSocketPath !== params.appSocketPath ? readFileTail(brokerLogPath) : undefined;
115
- if (brokerLogTail) {
116
- logDetails.push(`Broker log tail (${brokerLogPath}):\n${brokerLogTail}`);
117
- }
118
88
  return new Error([
119
89
  `fased-signerd did not become ready within ${Math.round(params.readyTimeoutMs / 1e3)}s.`,
120
- `Check logs: ${signerLogPath}${params.backendSocketPath !== params.appSocketPath ? `, ${brokerLogPath}` : ""}`,
121
- `Run manually: ${params.binPath} -socket ${params.backendSocketPath}`,
90
+ `Check logs: ${signerLogPath}`,
91
+ `Run manually: ${params.binPath} -socket ${params.socketPath}`,
122
92
  ...logDetails
123
93
  ].join("\n"));
124
94
  }
@@ -157,27 +127,18 @@ async function readSignerdHealth(socketPath) {
157
127
  }
158
128
  }
159
129
  function resolveInstallScript() {
160
- for (const rel of INSTALL_SCRIPT_RELPATHS) {
161
- const abs = path.resolve(rel);
162
- if (fs.existsSync(abs)) {
163
- return abs;
130
+ for (const rel of INSTALL_SCRIPT_RELURLS) {
131
+ const candidate = fileURLToPath(new URL(rel, import.meta.url));
132
+ if (syncFs.existsSync(candidate)) {
133
+ return candidate;
164
134
  }
165
135
  }
166
136
  return null;
167
137
  }
168
138
  function resolveBuildScript() {
169
- for (const rel of BUILD_SCRIPT_RELPATHS) {
170
- const abs = path.resolve(rel);
171
- if (fs.existsSync(abs)) {
172
- return abs;
173
- }
174
- }
175
- return null;
176
- }
177
- function resolveBrokerCliPath() {
178
- for (const rel of BROKER_CLI_RELPATHS) {
139
+ for (const rel of BUILD_SCRIPT_RELURLS) {
179
140
  const candidate = fileURLToPath(new URL(rel, import.meta.url));
180
- if (fs.existsSync(candidate)) {
141
+ if (syncFs.existsSync(candidate)) {
181
142
  return candidate;
182
143
  }
183
144
  }
@@ -190,17 +151,17 @@ function resolveLocalSignerdReleaseBaseUrl(installScriptPath) {
190
151
  const arch = process.arch === "arm64" ? "arm64" : "amd64";
191
152
  const assetPath = path.join(releaseDir, `fased-signerd-${platform}-${arch}`);
192
153
  const checksumsPath = path.join(releaseDir, "fased-signerd-checksums.txt");
193
- if (!fs.existsSync(assetPath) || !fs.existsSync(checksumsPath)) {
154
+ if (!syncFs.existsSync(assetPath) || !syncFs.existsSync(checksumsPath)) {
194
155
  return null;
195
156
  }
196
157
  return `file://${releaseDir}`;
197
158
  }
198
159
  function resolveGoBinary(env = process.env) {
199
160
  const explicit = String(env.FASED_GO_BIN ?? "").trim();
200
- if (explicit && fs.existsSync(explicit)) {
161
+ if (explicit && syncFs.existsSync(explicit)) {
201
162
  return explicit;
202
163
  }
203
- if (fs.existsSync("/usr/local/go/bin/go")) {
164
+ if (syncFs.existsSync("/usr/local/go/bin/go")) {
204
165
  return "/usr/local/go/bin/go";
205
166
  }
206
167
  const child = spawnSync("bash", ["-lc", "command -v go || true"], {
@@ -227,13 +188,14 @@ function isGoModernEnough(goBin) {
227
188
  if (child.status !== 0) {
228
189
  return false;
229
190
  }
230
- const match = String(child.stdout ?? "").match(/\bgo(\d+)\.(\d+)(?:\.\d+)?\b/);
191
+ const match = String(child.stdout ?? "").match(/\bgo(\d+)\.(\d+)(?:\.(\d+))?\b/);
231
192
  if (!match) {
232
193
  return false;
233
194
  }
234
195
  const major = Number(match[1]);
235
196
  const minor = Number(match[2]);
236
- return major > 1 || major === 1 && minor >= 21;
197
+ const patch = Number(match[3] ?? 0);
198
+ return major > 1 || major === 1 && (minor > 25 || minor === 25 && patch >= 7);
237
199
  }
238
200
  function hasExplicitSignerdAssetSource(env = process.env) {
239
201
  const baseUrl = String(env.FASED_LOCAL_SIGNER_BASE_URL ?? "").trim();
@@ -271,33 +233,12 @@ function runScriptOrThrow(params) {
271
233
  function copySignerdBinaryToTarget(params) {
272
234
  const sourcePath = path.resolve(params.sourcePath);
273
235
  const targetPath = path.resolve(params.targetPath);
274
- if (!fs.existsSync(sourcePath)) {
236
+ if (!syncFs.existsSync(sourcePath)) {
275
237
  throw new Error(`fased-signerd build completed but binary was not found at ${sourcePath}.`);
276
238
  }
277
- if (params.runAsUser) {
278
- if (runSignerIsolationHelper(params.runAsUser, [
279
- "install-binary",
280
- sourcePath,
281
- targetPath
282
- ]) === undefined) {
283
- runCommand({
284
- command: "sudo",
285
- args: [
286
- "bash",
287
- "-lc",
288
- [
289
- "set -euo pipefail",
290
- `install -d -m 755 -o ${JSON.stringify(params.runAsUser)} -g ${JSON.stringify(params.runAsUser)} ${JSON.stringify(path.dirname(targetPath))}`,
291
- `install -m 755 -o ${JSON.stringify(params.runAsUser)} -g ${JSON.stringify(params.runAsUser)} ${JSON.stringify(sourcePath)} ${JSON.stringify(targetPath)}`
292
- ].join("; ")
293
- ]
294
- });
295
- }
296
- return;
297
- }
298
- fs.mkdirSync(path.dirname(targetPath), { recursive: true });
299
- fs.copyFileSync(sourcePath, targetPath);
300
- fs.chmodSync(targetPath, 493);
239
+ syncFs.mkdirSync(path.dirname(targetPath), { recursive: true });
240
+ syncFs.copyFileSync(sourcePath, targetPath);
241
+ syncFs.chmodSync(targetPath, 493);
301
242
  }
302
243
  function buildSignerdBinaryFromSource(binPath) {
303
244
  if (String(process.env.FASED_SKIP_NATIVE_SIGNER_BUILD ?? "").trim() === "1") {
@@ -321,127 +262,93 @@ function buildSignerdBinaryFromSource(binPath) {
321
262
  const builtPath = path.resolve(path.dirname(script), "..", "dist-native", "fased-signerd");
322
263
  copySignerdBinaryToTarget({
323
264
  sourcePath: builtPath,
324
- targetPath: binPath,
325
- runAsUser: resolveLocalSignerRunAsUser(process.env)
265
+ targetPath: binPath
326
266
  });
327
267
  return true;
328
268
  }
329
- function installSignerdBinaryFromExplicitAsset(binPath) {
330
- if (!hasExplicitSignerdAssetSource(process.env)) {
331
- return false;
269
+ function resolveSignerdAssetInstallEnv(installScript) {
270
+ const env = { ...process.env };
271
+ if (hasExplicitSignerdAssetSource(env)) {
272
+ return env;
273
+ }
274
+ const localReleaseBaseUrl = resolveLocalSignerdReleaseBaseUrl(installScript);
275
+ if (localReleaseBaseUrl) {
276
+ env.FASED_LOCAL_SIGNER_BASE_URL = localReleaseBaseUrl;
277
+ env.FASED_LOCAL_SIGNER_VERSION = `v${VERSION.trim().replace(/^v/, "")}`;
278
+ env.FASED_LOCAL_SIGNER_LATEST_TAG = "";
279
+ env.FASED_LOCAL_SIGNER_ALLOW_UNATTESTED = "1";
280
+ env.FASED_LOCAL_SIGNER_FLAT_RELEASE = "1";
281
+ return env;
282
+ }
283
+ const normalizedVersion = VERSION.trim().replace(/^v/, "");
284
+ if (!/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(normalizedVersion)) {
285
+ throw new Error(`Cannot resolve a versioned fased-signerd asset for Fased ${VERSION}.`);
286
+ }
287
+ env.FASED_LOCAL_SIGNER_BASE_URL = DEFAULT_SIGNER_RELEASE_DOWNLOAD_BASE;
288
+ env.FASED_LOCAL_SIGNER_VERSION = `v${normalizedVersion}`;
289
+ env.FASED_LOCAL_SIGNER_LATEST_TAG = "";
290
+ return env;
291
+ }
292
+ function installSignerdBinaryFromAsset(binPath) {
293
+ if (process.platform === "win32") {
294
+ throw new Error("The local socket signer requires Unix sockets. On Windows, install and run Fased inside WSL2.");
295
+ }
296
+ if (process.platform !== "linux" && process.platform !== "darwin") {
297
+ throw new Error(`No fased-signerd release asset is available for ${process.platform}.`);
332
298
  }
333
299
  const script = resolveInstallScript();
334
300
  if (!script) {
335
- throw new Error(`install-fased-signerd.sh not found. Cannot install fased-signerd.\n` + `Expected at one of: ${INSTALL_SCRIPT_RELPATHS.join(", ")}\n` + `Install Go >= 1.21, or provide FASED_WALLET_LOCAL_SIGNER_BIN.`);
336
- }
337
- const runAsUser = resolveLocalSignerRunAsUser(process.env);
338
- const stagingPath = resolveAppOwnedSignerdStagingPath(process.env);
339
- const installDir = path.dirname(runAsUser ? stagingPath : binPath);
340
- const localReleaseBaseUrl = resolveLocalSignerdReleaseBaseUrl(script);
341
- const installCmd = [
342
- `FASED_LOCAL_SIGNER_BIN_DIR="${installDir}"`,
343
- ...localReleaseBaseUrl ? [`FASED_LOCAL_SIGNER_BASE_URL="${localReleaseBaseUrl}"`, "FASED_LOCAL_SIGNER_LATEST_TAG=\"\""] : [],
344
- `bash "${script}"`
345
- ].join(" ");
301
+ return false;
302
+ }
303
+ const installDir = path.dirname(binPath);
346
304
  const verboseInstall = String(process.env.FASED_INSTALL_VERBOSE ?? "").trim() === "1" || String(process.env.FASED_ONBOARD_VERBOSE ?? "").trim() === "1";
347
- try {
348
- execSync(installCmd, { stdio: verboseInstall ? "inherit" : "pipe" });
349
- } catch (err) {
350
- const stderr = err && typeof err === "object" && "stderr" in err ? String(err.stderr ?? "").trim() : "";
351
- const stdout = err && typeof err === "object" && "stdout" in err ? String(err.stdout ?? "").trim() : "";
352
- const detail = (stderr || stdout).split("\n").slice(-12).join("\n");
353
- throw new Error(detail || "fased-signerd install failed", { cause: err });
354
- }
355
- const installedPath = runAsUser ? stagingPath : binPath;
356
- if (!fs.existsSync(installedPath)) {
305
+ runScriptOrThrow({
306
+ script,
307
+ env: {
308
+ ...resolveSignerdAssetInstallEnv(script),
309
+ FASED_LOCAL_SIGNER_BIN_DIR: installDir
310
+ },
311
+ verbose: verboseInstall,
312
+ fallbackError: "fased-signerd asset install failed"
313
+ });
314
+ const installedPath = binPath;
315
+ if (!syncFs.existsSync(installedPath)) {
357
316
  throw new Error(`fased-signerd install script ran but binary not found at ${binPath}.\n` + `Check install script output above for errors.`);
358
317
  }
359
- if (runAsUser && installedPath !== binPath) {
360
- if (runSignerIsolationHelper(runAsUser, [
361
- "install-binary",
362
- installedPath,
363
- binPath
364
- ]) === undefined) {
365
- runCommand({
366
- command: "sudo",
367
- args: [
368
- "bash",
369
- "-lc",
370
- [
371
- "set -euo pipefail",
372
- `install -d -m 755 -o ${JSON.stringify(runAsUser)} -g ${JSON.stringify(runAsUser)} ${JSON.stringify(path.dirname(binPath))}`,
373
- `install -m 755 -o ${JSON.stringify(runAsUser)} -g ${JSON.stringify(runAsUser)} ${JSON.stringify(installedPath)} ${JSON.stringify(binPath)}`
374
- ].join("; ")
375
- ]
376
- });
377
- }
378
- }
379
- if (!fs.existsSync(binPath)) {
318
+ if (!syncFs.existsSync(binPath)) {
380
319
  throw new Error(`fased-signerd install script completed but target binary not found at ${binPath}.\n` + `Check install script output above for errors.`);
381
320
  }
382
321
  return true;
383
322
  }
384
323
  function installSignerdBinary(binPath) {
385
- if (buildSignerdBinaryFromSource(binPath)) {
386
- return;
324
+ const forceSourceBuild = String(process.env.FASED_BUILD_NATIVE_SIGNER_FROM_SOURCE ?? "").trim() === "1";
325
+ if (forceSourceBuild) {
326
+ if (buildSignerdBinaryFromSource(binPath)) {
327
+ return;
328
+ }
329
+ throw new Error("FASED_BUILD_NATIVE_SIGNER_FROM_SOURCE=1 requires a source checkout and Go >= 1.25.7.");
387
330
  }
388
- if (installSignerdBinaryFromExplicitAsset(binPath)) {
389
- return;
331
+ try {
332
+ if (installSignerdBinaryFromAsset(binPath)) {
333
+ return;
334
+ }
335
+ } catch (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 });
390
344
  }
391
345
  throw new Error([
392
- "fased-signerd is required only after you choose the local signer wallet path.",
393
- "Install Go >= 1.21 and rerun wallet setup so Fased can build the signer locally.",
394
- "Alternatively set FASED_WALLET_LOCAL_SIGNER_BIN to an existing signer binary,",
395
- "or set FASED_LOCAL_SIGNER_VERSION/FASED_LOCAL_SIGNER_BASE_URL to an explicit signer asset.",
396
- "Normal dashboard, Gateway, and Fased Network startup do not require fased-signerd."
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."
397
350
  ].join("\n"));
398
351
  }
399
- function hasScopedWalletEnvValue(env, prefix) {
400
- return Object.entries(env).some(([key, value]) => key.startsWith(`${prefix}__`) && typeof value === "string" && value.trim().length > 0);
401
- }
402
- function hasLocalSignerMaterialEnv(env) {
403
- 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()) {
404
- return true;
405
- }
406
- return Object.entries(env).some(([key, rawValue]) => {
407
- if (typeof rawValue !== "string" || rawValue.trim().length === 0) {
408
- return false;
409
- }
410
- return key === "FASED_WALLET_SOLANA_KEYSTORE_PATH" || key.startsWith("FASED_WALLET_SOLANA_KEYSTORE_PATH__");
411
- });
412
- }
413
- function normalizeWalletIdForEnvSuffix(walletId) {
414
- const normalized = walletId.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
415
- return normalized || undefined;
416
- }
417
- function normalizeWalletIdForFilename(walletId) {
418
- const normalized = walletId.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
419
- return normalized || undefined;
420
- }
421
- function inferScopedSignerKeystoreEnv(materialDir, env) {
422
- const inferred = {};
423
- try {
424
- const registry = readWalletProviderRegistry(env);
425
- for (const wallet of registry.wallets) {
426
- if (wallet.providerId !== "local-socket-signer") {
427
- continue;
428
- }
429
- const envSuffix = normalizeWalletIdForEnvSuffix(wallet.id)?.toUpperCase();
430
- const fileSuffix = normalizeWalletIdForFilename(wallet.id);
431
- if (!envSuffix || !fileSuffix) {
432
- continue;
433
- }
434
- const solanaKey = `FASED_WALLET_SOLANA_KEYSTORE_PATH__${envSuffix}`;
435
- if (!String(env[solanaKey] ?? "").trim()) {
436
- const candidate = path.join(materialDir, `keystore-solana-${fileSuffix}.v1.enc`);
437
- if (fs.existsSync(candidate)) {
438
- inferred[solanaKey] = candidate;
439
- }
440
- }
441
- }
442
- } catch {}
443
- return inferred;
444
- }
445
352
  function shouldSyncLocalSocketSignerFromConfig(params) {
446
353
  const cfg = params?.config ?? loadConfig();
447
354
  if (cfg.wallet?.runtime?.enabled === false) {
@@ -454,9 +361,6 @@ function shouldSyncLocalSocketSignerFromConfig(params) {
454
361
  if (resolveWalletProviderId(cfg, mergedEnv) === "local-socket-signer") {
455
362
  return true;
456
363
  }
457
- if (hasLocalSignerMaterialEnv(mergedEnv)) {
458
- return true;
459
- }
460
364
  try {
461
365
  const registry = readWalletProviderRegistry(mergedEnv);
462
366
  return registry.wallets.some((wallet) => wallet.providerId === "local-socket-signer");
@@ -464,142 +368,100 @@ function shouldSyncLocalSocketSignerFromConfig(params) {
464
368
  return false;
465
369
  }
466
370
  }
467
- function resolveLocalSignerPassphraseSource(materialDir, env) {
468
- const configuredFile = String(env.FASED_WALLET_PASSPHRASE_FILE ?? "").trim();
469
- const candidates = [configuredFile ? path.resolve(configuredFile) : "", path.join(materialDir, "passphrase")].filter(Boolean);
470
- for (const candidate of candidates) {
471
- try {
472
- const value = fs.readFileSync(candidate, "utf8").trim();
473
- if (value) {
474
- return {
475
- kind: "file",
476
- path: candidate,
477
- value
478
- };
479
- }
480
- } catch {}
481
- }
482
- const explicit = String(env.FASED_WALLET_PASSPHRASE ?? "").trim();
483
- if (explicit) {
484
- return {
485
- kind: "value",
486
- value: explicit
487
- };
488
- }
489
- return { kind: "none" };
490
- }
491
371
  function resolveSignerChildEnv(materialDir, env = process.env, cfg = loadConfig()) {
372
+ void materialDir;
492
373
  const configEnv = cfg.env?.vars ?? {};
493
374
  const mergedEnv = {
494
375
  ...env,
495
376
  ...configEnv
496
377
  };
497
- const inferredScopedEnv = inferScopedSignerKeystoreEnv(materialDir, mergedEnv);
498
- const signerEnv = {
499
- ...mergedEnv,
500
- ...inferredScopedEnv
501
- };
502
- const hasScopedSolKeystore = hasScopedWalletEnvValue(signerEnv, "FASED_WALLET_SOLANA_KEYSTORE_PATH");
503
- const childBaseEnv = { ...signerEnv };
504
- if (hasScopedSolKeystore) {
505
- delete childBaseEnv.FASED_WALLET_SOLANA_KEYSTORE_PATH;
506
- }
507
- const explicitSolPath = String(signerEnv.FASED_WALLET_SOLANA_KEYSTORE_PATH ?? "").trim();
508
- const passphraseSource = resolveLocalSignerPassphraseSource(materialDir, signerEnv);
509
- const isolatedPassphraseFile = resolveLocalSignerRunAsUser(signerEnv) ? path.resolve(String(signerEnv.FASED_WALLET_PASSPHRASE_FILE ?? "").trim() || path.join(materialDir, "passphrase")) : "";
510
- const solPath = hasScopedSolKeystore ? "" : explicitSolPath || path.join(materialDir, "keystore-solana.v1.enc");
511
- const chains = resolveSignerChainsEnvValue(cfg, signerEnv);
512
- const chainSet = new Set(chains.split(",").map((entry) => entry.trim().toLowerCase()).filter(Boolean));
513
- return {
514
- ...childBaseEnv,
515
- FASED_WALLET_CHAINS: chains,
516
- ...chainSet.has("solana") && solPath ? { FASED_WALLET_SOLANA_KEYSTORE_PATH: solPath } : {},
517
- ...passphraseSource.kind === "file" ? {
518
- FASED_WALLET_PASSPHRASE_FILE: passphraseSource.path,
519
- FASED_WALLET_PASSPHRASE: undefined
520
- } : isolatedPassphraseFile ? {
521
- FASED_WALLET_PASSPHRASE_FILE: isolatedPassphraseFile,
522
- FASED_WALLET_PASSPHRASE: undefined
523
- } : passphraseSource.kind === "value" ? { FASED_WALLET_PASSPHRASE: passphraseSource.value } : {}
524
- };
525
- }
526
- const LOCAL_SIGNER_EXPORTABLE_PREFIXES = [
527
- "FASED_WALLET_SOLANA_KEYSTORE_PATH",
528
- "FASED_WALLET_SOLANA_RPC_URL",
529
- "FASED_WALLET_RPC_URL",
530
- "FASED_WALLET_EMBEDDED_KEYSTORE_RPC_URL",
531
- "FASED_WALLET_CUSTODY_MODE",
532
- "FASED_WALLET_CUSTODY_WALLETS",
533
- "FASED_WALLET_CUSTODY_PASSKEY_CEREMONY",
534
- "FASED_WALLET_CUSTODY_EPHEMERAL_RECONSTRUCTION",
535
- "FASED_WALLET_CUSTODY_PHASE2_COMPLETE"
536
- ];
537
- function collectLocalSignerExportEnv(childEnv) {
538
- const out = {};
539
- for (const [key, rawValue] of Object.entries(childEnv)) {
540
- if (!LOCAL_SIGNER_EXPORTABLE_PREFIXES.some((prefix) => key === prefix || key.startsWith(`${prefix}__`))) {
541
- 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;
542
399
  }
400
+ }
401
+ for (const [key, rawValue] of Object.entries(mergedEnv)) {
543
402
  const value = String(rawValue ?? "").trim();
544
- if (!value) {
545
- 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;
546
406
  }
547
- out[key] = value;
548
407
  }
549
- return out;
550
- }
551
- function walletPolicyEnvSuffix(walletId) {
552
- return walletId.trim().toUpperCase().replace(/[^A-Z0-9]+/g, "_").replace(/^_+|_+$/g, "");
553
- }
554
- function addLocalSignerPolicyEnv(out, params) {
555
- const key = (name) => `${name}${params.prefix}`;
556
- out[key("FASED_WALLET_LOCAL_SIGNER_ROLE")] = params.role;
557
- out[key("FASED_WALLET_LOCAL_SIGNER_DIRECT_SIGNING")] = params.policy.directSigning ? "1" : "0";
558
- out[key("FASED_WALLET_LOCAL_SIGNER_CAPS_ENABLED")] = params.policy.capsEnabled ? "1" : "0";
559
- out[key("FASED_WALLET_LOCAL_SIGNER_SOLANA_MAX_PER_TX")] = params.policy.solana.caps.maxPerTx.toString();
560
- out[key("FASED_WALLET_LOCAL_SIGNER_SOLANA_MAX_DAILY")] = params.policy.solana.caps.maxDaily.toString();
561
- if (params.policy.solana.allowPrograms.length > 0) {
562
- 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;
563
412
  }
413
+ childEnv.FASED_WALLET_CHAINS = resolveSignerChainsEnvValue(cfg, mergedEnv);
414
+ return childEnv;
564
415
  }
565
- function collectLocalSignerPolicyExportEnv(cfg, env) {
566
- const out = {};
567
- const registry = readWalletProviderRegistry(env);
568
- const localSignerWallets = registry.wallets.filter((wallet) => wallet.providerId === "local-socket-signer" && wallet.id.trim());
569
- if (localSignerWallets.length === 0) {
570
- return out;
571
- }
572
- const baseRuntime = resolveWalletRuntimeConfig(cfg, env);
573
- for (const wallet of localSignerWallets) {
574
- const role = resolveWalletRoleForId({
575
- walletId: wallet.id,
576
- cfg,
577
- env
578
- });
579
- const runtime = applyWalletPolicyConfig({
580
- config: baseRuntime,
581
- cfg,
582
- env,
583
- walletId: wallet.id
584
- });
585
- const suffix = walletPolicyEnvSuffix(wallet.id);
586
- if (!suffix) {
587
- 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");
588
428
  }
589
- addLocalSignerPolicyEnv(out, {
590
- prefix: `__${suffix}`,
591
- role,
592
- 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();
593
444
  });
594
- if (registry.defaultWalletId === wallet.id || localSignerWallets.length === 1) {
595
- addLocalSignerPolicyEnv(out, {
596
- prefix: "",
597
- role,
598
- policy: runtime.policy
599
- });
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");
600
448
  }
449
+ return {
450
+ rpId: explicitRpId,
451
+ origins: uniqueOrigins.join(",")
452
+ };
453
+ }
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");
601
463
  }
602
- return out;
464
+ return `"${value.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"").replaceAll("$", "\\$").replaceAll("`", "\\`")}"`;
603
465
  }
604
466
  function renderLocalSignerEnvFile(params) {
605
467
  const cfg = params?.config ?? loadConfig();
@@ -610,22 +472,22 @@ function renderLocalSignerEnvFile(params) {
610
472
  const walletRoot = ensureWalletStateDir(mergedEnv).rootDir;
611
473
  const materialDir = resolveLocalSignerMaterialRootDir(mergedEnv);
612
474
  const socketPath = resolveLocalSignerSocketPath(mergedEnv);
613
- const backendSocketPath = resolveLocalSignerBackendSocketPath(mergedEnv);
475
+ const controlSocketPath = resolveLocalSignerControlSocketPath(mergedEnv);
476
+ const stateDbPath = resolveLocalSignerStateDbPath(mergedEnv);
477
+ const masterKeyPath = resolveLocalSignerMasterKeyPath(mergedEnv);
614
478
  const signerBinPath = resolveSignerdBinaryPath(mergedEnv);
615
479
  const childEnv = resolveSignerChildEnv(materialDir, mergedEnv, cfg);
616
- const exportLines = Object.entries({
617
- ...collectLocalSignerExportEnv(childEnv),
618
- ...collectLocalSignerPolicyExportEnv(cfg, mergedEnv)
619
- }).toSorted(([a], [b]) => a.localeCompare(b)).map(([key, value]) => `export ${key}="${value.replaceAll("\"", "\\\"")}"`);
480
+ const webAuthn = resolveLocalSignerWebAuthnConfig(cfg, mergedEnv);
620
481
  const lines = [
621
- `export FASED_WALLET_LOCAL_SIGNER_SOCKET="${socketPath}"`,
622
- ...backendSocketPath !== socketPath ? [`export FASED_WALLET_LOCAL_SIGNER_BACKEND_SOCKET="${backendSocketPath}"`] : [],
623
- ...materialDir !== walletRoot ? [`export FASED_WALLET_SIGNER_STATE_DIR="${materialDir}"`] : [],
624
- `export FASED_WALLET_CHAINS="${String(childEnv.FASED_WALLET_CHAINS ?? "solana").trim() || "solana"}"`,
625
- ...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()}"`] : [],
626
- ...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)}`] : [],
627
489
  "",
628
- `"${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"`
629
491
  ];
630
492
  return `${lines.join("\n")}\n`;
631
493
  }
@@ -636,11 +498,11 @@ function writeLocalSignerEnvFile(params) {
636
498
  ...cfg.env?.vars
637
499
  };
638
500
  const signerEnvPath = path.resolve(ensureWalletStateDir(mergedEnv).rootDir, "signer.env");
639
- fs.mkdirSync(path.dirname(signerEnvPath), {
501
+ syncFs.mkdirSync(path.dirname(signerEnvPath), {
640
502
  recursive: true,
641
503
  mode: 448
642
504
  });
643
- fs.writeFileSync(signerEnvPath, renderLocalSignerEnvFile({
505
+ syncFs.writeFileSync(signerEnvPath, renderLocalSignerEnvFile({
644
506
  config: cfg,
645
507
  env: mergedEnv
646
508
  }), {
@@ -648,250 +510,28 @@ function writeLocalSignerEnvFile(params) {
648
510
  mode: 384
649
511
  });
650
512
  try {
651
- fs.chmodSync(signerEnvPath, 384);
513
+ syncFs.chmodSync(signerEnvPath, 384);
652
514
  } catch {}
653
515
  return signerEnvPath;
654
516
  }
655
- function resolveCurrentUnixUser() {
656
- const explicit = String(process.env.USER ?? process.env.LOGNAME ?? "").trim();
657
- if (explicit) {
658
- return explicit;
659
- }
660
- try {
661
- return execSync("id -un", { encoding: "utf8" }).trim() || "app";
662
- } catch {
663
- return "app";
664
- }
665
- }
666
- function runSignerIsolationHelper(runAsUser, args, opts) {
667
- const helperArgs = signerMaintenanceHelperArgs(runAsUser, args);
668
- if (!helperArgs) {
669
- return undefined;
670
- }
671
- try {
672
- return runCommand({
673
- command: "sudo",
674
- args: [
675
- "-n",
676
- "-E",
677
- ...helperArgs
678
- ],
679
- capture: opts?.capture
680
- });
681
- } catch (err) {
682
- if (err instanceof Error) {
683
- throw err;
684
- }
685
- throw new Error(String(err), { cause: err });
686
- }
687
- }
688
- function startSignerIsolationHelperBackground(runAsUser, args, logPath, env = process.env) {
689
- const helperArgs = signerMaintenanceHelperArgs(runAsUser, args);
690
- if (!helperArgs) {
691
- return false;
692
- }
693
- const out = fs.openSync(logPath, "a", 384);
694
- const child = spawn("sudo", [
695
- "-n",
696
- "-E",
697
- ...helperArgs
698
- ], {
699
- detached: true,
700
- stdio: [
701
- "ignore",
702
- out,
703
- out
704
- ],
705
- env: {
706
- ...process.env,
707
- ...env
708
- }
709
- });
710
- child.unref();
711
- return true;
712
- }
713
- function signerMaintenanceHelperArgs(runAsUser, args) {
714
- if (fs.existsSync(SIGNER_MAINTENANCE_HELPER)) {
715
- return [SIGNER_MAINTENANCE_HELPER, ...args];
716
- }
717
- if (fs.existsSync(SIGNER_ISOLATION_HELPER)) {
718
- return [
719
- SIGNER_ISOLATION_HELPER,
720
- resolveCurrentUnixUser(),
721
- runAsUser,
722
- ...args
723
- ];
724
- }
725
- return undefined;
726
- }
727
- function ensureIsolatedSignerPaths(materialDir, backendSocketPath, appSocketPath, runAsUser) {
728
- const helperResult = runSignerIsolationHelper(runAsUser, [
729
- "prepare",
730
- materialDir,
731
- backendSocketPath,
732
- appSocketPath,
733
- resolveSignerdBinaryPath(process.env)
734
- ]);
735
- if (helperResult !== undefined) {
736
- return;
737
- }
738
- const appUser = resolveCurrentUnixUser();
739
- const signerWalletDir = path.dirname(materialDir);
740
- const signerRootDir = path.dirname(signerWalletDir);
741
- 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))}`];
742
- if (appSocketPath !== backendSocketPath) {
743
- 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))}`);
744
- }
745
- runCommand({
746
- command: "sudo",
747
- args: [
748
- "bash",
749
- "-lc",
750
- ["set -euo pipefail", ...commands].join("; ")
751
- ]
752
- });
753
- }
754
- function ensureLocalSignerPassphrase() {
755
- const materialDir = resolveLocalSignerMaterialRootDir(process.env);
756
- const runAsUser = resolveLocalSignerRunAsUser(process.env);
757
- const appSocketPath = resolveLocalSignerSocketPath(process.env);
758
- const backendSocketPath = resolveLocalSignerBackendSocketPath(process.env);
759
- if (runAsUser) {
760
- ensureIsolatedSignerPaths(materialDir, backendSocketPath, appSocketPath, runAsUser);
761
- }
762
- const existing = resolveLocalSignerPassphraseSource(materialDir, process.env);
763
- if (existing.kind === "file") {
764
- process.env.FASED_WALLET_PASSPHRASE_FILE = existing.path;
765
- delete process.env.FASED_WALLET_PASSPHRASE;
766
- return existing.value;
767
- }
768
- if (runAsUser) {
769
- const generated = existing.kind === "value" ? existing.value : randomBytes(24).toString("base64url");
770
- const stagingDir = ensureWalletStateDir(process.env).rootDir;
771
- const stagingPath = path.join(stagingDir, `.local-signer-passphrase-${process.pid}-${Date.now()}`);
772
- fs.mkdirSync(path.dirname(stagingPath), {
773
- recursive: true,
774
- mode: 448
775
- });
776
- fs.writeFileSync(stagingPath, `${generated}\n`, {
777
- encoding: "utf8",
778
- mode: 384
779
- });
780
- try {
781
- fs.chmodSync(stagingPath, 384);
782
- } catch {}
783
- let passphrasePath = path.join(materialDir, "passphrase");
784
- try {
785
- const helperResult = runSignerIsolationHelper(runAsUser, [
786
- "install-passphrase",
787
- stagingPath,
788
- materialDir
789
- ], { capture: true });
790
- if (helperResult === undefined) {
791
- runCommand({
792
- command: "sudo",
793
- args: [
794
- "bash",
795
- "-lc",
796
- [
797
- "set -euo pipefail",
798
- `test ! -e ${JSON.stringify(passphrasePath)}`,
799
- `install -d -m 700 -o ${JSON.stringify(runAsUser)} -g ${JSON.stringify(runAsUser)} ${JSON.stringify(materialDir)}`,
800
- `install -m 600 -o ${JSON.stringify(runAsUser)} -g ${JSON.stringify(runAsUser)} ${JSON.stringify(stagingPath)} ${JSON.stringify(passphrasePath)}`,
801
- `rm -f ${JSON.stringify(stagingPath)}`
802
- ].join("; ")
803
- ]
804
- });
805
- } else {
806
- passphrasePath = helperResult.trim() || passphrasePath;
807
- }
808
- process.env.FASED_WALLET_PASSPHRASE = generated;
809
- } catch (err) {
810
- try {
811
- fs.rmSync(stagingPath, { force: true });
812
- } catch {}
813
- const detail = err instanceof Error ? err.message : String(err);
814
- if (!detail.includes("passphrase already exists")) {
815
- throw err;
816
- }
817
- delete process.env.FASED_WALLET_PASSPHRASE;
818
- }
819
- process.env.FASED_WALLET_PASSPHRASE_FILE = passphrasePath;
820
- return process.env.FASED_WALLET_PASSPHRASE ?? "";
821
- }
822
- if (existing.kind === "value") {
823
- process.env.FASED_WALLET_PASSPHRASE = existing.value;
824
- delete process.env.FASED_WALLET_PASSPHRASE_FILE;
825
- return existing.value;
826
- }
827
- const generated = randomBytes(24).toString("base64url");
828
- const passphraseFile = path.join(materialDir, "passphrase");
829
- fs.mkdirSync(path.dirname(passphraseFile), {
830
- recursive: true,
831
- mode: 448
832
- });
833
- fs.writeFileSync(passphraseFile, `${generated}\n`, {
834
- encoding: "utf8",
835
- mode: 384
836
- });
837
- try {
838
- fs.chmodSync(passphraseFile, 384);
839
- } catch {}
840
- process.env.FASED_WALLET_PASSPHRASE_FILE = passphraseFile;
841
- delete process.env.FASED_WALLET_PASSPHRASE;
842
- return generated;
843
- }
844
517
  function startSignerdBackground(binPath, socketPath, materialDir, env = process.env) {
845
- const runAsUser = resolveLocalSignerRunAsUser(env);
846
518
  const childEnv = resolveSignerChildEnv(materialDir, env);
847
- const appSocketPath = resolveLocalSignerSocketPath(env);
848
- const backendSocketPath = resolveLocalSignerBackendSocketPath(env);
519
+ const controlSocketPath = resolveLocalSignerControlSocketPath(env);
520
+ const stateDbPath = resolveLocalSignerStateDbPath(env);
521
+ const masterKeyPath = resolveLocalSignerMasterKeyPath(env);
849
522
  const { pidPath, auditPath } = resolveLocalSignerSidecarPaths(socketPath);
850
- const logPath = resolveSignerdLogPath({
851
- appSocketPath,
852
- materialDir,
853
- runAsUser
854
- });
855
- if (runAsUser && process.getuid?.() === 0) {
856
- throw new Error("signer isolation should not be started directly as root");
857
- }
858
- if (runAsUser) {
859
- ensureIsolatedSignerPaths(materialDir, backendSocketPath, appSocketPath, runAsUser);
860
- const envArgs = Object.entries(childEnv).filter(([, value]) => typeof value === "string" && value.trim()).map(([key, value]) => `${key}=${value}`);
861
- try {
862
- if (!startSignerIsolationHelperBackground(runAsUser, [
863
- "start-signerd",
864
- binPath,
865
- socketPath,
866
- pidPath,
867
- auditPath
868
- ], logPath, childEnv)) {
869
- execSync([
870
- "sudo",
871
- "-u",
872
- runAsUser,
873
- "-H",
874
- "env",
875
- ...envArgs.map((value) => JSON.stringify(value)),
876
- "bash",
877
- "-lc",
878
- JSON.stringify([
879
- "set -euo pipefail",
880
- "umask 077",
881
- `${JSON.stringify(binPath)} -socket ${JSON.stringify(socketPath)} -pid-file ${JSON.stringify(pidPath)} -audit-log ${JSON.stringify(auditPath)} >> ${JSON.stringify(logPath)} 2>&1 &`
882
- ].join("; "))
883
- ].join(" "), { stdio: "ignore" });
884
- }
885
- return;
886
- } catch (err) {
887
- throw new Error(`failed to launch isolated signer as ${runAsUser}: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
888
- }
889
- }
890
- const out = fs.openSync(logPath, "a", 384);
891
- const err = fs.openSync(logPath, "a", 384);
523
+ const logPath = resolveSignerdLogPath({ materialDir });
524
+ const out = syncFs.openSync(logPath, "a", 384);
525
+ const err = syncFs.openSync(logPath, "a", 384);
892
526
  const child = spawn(binPath, [
893
527
  "-socket",
894
528
  socketPath,
529
+ "-control-socket",
530
+ controlSocketPath,
531
+ "-state-db",
532
+ stateDbPath,
533
+ "-master-key",
534
+ masterKeyPath,
895
535
  "-pid-file",
896
536
  pidPath,
897
537
  "-audit-log",
@@ -907,64 +547,13 @@ function startSignerdBackground(binPath, socketPath, materialDir, env = process.
907
547
  });
908
548
  child.unref();
909
549
  }
910
- function stopProcessBySocket(socketPath, runAsUser) {
550
+ function stopProcessBySocket(socketPath) {
911
551
  const { pidPath } = resolveLocalSignerSidecarPaths(socketPath);
912
552
  const legacyPidPath = `${socketPath}.pid`;
913
553
  const pidPaths = Array.from(new Set([pidPath, legacyPidPath]));
914
- if (runAsUser) {
915
- let stoppedWithHelper = false;
916
- for (const candidatePidPath of pidPaths) {
917
- const helperResult = runSignerIsolationHelper(runAsUser, [
918
- "stop",
919
- socketPath,
920
- candidatePidPath
921
- ]);
922
- if (helperResult !== undefined) {
923
- stoppedWithHelper = true;
924
- }
925
- }
926
- if (stoppedWithHelper) {
927
- return;
928
- }
929
- for (const candidatePidPath of pidPaths) {
930
- try {
931
- const rawPid = runCommand({
932
- command: "sudo",
933
- args: [
934
- "bash",
935
- "-lc",
936
- `if [ -f ${JSON.stringify(candidatePidPath)} ]; then cat ${JSON.stringify(candidatePidPath)}; fi`
937
- ],
938
- capture: true
939
- }).trim();
940
- const pid = Number.parseInt(rawPid, 10);
941
- if (Number.isFinite(pid) && pid > 0) {
942
- runCommand({
943
- command: "sudo",
944
- args: [
945
- "bash",
946
- "-lc",
947
- `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`
948
- ]
949
- });
950
- }
951
- } catch {}
952
- try {
953
- runCommand({
954
- command: "sudo",
955
- args: [
956
- "bash",
957
- "-lc",
958
- `rm -f ${JSON.stringify(socketPath)} ${JSON.stringify(candidatePidPath)}`
959
- ]
960
- });
961
- } catch {}
962
- }
963
- return;
964
- }
965
554
  for (const candidatePidPath of pidPaths) {
966
555
  try {
967
- const pid = Number.parseInt(fs.readFileSync(candidatePidPath, "utf8").trim(), 10);
556
+ const pid = Number.parseInt(syncFs.readFileSync(candidatePidPath, "utf8").trim(), 10);
968
557
  if (Number.isFinite(pid) && pid > 0) {
969
558
  try {
970
559
  const command = readProcessCommand(pid);
@@ -975,16 +564,16 @@ function stopProcessBySocket(socketPath, runAsUser) {
975
564
  }
976
565
  } catch {}
977
566
  try {
978
- fs.unlinkSync(candidatePidPath);
567
+ syncFs.unlinkSync(candidatePidPath);
979
568
  } catch {}
980
569
  }
981
570
  try {
982
- fs.unlinkSync(socketPath);
571
+ syncFs.unlinkSync(socketPath);
983
572
  } catch {}
984
573
  }
985
574
  function readProcessCommand(pid) {
986
575
  try {
987
- const cmdline = fs.readFileSync(`/proc/${pid}/cmdline`, "utf8").replaceAll("\0", " ").trim();
576
+ const cmdline = syncFs.readFileSync(`/proc/${pid}/cmdline`, "utf8").replaceAll("\0", " ").trim();
988
577
  if (cmdline) {
989
578
  return cmdline;
990
579
  }
@@ -997,116 +586,32 @@ function readProcessCommand(pid) {
997
586
  }
998
587
  function isLocalSignerProcessCommand(command) {
999
588
  const lower = command.toLowerCase();
1000
- return lower.includes("fased-signerd") || lower.includes("local-socket-signer-broker");
1001
- }
1002
- function startSignerBrokerBackground(appSocketPath, backendSocketPath, materialDir, env = process.env) {
1003
- const brokerCli = resolveBrokerCliPath();
1004
- if (!brokerCli) {
1005
- const tried = BROKER_CLI_RELPATHS.map((rel) => fileURLToPath(new URL(rel, import.meta.url)));
1006
- throw new Error(`wallet signer broker CLI not built. Tried: ${tried.join(", ")}`);
1007
- }
1008
- const logPath = path.join(path.dirname(appSocketPath), "local-signer-broker.log");
1009
- const runAsUser = resolveLocalSignerRunAsUser(env);
1010
- const { pidPath, auditPath } = resolveLocalSignerSidecarPaths(appSocketPath);
1011
- if (runAsUser) {
1012
- ensureIsolatedSignerPaths(materialDir, backendSocketPath, appSocketPath, runAsUser);
1013
- const envArgs = Object.entries({
1014
- ...env,
1015
- FASED_WALLET_LOCAL_SIGNER_SOCKET: appSocketPath,
1016
- FASED_WALLET_LOCAL_SIGNER_BACKEND_SOCKET: backendSocketPath
1017
- }).filter(([, value]) => typeof value === "string" && value.trim()).map(([key, value]) => `${key}=${value}`);
1018
- try {
1019
- if (!startSignerIsolationHelperBackground(runAsUser, [
1020
- "start-broker",
1021
- process.execPath,
1022
- brokerCli,
1023
- appSocketPath,
1024
- backendSocketPath,
1025
- pidPath,
1026
- auditPath
1027
- ], logPath, {
1028
- ...env,
1029
- FASED_WALLET_LOCAL_SIGNER_SOCKET: appSocketPath,
1030
- FASED_WALLET_LOCAL_SIGNER_BACKEND_SOCKET: backendSocketPath
1031
- })) {
1032
- execSync([
1033
- "sudo",
1034
- "-u",
1035
- runAsUser,
1036
- "-H",
1037
- "env",
1038
- ...envArgs.map((value) => JSON.stringify(value)),
1039
- "bash",
1040
- "-lc",
1041
- JSON.stringify([
1042
- "set -euo pipefail",
1043
- "umask 007",
1044
- `${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 &`
1045
- ].join("; "))
1046
- ].join(" "), { stdio: "ignore" });
1047
- }
1048
- return;
1049
- } catch (err) {
1050
- throw new Error(`failed to launch isolated signer broker as ${runAsUser}: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
1051
- }
1052
- }
1053
- const out = fs.openSync(logPath, "a", 384);
1054
- const err = fs.openSync(logPath, "a", 384);
1055
- const child = spawn(process.execPath, [
1056
- brokerCli,
1057
- "wallet",
1058
- "signer",
1059
- "broker",
1060
- "--socket",
1061
- appSocketPath,
1062
- "--backend-socket",
1063
- backendSocketPath,
1064
- "--pid-file",
1065
- pidPath,
1066
- "--audit-log",
1067
- auditPath
1068
- ], {
1069
- detached: true,
1070
- stdio: [
1071
- "ignore",
1072
- out,
1073
- err
1074
- ],
1075
- env: {
1076
- ...env,
1077
- FASED_WALLET_LOCAL_SIGNER_SOCKET: appSocketPath,
1078
- FASED_WALLET_LOCAL_SIGNER_BACKEND_SOCKET: backendSocketPath
1079
- }
1080
- });
1081
- child.unref();
589
+ return lower.includes("fased-signerd");
1082
590
  }
1083
591
  async function restartLocalSocketSigner(walletDir, env = process.env) {
1084
592
  void walletDir;
1085
593
  const socketPath = resolveLocalSignerSocketPath(env);
1086
- const backendSocketPath = resolveLocalSignerBackendSocketPath(env);
1087
594
  const binPath = resolveSignerdBinaryPath(env);
1088
595
  const materialDir = resolveLocalSignerMaterialRootDir(env);
1089
- const runAsUser = resolveLocalSignerRunAsUser(env);
1090
- if (!fs.existsSync(binPath)) {
1091
- return;
1092
- }
1093
- stopProcessBySocket(socketPath, runAsUser);
1094
- if (backendSocketPath !== socketPath) {
1095
- stopProcessBySocket(backendSocketPath, runAsUser);
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`);
1096
602
  }
1097
- startSignerdBackground(binPath, backendSocketPath, materialDir, env);
1098
- if (backendSocketPath !== socketPath) {
1099
- startSignerBrokerBackground(socketPath, backendSocketPath, materialDir, env);
603
+ if (!syncFs.existsSync(binPath)) {
604
+ return;
1100
605
  }
1101
- const readyTimeoutMs = runAsUser ? 2e4 : 8e3;
606
+ stopProcessBySocket(socketPath);
607
+ startSignerdBackground(binPath, socketPath, materialDir, env);
608
+ const readyTimeoutMs = 8e3;
1102
609
  if (!await waitForSignerdHealthy(socketPath, readyTimeoutMs)) {
1103
610
  throw buildSignerdReadinessError({
1104
- appSocketPath: socketPath,
1105
- backendSocketPath,
611
+ socketPath,
1106
612
  binPath,
1107
613
  materialDir,
1108
- readyTimeoutMs,
1109
- runAsUser
614
+ readyTimeoutMs
1110
615
  });
1111
616
  }
1112
617
  }
@@ -1130,7 +635,8 @@ async function syncLocalSocketSignerFromConfig(params) {
1130
635
  env: mergedEnv
1131
636
  });
1132
637
  const binPath = resolveSignerdBinaryPath(mergedEnv);
1133
- if (!fs.existsSync(binPath)) {
638
+ const externallyManaged = isLocalSignerExternallyManaged(mergedEnv);
639
+ if (!externallyManaged && !syncFs.existsSync(binPath)) {
1134
640
  installSignerdBinary(binPath);
1135
641
  }
1136
642
  if (params?.restart === false) {
@@ -1143,45 +649,10 @@ async function syncLocalSocketSignerFromConfig(params) {
1143
649
  await restartLocalSocketSigner(undefined, mergedEnv);
1144
650
  return {
1145
651
  performed: true,
1146
- restarted: true,
652
+ restarted: !externallyManaged,
1147
653
  signerEnvPath
1148
654
  };
1149
655
  }
1150
- function migrateLocalSignerKeystoreToMaterialDir(params) {
1151
- const runAsUser = resolveLocalSignerRunAsUser(process.env);
1152
- const materialDir = resolveLocalSignerMaterialRootDir(process.env);
1153
- const sourcePath = path.resolve(params.keystorePath);
1154
- if (!runAsUser) {
1155
- return sourcePath;
1156
- }
1157
- const targetPath = path.join(materialDir, path.basename(sourcePath));
1158
- if (targetPath === sourcePath) {
1159
- return targetPath;
1160
- }
1161
- ensureIsolatedSignerPaths(materialDir, resolveLocalSignerBackendSocketPath(process.env), resolveLocalSignerSocketPath(process.env), runAsUser);
1162
- if (fs.existsSync(targetPath) && !params.force) {
1163
- throw new Error(`Keystore already exists: ${targetPath}`);
1164
- }
1165
- if (runSignerIsolationHelper(runAsUser, [
1166
- "copy-keystore",
1167
- sourcePath,
1168
- targetPath
1169
- ]) === undefined) {
1170
- runCommand({
1171
- command: "sudo",
1172
- args: [
1173
- "bash",
1174
- "-lc",
1175
- [
1176
- "set -euo pipefail",
1177
- `install -D -m 600 -o ${JSON.stringify(runAsUser)} -g ${JSON.stringify(runAsUser)} ${JSON.stringify(sourcePath)} ${JSON.stringify(targetPath)}`,
1178
- `rm -f ${JSON.stringify(sourcePath)}`
1179
- ].join("; ")
1180
- ]
1181
- });
1182
- }
1183
- return targetPath;
1184
- }
1185
656
  const DEFAULT_WALLET_CHAINS = ["solana"];
1186
657
  const DEFAULT_WALLET_RUNTIME_HOST = "127.0.0.1";
1187
658
  const DEFAULT_WALLET_RUNTIME_PORT = 19444;
@@ -1208,6 +679,7 @@ function normalizeProviderId(value) {
1208
679
  case "local-socket-signer":
1209
680
  case "alchemy":
1210
681
  case "turnkey":
682
+ case "wallet-standard":
1211
683
  case "privy": return value;
1212
684
  default: return null;
1213
685
  }
@@ -1218,6 +690,7 @@ function providerLabel(providerId) {
1218
690
  case "local-socket-signer": return "Local native signer";
1219
691
  case "alchemy": return "Alchemy";
1220
692
  case "turnkey": return "Turnkey";
693
+ case "wallet-standard": return "Hardware / Wallet Standard";
1221
694
  case "privy": return "Privy";
1222
695
  default: return providerId;
1223
696
  }
@@ -1245,27 +718,15 @@ function applyWalletConfig(base, runtimeConfig) {
1245
718
  };
1246
719
  }
1247
720
  function hasConfiguredWalletMaterial(params) {
1248
- if (params.registry.wallets.length > 0) {
1249
- return true;
1250
- }
1251
- const mergedEnv = {
1252
- ...params.env,
1253
- ...params.config.env?.vars
1254
- };
1255
- if (String(mergedEnv.FASED_WALLET_SOLANA_KEYSTORE_PATH ?? "").trim() || hasScopedWalletEnvValue(mergedEnv, "FASED_WALLET_SOLANA_KEYSTORE_PATH")) {
1256
- return true;
1257
- }
1258
- try {
1259
- const walletDir = ensureWalletStateDir(mergedEnv).rootDir;
1260
- return fs.readdirSync(walletDir).some((entry) => /^keystore-solana(?:-[A-Za-z0-9_-]+)?\.v1\.enc$/.test(entry) || /^keystore-evm(?:-[A-Za-z0-9_-]+)?\.v1\.enc$/.test(entry));
1261
- } catch {
1262
- return false;
1263
- }
721
+ return params.registry.wallets.some((wallet) => wallet.providerId === "local-socket-signer");
1264
722
  }
1265
723
  const LOCAL_SIGNER_ENV_KEYS = [
1266
724
  "FASED_WALLET_LOCAL_SIGNER_SOCKET",
1267
725
  "FASED_WALLET_LOCAL_SIGNER_BACKEND_SOCKET",
726
+ "FASED_WALLET_LOCAL_SIGNER_CONTROL_SOCKET",
1268
727
  "FASED_WALLET_SIGNER_STATE_DIR",
728
+ "FASED_WALLET_LOCAL_SIGNER_STATE_DB",
729
+ "FASED_WALLET_LOCAL_SIGNER_MASTER_KEY",
1269
730
  "FASED_WALLET_LOCAL_SIGNER_RUN_AS_USER",
1270
731
  "FASED_WALLET_LOCAL_SIGNER_BIN",
1271
732
  "FASED_WALLET_PASSPHRASE_FILE"
@@ -1288,16 +749,21 @@ function clearLocalSignerEnv(base) {
1288
749
  }
1289
750
  async function configureWalletForOnboarding(params) {
1290
751
  const { flow, prompter } = params;
752
+ if (params.nextConfig.wallet?.provider?.id === "embedded-keystore") {
753
+ throwLegacyEmbeddedKeystoreMigrationRequired("legacy wallet configuration detected");
754
+ }
1291
755
  const current = params.nextConfig.wallet?.runtime;
1292
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
+ }
1293
763
  const currentEnabledProviders = WALLET_PROVIDER_IDS.filter((providerId) => registry.providers[providerId]?.enabled);
1294
764
  const onboardingProviderOptions = WALLET_PROVIDER_IDS.filter((providerId) => ["local-socket-signer"].includes(providerId));
1295
765
  const currentChains = normalizeChains(current?.chains);
1296
- const configuredWalletMaterial = hasConfiguredWalletMaterial({
1297
- config: params.nextConfig,
1298
- registry,
1299
- env: process.env
1300
- });
766
+ const configuredWalletMaterial = hasConfiguredWalletMaterial({ registry });
1301
767
  const defaultEnabled = current?.enabled === false ? false : Boolean(current?.enabled && configuredWalletMaterial);
1302
768
  const enabled = params.forceEnable === true ? true : flow === "quickstart" ? defaultEnabled : await prompter.confirm({
1303
769
  message: "Enable wallet integration?",
@@ -1351,74 +817,102 @@ async function configureWalletForOnboarding(params) {
1351
817
  if (defaultProvider === "local-socket-signer") {
1352
818
  const quietSignerNotes = flow === "quickstart";
1353
819
  const socketPath = resolveLocalSignerSocketPath(process.env);
1354
- const backendSocketPath = resolveLocalSignerBackendSocketPath(process.env);
1355
820
  const materialDir = resolveLocalSignerMaterialRootDir(process.env);
1356
821
  const binPath = resolveSignerdBinaryPath(process.env);
1357
- const runAsUser = resolveLocalSignerRunAsUser(process.env);
1358
- ensureLocalSignerPassphrase();
1359
- if (!fs.existsSync(binPath)) {
1360
- if (!quietSignerNotes) {
1361
- await prompter.note([`fased-signerd not found at: ${binPath}`, "Building fased-signerd locally when Go is available, or using an explicitly configured signer asset."].join("\n"), "Local socket signer");
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.");
1362
828
  }
1363
- await params.prepareLocalSigner?.({ binPath });
1364
- if (!fs.existsSync(binPath)) {
1365
- installSignerdBinary(binPath);
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 });
1366
839
  }
1367
- if (!quietSignerNotes) {
1368
- await prompter.note(`fased-signerd installed at: ${binPath}`, "Local socket signer");
1369
- }
1370
- }
1371
- const socketExists = isSignerdRunning(socketPath);
1372
- const socketHealthy = socketExists ? await isSignerdHealthy(socketPath) : false;
1373
- const desiredSignerChains = normalizeSignerChains(String(resolveSignerChildEnv(materialDir, process.env).FASED_WALLET_CHAINS ?? "").split(","));
1374
- const healthDetails = socketExists && socketHealthy ? await readSignerdHealth(socketPath) : {
1375
- ok: false,
1376
- chains: []
1377
- };
1378
- const missingSignerChains = desiredSignerChains.filter((chain) => !healthDetails.chains.includes(chain));
1379
- if (socketExists && socketHealthy && missingSignerChains.length === 0) {
1380
- process.env.FASED_WALLET_LOCAL_SIGNER_SOCKET = socketPath;
1381
- if (backendSocketPath !== socketPath) {
1382
- process.env.FASED_WALLET_LOCAL_SIGNER_BACKEND_SOCKET = backendSocketPath;
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.`);
1383
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;
1384
853
  if (!quietSignerNotes) {
1385
- await prompter.note(`fased-signerd already running at: ${socketPath}`, "Local socket signer");
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");
1386
855
  }
1387
- } else {
1388
- if (socketExists && (!socketHealthy || missingSignerChains.length > 0)) {
1389
- stopProcessBySocket(socketPath, runAsUser);
856
+ } else if (!externallyManagedSigner && !syncFs.existsSync(binPath)) {
857
+ const signerInstallProgress = quietSignerNotes ? prompter.progress("Installing local wallet signer…") : undefined;
858
+ if (!quietSignerNotes) {
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");
1390
860
  }
1391
- if (backendSocketPath !== socketPath) {
1392
- stopProcessBySocket(backendSocketPath, runAsUser);
861
+ try {
862
+ await params.prepareLocalSigner?.({ binPath });
863
+ if (!syncFs.existsSync(binPath)) {
864
+ installSignerdBinary(binPath);
865
+ }
866
+ signerInstallProgress?.stop("Local wallet signer installed.");
867
+ } catch (error) {
868
+ signerInstallProgress?.stop("Local wallet signer installation failed.");
869
+ throw error;
1393
870
  }
1394
- startSignerdBackground(binPath, backendSocketPath, materialDir);
1395
- if (backendSocketPath !== socketPath) {
1396
- startSignerBrokerBackground(socketPath, backendSocketPath, materialDir);
871
+ if (!quietSignerNotes) {
872
+ await prompter.note(`fased-signerd installed at: ${binPath}`, "Local socket signer");
1397
873
  }
1398
- const readyTimeoutMs = runAsUser ? 2e4 : 8e3;
1399
- if (await waitForSignerdHealthy(socketPath, readyTimeoutMs)) {
1400
- if (!quietSignerNotes) {
1401
- 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");
1402
- }
874
+ }
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) {
1403
885
  process.env.FASED_WALLET_LOCAL_SIGNER_SOCKET = socketPath;
1404
- if (backendSocketPath !== socketPath) {
1405
- 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");
1406
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"));
1407
891
  } else {
1408
- throw buildSignerdReadinessError({
1409
- appSocketPath: socketPath,
1410
- backendSocketPath,
1411
- binPath,
1412
- materialDir,
1413
- readyTimeoutMs,
1414
- runAsUser
1415
- });
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
+ }
1416
910
  }
1417
911
  }
1418
912
  }
1419
- const automationSigning = flow === "quickstart" ? current?.policy?.directSigning ?? true : await prompter.confirm({
1420
- message: "Allow automated wallet execution from approved Agent tools?",
1421
- 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
1422
916
  });
1423
917
  const toolAccessMode = flow === "quickstart" ? current?.toolAccess?.mode ?? "owner-only" : await prompter.select({
1424
918
  message: "Wallet tool access scope",
@@ -1444,7 +938,7 @@ async function configureWalletForOnboarding(params) {
1444
938
  initialValue: (current?.toolAccess?.allowAgents ?? []).join(",")
1445
939
  })) : [];
1446
940
  const solanaAllowPrograms = flow === "quickstart" ? (current?.policy?.solana?.allowPrograms ?? []).map((item) => item.trim()).filter(Boolean) : splitCsvList(await prompter.text({
1447
- message: "Solana program allowlist (comma separated, blank=any)",
941
+ message: "Solana program/token allowlist (comma separated, blank=deny all)",
1448
942
  initialValue: (current?.policy?.solana?.allowPrograms ?? []).join(",")
1449
943
  }));
1450
944
  const solanaMaxPerTx = flow === "quickstart" ? current?.policy?.solana?.maxPerTx?.trim() || DEFAULT_SOLANA_MAX_PER_TX : (await prompter.text({
@@ -1496,4 +990,4 @@ async function configureWalletForOnboarding(params) {
1496
990
  }
1497
991
 
1498
992
  //#endregion
1499
- export { configureWalletForOnboarding, installSignerdBinary, migrateLocalSignerKeystoreToMaterialDir, resolveSignerdBinaryPath, restartLocalSocketSigner, syncLocalSocketSignerFromConfig };
993
+ export { configureWalletForOnboarding, installSignerdBinary, resolveSignerdBinaryPath, restartLocalSocketSigner, syncLocalSocketSignerFromConfig, writeLocalSignerEnvFile };