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