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,500 +0,0 @@
1
- // publisher.mjs — builds/maintains the actor's public face on the remote pod:
2
- // webfinger, actor doc, collections, notes. The /ap/ tree is disposable:
3
- // publishProfile() rebuilds it.
4
- //
5
- // This file is the actor's public face — the actor document, discovery,
6
- // ACLs, the inbox posture, Move and retire. The rest is in the modules
7
- // beside it: collections.mjs (the published collections and the outbox
8
- // record), restore.mjs (catching up with the pod), notes.mjs (a note going
9
- // up), questions.mjs (polls) — each a set of functions taking the Publisher
10
- // as their first argument, reached here through one-line delegations.
11
-
12
- import fs from 'node:fs';
13
- import path from 'node:path';
14
- import { fileURLToPath } from 'node:url';
15
- import crypto from 'node:crypto';
16
- import * as wire from '../wire.mjs';
17
- import { USER_AGENT } from '../../shared/ua.mjs';
18
- import { HTTP_TIMEOUT_MS } from '../../shared/safefetch.mjs';
19
- import * as containers from '../../pod/containers.mjs';
20
- import * as discovery from '../../pod/discovery.mjs';
21
- import * as podActor from '../../pod/actor.mjs';
22
- import * as podInbox from '../../pod/inbox.mjs';
23
- import * as podFeatured from '../../pod/featured.mjs';
24
- import * as podPolicy from '../../pod/policy.mjs';
25
- import * as podNotes from '../../pod/notes.mjs';
26
- import * as collections from './collections.mjs';
27
- import { ALL_COLLECTIONS } from './collections.mjs';
28
- import * as restore from './restore.mjs';
29
- import * as notes from './notes.mjs';
30
- import * as questions from './questions.mjs';
31
-
32
- const AGENT_VERSION = JSON.parse(fs.readFileSync(
33
- path.join(path.dirname(fileURLToPath(import.meta.url)), '../../../package.json'), 'utf8')).version;
34
-
35
- export class Publisher {
36
- constructor({ config, remote, store, deliverer, publicKeyPem, assertionKey = null, log = console.log,
37
- probeFetch = null, resolveMention = null, resolveActor = null, clientOrigin = null,
38
- }) {
39
- this.config = config;
40
- this.remote = remote;
41
- this.store = store;
42
- this.deliverer = deliverer;
43
- this.publicKeyPem = publicKeyPem;
44
- this.assertionKey = assertionKey; // Ed25519 public half, multibase; null = no proofs
45
- // Where this identity's client surface answers, when that is an address a
46
- // stranger can reach. Null on a laptop, where the surface is on loopback
47
- // and advertising it to the world would name somewhere nobody can go.
48
- this.clientOrigin = clientOrigin;
49
- // A fronted identity (config.gateway.frontActor) advertises its ids on a
50
- // shared domain; the map tells RemotePod where each writes on the pod.
51
- const publicBase = config.gateway?.frontActor
52
- ? config.gateway.frontActor.replace(/ap\/actor\/?$/, '') : null;
53
- this.urls = wire.apUrls(config.remotePod, config.root, { publicBase });
54
- if (this.urls.toPod && this.remote?.setUrlMap) this.remote.setUrlMap(this.urls.toPod);
55
- // Credential-free by design — it asks what a stranger sees — but routed
56
- // through the pod's own cooldown and accounting, because it is still a
57
- // socket opened to that pod. Tests inject their own.
58
- this.probeFetch = probeFetch || ((u, i) => this.remote.probe(u, i));
59
- this.resolveMention = resolveMention;
60
- // An actor or object by its address, verified at its origin — how a reply
61
- // finds the inbox of the person it answers.
62
- this.resolveActor = resolveActor;
63
- // Per-poll rewrite windows, keyed by question id. See POLL_REWRITE_MS.
64
- this.pollTimers = new Map();
65
- this.log = log;
66
- }
67
-
68
- // Idempotent: (re)write webfinger + actor + collections + container ACLs.
69
- //
70
- // ~34 pod requests, so it does not run when it would rewrite the same bytes.
71
- // Every document below is derived from the actor doc, the handle, the host,
72
- // whether the actor is quiesced (which decides the inbox ACL) and the agent
73
- // version (which rides in nodeinfo) — so if none of those moved, there is
74
- // nothing to say. Phanpy's editor submits the whole form on every save, so
75
- // "saved without changing anything" is the common case, not a rare one.
76
- //
77
- // `force` is for the callers that publish precisely BECAUSE the pod does not
78
- // have what the digest says it has: the repair path, and the explicit
79
- // republish button. Without it, an actor lost from the pod would match the
80
- // digest, be skipped, and leave the agent reporting success while nobody can
81
- // resolve it.
82
- // The Gateway's outbox door for this account, when a Gateway is attached:
83
- // beside its inbox door, or under the fronted actor.
84
- gatewayOutbox() {
85
- const gw = this.config.gateway;
86
- if (!(gw && gw.url && gw.mode && gw.mode !== 'off')) return null;
87
- if (gw.frontActor) return String(gw.frontActor).replace(/ap\/actor\/?$/u, 'ap/outbox');
88
- return String(gw.url).replace(/ap\/inbox\/?$/u, 'ap/outbox');
89
- }
90
-
91
- // Every actor document this identity publishes, from one place: the
92
- // profile publish and a Move both write it, and a Move that rebuilt it
93
- // by hand dropped the gateway inbox, the outbox door and the collections.
94
- // `clientOrigin` is overridden only by the agent serving a client its own
95
- // local actor: standalone, the published document cannot name a loopback
96
- // address, but a client already talking to that address can be told it.
97
- actorDocFor({ priv = false, moderators = null, inbox = null, movedTo = this.config.movedTo || null,
98
- clientOrigin = this.clientOrigin } = {}) {
99
- const { urls } = this;
100
- return wire.actorDoc({
101
- urls, handle: wire.publicHandle(this.config), name: this.config.name, publicKeyPem: this.publicKeyPem,
102
- movedTo, kind: this.config.kind,
103
- approveJoins: wire.followsNeedApproval(this.config),
104
- assertionKey: this.assertionKey,
105
- summary: this.config.summary || null, icon: this.config.icon || null,
106
- image: this.config.image || null, fields: this.config.fields || [],
107
- webId: this.remote.webId || null,
108
- aliases: this.config.aliases || [],
109
- // A group says whether only its moderators may open posts (Lemmy's
110
- // term); a person's actor carries nothing of the kind.
111
- postingRestrictedToMods: this.config.kind === 'group' ? !!this.config.postingRestrictedToMods : null,
112
- moderators,
113
- pendingFollowers: priv ? urls.pendingFollowers : null,
114
- pendingFollowing: priv ? urls.pendingFollowing : null,
115
- blocked: priv ? urls.blocked : null,
116
- inbox,
117
- // The agent's own outbox endpoint, where it is reachable: a client
118
- // following the actor must arrive somewhere that will take a write.
119
- // Otherwise the Gateway's outbox door, when one is attached.
120
- outbox: clientOrigin ? `${clientOrigin}ap/outbox` : this.gatewayOutbox(),
121
- // How a client-to-server client finds the way in with nothing configured
122
- // by hand. Advertised only where the surface is publicly reachable.
123
- oauthAuthorize: clientOrigin ? `${clientOrigin}oauth/authorize` : null,
124
- oauthToken: clientOrigin ? `${clientOrigin}oauth/token` : null,
125
- });
126
- }
127
-
128
- // The human half: a page a browser can open and follow from. The actor
129
- // document is for servers; this is the address you hand to a person. It
130
- // shows who the account is, its fields, when it joined and what it pinned;
131
- // the counts and the posts live in the collections. Written only when its
132
- // content changed.
133
- async publishProfilePage({ force = false } = {}) {
134
- const { urls } = this;
135
- const host = new URL(urls.base).host;
136
- // When the account was made. Accounts from before this was recorded take
137
- // the date of their oldest post, or today.
138
- if (!this.config.createdAt) {
139
- const oldest = this.store.getStatuses().filter(s => s.kind === 'post' && s.published)
140
- .map(s => s.published).sort()[0];
141
- this.config.createdAt = oldest || new Date().toISOString();
142
- this.store.setConfig?.({ ...this.store.getConfig?.(), createdAt: this.config.createdAt });
143
- }
144
- const pinned = this.store.getStatuses()
145
- .filter(s => s.kind === 'post' && s.pinned && s.visibility !== 'private' && s.visibility !== 'direct')
146
- .sort((a, b) => String(b.published).localeCompare(String(a.published)))
147
- .slice(0, 5)
148
- .map(s => ({ content: s.content, published: s.published, url: s.noteId }));
149
- // The address as the world knows it: at the Gateway for a fronted
150
- // identity, on the pod's host otherwise, the actor id where no host can
151
- // answer for a handle.
152
- const frontActor = this.config.gateway?.frontActor;
153
- const address = frontActor ? `@${wire.publicHandle(this.config)}@${new URL(frontActor).host}`
154
- : (wire.webfingerHost(urls.base) ? `@${this.config.handle}@${host}` : urls.actor);
155
- const html = wire.profilePageHtml({
156
- name: this.config.name || this.config.handle,
157
- address,
158
- summary: this.config.summary ? wire.contentHtml(this.config.summary) : null,
159
- icon: this.config.icon || null,
160
- image: this.config.image || null,
161
- fields: this.config.fields || [],
162
- joined: this.config.createdAt,
163
- pinned,
164
- kind: this.config.kind,
165
- });
166
- const digest = crypto.createHash('sha256').update(html).digest('hex').slice(0, 32);
167
- const seen = this.store.read('published.json', {});
168
- if (!force && seen.pageDigest === digest) return false;
169
- await podActor.writeProfilePage(this.remote, urls, html);
170
- this.store.write('published.json', { ...this.store.read('published.json', {}), pageDigest: digest });
171
- return true;
172
- }
173
-
174
- async publishProfile({ force = false } = {}) {
175
- const { urls } = this;
176
- const host = new URL(urls.base).host;
177
-
178
- // The owner-only collections (FEP-4ccd, FEP-c648) are advertised only
179
- // where "owner-only" is real — the pod must provably enforce the private
180
- // container's ACL, the same bar private posts clear.
181
- const priv = await this.privateReady() === true;
182
- const moderators = (this.config.moderators || []).length ? urls.moderators : null;
183
- // An advertised inbox gateway (config.gateway.mode past 'off') becomes the
184
- // actor's inbox; deliveries reach the pod inbox through it. Absent → today.
185
- const gw = this.config.gateway;
186
- const gwActive = gw && gw.url && gw.mode && gw.mode !== 'off';
187
- // A forum's category advertises the forum's one inbox (config.inboxUrl):
188
- // deliveries to any category land there, and the forum routes them.
189
- const actorDoc = this.actorDocFor({ priv, moderators, inbox: gwActive ? gw.url : (this.config.inboxUrl || null) });
190
- const surface = crypto.createHash('sha256').update(JSON.stringify({
191
- actor: actorDoc, handle: this.config.handle, host,
192
- quiesced: !!this.config.quiescedAt, version: AGENT_VERSION,
193
- moderators: this.config.moderators || [],
194
- })).digest('hex').slice(0, 32);
195
- // The human page has its own gate: it changes with pins and the joined
196
- // date as well as with the profile, and costs one write when it does.
197
- await this.publishProfilePage({ force });
198
- if (!force && this.store.read('published.json', {}).surfaceDigest === surface) {
199
- this.log('profile unchanged — nothing republished');
200
- return { unreachable: [], updated: 0, skipped: true };
201
- }
202
-
203
- // Discovery is the pod's, and a pod has one WebFinger document: a forum's
204
- // category (config.forum names the forum) is not the pod's identity, so it
205
- // writes none — the forum's own actor, or a Gateway, answers for it.
206
- if (!this.config.forum) {
207
- await discovery.writeWebfinger(this.remote, urls,
208
- wire.jrd({ handle: this.config.handle, host, actor: urls.actor, page: urls.profileHtml }));
209
- await discovery.writeHostMeta(this.remote, urls, wire.hostMeta(urls.base));
210
-
211
- const nodeinfoDocUrl = urls.home + 'ap/nodeinfo-2.0';
212
- const localPosts = this.store.getStatuses().filter(s => s.kind === 'post').length;
213
- await discovery.writeNodeinfo(this.remote, urls, {
214
- pointer: wire.nodeinfoPointer(nodeinfoDocUrl),
215
- doc: wire.nodeinfoDoc({ version: AGENT_VERSION, localPosts }),
216
- });
217
- }
218
-
219
- const actor = actorDoc; // built above, for the digest
220
- await podActor.write(this.remote, urls, actor);
221
-
222
- // FEP-1b12: the moderator roster, public — it is what a recipient
223
- // validates a group's announced moderation against.
224
- if (moderators) {
225
- await podFeatured.writeModerators(this.remote, urls,
226
- wire.orderedCollection(urls.moderators, this.config.moderators));
227
- }
228
- // The gateway policy doc: the PUBLIC data a keyless gateway reads to decide
229
- // what concerns this identity. Written only while a gateway is advertised.
230
- if (gwActive) await this.publishGatewayPolicy();
231
-
232
- // WebID → actor: the profile card lists the actor as a foaf:account.
233
- // Best-effort — a profile that cannot be read or edited does not stop the
234
- // publish, it is logged and the rest of the surface still goes up.
235
- if (this.config.forum) { /* a category is not the pod owner's account */ } else try {
236
- const wrote = await podActor.linkInWebIdProfile(this.remote, {
237
- actorUrl: urls.actor,
238
- accountName: `@${this.config.handle}@${host}`,
239
- kind: this.config.kind,
240
- // Where a Solid client posts: dokieli reads `as:outbox` off the WebID.
241
- outbox: this.clientOrigin ? `${this.clientOrigin}ap/outbox` : this.gatewayOutbox(),
242
- });
243
- if (wrote) this.log('WebID profile now lists the actor as a foaf:account');
244
- } catch (e) {
245
- this.log(`WebID profile not updated with the actor link: ${e.message}`);
246
- }
247
-
248
- // inbox: public may only Append; owner (the agent) reads + drains. A
249
- // quiesced actor keeps its name resolving but takes no more mail, so a
250
- // republish must not re-open the door.
251
- await podInbox.writeKeep(this.remote, urls);
252
- await podInbox.setPosture(this.remote, urls, this.config.quiescedAt ? 'closed' : 'open');
253
- if (this.config.quiescedAt) this.log('inbox left closed — this actor is quiesced');
254
-
255
- // notes live under a public-Read container (acl:default covers new notes).
256
- await podNotes.provisionContainer(this.remote, urls);
257
-
258
- await this.publishCollections({ ...ALL_COLLECTIONS, force });
259
- const updated = await this.announceProfileChange(actor, { force });
260
- await this.ensurePrivateAcls();
261
- const unreachable = await this.verifyPublicSurface();
262
- // Last, and merged: announceProfileChange writes this document too.
263
- //
264
- // Only when the surface came back readable. Recording it regardless meant a
265
- // publish that half-landed still matched the digest, so the NEXT save — the
266
- // one the operator makes because the first did not work — was skipped as a
267
- // no-op and reported success. The digest is a record of what is up there,
268
- // and an unreachable document is not up there.
269
- if (!unreachable.length) {
270
- this.store.write('published.json',
271
- { ...this.store.read('published.json', {}), surfaceDigest: surface });
272
- }
273
- // Named as the fediverse sees it: a fronted actor's name and host are the
274
- // front's, not the pod's.
275
- const pubName = wire.publicHandle(this.config);
276
- const pubHost = this.config.gateway?.frontActor ? new URL(this.config.gateway.frontActor).host : host;
277
- this.log(this.config.gateway?.frontActor || wire.webfingerHost(urls.base)
278
- ? `profile published: @${pubName}@${pubHost} → ${urls.actor}`
279
- : `profile published → ${urls.actor} — NOT discoverable as @${pubName}@${pubHost}: `
280
- + 'this pod is a suffix-based host, and WebFinger is only answered at a host root');
281
- return { unreachable, updated };
282
- }
283
-
284
- // Fires when the document differs from the one last published — including
285
- // the first time, when there is nothing to differ from.
286
- //
287
- // Every call to publishProfile is already a DELIBERATE republish: setup, a
288
- // rename, an edit through the client or /config, `describe`, a key rotation,
289
- // or an actor document found missing from the pod. Starting the agent does not
290
- // call it. So the digest is not there to survive restarts — it is there for
291
- // the republish that changes nothing, which /config does whenever you save a
292
- // field the actor document does not carry.
293
- //
294
- // A silent first publish was considered and is WRONG here: it would spend a
295
- // real edit doing nothing but recording a digest, and that edit is exactly the
296
- // one whose invisibility this fixes.
297
- async announceProfileChange(actor, { force = false } = {}) {
298
- const digest = crypto.createHash('sha256').update(JSON.stringify(actor)).digest('hex').slice(0, 32);
299
- const seen = this.store.read('published.json', {});
300
- // A forced republish is the operator saying TELL THE WORLD — the digest
301
- // gate is for silent no-op saves, not for that.
302
- if (seen.actorDigest === digest && !force) return 0;
303
- this.store.write('published.json', { ...seen, actorDigest: digest, at: new Date().toISOString() });
304
- const inboxes = [...new Set(this.store.getContacts().followers
305
- .map(f => f.sharedInbox || f.inbox).filter(Boolean))];
306
- if (!inboxes.length) return 0;
307
- await this.deliverer.deliverToAll(inboxes,
308
- wire.updateActorActivity({ urls: this.urls, actor, serial: Date.now() }));
309
- this.log(`profile changed — Update delivered to ${inboxes.length} inbox(es)`);
310
- return inboxes.length;
311
- }
312
-
313
- // The mirror of ensurePrivateAcls, and the check this project lacked: these
314
- // documents MUST be readable by strangers or no server can see the actor.
315
- // A publish that dies half-way, or an ACL write that is accepted without
316
- // taking effect, is otherwise indistinguishable from success — the agent
317
- // reports itself configured and federating while nobody can find it.
318
- async verifyPublicSurface() {
319
- const { urls } = this;
320
- const targets = [
321
- // Discovery documents are the pod's, not a forum category's (see publishProfile).
322
- ...(this.config.forum ? [] : [['webfinger', urls.webfinger], ['host-meta', urls.base + '.well-known/host-meta']]),
323
- ['actor', urls.actor],
324
- ['notes', urls.notes],
325
- ['followers', urls.followers],
326
- ['following', urls.following],
327
- ['featured', urls.featured],
328
- ['outbox', urls.outbox],
329
- ];
330
- const unreachable = [];
331
- for (const [name, url] of targets) {
332
- if (!await this.publiclyReadable(url)) unreachable.push(name);
333
- }
334
- if (unreachable.length) {
335
- this.log(`FEDERATION: ${unreachable.join(', ')} not readable without credentials — `
336
- + 'other servers cannot resolve or fetch this actor');
337
- }
338
- return unreachable;
339
- }
340
-
341
- // An ACL write that silently failed — or was changed afterwards by anything
342
- // else touching the pod — leaves the private trees, signing keys included,
343
- // world-readable. bootstrap writes them once at setup and never returns, so
344
- // this runs on every connect: probe UNauthenticated, rewrite whatever
345
- // answers, and say so loudly if the rewrite does not take.
346
- async ensurePrivateAcls() {
347
- const findings = await containers.repairPrivateAcls(this.remote,
348
- [this.urls.home, this.urls.state],
349
- { isPublic: (u) => this.publiclyReadable(u) });
350
- // What a finding MEANS is ours to say; the library just reports.
351
- for (const f of findings) {
352
- this.log(`${f.url} was readable without credentials — rewriting its ACL`);
353
- if (f.error) { this.log(`SECURITY: ${f.url} is public and its ACL could not be rewritten: ${f.error}`); continue; }
354
- if (f.stillPublic) this.log(`SECURITY: ${f.url} is STILL readable without credentials — check the pod's ACLs`);
355
- }
356
- }
357
-
358
- // Deliberately credential-free: this asks what a stranger would see.
359
- // accept: */* matters — asking for turtle makes the server answer 501 on the
360
- // JSON documents (webfinger, actor), which reads as "unreachable" when the
361
- // world can in fact see them perfectly well.
362
- publiclyReadable(url) {
363
- return containers.probePublicReadability(this.probeFetch, url,
364
- { headers: { 'user-agent': USER_AGENT }, timeoutMs: HTTP_TIMEOUT_MS });
365
- }
366
-
367
- // Retire this identity for good: tell everyone who follows us to drop the
368
- // account, then leave a Tombstone where the actor was. The inbox stays
369
- // publicly Append-able on purpose — closing it would make deliveries 401,
370
- // which Mastodon treats as failure and retries, the opposite of the point.
371
- // A Delete stops well-behaved servers; anything that keeps delivering gets a
372
- // cheap 201 into a container we no longer read.
373
- async retireActor() {
374
- const { urls } = this;
375
- const contacts = this.store.getContacts();
376
- const inboxes = [...new Set(contacts.followers.map(f => f.sharedInbox || f.inbox).filter(Boolean))];
377
- const deletedAt = new Date().toISOString();
378
- await this.deliverer.deliverToAll(inboxes, wire.deleteActorActivity(urls, Date.parse(deletedAt)));
379
- await podActor.writeTombstone(this.remote, urls, wire.tombstoneDoc(urls, deletedAt, this.config.kind));
380
- this.store.setConfig({ ...this.store.getConfig(), retiredAt: deletedAt });
381
- await this.store.flush();
382
- this.log(`retired: Delete sent to ${inboxes.length} inbox(es), actor replaced with a Tombstone`);
383
- return { inboxes: inboxes.length, deletedAt };
384
- }
385
-
386
- // Stop accepting mail without giving up the name: deliveries get an immediate
387
- // 401 rather than a 201 into storage nobody will ever drain. WebFinger,
388
- // host-meta and the actor stay published, so the handle still resolves.
389
- async closeInbox() {
390
- await podInbox.setPosture(this.remote, this.urls, 'closed');
391
- const at = new Date().toISOString();
392
- this.store.setConfig({ ...this.store.getConfig(), quiescedAt: at });
393
- await this.store.flush();
394
- this.log(`inbox closed — @${this.config.handle} still resolves but accepts nothing`);
395
- return at;
396
- }
397
-
398
- // Undo closeInbox: mail flows again and the actor is no longer quiesced.
399
- async openInbox() {
400
- await podInbox.setPosture(this.remote, this.urls, 'open');
401
- const { quiescedAt, ...rest } = this.store.getConfig() || {};
402
- this.store.setConfig(rest);
403
- this.config.quiescedAt = undefined;
404
- await this.store.flush();
405
- this.log(`inbox re-opened — @${this.config.handle} is taking mail again`);
406
- }
407
-
408
- // Lock the inbox to a gateway: the public loses Append, so nothing reaches
409
- // the pod except through the gateway's verify-at-the-door. Reversible with
410
- // openInbox (public-Append) — the one-call rollback.
411
- async lockInboxToGateway(gatewayWebId) {
412
- await podInbox.setPosture(this.remote, this.urls, { gatewayWebId });
413
- this.log(`inbox locked to gateway ${gatewayWebId} — public delivery is refused`);
414
- }
415
-
416
- // Tell the fediverse the account lives somewhere else now. Well-behaved
417
- // servers migrate their followers to the target and stop delivering here.
418
- async publishMove(target) {
419
- const { urls } = this;
420
- const contacts = this.store.getContacts();
421
- const inboxes = [...new Set(contacts.followers.map(f => f.sharedInbox || f.inbox).filter(Boolean))];
422
- const at = new Date().toISOString();
423
- await this.deliverer.deliverToAll(inboxes, wire.moveActivity(urls, target, Date.parse(at)));
424
- this.config.movedTo = target; // so the republish below carries it
425
- this.store.setConfig({ ...this.store.getConfig(), movedTo: target, movedAt: at });
426
- const gw = this.config.gateway;
427
- const gwActive = gw && gw.url && gw.mode && gw.mode !== 'off';
428
- const priv = await this.privateReady() === true;
429
- const moderators = (this.config.moderators || []).length ? urls.moderators : null;
430
- await podActor.writeMoved(this.remote, urls, this.actorDocFor({
431
- priv, moderators, inbox: gwActive ? gw.url : null, movedTo: target,
432
- }));
433
- await this.store.flush();
434
- this.log(`moved to ${target}: Move sent to ${inboxes.length} inbox(es), actor now advertises movedTo`);
435
- return { inboxes: inboxes.length, target, movedAt: at };
436
- }
437
-
438
- // The gateway policy: a small PUBLIC document a keyless inbox gateway reads
439
- // to decide, at the edge, what to forward and what to drop. It carries only
440
- // public facts — the actor/followers URLs, the REAL pod inbox to forward to,
441
- // the accepted-following list (already public) and a mirror of the blocklist.
442
- // Publishing the blocklist here makes it public; that is a deliberate part of
443
- // running a gateway, surfaced to the operator in the admin UI.
444
- async publishGatewayPolicy() {
445
- const { urls } = this;
446
- const contacts = this.store.getContacts();
447
- const bl = this.store.getBlocklist();
448
- await podPolicy.write(this.remote, urls, {
449
- v: 1,
450
- actorUrl: urls.actor,
451
- followersUrl: urls.followers,
452
- followingUrl: urls.following,
453
- inboxUrl: urls.inbox, // the pod inbox the gateway forwards to
454
- notesPrefix: urls.notes,
455
- kind: this.config.kind || 'person',
456
- following: contacts.following.filter(f => f.accepted && !f.bsky).map(f => f.actor),
457
- blocklist: { domains: bl.domains || [], actors: bl.actors || [] },
458
- });
459
- }
460
-
461
- // collections.mjs
462
- publishOutbox(...a) { return collections.publishOutbox(this, ...a); }
463
- readPublishedOutbox(...a) { return collections.readPublishedOutbox(this, ...a); }
464
- publishFollowers(...a) { return collections.publishFollowers(this, ...a); }
465
- readPublishedFollowers(...a) { return collections.readPublishedFollowers(this, ...a); }
466
- publishCollections(...a) { return collections.publishCollections(this, ...a); }
467
- publishPending(...a) { return collections.publishPending(this, ...a); }
468
- publishBlocked(...a) { return collections.publishBlocked(this, ...a); }
469
- recordOutbox(...a) { return collections.recordOutbox(this, ...a); }
470
- unrecordOutbox(...a) { return collections.unrecordOutbox(this, ...a); }
471
- publishFeatured(...a) { return collections.publishFeatured(this, ...a); }
472
-
473
- // restore.mjs
474
- reconcileFollowers(...a) { return restore.reconcileFollowers(this, ...a); }
475
- reconcileOutbox(...a) { return restore.reconcileOutbox(this, ...a); }
476
- rebuildStatuses(...a) { return restore.rebuildStatuses(this, ...a); }
477
- healStatuses(...a) { return restore.healStatuses(this, ...a); }
478
-
479
- // notes.mjs
480
- ensureMediaContainer(...a) { return notes.ensureMediaContainer(this, ...a); }
481
- _containerExists(...a) { return notes.containerExists(this, ...a); }
482
- ensurePrivateContainer(...a) { return notes.ensurePrivateContainer(this, ...a); }
483
- privateReady(...a) { return notes.privateReady(this, ...a); }
484
- _mentionsFor(...a) { return notes.mentionsFor(this, ...a); }
485
- publishNote(...a) { return notes.publishNote(this, ...a); }
486
- authorizeQuote(...a) { return notes.authorizeQuote(this, ...a); }
487
- withdrawQuote(...a) { return notes.withdrawQuote(this, ...a); }
488
- grantQuote(...a) { return notes.grantQuote(this, ...a); }
489
- publishObject(...a) { return notes.publishObject(this, ...a); }
490
- updateNote(...a) { return notes.updateNote(this, ...a); }
491
-
492
- // questions.mjs
493
- publishQuestion(...a) { return questions.publishQuestion(this, ...a); }
494
- _pollActivity(...a) { return questions.pollActivity(this, ...a); }
495
- recordVote(...a) { return questions.recordVote(this, ...a); }
496
- _pollDirty(...a) { return questions.pollDirty(this, ...a); }
497
- republishPoll(...a) { return questions.republishPoll(this, ...a); }
498
- closeDuePolls(...a) { return questions.closeDuePolls(this, ...a); }
499
- stopPolls(...a) { return questions.stopPolls(this, ...a); }
500
- }