@fased/fased 0.1.62 → 0.1.64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (341) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +38 -30
  3. package/config/dependency-ownership.json +1 -1
  4. package/config/signer-policies/README.md +49 -0
  5. package/config/signer-policies/agent.json.template +32 -0
  6. package/config/signer-policies/mining.json.template +69 -0
  7. package/config/signer-policies/network.json.template +5 -0
  8. package/config/signer-policies/vault.json.template +70 -0
  9. package/config/signer-protocol-v2.json +76 -0
  10. package/config/typescript-noemit-baseline.json +680 -0
  11. package/dist/agents/auth-profiles/store.js +267 -31
  12. package/dist/agents/bash-tools.exec.js +10 -4
  13. package/dist/agents/current-model-catalog.js +3 -3
  14. package/dist/agents/memory-search.js +7 -1
  15. package/dist/agents/model-catalog-access.js +1 -2
  16. package/dist/agents/model-catalog-snapshot.js +1 -1
  17. package/dist/agents/model-catalog-source.js +1 -1
  18. package/dist/agents/model-metadata.js +1 -1
  19. package/dist/agents/model-selection.js +2 -28
  20. package/dist/agents/models-config.providers.js +6 -6
  21. package/dist/agents/pi-embedded-runner/compact.js +15 -7
  22. package/dist/agents/pi-embedded-runner/model.js +1 -1
  23. package/dist/agents/pi-embedded-runner/run/attempt.js +24 -13
  24. package/dist/agents/pi-embedded-runner/run.js +2 -0
  25. package/dist/agents/pi-embedded-runner/tool-split.js +40 -2
  26. package/dist/agents/pi-tool-definition-adapter.js +16 -1
  27. package/dist/agents/pi-tools.js +2 -1
  28. package/dist/agents/sandbox/config.js +1 -1
  29. package/dist/agents/sandbox/context.js +27 -2
  30. package/dist/agents/skills/env-overrides.js +38 -28
  31. package/dist/agents/skills/trust.js +93 -0
  32. package/dist/agents/skills/workspace.js +128 -0
  33. package/dist/agents/skills-clawhub.js +36 -11
  34. package/dist/agents/skills-marketplace-scan.js +49 -0
  35. package/dist/agents/tools/marketplace-offer-draft-tool.js +1 -1
  36. package/dist/agents/tools/mining-tool.js +1 -1
  37. package/dist/agents/tools/session-status-tool.js +1 -1
  38. package/dist/agents/tools/wallet-action-tool.js +172 -36
  39. package/dist/agents/tools/wallet-skill-policy.js +65 -23
  40. package/dist/agents/tools/wallet-tool.js +7 -11
  41. package/dist/auto-reply/reply/directive-handling.fast-lane.js +2 -1
  42. package/dist/auto-reply/reply/directive-handling.impl.js +2 -1
  43. package/dist/auto-reply/reply/directive-handling.model.js +2 -1
  44. package/dist/auto-reply/reply/directive-handling.persist.js +2 -2
  45. package/dist/auto-reply/reply/get-reply-directives-apply.js +2 -0
  46. package/dist/auto-reply/reply/get-reply-inline-actions.js +25 -1
  47. package/dist/auto-reply/reply/model-selection.js +7 -4
  48. package/dist/auto-reply/reply/session-reset-model.js +4 -4
  49. package/dist/brand.js +1 -1
  50. package/dist/build-info.json +3 -3
  51. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  52. package/dist/channel-web.js +2 -2
  53. package/dist/cli/gateway-cli/run.js +8 -0
  54. package/dist/cli/program/command-registry.js +1 -1
  55. package/dist/cli/program/register.onboard.js +2 -3
  56. package/dist/cli/program/register.wallet.js +16 -123
  57. package/dist/cli/providers-cli.js +1 -1
  58. package/dist/cli/sat-cli.js +129 -2
  59. package/dist/cli/update-cli/update-command.js +260 -11
  60. package/dist/commands/auth-choice.apply.api-providers.js +3 -3
  61. package/dist/commands/doctor-config-flow.js +1 -1
  62. package/dist/commands/doctor-legacy-config.js +2 -13
  63. package/dist/commands/model-picker.js +6 -6
  64. package/dist/commands/models/auth.js +1 -1
  65. package/dist/commands/onboard-auth.config-gateways.js +1 -1
  66. package/dist/commands/onboard-auth.credentials.js +32 -8
  67. package/dist/commands/onboard-non-interactive/local/wallet-config.js +28 -27
  68. package/dist/commands/onboard-non-interactive/local.js +5 -1
  69. package/dist/commands/wallet.js +568 -1911
  70. package/dist/config/config.js +2 -2
  71. package/dist/config/io.js +64 -2
  72. package/dist/config/schema.help.js +8 -7
  73. package/dist/config/schema.labels.js +5 -4
  74. package/dist/config/zod-schema.agent-runtime.js +1 -0
  75. package/dist/config/zod-schema.js +5 -0
  76. package/dist/control-ui/assets/{app-DLil9VO1.js → app-bR-fO5ty.js} +1511 -1555
  77. package/dist/control-ui/assets/{config-CU5Zi8Dh.js → config-LeImukCW.js} +10 -10
  78. package/dist/control-ui/assets/{federation-D10Q4eP_.js → federation-DgA9e-Pn.js} +364 -422
  79. package/dist/control-ui/assets/{index-DdE_NIXo.js → index-ckCxHwjy.js} +3 -3
  80. package/dist/control-ui/assets/{mining-BdhMn18r.js → mining-BgHFh0ei.js} +134 -114
  81. package/dist/control-ui/assets/mining-commit-DFpB_4JN.js +2 -0
  82. package/dist/control-ui/assets/{open-external-url-DwSiwMPl.js → open-external-url-ulF1B-sA.js} +3 -3
  83. package/dist/control-ui/assets/{providers-QMn4w2_f.js → providers-CIx9fxc6.js} +2 -2
  84. package/dist/control-ui/assets/{wallet-C2g294QT.js → wallet-DvStR_AT.js} +259 -377
  85. package/dist/control-ui/index.html +1 -1
  86. package/dist/control-ui/version.json +1 -1
  87. package/dist/daemon/systemd-system.js +56 -50
  88. package/dist/extensions/sat-mining/src/protocol-contract.js +43 -3
  89. package/dist/extensions/sat-mining/src/rpc-read.js +235 -67
  90. package/dist/extensions/sat-mining/src/sat-bond-layout.js +5 -2
  91. package/dist/extensions/sat-mining/src/signer-codec-manifest.js +406 -0
  92. package/dist/extensions/sat-mining/src/solana-submit.js +1038 -397
  93. package/dist/extensions/sat-mining/src/submission-ledger.js +349 -0
  94. package/dist/federation/auto-connect.js +98 -8
  95. package/dist/federation/marketplace-capability-adapter.js +33 -7
  96. package/dist/federation/marketplace-delivery-outbox.js +196 -0
  97. package/dist/federation/marketplace-delivery.js +351 -215
  98. package/dist/federation/marketplace-direct-payment.js +272 -141
  99. package/dist/federation/marketplace-escrow.js +607 -249
  100. package/dist/federation/marketplace-settlement-ledger.js +352 -0
  101. package/dist/federation/offers.js +9 -7
  102. package/dist/federation/peer-auth-v2.js +529 -0
  103. package/dist/gateway/a2a-http.js +425 -96
  104. package/dist/gateway/a2a-settlement.js +259 -153
  105. package/dist/gateway/a2a-task-store.js +524 -0
  106. package/dist/gateway/auth.js +38 -2
  107. package/dist/gateway/config-reload.js +46 -33
  108. package/dist/gateway/durable-capacity.js +31 -0
  109. package/dist/gateway/federation-http.js +540 -135
  110. package/dist/gateway/federation-marketplace-run-store.js +397 -0
  111. package/dist/gateway/federation-marketplace.js +535 -230
  112. package/dist/gateway/mutating-admin-rpc-rate-limit.js +5 -0
  113. package/dist/gateway/security-path.js +1 -1
  114. package/dist/gateway/server-http.js +1084 -1205
  115. package/dist/gateway/server-methods/models.js +1 -1
  116. package/dist/gateway/server-methods/secrets.js +40 -0
  117. package/dist/gateway/server-methods.js +1 -2
  118. package/dist/gateway/server-reload-handlers.js +196 -66
  119. package/dist/gateway/server-runtime-config.js +1 -1
  120. package/dist/gateway/server-secrets-runtime.js +288 -0
  121. package/dist/gateway/server.impl.js +73 -42
  122. package/dist/gateway/sessions-patch.js +3 -5
  123. package/dist/index.js +4 -2
  124. package/dist/infra/fasedhub.js +64 -1
  125. package/dist/infra/local-source-paired-update.js +538 -0
  126. package/dist/infra/restart.js +41 -10
  127. package/dist/infra/update-runner.js +96 -0
  128. package/dist/light-update-status.js +11 -11
  129. package/dist/memory/backend-config.js +2 -1
  130. package/dist/memory/session-files.js +16 -2
  131. package/dist/plugin-sdk/{accounts-DxblobYM.js → accounts-Brn3-uWN.js} +1 -1
  132. package/dist/plugin-sdk/{actions-D4UNimWa.js → actions-DRu135h0.js} +2 -2
  133. package/dist/plugin-sdk/{active-listener-CFuy106s.js → active-listener-B_NMtgPL.js} +1 -1
  134. package/dist/plugin-sdk/{audio-preflight-Bwi2Oq03.js → audio-preflight-BYxAt775.js} +27 -27
  135. package/dist/plugin-sdk/{audit-Cs0K7LBp.js → audit-C5d2z5U1.js} +1 -1
  136. package/dist/plugin-sdk/{channel-access-D5aHoTY8.js → channel-access-CcFRdRPO.js} +1 -1
  137. package/dist/plugin-sdk/{channel-access-configure-Dvch9mMQ.js → channel-access-configure-DaI5wevh.js} +1 -1
  138. package/dist/plugin-sdk/channel-plugin-common.d.ts +3 -3
  139. package/dist/plugin-sdk/channel-plugin-common.js +4 -4
  140. package/dist/plugin-sdk/{chunk-B0FUGc5A.js → chunk-CyoyqpbG.js} +1 -1
  141. package/dist/plugin-sdk/{chunk-items-DzT4L4Ty.js → chunk-items-DIRHG2gJ.js} +1 -1
  142. package/dist/plugin-sdk/command-status.d.ts +2 -2
  143. package/dist/plugin-sdk/command-status.js +27 -27
  144. package/dist/plugin-sdk/{commands-registry-DmDRhRAU.js → commands-registry-BfqdEWdF.js} +2 -2
  145. package/dist/plugin-sdk/{common-CfUOowOp.js → common-HCcXU-NR.js} +1 -1
  146. package/dist/plugin-sdk/{config-mxULv0fQ.js → config-DlWVeLFB.js} +7113 -6877
  147. package/dist/plugin-sdk/{config-schema-BrkmtFxB.js → config-schema-BOT7kHb6.js} +6779 -3696
  148. package/dist/plugin-sdk/device-pair.d.ts +1 -1
  149. package/dist/plugin-sdk/discord.d.ts +6 -6
  150. package/dist/plugin-sdk/discord.js +19 -19
  151. package/dist/plugin-sdk/file-lock.js +35 -4
  152. package/dist/plugin-sdk/{filesystem-manager-ZoHU8oA7.js → filesystem-manager-DoI8IBNF.js} +1 -1
  153. package/dist/plugin-sdk/{group-mentions-BHRff6j8.d.ts → group-mentions-q3hUTCt7.d.ts} +1 -1
  154. package/dist/plugin-sdk/{handle-action-DbVCF9HZ.js → handle-action-hvwapMKg.js} +13 -13
  155. package/dist/plugin-sdk/{helpers-BXbdgs5H.js → helpers-DMBU4oTo.js} +1 -1
  156. package/dist/plugin-sdk/{image-ops-D7jPxaph.js → image-ops-DQi0n_Tt.js} +1 -1
  157. package/dist/plugin-sdk/index.d.ts +21 -39
  158. package/dist/plugin-sdk/index.js +29 -29
  159. package/dist/plugin-sdk/{login-BRxww6nv.js → login-Ca583Okc.js} +3 -3
  160. package/dist/plugin-sdk/{login-qr-DJXsk9XH.js → login-qr-CGciBSNP.js} +3 -3
  161. package/dist/plugin-sdk/{manager-Y2S_5IlI.js → manager-BegWxNYX.js} +4 -4
  162. package/dist/plugin-sdk/{markdown-tables-DHLFQqaF.js → markdown-tables-DCOXEUfZ.js} +1 -1
  163. package/dist/plugin-sdk/{media-Czo4carg.js → media-CAdQF5TG.js} +47 -47
  164. package/dist/plugin-sdk/{memory-search-DTjIMw8J.js → memory-search-CrFPOeTH.js} +4 -1
  165. package/dist/plugin-sdk/{monitor-M_ta2lMc.js → monitor-BCFDaqJ-.js} +31 -31
  166. package/dist/plugin-sdk/{monitor-wYVncM9w.js → monitor-BgbjRiPM.js} +32 -32
  167. package/dist/plugin-sdk/{openresponses-http-B2GmeSt4.js → openresponses-http-DFf87SMM.js} +27 -27
  168. package/dist/plugin-sdk/{optional-runtime-dependency-DdMbxS24.js → optional-runtime-dependency-0dznm3ZO.js} +1 -1
  169. package/dist/plugin-sdk/{outbound-Db8mRN6P.js → outbound-BgVDdHxx.js} +6 -6
  170. package/dist/plugin-sdk/{outbound-attachment-BqFjcypb.js → outbound-attachment-QqTnIhZL.js} +2 -2
  171. package/dist/plugin-sdk/{pairing-message-B3bHYGaw.d.ts → pairing-message-CS-9BHug.d.ts} +2 -2
  172. package/dist/plugin-sdk/{pairing-message-B3GC_7CD.js → pairing-message-D0CoLHRJ.js} +1 -1
  173. package/dist/plugin-sdk/{pairing-store-CQikgIbw.js → pairing-store-BChahgI2.js} +2 -2
  174. package/dist/plugin-sdk/{pi-model-discovery-CY3m5zJP.js → pi-model-discovery-BX_JLcEJ.js} +1 -1
  175. package/dist/plugin-sdk/{pi-model-discovery-runtime-DSh1T2pn.js → pi-model-discovery-runtime-DxTMz_XF.js} +2 -2
  176. package/dist/plugin-sdk/provider-web-search-config-contract.d.ts +2 -2
  177. package/dist/plugin-sdk/{pw-ai-AFUqn-tQ.js → pw-ai-natjh3rm.js} +3 -3
  178. package/dist/plugin-sdk/{qmd-manager-DSyx77KY.js → qmd-manager-CgRaTxUq.js} +1 -1
  179. package/dist/plugin-sdk/{query-expansion-CAI2kKLQ.js → query-expansion-BG3FgzF7.js} +14 -3
  180. package/dist/plugin-sdk/{replies-Fzh9PhXl.js → replies-ChYo5ZQM.js} +2 -2
  181. package/dist/plugin-sdk/{resolve-outbound-target-KpF3co6T.js → resolve-outbound-target-D-KGgHJZ.js} +1 -1
  182. package/dist/plugin-sdk/{resolve-route-CDl74O19.js → resolve-route-C8tvQm8Z.js} +1 -1
  183. package/dist/plugin-sdk/{retry-policy-D4uiqUOG.js → retry-policy-C6SjKjQA.js} +1 -1
  184. package/dist/plugin-sdk/{runtime-Bb6KRnCC.js → runtime-2B9_WKoI.js} +8 -3
  185. package/dist/plugin-sdk/{runtime-dependency-DLDmB94f.js → runtime-dependency-DczkvPxn.js} +2 -2
  186. package/dist/plugin-sdk/sat-runtime.d.ts +549 -309
  187. package/dist/plugin-sdk/sat-runtime.js +29 -29
  188. package/dist/plugin-sdk/{send-C4k4JuJN.js → send-B6KkGKZD.js} +6 -6
  189. package/dist/plugin-sdk/{send-D-uDKGIq.js → send-CFzGKJXo.js} +6 -6
  190. package/dist/plugin-sdk/{send-Du8ykP1k.js → send-DCO441U7.js} +8 -8
  191. package/dist/plugin-sdk/{send-5nFktR1n.js → send-gDoGA-7e.js} +5 -5
  192. package/dist/plugin-sdk/{send-Bfzf11VY.js → send-q9HXrSwK.js} +7 -7
  193. package/dist/plugin-sdk/{session-CS-Td9EM.js → session-DHD4ZKyG.js} +2 -2
  194. package/dist/plugin-sdk/{skill-commands-CFAlXWHk.js → skill-commands-FKEPLS7T.js} +201 -26
  195. package/dist/plugin-sdk/{slack-CxE1mfzj.js → slack-BUTt8cxK.js} +39 -39
  196. package/dist/plugin-sdk/{slack-actions-BcMkNv1Z.js → slack-actions-DhbMcp8M.js} +11 -11
  197. package/dist/plugin-sdk/slack.d.ts +6 -6
  198. package/dist/plugin-sdk/slack.js +9 -9
  199. package/dist/plugin-sdk/{sticker-cache-CGzGWu00.js → sticker-cache-D0wHVqjt.js} +3 -3
  200. package/dist/plugin-sdk/{tables-bFXyRpkx.js → tables-B3jr0Pvq.js} +1 -1
  201. package/dist/plugin-sdk/{targets-DH7a9lKB.js → targets-CtSbTxYX.js} +1 -1
  202. package/dist/plugin-sdk/{telegram-actions-DaEcYQ8r.js → telegram-actions-CC_qN4v0.js} +30 -30
  203. package/dist/plugin-sdk/telegram.d.ts +6 -6
  204. package/dist/plugin-sdk/telegram.js +8 -8
  205. package/dist/plugin-sdk/{thread-bindings.lifecycle-CgOvvdw8.js → thread-bindings.lifecycle-QbkNl-m6.js} +3 -3
  206. package/dist/plugin-sdk/{thread-bindings.state-CQpk44Hq.js → thread-bindings.state-D9hg_M1c.js} +2 -2
  207. package/dist/plugin-sdk/tmp-fased-dir-xOTGlzBE.d.ts +486 -0
  208. package/dist/plugin-sdk/{types-vcJrC_uv.d.ts → types-C3avNytw.d.ts} +3 -4
  209. package/dist/plugin-sdk/{types-DyhXnYUr.d.ts → types-D0kDG8l0.d.ts} +3 -2
  210. package/dist/plugin-sdk/{types-DFAW-n1J.d.ts → types-DWfbSAaM.d.ts} +2 -2
  211. package/dist/plugin-sdk/{types.plugin-_fbu40Re.d.ts → types.plugin-BcSBq42x.d.ts} +8 -3
  212. package/dist/plugin-sdk/{web-CInY-lCv.js → web-BXsJ1vqe.js} +32 -32
  213. package/dist/plugin-sdk/{whatsapp-actions-BtSc2CBv.js → whatsapp-actions-Bj_rOncr.js} +13 -13
  214. package/dist/plugin-sdk/{whatsapp-heartbeat-Bg9TuYzl.js → whatsapp-heartbeat-B1WHkJIg.js} +2 -2
  215. package/dist/plugin-sdk/whatsapp.d.ts +13 -13
  216. package/dist/plugin-sdk/whatsapp.js +12 -12
  217. package/dist/plugin-sdk/{wsl-C4DGFkOd.js → wsl-J-HscWxS.js} +3 -3
  218. package/dist/plugin-sdk/{zod-schema.providers-core-BEbNizPe.d.ts → zod-schema.providers-core-CIzCjXH9.d.ts} +61 -61
  219. package/dist/providers/refresh.js +1 -1
  220. package/dist/providers/registry.js +1 -1
  221. package/dist/secrets/apply.js +1 -1
  222. package/dist/secrets/runtime.js +1 -1
  223. package/dist/wallet/external-submission-ledger.js +376 -0
  224. package/dist/wallet/legacy-embedded-keystore.js +40 -0
  225. package/dist/wallet/local-socket-signer-archive.js +30 -0
  226. package/dist/wallet/local-socket-signer-lifecycle.js +97 -0
  227. package/dist/wallet/local-socket-signer-policy.js +166 -0
  228. package/dist/wallet/local-socket-signer-protocol.js +561 -118
  229. package/dist/wallet/native-signer-wallet-id.js +25 -0
  230. package/dist/wallet/providers/alchemy-adapter.js +3 -0
  231. package/dist/wallet/providers/local-socket-signer-adapter.js +380 -105
  232. package/dist/wallet/providers/turnkey-adapter.js +292 -34
  233. package/dist/wallet/providers/wallet-standard-adapter.js +100 -0
  234. package/dist/wallet/signer-network-admin.js +184 -0
  235. package/dist/wallet/signer-protocol-v2.generated.js +90 -0
  236. package/dist/wallet/solana-bond-signing.js +157 -69
  237. package/dist/wallet/solana-reviewed-transaction.js +375 -0
  238. package/dist/wallet/solana-swap.js +520 -64
  239. package/dist/wallet/solana-transaction-inspection.js +106 -4
  240. package/dist/wallet/solana-trigger.js +658 -320
  241. package/dist/wallet/turnkey-reviewed-state.js +370 -0
  242. package/dist/wallet/wallet-approval-auth.js +17 -28
  243. package/dist/wallet/wallet-atomic-state.js +53 -0
  244. package/dist/wallet/wallet-canary.js +0 -1
  245. package/dist/wallet/wallet-inbound-events.js +16 -11
  246. package/dist/wallet/wallet-policy-simulation.js +1 -1
  247. package/dist/wallet/wallet-policy.js +90 -36
  248. package/dist/wallet/wallet-provider-capabilities.js +28 -4
  249. package/dist/wallet/wallet-provider-registry.js +57 -44
  250. package/dist/wallet/wallet-provider-resolver.js +62 -63
  251. package/dist/wallet/wallet-runtime-config.js +28 -41
  252. package/dist/wallet/wallet-secrets-store.js +22 -0
  253. package/dist/wallet/wallet-send-approvals.js +1891 -589
  254. package/dist/wallet/wallet-send-execution-ledger.js +292 -0
  255. package/dist/wallet/wallet-settlement-links.js +14 -13
  256. package/dist/wallet/wallet-standard-review.js +353 -0
  257. package/dist/wallet/wallet-status.js +0 -9
  258. package/dist/wallet/wallet-task-ledger.js +16 -2
  259. package/dist/web/auto-reply/heartbeat-runner.js +1 -1
  260. package/dist/web/auto-reply/monitor.js +2 -2
  261. package/dist/wizard/host-security-capability.js +14 -23
  262. package/dist/wizard/onboarding.finalize.js +63 -184
  263. package/dist/wizard/onboarding.host-security.js +117 -1367
  264. package/dist/wizard/onboarding.js +172 -217
  265. package/dist/wizard/onboarding.wallet.js +232 -785
  266. package/extensions/acpx/package.json +1 -1
  267. package/extensions/bluebubbles/package.json +1 -1
  268. package/extensions/copilot-proxy/package.json +1 -1
  269. package/extensions/diagnostics-otel/package.json +1 -1
  270. package/extensions/fased-federation/package.json +1 -1
  271. package/extensions/google-gemini-cli-auth/package.json +1 -1
  272. package/extensions/imessage/package.json +1 -1
  273. package/extensions/irc/package.json +1 -1
  274. package/extensions/line/package.json +1 -1
  275. package/extensions/llm-task/package.json +1 -1
  276. package/extensions/lobster/package.json +1 -1
  277. package/extensions/matrix/CHANGELOG.md +12 -0
  278. package/extensions/matrix/package.json +1 -1
  279. package/extensions/mattermost/package.json +1 -1
  280. package/extensions/memory-core/package.json +2 -2
  281. package/extensions/memory-lancedb/package.json +1 -1
  282. package/extensions/minimax-portal-auth/package.json +1 -1
  283. package/extensions/msteams/CHANGELOG.md +12 -0
  284. package/extensions/msteams/package.json +1 -1
  285. package/extensions/nextcloud-talk/package.json +1 -1
  286. package/extensions/nostr/CHANGELOG.md +12 -0
  287. package/extensions/nostr/package.json +1 -1
  288. package/extensions/open-prose/package.json +1 -1
  289. package/extensions/sat-mining/index.ts +498 -153
  290. package/extensions/sat-mining/package.json +1 -1
  291. package/extensions/sat-mining/signer-codec-schema.v1.json +374 -0
  292. package/extensions/sat-mining/src/audit-store.ts +50 -5
  293. package/extensions/sat-mining/src/claim-service.ts +85 -14
  294. package/extensions/sat-mining/src/config.ts +9 -0
  295. package/extensions/sat-mining/src/cycle-progress.ts +16 -8
  296. package/extensions/sat-mining/src/epoch-service.ts +9 -5
  297. package/extensions/sat-mining/src/gateway-runner.ts +21 -1
  298. package/extensions/sat-mining/src/protocol-contract.ts +45 -1
  299. package/extensions/sat-mining/src/recovery-service.ts +312 -0
  300. package/extensions/sat-mining/src/round-watcher.ts +391 -81
  301. package/extensions/sat-mining/src/rpc-read.ts +330 -89
  302. package/extensions/sat-mining/src/runtime.ts +14 -0
  303. package/extensions/sat-mining/src/sat-bond-layout.ts +6 -0
  304. package/extensions/sat-mining/src/signer-codec-manifest.ts +429 -0
  305. package/extensions/sat-mining/src/solana-submit.ts +1199 -428
  306. package/extensions/sat-mining/src/submission-ledger.ts +433 -0
  307. package/extensions/signal/package.json +1 -1
  308. package/extensions/synology-chat/package.json +1 -1
  309. package/extensions/tlon/package.json +1 -1
  310. package/extensions/twitch/CHANGELOG.md +12 -0
  311. package/extensions/twitch/package.json +1 -1
  312. package/extensions/voice-call/CHANGELOG.md +12 -0
  313. package/extensions/voice-call/package.json +1 -1
  314. package/extensions/zalo/CHANGELOG.md +12 -0
  315. package/extensions/zalo/package.json +1 -1
  316. package/extensions/zalouser/CHANGELOG.md +12 -0
  317. package/extensions/zalouser/package.json +1 -1
  318. package/install.sh +4955 -0
  319. package/package.json +23 -5
  320. package/scripts/fased-host-updater.mjs +1421 -0
  321. package/scripts/fased-host-updaterctl.mjs +186 -0
  322. package/scripts/fased-managed-service.sh +36 -0
  323. package/scripts/fased-managed-updater.mjs +3065 -156
  324. package/scripts/fased-signer-enroll-hosting.sh +275 -0
  325. package/scripts/fased-signer-network-hosting.sh +143 -0
  326. package/scripts/fased-signer-owner-policy.mjs +1430 -0
  327. package/scripts/fased-signer-policy-hosting.sh +50 -0
  328. package/scripts/fased-signer-policy-local.sh +26 -0
  329. package/scripts/hosted-release-manifest.mjs +185 -0
  330. package/scripts/install-fased-signerd.sh +183 -108
  331. package/scripts/install-managed-runtime.mjs +212 -10
  332. package/scripts/managed-runtime-layout.mjs +75 -3
  333. package/scripts/migrate-hosted-signer-v2.mjs +58 -0
  334. package/scripts/start-managed.sh +169 -360
  335. package/dist/control-ui/assets/mining-commit-R-6nu87p.js +0 -2
  336. package/dist/plugin-sdk/tmp-fased-dir-CS6zsYEE.d.ts +0 -77
  337. package/dist/wallet/local-socket-signer-broker.js +0 -224
  338. package/dist/wallet/local-socket-signer-custody.js +0 -42
  339. package/dist/wallet/providers/embedded-keystore-adapter.js +0 -402
  340. package/dist/wallet/providers/privy-adapter.js +0 -80
  341. package/dist/wallet/wallet-custody.js +0 -1836
@@ -1,9 +1,11 @@
1
- import { loadConfig, readConfigFileSnapshotForWrite, writeConfigFile } from "../config/io.js";
1
+ import { loadConfig, readConfigFileSnapshotForWrite, updateConfigFile, writeConfigFile } from "../config/io.js";
2
2
  import "../config/config.js";
3
3
  import { deleteManualFederationOfferConfig, deleteMarketplaceOrderConfig, deleteMarketplaceRequestConfig, listLocalFederationOffers, listLocalMarketplaceOrders, listLocalMarketplaceRequests, resolveOfferPaymentDefaults, upsertManualFederationOfferConfig, upsertMarketplaceOrderConfig, upsertMarketplaceRequestConfig, validateMarketplaceDeliveryTargetConfig } from "../federation/offers.js";
4
4
  import { resolveAgentPublicOrigin, resolveFederationBaseUrl, resolveFederationHandle } from "../federation/runtime.js";
5
5
  import { loadFederationBearerToken, persistFederationAccessToken } from "../federation/access-token.js";
6
+ import { readResponseWithLimit } from "../media/read-response-with-limit.js";
6
7
  import { buildAttestation } from "../federation/attestation.js";
8
+ import { FEDERATION_MARKETPLACE_DELIVERY_PATH, FEDERATION_MARKETPLACE_ORDER_PATH, authorizeFederationPeerRequestV2, buildSignedFederationPeerRequest, checkFederationPeerIngressBudget, isTrustedFederationPeerUrl, reserveAuthorizedFederationPeerRequest } from "../federation/peer-auth-v2.js";
7
9
  import { runMarketplaceCapabilityAdapter } from "../federation/marketplace-capability-adapter.js";
8
10
  import { deliverMarketplaceContentSummarizeResult } from "../federation/marketplace-delivery.js";
9
11
  import { payMarketplaceOrderDirect } from "../federation/marketplace-direct-payment.js";
@@ -11,6 +13,7 @@ import { cancelMarketplaceSolanaEscrow, fundMarketplaceSolanaEscrow, refundMarke
11
13
  import os from "node:os";
12
14
  import path from "node:path";
13
15
  import { readFile, readdir } from "node:fs/promises";
16
+ import crypto from "node:crypto";
14
17
 
15
18
  //#region src/gateway/federation-http.ts
16
19
  const OPERATOR_ECONOMY_FEE_LANES = [
@@ -20,6 +23,8 @@ const OPERATOR_ECONOMY_FEE_LANES = [
20
23
  "routing"
21
24
  ];
22
25
  const DEFAULT_OPERATOR_ECONOMY_DISABLED_REASON = "fee collection is disabled until the multi-day measurement history threshold is met";
26
+ const FEDERATION_PEER_RESPONSE_MAX_BYTES = 512 * 1024;
27
+ const FEDERATION_PEER_REQUEST_TIMEOUT_MS = 1e4;
23
28
  function pathLooksSimulated(candidate) {
24
29
  return typeof candidate === "string" && /operator-economy-simulated|simulated/u.test(candidate);
25
30
  }
@@ -64,6 +69,16 @@ function sendJson(res, status, body) {
64
69
  res.setHeader("Content-Type", "application/json; charset=utf-8");
65
70
  res.end(JSON.stringify(body));
66
71
  }
72
+ function sendPeerAuthRejection(res, result) {
73
+ if (result.retryAfterMs && result.retryAfterMs > 0) {
74
+ res.setHeader("Retry-After", String(Math.max(1, Math.ceil(result.retryAfterMs / 1e3))));
75
+ }
76
+ sendJson(res, result.statusCode, {
77
+ status: "rejected",
78
+ code: result.code,
79
+ reason: result.reason
80
+ });
81
+ }
67
82
  function asObject(value) {
68
83
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
69
84
  }
@@ -136,6 +151,18 @@ function safeConfigIdSegment(value) {
136
151
  function safeMarketplaceOrderIdSegment(value) {
137
152
  return safeConfigIdSegment(value).replace(/[._:]+/gu, "-").replace(/^-+|-+$/gu, "").slice(0, 96);
138
153
  }
154
+ function collisionResistantInboundOrderId(params) {
155
+ const senderHandle = normalizeComparableValue(params.senderHandle);
156
+ const remoteOrderId = params.remoteOrderId.trim();
157
+ const senderSegment = safeMarketplaceOrderIdSegment(senderHandle).slice(0, 40) || "federation";
158
+ const orderSegment = safeMarketplaceOrderIdSegment(remoteOrderId).slice(0, 40) || "order";
159
+ const digest = crypto.createHash("sha256").update(JSON.stringify([
160
+ params.kind,
161
+ senderHandle,
162
+ remoteOrderId
163
+ ]), "utf8").digest("hex").slice(0, 32);
164
+ return `inbound-${params.kind}-${senderSegment}-${orderSegment}-${digest}`;
165
+ }
139
166
  function toPositiveInteger(value) {
140
167
  const numeric = Number(value);
141
168
  return Number.isFinite(numeric) && numeric > 0 ? Math.floor(numeric) : 0;
@@ -444,12 +471,12 @@ async function readJsonBody(req, maxBytes) {
444
471
  });
445
472
  });
446
473
  }
447
- function resolveForwardedAuthorization(req, apiToken) {
448
- const rawHeader = typeof req.headers.authorization === "string" ? req.headers.authorization.trim() : "";
449
- if (rawHeader) {
450
- return rawHeader;
474
+ function resolveFederationAuthorization(apiToken) {
475
+ const token = apiToken?.trim() ?? "";
476
+ if (!token) {
477
+ return undefined;
451
478
  }
452
- return apiToken?.trim() ? `Bearer ${apiToken.trim()}` : undefined;
479
+ return token.toLowerCase().startsWith("bearer ") ? token : `Bearer ${token}`;
453
480
  }
454
481
  function resolveLocalOffersOrigin(req) {
455
482
  const configured = resolveAgentPublicOrigin(process.env).trim();
@@ -636,6 +663,12 @@ function validateMarketplaceOrderBody(value) {
636
663
  function normalizeComparableValue(value) {
637
664
  return String(value ?? "").trim().toLowerCase();
638
665
  }
666
+ function findCorrelatedMarketplaceBuyerOrder(params) {
667
+ const sellerHandle = normalizeComparableValue(params.sellerHandle);
668
+ const sellerOrderId = params.sellerOrderId.trim();
669
+ const serviceKind = normalizeComparableValue(params.serviceKind);
670
+ return listLocalMarketplaceOrders(params.config).find((entry) => entry.source === "local" && normalizeComparableValue(entry.order.sellerHandle) === sellerHandle && entry.order.sellerOrderId?.trim() === sellerOrderId && normalizeComparableValue(entry.order.serviceKind) === serviceKind) ?? null;
671
+ }
639
672
  function pricingMatches(expected, actual) {
640
673
  const expectedCurrency = normalizeComparableValue(expected?.currency || "USDC");
641
674
  const actualCurrency = normalizeComparableValue(actual?.currency || "USDC");
@@ -692,6 +725,14 @@ function validateSellerMarketplaceOrderIntake(params) {
692
725
  reason: "seller cannot intake an order from itself"
693
726
  };
694
727
  }
728
+ const requestedBuyer = params.order.buyerHandle?.trim() || "";
729
+ if (requestedBuyer && normalizeComparableValue(requestedBuyer) !== normalizeComparableValue(params.senderHandle)) {
730
+ return {
731
+ ok: false,
732
+ status: 409,
733
+ reason: "order buyerHandle does not match signed sender"
734
+ };
735
+ }
695
736
  const requestedSeller = params.order.sellerHandle?.trim() || "";
696
737
  if (requestedSeller && normalizeComparableValue(requestedSeller) !== normalizeComparableValue(params.localHandle)) {
697
738
  return {
@@ -721,6 +762,38 @@ function validateSellerMarketplaceOrderIntake(params) {
721
762
  reason: "local seller offer not found"
722
763
  };
723
764
  }
765
+ const requiredTier = normalizeComparableValue(localOffer.offer.requiredTrustOrBondTier || "verified");
766
+ const peerTier = params.peerBondTier ?? "none";
767
+ const peerTierRank = peerTier === "operator-bond" ? 2 : peerTier === "basic-bond" ? 1 : 0;
768
+ const requiredTierRank = requiredTier === "verified" || requiredTier === "none" ? 0 : requiredTier === "basic-bond" ? 1 : requiredTier === "operator-bond" ? 2 : -1;
769
+ if (requiredTierRank < 0) {
770
+ return {
771
+ ok: false,
772
+ status: 409,
773
+ reason: `local seller offer requires unsupported trust tier ${localOffer.offer.requiredTrustOrBondTier}`
774
+ };
775
+ }
776
+ if (peerTierRank < requiredTierRank) {
777
+ return {
778
+ ok: false,
779
+ status: 403,
780
+ reason: `seller offer requires ${requiredTier}; verified peer has ${peerTier}`
781
+ };
782
+ }
783
+ if (localOffer.offer.automation?.allowed === true && localOffer.offer.automation.humanApprovalRequired !== true) {
784
+ return {
785
+ ok: false,
786
+ status: 409,
787
+ reason: "seller offer requests unreviewed automation, which is unavailable until its policy is enforced"
788
+ };
789
+ }
790
+ if (!localOffer.offer.paymentDefaults?.payee.address.trim()) {
791
+ return {
792
+ ok: false,
793
+ status: 409,
794
+ reason: "local seller offer has no wallet-backed payment destination"
795
+ };
796
+ }
724
797
  if (normalizeComparableValue(localOffer.offer.visibility) === "private") {
725
798
  return {
726
799
  ok: false,
@@ -771,59 +844,87 @@ function validateSellerMarketplaceOrderIntake(params) {
771
844
  }
772
845
  function buildSellerMarketplaceOrderInput(params) {
773
846
  const now = new Date().toISOString();
774
- const delivery = { ...params.order.delivery };
775
- delete delivery.target;
776
- const pricing = params.offer.offer.pricing ?? params.order.pricing;
777
- const currency = pricing?.currency || params.order.paymentIntent?.currency || "USDC";
847
+ const pricing = params.offer.offer.pricing;
848
+ const paymentDefaults = params.offer.offer.paymentDefaults;
849
+ const currency = pricing.currency || paymentDefaults?.currency || "USDC";
850
+ const paymentChain = paymentDefaults?.chain ?? paymentDefaults?.payee?.chain;
778
851
  return {
779
- ...params.order,
780
852
  id: params.inboundOrderId,
781
853
  source: "federation",
782
- status: params.order.status === "delivered" ? "delivered" : params.order.paymentIntent?.status === "verified" || params.order.settlement?.status === "settled" || params.order.settlement?.status === "verified" ? "running" : "accepted",
854
+ status: "accepted",
783
855
  offerId: params.offer.offer.id,
856
+ ...params.order.requestId?.trim() ? { requestId: params.order.requestId.trim() } : {},
784
857
  buyerHandle: params.buyerHandle,
785
858
  sellerHandle: params.localHandle,
859
+ peerNodeId: params.peerNodeId,
860
+ peerRemoteOrderId: params.peerRemoteOrderId,
861
+ peerRequestDigest: params.peerRequestDigest,
786
862
  sellerOrderId: params.inboundOrderId,
787
863
  sellerSyncStatus: "accepted",
788
864
  sellerSyncedAt: now,
789
- sellerAcceptedAt: params.order.sellerAcceptedAt ?? now,
865
+ sellerAcceptedAt: now,
790
866
  serviceKind: params.offer.offer.serviceKind,
791
867
  title: params.offer.offer.title,
792
868
  pricing,
793
- fulfillmentMode: params.offer.offer.fulfillmentMode ?? params.order.fulfillmentMode,
794
- receiptRules: params.offer.offer.receiptRules ?? params.order.receiptRules,
869
+ fulfillmentMode: params.offer.offer.fulfillmentMode === "human" ? "human" : "agent-approval",
870
+ receiptRules: params.offer.offer.receiptRules,
795
871
  paymentIntent: {
796
- ...params.order.paymentIntent,
797
- status: params.order.paymentIntent?.status === "verified" || params.order.paymentIntent?.status === "submitted" ? params.order.paymentIntent.status : "requires_payment",
798
- amount: pricing?.amount ?? params.order.paymentIntent?.amount,
872
+ status: "requires_payment",
873
+ amount: pricing.amount,
799
874
  currency,
800
- unit: pricing?.unit ?? params.order.paymentIntent?.unit,
801
- method: params.order.paymentIntent?.method ?? "agent-wallet",
802
- acceptedAssets: params.order.paymentIntent?.acceptedAssets ?? params.offer.offer.acceptedAssets ?? params.offer.offer.paymentRails ?? [currency],
875
+ unit: pricing.unit,
876
+ method: "agent-wallet",
877
+ chain: paymentChain,
878
+ assetKind: paymentDefaults?.asset?.kind,
879
+ assetAddress: paymentDefaults?.asset?.address,
880
+ assetDecimals: paymentDefaults?.assetDecimals,
881
+ acceptedAssets: params.offer.offer.acceptedAssets ?? params.offer.offer.paymentRails ?? [currency],
803
882
  payeeHandle: params.localHandle,
804
- payeeAddress: params.order.paymentIntent?.payeeAddress ?? params.offer.offer.paymentDefaults?.payee?.address
883
+ payeeAddress: paymentDefaults?.payee.address
805
884
  },
806
885
  settlement: {
807
- ...params.order.settlement,
808
- status: params.order.settlement?.status === "verified" || params.order.settlement?.status === "submitted" || params.order.settlement?.status === "settled" ? params.order.settlement.status : "requires_payment",
809
- amount: pricing?.amount ?? params.order.settlement?.amount,
886
+ mode: "direct",
887
+ status: "requires_payment",
888
+ amount: pricing.amount,
810
889
  currency,
811
- payeeAddress: params.order.settlement?.payeeAddress ?? params.offer.offer.paymentDefaults?.payee?.address,
812
- notes: "Seller intake accepted from remote buyer checkout."
890
+ chain: paymentChain,
891
+ assetKind: paymentDefaults?.asset?.kind,
892
+ assetAddress: paymentDefaults?.asset?.address,
893
+ assetDecimals: paymentDefaults?.assetDecimals,
894
+ payeeAddress: paymentDefaults?.payee.address,
895
+ escrow: {
896
+ status: "not_applicable",
897
+ holdPolicy: "none",
898
+ releaseRequired: false,
899
+ updatedAt: now
900
+ },
901
+ notes: "Peer-reported payment evidence awaits independent local chain verification."
813
902
  },
814
903
  delivery: {
815
- ...delivery,
816
- status: delivery.status ?? "pending",
817
- targetKind: delivery.targetKind ?? "federation",
818
- targetStatus: delivery.targetStatus ?? "ready",
819
- targetLabel: delivery.targetLabel ?? "Buyer delivery target",
820
- targetMasked: delivery.targetMasked ?? params.buyerHandle,
904
+ status: "pending",
905
+ fulfillmentMode: params.offer.offer.fulfillmentMode === "human" ? "human" : "agent-approval",
906
+ inputShape: params.offer.offer.inputShape,
907
+ deliveryShape: params.offer.offer.deliveryShape,
908
+ target: {
909
+ targetId: `${params.inboundOrderId}-buyer-federation`,
910
+ source: "order",
911
+ owner: "buyer",
912
+ kind: "federation",
913
+ status: "ready",
914
+ label: "Buyer federation node",
915
+ descriptor: params.buyerHandle,
916
+ maskedTarget: params.buyerHandle,
917
+ scope: {
918
+ orderId: params.inboundOrderId,
919
+ serviceKind: params.offer.offer.serviceKind
920
+ },
921
+ federation: { handle: params.buyerHandle }
922
+ },
821
923
  updatedAt: now
822
924
  },
823
925
  receipt: {
824
- ...params.order.receipt,
825
- status: params.order.receipt?.status ?? "pending",
826
- notes: params.order.receipt?.notes ?? "Awaiting payment proof and delivery evidence."
926
+ status: "pending",
927
+ notes: "Awaiting independently verified payment proof and delivery evidence."
827
928
  }
828
929
  };
829
930
  }
@@ -833,7 +934,8 @@ function buildSellerMarketplaceOrderEndpoint(endpoint) {
833
934
  return null;
834
935
  }
835
936
  try {
836
- return new URL("/api/federation/marketplace/orders", raw);
937
+ const url = new URL("/api/federation/marketplace/orders", raw);
938
+ return isTrustedFederationPeerUrl(url) ? url : null;
837
939
  } catch {
838
940
  return null;
839
941
  }
@@ -849,10 +951,13 @@ async function forwardRequest(params) {
849
951
  const url = new URL(req.url ?? "/", "http://localhost");
850
952
  const targetPath = overridePath ?? url.pathname + url.search;
851
953
  const target = new URL(targetPath, baseUrl);
954
+ if (!isTrustedFederationPeerUrl(target)) {
955
+ throw new Error("federation upstream must use HTTPS or an explicit loopback HTTP URL");
956
+ }
852
957
  const headers = new Headers();
853
- const authorization = resolveForwardedAuthorization(req, apiToken) || (fallbackFederationToken ? await loadFederationBearerToken(process.env) : "");
958
+ const authorization = resolveFederationAuthorization(apiToken) || (fallbackFederationToken ? resolveFederationAuthorization(await loadFederationBearerToken(process.env)) : undefined);
854
959
  if (authorization) {
855
- headers.set("Authorization", authorization.toLowerCase().startsWith("bearer ") ? authorization : `Bearer ${authorization}`);
960
+ headers.set("Authorization", authorization);
856
961
  }
857
962
  let bodyPayload;
858
963
  if (body !== undefined) {
@@ -862,9 +967,11 @@ async function forwardRequest(params) {
862
967
  const upstream = await fetch(target, {
863
968
  method: req.method,
864
969
  headers,
865
- body: bodyPayload
970
+ body: bodyPayload,
971
+ redirect: "error",
972
+ signal: AbortSignal.timeout(FEDERATION_PEER_REQUEST_TIMEOUT_MS)
866
973
  });
867
- const text = await upstream.text();
974
+ const text = (await readResponseWithLimit(upstream, FEDERATION_PEER_RESPONSE_MAX_BYTES, { onOverflow: () => new Error("federation upstream response exceeded the size limit") })).toString("utf8");
868
975
  if (upstream.ok && persistFederationToken) {
869
976
  await persistFederationTokenResponse(text);
870
977
  }
@@ -879,20 +986,25 @@ async function forwardOperatorEconomyFeeRequest(params) {
879
986
  const { req, res, baseUrl, apiToken, fallbackFederationToken } = params;
880
987
  const url = new URL(req.url ?? "/", "http://localhost");
881
988
  const target = new URL(url.pathname + url.search, baseUrl);
989
+ if (!isTrustedFederationPeerUrl(target)) {
990
+ throw new Error("federation upstream must use HTTPS or an explicit loopback HTTP URL");
991
+ }
882
992
  const headers = new Headers();
883
- const authorization = resolveForwardedAuthorization(req, apiToken) || (fallbackFederationToken ? await loadFederationBearerToken(process.env) : "");
993
+ const authorization = resolveFederationAuthorization(apiToken) || (fallbackFederationToken ? resolveFederationAuthorization(await loadFederationBearerToken(process.env)) : undefined);
884
994
  if (authorization) {
885
- headers.set("Authorization", authorization.toLowerCase().startsWith("bearer ") ? authorization : `Bearer ${authorization}`);
995
+ headers.set("Authorization", authorization);
886
996
  }
887
997
  try {
888
998
  const upstream = await fetch(target, {
889
999
  method: req.method,
890
- headers
1000
+ headers,
1001
+ redirect: "error",
1002
+ signal: AbortSignal.timeout(FEDERATION_PEER_REQUEST_TIMEOUT_MS)
891
1003
  });
892
1004
  if (upstream.status === 404 && await tryServeLocalOperatorEconomyFeeFallback(req, res)) {
893
1005
  return;
894
1006
  }
895
- const text = await upstream.text();
1007
+ const text = (await readResponseWithLimit(upstream, FEDERATION_PEER_RESPONSE_MAX_BYTES, { onOverflow: () => new Error("federation upstream response exceeded the size limit") })).toString("utf8");
896
1008
  res.statusCode = upstream.status;
897
1009
  const contentType = upstream.headers.get("content-type");
898
1010
  if (contentType) {
@@ -1394,7 +1506,7 @@ async function handleFederationHttpRequest(req, res, opts) {
1394
1506
  if (!endpoint) {
1395
1507
  sendJson(res, 400, {
1396
1508
  status: "rejected",
1397
- reason: "seller endpoint is required for marketplace order intake"
1509
+ reason: "seller endpoint must use HTTPS (plain HTTP is allowed only for an explicit loopback URL)"
1398
1510
  });
1399
1511
  return true;
1400
1512
  }
@@ -1403,6 +1515,14 @@ async function handleFederationHttpRequest(req, res, opts) {
1403
1515
  env: process.env,
1404
1516
  fallbackDomain: federationBase ? new URL(federationBase).hostname : "localhost"
1405
1517
  });
1518
+ const sellerHandle = currentOrder.order.sellerHandle?.trim() || "";
1519
+ if (!sellerHandle) {
1520
+ sendJson(res, 409, {
1521
+ status: "rejected",
1522
+ reason: "seller handle is required for signed marketplace order intake"
1523
+ });
1524
+ return true;
1525
+ }
1406
1526
  const orderPayload = {
1407
1527
  ...currentOrder.order,
1408
1528
  delivery: {
@@ -1411,15 +1531,24 @@ async function handleFederationHttpRequest(req, res, opts) {
1411
1531
  }
1412
1532
  };
1413
1533
  try {
1534
+ const signedRequest = buildSignedFederationPeerRequest({
1535
+ senderHandle: localHandle,
1536
+ recipientHandle: sellerHandle,
1537
+ path: FEDERATION_MARKETPLACE_ORDER_PATH,
1538
+ body: orderPayload,
1539
+ env: process.env
1540
+ });
1414
1541
  const upstream = await fetch(endpoint, {
1415
1542
  method: "POST",
1416
1543
  headers: {
1417
1544
  "Content-Type": "application/json",
1418
- "x-fased-sender-handle": localHandle
1545
+ ...signedRequest.headers
1419
1546
  },
1420
- body: JSON.stringify(orderPayload)
1547
+ body: signedRequest.body,
1548
+ redirect: "error",
1549
+ signal: AbortSignal.timeout(FEDERATION_PEER_REQUEST_TIMEOUT_MS)
1421
1550
  });
1422
- const text = await upstream.text();
1551
+ const text = (await readResponseWithLimit(upstream, FEDERATION_PEER_RESPONSE_MAX_BYTES, { onOverflow: () => new Error("seller intake response exceeded the size limit") })).toString("utf8");
1423
1552
  let upstreamBody = text;
1424
1553
  if (text.trim()) {
1425
1554
  try {
@@ -1635,7 +1764,8 @@ async function handleFederationHttpRequest(req, res, opts) {
1635
1764
  const delivered = await deliverMarketplaceContentSummarizeResult({
1636
1765
  config: snapshot.config,
1637
1766
  orderId,
1638
- result: resultPayload
1767
+ result: resultPayload,
1768
+ deps: opts.marketplaceDeliverySsrfLookupFn ? { ssrfLookupFn: opts.marketplaceDeliverySsrfLookupFn } : undefined
1639
1769
  });
1640
1770
  if ("error" in delivered) {
1641
1771
  sendJson(res, delivered.statusCode, {
@@ -1691,6 +1821,14 @@ async function handleFederationHttpRequest(req, res, opts) {
1691
1821
  res.end("Method Not Allowed");
1692
1822
  return true;
1693
1823
  }
1824
+ const ingressBudget = checkFederationPeerIngressBudget({
1825
+ clientIp: opts.peerAuthClientIp ?? req.socket?.remoteAddress,
1826
+ rateLimiter: opts.peerAuthDeps?.rateLimiter
1827
+ });
1828
+ if (!ingressBudget.ok) {
1829
+ sendPeerAuthRejection(res, ingressBudget);
1830
+ return true;
1831
+ }
1694
1832
  const body = await readJsonBody(req, maxBodyBytes);
1695
1833
  if (!body.ok) {
1696
1834
  sendJson(res, 400, {
@@ -1699,6 +1837,28 @@ async function handleFederationHttpRequest(req, res, opts) {
1699
1837
  });
1700
1838
  return true;
1701
1839
  }
1840
+ const federationBase = resolveFederationBaseUrl(process.env);
1841
+ const localHandle = resolveFederationHandle({
1842
+ env: process.env,
1843
+ fallbackDomain: federationBase ? new URL(federationBase).hostname : "localhost"
1844
+ });
1845
+ const peerAuth = await authorizeFederationPeerRequestV2({
1846
+ req,
1847
+ body: body.value,
1848
+ recipientHandle: localHandle,
1849
+ expectedPath: FEDERATION_MARKETPLACE_ORDER_PATH,
1850
+ directoryBaseUrl: federationBase,
1851
+ directoryApiToken: apiToken,
1852
+ clientIp: opts.peerAuthClientIp,
1853
+ env: process.env,
1854
+ deps: opts.peerAuthDeps,
1855
+ ingressBudgetApplied: true,
1856
+ deferReplayReservation: true
1857
+ });
1858
+ if (!peerAuth.ok) {
1859
+ sendPeerAuthRejection(res, peerAuth);
1860
+ return true;
1861
+ }
1702
1862
  const parsed = validateMarketplaceOrderBody(body.value);
1703
1863
  if (!parsed.ok) {
1704
1864
  sendJson(res, 400, {
@@ -1707,48 +1867,138 @@ async function handleFederationHttpRequest(req, res, opts) {
1707
1867
  });
1708
1868
  return true;
1709
1869
  }
1710
- const federationBase = resolveFederationBaseUrl(process.env);
1711
- const localHandle = resolveFederationHandle({
1712
- env: process.env,
1713
- fallbackDomain: federationBase ? new URL(federationBase).hostname : "localhost"
1714
- });
1715
1870
  const origin = resolveLocalOffersOrigin(req);
1716
- const senderHandle = (typeof req.headers["x-fased-sender-handle"] === "string" ? req.headers["x-fased-sender-handle"].trim() : "") || (typeof req.headers["x-fased-federation-sender"] === "string" ? req.headers["x-fased-federation-sender"].trim() : "");
1717
- const { snapshot, writeOptions } = await readConfigFileSnapshotForWrite();
1718
- const validation = validateSellerMarketplaceOrderIntake({
1719
- config: snapshot.config,
1720
- origin,
1721
- localHandle,
1722
- senderHandle,
1723
- order: parsed.value
1871
+ const senderHandle = peerAuth.senderHandle;
1872
+ const buyerHandle = senderHandle || parsed.value.buyerHandle?.trim() || "";
1873
+ const remoteOrderId = parsed.value.id || parsed.value.offerId || parsed.value.title || "order";
1874
+ const inboundOrderId = collisionResistantInboundOrderId({
1875
+ kind: "order",
1876
+ senderHandle: buyerHandle,
1877
+ remoteOrderId
1724
1878
  });
1725
- if (!validation.ok) {
1726
- sendJson(res, validation.status, {
1879
+ const transaction = await updateConfigFile(async (currentConfig) => {
1880
+ const validation = validateSellerMarketplaceOrderIntake({
1881
+ config: currentConfig,
1882
+ origin,
1883
+ localHandle,
1884
+ senderHandle,
1885
+ peerBondTier: peerAuth.bondTier,
1886
+ order: parsed.value
1887
+ });
1888
+ if (!validation.ok) {
1889
+ return {
1890
+ config: currentConfig,
1891
+ result: {
1892
+ kind: "rejected",
1893
+ status: validation.status,
1894
+ reason: validation.reason
1895
+ },
1896
+ write: false
1897
+ };
1898
+ }
1899
+ const existingInboundOrder = listLocalMarketplaceOrders(currentConfig).find((entry) => entry.configId === inboundOrderId);
1900
+ if (existingInboundOrder) {
1901
+ const ownershipMatches = existingInboundOrder.source === "federation" && normalizeComparableValue(existingInboundOrder.order.buyerHandle) === normalizeComparableValue(buyerHandle) && normalizeComparableValue(existingInboundOrder.order.sellerHandle) === normalizeComparableValue(localHandle) && normalizeComparableValue(existingInboundOrder.order.offerId) === normalizeComparableValue(validation.value.offer.offer.id) && existingInboundOrder.order.peerNodeId?.trim().toLowerCase() === peerAuth.nodeId.toLowerCase() && existingInboundOrder.order.peerRemoteOrderId === remoteOrderId;
1902
+ if (!ownershipMatches) {
1903
+ return {
1904
+ config: currentConfig,
1905
+ result: {
1906
+ kind: "rejected",
1907
+ status: 409,
1908
+ reason: "federation order identity conflicts with an existing local order"
1909
+ },
1910
+ write: false
1911
+ };
1912
+ }
1913
+ if (existingInboundOrder.order.peerRequestDigest !== peerAuth.bodySha256) {
1914
+ return {
1915
+ config: currentConfig,
1916
+ result: {
1917
+ kind: "rejected",
1918
+ status: 409,
1919
+ reason: "federation order is immutable after its first accepted request"
1920
+ },
1921
+ write: false
1922
+ };
1923
+ }
1924
+ const replay = await reserveAuthorizedFederationPeerRequest({
1925
+ authorization: peerAuth,
1926
+ env: process.env,
1927
+ deps: opts.peerAuthDeps
1928
+ });
1929
+ if (!replay.ok) {
1930
+ return {
1931
+ config: currentConfig,
1932
+ result: {
1933
+ kind: "auth-rejected",
1934
+ rejection: replay
1935
+ },
1936
+ write: false
1937
+ };
1938
+ }
1939
+ return {
1940
+ config: currentConfig,
1941
+ result: {
1942
+ kind: "accepted",
1943
+ created: false,
1944
+ order: existingInboundOrder
1945
+ },
1946
+ write: false
1947
+ };
1948
+ }
1949
+ const replay = await reserveAuthorizedFederationPeerRequest({
1950
+ authorization: peerAuth,
1951
+ env: process.env,
1952
+ deps: opts.peerAuthDeps
1953
+ });
1954
+ if (!replay.ok) {
1955
+ return {
1956
+ config: currentConfig,
1957
+ result: {
1958
+ kind: "auth-rejected",
1959
+ rejection: replay
1960
+ },
1961
+ write: false
1962
+ };
1963
+ }
1964
+ const upserted = upsertMarketplaceOrderConfig({
1965
+ config: currentConfig,
1966
+ input: buildSellerMarketplaceOrderInput({
1967
+ order: parsed.value,
1968
+ localHandle,
1969
+ buyerHandle,
1970
+ inboundOrderId,
1971
+ peerNodeId: peerAuth.nodeId,
1972
+ peerRemoteOrderId: remoteOrderId,
1973
+ peerRequestDigest: peerAuth.bodySha256,
1974
+ offer: validation.value.offer
1975
+ })
1976
+ });
1977
+ return {
1978
+ config: upserted.config,
1979
+ result: {
1980
+ kind: "accepted",
1981
+ created: upserted.created,
1982
+ order: listLocalMarketplaceOrders(upserted.config).find((entry) => entry.configId === upserted.order.id)
1983
+ }
1984
+ };
1985
+ });
1986
+ if (transaction.result.kind === "rejected") {
1987
+ sendJson(res, transaction.result.status, {
1727
1988
  status: "rejected",
1728
- reason: validation.reason
1989
+ reason: transaction.result.reason
1729
1990
  });
1730
1991
  return true;
1731
1992
  }
1732
- const buyerHandle = senderHandle || parsed.value.buyerHandle?.trim() || "";
1733
- const senderSegment = safeMarketplaceOrderIdSegment(buyerHandle || "buyer");
1734
- const orderSegment = safeMarketplaceOrderIdSegment(parsed.value.id || parsed.value.offerId || parsed.value.title || "order");
1735
- const inboundOrderId = `inbound-${senderSegment || "buyer"}-${orderSegment || "order"}`;
1736
- const result = upsertMarketplaceOrderConfig({
1737
- config: snapshot.config,
1738
- input: buildSellerMarketplaceOrderInput({
1739
- order: parsed.value,
1740
- localHandle,
1741
- buyerHandle,
1742
- inboundOrderId,
1743
- offer: validation.value.offer
1744
- })
1745
- });
1746
- await writeConfigFile(result.config, writeOptions);
1993
+ if (transaction.result.kind === "auth-rejected") {
1994
+ sendPeerAuthRejection(res, transaction.result.rejection);
1995
+ return true;
1996
+ }
1747
1997
  sendJson(res, 200, {
1748
1998
  ok: true,
1749
1999
  accepted: true,
1750
- created: result.created,
1751
- order: listLocalMarketplaceOrders(result.config).find((entry) => entry.configId === result.order.id)
2000
+ created: transaction.result.created,
2001
+ order: transaction.result.order
1752
2002
  });
1753
2003
  return true;
1754
2004
  }
@@ -1760,6 +2010,14 @@ async function handleFederationHttpRequest(req, res, opts) {
1760
2010
  res.end("Method Not Allowed");
1761
2011
  return true;
1762
2012
  }
2013
+ const ingressBudget = checkFederationPeerIngressBudget({
2014
+ clientIp: opts.peerAuthClientIp ?? req.socket?.remoteAddress,
2015
+ rateLimiter: opts.peerAuthDeps?.rateLimiter
2016
+ });
2017
+ if (!ingressBudget.ok) {
2018
+ sendPeerAuthRejection(res, ingressBudget);
2019
+ return true;
2020
+ }
1763
2021
  const body = await readJsonBody(req, maxBodyBytes);
1764
2022
  if (!body.ok) {
1765
2023
  sendJson(res, 400, {
@@ -1768,6 +2026,28 @@ async function handleFederationHttpRequest(req, res, opts) {
1768
2026
  });
1769
2027
  return true;
1770
2028
  }
2029
+ const federationBase = resolveFederationBaseUrl(process.env);
2030
+ const localHandle = resolveFederationHandle({
2031
+ env: process.env,
2032
+ fallbackDomain: federationBase ? new URL(federationBase).hostname : "localhost"
2033
+ });
2034
+ const peerAuth = await authorizeFederationPeerRequestV2({
2035
+ req,
2036
+ body: body.value,
2037
+ recipientHandle: localHandle,
2038
+ expectedPath: FEDERATION_MARKETPLACE_DELIVERY_PATH,
2039
+ directoryBaseUrl: federationBase,
2040
+ directoryApiToken: apiToken,
2041
+ clientIp: opts.peerAuthClientIp,
2042
+ env: process.env,
2043
+ deps: opts.peerAuthDeps,
2044
+ ingressBudgetApplied: true,
2045
+ deferReplayReservation: true
2046
+ });
2047
+ if (!peerAuth.ok) {
2048
+ sendPeerAuthRejection(res, peerAuth);
2049
+ return true;
2050
+ }
1771
2051
  const parsed = validateMarketplaceDeliveryInboxBody(body.value);
1772
2052
  if (!parsed.ok) {
1773
2053
  sendJson(res, 400, {
@@ -1776,67 +2056,181 @@ async function handleFederationHttpRequest(req, res, opts) {
1776
2056
  });
1777
2057
  return true;
1778
2058
  }
1779
- const federationBase = resolveFederationBaseUrl(process.env);
1780
- const localHandle = resolveFederationHandle({
1781
- env: process.env,
1782
- fallbackDomain: federationBase ? new URL(federationBase).hostname : "localhost"
2059
+ const senderHandle = peerAuth.senderHandle;
2060
+ const inboundOrderId = collisionResistantInboundOrderId({
2061
+ kind: "delivery",
2062
+ senderHandle,
2063
+ remoteOrderId: parsed.value.orderId
1783
2064
  });
1784
- const senderHandle = (typeof req.headers["x-fased-sender-handle"] === "string" ? req.headers["x-fased-sender-handle"].trim() : "") || (typeof req.headers["x-fased-federation-sender"] === "string" ? req.headers["x-fased-federation-sender"].trim() : "");
1785
- const senderSegment = safeMarketplaceOrderIdSegment(senderHandle || "federation");
1786
- const orderSegment = safeMarketplaceOrderIdSegment(parsed.value.orderId);
1787
- const inboundOrderId = `inbound-${senderSegment || "federation"}-${orderSegment || "order"}`;
1788
2065
  const deliveredAt = parsed.value.deliveredAt || new Date().toISOString();
1789
2066
  const payment = parsed.value.payment;
1790
- const { snapshot, writeOptions } = await readConfigFileSnapshotForWrite();
1791
- const result = upsertMarketplaceOrderConfig({
1792
- config: snapshot.config,
1793
- input: {
1794
- id: inboundOrderId,
1795
- source: "federation",
1796
- status: "delivered",
1797
- offerId: parsed.value.offerId,
1798
- buyerHandle: localHandle,
1799
- ...senderHandle ? { sellerHandle: senderHandle } : {},
1800
- title: "Federated content summary",
1801
- serviceKind: "content.summarize",
1802
- paymentIntent: {
1803
- status: payment?.status === "verified" ? "verified" : "submitted",
1804
- ...payment?.txRef ? { txRef: payment.txRef } : {}
1805
- },
1806
- delivery: {
2067
+ const transaction = await updateConfigFile(async (currentConfig) => {
2068
+ const buyerOrder = findCorrelatedMarketplaceBuyerOrder({
2069
+ config: currentConfig,
2070
+ sellerHandle: senderHandle,
2071
+ sellerOrderId: parsed.value.orderId,
2072
+ serviceKind: parsed.value.serviceKind
2073
+ });
2074
+ if (!buyerOrder) {
2075
+ return {
2076
+ config: currentConfig,
2077
+ result: {
2078
+ kind: "rejected",
2079
+ status: 409,
2080
+ reason: "federation delivery does not match an existing local buyer order"
2081
+ },
2082
+ write: false
2083
+ };
2084
+ }
2085
+ if (buyerOrder.order.offerId?.trim() && parsed.value.offerId.trim() && buyerOrder.order.offerId.trim() !== parsed.value.offerId.trim()) {
2086
+ return {
2087
+ config: currentConfig,
2088
+ result: {
2089
+ kind: "rejected",
2090
+ status: 409,
2091
+ reason: "federation delivery offer does not match the local buyer order"
2092
+ },
2093
+ write: false
2094
+ };
2095
+ }
2096
+ const existingInboundDelivery = listLocalMarketplaceOrders(currentConfig).find((entry) => entry.configId === inboundOrderId);
2097
+ if (existingInboundDelivery) {
2098
+ const ownershipMatches = existingInboundDelivery.source === "federation" && normalizeComparableValue(existingInboundDelivery.order.sellerHandle) === normalizeComparableValue(senderHandle) && existingInboundDelivery.order.sellerOrderId?.trim() === parsed.value.orderId && normalizeComparableValue(existingInboundDelivery.order.buyerHandle) === normalizeComparableValue(localHandle) && existingInboundDelivery.order.peerNodeId?.trim().toLowerCase() === peerAuth.nodeId.toLowerCase() && existingInboundDelivery.order.peerRemoteOrderId === parsed.value.orderId;
2099
+ if (!ownershipMatches) {
2100
+ return {
2101
+ config: currentConfig,
2102
+ result: {
2103
+ kind: "rejected",
2104
+ status: 409,
2105
+ reason: "federation delivery identity conflicts with an existing local order"
2106
+ },
2107
+ write: false
2108
+ };
2109
+ }
2110
+ if (existingInboundDelivery.order.peerDeliveryDigest !== peerAuth.bodySha256) {
2111
+ return {
2112
+ config: currentConfig,
2113
+ result: {
2114
+ kind: "rejected",
2115
+ status: 409,
2116
+ reason: "federation delivery is immutable after its first accepted request"
2117
+ },
2118
+ write: false
2119
+ };
2120
+ }
2121
+ const replay = await reserveAuthorizedFederationPeerRequest({
2122
+ authorization: peerAuth,
2123
+ env: process.env,
2124
+ deps: opts.peerAuthDeps
2125
+ });
2126
+ if (!replay.ok) {
2127
+ return {
2128
+ config: currentConfig,
2129
+ result: {
2130
+ kind: "auth-rejected",
2131
+ rejection: replay
2132
+ },
2133
+ write: false
2134
+ };
2135
+ }
2136
+ return {
2137
+ config: currentConfig,
2138
+ result: {
2139
+ kind: "accepted",
2140
+ delivery: existingInboundDelivery
2141
+ },
2142
+ write: false
2143
+ };
2144
+ }
2145
+ const replay = await reserveAuthorizedFederationPeerRequest({
2146
+ authorization: peerAuth,
2147
+ env: process.env,
2148
+ deps: opts.peerAuthDeps
2149
+ });
2150
+ if (!replay.ok) {
2151
+ return {
2152
+ config: currentConfig,
2153
+ result: {
2154
+ kind: "auth-rejected",
2155
+ rejection: replay
2156
+ },
2157
+ write: false
2158
+ };
2159
+ }
2160
+ const upserted = upsertMarketplaceOrderConfig({
2161
+ config: currentConfig,
2162
+ input: {
2163
+ id: inboundOrderId,
2164
+ source: "federation",
1807
2165
  status: "delivered",
1808
- fulfillmentMode: "agent",
1809
- deliveryShape: "summary-v0",
1810
- targetKind: "federation",
1811
- targetStatus: "ready",
1812
- targetLabel: senderHandle ? "Federation sender" : "Federation",
1813
- targetMasked: senderHandle || "federation",
1814
- resultRef: parsed.value.resultRef,
1815
- ...parsed.value.artifactRef ? { artifactRef: parsed.value.artifactRef } : {},
1816
- notes: `Received content.summarize result over federation: ${parsed.value.resultSummary}`,
1817
- deliveredAt,
1818
- updatedAt: deliveredAt
1819
- },
1820
- receipt: {
1821
- status: "issued",
1822
- ...payment?.invoiceId ? { invoiceId: payment.invoiceId } : {},
1823
- ...payment?.receiptId ? { receiptId: payment.receiptId } : {},
1824
- ...payment?.txRef ? { txRef: payment.txRef } : {},
1825
- resultRef: parsed.value.resultRef,
1826
- notes: "Federation delivery stored as a read-only Marketplace inbox item."
2166
+ offerId: parsed.value.offerId,
2167
+ buyerHandle: localHandle,
2168
+ ...senderHandle ? { sellerHandle: senderHandle } : {},
2169
+ sellerOrderId: parsed.value.orderId,
2170
+ peerNodeId: peerAuth.nodeId,
2171
+ peerRemoteOrderId: parsed.value.orderId,
2172
+ peerDeliveryDigest: peerAuth.bodySha256,
2173
+ title: "Federated content summary",
2174
+ serviceKind: "content.summarize",
2175
+ paymentIntent: {
2176
+ status: "submitted",
2177
+ ...payment?.txRef ? { txRef: payment.txRef } : {}
2178
+ },
2179
+ settlement: {
2180
+ mode: "direct",
2181
+ status: "submitted",
2182
+ ...payment?.invoiceId ? { invoiceId: payment.invoiceId } : {},
2183
+ ...payment?.receiptId ? { receiptId: payment.receiptId } : {},
2184
+ ...payment?.txRef ? { txRef: payment.txRef } : {},
2185
+ notes: "Peer-reported payment evidence has not been locally chain-verified.",
2186
+ updatedAt: deliveredAt
2187
+ },
2188
+ delivery: {
2189
+ status: "delivered",
2190
+ fulfillmentMode: "agent",
2191
+ deliveryShape: "summary-v0",
2192
+ targetKind: "federation",
2193
+ targetStatus: "ready",
2194
+ targetLabel: senderHandle ? "Federation sender" : "Federation",
2195
+ targetMasked: senderHandle || "federation",
2196
+ resultRef: parsed.value.resultRef,
2197
+ ...parsed.value.artifactRef ? { artifactRef: parsed.value.artifactRef } : {},
2198
+ notes: `Received content.summarize result over federation: ${parsed.value.resultSummary}`,
2199
+ deliveredAt,
2200
+ updatedAt: deliveredAt
2201
+ },
2202
+ receipt: {
2203
+ status: "pending",
2204
+ resultRef: parsed.value.resultRef,
2205
+ notes: "Federation delivery stored as a read-only Marketplace inbox item; no local receipt has been issued and peer payment evidence remains unverified."
2206
+ },
2207
+ resultRef: parsed.value.resultRef
1827
2208
  },
1828
- ...payment?.invoiceId ? { invoiceId: payment.invoiceId } : {},
1829
- ...payment?.receiptId ? { receiptId: payment.receiptId } : {},
1830
- ...payment?.txRef ? { txRef: payment.txRef } : {},
1831
- resultRef: parsed.value.resultRef
1832
- },
1833
- now: deliveredAt
2209
+ now: deliveredAt
2210
+ });
2211
+ return {
2212
+ config: upserted.config,
2213
+ result: {
2214
+ kind: "accepted",
2215
+ delivery: listLocalMarketplaceOrders(upserted.config).find((entry) => entry.configId === upserted.order.id)
2216
+ }
2217
+ };
1834
2218
  });
1835
- await writeConfigFile(result.config, writeOptions);
2219
+ if (transaction.result.kind === "rejected") {
2220
+ sendJson(res, transaction.result.status, {
2221
+ status: "rejected",
2222
+ reason: transaction.result.reason
2223
+ });
2224
+ return true;
2225
+ }
2226
+ if (transaction.result.kind === "auth-rejected") {
2227
+ sendPeerAuthRejection(res, transaction.result.rejection);
2228
+ return true;
2229
+ }
1836
2230
  sendJson(res, 200, {
1837
2231
  ok: true,
1838
2232
  accepted: true,
1839
- delivery: listLocalMarketplaceOrders(result.config).find((entry) => entry.configId === result.order.id)
2233
+ delivery: transaction.result.delivery
1840
2234
  });
1841
2235
  return true;
1842
2236
  }
@@ -1848,6 +2242,17 @@ async function handleFederationHttpRequest(req, res, opts) {
1848
2242
  });
1849
2243
  return true;
1850
2244
  }
2245
+ try {
2246
+ if (!isTrustedFederationPeerUrl(new URL("/", baseUrl))) {
2247
+ throw new Error("untrusted transport");
2248
+ }
2249
+ } catch {
2250
+ sendJson(res, 503, {
2251
+ status: "unavailable",
2252
+ reason: "federation base URL must use HTTPS (plain HTTP is allowed only for an explicit loopback URL)"
2253
+ });
2254
+ return true;
2255
+ }
1851
2256
  const shouldUseLocalFederationReadAuth = segments[0] === "operator-economy" && segments[1] === "fees";
1852
2257
  if (req.method === "GET" && shouldUseLocalFederationReadAuth) {
1853
2258
  const operatorEconomySubPath = segments[2] ?? "";