@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.
- package/CHANGELOG.md +43 -0
- package/README.md +38 -30
- package/config/dependency-ownership.json +1 -1
- package/config/signer-policies/README.md +49 -0
- package/config/signer-policies/agent.json.template +32 -0
- package/config/signer-policies/mining.json.template +69 -0
- package/config/signer-policies/network.json.template +5 -0
- package/config/signer-policies/vault.json.template +70 -0
- package/config/signer-protocol-v2.json +76 -0
- package/config/typescript-noemit-baseline.json +680 -0
- package/dist/acp/client.js +2 -2
- package/dist/acp/secret-file.js +2 -2
- package/dist/agents/agent-scope.js +2 -2
- package/dist/agents/apply-patch.js +8 -8
- package/dist/agents/auth-profiles/paths.js +2 -2
- package/dist/agents/auth-profiles/store.js +269 -33
- package/dist/agents/bash-tools.exec.js +10 -4
- package/dist/agents/bash-tools.shared.js +2 -2
- package/dist/agents/cli-credentials.js +4 -4
- package/dist/agents/current-model-catalog.js +3 -3
- package/dist/agents/docs-path.js +3 -3
- package/dist/agents/embedded-pi-mcp.js +4 -4
- package/dist/agents/identity-avatar.js +3 -3
- package/dist/agents/identity-file.js +2 -2
- package/dist/agents/memory-search.js +7 -1
- package/dist/agents/model-catalog-access.js +1 -2
- package/dist/agents/model-catalog-snapshot.js +1 -1
- package/dist/agents/model-catalog-source.js +1 -1
- package/dist/agents/model-metadata.js +1 -1
- package/dist/agents/model-selection.js +2 -28
- package/dist/agents/models-config.providers.js +6 -6
- package/dist/agents/openai-codex-app-server.js +5 -5
- package/dist/agents/pi-embedded-runner/compact.js +15 -7
- package/dist/agents/pi-embedded-runner/model.js +1 -1
- package/dist/agents/pi-embedded-runner/run/attempt.js +24 -13
- package/dist/agents/pi-embedded-runner/run.js +2 -0
- package/dist/agents/pi-embedded-runner/tool-split.js +40 -2
- package/dist/agents/pi-embedded-subscribe.raw-stream.js +3 -3
- package/dist/agents/pi-extensions/compaction-safeguard.js +3 -3
- package/dist/agents/pi-model-discovery.js +6 -6
- package/dist/agents/pi-project-settings.js +3 -3
- package/dist/agents/pi-tool-definition-adapter.js +16 -1
- package/dist/agents/pi-tools.js +2 -1
- package/dist/agents/sandbox/config.js +1 -1
- package/dist/agents/sandbox/context.js +27 -2
- package/dist/agents/sandbox/fs-bridge.js +2 -2
- package/dist/agents/session-dirs.js +4 -4
- package/dist/agents/session-write-lock.js +13 -13
- package/dist/agents/shell-utils.js +5 -5
- package/dist/agents/skills/bundled-dir.js +4 -4
- package/dist/agents/skills/env-overrides.js +38 -28
- package/dist/agents/skills/local-loader.js +6 -6
- package/dist/agents/skills/plugin-skills.js +2 -2
- package/dist/agents/skills/trust.js +93 -0
- package/dist/agents/skills/workspace.js +141 -13
- package/dist/agents/skills-clawhub.js +36 -11
- package/dist/agents/skills-install-download.js +7 -7
- package/dist/agents/skills-install-plan.js +2 -2
- package/dist/agents/skills-install.js +2 -2
- package/dist/agents/skills-marketplace-scan.js +49 -0
- package/dist/agents/skills-status.js +2 -2
- package/dist/agents/subagent-depth.js +2 -2
- package/dist/agents/system-prompt-params.js +2 -2
- package/dist/agents/tools/marketplace-offer-draft-tool.js +1 -1
- package/dist/agents/tools/mining-tool.js +1 -1
- package/dist/agents/tools/session-status-tool.js +1 -1
- package/dist/agents/tools/wallet-action-tool.js +172 -36
- package/dist/agents/tools/wallet-skill-policy.js +65 -23
- package/dist/agents/tools/wallet-tool.js +7 -11
- package/dist/agents/workspace.js +22 -22
- package/dist/auto-reply/reply/agent-runner-execution.js +2 -2
- package/dist/auto-reply/reply/agent-runner.js +2 -2
- package/dist/auto-reply/reply/commands-export-session.js +7 -7
- package/dist/auto-reply/reply/directive-handling.fast-lane.js +2 -1
- package/dist/auto-reply/reply/directive-handling.impl.js +2 -1
- package/dist/auto-reply/reply/directive-handling.model.js +2 -1
- package/dist/auto-reply/reply/directive-handling.persist.js +2 -2
- package/dist/auto-reply/reply/get-reply-directives-apply.js +2 -0
- package/dist/auto-reply/reply/get-reply-inline-actions.js +25 -1
- package/dist/auto-reply/reply/model-selection.js +7 -4
- package/dist/auto-reply/reply/post-compaction-audit.js +3 -3
- package/dist/auto-reply/reply/post-compaction-context.js +3 -3
- package/dist/auto-reply/reply/session-reset-model.js +4 -4
- package/dist/auto-reply/reply/session.js +3 -3
- package/dist/auto-reply/skill-commands.js +3 -3
- package/dist/auto-reply/status.js +6 -6
- package/dist/brand.js +1 -1
- package/dist/browser/chrome.executables.js +3 -3
- package/dist/browser/chrome.js +3 -3
- package/dist/browser/chrome.profile-decoration.js +6 -6
- package/dist/browser/profiles-service.js +2 -2
- package/dist/browser/server-context.js +2 -2
- package/dist/browser/trash.js +4 -4
- package/dist/build-info.json +3 -3
- package/dist/canvas-host/a2ui/.bundle.hash +1 -1
- package/dist/canvas-host/server.js +4 -4
- package/dist/capabilities/catalog.js +3 -3
- package/dist/channel-web.js +2 -2
- package/dist/channels/plugins/catalog.js +3 -3
- package/dist/cli/browser-cli-extension.js +6 -6
- package/dist/cli/command-format.js +2 -2
- package/dist/cli/cron-cli/register.cron-simple.js +2 -2
- package/dist/cli/dns-cli.js +9 -9
- package/dist/cli/gateway-cli/dev.js +5 -5
- package/dist/cli/gateway-cli/run.js +10 -2
- package/dist/cli/hooks-cli.js +5 -5
- package/dist/cli/lightweight/gateway-runtime-probe.js +2 -2
- package/dist/cli/memory-cli.js +7 -7
- package/dist/cli/nodes-media-utils.js +2 -2
- package/dist/cli/plugins-cli.js +2 -2
- package/dist/cli/program/command-registry.js +1 -1
- package/dist/cli/program/register.onboard.js +2 -3
- package/dist/cli/program/register.wallet.js +16 -123
- package/dist/cli/providers-cli.js +1 -1
- package/dist/cli/sat-cli.js +129 -2
- package/dist/cli/secrets-cli.js +3 -3
- package/dist/cli/update-cli/update-command.js +260 -11
- package/dist/cli/windows-argv.js +2 -2
- package/dist/commands/auth-choice.apply.api-providers.js +3 -3
- package/dist/commands/dashboard-link.js +3 -3
- package/dist/commands/doctor-config-flow.js +1 -1
- package/dist/commands/doctor-install.js +5 -5
- package/dist/commands/doctor-legacy-config.js +2 -13
- package/dist/commands/doctor-memory-search.js +2 -2
- package/dist/commands/doctor-platform-notes.js +2 -2
- package/dist/commands/doctor-sandbox.js +2 -2
- package/dist/commands/doctor-state-integrity.js +23 -23
- package/dist/commands/doctor-workspace.js +3 -3
- package/dist/commands/doctor.js +3 -3
- package/dist/commands/managed-up.js +2 -2
- package/dist/commands/model-picker.js +6 -6
- package/dist/commands/models/auth.js +1 -1
- package/dist/commands/onboard-auth.config-gateways.js +1 -1
- package/dist/commands/onboard-auth.credentials.js +35 -11
- package/dist/commands/onboard-non-interactive/local/wallet-config.js +28 -27
- package/dist/commands/onboard-non-interactive/local.js +5 -1
- package/dist/commands/onboarding/plugin-install.js +2 -2
- package/dist/commands/sessions-cleanup.js +2 -2
- package/dist/commands/signal-install.js +6 -6
- package/dist/commands/status-all/channels.js +2 -2
- package/dist/commands/wallet.js +570 -1913
- package/dist/config/cache-utils.js +2 -2
- package/dist/config/config.js +2 -2
- package/dist/config/includes.js +5 -5
- package/dist/config/io.js +66 -4
- package/dist/config/paths.js +3 -3
- package/dist/config/sat-runtime-ids.js +3 -3
- package/dist/config/schema.help.js +8 -7
- package/dist/config/schema.labels.js +5 -4
- package/dist/config/sessions/disk-budget.js +6 -6
- package/dist/config/sessions/paths.js +2 -2
- package/dist/config/sessions/store.js +17 -17
- package/dist/config/sessions/transcript.js +4 -4
- package/dist/config/talk.js +2 -2
- package/dist/config/zod-schema.agent-runtime.js +1 -0
- package/dist/config/zod-schema.js +5 -0
- package/dist/control-ui/assets/{app-BXA4gPvN.js → app-bR-fO5ty.js} +1511 -1555
- package/dist/control-ui/assets/{config-CU5Zi8Dh.js → config-LeImukCW.js} +10 -10
- package/dist/control-ui/assets/{federation-D10Q4eP_.js → federation-DgA9e-Pn.js} +364 -422
- package/dist/control-ui/assets/{index-D2QbJhVg.js → index-ckCxHwjy.js} +3 -3
- package/dist/control-ui/assets/{mining-BdhMn18r.js → mining-BgHFh0ei.js} +134 -114
- package/dist/control-ui/assets/mining-commit-DFpB_4JN.js +2 -0
- package/dist/control-ui/assets/{open-external-url-DwSiwMPl.js → open-external-url-ulF1B-sA.js} +3 -3
- package/dist/control-ui/assets/{providers-QMn4w2_f.js → providers-CIx9fxc6.js} +2 -2
- package/dist/control-ui/assets/{wallet-C2g294QT.js → wallet-DvStR_AT.js} +259 -377
- package/dist/control-ui/index.html +1 -1
- package/dist/control-ui/version.json +1 -1
- package/dist/cron/service/store.js +2 -2
- package/dist/cron/store.js +6 -6
- package/dist/daemon/systemd-system.js +60 -54
- package/dist/discord/monitor/thread-bindings.state.js +2 -2
- package/dist/extensions/sat-mining/src/protocol-contract.js +43 -3
- package/dist/extensions/sat-mining/src/rpc-read.js +235 -67
- package/dist/extensions/sat-mining/src/sat-bond-layout.js +14 -11
- package/dist/extensions/sat-mining/src/signer-codec-manifest.js +406 -0
- package/dist/extensions/sat-mining/src/solana-submit.js +1038 -397
- package/dist/extensions/sat-mining/src/submission-ledger.js +349 -0
- package/dist/federation/auto-connect.js +98 -8
- package/dist/federation/marketplace-capability-adapter.js +33 -7
- package/dist/federation/marketplace-delivery-outbox.js +196 -0
- package/dist/federation/marketplace-delivery.js +351 -215
- package/dist/federation/marketplace-direct-payment.js +272 -141
- package/dist/federation/marketplace-escrow.js +607 -249
- package/dist/federation/marketplace-settlement-ledger.js +352 -0
- package/dist/federation/offers.js +9 -7
- package/dist/federation/peer-auth-v2.js +529 -0
- package/dist/gateway/a2a-http.js +425 -96
- package/dist/gateway/a2a-settlement.js +259 -153
- package/dist/gateway/a2a-task-store.js +524 -0
- package/dist/gateway/auth.js +38 -2
- package/dist/gateway/config-reload.js +46 -33
- package/dist/gateway/control-ui-boot-check.js +6 -6
- package/dist/gateway/control-ui-login.js +7 -7
- package/dist/gateway/control-ui.js +9 -9
- package/dist/gateway/durable-capacity.js +31 -0
- package/dist/gateway/federation-http.js +540 -135
- package/dist/gateway/federation-marketplace-run-store.js +397 -0
- package/dist/gateway/federation-marketplace.js +535 -230
- package/dist/gateway/hooks-mapping.js +4 -4
- package/dist/gateway/mutating-admin-rpc-rate-limit.js +5 -0
- package/dist/gateway/security-path.js +1 -1
- package/dist/gateway/server-discovery.js +2 -2
- package/dist/gateway/server-http.js +1088 -1209
- package/dist/gateway/server-methods/chat.js +3 -3
- package/dist/gateway/server-methods/models.js +1 -1
- package/dist/gateway/server-methods/secrets.js +40 -0
- package/dist/gateway/server-methods/sessions.js +6 -6
- package/dist/gateway/server-methods/usage.js +2 -2
- package/dist/gateway/server-methods.js +1 -2
- package/dist/gateway/server-reload-handlers.js +196 -66
- package/dist/gateway/server-runtime-config.js +1 -1
- package/dist/gateway/server-secrets-runtime.js +288 -0
- package/dist/gateway/server.impl.js +73 -42
- package/dist/gateway/session-compaction-checkpoints.js +4 -4
- package/dist/gateway/session-utils.fs.js +24 -24
- package/dist/gateway/session-utils.js +6 -6
- package/dist/gateway/sessions-patch.js +3 -5
- package/dist/hooks/bundled-dir.js +4 -4
- package/dist/hooks/gmail-setup-utils.js +6 -6
- package/dist/hooks/import-url.js +2 -2
- package/dist/hooks/loader.js +4 -4
- package/dist/hooks/workspace.js +7 -7
- package/dist/index.js +4 -2
- package/dist/infra/archive.js +17 -17
- package/dist/infra/boundary-file-read.js +2 -2
- package/dist/infra/boundary-path.js +15 -15
- package/dist/infra/brew.js +2 -2
- package/dist/infra/control-ui-assets.js +14 -14
- package/dist/infra/device-auth-store.js +8 -8
- package/dist/infra/device-identity.js +8 -8
- package/dist/infra/dotenv.js +2 -2
- package/dist/infra/exec-approvals.js +8 -8
- package/dist/infra/exec-command-resolution.js +4 -4
- package/dist/infra/exec-safe-bin-trust.js +2 -2
- package/dist/infra/fased-root.js +5 -5
- package/dist/infra/fasedhub.js +64 -1
- package/dist/infra/fs-safe.js +14 -14
- package/dist/infra/gateway-lock.js +11 -11
- package/dist/infra/git-commit.js +5 -5
- package/dist/infra/git-root.js +4 -4
- package/dist/infra/hosted-runtime-artifact.js +3 -3
- package/dist/infra/is-main.js +2 -2
- package/dist/infra/json-file.js +7 -7
- package/dist/infra/local-source-paired-update.js +538 -0
- package/dist/infra/managed-runtime-bootstrap.js +6 -6
- package/dist/infra/outbound/delivery-queue.js +16 -16
- package/dist/infra/path-env.js +3 -3
- package/dist/infra/ports-lsof.js +4 -4
- package/dist/infra/provider-usage.auth.js +3 -3
- package/dist/infra/restart.js +41 -10
- package/dist/infra/safe-open-sync.js +2 -2
- package/dist/infra/session-cost-usage.js +9 -9
- package/dist/infra/shell-env.js +2 -2
- package/dist/infra/state-migrations.fs.js +6 -6
- package/dist/infra/state-migrations.js +23 -23
- package/dist/infra/tmp-fased-dir.js +6 -6
- package/dist/infra/update-runner.js +96 -0
- package/dist/infra/widearea-dns.js +3 -3
- package/dist/infra/wsl.js +2 -2
- package/dist/light-update-status.js +11 -11
- package/dist/line/accounts.js +2 -2
- package/dist/line/download.js +2 -2
- package/dist/logging/config.js +3 -3
- package/dist/logging/logger.js +7 -7
- package/dist/managed/federation.js +3 -3
- package/dist/managed/tunnel.js +3 -3
- package/dist/media/store.js +13 -13
- package/dist/media-understanding/runner.js +3 -3
- package/dist/memory/backend-config.js +2 -1
- package/dist/memory/embeddings.js +2 -2
- package/dist/memory/internal.js +10 -10
- package/dist/memory/manager-sync-ops.js +7 -7
- package/dist/memory/repair-executor.js +17 -17
- package/dist/memory/session-files.js +16 -2
- package/dist/node-host/invoke-system-run-plan.js +8 -8
- package/dist/node-host/invoke.js +2 -2
- package/dist/node-host/runner.js +3 -3
- package/dist/pairing/pairing-store.js +3 -3
- package/dist/plugin-sdk/{accounts-DxblobYM.js → accounts-Brn3-uWN.js} +1 -1
- package/dist/plugin-sdk/{actions-D4UNimWa.js → actions-DRu135h0.js} +2 -2
- package/dist/plugin-sdk/{active-listener-CFuy106s.js → active-listener-B_NMtgPL.js} +1 -1
- package/dist/plugin-sdk/{audio-preflight-Bv0hUOWL.js → audio-preflight-BYxAt775.js} +27 -27
- package/dist/plugin-sdk/{audit-Cs0K7LBp.js → audit-C5d2z5U1.js} +1 -1
- package/dist/plugin-sdk/{channel-access-D5aHoTY8.js → channel-access-CcFRdRPO.js} +1 -1
- package/dist/plugin-sdk/{channel-access-configure-Dvch9mMQ.js → channel-access-configure-DaI5wevh.js} +1 -1
- package/dist/plugin-sdk/channel-plugin-common.d.ts +3 -3
- package/dist/plugin-sdk/channel-plugin-common.js +4 -4
- package/dist/plugin-sdk/{chunk-B0FUGc5A.js → chunk-CyoyqpbG.js} +1 -1
- package/dist/plugin-sdk/{chunk-items-Bk--NU3E.js → chunk-items-DIRHG2gJ.js} +1 -1
- package/dist/plugin-sdk/command-status.d.ts +2 -2
- package/dist/plugin-sdk/command-status.js +27 -27
- package/dist/plugin-sdk/{commands-registry-DmDRhRAU.js → commands-registry-BfqdEWdF.js} +2 -2
- package/dist/plugin-sdk/{common-CfUOowOp.js → common-HCcXU-NR.js} +1 -1
- package/dist/plugin-sdk/{config-mxULv0fQ.js → config-DlWVeLFB.js} +7113 -6877
- package/dist/plugin-sdk/{config-schema-CGW6J3Pv.js → config-schema-BOT7kHb6.js} +6779 -3696
- package/dist/plugin-sdk/device-pair.d.ts +1 -1
- package/dist/plugin-sdk/discord.d.ts +5 -5
- package/dist/plugin-sdk/discord.js +19 -19
- package/dist/plugin-sdk/file-lock.js +35 -4
- package/dist/plugin-sdk/{filesystem-manager-ZoHU8oA7.js → filesystem-manager-DoI8IBNF.js} +1 -1
- package/dist/plugin-sdk/{group-mentions-BHRff6j8.d.ts → group-mentions-q3hUTCt7.d.ts} +1 -1
- package/dist/plugin-sdk/{handle-action-DbVCF9HZ.js → handle-action-hvwapMKg.js} +13 -13
- package/dist/plugin-sdk/{helpers-BXbdgs5H.js → helpers-DMBU4oTo.js} +1 -1
- package/dist/plugin-sdk/{image-ops-D7jPxaph.js → image-ops-DQi0n_Tt.js} +1 -1
- package/dist/plugin-sdk/index.d.ts +9 -27
- package/dist/plugin-sdk/index.js +29 -29
- package/dist/plugin-sdk/json-store.js +6 -6
- package/dist/plugin-sdk/{login-BRxww6nv.js → login-Ca583Okc.js} +3 -3
- package/dist/plugin-sdk/{login-qr-DJXsk9XH.js → login-qr-CGciBSNP.js} +3 -3
- package/dist/plugin-sdk/{manager-Y2S_5IlI.js → manager-BegWxNYX.js} +4 -4
- package/dist/plugin-sdk/{markdown-tables-DHLFQqaF.js → markdown-tables-DCOXEUfZ.js} +1 -1
- package/dist/plugin-sdk/{media-Czo4carg.js → media-CAdQF5TG.js} +47 -47
- package/dist/plugin-sdk/{memory-search-DTjIMw8J.js → memory-search-CrFPOeTH.js} +4 -1
- package/dist/plugin-sdk/{monitor-B9Cmt_Je.js → monitor-BCFDaqJ-.js} +31 -31
- package/dist/plugin-sdk/{monitor-B71iwAgC.js → monitor-BgbjRiPM.js} +32 -32
- package/dist/plugin-sdk/{openresponses-http-Du4GMmwo.js → openresponses-http-DFf87SMM.js} +27 -27
- package/dist/plugin-sdk/{optional-runtime-dependency-DdMbxS24.js → optional-runtime-dependency-0dznm3ZO.js} +1 -1
- package/dist/plugin-sdk/{outbound-Db8mRN6P.js → outbound-BgVDdHxx.js} +6 -6
- package/dist/plugin-sdk/{outbound-attachment-BqFjcypb.js → outbound-attachment-QqTnIhZL.js} +2 -2
- package/dist/plugin-sdk/{pairing-message-BMkkS038.d.ts → pairing-message-CS-9BHug.d.ts} +2 -2
- package/dist/plugin-sdk/{pairing-message-B3GC_7CD.js → pairing-message-D0CoLHRJ.js} +1 -1
- package/dist/plugin-sdk/{pairing-store-CQikgIbw.js → pairing-store-BChahgI2.js} +2 -2
- package/dist/plugin-sdk/{pi-model-discovery-CY3m5zJP.js → pi-model-discovery-BX_JLcEJ.js} +1 -1
- package/dist/plugin-sdk/{pi-model-discovery-runtime-DSh1T2pn.js → pi-model-discovery-runtime-DxTMz_XF.js} +2 -2
- package/dist/plugin-sdk/provider-web-search-config-contract.d.ts +2 -2
- package/dist/plugin-sdk/{pw-ai-AFUqn-tQ.js → pw-ai-natjh3rm.js} +3 -3
- package/dist/plugin-sdk/{qmd-manager-DSyx77KY.js → qmd-manager-CgRaTxUq.js} +1 -1
- package/dist/plugin-sdk/{query-expansion-CAI2kKLQ.js → query-expansion-BG3FgzF7.js} +14 -3
- package/dist/plugin-sdk/{replies-Fzh9PhXl.js → replies-ChYo5ZQM.js} +2 -2
- package/dist/plugin-sdk/{resolve-outbound-target-KpF3co6T.js → resolve-outbound-target-D-KGgHJZ.js} +1 -1
- package/dist/plugin-sdk/{resolve-route-CDl74O19.js → resolve-route-C8tvQm8Z.js} +1 -1
- package/dist/plugin-sdk/{retry-policy-D4uiqUOG.js → retry-policy-C6SjKjQA.js} +1 -1
- package/dist/plugin-sdk/{runtime-Bb6KRnCC.js → runtime-2B9_WKoI.js} +8 -3
- package/dist/plugin-sdk/{runtime-dependency-DLDmB94f.js → runtime-dependency-DczkvPxn.js} +2 -2
- package/dist/plugin-sdk/sat-runtime.d.ts +549 -309
- package/dist/plugin-sdk/sat-runtime.js +29 -29
- package/dist/plugin-sdk/{send-C4k4JuJN.js → send-B6KkGKZD.js} +6 -6
- package/dist/plugin-sdk/{send-D-uDKGIq.js → send-CFzGKJXo.js} +6 -6
- package/dist/plugin-sdk/{send-Du8ykP1k.js → send-DCO441U7.js} +8 -8
- package/dist/plugin-sdk/{send-5nFktR1n.js → send-gDoGA-7e.js} +5 -5
- package/dist/plugin-sdk/{send-Bfzf11VY.js → send-q9HXrSwK.js} +7 -7
- package/dist/plugin-sdk/{session-CS-Td9EM.js → session-DHD4ZKyG.js} +2 -2
- package/dist/plugin-sdk/{skill-commands-CFAlXWHk.js → skill-commands-FKEPLS7T.js} +201 -26
- package/dist/plugin-sdk/{slack-D_bbE6Yd.js → slack-BUTt8cxK.js} +39 -39
- package/dist/plugin-sdk/{slack-actions-BcMkNv1Z.js → slack-actions-DhbMcp8M.js} +11 -11
- package/dist/plugin-sdk/slack.d.ts +5 -5
- package/dist/plugin-sdk/slack.js +9 -9
- package/dist/plugin-sdk/{sticker-cache-Dqo9nRhi.js → sticker-cache-D0wHVqjt.js} +3 -3
- package/dist/plugin-sdk/{tables-bFXyRpkx.js → tables-B3jr0Pvq.js} +1 -1
- package/dist/plugin-sdk/{targets-DH7a9lKB.js → targets-CtSbTxYX.js} +1 -1
- package/dist/plugin-sdk/{telegram-actions-Cnag7JTH.js → telegram-actions-CC_qN4v0.js} +30 -30
- package/dist/plugin-sdk/telegram.d.ts +5 -5
- package/dist/plugin-sdk/telegram.js +8 -8
- package/dist/plugin-sdk/{thread-bindings.lifecycle-CgOvvdw8.js → thread-bindings.lifecycle-QbkNl-m6.js} +3 -3
- package/dist/plugin-sdk/{thread-bindings.state-CQpk44Hq.js → thread-bindings.state-D9hg_M1c.js} +2 -2
- package/dist/plugin-sdk/tmp-fased-dir-xOTGlzBE.d.ts +486 -0
- package/dist/plugin-sdk/{types-CLIvQaVg.d.ts → types-C3avNytw.d.ts} +3 -4
- package/dist/plugin-sdk/{types-DyhXnYUr.d.ts → types-D0kDG8l0.d.ts} +3 -2
- package/dist/plugin-sdk/{types-BYDL6dQj.d.ts → types-DWfbSAaM.d.ts} +2 -2
- package/dist/plugin-sdk/{types.plugin-_fbu40Re.d.ts → types.plugin-BcSBq42x.d.ts} +8 -3
- package/dist/plugin-sdk/{web-BInKQkc4.js → web-BXsJ1vqe.js} +32 -32
- package/dist/plugin-sdk/{whatsapp-actions-BtSc2CBv.js → whatsapp-actions-Bj_rOncr.js} +13 -13
- package/dist/plugin-sdk/{whatsapp-heartbeat-Bg9TuYzl.js → whatsapp-heartbeat-B1WHkJIg.js} +2 -2
- package/dist/plugin-sdk/whatsapp.d.ts +5 -5
- package/dist/plugin-sdk/whatsapp.js +12 -12
- package/dist/plugin-sdk/{wsl-C4DGFkOd.js → wsl-J-HscWxS.js} +3 -3
- package/dist/plugins/bundled-dir.js +3 -3
- package/dist/plugins/discovery.js +11 -11
- package/dist/plugins/loader.js +13 -13
- package/dist/plugins/manifest-registry.js +2 -2
- package/dist/plugins/manifest.js +4 -4
- package/dist/plugins/path-safety.js +3 -3
- package/dist/plugins/status-cache.js +9 -9
- package/dist/plugins/update.js +3 -3
- package/dist/plugins/web-search-install-catalog.js +3 -3
- package/dist/providers/refresh.js +1 -1
- package/dist/providers/registry.js +1 -1
- package/dist/secrets/apply.js +17 -17
- package/dist/secrets/audit.js +11 -11
- package/dist/secrets/runtime.js +1 -1
- package/dist/secrets/shared.js +9 -9
- package/dist/security/scan-paths.js +2 -2
- package/dist/shared/config-eval.js +2 -2
- package/dist/shared/pid-alive.js +2 -2
- package/dist/tasks/standing-orders.js +5 -5
- package/dist/tasks/task-flow-registry.js +5 -5
- package/dist/tasks/task-registry.js +5 -5
- package/dist/tasks/workflow-definitions.js +6 -6
- package/dist/telegram/token.js +5 -5
- package/dist/utils.js +5 -5
- package/dist/wallet/external-submission-ledger.js +376 -0
- package/dist/wallet/legacy-embedded-keystore.js +40 -0
- package/dist/wallet/local-socket-signer-archive.js +30 -0
- package/dist/wallet/local-socket-signer-lifecycle.js +97 -0
- package/dist/wallet/local-socket-signer-policy.js +166 -0
- package/dist/wallet/local-socket-signer-protocol.js +561 -118
- package/dist/wallet/native-signer-wallet-id.js +25 -0
- package/dist/wallet/providers/alchemy-adapter.js +3 -0
- package/dist/wallet/providers/local-socket-signer-adapter.js +382 -107
- package/dist/wallet/providers/turnkey-adapter.js +292 -34
- package/dist/wallet/providers/wallet-standard-adapter.js +100 -0
- package/dist/wallet/signer-network-admin.js +184 -0
- package/dist/wallet/signer-protocol-v2.generated.js +90 -0
- package/dist/wallet/solana-bond-signing.js +157 -69
- package/dist/wallet/solana-reviewed-transaction.js +375 -0
- package/dist/wallet/solana-swap.js +520 -64
- package/dist/wallet/solana-transaction-inspection.js +106 -4
- package/dist/wallet/solana-trigger.js +658 -320
- package/dist/wallet/turnkey-reviewed-state.js +370 -0
- package/dist/wallet/wallet-approval-auth.js +20 -31
- package/dist/wallet/wallet-atomic-state.js +53 -0
- package/dist/wallet/wallet-audit-log.js +5 -5
- package/dist/wallet/wallet-canary.js +0 -1
- package/dist/wallet/wallet-inbound-events.js +19 -14
- package/dist/wallet/wallet-observability.js +5 -5
- package/dist/wallet/wallet-policy-simulation.js +1 -1
- package/dist/wallet/wallet-policy.js +95 -41
- package/dist/wallet/wallet-provider-capabilities.js +28 -4
- package/dist/wallet/wallet-provider-registry.js +64 -51
- package/dist/wallet/wallet-provider-resolver.js +62 -63
- package/dist/wallet/wallet-runtime-config.js +31 -44
- package/dist/wallet/wallet-secrets-store.js +44 -22
- package/dist/wallet/wallet-send-approvals.js +1894 -592
- package/dist/wallet/wallet-send-execution-ledger.js +292 -0
- package/dist/wallet/wallet-settlement-links.js +17 -16
- package/dist/wallet/wallet-standard-review.js +353 -0
- package/dist/wallet/wallet-status.js +0 -9
- package/dist/wallet/wallet-task-ledger.js +16 -2
- package/dist/web/accounts.js +3 -3
- package/dist/web/auth-store.js +18 -18
- package/dist/web/auto-reply/heartbeat-runner.js +1 -1
- package/dist/web/auto-reply/monitor.js +2 -2
- package/dist/web/session.js +4 -4
- package/dist/wizard/host-security-capability.js +15 -24
- package/dist/wizard/onboarding.finalize.js +63 -184
- package/dist/wizard/onboarding.host-security.js +118 -1368
- package/dist/wizard/onboarding.js +180 -242
- package/dist/wizard/onboarding.wallet.js +323 -829
- package/extensions/acpx/package.json +1 -1
- package/extensions/bluebubbles/package.json +1 -1
- package/extensions/copilot-proxy/package.json +1 -1
- package/extensions/diagnostics-otel/package.json +1 -1
- package/extensions/fased-federation/package.json +1 -1
- package/extensions/google-gemini-cli-auth/package.json +1 -1
- package/extensions/imessage/package.json +1 -1
- package/extensions/irc/package.json +1 -1
- package/extensions/line/package.json +1 -1
- package/extensions/llm-task/package.json +1 -1
- package/extensions/lobster/package.json +1 -1
- package/extensions/matrix/CHANGELOG.md +18 -0
- package/extensions/matrix/package.json +1 -1
- package/extensions/mattermost/package.json +1 -1
- package/extensions/memory-core/package.json +2 -2
- package/extensions/memory-lancedb/package.json +1 -1
- package/extensions/minimax-portal-auth/package.json +1 -1
- package/extensions/msteams/CHANGELOG.md +18 -0
- package/extensions/msteams/package.json +1 -1
- package/extensions/nextcloud-talk/package.json +1 -1
- package/extensions/nostr/CHANGELOG.md +18 -0
- package/extensions/nostr/package.json +1 -1
- package/extensions/open-prose/package.json +1 -1
- package/extensions/sat-mining/index.ts +498 -153
- package/extensions/sat-mining/package.json +1 -1
- package/extensions/sat-mining/signer-codec-schema.v1.json +374 -0
- package/extensions/sat-mining/src/audit-store.ts +50 -5
- package/extensions/sat-mining/src/claim-service.ts +85 -14
- package/extensions/sat-mining/src/config.ts +9 -0
- package/extensions/sat-mining/src/cycle-progress.ts +16 -8
- package/extensions/sat-mining/src/epoch-service.ts +9 -5
- package/extensions/sat-mining/src/gateway-runner.ts +21 -1
- package/extensions/sat-mining/src/protocol-contract.ts +45 -1
- package/extensions/sat-mining/src/recovery-service.ts +312 -0
- package/extensions/sat-mining/src/round-watcher.ts +391 -81
- package/extensions/sat-mining/src/rpc-read.ts +330 -89
- package/extensions/sat-mining/src/runtime.ts +14 -0
- package/extensions/sat-mining/src/sat-bond-layout.ts +6 -0
- package/extensions/sat-mining/src/signer-codec-manifest.ts +429 -0
- package/extensions/sat-mining/src/solana-submit.ts +1199 -428
- package/extensions/sat-mining/src/submission-ledger.ts +433 -0
- package/extensions/signal/package.json +1 -1
- package/extensions/synology-chat/package.json +1 -1
- package/extensions/tlon/package.json +1 -1
- package/extensions/twitch/CHANGELOG.md +18 -0
- package/extensions/twitch/package.json +1 -1
- package/extensions/voice-call/CHANGELOG.md +18 -0
- package/extensions/voice-call/package.json +1 -1
- package/extensions/zalo/CHANGELOG.md +18 -0
- package/extensions/zalo/package.json +1 -1
- package/extensions/zalouser/CHANGELOG.md +18 -0
- package/extensions/zalouser/package.json +1 -1
- package/install.sh +4955 -0
- package/package.json +24 -5
- package/scripts/fased-host-updater.mjs +1421 -0
- package/scripts/fased-host-updaterctl.mjs +186 -0
- package/scripts/fased-managed-service.sh +36 -0
- package/scripts/fased-managed-updater.mjs +3065 -156
- package/scripts/fased-signer-enroll-hosting.sh +275 -0
- package/scripts/fased-signer-network-hosting.sh +143 -0
- package/scripts/fased-signer-owner-policy.mjs +1430 -0
- package/scripts/fased-signer-policy-hosting.sh +50 -0
- package/scripts/fased-signer-policy-local.sh +26 -0
- package/scripts/hosted-release-manifest.mjs +185 -0
- package/scripts/install-fased-signerd.sh +217 -0
- package/scripts/install-managed-runtime.mjs +212 -10
- package/scripts/managed-runtime-layout.mjs +75 -3
- package/scripts/migrate-hosted-signer-v2.mjs +58 -0
- package/scripts/start-managed.sh +169 -360
- package/dist/control-ui/assets/mining-commit-R-6nu87p.js +0 -2
- package/dist/plugin-sdk/tmp-fased-dir-BdFQvrvw.d.ts +0 -77
- package/dist/wallet/local-socket-signer-broker.js +0 -224
- package/dist/wallet/local-socket-signer-custody.js +0 -42
- package/dist/wallet/providers/embedded-keystore-adapter.js +0 -402
- package/dist/wallet/providers/privy-adapter.js +0 -80
- 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 {
|
|
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
|
|
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
|
-
|
|
24
|
-
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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}
|
|
121
|
-
`Run manually: ${params.binPath} -socket ${params.
|
|
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
|
|
161
|
-
const
|
|
162
|
-
if (
|
|
163
|
-
return
|
|
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
|
|
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 (
|
|
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 (!
|
|
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 &&
|
|
161
|
+
if (explicit && syncFs.existsSync(explicit)) {
|
|
201
162
|
return explicit;
|
|
202
163
|
}
|
|
203
|
-
if (
|
|
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+)(
|
|
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
|
-
|
|
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 (!
|
|
236
|
+
if (!syncFs.existsSync(sourcePath)) {
|
|
275
237
|
throw new Error(`fased-signerd build completed but binary was not found at ${sourcePath}.`);
|
|
276
238
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
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
|
|
330
|
-
|
|
331
|
-
|
|
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
|
-
|
|
336
|
-
}
|
|
337
|
-
const
|
|
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
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
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 (
|
|
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
|
-
|
|
386
|
-
|
|
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
|
-
|
|
389
|
-
|
|
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
|
-
|
|
393
|
-
|
|
394
|
-
"
|
|
395
|
-
"
|
|
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
|
|
498
|
-
const
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
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
|
-
|
|
545
|
-
|
|
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
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
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
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
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
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
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
|
-
|
|
595
|
-
|
|
596
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
`export
|
|
625
|
-
...
|
|
626
|
-
|
|
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
|
-
|
|
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
|
-
|
|
501
|
+
syncFs.mkdirSync(path.dirname(signerEnvPath), {
|
|
640
502
|
recursive: true,
|
|
641
503
|
mode: 448
|
|
642
504
|
});
|
|
643
|
-
|
|
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
|
-
|
|
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
|
|
848
|
-
const
|
|
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
|
-
|
|
852
|
-
|
|
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
|
|
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(
|
|
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
|
-
|
|
567
|
+
syncFs.unlinkSync(candidatePidPath);
|
|
979
568
|
} catch {}
|
|
980
569
|
}
|
|
981
570
|
try {
|
|
982
|
-
|
|
571
|
+
syncFs.unlinkSync(socketPath);
|
|
983
572
|
} catch {}
|
|
984
573
|
}
|
|
985
574
|
function readProcessCommand(pid) {
|
|
986
575
|
try {
|
|
987
|
-
const cmdline =
|
|
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")
|
|
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
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
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
|
-
|
|
1098
|
-
|
|
1099
|
-
startSignerBrokerBackground(socketPath, backendSocketPath, materialDir, env);
|
|
603
|
+
if (!syncFs.existsSync(binPath)) {
|
|
604
|
+
return;
|
|
1100
605
|
}
|
|
1101
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
|
1358
|
-
|
|
1359
|
-
if (
|
|
1360
|
-
|
|
1361
|
-
|
|
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
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
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
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
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
|
|
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
|
-
|
|
1389
|
-
|
|
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
|
-
|
|
1392
|
-
|
|
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
|
-
|
|
1395
|
-
|
|
1396
|
-
startSignerBrokerBackground(socketPath, backendSocketPath, materialDir);
|
|
871
|
+
if (!quietSignerNotes) {
|
|
872
|
+
await prompter.note(`fased-signerd installed at: ${binPath}`, "Local socket signer");
|
|
1397
873
|
}
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
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 (
|
|
1405
|
-
|
|
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
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
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 ??
|
|
1420
|
-
message: "Allow automated wallet execution
|
|
1421
|
-
initialValue: current?.policy?.directSigning ??
|
|
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=
|
|
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,
|
|
993
|
+
export { configureWalletForOnboarding, installSignerdBinary, resolveSignerdBinaryPath, restartLocalSocketSigner, syncLocalSocketSignerFromConfig, writeLocalSignerEnvFile };
|