@fangorn-network/westmarch 0.0.20-dev → 0.1.1

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 (386) hide show
  1. package/README.md +35 -5
  2. package/docs/app-to-agent.md +113 -620
  3. package/package.json +18 -18
  4. package/site/dist/assets/__vite-browser-external-D2U8RZpp.js +1 -0
  5. package/site/dist/assets/embed.worker-CmS57Zti.js +1 -0
  6. package/site/dist/assets/index-CkP1WhkV.js +8 -0
  7. package/site/dist/assets/roles-BDPDWrwo.js +1 -0
  8. package/site/dist/assets/rolldown-runtime-COiEfhsA.js +1 -0
  9. package/site/dist/assets/transformers.web-CUZUFjAK.js +49 -0
  10. package/site/dist/assets/transformers.web-CwR_IYQV.js +40 -0
  11. package/site/dist/index.html +4 -5
  12. package/site/feed.js +16 -1
  13. package/site/no-model.js +4 -0
  14. package/site/worker.js +382 -59
  15. package/src/agent/agent-card.js +51 -37
  16. package/src/agent/mcp.js +173 -500
  17. package/src/agent/tools.js +3 -2
  18. package/src/agent/ui.js +1 -1
  19. package/src/agent/view-tools.js +11 -30
  20. package/src/agent/x402.js +194 -85
  21. package/src/core/embed.js +56 -53
  22. package/src/core/embed.worker.js +1 -1
  23. package/src/core/rank.js +1 -1
  24. package/src/core/roles.js +7 -7
  25. package/src/core/shard.js +12 -8
  26. package/src/discover/apps.js +103 -67
  27. package/src/discover/corpora.js +1 -1
  28. package/src/discover/directory.js +3 -3
  29. package/src/market/cohort.js +1 -1
  30. package/src/market/demand.js +1 -1
  31. package/src/market/reactions.js +24 -18
  32. package/src/publish/devnet.js +146 -0
  33. package/src/publish/graph.js +1 -1
  34. package/src/publish/init.js +74 -0
  35. package/src/publish/lint.js +3 -2
  36. package/src/publish/ship.js +433 -288
  37. package/src/publish/view-cli.js +14 -10
  38. package/src/publish/view.js +485 -255
  39. package/src/taste/steam.js +1 -1
  40. package/src/taste/taste-doc.js +4 -3
  41. package/src/taste/taste.js +3 -3
  42. package/src/taste/vault.js +4 -2
  43. package/template/README.md +48 -0
  44. package/template/app.json +27 -0
  45. package/template/eval/goals.md +7 -0
  46. package/template/eval/golden.jsonl +0 -0
  47. package/template/gitignore +3 -0
  48. package/template/package.json +13 -0
  49. package/template/wrangler.json +12 -0
  50. package/plugin/scripts/fx.mjs +0 -268
  51. package/site/dist/assets/AccountNotFoundScreen-C4njXc69-BOfmiSH1.js +0 -1
  52. package/site/dist/assets/AddFundsSelectionScreen-RAoCn7ej-D_EsIeJu.js +0 -31
  53. package/site/dist/assets/Address-H8kD1d9O-C85sJ79B.js +0 -17
  54. package/site/dist/assets/AffirmativeConsentScreen-BG0MJxku-DdEPFOww.js +0 -6
  55. package/site/dist/assets/AllowlistRejectionScreen-Cae6e1Od-DhVy9iGN.js +0 -1
  56. package/site/dist/assets/ApiController-CYdiyu9B.js +0 -5
  57. package/site/dist/assets/ArrowsRightLeftIcon-cp60yobq.js +0 -1
  58. package/site/dist/assets/AuthenticateWithWalletScreen-CU6hIcFT-8rbYiYYE.js +0 -1
  59. package/site/dist/assets/AwaitingEvmToSolBridgingScreen-D0e_sZMS-2pkNfCno.js +0 -1
  60. package/site/dist/assets/AwaitingExternalEthereumTransferScreen-ur5HL4R7-7AQt25_k.js +0 -1
  61. package/site/dist/assets/AwaitingPasswordlessCodeScreen-DRHz76mM-BZY30TGs.js +0 -103
  62. package/site/dist/assets/AwaitingSolToEvmBridgingScreen-CdLRrGD2-BILOOLi_.js +0 -1
  63. package/site/dist/assets/BridgeNetworkSelectionView-CFGjB2nO-CvsbQcFv.js +0 -68
  64. package/site/dist/assets/CaptchaScreen-DbpvkIGx-D8eDcAkc.js +0 -1
  65. package/site/dist/assets/CheckCircleIcon-BM409mGI.js +0 -1
  66. package/site/dist/assets/CheckCircleIcon-NCOQc0Gs.js +0 -1
  67. package/site/dist/assets/Checkbox-D1EDeo41-rHlvsZzP.js +0 -52
  68. package/site/dist/assets/ChevronDownIcon-CW-4MSpE.js +0 -1
  69. package/site/dist/assets/Chip-CZKIKt9K-CutJG010.js +0 -26
  70. package/site/dist/assets/CoinbaseOnrampStatusScreen-BbyCJDXL-DLs9zX-Q.js +0 -16
  71. package/site/dist/assets/ConnectEmailForm-B-VRE0MN-N9J6T_YL.js +0 -3
  72. package/site/dist/assets/ConnectLedgerScreen-DWHABGot-h0uYIUi6.js +0 -10
  73. package/site/dist/assets/ConnectOnlyLandingScreen-CZYx9Bmp-LeuDMN1E.js +0 -1
  74. package/site/dist/assets/ConnectOnlyStatusScreen-DCRzjxHx-DaRa59tZ.js +0 -1
  75. package/site/dist/assets/ConnectOrCreateScreen-CUUYUO07-crHzh-TL.js +0 -1
  76. package/site/dist/assets/ConnectPhoneForm-Dek5xn4C-BEA4FmhI.js +0 -95
  77. package/site/dist/assets/ConnectWalletView-DqI-PTvX-CMQIad63.js +0 -74
  78. package/site/dist/assets/ConnectionStatusScreen-fUfROm9m-BeWLchcf.js +0 -7
  79. package/site/dist/assets/ConnectionStatusScreen-fUfROm9m-wu3a3kw4.js +0 -1
  80. package/site/dist/assets/ConnectorUtil-Cq4lWnfe.js +0 -21
  81. package/site/dist/assets/ConstantsUtil-DmLsRHAl.js +0 -198
  82. package/site/dist/assets/CopyToClipboard-i_OQSBJr-DujUHNzW.js +0 -39
  83. package/site/dist/assets/CopyableText-CQapvaMr-DMIdpDaU.js +0 -28
  84. package/site/dist/assets/CrossAppAuthScreen-CLw7ymZq-Csju14vF.js +0 -1
  85. package/site/dist/assets/CustomLandingScreenView-Cm_MBSyX-CBOrbNLo.js +0 -22
  86. package/site/dist/assets/DelegatedActionsConsentScreen-DYF2U4y5-BL5hzNUI.js +0 -1
  87. package/site/dist/assets/DelegatedActionsRevokeScreen-bPp6Uwyh-fcO51q5H.js +0 -1
  88. package/site/dist/assets/DepositAddressAddressStepView-4vdkb55L-B3vY888G.js +0 -187
  89. package/site/dist/assets/DepositAddressScreen-DBLRs7aI-DmdQaFI2.js +0 -8
  90. package/site/dist/assets/DepositAddressV3Screen-CDqIEMQw-BoupRyfV.js +0 -1
  91. package/site/dist/assets/EmailInputForm-Cqo1mda8-B2NG3K5V.js +0 -124
  92. package/site/dist/assets/EmbeddedWalletConnectingScreen-D4T7DUzs-D_AVgO4s.js +0 -1
  93. package/site/dist/assets/EmbeddedWalletCreatedScreen-D7WhW5ve-CLsJbPYQ.js +0 -1
  94. package/site/dist/assets/EmbeddedWalletKeyExportScreen-CqV4NYQ3-C9sAExzE.js +0 -23
  95. package/site/dist/assets/EmbeddedWalletOnAccountCreateScreen-BoKb3Q2S-DxaWAAKa.js +0 -1
  96. package/site/dist/assets/EmbeddedWalletPasswordUpdateScreen-D3rMlqS7-CxrfLr5q.js +0 -1
  97. package/site/dist/assets/EmbeddedWalletPasswordUpdateSplashScreen-BPFkWJeE-DHZRGoTN.js +0 -1
  98. package/site/dist/assets/EnrollTotp-Dibew4dp-SA2pvNEc.js +0 -66
  99. package/site/dist/assets/EnvelopeIcon-Dh2vSVj4.js +0 -1
  100. package/site/dist/assets/ErrorBanner-BcpGRt0h-BKOYTyCi.js +0 -21
  101. package/site/dist/assets/ErrorMessage-D8VaAP5m-CrZOnUyg.js +0 -8
  102. package/site/dist/assets/ErrorScreen-DipdTWzE-DcxVjMO4.js +0 -1
  103. package/site/dist/assets/ErrorScreen-DipdTWzE-c8g8En21.js +0 -4
  104. package/site/dist/assets/ExclamationCircleIcon-DUA2wMfO.js +0 -1
  105. package/site/dist/assets/ExclamationTriangleIcon-eZfaqcFq.js +0 -1
  106. package/site/dist/assets/FarcasterConnectStatusScreen-51tjUxqq-s_s5M2CZ.js +0 -75
  107. package/site/dist/assets/FarcasterSignerStatusScreen-6x-Uh95G-BHBR4k8H.js +0 -28
  108. package/site/dist/assets/FiatOnrampScreen-BAhYeiTI-BH_sU6EV.js +0 -491
  109. package/site/dist/assets/FingerPrintIcon-Dm6kxmg7.js +0 -1
  110. package/site/dist/assets/FundSolWalletWithExternalSolanaWallet-WtLwYaOl-B8TRV36j.js +0 -1
  111. package/site/dist/assets/FundWalletMethodHeader-BWNkokCT-BQdbJ5Bb.js +0 -1
  112. package/site/dist/assets/FundWithBankDepositScreen-DajkHxBD-9rPtyA3d.js +0 -58
  113. package/site/dist/assets/FundingEditAmountScreen-CpvpiO4M-GaXigGI9.js +0 -1
  114. package/site/dist/assets/FundingMethodSelectionScreen-Db7vhRlE-UsmtTqv4.js +0 -1
  115. package/site/dist/assets/GlobeAltIcon-vqrVV5ho.js +0 -1
  116. package/site/dist/assets/GooglePay-B53WnudL-BX5_WtCx.js +0 -1
  117. package/site/dist/assets/HCaptchaWrapper-CUi9LJsp-D-I8j7NO.js +0 -4
  118. package/site/dist/assets/InAppBrowserLoginNotPossible-BMR9br1T-BY3j50El.js +0 -1
  119. package/site/dist/assets/InfoBanner-Cb3p1z12-DLBl_Ywy.js +0 -22
  120. package/site/dist/assets/InjectedWalletIcon-DLcYOGDj-DLSV_NjP.js +0 -1
  121. package/site/dist/assets/InstallWalletScreen-DTA8dwLv-CxreyXoD.js +0 -1
  122. package/site/dist/assets/JsonTree-BHzNC-ic-DcNcbSoM.js +0 -34
  123. package/site/dist/assets/LabelXs-oqZNqbm_-iafrj25N.js +0 -6
  124. package/site/dist/assets/LandingScreen-CGWUoSUG-DK1EY-6v.js +0 -1
  125. package/site/dist/assets/Layouts-BMRfo5hw-CeCOyCLK.js +0 -77
  126. package/site/dist/assets/Link-BdDilT2T-BEDEdHuS.js +0 -40
  127. package/site/dist/assets/LinkConflictScreen-C3ZEPsgl-ClWoqvEL.js +0 -77
  128. package/site/dist/assets/LinkEmailScreen-dKo9frwc-C3v7glH0.js +0 -1
  129. package/site/dist/assets/LinkPasskeyScreen-zbrl_AI_-D72Gb0LW.js +0 -117
  130. package/site/dist/assets/LinkPasskeyScreen-zbrl_AI_-DYIl5Kr2.js +0 -1
  131. package/site/dist/assets/LinkPhoneScreen-oIUpfj0w-C0DuD14j.js +0 -1
  132. package/site/dist/assets/Loader-UAZqxsnw-Cy7hEE2f.js +0 -62
  133. package/site/dist/assets/LoadingSkeleton-BMsgO5PV-CiEyS5PZ.js +0 -17
  134. package/site/dist/assets/LockClosedIcon-BgY_5v_Z.js +0 -1
  135. package/site/dist/assets/LoginFailedScreen-CN0dJV6U-DhUMiJZJ.js +0 -1
  136. package/site/dist/assets/ManualTransferScreen-CHqcuqbs-tXiZDHmN.js +0 -1
  137. package/site/dist/assets/MfaAuthEnrollmentFlowScreen-Dj7gPTRC-CaW7xeCC.js +0 -1
  138. package/site/dist/assets/MfaAuthVerifyFlowScreen-DbFpyWMy-CeRqvx8k.js +0 -1
  139. package/site/dist/assets/MfaEnrollmentFlowScreen-DhLnGgnr-BkN1k4lh.js +0 -1
  140. package/site/dist/assets/MfaVerifyFlowScreen-C_XPjMRP-CXSqLVbE.js +0 -1
  141. package/site/dist/assets/ModalFooter-BCuEK_nz-c-khvG4Y.js +0 -403
  142. package/site/dist/assets/MoonpayStatusScreen-CzTUtokc-BRFN2eXr.js +0 -16
  143. package/site/dist/assets/OAuthStatusScreen-ufbOa-da-CeiGcSia.js +0 -1
  144. package/site/dist/assets/OpenLink-CUpJ1mOr-DfAvJRyK.js +0 -39
  145. package/site/dist/assets/PasskeySelectSignupOrLogin-xh2ScC_l-CIf5I7wU.js +0 -1
  146. package/site/dist/assets/PasskeyStatusScreen-Bla8MaOk-CLoL7QC-.js +0 -6
  147. package/site/dist/assets/PasswordRecoveryScreen-awGXZkQk-B9F67IYV.js +0 -20
  148. package/site/dist/assets/PhArrowCircleDown-D97G-BOA.js +0 -14
  149. package/site/dist/assets/PhArrowClockwise-DVgyFuez.js +0 -14
  150. package/site/dist/assets/PhArrowDown-87b1BizM.js +0 -14
  151. package/site/dist/assets/PhArrowLeft-Da0MfnbK.js +0 -14
  152. package/site/dist/assets/PhArrowRight-AdrIL4DW.js +0 -14
  153. package/site/dist/assets/PhArrowSquareOut-Bk2CMG3I.js +0 -14
  154. package/site/dist/assets/PhArrowUp-4sgyKPGx.js +0 -14
  155. package/site/dist/assets/PhArrowUpRight-BNghD2zq.js +0 -14
  156. package/site/dist/assets/PhArrowsClockwise-BXRn1GEl.js +0 -14
  157. package/site/dist/assets/PhArrowsDownUp-DM09Jo_3.js +0 -14
  158. package/site/dist/assets/PhArrowsLeftRight-MuKBLUoF.js +0 -14
  159. package/site/dist/assets/PhBank-fHbgCqqZ.js +0 -14
  160. package/site/dist/assets/PhBrowser-2-9m-AkM.js +0 -14
  161. package/site/dist/assets/PhCaretDown-BDNabFuL.js +0 -14
  162. package/site/dist/assets/PhCaretLeft-CI460R-A.js +0 -14
  163. package/site/dist/assets/PhCaretRight-D7ZS3Ls8.js +0 -14
  164. package/site/dist/assets/PhCaretUp-C-iIfyvO.js +0 -14
  165. package/site/dist/assets/PhCheck-DxQXn57r.js +0 -14
  166. package/site/dist/assets/PhCircleHalf-MJZeRBGF.js +0 -14
  167. package/site/dist/assets/PhClock-x3MS3Mwi.js +0 -14
  168. package/site/dist/assets/PhCompass-hYn3HhGN.js +0 -14
  169. package/site/dist/assets/PhCopy-CWNYcABI.js +0 -14
  170. package/site/dist/assets/PhCreditCard-Fy4pzwtt.js +0 -14
  171. package/site/dist/assets/PhCurrencyDollar-BgiaenCi.js +0 -14
  172. package/site/dist/assets/PhDesktop-CxrzCSv5.js +0 -14
  173. package/site/dist/assets/PhDeviceMobile-CsyA5Goi.js +0 -14
  174. package/site/dist/assets/PhDotsThree-D-k1BiYg.js +0 -14
  175. package/site/dist/assets/PhEnvelope-D8kzeNox.js +0 -14
  176. package/site/dist/assets/PhFunnelSimple-CfQs5DAW.js +0 -14
  177. package/site/dist/assets/PhGlobe-DvmSc9_A.js +0 -14
  178. package/site/dist/assets/PhIdentificationCard-CfkZm-5K.js +0 -14
  179. package/site/dist/assets/PhImage-Cf0J2ZfJ.js +0 -14
  180. package/site/dist/assets/PhInfo-Bwi51-2X.js +0 -14
  181. package/site/dist/assets/PhLightbulb-C4FZV7Es.js +0 -14
  182. package/site/dist/assets/PhMagnifyingGlass-C9u8jSCJ.js +0 -14
  183. package/site/dist/assets/PhPaperPlaneRight-zxQVvPLX.js +0 -14
  184. package/site/dist/assets/PhPlus-C4UoW-2n.js +0 -14
  185. package/site/dist/assets/PhPower-CFyWDmgD.js +0 -14
  186. package/site/dist/assets/PhPuzzlePiece-BjoNVfST.js +0 -14
  187. package/site/dist/assets/PhQrCode-CP3tFauY.js +0 -14
  188. package/site/dist/assets/PhQuestion-wOWJBd3G.js +0 -14
  189. package/site/dist/assets/PhQuestionMark-CV0k9C0Y.js +0 -14
  190. package/site/dist/assets/PhSealCheck-vIKzREuN.js +0 -14
  191. package/site/dist/assets/PhSignOut-CxELV-i0.js +0 -14
  192. package/site/dist/assets/PhSpinner-CGDmbzUd.js +0 -14
  193. package/site/dist/assets/PhTrash-C9PD0SAt.js +0 -14
  194. package/site/dist/assets/PhUser-0sWNXQeZ.js +0 -14
  195. package/site/dist/assets/PhVault-6JTskUcC.js +0 -14
  196. package/site/dist/assets/PhWarning-CoKs3gz6.js +0 -14
  197. package/site/dist/assets/PhWarningCircle-BXHw1fCt.js +0 -14
  198. package/site/dist/assets/PhX-DX0SFIqC.js +0 -14
  199. package/site/dist/assets/PhoneIcon-CvIiPwYQ.js +0 -1
  200. package/site/dist/assets/PinInput-DbZ0b1i1-CDXZBdQx.js +0 -247
  201. package/site/dist/assets/PrivyPluginContext-DA-yS5vO-CtQWBiV2.js +0 -1
  202. package/site/dist/assets/QrCode-DbnZwlkH-h6Bhoe5U.js +0 -22
  203. package/site/dist/assets/RecoveryOAuthStatusScreen-CsgtTI2x-D-A5P3PX.js +0 -1
  204. package/site/dist/assets/RecoveryPasswordCreateScreen-Be-qJ-LU-C8lTSeSr.js +0 -1
  205. package/site/dist/assets/RecoverySelectionScreen-D1iGtpmi-P5P3plkH.js +0 -7
  206. package/site/dist/assets/SIWXUtil-TUCxa7aA.js +0 -1
  207. package/site/dist/assets/Screen-D30meWsY-CCeVNuWs.js +0 -175
  208. package/site/dist/assets/ScreenHeader-CHmc4-Lu-DjyrH7IK.js +0 -31
  209. package/site/dist/assets/ScreenLayout-OwaQQcxH-CrQM6kvA.js +0 -1
  210. package/site/dist/assets/SelectSourceAsset-CWFog6gm-D7yTw87h.js +0 -140
  211. package/site/dist/assets/SetAutomaticRecoveryScreen-BO_OEqAZ-f0iDIGmw.js +0 -14
  212. package/site/dist/assets/SetWalletPasswordForm-DwCTVtAh-kq1zvA7d.js +0 -11
  213. package/site/dist/assets/ShieldCheckIcon-o6RHYev1.js +0 -1
  214. package/site/dist/assets/SignRequestScreen-BHKv32KR-DDpMYyJ1.js +0 -26
  215. package/site/dist/assets/SolanaWalletConnector-C7Lqk-Wz-DPwhuzcI.js +0 -1
  216. package/site/dist/assets/StackedContainer-B2vaEl56-eQCM2cmv.js +0 -24
  217. package/site/dist/assets/StandardSignAndSendTransactionScreen-CfhIXVtd-BJ-onCjv.js +0 -20
  218. package/site/dist/assets/Subtitle-CV-2yKE4-zxWw2m1Q.js +0 -13
  219. package/site/dist/assets/TelegramAuthScreen-UL9zLfO8-NWlMgNRX.js +0 -1
  220. package/site/dist/assets/Title-BnzYV3Is-B_JpTatT.js +0 -7
  221. package/site/dist/assets/TodoList-DnyULl18-C8Eyy28X.js +0 -36
  222. package/site/dist/assets/TransactionDetails-e9nesl30-C4MpIHR-.js +0 -109
  223. package/site/dist/assets/TransactionErrorView-BDBNqA_f-p1T9WVMx.js +0 -178
  224. package/site/dist/assets/TransferFromWalletScreen-Qvn1Yuno-88IQcRnY.js +0 -31
  225. package/site/dist/assets/TransferOrBridgeLoadingScreen-Dl1I_iIz-1fDAIxAf.js +0 -6
  226. package/site/dist/assets/TurnstileWrapper-B7sNddys-Jwy1tJtm.js +0 -1
  227. package/site/dist/assets/UpdateEmailScreen-DVOhPV0H-Jj2DQW8D.js +0 -1
  228. package/site/dist/assets/UpdatePhoneScreen-CXRswTNZ-DwYIUBqi.js +0 -1
  229. package/site/dist/assets/UserLimitReachedScreen-5we2DHn8-D7m70C4b.js +0 -1
  230. package/site/dist/assets/Value-DTgR824E-BRuIGiec.js +0 -27
  231. package/site/dist/assets/WalletCards-DH1rqayz-Bgp9Fsgs.js +0 -1
  232. package/site/dist/assets/WalletIcon-gKf0gTw6.js +0 -1
  233. package/site/dist/assets/WalletInfoCard-sq_2wgFg-DpNSAGte.js +0 -29
  234. package/site/dist/assets/WalletInterstitialScreen-Sh7LfsAh-DG-RuO-F.js +0 -1
  235. package/site/dist/assets/WalletLink-DK5LB5Ha-DHaL350n.js +0 -20
  236. package/site/dist/assets/WalletOverflowButton-bfCnp-9T-CUyIJFzt.js +0 -1
  237. package/site/dist/assets/WarningBanner-ZZqCEtZK-2eU4Qo74.js +0 -21
  238. package/site/dist/assets/__vite-browser-external-CitKSmYk.js +0 -1
  239. package/site/dist/assets/_esm-B26FZg5Y.js +0 -7
  240. package/site/dist/assets/_esm-BdeM6pxJ.js +0 -1
  241. package/site/dist/assets/accounts-D96UKWkC.js +0 -1
  242. package/site/dist/assets/accounts-DiZ-0VmN.js +0 -1
  243. package/site/dist/assets/analytics-mkkvFRju-BRBcadka.js +0 -1
  244. package/site/dist/assets/baseSepolia-DX-qGsik.js +0 -1
  245. package/site/dist/assets/basic-oIwwSyu4.js +0 -1575
  246. package/site/dist/assets/capitalizeFirstLetter-DmLYqXsO-DtOXx-2n.js +0 -1
  247. package/site/dist/assets/ccip-65sIfSPR.js +0 -1
  248. package/site/dist/assets/ccip-BZvRTL6g.js +0 -7
  249. package/site/dist/assets/ccip-DA_zG341.js +0 -1
  250. package/site/dist/assets/chain-DM3oH6Px.js +0 -8
  251. package/site/dist/assets/check-BPgcwEPP.js +0 -1
  252. package/site/dist/assets/chevron-down-BSsbXkK-.js +0 -1
  253. package/site/dist/assets/circle-alert-DWaeLZNj.js +0 -1
  254. package/site/dist/assets/circle-check-big-Chb1ivw9.js +0 -1
  255. package/site/dist/assets/circle-x-AV9Cet0R.js +0 -1
  256. package/site/dist/assets/client-CZG0ndth.js +0 -2
  257. package/site/dist/assets/client-Dsa7_3V3.js +0 -9
  258. package/site/dist/assets/coinbase-DCPC2L_R-D3aIgpyd.js +0 -3
  259. package/site/dist/assets/copy-Ckr0AkL6.js +0 -1
  260. package/site/dist/assets/core-snws5xwv.js +0 -12
  261. package/site/dist/assets/createLucideIcon-IPW50ckY.js +0 -1
  262. package/site/dist/assets/defineChain-CiuCXUZW.js +0 -1
  263. package/site/dist/assets/dijkstra-Drfl93V4.js +0 -1
  264. package/site/dist/assets/dist-Cm7pNAtL.js +0 -1
  265. package/site/dist/assets/dist-CoS6E-3x.js +0 -1
  266. package/site/dist/assets/dist-Dl07uGq1.js +0 -1
  267. package/site/dist/assets/dist-DnGANz51.js +0 -16
  268. package/site/dist/assets/embed.worker-ugGhEqCV.js +0 -1
  269. package/site/dist/assets/errors-BQ3N-RVt-BLqjTPNR.js +0 -1
  270. package/site/dist/assets/esm-B5cbIQaz.js +0 -37
  271. package/site/dist/assets/esm-ByALO6mA.js +0 -1
  272. package/site/dist/assets/esm-C3DslVJx.js +0 -1
  273. package/site/dist/assets/esm-Ci1ogKuK.js +0 -1
  274. package/site/dist/assets/esm-n2lWo7kz.js +0 -1
  275. package/site/dist/assets/ethers-zTjbodk5-wHhL_BQ1.js +0 -1
  276. package/site/dist/assets/eventemitter3-CL76jPsW.js +0 -1
  277. package/site/dist/assets/external-link-D-uehwM2.js +0 -1
  278. package/site/dist/assets/farcaster-DPlSjvF5-BqTvDXBR.js +0 -1
  279. package/site/dist/assets/features-DQ0UYy6K.js +0 -2
  280. package/site/dist/assets/fingerprint-pattern-D8ytA16Q.js +0 -1
  281. package/site/dist/assets/floating-ui.react-dom-DKWOL9UG.js +0 -1
  282. package/site/dist/assets/floating-ui.react-xRkl9O3u.js +0 -1
  283. package/site/dist/assets/formatErc20TokenAmount-BuPk9xcy-BVqOKQif.js +0 -1
  284. package/site/dist/assets/formatter-CVbp4SlJ.js +0 -1
  285. package/site/dist/assets/get-entropy-details-for-user-dHRH5Gpi-Dukf7_kJ.js +0 -1
  286. package/site/dist/assets/getChainName-DjpPdUSc-DUyKDrnp.js +0 -1
  287. package/site/dist/assets/getErc20Balance-jXNpFf0N-Bb9WM31M.js +0 -1
  288. package/site/dist/assets/getErc20TokenInfo-BNTrzLMP-KPrdn04K.js +0 -1
  289. package/site/dist/assets/getFormattedUsdFromLamports-De3U9GlO-Gx4_wxON.js +0 -1
  290. package/site/dist/assets/getUsdcMintAddress-BVgm9GRo-r2UnDeEy.js +0 -1
  291. package/site/dist/assets/guards-BIW-GiMJ-CHfXo8XI.js +0 -1
  292. package/site/dist/assets/hooks.module-BYZGfwDl.js +0 -1
  293. package/site/dist/assets/hourglass-CPG1HZmf.js +0 -1
  294. package/site/dist/assets/id-UqEwL2BI-BkKm533U.js +0 -1
  295. package/site/dist/assets/index-1HYFYA-W.js +0 -8
  296. package/site/dist/assets/index-BdEw5wyl-ChSJvPZk.js +0 -1
  297. package/site/dist/assets/index-CWARkn2w-Cezl_uBr.js +0 -61
  298. package/site/dist/assets/index-DvMddJcd-BatvgC-S.js +0 -12
  299. package/site/dist/assets/index-YiCw0Y_x-CX5i3Z87.js +0 -437
  300. package/site/dist/assets/index.browser-B-i-Lj3J.js +0 -1
  301. package/site/dist/assets/index.browser-BMmUcykv.js +0 -1
  302. package/site/dist/assets/index.es-BmqyoBEM.js +0 -2
  303. package/site/dist/assets/index.es-C1v1or--.js +0 -3
  304. package/site/dist/assets/isPaymentRequestAvailable-Bq1cemEn-By6r0dVB.js +0 -1
  305. package/site/dist/assets/jsx-runtime-BbDfbRii.js +0 -1
  306. package/site/dist/assets/landmark-CH6LV6F0.js +0 -1
  307. package/site/dist/assets/lib-BsS8z8Lj.js +0 -1
  308. package/site/dist/assets/localBatchGatewayRequest-3kLrX_c7.js +0 -32
  309. package/site/dist/assets/lock-DcskI4Bb.js +0 -1
  310. package/site/dist/assets/logger-CsXm3oIv-B0UYpNFI.js +0 -1
  311. package/site/dist/assets/mail-Bs0r5yOi.js +0 -1
  312. package/site/dist/assets/modal-context-CwmSeFcv-DruX9J1E.js +0 -1
  313. package/site/dist/assets/native-BKj27wfc.js +0 -1
  314. package/site/dist/assets/ofetch.CWycOUEr-CHp-crOQ.js +0 -1
  315. package/site/dist/assets/parseAbi-Csg-8t59.js +0 -2
  316. package/site/dist/assets/parseAbiParameters-BWbIMYe4.js +0 -1
  317. package/site/dist/assets/parseEther-VjhVo3dd.js +0 -1
  318. package/site/dist/assets/pkce-DXF0yKjP-ChQhE0st.js +0 -1
  319. package/site/dist/assets/preload-helper-uBIymjUX.js +0 -1
  320. package/site/dist/assets/privy-context-81KmsBw2-CjWGzyg7.js +0 -2
  321. package/site/dist/assets/property-DuZ9DLP4.js +0 -3
  322. package/site/dist/assets/react-DCXkXSrv.js +0 -1
  323. package/site/dist/assets/react-DQnMl2Xv.js +0 -1
  324. package/site/dist/assets/react-DrJ0MmMO.js +0 -1
  325. package/site/dist/assets/react-auth-sHnipzbS.js +0 -1
  326. package/site/dist/assets/react-dom-5dW7aKoP.js +0 -1
  327. package/site/dist/assets/reservoir-x-nGuZkT-BMX127i6.js +0 -1
  328. package/site/dist/assets/roles-32M1l9jI.js +0 -1
  329. package/site/dist/assets/rolldown-runtime-BcKkbAw3.js +0 -1
  330. package/site/dist/assets/safe-url-D7SRPu33-CkMedGro.js +0 -1
  331. package/site/dist/assets/secp256k1-C4nzcPo-.js +0 -1
  332. package/site/dist/assets/secp256k1-D92aGNnU.js +0 -1
  333. package/site/dist/assets/secp256k1-DmbwyFau.js +0 -1
  334. package/site/dist/assets/secp256k1-_MleGpqL.js +0 -1
  335. package/site/dist/assets/secp256k1-xl2og-wO.js +0 -1
  336. package/site/dist/assets/serializeAuthorizationList-CX8hGL56.js +0 -1
  337. package/site/dist/assets/serializeSignature-DfxkPnu-.js +0 -2
  338. package/site/dist/assets/sha2-BU_QyRTx.js +0 -1
  339. package/site/dist/assets/shared-DRZUmnL5-xqAUARWo.js +0 -193
  340. package/site/dist/assets/shared-FM0rljBt--Avg6q2R.js +0 -21
  341. package/site/dist/assets/shouldProceedtoEmbeddedWalletCreationFlow-B3vyavrh-BK_FBkaE.js +0 -1
  342. package/site/dist/assets/simulateTransaction-CfuOrae4-64lWYJAL.js +0 -1
  343. package/site/dist/assets/smartphone-CcEZ-XAy.js +0 -1
  344. package/site/dist/assets/src-BBjkfi-Q.js +0 -1
  345. package/site/dist/assets/storage-Ufl800Ne-D0bZBw-H.js +0 -9
  346. package/site/dist/assets/stringify-BlDm7tuc.js +0 -21
  347. package/site/dist/assets/stripe.esm-CZRDpc-N.js +0 -2
  348. package/site/dist/assets/styles-BsotlekN-CXEB4Ud9.js +0 -9
  349. package/site/dist/assets/styles-Cm9H63WO-Br4QtzUL.js +0 -92
  350. package/site/dist/assets/styles-DVyDvTdj-BTABn2K8.js +0 -74
  351. package/site/dist/assets/styles-hslXy6xG-D-49b1rk.js +0 -164
  352. package/site/dist/assets/telegram-B-JqnkqZ-RBoY3PLx.js +0 -1
  353. package/site/dist/assets/to-ui-error-DWe-qwS3-Cx2bk1kZ.js +0 -4
  354. package/site/dist/assets/toViemAccount-B5iDGq1b-Ba8L7zed.js +0 -2
  355. package/site/dist/assets/transaction-BNTP-bFm-M5tJx8vY.js +0 -1
  356. package/site/dist/assets/transfer-6YztDh-t-BVQkTvG-.js +0 -1
  357. package/site/dist/assets/transformers.web-CVP3pJOS.js +0 -40
  358. package/site/dist/assets/transformers.web-D8W1u_I2.js +0 -49
  359. package/site/dist/assets/triangle-alert-CWjYH75H.js +0 -1
  360. package/site/dist/assets/twitch-5IOe4sIQ-lDtgk0os.js +0 -1
  361. package/site/dist/assets/types-4CuOD23i.js +0 -1
  362. package/site/dist/assets/use-headless-crypto-deposit-Yn-QRLB3-LUIVFBSd.js +0 -1
  363. package/site/dist/assets/use-unlink-wallet-BKK4_5Ur-CC2pq27z.js +0 -1
  364. package/site/dist/assets/useActiveWallet-B-yZYGc6-D7Ik-LxJ.js +0 -123
  365. package/site/dist/assets/useGetSolPrice-x7gfUIHJ-D1hmQfpA.js +0 -1
  366. package/site/dist/assets/useGetTokenPrice-CaoUkb79-ZnbuJ-vP.js +0 -1
  367. package/site/dist/assets/useI18n-C-5n_noF-BcqC76yj.js +0 -1
  368. package/site/dist/assets/useWalletBalance-CZnP5f_f-Dw2GIJ4g.js +0 -1
  369. package/site/dist/assets/useWallets-X1ndvJ_U-DRjz7uYD.js +0 -1
  370. package/site/dist/assets/utils-BkR7EQjg.js +0 -1
  371. package/site/dist/assets/utils-DJhBNOZA.js +0 -1
  372. package/site/dist/assets/utils-DdckVfQr.js +0 -1
  373. package/site/dist/assets/w3m-modal-BeVrBOIf.js +0 -1174
  374. package/site/dist/assets/wallet-Bn85Sg2W.js +0 -1
  375. package/site/dist/assets/wallet-Bnzeiqed.js +0 -2
  376. package/site/dist/assets/wallet-connect-BvJtUsDt-Y03hhqFN.js +0 -1
  377. package/site/dist/assets/walletconnect-registry-xxG8XLCb-D1SSFvMp.js +0 -1
  378. package/site/dist/assets/walletconnect-v2-3frQFwbg-CrtLXofr.js +0 -12
  379. package/site/dist/assets/wui-ux-by-reown-D34ILNLS.js +0 -1147
  380. package/site/dist/assets/x-rlSp9U7W.js +0 -1
  381. package/src/discover/explore.js +0 -271
  382. package/src/market/settle.js +0 -165
  383. package/src/market/terms.js +0 -200
  384. package/src/publish/cli.js +0 -336
  385. package/src/publish/envelope.js +0 -185
  386. package/src/publish/manifest.js +0 -142
@@ -3,43 +3,35 @@
3
3
  This walks an app owner from an empty directory to a live app that people can use in a
4
4
  browser and any agent can find, verify and query. By the end you have:
5
5
 
6
- - **an app** claimed on Fangorn, owned by your wallet;
7
- - **a schema** committed on chain: what your data's types are, which field is what, and
6
+ - **an app**: a repo under your wallet, `<your address>:<app>`; repo names are per publisher, so there is nothing to claim;
7
+ - **a schema** in `app.json`: what your data's types are, which field is what, and
8
8
  which relations are allowed between them;
9
- - **your data** committed on chain under your namespace;
9
+ - **your data** committed on chain into that repo;
10
10
  - **a view** of it: your committed data embedded and sharded as static files, so people
11
11
  and agents can search it by meaning, updated with one command after each commit;
12
- - **a static site** with no server: the view, and a page for people;
13
- - **an agent card** bound to the app on chain, and registered as an ERC-8004 agent;
14
- - **agents using it:** anyone running `fangorn-mcp` can `open-app` it by name.
12
+ - **a site**: the view, a page for people, and an MCP endpoint for agents (one Cloudflare
13
+ Worker, deployed by `westmarch-ship`);
14
+ - **an agent card** bound to the app on chain (the repo's `@card` record), and registered as an ERC-8004 agent;
15
+ - **agents finding it:** the Fangorn explorer indexes every publisher, so `fangorn-mcp`'s
16
+ `search` finds your records and names you as their publisher.
15
17
 
16
18
  Where the data comes from is up to you. The guide starts from a JSON array of objects,
17
19
  which anything can produce: a scraper, a database export, a converted spreadsheet.
18
20
 
19
21
  ```
20
- your data ─► data/graph.json ─► fangorn commit/push ─┐
21
- schema/graph.json ─► fangorn commit/push ────────────┤ (namespace fangorn.schema)
22
- ▼
23
- chain ─► westmarch-view ─► site/view/
24
- (embeds only what changed, applies the schema)
25
- app/ (page) ─► vite build ─► site/ ◄── the page loads site/view
26
- pipeline/agent-card.mjs ─► site/.well-known/agent-card.json (views: [site/view]) ─► deploy
27
- │
28
- fangorn app agent ─► ERC-8004 registration + AppRegistry agent_uri = card URL
29
- │
30
- fangorn-mcp: open-app my-app ─► reads the binding, verifies the card, searches the view
22
+ sources ─► fangorn commit/push ─► chain (<you>:<app>)
23
+ │
24
+ app.json (types) ─► the Worker's CONFIG
25
+ ▼
26
+ the Worker's cron ─► embeds what changed ─► view in R2 ─► page, /mcp, data tools
27
+ │
28
+ westmarch-ship ─► card at <site>/.well-known/agent-card.json
29
+ └─► fangorn card <url> --repo <app> ─► the repo's @card = card URL, + ERC-8004 agent
30
+ │
31
+ the explorer (indexes every publisher) ◄── fangorn-mcp: search, publishers, get, buy
31
32
  ```
32
33
 
33
- **Two tiers, one format.**
34
- - **Free, local:** `westmarch-view` (step 7). It turns your commits into a view on your
35
- own machine or CI. It embeds on CPU, ~14 records/s, only what changed since the last
36
- run, and you host the files with your site.
37
- - **Hosted:** quickbeam watches apps on chain, embeds on a GPU, and serves the view
38
- itself. It will also do things one app's files can't, such as state across apps. It
39
- writes the same files, so moving an app to it means changing the `views` URL in its
40
- card.
41
-
42
- It runs on Arbitrum Sepolia.
34
+ Everything runs on Base Sepolia: `"network": "baseSepolia"` in `app.json`.
43
35
 
44
36
  ## The short way: one config, one command
45
37
 
@@ -58,44 +50,80 @@ update: each step checks the chain (or Cloudflare) first and acts only on a diff
58
50
  "presentation": { "facets": ["category"] } } },
59
51
  "relations": [],
60
52
  "sources": [{ "namespace": "things", "command": ["python", "-m", "my_source"] }],
53
+ "network": "baseSepolia", // required
61
54
  "fangorn": "fangorn", // the CLI that signs
62
- "paid": { "price": "0.01", "network": "arbitrum-sepolia", "description": "the full record" }
55
+ "paid": { "price": "0.01", "description": "the full record" } // optional: USDC on the app's network, x402 v2
63
56
  }
64
57
  ```
65
58
 
66
59
  ```sh
67
- npx westmarch-ship app.json # claim, join, schema, crawl + publish, view, site, card, deploy, agent
60
+ npx westmarch-ship app.json # repo, crawl + publish, site, card, deploy, agent
68
61
  npx westmarch-ship app.json --dry-run # every step, printed, none done
69
62
  npx westmarch-ship app.json --no-crawl # rebuild view, site and card from what is on chain
70
63
  npx westmarch-ship app.json --replace # each source's crawl becomes its namespace's whole state
64
+ npx westmarch-ship app.json --index # build the deployed app's view once, on this machine
71
65
  ```
72
66
 
73
- What it does, in order (the long way, below, is the same steps by hand):
67
+ The app is one Cloudflare Worker: a cron that indexes the chain into R2 (embedding with
68
+ Workers AI), the view, `/mcp`, and the page. What `ship` does, in order:
74
69
 
75
- 1. **claim** the app on the first run, and record the claim block (the card's `fromBlock`);
76
- 2. **join** it as a publisher, if the wallet has not;
77
- 3. **commit the schema** (`types`, `relations`) to `fangorn.schema`, if the chain's differs;
78
- 4. **run each source**: a command that publishes into `--namespace` when given `--publish`.
79
- Every quickbeam scraper Source does. A source with `"each": "rows.json"` is a template,
70
+ 1. **create the app's repo** (`fangorn repo init <app>`) on the first run, and record that block (the card's `fromBlock`);
71
+ 2. **run each source**: a command that publishes into `--namespace` when given `--publish`.
72
+ A source with `"each": "rows.json"` is a template,
80
73
  one source per row (`{field}`, `{field|slug}`, `{args...}`), so adding a town, a feed or
81
74
  an account is adding a row;
82
- 5. **westmarch-view**: the app's commits → `.ship/site/view`, embedding only what changed.
83
- A removal rewrites the domain, so a retracted record's bytes stop being served;
84
- 6. **the site**: the stock page (search, plus a feed of what is coming up and what just
85
- happened, by the `temporal` role and the first `facets` field), the agent card and
86
- `_headers`. With `paid`, also `_worker.js`, which sells each record over x402; only
87
- records whose sha256 a live row carries are for sale;
88
- 7. **deploy** to Cloudflare Pages (`wrangler`; the project is created if missing);
89
- 8. **register** the ERC-8004 agent and bind the card, if the chain points elsewhere.
75
+ 3. **the site** in `.ship/site`: the stock page (search, plus a feed of what is coming up and
76
+ what just happened, by the `temporal` role and the first `facets` field; for rows that are
77
+ not meetings, an explore page: `site.examples` as searches to try, the latest records by
78
+ `temporal`, and each declared facet's values to browse), the agent card,
79
+ `/openapi.json` and `/.well-known/x402` when anything is for sale, and `_headers`;
80
+ 4. **deploy the Worker** (`wrangler deploy`): its R2 bucket (created if missing), Workers AI,
81
+ the cron (`site.cron`, default every 5 minutes) and its CONFIG, which carries the schema. With `paid`, it sells each
82
+ record over x402; only records whose sha256 a live row carries are for sale. If the app
83
+ has no view yet, ship builds the first one here (`--index`) rather than wait for the cron;
84
+ 5. **bind the card** (`fangorn card <url> --repo <app>`): the repo's `@card` record names the
85
+ card URL, and an ERC-8004 agent is minted from the card. Only when `@card` points
86
+ elsewhere; an agent is minted once, later runs only rebind.
90
87
 
91
88
  State lives in `.ship/` next to `app.json`. In CI, cache `.ship/cache` and `.ship/stage`
92
- and commit `.ship/state.json`; Quorum's `.github/workflows/ship.yml` is a working example
93
- (daily cron, and on every push to its config).
94
-
95
- **What was run:** all of it, for Quorum (`https://quorum-bua.pages.dev`, agent `421614:245`):
96
- five towns' meetings crawled, published, embedded and deployed by `westmarch-ship`, then
97
- searched and bought (x402, settled on Arbitrum Sepolia) from `fangorn-mcp` in a fresh
98
- session. Kingsfoil, the reference at the end, predates `ship` and was built by hand.
89
+ and commit `.ship/state.json`.
90
+
91
+ ## A private app: the data is the product
92
+
93
+ `"private": { "pass": { "price": "5", "days": 30 }, "call": "0.01" }` in `app.json`, settled like
94
+ `paid` (`network`, `payTo`, `asset`, `facilitator`). What changes:
95
+
96
+ - **Real money.** Settlement is set apart from the app's chain: `"network": "base"` in `private`
97
+ (or `paid`) charges USDC on Base while the app and its agent stay on Base Sepolia, where the
98
+ Fangorn contracts are. Set `payTo` to a wallet you control and have backed up; it defaults to
99
+ the owner key ship signs with. On a mainnet the page's button opens Privy's card funding
100
+ (enable it in the Privy dashboard) instead of the faucet. Try a low price first.
101
+
102
+ - **Storage.** Each source stages its records (it needs `--output-dir`); ship seals every row whole,
103
+ `{sealed: base64(nonce ‖ AES-256-GCM(rowKey, row))}` with rowKey = HMAC-SHA256(PAID_SECRET,
104
+ "fangorn.row.v1"), and commits that. The nonce is derived from the row, so an unchanged row keeps
105
+ its CID and is not re-embedded. PAID_SECRET is `$PAID_SECRET` or made once in `.ship/secret`:
106
+ **back it up**, it is the only way to open the rows.
107
+ - **Index.** The Worker (which ship gives the secret on every deploy) opens rows in its cron and
108
+ builds the view in R2 as for any app. `--index` does the same once on your machine
109
+ (`wrangler dev` against the app's own R2 and Workers AI), for the first view or a catalog too
110
+ big for one cron run.
111
+ - **Serving.** `/view/*` (but `cdn/catalog`) and every MCP tool but `describe` need access: a pass
112
+ (`POST /pass`, x402 v2, a bearer token good for `days`) or one x402 payment per tool call over
113
+ x402's MCP transport (`_meta["x402/payment"]`). A call that fails or misses is not charged.
114
+ An agent pays with any x402 client pointed at the app's `/mcp`.
115
+ - **Discovery.** The card's Fangorn extension carries `access` (pass URL and prices), and its
116
+ skills are tagged `x402`, so the ERC-8004 registration says `x402Support: true`.
117
+ `/openapi.json` and `/.well-known/x402` list `POST /pass` for x402scan; the facilitator's Bazaar
118
+ lists `/pass` and each tool (as `type: "mcp"`, resource `<app>/mcp`) after its first sale, so make
119
+ one paid call yourself (paying from `payTo` is accepted). Check with
120
+ `curl "https://facilitator.payai.network/discovery/resources?payTo=<payTo>"`. Mainnet agents find
121
+ the app here, since its ERC-8004 registration is on Base Sepolia.
122
+ - **The page.** Without a pass it shows what is inside (the free catalog and `describe`: how many
123
+ records, when they were updated), a "Get a pass" button (Privy sign-in, then x402; on a testnet
124
+ it links the faucet when the balance is short), and the lines to add the app to an agent
125
+ (`#/agents`). The pass is kept on the device and sent with every read, and the agent line
126
+ carries it too. Add the app's origin to Privy's allowed origins.
99
127
 
100
128
  ## Grading a change before it ships
101
129
 
@@ -142,574 +170,63 @@ Without labels, it also searches 200 sampled rows by their own titles (`known@1`
142
170
 
143
171
  Worse is: mean precision or `known@10` down more than 0.05, any question the base
144
172
  answered now answered by nothing, a `hard` record check rising, or any floor or ceiling
145
- missed. Quorum's `.github/workflows/eval.yml` runs this on every pull request, from the
146
- records and vectors its `ship.yml` leaves in the cache, with no secrets; make it a
147
- required check on `main`, and put `eval/`, `.github/` and `app.json` under `CODEOWNERS`,
173
+ missed. Run it in CI on every pull request, from the records and vectors a scheduled
174
+ `westmarch-ship` leaves in its cache, with no secrets; make it a required check on `main`, and put `eval/`, `.github/` and `app.json` under `CODEOWNERS`,
148
175
  or a PR can change the gate along with what it gates.
149
176
 
150
177
  What is not graded yet: a change to how a source *parses*. `--local` reads what the
151
178
  sources last staged; it does not replay their parsing over the raw documents.
152
179
 
153
- ## The long way, by hand
154
-
155
- What `ship` does, one step at a time: for understanding it, or for doing one step differently.
156
-
157
- ### What you need
158
-
159
- - Node 22.
160
- - The Fangorn CLI: `npm i -g @fangorn-network/sdk@2026.9.22-dev` (gives `fangorn`).
161
- - A wallet on Arbitrum Sepolia with a little ETH. **It owns the app forever**, so use the
162
- wallet that will publish the data.
163
- - A Pinata JWT. Registering the ERC-8004 agent (step 11) pins a file to IPFS.
164
- - A static host that serves `/.well-known/` and custom headers. Cloudflare Pages does
165
- both, and is what the commands below use.
166
-
167
- Start the project:
168
-
169
- ```sh
170
- mkdir my-app && cd my-app && npm init -y && npm pkg set type=module
171
- npm i @fangorn-network/westmarch @huggingface/transformers vite
172
- mkdir -p data schema pipeline app
173
- ```
174
-
175
- The Fangorn SDK and viem come with westmarch as peer dependencies. transformers embeds:
176
- your records in `westmarch-view`, and the reader's query in their browser.
177
-
178
- ## 1. Set up the CLI
179
-
180
- ```sh
181
- fangorn init # writes ~/.fangorn/config.json: key, Pinata JWT, gateway
182
- fangorn wallet # confirm which address will sign
183
- fangorn set-app my-app # the app every later command uses; --app <name> overrides it once
184
- ```
185
-
186
- > The config file wins over `ETH_PRIVATE_KEY` in the environment. If `fangorn wallet`
187
- > shows the wrong address, that's why.
188
-
189
- ## 2. Claim the app
190
-
191
- First, note the current block. It becomes the card's `fromBlock` (step 9), which is where
192
- readers start scanning the chain for your app:
193
-
194
- ```sh
195
- node -e 'const {createPublicClient,http}=require("viem");const {arbitrumSepolia}=require("viem/chains");
196
- createPublicClient({chain:arbitrumSepolia,transport:http()}).getBlockNumber().then(String).then(console.log)'
197
- ```
198
-
199
- Then claim the name:
200
-
201
- ```sh
202
- fangorn app info # "unclaimed", and the wallet you expect
203
- fangorn app claim # first come, first served; permanent
204
- ```
205
-
206
- With no flags, `claim` uses placeholder terms. Publish real ones later with
207
- `fangorn app terms <hash> <uri>`; anyone who joined under the old terms has to accept again.
208
- `--fee <wei>` sets what joining costs other publishers.
209
-
210
- ## 3. Become a publisher in it
211
-
212
- Owning the app makes you its first publisher. You still need global standing:
213
-
214
- ```sh
215
- fangorn register # DataRegistry.register(), then joins this app
216
- fangorn app info # DataRegistry: registered · This app: joined
217
- ```
218
-
219
- Without the join, every push reverts with `NotRegisteredForApp`.
220
-
221
- ## 4. Your data, as a graph
222
-
223
- A commit is a graph: vertices, each with an `id`, a `tag` (its type) and a `payload` (the
224
- record), and optional edges between them. Start from rows, `data/rows.json`:
225
-
226
- ```json
227
- [
228
- { "id": "oak", "name": "Oak", "species": "Quercus robur", "family": "Fagaceae", "region": "Europe",
229
- "description": "Long-lived deciduous tree with lobed leaves and acorns; hard durable timber …" },
230
- { "id": "baobab", "name": "Baobab", "species": "Adansonia digitata", "family": "Malvaceae", "region": "Africa",
231
- "description": "Massive trunk stores water through the dry season; fruit pulp is rich in vitamin C." }
232
- ]
233
- ```
234
-
235
- ```js
236
- // pipeline/graph.mjs — data/rows.json → data/graph.json
237
- import { readFileSync, writeFileSync } from "node:fs";
238
- const rows = JSON.parse(readFileSync("data/rows.json", "utf8"));
239
- writeFileSync("data/graph.json", JSON.stringify({
240
- vertices: rows.map((r) => ({ id: r.id, tag: "my-app.tree.v1", payload: r })),
241
- // edges: [{ rel: "grows_in", from: "oak", to: "europe" }] // between vertex ids, if you have them
242
- }));
243
- ```
244
-
245
- What makes records work well:
246
-
247
- - **A stable id.** The same thing keeps the same id across commits.
248
- - **Text that says what the record is, in words.** This is what gets embedded, so it's
249
- what "search by meaning" matches. A title alone is weak; a sentence or a paragraph is good.
250
- - **Fields worth filtering or counting on**, such as a category, a region, or a date. Agents
251
- use these with `where` and `count`.
252
- - **One tag per kind of thing.** Version it (`.v1`) so a later shape can be a new type.
253
-
254
- ## 5. Declare the schema
255
-
256
- The schema says what your data is. You commit it to a reserved namespace,
257
- `fangorn.schema`, in your app:
258
- - **Types:** each is a vertex tagged `fangorn.type.v1`.
259
- - **Relations:** the relations allowed between types are edges between those vertices.
260
-
261
- `westmarch-view` (and hosted quickbeam) read it for every record they embed, so it decides:
262
- - **what gets embedded:** the `text` role, plus title, subtitle and tags;
263
- - **what readers see:** titles, links, labels;
264
- - **what agents can do:** filters, counts, and choosing among views by their description.
265
-
266
- `schema/graph.json`:
267
-
268
- ```json
269
- {
270
- "vertices": [
271
- { "id": "tree", "tag": "fangorn.type.v1", "payload": {
272
- "tag": "my-app.tree.v1",
273
- "description": "Tree species: what they are, where they grow, and what they are good for.",
274
- "role_map": { "identity": "id", "title": "name", "subtitle": "species",
275
- "tags": ["family", "region"], "text": ["description"] },
276
- "presentation": { "externalUrl": { "my-app.tree.v1": "https://en.wikipedia.org/wiki/{species}" } } } }
277
- ],
278
- "edges": []
279
- }
280
- ```
281
-
282
- ```sh
283
- cd schema && fangorn repo init fangorn.schema \
284
- && fangorn commit graph.json -m "schema v1" --replace && fangorn push && cd ..
285
- ```
286
-
287
- `fangorn repo init` tracks one namespace per directory (in `.fangorn/`), which is why the
288
- schema is committed from its own `schema/` folder.
289
-
290
- **`role_map`** says which field is what:
291
-
292
- | role | means | used for |
293
- |---|---|---|
294
- | `identity` | the id field | `get`, links, updates |
295
- | `text` | the field(s) whose words get embedded | search by meaning |
296
- | `title`, `subtitle` | what a record is called | result lists, and embedded too |
297
- | `tags` | categories | filters, `count`, and embedded too |
298
- | `temporal`, `spatial`, `measures`, `relations`, `media` | dates, places, numbers, links to other records, playable media | sorting, facets, previews |
299
-
300
- **Rules:**
301
- - **Declared means declared.** A role you leave out stays empty; it isn't guessed.
302
- - **Undeclared types are still guessed:** a type the schema doesn't mention gets a role
303
- map inferred from its fields. That works for display and often fails for search: a
304
- field called `desc` or `nct` isn't recognized, and a `price` can end up as the date.
305
- Declare every type you publish.
306
- - **Only the app owner's schema counts.** Any publisher can write a namespace called
307
- `fangorn.schema`; only the owner's is read.
308
- - **You can change it at any time** with another commit. A view takes the new role map and
309
- description on its next `westmarch-view` run. **Records already embedded keep the text they were embedded
310
- with** until they're re-embedded, so change the `text` role early.
311
- - **Relations** (edges between type vertices, e.g. `{ "rel": "grows_in", "from": "tree",
312
- "to": "region" }`) declare which edges the app allows. Nothing enforces them yet; they
313
- are what publisher graphs will be validated against.
314
-
315
- ## 6. Commit and push the data
316
-
317
- ```sh
318
- node pipeline/graph.mjs
319
- fangorn repo init my-app # the data namespace, tracked in ./.fangorn
320
- fangorn commit data/graph.json -m "first data" --replace # local; builds and uploads the commit
321
- fangorn push # the on-chain transaction
322
- ```
323
-
324
- `--replace` makes the file the namespace's whole state, so records you removed are
325
- removed. Without it, the commit adds to what's already there. `fangorn status` compares
326
- your local tip with the one on chain.
327
-
328
- ## 7. Publish the view
329
-
330
- ```sh
331
- npx westmarch-view --app my-app --namespace my-app --out site/view --from-block 311700000
332
- # my-app: 1 publisher(s) since block 311700000
333
- # …-my-app: +20 embedded, -0 removed, 20 live
334
- ```
335
-
336
- It reads the chain and writes files; it needs no key. For every publisher of each
337
- `--namespace` in the app, it:
338
- - reads their records;
339
- - embeds the ones the view doesn't hold yet, with your schema's roles;
340
- - marks the ones that were removed as deleted;
341
- - writes a new shard;
342
- - refits the coverage sketch once the view has doubled.
343
-
344
- The view's own files are its state, so a second run with nothing new on chain changes
345
- nothing, and it runs the same on a laptop, in CI, or from an agent. The first run
346
- downloads the embedding model (131 MB, kept in `~/.cache/fangorn-mcp/models`, shared with
347
- `fangorn-mcp`). Use `--from-block` with the block you noted in step 2.
348
-
349
- ```
350
- site/view/cdn/catalog every publisher's domain, with descriptions and coverage
351
- site/view/cdn/domains/<app>-<publisher>-<ns>/manifest role map, embedder, shards with their sha256, tombstones
352
- site/view/cdn/domains/<app>-<publisher>-<ns>/shards/shard-NNNN-<sha>.ndjson.gz
353
- ```
354
-
355
- Check it the way a reader will:
356
-
357
- ```sh
358
- (cd site && python3 -m http.server 8765 &)
359
- npx westmarch-lint http://127.0.0.1:8765/view
360
- # …-my-app — 20 rows — nothing to fix
361
- ```
362
-
363
- `lint.js` reports three levels:
364
- - **blocking:** readers can't find you (no coverage, wrong model);
365
- - **findable, but not readable** (no text role);
366
- - **readable, but a dead end** (nowhere for a hit to link to).
367
-
368
- The fix for most findings is in the schema (step 5).
369
-
370
- A view remembers which embedder made it, and `westmarch-view` refuses to add to a view
371
- another one built. Vectors from two encoders don't compare. Pass `--rebake` to start
372
- a view over.
373
-
374
- ## 8. The page
375
-
376
- A page for people, with the same search exposed to agents in the browser as WebMCP tools.
377
- It reads the view: shards are checked against their manifests' sha256, the query is
378
- embedded in the reader's tab, and nothing about the query leaves it.
180
+ ## How agents find your data
379
181
 
380
- ```js
381
- // app/agent.js — the page's tools for agents in a browser. `ctx` holds the loaded rows.
382
- import { search, getRow } from "@fangorn-network/westmarch/tools";
383
-
384
- const ok = (o) => ({ content: [{ type: "text", text: JSON.stringify(o) }] });
385
-
386
- export function registerAgent(ctx) {
387
- const mc = document.modelContext;
388
- if (!mc?.registerTool) return; // most browsers have no WebMCP yet
389
- mc.registerTool({
390
- name: "search-trees",
391
- description: "Search tree species by meaning: a question in plain words. Call this first.",
392
- inputSchema: { type: "object", properties: { query: { type: "string" }, limit: { type: "number" } }, required: ["query"] },
393
- execute: async ({ query, limit = 10 }) =>
394
- ok(search(ctx.rows, query, ctx.roles, { qv: await ctx.queryVector(query), limit })),
395
- });
396
- mc.registerTool({
397
- name: "get-tree",
398
- description: "One tree species in full, by the id search returns.",
399
- inputSchema: { type: "object", properties: { id: { type: "string" } }, required: ["id"] },
400
- execute: async ({ id }) => ok(getRow(ctx.rows, id, ctx.roles) ?? { error: `no tree ${id}` }),
401
- });
402
- }
403
- ```
404
-
405
- ```js
406
- // app/main.js
407
- import { configure, loadShard } from "@fangorn-network/westmarch/shard";
408
- import { rolesFrom, textOf } from "@fangorn-network/westmarch/roles";
409
- import { search } from "@fangorn-network/westmarch/tools";
410
- import { embedQuery } from "@fangorn-network/westmarch/embed";
411
- import { registerAgent } from "./agent.js";
412
-
413
- export const VIEW = new URL("view", location.href).href; // step 7
414
-
415
- const ctx = { rows: [], roles: rolesFrom([]), queryVector: (q) => embedQuery(q).catch(() => null) };
416
- configure({ onManifests: (ms) => { ctx.roles = rolesFrom(ms); }, rowText: (f) => textOf(f, ctx.roles) });
417
- ctx.rows = await loadShard(VIEW); // checked against the manifests' sha256
418
- registerAgent(ctx); // only now: a tool called before its data loads answers wrong, silently
419
-
420
- // The human side: the same search, as a list.
421
- const input = document.querySelector("input"), list = document.querySelector("ul");
422
- input.placeholder = `Search ${ctx.rows.length} trees…`;
423
- input.oninput = async () => {
424
- const q = input.value;
425
- const hits = search(ctx.rows, q, ctx.roles, { qv: await ctx.queryVector(q), limit: 10 });
426
- if (q !== input.value) return; // a newer keystroke won
427
- list.replaceChildren(...hits.map((h) => {
428
- const li = document.createElement("li"), a = document.createElement("a");
429
- a.textContent = h.title; if (h.url) a.href = h.url;
430
- li.append(a, ` — ${h.subtitle ?? ""}`);
431
- return li;
432
- }));
433
- };
434
- ```
435
-
436
- ```html
437
- <!-- app/index.html -->
438
- <!doctype html>
439
- <meta charset="utf-8">
440
- <meta name="viewport" content="width=device-width, initial-scale=1">
441
- <title>My App</title>
442
- <h1>My App</h1>
443
- <input type="search" autofocus placeholder="Loading…">
444
- <ul></ul>
445
- <script type="module" src="./main.js"></script>
446
- ```
447
-
448
- ```js
449
- // app/vite.config.js
450
- export default {
451
- base: "./",
452
- // westmarch's embedder finds its worker by `new URL(…, import.meta.url)`;
453
- // pre-bundling loses that and search silently drops to word matching.
454
- optimizeDeps: { exclude: ["@fangorn-network/westmarch"] },
455
- build: { outDir: "../site", emptyOutDir: false }, // keep site/view
456
- };
457
- ```
458
-
459
- ```sh
460
- (cd app && npx vite build)
461
- ```
462
-
463
- `@fangorn-network/westmarch/tools` also has `describe`, `browse`, `facet` and `neighbors`,
464
- if the page needs more than search. `watchShard(fn)` tells the page when the view gains
465
- shards, if it should update without a reload.
466
-
467
- ## 9. The agent card
468
-
469
- A WebMCP tool only exists inside a loaded tab, so the card is the only machine-readable
470
- record that the app, its tools and its view exist. The tool list isn't written by hand:
471
- `captureTools` runs your register function against a recording stub, so the card lists
472
- exactly what the page registers. No `execute` runs.
473
-
474
- ```js
475
- // pipeline/agent-card.mjs — site/ → site/.well-known/agent-card.json + site/_headers
476
- import { mkdirSync, readdirSync, writeFileSync } from "node:fs";
477
- import { agentCard, captureTools } from "@fangorn-network/westmarch/agent-card";
478
- import { registerAgent } from "../app/agent.js";
479
-
480
- const SITE = "https://my-app.pages.dev";
481
- const VIEW = `${SITE}/view`; // step 7; a hosted quickbeam view's URL goes here instead
482
-
483
- const card = agentCard({
484
- name: "My App",
485
- description: "Tree species: what they are, where they grow, and what they are good for. Search by meaning.",
486
- url: `${SITE}/`, // the page that registers the tools
487
- version: "1",
488
- tools: await captureTools(() => registerAgent({})),
489
- tags: ["trees"],
490
- fangorn: {
491
- app: "my-app", // the name you claimed
492
- fromBlock: 311700000, // the block you noted before claiming
493
- namespaces: ["my-app"], // your data namespace(s)
494
- views: [VIEW],
495
- },
496
- });
497
- mkdirSync("site/.well-known", { recursive: true });
498
- writeFileSync("site/.well-known/agent-card.json", JSON.stringify(card, null, 1));
499
-
500
- // The card is read from other origins, so it needs CORS, and so does the view, for
501
- // agents and pages elsewhere. Bundles and shards are named by their digest, so they
502
- // never change: tell caches so. Shards only in the reader's own browser (`private`): a
503
- // retracted record's bytes must stop being served by shared caches once the deploy drops
504
- // its shard. (Pages allows one * per rule, hence a rule per domain.)
505
- const immutable = " Cache-Control: public, max-age=31536000, immutable\n";
506
- const mine = " Cache-Control: private, max-age=31536000, immutable\n";
507
- writeFileSync("site/_headers",
508
- "/.well-known/agent-card.json\n Access-Control-Allow-Origin: *\n"
509
- + "/view/*\n Access-Control-Allow-Origin: *\n"
510
- + `/assets/*\n${immutable}`
511
- + readdirSync("site/view/cdn/domains").map((d) => `/view/cdn/domains/${d}/shards/*\n${mine}`).join(""));
512
- ```
513
-
514
- ```sh
515
- node pipeline/agent-card.mjs
516
- ```
517
-
518
- - **The `fangorn` block** becomes an A2A extension (`https://fangorn.network/a2a/app/v1`).
519
- It holds the chain, the registry addresses (taken from the SDK, so they can't drift), your
520
- appId, `fromBlock`, `namespaces` and `views`. `agentCard` validates them the same way
521
- readers will, so a bad card fails your build, not someone else's discovery.
522
- - **`views` is what agents search:** `fangorn-mcp` reads it directly, with no browser.
523
- - **`url` is the page that registers the tools,** not just any page on the site.
524
- - **`fromBlock`** only affects speed: readers query logs 1000 blocks per call, so an early
525
- block is slow, and 0 means hundreds of thousands of calls.
526
- - **Keep these values fixed.** A rebuild that drops the extension or changes `url`
527
- publishes a card every reader rejects.
528
-
529
- ## 10. Deploy and check
530
-
531
- ```sh
532
- npx wrangler pages deploy site --project-name my-app
533
- curl -sD - https://my-app.pages.dev/.well-known/agent-card.json -o /dev/null | grep -iE "^HTTP|access-control"
534
- # HTTP/2 200 · access-control-allow-origin: *
535
- ```
536
-
537
- Open `https://my-app.pages.dev/` and search: that's the app for people. The card must be
538
- live before step 11, because the CLI fetches it.
539
-
540
- ## 11. Register the agent and bind the card
541
-
542
- ```sh
543
- fangorn app agent https://my-app.pages.dev/.well-known/agent-card.json
544
- # Agent ID: 421614:…
545
- # Card: https://my-app.pages.dev/.well-known/agent-card.json
546
- # Tx: 0x…
547
- ```
548
-
549
- This does two things:
550
-
551
- 1. **ERC-8004.** It builds a registration file from the card (name, description, A2A
552
- endpoint = the card URL, trust = reputation, x402 only if a skill is tagged `x402`),
553
- pins it to Pinata, and mints an agent in the identity registry at
554
- `0x8004A818BFB912233c491871b3d84c89A494BD9e`. That's the same address as on Ethereum
555
- and Base Sepolia. [8004scan](https://8004scan.io) indexes it within minutes.
556
- 2. **Fangorn.** It calls `setAppAgentUri(cardUrl)`, which emits `AppAgentChanged`. That log
557
- is the app directory: `list-apps` and `listApps` read it.
558
-
559
- `--skip-register` does only the second step, e.g. to move an already-registered card to a
560
- new URL.
561
-
562
- ## 12. Use it as an agent would
182
+ Through the **Fangorn explorer** (`https://explorer.fangorn.network`), the app that indexes
183
+ every publisher. Once your records are pushed, its next index run (every few minutes) picks
184
+ them up, and anyone running `fangorn-mcp` finds them with `search`, sees your repos under
185
+ `publishers`, and buys what you sell with `buy`. You register nothing for this: being a
186
+ publisher is enough. [docs/mcp.md](mcp.md) has the setup.
563
187
 
564
- Register `fangorn-mcp` once (see [Using Fangorn apps from an agent](#using-fangorn-apps-from-an-agent)),
565
- then in any agent:
566
-
567
- ```
568
- open-app my-app → verified: true, data_tools: true
569
- my-app__describe → the view's domains, with the schema's description
570
- my-app__search "trees that survive fire" → cork oak, oak, redwood (ranked by meaning)
571
- my-app__get "baobab" → the whole record
572
- ```
573
-
574
- `open-app` reads your app's binding from the registry, fetches the card, and checks that
575
- the binding points back at exactly that URL. Only then does it add the tools.
576
-
577
- ## 13. Update it
578
-
579
- New or changed data is a commit, then the view, then a deploy:
580
-
581
- ```sh
582
- node pipeline/graph.mjs && fangorn commit data/graph.json -m "…" --replace && fangorn push
583
- npx westmarch-view --app my-app --namespace my-app --out site/view --from-block 311700000
584
- node pipeline/agent-card.mjs && npx wrangler pages deploy site --project-name my-app
585
- ```
586
-
587
- Only what changed is embedded and uploaded. The page and agents see it on their next
588
- load, since the view URL, the card and the on-chain binding all stay the same. Run
589
- `fangorn app agent` again only if the card moves. Anything that can run those three lines
590
- can keep an app current: a cron job, a CI workflow, or an agent that just published.
591
-
592
- ---
593
-
594
- ## Using Fangorn apps from an agent
595
-
596
- `fangorn-mcp` is one MCP server for every Fangorn app: a new app needs no new
597
- registration. Install it through npm (node ≥ 20):
598
-
599
- ```sh
600
- claude mcp add fangorn -e FANGORN_LOG_WINDOW=100000 -- \
601
- npx -y -p @fangorn-network/westmarch -p @huggingface/transformers fangorn-mcp
602
- ```
603
-
604
- Without `-p @huggingface/transformers`, the install is ~500 MB lighter and `search` ranks by
605
- words, not meaning. Without `FANGORN_LOG_WINDOW`, `list-apps` scans the chain 1,000 blocks
606
- per call and takes minutes.
607
-
608
- [docs/mcp.md](mcp.md) has the full setup: other MCP clients, every setting, running from
609
- source, and troubleshooting. A single-file binary (`bun build-bin.js`) exists but is not
610
- published yet.
611
-
612
- It starts with three tools:
613
- - **`list-apps`** reads the apps off the chain and keeps the ones whose cards verify.
614
- - **`open-app <name>`** reads that app's binding from the registry (one call), verifies its
615
- card, and adds the app's tools as `<name>__<tool>`.
616
- - **`call-app-tool`** reaches the same tools, for clients that don't refresh their tool list.
617
-
618
- **Two kinds of tools.**
619
- - **Data tools:** an app whose card names `views` gets these at once, with no browser:
620
- `describe`, `search`, `get`, `similar`, `count`, `browse`. westmarch runs them over the
621
- app's shards, checked against their manifests' sha256. `search` reads the 3 views whose
622
- coverage sketch best matches the query and names the next candidates.
623
- - **Page tools:** `open-app` with `page: true` also opens `card.url` in a browser for the
624
- page's own WebMCP tools. An app with no views gets only these.
625
-
626
- **How fast** (Kingsfoil: 23 views, 60,760 trials):
627
-
628
- | | time |
629
- |---|---|
630
- | `open-app` | 0.4–2 s |
631
- | `describe` | under 5 ms |
632
- | first `search`, caches warm | 0.4–1.8 s |
633
- | later searches | ~0.2–0.5 s |
634
- | first `search` from an empty machine (single file) | ~3.5 s, by words while the model downloads |
635
- | page tools (browser) | 5–15 s to open, ~2 GB of memory |
636
-
637
- On a new machine, the embedding model (131 MB, kept in `~/.cache/fangorn-mcp/models`)
638
- downloads in the background. Until it lands, `search` ranks by words and says so in
639
- `ranked_by`; then it switches to meaning. Shards are downloaded once and kept by content
640
- hash.
641
-
642
- **Where the browser comes from (page tools only).** Anything that speaks the Chrome DevTools
643
- Protocol and has WebMCP will do: a headless Chromium service, a hosted browser, or the local
644
- Chrome.
645
-
646
- | setting | browser |
647
- |---|---|
648
- | `FANGORN_MCP_CDP=http://host:9222` (or `--cdp`) | a running browser's DevTools address |
649
- | `FANGORN_MCP_CDP=ws://host:9222/devtools/browser/…` | a browser WebSocket endpoint, which is what hosted services hand out |
650
- | neither | the local Chrome 150+ (`CHROME=/path` if it isn't `google-chrome`), headless (`--headed` to watch) |
651
-
652
- A remote browser is never closed; only the tabs the server opened are. A local Chrome is
653
- shared by every session on the machine and closes when the last one exits. Its profile is
654
- `~/.cache/westmarch-mcp/`. `--from-block <n>` points the server at another deployment.
188
+ Your app's own Worker still serves its page and its own MCP endpoint at `/mcp`, with your
189
+ schema and your tools, for agents that come to your app directly.
655
190
 
656
191
  ## Discovering apps in code
657
192
 
658
- Every route ends in `discoverApp(cardUrl)`, which accepts a card only if:
659
-
660
- - the card carries the Fangorn extension for **this** chain and **this** AppRegistry, and
661
- - `appAgentUri(card.appId)` on chain is **exactly** the card URL.
193
+ An app is a publisher's repo whose `@card` record points at an agent card that points back.
194
+ `discoverCard(cardUrl)` accepts a card only if:
662
195
 
663
- The registry and the index are where you look. The on-chain binding is what you trust.
664
- `examples/demo/discover.mjs` runs all three routes:
665
-
666
- ```sh
667
- node examples/demo/discover.mjs card https://kingsfoil.pages.dev/.well-known/agent-card.json
668
- node examples/demo/discover.mjs chain 311637349 # every app bound since this block
669
- node examples/demo/discover.mjs search Kingsfoil # the public ERC-8004 index
670
- ```
196
+ - the card carries the Fangorn repo extension for **this** chain and **this** DataRegistry,
197
+ naming a publisher and a repo, and
198
+ - that repo's `@card` on chain is **exactly** the card URL.
671
199
 
672
200
  **From a card URL:**
673
201
 
674
202
  ```js
675
- import { Fangorn, FangornConfig } from "@fangorn-network/sdk";
203
+ import { Fangorn } from "@fangorn-network/sdk";
676
204
  import { generatePrivateKey } from "viem/accounts";
677
205
  // Fangorn.create needs a signer; a throwaway key is fine for reading.
678
- const fangorn = Fangorn.create({ privateKey: generatePrivateKey(), config: FangornConfig });
679
- const { card, appId, fromBlock, namespaces } = await fangorn.discoverApp(cardUrl);
680
- ```
681
-
682
- **From the chain** (every Fangorn app; no index, no server):
683
-
684
- ```js
685
- import { listApps } from "@fangorn-network/westmarch/apps";
686
- const { apps, rejected } = await listApps(fangorn, { fromBlock: 311637349n });
687
- // apps: [{ name, appId, card, url, views, namespaces, fromBlock }]
688
- // rejected: [{ card, why }]: unreachable or not bound, reported rather than dropped
206
+ const fangorn = Fangorn.create({ privateKey: generatePrivateKey() });
207
+ const { card, publisher, repo } = await fangorn.discoverCard(cardUrl);
689
208
  ```
690
209
 
691
210
  **From ERC-8004** (for general-purpose agents that know nothing about Fangorn):
692
211
 
693
212
  ```js
694
213
  const api = "https://8004scan.io/api/v1/agents";
695
- const { items } = await (await fetch(`${api}?chain_id=421614&search=Kingsfoil`)).json();
696
- const detail = await (await fetch(`${api}/421614/${items[0].token_id}`)).json();
697
- const found = await fangorn.discoverApp(detail.services.a2a.endpoint);
214
+ const { items } = await (await fetch(`${api}?chain_id=84532&search=My%20App`)).json();
215
+ const detail = await (await fetch(`${api}/84532/${items[0].token_id}`)).json();
216
+ const found = await fangorn.discoverCard(detail.services.a2a.endpoint);
698
217
  ```
699
218
 
700
219
  **The committed data itself:**
701
220
 
702
221
  ```js
703
- fangorn.setAppId(appId);
704
- const timelines = await fangorn.appNamespaces({ namespace: namespaces[0], fromBlock });
705
- const { contents } = await fangorn.readNamespace(timelines[0].owner, namespaces[0]);
222
+ const { contents } = await fangorn.readRepo(publisher, repo);
706
223
  ```
707
224
 
708
225
  ## What the binding proves, and what it doesn't
709
226
 
710
- - **Proves:** whoever owns the app on chain chose this card URL. Anyone can serve a card
711
- claiming any appId, and `discoverApp` rejects it unless the app points back at that exact
712
- URL. This is tested (`src/discover-app.test.ts`, `src/app-agent.e2e.test.ts`).
227
+ - **Proves:** the repo's publisher chose this card URL. Anyone can serve a card naming any
228
+ repo, and `discoverCard` rejects it unless the repo's `@card` points back at that exact
229
+ URL. This is tested in the SDK (`src/discover-card.test.ts`, `src/e2e/cards.e2e.test.ts`).
713
230
  - **Doesn't prove:** that the page behind `card.url` behaves as described, or that the card
714
231
  wasn't changed after binding. The card is live HTTP, not content-addressed, and the
715
232
  binding is to the URL. That's deliberate: the card is mostly endpoints, which change, and
@@ -720,47 +237,23 @@ const { contents } = await fangorn.readNamespace(timelines[0].owner, namespaces[
720
237
 
721
238
  | symptom | cause |
722
239
  |---|---|
723
- | push reverts `NotRegisteredForApp` | registered globally but not joined; `fangorn register` does both |
724
- | `not bound to app … on-chain agent_uri is …` | URLs must match exactly. A trailing slash, `http` vs `https`, or a different path counts as a different card. |
240
+ | card rejected as not bound | URLs must match exactly. A trailing slash, `http` vs `https`, or a different path counts as a different card. |
725
241
  | card rejected after a rebuild | the build dropped the `fangorn` block or changed `fromBlock`/`url`; keep them fixed |
726
242
  | discovery works in Node, fails in a browser | no `Access-Control-Allow-Origin` on the card |
727
- | `listApps` takes forever | `fromBlock` is too early; the scan is 1000 blocks per RPC call |
728
- | `app agent`: "ERC-8004 registration pins the registration file to IPFS" | no Pinata JWT in `fangorn init` |
729
- | `app agent` signs as the wrong wallet | `~/.fangorn/config.json` beats `ETH_PRIVATE_KEY` |
243
+ | `fangorn card`: "ERC-8004 registration pins the registration file to IPFS" | no Pinata JWT in `fangorn init` |
244
+ | `fangorn card` signs as the wrong wallet | `~/.fangorn/config.json` beats `ETH_PRIVATE_KEY` |
245
+ | `fangorn card`: "replacement transaction underpriced" | a nonce race after the mint; the agent exists, so rerun with `--skip-register` |
246
+ | the 8004 registration says `x402Support: false` | no card skill is tagged `x402`; rebuild the card, then `fangorn card <card> --repo <app> --update <agentId>` |
247
+ | a private app's page says the data is still being indexed | the cron hasn't run yet; `westmarch-ship app.json --index` |
730
248
  | search on the page matches words, never meaning | the bundler pre-bundled westmarch; `optimizeDeps.exclude` it |
731
- | an agent's first call returns empty, later calls work | the tools registered before their data loaded (step 8) |
249
+ | an agent's first call returns empty, later calls work | the tools registered before their data loaded |
732
250
  | an agent opens the page and finds no tools | `card.url` is not the page that registers them |
733
- | `search` ignores what records say, or titles are ids | the type isn't declared in the schema, so its roles were guessed; declare it (step 5) |
251
+ | `search` ignores what records say, or titles are ids | the type isn't declared in the schema, so its roles were guessed; declare it in `app.json` |
734
252
  | a schema change didn't change search results | records keep the text they were embedded with; only newly embedded records use the new `text` role |
735
- | the view is empty after a push | `westmarch-view` wasn't rerun, `--from-block` is after your commits, or the push hasn't landed (`fangorn status`) |
736
- | `westmarch-view`: "vectors from two encoders are not comparable" | the view was built by another embedder (e.g. quickbeam); `--rebake`, or keep publishing it with that tool |
737
- | `westmarch-view` runs for minutes before doing anything | `--from-block` is far too early; the log scan is 1000 blocks per call |
253
+ | the view is empty after a push | the Worker's cron hasn't run yet (`westmarch-ship app.json --index`), or the push hasn't landed (`fangorn status`) |
254
+ | `westmarch-view`: "vectors from two encoders are not comparable" | the view was built by another embedder; `--rebake`, or keep publishing it with that tool |
738
255
  | lint: "nowhere to go" | no `presentation.externalUrl` in the schema, so a hit links nowhere |
739
256
  | `fangorn-mcp`: "this browser has no WebMCP" | page tools only: the browser lacks `document.modelContext`; it needs Chrome 150+ with `--enable-features=WebMCP` |
740
257
  | `fangorn-mcp`: "no browser: cannot launch google-chrome" | page tools only: no local Chrome; set `FANGORN_MCP_CDP` |
741
- | a log scan misses a transaction you just sent | fixed in the SDK (uncached head block); upgrade to ≥ 2026.9.22-dev |
742
258
  | IPFS reads return "switching to a service worker gateway" | ipfs.io stopped serving raw content; use a Pinata gateway |
743
259
  | the page isn't listed as a "Web" service on 8004scan | agent0-sdk has no web endpoint type; agents find the page through the card's `url` |
744
-
745
- ## Reference: Kingsfoil
746
-
747
- Kingsfoil has 60,760 clinical trials in 23 views. It predates the schema and quickbeam
748
- path: it embeds and bakes its own views (`kingsfoil/pipeline/bake.mjs`) and serves them
749
- from its own site. That works, but every update is a manual rebake and redeploy.
750
-
751
- | | |
752
- |---|---|
753
- | site | https://kingsfoil.pages.dev |
754
- | tools page (`card.url`) | https://kingsfoil.pages.dev/provider.html (the root is the patient view, with no tools) |
755
- | card | https://kingsfoil.pages.dev/.well-known/agent-card.json |
756
- | app | `kingsfoil` = `0x3069aaab9c73b01fb147a97106c7e595534850af5cab0e0ff7f2a55554d5c8ca` |
757
- | owner | `0x7a7849231cF7Ab1EA003BcF0063CB89704D7Cce9` |
758
- | fromBlock | 311671082 |
759
- | claim tx | `0x881a88e4775c6360f799503b098f00ef470806278f7b282045d6cc881df3cf72` |
760
- | ERC-8004 | agent `421614:226`, registration `ipfs://bafkreigmy2ubgzrsp55fnf77fykabapuv4y42nk6peq4ko3oi3w2x7u7ay` |
761
- | bind tx | `0xc7d8726d31bb9698ba2bfae3bc107a6111062d4b7e26526301a984638ab94c9f` |
762
- | card script | `kingsfoil/pipeline/agent-card.mjs` |
763
-
764
- Deployment (Arbitrum Sepolia, 421614): AppRegistry `0x11d228c4774af3d9cae3b4b6874a12576a1a83ec`,
765
- DataRegistry `0x775026e905d7b58b34d16bcbd385fa630ee36c26`, earliest AppRegistry event at block
766
- 311637349.