fedipod-server 0.28.1 → 0.30.0

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 (438) hide show
  1. package/README.md +38 -34
  2. package/config/server.json +22 -0
  3. package/dist/claims.js +3 -2
  4. package/dist/directory.d.ts +2 -0
  5. package/dist/door.d.ts +4 -0
  6. package/dist/door.js +31 -0
  7. package/dist/fedipod.d.ts +16 -0
  8. package/dist/fedipod.js +13 -0
  9. package/dist/front.d.ts +3 -0
  10. package/dist/front.js +94 -0
  11. package/dist/handler.d.ts +39 -101
  12. package/dist/handler.js +86 -617
  13. package/dist/handler.jsonld +6 -30
  14. package/dist/identities.d.ts +62 -0
  15. package/dist/identities.js +236 -0
  16. package/dist/mounts.d.ts +43 -0
  17. package/dist/mounts.js +123 -0
  18. package/dist/opt-in.d.ts +46 -0
  19. package/dist/opt-in.js +176 -0
  20. package/dist/store-pod.js +37 -0
  21. package/package.json +10 -18
  22. package/lib/client/c2s.mjs +0 -582
  23. package/lib/client/localapi.mjs +0 -87
  24. package/lib/client/masto/accounts.mjs +0 -309
  25. package/lib/client/masto/body.mjs +0 -156
  26. package/lib/client/masto/index.mjs +0 -206
  27. package/lib/client/masto/instance.mjs +0 -112
  28. package/lib/client/masto/media.mjs +0 -79
  29. package/lib/client/masto/oauth.mjs +0 -656
  30. package/lib/client/masto/render.mjs +0 -518
  31. package/lib/client/masto/statuses.mjs +0 -372
  32. package/lib/client/masto/timelines.mjs +0 -438
  33. package/lib/client/oidc-auth.mjs +0 -62
  34. package/lib/client/streaming.mjs +0 -154
  35. package/lib/client/webpush.mjs +0 -98
  36. package/lib/connections/acctfeed.mjs +0 -257
  37. package/lib/connections/atproto.mjs +0 -309
  38. package/lib/connections/bskyfeed.mjs +0 -243
  39. package/lib/connections/bskygroup.mjs +0 -162
  40. package/lib/connections/fediacct.mjs +0 -253
  41. package/lib/connections/import.mjs +0 -357
  42. package/lib/connections/tagfeed.mjs +0 -212
  43. package/lib/connections/vault.mjs +0 -114
  44. package/lib/core/as2.mjs +0 -206
  45. package/lib/core/contexts/activitystreams.json +0 -379
  46. package/lib/core/contexts/anno.json +0 -126
  47. package/lib/core/contexts/did-v1.json +0 -57
  48. package/lib/core/contexts/fep-5711.json +0 -36
  49. package/lib/core/contexts/gotosocial.json +0 -86
  50. package/lib/core/contexts/identity-v1.json +0 -152
  51. package/lib/core/contexts/index.mjs +0 -63
  52. package/lib/core/contexts/join-lemmy.json +0 -33
  53. package/lib/core/contexts/joinmastodon.json +0 -28
  54. package/lib/core/contexts/litepub-0.1.json +0 -60
  55. package/lib/core/contexts/map.json +0 -19
  56. package/lib/core/contexts/miscellany.json +0 -19
  57. package/lib/core/contexts/quotes.json +0 -18
  58. package/lib/core/contexts/schemaorg.json +0 -8845
  59. package/lib/core/contexts/security-data-integrity-v1.json +0 -78
  60. package/lib/core/contexts/security-data-integrity-v2.json +0 -81
  61. package/lib/core/contexts/security-multikey-v1.json +0 -35
  62. package/lib/core/contexts/security-v1.json +0 -74
  63. package/lib/core/contexts/webfinger.json +0 -10
  64. package/lib/core/deliver.mjs +0 -371
  65. package/lib/core/graphview.mjs +0 -327
  66. package/lib/core/intake/activities.mjs +0 -633
  67. package/lib/core/intake/activity.mjs +0 -318
  68. package/lib/core/intake/channel.mjs +0 -144
  69. package/lib/core/intake/group.mjs +0 -256
  70. package/lib/core/intake/index.mjs +0 -701
  71. package/lib/core/intake/notes.mjs +0 -360
  72. package/lib/core/intake/verify.mjs +0 -142
  73. package/lib/core/keys.mjs +0 -153
  74. package/lib/core/lease.mjs +0 -206
  75. package/lib/core/polls.mjs +0 -105
  76. package/lib/core/profile-page.mjs +0 -104
  77. package/lib/core/proof.mjs +0 -79
  78. package/lib/core/publisher/collections.mjs +0 -237
  79. package/lib/core/publisher/index.mjs +0 -500
  80. package/lib/core/publisher/notes.mjs +0 -453
  81. package/lib/core/publisher/questions.mjs +0 -239
  82. package/lib/core/publisher/restore.mjs +0 -224
  83. package/lib/core/shapes/activitystreams.ttl +0 -157
  84. package/lib/core/shapes/index.mjs +0 -132
  85. package/lib/core/shapes/shapes-text.mjs +0 -13
  86. package/lib/core/social.mjs +0 -531
  87. package/lib/core/storage.mjs +0 -176
  88. package/lib/core/store.mjs +0 -568
  89. package/lib/core/wire-quotes.mjs +0 -71
  90. package/lib/core/wire.mjs +0 -889
  91. package/lib/device/account.mjs +0 -87
  92. package/lib/device/admin/index.mjs +0 -13
  93. package/lib/device/admin/origins.mjs +0 -35
  94. package/lib/device/admin/routes/connections.mjs +0 -144
  95. package/lib/device/admin/routes/gateway.mjs +0 -199
  96. package/lib/device/admin/routes/lifecycle.mjs +0 -200
  97. package/lib/device/admin/routes/owner.mjs +0 -322
  98. package/lib/device/admin/routes/setup.mjs +0 -412
  99. package/lib/device/admin/routes/social.mjs +0 -188
  100. package/lib/device/admin/server.mjs +0 -95
  101. package/lib/device/admin/static.mjs +0 -247
  102. package/lib/device/admin/surface.mjs +0 -315
  103. package/lib/device/certs.mjs +0 -281
  104. package/lib/device/cli/commands/account.mjs +0 -586
  105. package/lib/device/cli/commands/run.mjs +0 -278
  106. package/lib/device/cli/commands/service.mjs +0 -221
  107. package/lib/device/cli/commands/setup.mjs +0 -447
  108. package/lib/device/cli/commands/state.mjs +0 -561
  109. package/lib/device/cli/context.mjs +0 -288
  110. package/lib/device/export-collections.mjs +0 -161
  111. package/lib/device/gateway-move.mjs +0 -58
  112. package/lib/device/home.mjs +0 -161
  113. package/lib/device/migrate.mjs +0 -115
  114. package/lib/device/ports.mjs +0 -27
  115. package/lib/device/remote.mjs +0 -54
  116. package/lib/device/setup.mjs +0 -459
  117. package/lib/device/update.mjs +0 -96
  118. package/lib/gateway/caches.mjs +0 -36
  119. package/lib/gateway/directory.mjs +0 -90
  120. package/lib/gateway/front-core.mjs +0 -970
  121. package/lib/gateway/gateway-core.mjs +0 -165
  122. package/lib/gateway/headers.mjs +0 -49
  123. package/lib/gateway/httpsig.mjs +0 -159
  124. package/lib/gateway/notices.mjs +0 -84
  125. package/lib/gateway/quiet.mjs +0 -189
  126. package/lib/pod/README.md +0 -61
  127. package/lib/pod/actor.mjs +0 -96
  128. package/lib/pod/collection.mjs +0 -77
  129. package/lib/pod/containers.mjs +0 -140
  130. package/lib/pod/discovery.mjs +0 -46
  131. package/lib/pod/featured.mjs +0 -18
  132. package/lib/pod/followers.mjs +0 -24
  133. package/lib/pod/following.mjs +0 -10
  134. package/lib/pod/http.mjs +0 -74
  135. package/lib/pod/inbox.mjs +0 -166
  136. package/lib/pod/links.mjs +0 -35
  137. package/lib/pod/media.mjs +0 -27
  138. package/lib/pod/notes.mjs +0 -80
  139. package/lib/pod/notifications.mjs +0 -89
  140. package/lib/pod/outbox.mjs +0 -18
  141. package/lib/pod/package.json +0 -19
  142. package/lib/pod/policy.mjs +0 -58
  143. package/lib/pod/private.mjs +0 -19
  144. package/lib/pod/root.mjs +0 -104
  145. package/lib/pod/state.mjs +0 -40
  146. package/lib/pod/transport.mjs +0 -525
  147. package/lib/pod/urls.mjs +0 -85
  148. package/lib/server/embed.mjs +0 -423
  149. package/lib/session/README.md +0 -137
  150. package/lib/session/demo.html +0 -94
  151. package/lib/session/fedi-account.mjs +0 -408
  152. package/lib/session/fedi-login.mjs +0 -123
  153. package/lib/session/oidc-session.mjs +0 -233
  154. package/lib/session/package.json +0 -49
  155. package/lib/shared/guard.mjs +0 -213
  156. package/lib/shared/links.mjs +0 -9
  157. package/lib/shared/safefetch.mjs +0 -235
  158. package/lib/shared/ua.mjs +0 -29
  159. package/phanpy/dist/404.html +0 -40
  160. package/phanpy/dist/_headers +0 -2
  161. package/phanpy/dist/apple-touch-icon.png +0 -0
  162. package/phanpy/dist/assets/ICONS-DIPJeU0C.js +0 -2
  163. package/phanpy/dist/assets/ICONS-DIPJeU0C.js.map +0 -1
  164. package/phanpy/dist/assets/ScheduledAtField-D-Abfo4J.js +0 -183
  165. package/phanpy/dist/assets/ScheduledAtField-D-Abfo4J.js.map +0 -1
  166. package/phanpy/dist/assets/Temml-CCjoacWt.woff2 +0 -0
  167. package/phanpy/dist/assets/__vite-browser-external-C7iut881.js +0 -2
  168. package/phanpy/dist/assets/__vite-browser-external-C7iut881.js.map +0 -1
  169. package/phanpy/dist/assets/boosts-carousel-BiOaNdMT.jpg +0 -0
  170. package/phanpy/dist/assets/catch-up-O5q5C75Z.png +0 -0
  171. package/phanpy/dist/assets/chunk-aKtaBQYM.js +0 -1
  172. package/phanpy/dist/assets/compose-CFEx7wb4.js +0 -2
  173. package/phanpy/dist/assets/compose-CFEx7wb4.js.map +0 -1
  174. package/phanpy/dist/assets/compose-ZJN5xSb4.js +0 -35
  175. package/phanpy/dist/assets/compose-ZJN5xSb4.js.map +0 -1
  176. package/phanpy/dist/assets/debug.module-YFQgwnIr.js +0 -52
  177. package/phanpy/dist/assets/debug.module-YFQgwnIr.js.map +0 -1
  178. package/phanpy/dist/assets/dom-p1YcJ5Rw.js +0 -2
  179. package/phanpy/dist/assets/dom-p1YcJ5Rw.js.map +0 -1
  180. package/phanpy/dist/assets/home-mobile-dark@2x-Dc-J8-SM.png +0 -0
  181. package/phanpy/dist/assets/home-mobile-light@2x-Bd3fod8m.png +0 -0
  182. package/phanpy/dist/assets/home-tablet-dark@2x-BgcNI8le.png +0 -0
  183. package/phanpy/dist/assets/home-tablet-light@2x-BW2RV9T6.png +0 -0
  184. package/phanpy/dist/assets/icons/add-circle-line-WD0mwORD.js +0 -1
  185. package/phanpy/dist/assets/icons/alert-line-CpFUMf9s.js +0 -1
  186. package/phanpy/dist/assets/icons/android-2-line-Bzxnrd3S.js +0 -1
  187. package/phanpy/dist/assets/icons/announcement-line-5nwaJFJm.js +0 -1
  188. package/phanpy/dist/assets/icons/arrow-down-circle-line-a7Id9-Iv.js +0 -1
  189. package/phanpy/dist/assets/icons/arrow-down-line-B8uzXzq6.js +0 -1
  190. package/phanpy/dist/assets/icons/arrow-left-line-CGrGT9C-.js +0 -1
  191. package/phanpy/dist/assets/icons/arrow-right-line-CE-HERfX.js +0 -1
  192. package/phanpy/dist/assets/icons/arrow-to-up-line-CvdQjmcy.js +0 -1
  193. package/phanpy/dist/assets/icons/arrow-up-circle-line-DEparSNF.js +0 -1
  194. package/phanpy/dist/assets/icons/arrow-up-line-C7yBeugc.js +0 -1
  195. package/phanpy/dist/assets/icons/arrows-right-line-BtbXmJGP.js +0 -1
  196. package/phanpy/dist/assets/icons/at-line-CF66Bznk.js +0 -1
  197. package/phanpy/dist/assets/icons/attachment-line-BY8It-l9.js +0 -1
  198. package/phanpy/dist/assets/icons/blockquote-line-BS6pU_hm.js +0 -1
  199. package/phanpy/dist/assets/icons/board-line-C0UWXyA6.js +0 -1
  200. package/phanpy/dist/assets/icons/bookmark-line-1CtX5JLF.js +0 -1
  201. package/phanpy/dist/assets/icons/building-5-line-Dd59MzbO.js +0 -1
  202. package/phanpy/dist/assets/icons/bus-2-line-6_dvA0GG.js +0 -1
  203. package/phanpy/dist/assets/icons/calendar-day-line-Crpl_fmu.js +0 -1
  204. package/phanpy/dist/assets/icons/calendar-month-line-vommts2C.js +0 -1
  205. package/phanpy/dist/assets/icons/calendar-time-add-line-BU_DTlQk.js +0 -1
  206. package/phanpy/dist/assets/icons/camera-line-Cv69MEei.js +0 -1
  207. package/phanpy/dist/assets/icons/celebrate-line-B8DvIoi0.js +0 -1
  208. package/phanpy/dist/assets/icons/chart-bar-line-FK-QOmtF.js +0 -1
  209. package/phanpy/dist/assets/icons/chart-line-line-BGcJAFWw.js +0 -1
  210. package/phanpy/dist/assets/icons/chat-3-line-YFK2-hwz.js +0 -1
  211. package/phanpy/dist/assets/icons/check-circle-line-DsAxFr5-.js +0 -1
  212. package/phanpy/dist/assets/icons/clipboard-line-BBp364gJ.js +0 -1
  213. package/phanpy/dist/assets/icons/close-circle-line-CICdiuGz.js +0 -1
  214. package/phanpy/dist/assets/icons/close-line-DYrFWcjv.js +0 -1
  215. package/phanpy/dist/assets/icons/cloud-line-tiQAbah8.js +0 -1
  216. package/phanpy/dist/assets/icons/code-line-fAl8iiV3.js +0 -1
  217. package/phanpy/dist/assets/icons/comment-2-line-CR3g7WeB.js +0 -1
  218. package/phanpy/dist/assets/icons/copy-2-line-CLW69yF5.js +0 -1
  219. package/phanpy/dist/assets/icons/delete-2-line-B8xmbr4n.js +0 -1
  220. package/phanpy/dist/assets/icons/document-3-line-DBGNhAg2.js +0 -1
  221. package/phanpy/dist/assets/icons/document-line-C7mb9sb7.js +0 -1
  222. package/phanpy/dist/assets/icons/down-line-BGqSSThd.js +0 -1
  223. package/phanpy/dist/assets/icons/edit-4-line-DcgGaEhx.js +0 -1
  224. package/phanpy/dist/assets/icons/emoji-2-line-D8YgnM-y.js +0 -1
  225. package/phanpy/dist/assets/icons/exit-line-Dc07BS-x.js +0 -1
  226. package/phanpy/dist/assets/icons/external-link-line-BvpSkmjO.js +0 -1
  227. package/phanpy/dist/assets/icons/eye-2-line-CiWXx-f_.js +0 -1
  228. package/phanpy/dist/assets/icons/eye-close-line-BgTaSJ4P.js +0 -1
  229. package/phanpy/dist/assets/icons/filter-2-line-B_mR2usy.js +0 -1
  230. package/phanpy/dist/assets/icons/filter-line-C_nm-3cU.js +0 -1
  231. package/phanpy/dist/assets/icons/flag-1-line-C2HB05I0.js +0 -1
  232. package/phanpy/dist/assets/icons/forbid-circle-line-D3LTT9yd.js +0 -1
  233. package/phanpy/dist/assets/icons/formula-line-DLrt2d6j.js +0 -1
  234. package/phanpy/dist/assets/icons/grid-line-CgUmEikf.js +0 -1
  235. package/phanpy/dist/assets/icons/group-line-C5fnX0wU.js +0 -1
  236. package/phanpy/dist/assets/icons/hand-finger-2-line-BGgxbuL9.js +0 -1
  237. package/phanpy/dist/assets/icons/hashtag-line-CyTEEXBb.js +0 -1
  238. package/phanpy/dist/assets/icons/heart-crack-line-BZddbO2C.js +0 -1
  239. package/phanpy/dist/assets/icons/heart-line-CJ7K47J4.js +0 -1
  240. package/phanpy/dist/assets/icons/history-2-line-kWWvFzqv.js +0 -1
  241. package/phanpy/dist/assets/icons/history-line-bGQiwbTH.js +0 -1
  242. package/phanpy/dist/assets/icons/home-3-line-fRU1zeLG.js +0 -1
  243. package/phanpy/dist/assets/icons/information-line-iNk6-sOY.js +0 -1
  244. package/phanpy/dist/assets/icons/keyboard-line-DsCyuQNz.js +0 -1
  245. package/phanpy/dist/assets/icons/layout-4-line-CZkOX52Z.js +0 -1
  246. package/phanpy/dist/assets/icons/layout-5-line-B_znt5wO.js +0 -1
  247. package/phanpy/dist/assets/icons/left-line-eUw0tya4.js +0 -1
  248. package/phanpy/dist/assets/icons/lightning-line-DaKoDdix.js +0 -1
  249. package/phanpy/dist/assets/icons/link-2-line-DCqLmMA7.js +0 -1
  250. package/phanpy/dist/assets/icons/list-check-line-BAxosBh9.js +0 -1
  251. package/phanpy/dist/assets/icons/lock-line-CPoaXeUW.js +0 -1
  252. package/phanpy/dist/assets/icons/mail-line-B0P8aa1L.js +0 -1
  253. package/phanpy/dist/assets/icons/moon-line-CEqNAToP.js +0 -1
  254. package/phanpy/dist/assets/icons/more-1-fill-Dw2e5xab.js +0 -1
  255. package/phanpy/dist/assets/icons/more-3-line-BIiVjYTz.js +0 -1
  256. package/phanpy/dist/assets/icons/notification-line-BZZq2Gtu.js +0 -1
  257. package/phanpy/dist/assets/icons/pencil-line-CKh8-A1v.js +0 -1
  258. package/phanpy/dist/assets/icons/photo-album-line-Bnfg2u40.js +0 -1
  259. package/phanpy/dist/assets/icons/pin-line-DpTolyhs.js +0 -1
  260. package/phanpy/dist/assets/icons/play-fill-B0-G_8lX.js +0 -1
  261. package/phanpy/dist/assets/icons/qrcode-2-line-B6SPCaHn.js +0 -1
  262. package/phanpy/dist/assets/icons/quill-pen-line-B6LUrnCP.js +0 -1
  263. package/phanpy/dist/assets/icons/quote-left-fill-hQZ1cj_6.js +0 -1
  264. package/phanpy/dist/assets/icons/radar-line-CKyroTth.js +0 -1
  265. package/phanpy/dist/assets/icons/react-line-CoIKhhtK.js +0 -1
  266. package/phanpy/dist/assets/icons/refresh-2-line-D0mTF97S.js +0 -1
  267. package/phanpy/dist/assets/icons/right-line-B3KWJaqc.js +0 -1
  268. package/phanpy/dist/assets/icons/rocket-line-4KZl537D.js +0 -1
  269. package/phanpy/dist/assets/icons/round-fill-C69NTrZ0.js +0 -1
  270. package/phanpy/dist/assets/icons/round-line-B9k_1uo-.js +0 -1
  271. package/phanpy/dist/assets/icons/route-line-B28ReHcE.js +0 -1
  272. package/phanpy/dist/assets/icons/rows-4-line-CrBniagM.js +0 -1
  273. package/phanpy/dist/assets/icons/scan-line-DSf74YdP.js +0 -1
  274. package/phanpy/dist/assets/icons/search-2-line-B6geIU1z.js +0 -1
  275. package/phanpy/dist/assets/icons/settings-3-line-azJwK47w.js +0 -1
  276. package/phanpy/dist/assets/icons/settings-6-line-C_5zc5YR.js +0 -1
  277. package/phanpy/dist/assets/icons/share-2-line-D5MxWjrm.js +0 -1
  278. package/phanpy/dist/assets/icons/share-forward-line-Cp61fZzZ.js +0 -1
  279. package/phanpy/dist/assets/icons/sparkles-2-line-DYYyXTaW.js +0 -1
  280. package/phanpy/dist/assets/icons/sparkles-line-C2hY1s6I.js +0 -1
  281. package/phanpy/dist/assets/icons/time-line-BF3eet1v.js +0 -1
  282. package/phanpy/dist/assets/icons/transfer-4-line-oisfvTBw.js +0 -1
  283. package/phanpy/dist/assets/icons/translate-line-BFWol2Ce.js +0 -1
  284. package/phanpy/dist/assets/icons/unlock-line-X_x5vwv3.js +0 -1
  285. package/phanpy/dist/assets/icons/upload-3-line-DgzwUJeW.js +0 -1
  286. package/phanpy/dist/assets/icons/user-4-line-BVSynZCp.js +0 -1
  287. package/phanpy/dist/assets/icons/user-add-2-line-BlGS5Q8q.js +0 -1
  288. package/phanpy/dist/assets/icons/user-add-line-BbXdHG4z.js +0 -1
  289. package/phanpy/dist/assets/icons/user-edit-line-BFAHOV0X.js +0 -1
  290. package/phanpy/dist/assets/icons/user-follow-line-BXux0wSd.js +0 -1
  291. package/phanpy/dist/assets/icons/user-star-line-DdYlpHLU.js +0 -1
  292. package/phanpy/dist/assets/icons/user-warning-line-Cj43j0_E.js +0 -1
  293. package/phanpy/dist/assets/icons/user-x-line-DIDgNlPH.js +0 -1
  294. package/phanpy/dist/assets/icons/volume-line-Bs390KQt.js +0 -1
  295. package/phanpy/dist/assets/icons/volume-mute-line-DzlzIqFZ.js +0 -1
  296. package/phanpy/dist/assets/icons/walk-line-yNXfB-md.js +0 -1
  297. package/phanpy/dist/assets/icons/world-2-line-CenkGIwj.js +0 -1
  298. package/phanpy/dist/assets/icons/zoom-in-line-CaENUphf.js +0 -1
  299. package/phanpy/dist/assets/icons/zoom-out-line-BDNk0EWo.js +0 -1
  300. package/phanpy/dist/assets/instances-BNPptnn-.json +0 -333
  301. package/phanpy/dist/assets/locales/ar-SA-CfheRbCu.js +0 -1
  302. package/phanpy/dist/assets/locales/ca-ES-DHRW86Tz.js +0 -1
  303. package/phanpy/dist/assets/locales/cs-CZ-CKoqEuA1.js +0 -1
  304. package/phanpy/dist/assets/locales/de-DE-ByRPMRFy.js +0 -1
  305. package/phanpy/dist/assets/locales/eo-UY-CPC7wLCS.js +0 -1
  306. package/phanpy/dist/assets/locales/es-ES-dJa0Fd9t.js +0 -1
  307. package/phanpy/dist/assets/locales/eu-ES-C3djlPR2.js +0 -1
  308. package/phanpy/dist/assets/locales/fa-IR-DTT490Ck.js +0 -1
  309. package/phanpy/dist/assets/locales/fi-FI-B23MCPuz.js +0 -1
  310. package/phanpy/dist/assets/locales/fr-FR-BcaQsO5C.js +0 -1
  311. package/phanpy/dist/assets/locales/gl-ES-BR-CVKBN.js +0 -1
  312. package/phanpy/dist/assets/locales/he-IL-CfheRbCu.js +0 -1
  313. package/phanpy/dist/assets/locales/hu-HU-CfheRbCu.js +0 -1
  314. package/phanpy/dist/assets/locales/it-IT-C1136UFV.js +0 -1
  315. package/phanpy/dist/assets/locales/ja-JP-CRZmRAhD.js +0 -1
  316. package/phanpy/dist/assets/locales/kab-Cz8xH_TT.js +0 -1
  317. package/phanpy/dist/assets/locales/ko-KR-CG9UiY4o.js +0 -1
  318. package/phanpy/dist/assets/locales/lt-LT-CJcI6sHZ.js +0 -1
  319. package/phanpy/dist/assets/locales/nb-NO-CfheRbCu.js +0 -1
  320. package/phanpy/dist/assets/locales/nl-NL-DxN4Yc1t.js +0 -1
  321. package/phanpy/dist/assets/locales/oc-FR-CfheRbCu.js +0 -1
  322. package/phanpy/dist/assets/locales/pl-PL-DkwGXf6P.js +0 -1
  323. package/phanpy/dist/assets/locales/pseudo-LOCALE-CQ4XxkQ0.js +0 -1
  324. package/phanpy/dist/assets/locales/pt-BR-nGL0HdFS.js +0 -1
  325. package/phanpy/dist/assets/locales/pt-PT-Cp17aj2z.js +0 -1
  326. package/phanpy/dist/assets/locales/ru-RU-0Y8cIoaK.js +0 -1
  327. package/phanpy/dist/assets/locales/th-TH-CfheRbCu.js +0 -1
  328. package/phanpy/dist/assets/locales/tok-CfheRbCu.js +0 -1
  329. package/phanpy/dist/assets/locales/tr-TR-CfheRbCu.js +0 -1
  330. package/phanpy/dist/assets/locales/uk-UA-C5E3dFBf.js +0 -1
  331. package/phanpy/dist/assets/locales/zh-CN-BHJi7_et.js +0 -1
  332. package/phanpy/dist/assets/locales/zh-TW-CfheRbCu.js +0 -1
  333. package/phanpy/dist/assets/main-BdqNbG-a.js +0 -26
  334. package/phanpy/dist/assets/main-BdqNbG-a.js.map +0 -1
  335. package/phanpy/dist/assets/mock-home-DGhR__He.js +0 -2
  336. package/phanpy/dist/assets/mock-home-DGhR__He.js.map +0 -1
  337. package/phanpy/dist/assets/multi-column-ROck0NVt.jpg +0 -0
  338. package/phanpy/dist/assets/multi-hashtag-timeline-DH5INVHi.jpg +0 -0
  339. package/phanpy/dist/assets/nested-comments-thread-hv59kZLc.jpg +0 -0
  340. package/phanpy/dist/assets/polyfill-force-H8sUkLGp.js +0 -2
  341. package/phanpy/dist/assets/polyfill-force-H8sUkLGp.js.map +0 -1
  342. package/phanpy/dist/assets/pwa-viewport-ogKbF51K.js +0 -20
  343. package/phanpy/dist/assets/pwa-viewport-ogKbF51K.js.map +0 -1
  344. package/phanpy/dist/assets/style-B1iLPTPR.css +0 -2
  345. package/phanpy/dist/assets/temml-DuyIQMei.js +0 -97
  346. package/phanpy/dist/assets/temml-DuyIQMei.js.map +0 -1
  347. package/phanpy/dist/assets/tinyld.light.node-DtbBhLYC.js +0 -2
  348. package/phanpy/dist/assets/tinyld.light.node-DtbBhLYC.js.map +0 -1
  349. package/phanpy/dist/assets/year-in-posts-B2lywqdx.png +0 -0
  350. package/phanpy/dist/assets/year-in-posts-DJv_star.js +0 -3
  351. package/phanpy/dist/assets/year-in-posts-DJv_star.js.map +0 -1
  352. package/phanpy/dist/compose/index.html +0 -20
  353. package/phanpy/dist/favicon.ico +0 -0
  354. package/phanpy/dist/index.html +0 -73
  355. package/phanpy/dist/logo-192.png +0 -0
  356. package/phanpy/dist/logo-512.png +0 -0
  357. package/phanpy/dist/logo-badge-72.png +0 -0
  358. package/phanpy/dist/logo-maskable-512.png +0 -0
  359. package/phanpy/dist/logo-monochrome-512.png +0 -0
  360. package/phanpy/dist/logo-monochrome-maskable-512.png +0 -0
  361. package/phanpy/dist/manifest.webmanifest +0 -1
  362. package/phanpy/dist/og-image-2.jpg +0 -0
  363. package/phanpy/dist/og-image.png +0 -0
  364. package/phanpy/dist/robots.txt +0 -2
  365. package/phanpy/dist/sw.js +0 -2
  366. package/phanpy/dist/sw.js.map +0 -1
  367. package/phanpy/dist/version.json +0 -1
  368. package/run-agent.mjs +0 -897
  369. package/vendor/gate.cjs +0 -138
  370. package/vendor/idp-grant.cjs +0 -394
  371. package/vendor/jfetch.cjs +0 -16
  372. package/web/admin/actors.js +0 -145
  373. package/web/admin/bar.css +0 -126
  374. package/web/admin/bar.js +0 -99
  375. package/web/admin/client/client.js +0 -66
  376. package/web/admin/client/index.html +0 -69
  377. package/web/admin/client-pick.js +0 -79
  378. package/web/admin/common.js +0 -23
  379. package/web/admin/connections.js +0 -112
  380. package/web/admin/gateway.js +0 -133
  381. package/web/admin/group.js +0 -258
  382. package/web/admin/index.html +0 -603
  383. package/web/admin/notices-bar.js +0 -90
  384. package/web/admin/oauth-signin.mjs +0 -37
  385. package/web/admin/record.js +0 -390
  386. package/web/admin/setup/index.html +0 -241
  387. package/web/admin/setup/setup.js +0 -326
  388. package/web/admin/tokens.css +0 -68
  389. package/web/admin/upkeep.js +0 -177
  390. package/web/admin/window.css +0 -55
  391. package/web/admin/window.js +0 -136
  392. package/web/app/README.md +0 -81
  393. package/web/app/admin-facade.mjs +0 -588
  394. package/web/app/agent.mjs +0 -663
  395. package/web/app/atproto-browser.mjs +0 -86
  396. package/web/app/authorize_interaction.html +0 -56
  397. package/web/app/authorize_interaction.js +0 -69
  398. package/web/app/boot.mjs +0 -576
  399. package/web/app/deliver-relay.mjs +0 -129
  400. package/web/app/dist/boot.js +0 -34218
  401. package/web/app/dist/boot.js.map +0 -7
  402. package/web/app/dist/sw.js +0 -72810
  403. package/web/app/dist/sw.js.map +0 -7
  404. package/web/app/fediacct-browser.mjs +0 -146
  405. package/web/app/gateway-move.mjs +0 -60
  406. package/web/app/idb-kv.mjs +0 -35
  407. package/web/app/index.html +0 -242
  408. package/web/app/keys-browser.mjs +0 -71
  409. package/web/app/keystore.mjs +0 -82
  410. package/web/app/oidc-session.mjs +0 -6
  411. package/web/app/pod-auth.mjs +0 -248
  412. package/web/app/pod-remote.mjs +0 -84
  413. package/web/app/shims/_globals.mjs +0 -2
  414. package/web/app/shims/fedify-sig.mjs +0 -77
  415. package/web/app/shims/node-crypto.mjs +0 -113
  416. package/web/app/shims/node-fs.mjs +0 -17
  417. package/web/app/shims/node-path.mjs +0 -19
  418. package/web/app/shims/node-url.mjs +0 -7
  419. package/web/app/shims/prelude.js +0 -58
  420. package/web/app/shims/safefetch.mjs +0 -58
  421. package/web/app/shims/shapes-text.mjs +0 -8
  422. package/web/app/shims/web-push.mjs +0 -14
  423. package/web/app/signup.mjs +0 -318
  424. package/web/app/sw-src.mjs +0 -220
  425. package/web/app/update.js +0 -70
  426. package/web/front/admin.html +0 -55
  427. package/web/front/admin.js +0 -89
  428. package/web/front/install.sh +0 -69
  429. package/web/front/new-account.html +0 -35
  430. package/web/front/new-account.js +0 -13
  431. package/web/front/notices.html +0 -69
  432. package/web/front/notices.js +0 -107
  433. package/web/front/run.html +0 -58
  434. package/web/front/run.js +0 -195
  435. package/web/front/solid-oidc-client.js +0 -6
  436. package/web/icons/forum.svg +0 -11
  437. package/web/icons/gateway.svg +0 -14
  438. package/web/icons/local.svg +0 -11
@@ -1,78 +0,0 @@
1
- {
2
- "@context": {
3
- "id": "@id",
4
- "type": "@type",
5
- "@protected": true,
6
- "digestMultibase": {
7
- "@id": "https://w3id.org/security#digestMultibase",
8
- "@type": "https://w3id.org/security#multibase"
9
- },
10
- "proof": {
11
- "@id": "https://w3id.org/security#proof",
12
- "@type": "@id",
13
- "@container": "@graph"
14
- },
15
- "DataIntegrityProof": {
16
- "@id": "https://w3id.org/security#DataIntegrityProof",
17
- "@context": {
18
- "@protected": true,
19
- "id": "@id",
20
- "type": "@type",
21
- "challenge": "https://w3id.org/security#challenge",
22
- "created": {
23
- "@id": "http://purl.org/dc/terms/created",
24
- "@type": "http://www.w3.org/2001/XMLSchema#dateTime"
25
- },
26
- "domain": "https://w3id.org/security#domain",
27
- "expires": {
28
- "@id": "https://w3id.org/security#expiration",
29
- "@type": "http://www.w3.org/2001/XMLSchema#dateTime"
30
- },
31
- "nonce": "https://w3id.org/security#nonce",
32
- "proofPurpose": {
33
- "@id": "https://w3id.org/security#proofPurpose",
34
- "@type": "@vocab",
35
- "@context": {
36
- "@protected": true,
37
- "id": "@id",
38
- "type": "@type",
39
- "assertionMethod": {
40
- "@id": "https://w3id.org/security#assertionMethod",
41
- "@type": "@id",
42
- "@container": "@set"
43
- },
44
- "authentication": {
45
- "@id": "https://w3id.org/security#authenticationMethod",
46
- "@type": "@id",
47
- "@container": "@set"
48
- },
49
- "capabilityInvocation": {
50
- "@id": "https://w3id.org/security#capabilityInvocationMethod",
51
- "@type": "@id",
52
- "@container": "@set"
53
- },
54
- "capabilityDelegation": {
55
- "@id": "https://w3id.org/security#capabilityDelegationMethod",
56
- "@type": "@id",
57
- "@container": "@set"
58
- },
59
- "keyAgreement": {
60
- "@id": "https://w3id.org/security#keyAgreementMethod",
61
- "@type": "@id",
62
- "@container": "@set"
63
- }
64
- }
65
- },
66
- "cryptosuite": "https://w3id.org/security#cryptosuite",
67
- "proofValue": {
68
- "@id": "https://w3id.org/security#proofValue",
69
- "@type": "https://w3id.org/security#multibase"
70
- },
71
- "verificationMethod": {
72
- "@id": "https://w3id.org/security#verificationMethod",
73
- "@type": "@id"
74
- }
75
- }
76
- }
77
- }
78
- }
@@ -1,81 +0,0 @@
1
- {
2
- "@context": {
3
- "id": "@id",
4
- "type": "@type",
5
- "@protected": true,
6
- "proof": {
7
- "@id": "https://w3id.org/security#proof",
8
- "@type": "@id",
9
- "@container": "@graph"
10
- },
11
- "DataIntegrityProof": {
12
- "@id": "https://w3id.org/security#DataIntegrityProof",
13
- "@context": {
14
- "@protected": true,
15
- "id": "@id",
16
- "type": "@type",
17
- "challenge": "https://w3id.org/security#challenge",
18
- "created": {
19
- "@id": "http://purl.org/dc/terms/created",
20
- "@type": "http://www.w3.org/2001/XMLSchema#dateTime"
21
- },
22
- "domain": "https://w3id.org/security#domain",
23
- "expires": {
24
- "@id": "https://w3id.org/security#expiration",
25
- "@type": "http://www.w3.org/2001/XMLSchema#dateTime"
26
- },
27
- "nonce": "https://w3id.org/security#nonce",
28
- "previousProof": {
29
- "@id": "https://w3id.org/security#previousProof",
30
- "@type": "@id"
31
- },
32
- "proofPurpose": {
33
- "@id": "https://w3id.org/security#proofPurpose",
34
- "@type": "@vocab",
35
- "@context": {
36
- "@protected": true,
37
- "id": "@id",
38
- "type": "@type",
39
- "assertionMethod": {
40
- "@id": "https://w3id.org/security#assertionMethod",
41
- "@type": "@id",
42
- "@container": "@set"
43
- },
44
- "authentication": {
45
- "@id": "https://w3id.org/security#authenticationMethod",
46
- "@type": "@id",
47
- "@container": "@set"
48
- },
49
- "capabilityInvocation": {
50
- "@id": "https://w3id.org/security#capabilityInvocationMethod",
51
- "@type": "@id",
52
- "@container": "@set"
53
- },
54
- "capabilityDelegation": {
55
- "@id": "https://w3id.org/security#capabilityDelegationMethod",
56
- "@type": "@id",
57
- "@container": "@set"
58
- },
59
- "keyAgreement": {
60
- "@id": "https://w3id.org/security#keyAgreementMethod",
61
- "@type": "@id",
62
- "@container": "@set"
63
- }
64
- }
65
- },
66
- "cryptosuite": {
67
- "@id": "https://w3id.org/security#cryptosuite",
68
- "@type": "https://w3id.org/security#cryptosuiteString"
69
- },
70
- "proofValue": {
71
- "@id": "https://w3id.org/security#proofValue",
72
- "@type": "https://w3id.org/security#multibase"
73
- },
74
- "verificationMethod": {
75
- "@id": "https://w3id.org/security#verificationMethod",
76
- "@type": "@id"
77
- }
78
- }
79
- }
80
- }
81
- }
@@ -1,35 +0,0 @@
1
- {
2
- "@context": {
3
- "id": "@id",
4
- "type": "@type",
5
- "@protected": true,
6
- "Multikey": {
7
- "@id": "https://w3id.org/security#Multikey",
8
- "@context": {
9
- "@protected": true,
10
- "id": "@id",
11
- "type": "@type",
12
- "controller": {
13
- "@id": "https://w3id.org/security#controller",
14
- "@type": "@id"
15
- },
16
- "revoked": {
17
- "@id": "https://w3id.org/security#revoked",
18
- "@type": "http://www.w3.org/2001/XMLSchema#dateTime"
19
- },
20
- "expires": {
21
- "@id": "https://w3id.org/security#expiration",
22
- "@type": "http://www.w3.org/2001/XMLSchema#dateTime"
23
- },
24
- "publicKeyMultibase": {
25
- "@id": "https://w3id.org/security#publicKeyMultibase",
26
- "@type": "https://w3id.org/security#multibase"
27
- },
28
- "secretKeyMultibase": {
29
- "@id": "https://w3id.org/security#secretKeyMultibase",
30
- "@type": "https://w3id.org/security#multibase"
31
- }
32
- }
33
- }
34
- }
35
- }
@@ -1,74 +0,0 @@
1
- {
2
- "@context": {
3
- "id": "@id",
4
- "type": "@type",
5
- "dc": "http://purl.org/dc/terms/",
6
- "sec": "https://w3id.org/security#",
7
- "xsd": "http://www.w3.org/2001/XMLSchema#",
8
- "EcdsaKoblitzSignature2016": "sec:EcdsaKoblitzSignature2016",
9
- "Ed25519Signature2018": "sec:Ed25519Signature2018",
10
- "EncryptedMessage": "sec:EncryptedMessage",
11
- "GraphSignature2012": "sec:GraphSignature2012",
12
- "LinkedDataSignature2015": "sec:LinkedDataSignature2015",
13
- "LinkedDataSignature2016": "sec:LinkedDataSignature2016",
14
- "CryptographicKey": "sec:Key",
15
- "authenticationTag": "sec:authenticationTag",
16
- "canonicalizationAlgorithm": "sec:canonicalizationAlgorithm",
17
- "cipherAlgorithm": "sec:cipherAlgorithm",
18
- "cipherData": "sec:cipherData",
19
- "cipherKey": "sec:cipherKey",
20
- "created": {
21
- "@id": "dc:created",
22
- "@type": "xsd:dateTime"
23
- },
24
- "creator": {
25
- "@id": "dc:creator",
26
- "@type": "@id"
27
- },
28
- "digestAlgorithm": "sec:digestAlgorithm",
29
- "digestValue": "sec:digestValue",
30
- "domain": "sec:domain",
31
- "encryptionKey": "sec:encryptionKey",
32
- "expiration": {
33
- "@id": "sec:expiration",
34
- "@type": "xsd:dateTime"
35
- },
36
- "expires": {
37
- "@id": "sec:expiration",
38
- "@type": "xsd:dateTime"
39
- },
40
- "initializationVector": "sec:initializationVector",
41
- "iterationCount": "sec:iterationCount",
42
- "nonce": "sec:nonce",
43
- "normalizationAlgorithm": "sec:normalizationAlgorithm",
44
- "owner": {
45
- "@id": "sec:owner",
46
- "@type": "@id"
47
- },
48
- "password": "sec:password",
49
- "privateKey": {
50
- "@id": "sec:privateKey",
51
- "@type": "@id"
52
- },
53
- "privateKeyPem": "sec:privateKeyPem",
54
- "publicKey": {
55
- "@id": "sec:publicKey",
56
- "@type": "@id"
57
- },
58
- "publicKeyBase58": "sec:publicKeyBase58",
59
- "publicKeyPem": "sec:publicKeyPem",
60
- "publicKeyWif": "sec:publicKeyWif",
61
- "publicKeyService": {
62
- "@id": "sec:publicKeyService",
63
- "@type": "@id"
64
- },
65
- "revoked": {
66
- "@id": "sec:revoked",
67
- "@type": "xsd:dateTime"
68
- },
69
- "salt": "sec:salt",
70
- "signature": "sec:signature",
71
- "signatureAlgorithm": "sec:signingAlgorithm",
72
- "signatureValue": "sec:signatureValue"
73
- }
74
- }
@@ -1,10 +0,0 @@
1
- {
2
- "@context": {
3
- "wf": "https://purl.archive.org/socialweb/webfinger#",
4
- "xsd": "http://www.w3.org/2001/XMLSchema#",
5
- "webfinger": {
6
- "@id": "wf:webfinger",
7
- "@type": "xsd:string"
8
- }
9
- }
10
- }
@@ -1,371 +0,0 @@
1
- // deliver.mjs — signed outbound delivery + retry queue. Fedify's signRequest
2
- // does draft-cavage HTTP Signatures (what Mastodon verifies). Failures go to
3
- // a JSON-file queue with exponential backoff; peers that stay down are
4
- // dropped after MAX_ATTEMPTS (~3 days).
5
-
6
- import { signRequest } from '@fedify/fedify/sig';
7
- import { webcrypto } from 'node:crypto';
8
- import { attachProof } from './proof.mjs';
9
- import { pinnedFor, retryAfterMs, HTTP_TIMEOUT_MS } from '../shared/safefetch.mjs';
10
- import { USER_AGENT } from '../shared/ua.mjs';
11
- import { dropFollower } from './store.mjs';
12
-
13
- const MAX_REDIRECTS = 3;
14
-
15
- // draft-cavage signs `(request-target): <method> <path><query>`. Fedify 2.3.4
16
- // builds that line from the pathname alone, so a signature over any URL
17
- // carrying a query is one a correct verifier cannot reconstruct — a paged
18
- // collection at `?page=2`, a WordPress inbox at `?rest_route=…`. Re-sign just
19
- // that line, over the header list Fedify already chose; a URL with no query
20
- // comes back untouched, and anything unexpected is left exactly as signed.
21
- export async function withQueryInTarget(signed, privateKey) {
22
- const url = new URL(signed.url);
23
- if (!url.search) return signed;
24
- const header = signed.headers.get('signature');
25
- if (!header) return signed;
26
- const fields = Object.fromEntries(
27
- [...header.matchAll(/([A-Za-z]+)="([^"]*)"/g)].map(m => [m[1], m[2]]));
28
- const names = fields.headers?.split(/\s+/g) || [];
29
- if (!fields.signature || !names.includes('(request-target)')) return signed;
30
- if (names.some(n => n.startsWith('(') && n !== '(request-target)')) return signed;
31
- const message = names.map(n => n === '(request-target)'
32
- ? `(request-target): ${signed.method.toLowerCase()} ${url.pathname}${url.search}`
33
- : `${n}: ${(n === 'host' ? signed.headers.get('host') || url.host : signed.headers.get(n)) ?? ''}`)
34
- .join('\n');
35
- const sig = await webcrypto.subtle.sign('RSASSA-PKCS1-v1_5', privateKey,
36
- new TextEncoder().encode(message));
37
- const headers = new Headers(signed.headers);
38
- headers.set('signature',
39
- header.replace(/signature="[^"]*"/, `signature="${Buffer.from(sig).toString('base64')}"`));
40
- const init = { method: signed.method, headers, signal: signed.signal };
41
- if (signed.method !== 'GET' && signed.method !== 'HEAD') init.body = await signed.arrayBuffer();
42
- return new Request(signed.url, init);
43
- }
44
-
45
- const MAX_ATTEMPTS = 12; // 2^12 min ≈ 68h of backoff
46
- const TICK_MS = 60_000;
47
- const MAX_QUEUE = 2000; // beyond it, overflow dead-letters
48
-
49
- // Is this failure about the SERVER, or about this one recipient? A 410 Gone,
50
- // a 403, a 401 from an authorized-fetch instance are answers about one inbox;
51
- // cooling the whole host for them starves every other recipient on it — which
52
- // on a big instance is most of your followers. Only a refusal that is about
53
- // the server, or no answer at all, is grounds to leave it alone.
54
- const hostOf = (inbox) => { try { return new URL(inbox).host; } catch { return inbox; } };
55
- const aboutTheHost = (e) => !e.status || e.status >= 500 || e.status === 429;
56
- // A 410 says the inbox is gone for good and a 404 that nothing answers there:
57
- // neither earns a second POST. Gone also means whoever read there has left.
58
- const unsalvageable = (e) => e.status === 404 || e.status === 410;
59
-
60
- export class Deliverer {
61
- // passive: signing-only (viewer-mode agents) — no queue drain timer, so a
62
- // read-only agent never mutates shared delivery state. startQueue() flips
63
- // it live when a viewer is promoted to active.
64
- constructor({ store, keyId, rsaPrivate, actorId = null, edPrivate = null, proofKeyId = null,
65
- log = console.log, passive = false, onGone = null }) {
66
- this.store = store;
67
- // Called after followers at a gone inbox are dropped: the agent republishes
68
- // its followers collection.
69
- this.onGone = onGone;
70
- this.keyId = keyId;
71
- this.rsaPrivate = rsaPrivate;
72
- this.actorId = actorId;
73
- this.edPrivate = edPrivate;
74
- this.proofKeyId = proofKeyId;
75
- this.log = log;
76
- // How many fresh deliveries one attempt may carry. The Node deliverer
77
- // opens a socket per recipient; the browser's relay deliverer sends a
78
- // list, and sets this to what the relay accepts.
79
- this.batchSize = 1;
80
- if (!passive) this.startQueue();
81
- }
82
-
83
- startQueue() {
84
- if (this.timer) return;
85
- this.timer = setInterval(() => this.drainQueue().catch(e => this.log(`queue: ${e.message}`)), TICK_MS);
86
- this.timer.unref?.();
87
- }
88
-
89
- // Signed fetch — also used for GETs so authorized-fetch instances answer
90
- // us. Targets come from attacker-supplied documents, so every hop is
91
- // address-checked and the connection is pinned to the checked address.
92
- // Redirects are followed by hand because each hop needs its own signature
93
- // (the signature covers the request target).
94
- async signedFetch(url, init = {}) {
95
- let current = url;
96
- // Named on every hop: a remote operator seeing our deliveries or our
97
- // verify-by-dereference GETs should know what we are.
98
- //
99
- // And bounded. This was the one remote-facing path in the project without a
100
- // deadline — publisher, intake and the token grant all set one — so a peer
101
- // that accepted the connection and then stalled held us for undici's 300s
102
- // default. Against a 60s queue tick that is five overlapping drains
103
- // re-POSTing the same activity at a server already in trouble. One signal,
104
- // built once, so it covers all four redirect hops together rather than
105
- // giving each hop a fresh deadline.
106
- const withUa = {
107
- ...init,
108
- signal: init.signal || AbortSignal.timeout(HTTP_TIMEOUT_MS),
109
- headers: { 'user-agent': USER_AGENT, ...(init.headers || {}) },
110
- };
111
- for (let hop = 0; hop <= MAX_REDIRECTS; hop++) {
112
- const dispatcher = await pinnedFor(current);
113
- const req = new Request(current, withUa);
114
- const signed = await withQueryInTarget(
115
- await signRequest(req, this.rsaPrivate, new URL(this.keyId)), this.rsaPrivate);
116
- const res = await fetch(signed, { ...(dispatcher ? { dispatcher } : {}), redirect: 'manual' });
117
- if (res.status >= 300 && res.status < 400 && res.headers.get('location')) {
118
- current = new URL(res.headers.get('location'), current).href;
119
- continue;
120
- }
121
- // Where this ended up, which is not always where it was asked to go.
122
- // Callers compare the document's own id against the URL they REQUESTED,
123
- // so without this an open redirector on a victim's origin let an
124
- // attacker's server answer for a victim's URL and be believed.
125
- try { Object.defineProperty(res, 'finalUrl', { value: current, configurable: true }); }
126
- catch { /* a frozen Response: the caller falls back to the requested URL */ }
127
- return res;
128
- }
129
- throw new Error(`too many redirects from ${url}`);
130
- }
131
-
132
- async deliverNow(inbox, activity) {
133
- const res = await this.signedFetch(inbox, {
134
- method: 'POST',
135
- headers: { 'content-type': 'application/activity+json' },
136
- body: JSON.stringify(activity),
137
- });
138
- if (res.status >= 400) {
139
- // Carry what the server told us instead of flattening it to a string.
140
- // The Response used to be discarded here, headers and all, which is why
141
- // a `Retry-After: 3600` was answered with four more POSTs inside the
142
- // hour it asked for.
143
- const err = new Error(`POST ${inbox} → ${res.status}`);
144
- err.status = res.status;
145
- // Only when the header is ACTUALLY there. Defaulting made every 503
146
- // without one — which is most of them — collapse the exponential ladder
147
- // to a flat 60s, the opposite of the intent. A remote's own instruction
148
- // is honoured up to a day rather than the pod path's 30-minute ceiling:
149
- // MAX_ATTEMPTS bounds the wait anyway.
150
- const ra = retryAfterMs(res, 24 * 60 * 60_000);
151
- if (ra != null) err.retryAfterMs = ra;
152
- throw err;
153
- }
154
- return res;
155
- }
156
-
157
- // Deliver, queueing on failure.
158
- // FEP-8b32: a proof on everything WE author, so a server that meets one of
159
- // our activities second-hand can verify it without asking us.
160
- //
161
- // Only ours. An activity we forward on someone else's behalf keeps whatever
162
- // proof its author gave it — signing that one as an assertion of ours would
163
- // be claiming to have written it.
164
- async proofed(activity) {
165
- if (!this.edPrivate || !this.proofKeyId || !activity || typeof activity !== 'object') return activity;
166
- if (activity.proof) return activity;
167
- const actor = typeof activity.actor === 'string' ? activity.actor : activity.actor?.id;
168
- if (!this.actorId || actor !== this.actorId) return activity;
169
- try {
170
- return await attachProof(activity,
171
- { privateKey: this.edPrivate, verificationMethod: this.proofKeyId });
172
- } catch (e) {
173
- this.log(`proof: ${e.message}`); // an unproved activity still federates
174
- return activity;
175
- }
176
- }
177
-
178
- // One attempt per target, answered in order: `{ ok: true }` or `{ error }`
179
- // with the error deliverNow would have thrown. Here one at a time; the
180
- // relay deliverer sends the whole list in one call.
181
- async deliverManyNow(targets) {
182
- const out = [];
183
- for (const t of targets) {
184
- try { await this.deliverNow(t.inbox, t.activity); out.push({ ok: true }); }
185
- catch (error) { out.push({ error }); }
186
- }
187
- return out;
188
- }
189
-
190
- async deliver(inbox, activity) {
191
- // Proved before anything else, so the copy that goes on the queue is the
192
- // copy that was signed — a retry days later must not post a bare activity.
193
- const signed = await this.proofed(activity);
194
- if (this._queueIfCooling(inbox, signed)) return;
195
- const [result] = await this.deliverManyNow([{ inbox, activity: signed }]);
196
- await this._settle(inbox, signed, result);
197
- }
198
-
199
- // A host we already know is refusing: queue without asking again. This is
200
- // the path a FRESH activity takes, so without it a fan-out to a struggling
201
- // server opened one socket per follower before any of this applied.
202
- _queueIfCooling(inbox, signed) {
203
- const host = hostOf(inbox);
204
- const until = this._cooling?.get(host);
205
- if (!until || until <= Date.now()) return false;
206
- this.log(`${host} is cooling — queueing ${signed.type} rather than asking again`);
207
- this._enqueue({ inbox, activity: signed, attempts: 1, nextAt: until });
208
- return true;
209
- }
210
-
211
- // What one attempt's outcome means for the queue.
212
- async _settle(inbox, signed, result) {
213
- if (result.ok) { this.log(`delivered ${signed.type} → ${inbox}`); return; }
214
- const e = result.error;
215
- if (unsalvageable(e)) { await this._unsalvageable(inbox, e); return; }
216
- this.log(`delivery failed (${e.message}) — queued`);
217
- const wait = e.retryAfterMs || 60_000;
218
- if (aboutTheHost(e)) {
219
- this._cooling ||= new Map();
220
- this._cooling.set(hostOf(inbox), Date.now() + wait);
221
- }
222
- this._enqueue({ inbox, activity: signed, attempts: 1, nextAt: Date.now() + wait });
223
- }
224
-
225
- // Not retried, and on a 410 the followers who received there are dropped,
226
- // so the next fan-out stops asking; the followers collection is republished
227
- // through onGone. A 404 drops only this delivery.
228
- async _unsalvageable(inbox, e) {
229
- if (e.status !== 410) { this.log(`delivery dropped (${e.message}) — nothing answers there`); return; }
230
- const contacts = this.store.getContacts?.();
231
- const left = (contacts?.followers || []).filter(f => f.inbox === inbox || f.sharedInbox === inbox);
232
- this.log(`${inbox} is gone (410) — delivery dropped${left.length ? `, ${left.length} follower(s) there forgotten` : ''}`);
233
- if (!left.length) return;
234
- for (const f of left) dropFollower(contacts, f.actor, 'inbox-gone');
235
- this.store.setContacts(contacts);
236
- try { await this.onGone?.(); } catch (err) { this.log(`followers republish after 410: ${err.message}`); }
237
- }
238
-
239
- // The queue has a ceiling: a fan-out into a sea of down servers must not grow
240
- // queue.json without bound. Overflow keeps the evidence as a dead letter.
241
- _enqueue(item) {
242
- const q = this.store.getQueue();
243
- if (q.length >= MAX_QUEUE) {
244
- this.log(`delivery queue is full (${MAX_QUEUE}) — dead-lettering ${item.activity?.type} → ${item.inbox}`);
245
- this.store.addDeadLetter({ inboxUrl: item.inbox, reason: 'delivery queue full', activity: item.activity });
246
- return;
247
- }
248
- q.push(item);
249
- this.store.setQueue(q);
250
- }
251
-
252
- async deliverToAll(inboxes, activity) {
253
- // Proved once for the whole fan-out: every recipient gets the same bytes,
254
- // and one signature is computed rather than one per follower.
255
- const signed = await this.proofed(activity);
256
- // Shared inboxes deduplicate fan-out to the same server. Sent in batches
257
- // of batchSize, and a host found cooling by one batch is not asked again
258
- // by the next.
259
- const targets = [...new Set(inboxes)].map((inbox) => ({ inbox, activity: signed }));
260
- for (let i = 0; i < targets.length; i += this.batchSize) {
261
- const chunk = targets.slice(i, i + this.batchSize).filter((t) => !this._queueIfCooling(t.inbox, signed));
262
- if (!chunk.length) continue;
263
- const results = await this.deliverManyNow(chunk);
264
- for (let k = 0; k < chunk.length; k++) await this._settle(chunk[k].inbox, signed, results[k]);
265
- }
266
- }
267
-
268
- // Serialized, for the same reason Intake.drain is: the tick is 60s and a
269
- // drain over slow peers outlasts it, so a second run started on top of the
270
- // first. Both read their own clone of queue.json, so both re-POSTed every due
271
- // activity — duplicate deliveries at a server that is already failing — and
272
- // both then wrote `keep` back from a stale snapshot, losing the attempt
273
- // counts that are supposed to make the backoff converge.
274
- async drainQueue() {
275
- if (this._draining) { this._drainAgain = true; return this._draining; }
276
- this._draining = this._drainQueueOnce().finally(() => {
277
- this._draining = null;
278
- if (this._drainAgain) { this._drainAgain = false; this.drainQueue().catch(e => this.log(`queue: ${e.message}`)); }
279
- });
280
- return this._draining;
281
- }
282
-
283
- // The queue is walked per HOST, not per item.
284
- //
285
- // It used to be a flat sequential loop, which had two costs against a server
286
- // that was down. Every due item for that host was POSTed — the first failure
287
- // told us nothing the code could act on — and because each request carries a
288
- // 15s deadline, K items for one dead host held the drain for up to 15K
289
- // seconds while every healthy peer's activity waited behind them. Compounding
290
- // requests at a struggling server, and head-of-line blocking of everyone
291
- // else: the shape of the incident this project has a postmortem for.
292
- //
293
- // One failure now cools the whole host for the rest of this drain, and a
294
- // Retry-After it sent decides for how long. The breaker is in memory; what
295
- // persists is nextAt, which the queue already carried.
296
- async _drainQueueOnce() {
297
- const q = this.store.getQueue();
298
- if (!q.length) return;
299
- const now = Date.now();
300
- const keep = [];
301
- // Whether anything actually moved. The only early return above is on an
302
- // EMPTY queue, so one item with a far-future nextAt used to rewrite
303
- // queue.json on all 1440 ticks of a day with byte-identical content —
304
- // PodStore.write has no equality check, and the ordinary ladder reaches
305
- // hours-long waits by attempt 6, so any peer that fails a few times got
306
- // there. A length compare is not enough: getQueue() hands back a clone, so
307
- // attempts/nextAt are mutated in place and the count stays the same.
308
- let changed = false;
309
- // Outlives the drain: a Retry-After only reached items that happened to be
310
- // due in the same pass, so a sibling due a minute later ignored it entirely.
311
- this._cooling ||= new Map(); // host → until
312
- for (const [h, until] of this._cooling) if (until <= now) this._cooling.delete(h);
313
-
314
- const ladderFor = (item) => {
315
- const base = Math.min(2 ** item.attempts, 2 ** MAX_ATTEMPTS) * 60_000;
316
- return Math.round(base * (0.85 + Math.random() * 0.3)); // jittered, not lockstep
317
- };
318
-
319
- for (const item of q) {
320
- // Due first, THEN cooling: an item that is not yet due must not have its
321
- // ladder advanced on every 60s tick of a 15-minute cooldown. Once
322
- // deferred its nextAt IS the cooldown, so it advances exactly once per
323
- // cooldown — the same rate as the item that opened the socket.
324
- if (item.nextAt > now) { keep.push(item); continue; }
325
- const host = hostOf(item.inbox);
326
- const until = this._cooling.get(host);
327
- if (until && until > now) {
328
- // A sibling already failed against this host. Defer without opening a
329
- // socket — but ADVANCE THE LADDER, or only the one item that actually
330
- // made a request ever climbs it and a queue of K items takes K times
331
- // as long to give up as the module promises.
332
- item.attempts += 1;
333
- changed = true;
334
- if (item.attempts > MAX_ATTEMPTS) {
335
- this.log(`giving up on ${item.inbox} after ${MAX_ATTEMPTS} attempts`);
336
- continue;
337
- }
338
- item.nextAt = Math.max(item.nextAt, until);
339
- keep.push(item);
340
- continue;
341
- }
342
- try {
343
- await this.deliverNow(item.inbox, item.activity);
344
- changed = true;
345
- this.log(`retry ok: ${item.activity.type} → ${item.inbox}`);
346
- } catch (e) {
347
- if (unsalvageable(e)) { changed = true; await this._unsalvageable(item.inbox, e); continue; }
348
- item.attempts += 1;
349
- changed = true;
350
- const wait = e.retryAfterMs || ladderFor(item);
351
- // Cool the host BEFORE the give-up test: a failure is evidence about
352
- // the server whether or not the item that produced it survives, and
353
- // skipping this left the flat behaviour intact for exactly the backlog
354
- // that has just aged out.
355
- if (aboutTheHost(e)) {
356
- this._cooling.set(host, now + wait);
357
- if (e.retryAfterMs) this.log(`${host} asked for ${Math.round(wait / 1000)}s — honouring it`);
358
- }
359
- if (item.attempts > MAX_ATTEMPTS) {
360
- this.log(`giving up on ${item.inbox} after ${MAX_ATTEMPTS} attempts`);
361
- continue;
362
- }
363
- item.nextAt = now + wait;
364
- keep.push(item);
365
- }
366
- }
367
- if (changed) this.store.setQueue(keep);
368
- }
369
-
370
- stop() { clearInterval(this.timer); this.timer = null; }
371
- }