fedipod-server 0.29.0 → 0.31.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 (444) hide show
  1. package/README.md +16 -6
  2. package/dist/claims.d.ts +2 -0
  3. package/dist/claims.js +13 -4
  4. package/dist/door.d.ts +4 -0
  5. package/dist/door.js +31 -0
  6. package/dist/fedipod.d.ts +15 -0
  7. package/dist/fedipod.js +13 -0
  8. package/dist/front.d.ts +3 -0
  9. package/dist/front.js +92 -0
  10. package/dist/handler.d.ts +36 -101
  11. package/dist/handler.js +93 -644
  12. package/dist/handler.jsonld +2 -30
  13. package/dist/identities.d.ts +71 -0
  14. package/dist/identities.js +236 -0
  15. package/dist/mounts.d.ts +43 -0
  16. package/dist/mounts.js +123 -0
  17. package/dist/opt-in.d.ts +46 -0
  18. package/dist/opt-in.js +176 -0
  19. package/dist/private-read.d.ts +4 -0
  20. package/dist/private-read.js +34 -0
  21. package/package.json +12 -20
  22. package/lib/client/c2s.mjs +0 -636
  23. package/lib/client/localapi.mjs +0 -87
  24. package/lib/client/masto/accounts.mjs +0 -310
  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 -387
  65. package/lib/core/graphview.mjs +0 -327
  66. package/lib/core/intake/activities.mjs +0 -649
  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 -258
  70. package/lib/core/intake/index.mjs +0 -732
  71. package/lib/core/intake/notes.mjs +0 -367
  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/place.mjs +0 -82
  76. package/lib/core/polls.mjs +0 -105
  77. package/lib/core/profile-page.mjs +0 -104
  78. package/lib/core/proof.mjs +0 -79
  79. package/lib/core/publisher/collections.mjs +0 -272
  80. package/lib/core/publisher/index.mjs +0 -522
  81. package/lib/core/publisher/notes.mjs +0 -519
  82. package/lib/core/publisher/own.mjs +0 -143
  83. package/lib/core/publisher/questions.mjs +0 -241
  84. package/lib/core/publisher/restore.mjs +0 -224
  85. package/lib/core/shapes/activitystreams.ttl +0 -157
  86. package/lib/core/shapes/index.mjs +0 -132
  87. package/lib/core/shapes/shapes-text.mjs +0 -13
  88. package/lib/core/social.mjs +0 -561
  89. package/lib/core/storage.mjs +0 -176
  90. package/lib/core/store.mjs +0 -578
  91. package/lib/core/wire-quotes.mjs +0 -71
  92. package/lib/core/wire.mjs +0 -925
  93. package/lib/device/account.mjs +0 -87
  94. package/lib/device/admin/index.mjs +0 -13
  95. package/lib/device/admin/origins.mjs +0 -35
  96. package/lib/device/admin/routes/connections.mjs +0 -144
  97. package/lib/device/admin/routes/gateway.mjs +0 -199
  98. package/lib/device/admin/routes/lifecycle.mjs +0 -200
  99. package/lib/device/admin/routes/owner.mjs +0 -322
  100. package/lib/device/admin/routes/setup.mjs +0 -428
  101. package/lib/device/admin/routes/social.mjs +0 -188
  102. package/lib/device/admin/server.mjs +0 -95
  103. package/lib/device/admin/static.mjs +0 -247
  104. package/lib/device/admin/surface.mjs +0 -315
  105. package/lib/device/certs.mjs +0 -281
  106. package/lib/device/cli/commands/account.mjs +0 -586
  107. package/lib/device/cli/commands/run.mjs +0 -278
  108. package/lib/device/cli/commands/service.mjs +0 -221
  109. package/lib/device/cli/commands/setup.mjs +0 -489
  110. package/lib/device/cli/commands/state.mjs +0 -561
  111. package/lib/device/cli/context.mjs +0 -288
  112. package/lib/device/export-collections.mjs +0 -161
  113. package/lib/device/gateway-move.mjs +0 -58
  114. package/lib/device/home.mjs +0 -161
  115. package/lib/device/migrate.mjs +0 -115
  116. package/lib/device/ports.mjs +0 -27
  117. package/lib/device/remote.mjs +0 -54
  118. package/lib/device/setup.mjs +0 -496
  119. package/lib/device/update.mjs +0 -96
  120. package/lib/gateway/caches.mjs +0 -36
  121. package/lib/gateway/directory.mjs +0 -90
  122. package/lib/gateway/front-core.mjs +0 -966
  123. package/lib/gateway/gateway-core.mjs +0 -244
  124. package/lib/gateway/headers.mjs +0 -49
  125. package/lib/gateway/httpsig.mjs +0 -159
  126. package/lib/gateway/notices.mjs +0 -84
  127. package/lib/gateway/quiet.mjs +0 -189
  128. package/lib/gateway/relay-extras.mjs +0 -89
  129. package/lib/gateway/token-claims.mjs +0 -16
  130. package/lib/pod/README.md +0 -61
  131. package/lib/pod/actor.mjs +0 -96
  132. package/lib/pod/collection.mjs +0 -77
  133. package/lib/pod/containers.mjs +0 -140
  134. package/lib/pod/discovery.mjs +0 -46
  135. package/lib/pod/featured.mjs +0 -18
  136. package/lib/pod/followers.mjs +0 -24
  137. package/lib/pod/following.mjs +0 -10
  138. package/lib/pod/http.mjs +0 -74
  139. package/lib/pod/inbox.mjs +0 -166
  140. package/lib/pod/links.mjs +0 -35
  141. package/lib/pod/location.mjs +0 -52
  142. package/lib/pod/media.mjs +0 -27
  143. package/lib/pod/notes.mjs +0 -78
  144. package/lib/pod/notifications.mjs +0 -89
  145. package/lib/pod/outbox.mjs +0 -18
  146. package/lib/pod/package.json +0 -19
  147. package/lib/pod/policy.mjs +0 -58
  148. package/lib/pod/private.mjs +0 -19
  149. package/lib/pod/root.mjs +0 -104
  150. package/lib/pod/state.mjs +0 -40
  151. package/lib/pod/transport.mjs +0 -674
  152. package/lib/pod/type-index.mjs +0 -101
  153. package/lib/pod/urls.mjs +0 -91
  154. package/lib/server/embed.mjs +0 -424
  155. package/lib/session/README.md +0 -137
  156. package/lib/session/demo.html +0 -94
  157. package/lib/session/fedi-account.mjs +0 -417
  158. package/lib/session/fedi-login.mjs +0 -123
  159. package/lib/session/oidc-session.mjs +0 -233
  160. package/lib/session/package.json +0 -49
  161. package/lib/shared/guard.mjs +0 -213
  162. package/lib/shared/links.mjs +0 -9
  163. package/lib/shared/safefetch.mjs +0 -235
  164. package/lib/shared/ua.mjs +0 -29
  165. package/phanpy/dist/404.html +0 -40
  166. package/phanpy/dist/_headers +0 -2
  167. package/phanpy/dist/apple-touch-icon.png +0 -0
  168. package/phanpy/dist/assets/ICONS-DIPJeU0C.js +0 -2
  169. package/phanpy/dist/assets/ICONS-DIPJeU0C.js.map +0 -1
  170. package/phanpy/dist/assets/ScheduledAtField-D-Abfo4J.js +0 -183
  171. package/phanpy/dist/assets/ScheduledAtField-D-Abfo4J.js.map +0 -1
  172. package/phanpy/dist/assets/Temml-CCjoacWt.woff2 +0 -0
  173. package/phanpy/dist/assets/__vite-browser-external-C7iut881.js +0 -2
  174. package/phanpy/dist/assets/__vite-browser-external-C7iut881.js.map +0 -1
  175. package/phanpy/dist/assets/boosts-carousel-BiOaNdMT.jpg +0 -0
  176. package/phanpy/dist/assets/catch-up-O5q5C75Z.png +0 -0
  177. package/phanpy/dist/assets/chunk-aKtaBQYM.js +0 -1
  178. package/phanpy/dist/assets/compose-CFEx7wb4.js +0 -2
  179. package/phanpy/dist/assets/compose-CFEx7wb4.js.map +0 -1
  180. package/phanpy/dist/assets/compose-ZJN5xSb4.js +0 -35
  181. package/phanpy/dist/assets/compose-ZJN5xSb4.js.map +0 -1
  182. package/phanpy/dist/assets/debug.module-YFQgwnIr.js +0 -52
  183. package/phanpy/dist/assets/debug.module-YFQgwnIr.js.map +0 -1
  184. package/phanpy/dist/assets/dom-p1YcJ5Rw.js +0 -2
  185. package/phanpy/dist/assets/dom-p1YcJ5Rw.js.map +0 -1
  186. package/phanpy/dist/assets/home-mobile-dark@2x-Dc-J8-SM.png +0 -0
  187. package/phanpy/dist/assets/home-mobile-light@2x-Bd3fod8m.png +0 -0
  188. package/phanpy/dist/assets/home-tablet-dark@2x-BgcNI8le.png +0 -0
  189. package/phanpy/dist/assets/home-tablet-light@2x-BW2RV9T6.png +0 -0
  190. package/phanpy/dist/assets/icons/add-circle-line-WD0mwORD.js +0 -1
  191. package/phanpy/dist/assets/icons/alert-line-CpFUMf9s.js +0 -1
  192. package/phanpy/dist/assets/icons/android-2-line-Bzxnrd3S.js +0 -1
  193. package/phanpy/dist/assets/icons/announcement-line-5nwaJFJm.js +0 -1
  194. package/phanpy/dist/assets/icons/arrow-down-circle-line-a7Id9-Iv.js +0 -1
  195. package/phanpy/dist/assets/icons/arrow-down-line-B8uzXzq6.js +0 -1
  196. package/phanpy/dist/assets/icons/arrow-left-line-CGrGT9C-.js +0 -1
  197. package/phanpy/dist/assets/icons/arrow-right-line-CE-HERfX.js +0 -1
  198. package/phanpy/dist/assets/icons/arrow-to-up-line-CvdQjmcy.js +0 -1
  199. package/phanpy/dist/assets/icons/arrow-up-circle-line-DEparSNF.js +0 -1
  200. package/phanpy/dist/assets/icons/arrow-up-line-C7yBeugc.js +0 -1
  201. package/phanpy/dist/assets/icons/arrows-right-line-BtbXmJGP.js +0 -1
  202. package/phanpy/dist/assets/icons/at-line-CF66Bznk.js +0 -1
  203. package/phanpy/dist/assets/icons/attachment-line-BY8It-l9.js +0 -1
  204. package/phanpy/dist/assets/icons/blockquote-line-BS6pU_hm.js +0 -1
  205. package/phanpy/dist/assets/icons/board-line-C0UWXyA6.js +0 -1
  206. package/phanpy/dist/assets/icons/bookmark-line-1CtX5JLF.js +0 -1
  207. package/phanpy/dist/assets/icons/building-5-line-Dd59MzbO.js +0 -1
  208. package/phanpy/dist/assets/icons/bus-2-line-6_dvA0GG.js +0 -1
  209. package/phanpy/dist/assets/icons/calendar-day-line-Crpl_fmu.js +0 -1
  210. package/phanpy/dist/assets/icons/calendar-month-line-vommts2C.js +0 -1
  211. package/phanpy/dist/assets/icons/calendar-time-add-line-BU_DTlQk.js +0 -1
  212. package/phanpy/dist/assets/icons/camera-line-Cv69MEei.js +0 -1
  213. package/phanpy/dist/assets/icons/celebrate-line-B8DvIoi0.js +0 -1
  214. package/phanpy/dist/assets/icons/chart-bar-line-FK-QOmtF.js +0 -1
  215. package/phanpy/dist/assets/icons/chart-line-line-BGcJAFWw.js +0 -1
  216. package/phanpy/dist/assets/icons/chat-3-line-YFK2-hwz.js +0 -1
  217. package/phanpy/dist/assets/icons/check-circle-line-DsAxFr5-.js +0 -1
  218. package/phanpy/dist/assets/icons/clipboard-line-BBp364gJ.js +0 -1
  219. package/phanpy/dist/assets/icons/close-circle-line-CICdiuGz.js +0 -1
  220. package/phanpy/dist/assets/icons/close-line-DYrFWcjv.js +0 -1
  221. package/phanpy/dist/assets/icons/cloud-line-tiQAbah8.js +0 -1
  222. package/phanpy/dist/assets/icons/code-line-fAl8iiV3.js +0 -1
  223. package/phanpy/dist/assets/icons/comment-2-line-CR3g7WeB.js +0 -1
  224. package/phanpy/dist/assets/icons/copy-2-line-CLW69yF5.js +0 -1
  225. package/phanpy/dist/assets/icons/delete-2-line-B8xmbr4n.js +0 -1
  226. package/phanpy/dist/assets/icons/document-3-line-DBGNhAg2.js +0 -1
  227. package/phanpy/dist/assets/icons/document-line-C7mb9sb7.js +0 -1
  228. package/phanpy/dist/assets/icons/down-line-BGqSSThd.js +0 -1
  229. package/phanpy/dist/assets/icons/edit-4-line-DcgGaEhx.js +0 -1
  230. package/phanpy/dist/assets/icons/emoji-2-line-D8YgnM-y.js +0 -1
  231. package/phanpy/dist/assets/icons/exit-line-Dc07BS-x.js +0 -1
  232. package/phanpy/dist/assets/icons/external-link-line-BvpSkmjO.js +0 -1
  233. package/phanpy/dist/assets/icons/eye-2-line-CiWXx-f_.js +0 -1
  234. package/phanpy/dist/assets/icons/eye-close-line-BgTaSJ4P.js +0 -1
  235. package/phanpy/dist/assets/icons/filter-2-line-B_mR2usy.js +0 -1
  236. package/phanpy/dist/assets/icons/filter-line-C_nm-3cU.js +0 -1
  237. package/phanpy/dist/assets/icons/flag-1-line-C2HB05I0.js +0 -1
  238. package/phanpy/dist/assets/icons/forbid-circle-line-D3LTT9yd.js +0 -1
  239. package/phanpy/dist/assets/icons/formula-line-DLrt2d6j.js +0 -1
  240. package/phanpy/dist/assets/icons/grid-line-CgUmEikf.js +0 -1
  241. package/phanpy/dist/assets/icons/group-line-C5fnX0wU.js +0 -1
  242. package/phanpy/dist/assets/icons/hand-finger-2-line-BGgxbuL9.js +0 -1
  243. package/phanpy/dist/assets/icons/hashtag-line-CyTEEXBb.js +0 -1
  244. package/phanpy/dist/assets/icons/heart-crack-line-BZddbO2C.js +0 -1
  245. package/phanpy/dist/assets/icons/heart-line-CJ7K47J4.js +0 -1
  246. package/phanpy/dist/assets/icons/history-2-line-kWWvFzqv.js +0 -1
  247. package/phanpy/dist/assets/icons/history-line-bGQiwbTH.js +0 -1
  248. package/phanpy/dist/assets/icons/home-3-line-fRU1zeLG.js +0 -1
  249. package/phanpy/dist/assets/icons/information-line-iNk6-sOY.js +0 -1
  250. package/phanpy/dist/assets/icons/keyboard-line-DsCyuQNz.js +0 -1
  251. package/phanpy/dist/assets/icons/layout-4-line-CZkOX52Z.js +0 -1
  252. package/phanpy/dist/assets/icons/layout-5-line-B_znt5wO.js +0 -1
  253. package/phanpy/dist/assets/icons/left-line-eUw0tya4.js +0 -1
  254. package/phanpy/dist/assets/icons/lightning-line-DaKoDdix.js +0 -1
  255. package/phanpy/dist/assets/icons/link-2-line-DCqLmMA7.js +0 -1
  256. package/phanpy/dist/assets/icons/list-check-line-BAxosBh9.js +0 -1
  257. package/phanpy/dist/assets/icons/lock-line-CPoaXeUW.js +0 -1
  258. package/phanpy/dist/assets/icons/mail-line-B0P8aa1L.js +0 -1
  259. package/phanpy/dist/assets/icons/moon-line-CEqNAToP.js +0 -1
  260. package/phanpy/dist/assets/icons/more-1-fill-Dw2e5xab.js +0 -1
  261. package/phanpy/dist/assets/icons/more-3-line-BIiVjYTz.js +0 -1
  262. package/phanpy/dist/assets/icons/notification-line-BZZq2Gtu.js +0 -1
  263. package/phanpy/dist/assets/icons/pencil-line-CKh8-A1v.js +0 -1
  264. package/phanpy/dist/assets/icons/photo-album-line-Bnfg2u40.js +0 -1
  265. package/phanpy/dist/assets/icons/pin-line-DpTolyhs.js +0 -1
  266. package/phanpy/dist/assets/icons/play-fill-B0-G_8lX.js +0 -1
  267. package/phanpy/dist/assets/icons/qrcode-2-line-B6SPCaHn.js +0 -1
  268. package/phanpy/dist/assets/icons/quill-pen-line-B6LUrnCP.js +0 -1
  269. package/phanpy/dist/assets/icons/quote-left-fill-hQZ1cj_6.js +0 -1
  270. package/phanpy/dist/assets/icons/radar-line-CKyroTth.js +0 -1
  271. package/phanpy/dist/assets/icons/react-line-CoIKhhtK.js +0 -1
  272. package/phanpy/dist/assets/icons/refresh-2-line-D0mTF97S.js +0 -1
  273. package/phanpy/dist/assets/icons/right-line-B3KWJaqc.js +0 -1
  274. package/phanpy/dist/assets/icons/rocket-line-4KZl537D.js +0 -1
  275. package/phanpy/dist/assets/icons/round-fill-C69NTrZ0.js +0 -1
  276. package/phanpy/dist/assets/icons/round-line-B9k_1uo-.js +0 -1
  277. package/phanpy/dist/assets/icons/route-line-B28ReHcE.js +0 -1
  278. package/phanpy/dist/assets/icons/rows-4-line-CrBniagM.js +0 -1
  279. package/phanpy/dist/assets/icons/scan-line-DSf74YdP.js +0 -1
  280. package/phanpy/dist/assets/icons/search-2-line-B6geIU1z.js +0 -1
  281. package/phanpy/dist/assets/icons/settings-3-line-azJwK47w.js +0 -1
  282. package/phanpy/dist/assets/icons/settings-6-line-C_5zc5YR.js +0 -1
  283. package/phanpy/dist/assets/icons/share-2-line-D5MxWjrm.js +0 -1
  284. package/phanpy/dist/assets/icons/share-forward-line-Cp61fZzZ.js +0 -1
  285. package/phanpy/dist/assets/icons/sparkles-2-line-DYYyXTaW.js +0 -1
  286. package/phanpy/dist/assets/icons/sparkles-line-C2hY1s6I.js +0 -1
  287. package/phanpy/dist/assets/icons/time-line-BF3eet1v.js +0 -1
  288. package/phanpy/dist/assets/icons/transfer-4-line-oisfvTBw.js +0 -1
  289. package/phanpy/dist/assets/icons/translate-line-BFWol2Ce.js +0 -1
  290. package/phanpy/dist/assets/icons/unlock-line-X_x5vwv3.js +0 -1
  291. package/phanpy/dist/assets/icons/upload-3-line-DgzwUJeW.js +0 -1
  292. package/phanpy/dist/assets/icons/user-4-line-BVSynZCp.js +0 -1
  293. package/phanpy/dist/assets/icons/user-add-2-line-BlGS5Q8q.js +0 -1
  294. package/phanpy/dist/assets/icons/user-add-line-BbXdHG4z.js +0 -1
  295. package/phanpy/dist/assets/icons/user-edit-line-BFAHOV0X.js +0 -1
  296. package/phanpy/dist/assets/icons/user-follow-line-BXux0wSd.js +0 -1
  297. package/phanpy/dist/assets/icons/user-star-line-DdYlpHLU.js +0 -1
  298. package/phanpy/dist/assets/icons/user-warning-line-Cj43j0_E.js +0 -1
  299. package/phanpy/dist/assets/icons/user-x-line-DIDgNlPH.js +0 -1
  300. package/phanpy/dist/assets/icons/volume-line-Bs390KQt.js +0 -1
  301. package/phanpy/dist/assets/icons/volume-mute-line-DzlzIqFZ.js +0 -1
  302. package/phanpy/dist/assets/icons/walk-line-yNXfB-md.js +0 -1
  303. package/phanpy/dist/assets/icons/world-2-line-CenkGIwj.js +0 -1
  304. package/phanpy/dist/assets/icons/zoom-in-line-CaENUphf.js +0 -1
  305. package/phanpy/dist/assets/icons/zoom-out-line-BDNk0EWo.js +0 -1
  306. package/phanpy/dist/assets/instances-BNPptnn-.json +0 -333
  307. package/phanpy/dist/assets/locales/ar-SA-CfheRbCu.js +0 -1
  308. package/phanpy/dist/assets/locales/ca-ES-DHRW86Tz.js +0 -1
  309. package/phanpy/dist/assets/locales/cs-CZ-CKoqEuA1.js +0 -1
  310. package/phanpy/dist/assets/locales/de-DE-ByRPMRFy.js +0 -1
  311. package/phanpy/dist/assets/locales/eo-UY-CPC7wLCS.js +0 -1
  312. package/phanpy/dist/assets/locales/es-ES-dJa0Fd9t.js +0 -1
  313. package/phanpy/dist/assets/locales/eu-ES-C3djlPR2.js +0 -1
  314. package/phanpy/dist/assets/locales/fa-IR-DTT490Ck.js +0 -1
  315. package/phanpy/dist/assets/locales/fi-FI-B23MCPuz.js +0 -1
  316. package/phanpy/dist/assets/locales/fr-FR-BcaQsO5C.js +0 -1
  317. package/phanpy/dist/assets/locales/gl-ES-BR-CVKBN.js +0 -1
  318. package/phanpy/dist/assets/locales/he-IL-CfheRbCu.js +0 -1
  319. package/phanpy/dist/assets/locales/hu-HU-CfheRbCu.js +0 -1
  320. package/phanpy/dist/assets/locales/it-IT-C1136UFV.js +0 -1
  321. package/phanpy/dist/assets/locales/ja-JP-CRZmRAhD.js +0 -1
  322. package/phanpy/dist/assets/locales/kab-Cz8xH_TT.js +0 -1
  323. package/phanpy/dist/assets/locales/ko-KR-CG9UiY4o.js +0 -1
  324. package/phanpy/dist/assets/locales/lt-LT-CJcI6sHZ.js +0 -1
  325. package/phanpy/dist/assets/locales/nb-NO-CfheRbCu.js +0 -1
  326. package/phanpy/dist/assets/locales/nl-NL-DxN4Yc1t.js +0 -1
  327. package/phanpy/dist/assets/locales/oc-FR-CfheRbCu.js +0 -1
  328. package/phanpy/dist/assets/locales/pl-PL-DkwGXf6P.js +0 -1
  329. package/phanpy/dist/assets/locales/pseudo-LOCALE-CQ4XxkQ0.js +0 -1
  330. package/phanpy/dist/assets/locales/pt-BR-nGL0HdFS.js +0 -1
  331. package/phanpy/dist/assets/locales/pt-PT-Cp17aj2z.js +0 -1
  332. package/phanpy/dist/assets/locales/ru-RU-0Y8cIoaK.js +0 -1
  333. package/phanpy/dist/assets/locales/th-TH-CfheRbCu.js +0 -1
  334. package/phanpy/dist/assets/locales/tok-CfheRbCu.js +0 -1
  335. package/phanpy/dist/assets/locales/tr-TR-CfheRbCu.js +0 -1
  336. package/phanpy/dist/assets/locales/uk-UA-C5E3dFBf.js +0 -1
  337. package/phanpy/dist/assets/locales/zh-CN-BHJi7_et.js +0 -1
  338. package/phanpy/dist/assets/locales/zh-TW-CfheRbCu.js +0 -1
  339. package/phanpy/dist/assets/main-BdqNbG-a.js +0 -26
  340. package/phanpy/dist/assets/main-BdqNbG-a.js.map +0 -1
  341. package/phanpy/dist/assets/mock-home-DGhR__He.js +0 -2
  342. package/phanpy/dist/assets/mock-home-DGhR__He.js.map +0 -1
  343. package/phanpy/dist/assets/multi-column-ROck0NVt.jpg +0 -0
  344. package/phanpy/dist/assets/multi-hashtag-timeline-DH5INVHi.jpg +0 -0
  345. package/phanpy/dist/assets/nested-comments-thread-hv59kZLc.jpg +0 -0
  346. package/phanpy/dist/assets/polyfill-force-H8sUkLGp.js +0 -2
  347. package/phanpy/dist/assets/polyfill-force-H8sUkLGp.js.map +0 -1
  348. package/phanpy/dist/assets/pwa-viewport-ogKbF51K.js +0 -20
  349. package/phanpy/dist/assets/pwa-viewport-ogKbF51K.js.map +0 -1
  350. package/phanpy/dist/assets/style-B1iLPTPR.css +0 -2
  351. package/phanpy/dist/assets/temml-DuyIQMei.js +0 -97
  352. package/phanpy/dist/assets/temml-DuyIQMei.js.map +0 -1
  353. package/phanpy/dist/assets/tinyld.light.node-DtbBhLYC.js +0 -2
  354. package/phanpy/dist/assets/tinyld.light.node-DtbBhLYC.js.map +0 -1
  355. package/phanpy/dist/assets/year-in-posts-B2lywqdx.png +0 -0
  356. package/phanpy/dist/assets/year-in-posts-DJv_star.js +0 -3
  357. package/phanpy/dist/assets/year-in-posts-DJv_star.js.map +0 -1
  358. package/phanpy/dist/compose/index.html +0 -20
  359. package/phanpy/dist/favicon.ico +0 -0
  360. package/phanpy/dist/index.html +0 -73
  361. package/phanpy/dist/logo-192.png +0 -0
  362. package/phanpy/dist/logo-512.png +0 -0
  363. package/phanpy/dist/logo-badge-72.png +0 -0
  364. package/phanpy/dist/logo-maskable-512.png +0 -0
  365. package/phanpy/dist/logo-monochrome-512.png +0 -0
  366. package/phanpy/dist/logo-monochrome-maskable-512.png +0 -0
  367. package/phanpy/dist/manifest.webmanifest +0 -1
  368. package/phanpy/dist/og-image-2.jpg +0 -0
  369. package/phanpy/dist/og-image.png +0 -0
  370. package/phanpy/dist/robots.txt +0 -2
  371. package/phanpy/dist/sw.js +0 -2
  372. package/phanpy/dist/sw.js.map +0 -1
  373. package/phanpy/dist/version.json +0 -1
  374. package/run-agent.mjs +0 -899
  375. package/vendor/gate.cjs +0 -138
  376. package/vendor/idp-grant.cjs +0 -394
  377. package/vendor/jfetch.cjs +0 -16
  378. package/web/admin/actors.js +0 -147
  379. package/web/admin/bar.css +0 -126
  380. package/web/admin/bar.js +0 -99
  381. package/web/admin/client/client.js +0 -66
  382. package/web/admin/client/index.html +0 -69
  383. package/web/admin/client-pick.js +0 -79
  384. package/web/admin/common.js +0 -23
  385. package/web/admin/connections.js +0 -112
  386. package/web/admin/gateway.js +0 -133
  387. package/web/admin/group.js +0 -258
  388. package/web/admin/index.html +0 -612
  389. package/web/admin/notices-bar.js +0 -90
  390. package/web/admin/oauth-signin.mjs +0 -37
  391. package/web/admin/record.js +0 -393
  392. package/web/admin/setup/index.html +0 -257
  393. package/web/admin/setup/setup.js +0 -339
  394. package/web/admin/tokens.css +0 -68
  395. package/web/admin/upkeep.js +0 -177
  396. package/web/admin/window.css +0 -55
  397. package/web/admin/window.js +0 -136
  398. package/web/app/README.md +0 -81
  399. package/web/app/admin-facade.mjs +0 -588
  400. package/web/app/agent.mjs +0 -672
  401. package/web/app/atproto-browser.mjs +0 -86
  402. package/web/app/authorize_interaction.html +0 -56
  403. package/web/app/authorize_interaction.js +0 -69
  404. package/web/app/boot.mjs +0 -616
  405. package/web/app/deliver-relay.mjs +0 -129
  406. package/web/app/dist/boot.js +0 -34562
  407. package/web/app/dist/boot.js.map +0 -7
  408. package/web/app/dist/sw.js +0 -73456
  409. package/web/app/dist/sw.js.map +0 -7
  410. package/web/app/fediacct-browser.mjs +0 -146
  411. package/web/app/gateway-move.mjs +0 -60
  412. package/web/app/idb-kv.mjs +0 -35
  413. package/web/app/index.html +0 -258
  414. package/web/app/keys-browser.mjs +0 -71
  415. package/web/app/keystore.mjs +0 -82
  416. package/web/app/oidc-session.mjs +0 -6
  417. package/web/app/pod-auth.mjs +0 -248
  418. package/web/app/pod-remote.mjs +0 -84
  419. package/web/app/shims/_globals.mjs +0 -2
  420. package/web/app/shims/fedify-sig.mjs +0 -77
  421. package/web/app/shims/node-crypto.mjs +0 -113
  422. package/web/app/shims/node-fs.mjs +0 -17
  423. package/web/app/shims/node-path.mjs +0 -19
  424. package/web/app/shims/node-url.mjs +0 -7
  425. package/web/app/shims/prelude.js +0 -58
  426. package/web/app/shims/safefetch.mjs +0 -58
  427. package/web/app/shims/shapes-text.mjs +0 -8
  428. package/web/app/shims/web-push.mjs +0 -14
  429. package/web/app/signup.mjs +0 -357
  430. package/web/app/sw-src.mjs +0 -220
  431. package/web/app/update.js +0 -70
  432. package/web/front/admin.html +0 -55
  433. package/web/front/admin.js +0 -89
  434. package/web/front/install.sh +0 -69
  435. package/web/front/new-account.html +0 -35
  436. package/web/front/new-account.js +0 -13
  437. package/web/front/notices.html +0 -69
  438. package/web/front/notices.js +0 -107
  439. package/web/front/run.html +0 -64
  440. package/web/front/run.js +0 -221
  441. package/web/front/solid-oidc-client.js +0 -6
  442. package/web/icons/forum.svg +0 -11
  443. package/web/icons/gateway.svg +0 -14
  444. 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,387 +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
- // A fresh activity of this actor's, for the outbox its owner reads. Never
191
- // allowed to stop a delivery; a retry from the queue is not fresh.
192
- _sent(activity) {
193
- try { this.onSent?.(activity); } catch (e) { this.log(`owner's outbox record: ${e.message}`); }
194
- }
195
-
196
- // An inbox on a blocked domain is never delivered to, whoever still lists it.
197
- _blockedInbox(inbox) {
198
- try { return !!this.store.getBlocklist?.().domains.length && this.store.isBlocked?.(new URL(inbox).origin + '/'); }
199
- catch { return false; }
200
- }
201
-
202
- async deliver(inbox, activity) {
203
- this._sent(activity);
204
- if (this._blockedInbox(inbox)) return;
205
- // Proved before anything else, so the copy that goes on the queue is the
206
- // copy that was signed — a retry days later must not post a bare activity.
207
- const signed = await this.proofed(activity);
208
- if (this._queueIfCooling(inbox, signed)) return;
209
- const [result] = await this.deliverManyNow([{ inbox, activity: signed }]);
210
- await this._settle(inbox, signed, result);
211
- }
212
-
213
- // A host we already know is refusing: queue without asking again. This is
214
- // the path a FRESH activity takes, so without it a fan-out to a struggling
215
- // server opened one socket per follower before any of this applied.
216
- _queueIfCooling(inbox, signed) {
217
- const host = hostOf(inbox);
218
- const until = this._cooling?.get(host);
219
- if (!until || until <= Date.now()) return false;
220
- this.log(`${host} is cooling — queueing ${signed.type} rather than asking again`);
221
- this._enqueue({ inbox, activity: signed, attempts: 1, nextAt: until });
222
- return true;
223
- }
224
-
225
- // What one attempt's outcome means for the queue.
226
- async _settle(inbox, signed, result) {
227
- if (result.ok) { this.log(`delivered ${signed.type} → ${inbox}`); return; }
228
- const e = result.error;
229
- if (unsalvageable(e)) { await this._unsalvageable(inbox, e); return; }
230
- this.log(`delivery failed (${e.message}) — queued`);
231
- const wait = e.retryAfterMs || 60_000;
232
- if (aboutTheHost(e)) {
233
- this._cooling ||= new Map();
234
- this._cooling.set(hostOf(inbox), Date.now() + wait);
235
- }
236
- this._enqueue({ inbox, activity: signed, attempts: 1, nextAt: Date.now() + wait });
237
- }
238
-
239
- // Not retried, and on a 410 the followers who received there are dropped,
240
- // so the next fan-out stops asking; the followers collection is republished
241
- // through onGone. A 404 drops only this delivery.
242
- async _unsalvageable(inbox, e) {
243
- if (e.status !== 410) { this.log(`delivery dropped (${e.message}) — nothing answers there`); return; }
244
- const contacts = this.store.getContacts?.();
245
- const left = (contacts?.followers || []).filter(f => f.inbox === inbox || f.sharedInbox === inbox);
246
- this.log(`${inbox} is gone (410) — delivery dropped${left.length ? `, ${left.length} follower(s) there forgotten` : ''}`);
247
- if (!left.length) return;
248
- for (const f of left) dropFollower(contacts, f.actor, 'inbox-gone');
249
- this.store.setContacts(contacts);
250
- try { await this.onGone?.(); } catch (err) { this.log(`followers republish after 410: ${err.message}`); }
251
- }
252
-
253
- // The queue has a ceiling: a fan-out into a sea of down servers must not grow
254
- // queue.json without bound. Overflow keeps the evidence as a dead letter.
255
- _enqueue(item) {
256
- const q = this.store.getQueue();
257
- if (q.length >= MAX_QUEUE) {
258
- this.log(`delivery queue is full (${MAX_QUEUE}) — dead-lettering ${item.activity?.type} → ${item.inbox}`);
259
- this.store.addDeadLetter({ inboxUrl: item.inbox, reason: 'delivery queue full', activity: item.activity });
260
- return;
261
- }
262
- q.push(item);
263
- this.store.setQueue(q);
264
- }
265
-
266
- async deliverToAll(inboxes, activity) {
267
- this._sent(activity);
268
- // Proved once for the whole fan-out: every recipient gets the same bytes,
269
- // and one signature is computed rather than one per follower.
270
- const signed = await this.proofed(activity);
271
- // Shared inboxes deduplicate fan-out to the same server. Sent in batches
272
- // of batchSize, and a host found cooling by one batch is not asked again
273
- // by the next.
274
- const targets = [...new Set(inboxes)].filter((inbox) => !this._blockedInbox(inbox))
275
- .map((inbox) => ({ inbox, activity: signed }));
276
- for (let i = 0; i < targets.length; i += this.batchSize) {
277
- const chunk = targets.slice(i, i + this.batchSize).filter((t) => !this._queueIfCooling(t.inbox, signed));
278
- if (!chunk.length) continue;
279
- const results = await this.deliverManyNow(chunk);
280
- for (let k = 0; k < chunk.length; k++) await this._settle(chunk[k].inbox, signed, results[k]);
281
- }
282
- }
283
-
284
- // Serialized, for the same reason Intake.drain is: the tick is 60s and a
285
- // drain over slow peers outlasts it, so a second run started on top of the
286
- // first. Both read their own clone of queue.json, so both re-POSTed every due
287
- // activity — duplicate deliveries at a server that is already failing — and
288
- // both then wrote `keep` back from a stale snapshot, losing the attempt
289
- // counts that are supposed to make the backoff converge.
290
- async drainQueue() {
291
- if (this._draining) { this._drainAgain = true; return this._draining; }
292
- this._draining = this._drainQueueOnce().finally(() => {
293
- this._draining = null;
294
- if (this._drainAgain) { this._drainAgain = false; this.drainQueue().catch(e => this.log(`queue: ${e.message}`)); }
295
- });
296
- return this._draining;
297
- }
298
-
299
- // The queue is walked per HOST, not per item.
300
- //
301
- // It used to be a flat sequential loop, which had two costs against a server
302
- // that was down. Every due item for that host was POSTed — the first failure
303
- // told us nothing the code could act on — and because each request carries a
304
- // 15s deadline, K items for one dead host held the drain for up to 15K
305
- // seconds while every healthy peer's activity waited behind them. Compounding
306
- // requests at a struggling server, and head-of-line blocking of everyone
307
- // else: the shape of the incident this project has a postmortem for.
308
- //
309
- // One failure now cools the whole host for the rest of this drain, and a
310
- // Retry-After it sent decides for how long. The breaker is in memory; what
311
- // persists is nextAt, which the queue already carried.
312
- async _drainQueueOnce() {
313
- const q = this.store.getQueue();
314
- if (!q.length) return;
315
- const now = Date.now();
316
- const keep = [];
317
- // Whether anything actually moved. The only early return above is on an
318
- // EMPTY queue, so one item with a far-future nextAt used to rewrite
319
- // queue.json on all 1440 ticks of a day with byte-identical content —
320
- // PodStore.write has no equality check, and the ordinary ladder reaches
321
- // hours-long waits by attempt 6, so any peer that fails a few times got
322
- // there. A length compare is not enough: getQueue() hands back a clone, so
323
- // attempts/nextAt are mutated in place and the count stays the same.
324
- let changed = false;
325
- // Outlives the drain: a Retry-After only reached items that happened to be
326
- // due in the same pass, so a sibling due a minute later ignored it entirely.
327
- this._cooling ||= new Map(); // host → until
328
- for (const [h, until] of this._cooling) if (until <= now) this._cooling.delete(h);
329
-
330
- const ladderFor = (item) => {
331
- const base = Math.min(2 ** item.attempts, 2 ** MAX_ATTEMPTS) * 60_000;
332
- return Math.round(base * (0.85 + Math.random() * 0.3)); // jittered, not lockstep
333
- };
334
-
335
- for (const item of q) {
336
- // Due first, THEN cooling: an item that is not yet due must not have its
337
- // ladder advanced on every 60s tick of a 15-minute cooldown. Once
338
- // deferred its nextAt IS the cooldown, so it advances exactly once per
339
- // cooldown — the same rate as the item that opened the socket.
340
- if (item.nextAt > now) { keep.push(item); continue; }
341
- const host = hostOf(item.inbox);
342
- const until = this._cooling.get(host);
343
- if (until && until > now) {
344
- // A sibling already failed against this host. Defer without opening a
345
- // socket — but ADVANCE THE LADDER, or only the one item that actually
346
- // made a request ever climbs it and a queue of K items takes K times
347
- // as long to give up as the module promises.
348
- item.attempts += 1;
349
- changed = true;
350
- if (item.attempts > MAX_ATTEMPTS) {
351
- this.log(`giving up on ${item.inbox} after ${MAX_ATTEMPTS} attempts`);
352
- continue;
353
- }
354
- item.nextAt = Math.max(item.nextAt, until);
355
- keep.push(item);
356
- continue;
357
- }
358
- try {
359
- await this.deliverNow(item.inbox, item.activity);
360
- changed = true;
361
- this.log(`retry ok: ${item.activity.type} → ${item.inbox}`);
362
- } catch (e) {
363
- if (unsalvageable(e)) { changed = true; await this._unsalvageable(item.inbox, e); continue; }
364
- item.attempts += 1;
365
- changed = true;
366
- const wait = e.retryAfterMs || ladderFor(item);
367
- // Cool the host BEFORE the give-up test: a failure is evidence about
368
- // the server whether or not the item that produced it survives, and
369
- // skipping this left the flat behaviour intact for exactly the backlog
370
- // that has just aged out.
371
- if (aboutTheHost(e)) {
372
- this._cooling.set(host, now + wait);
373
- if (e.retryAfterMs) this.log(`${host} asked for ${Math.round(wait / 1000)}s — honouring it`);
374
- }
375
- if (item.attempts > MAX_ATTEMPTS) {
376
- this.log(`giving up on ${item.inbox} after ${MAX_ATTEMPTS} attempts`);
377
- continue;
378
- }
379
- item.nextAt = now + wait;
380
- keep.push(item);
381
- }
382
- }
383
- if (changed) this.store.setQueue(keep);
384
- }
385
-
386
- stop() { clearInterval(this.timer); this.timer = null; }
387
- }