fedipod-server 0.29.0 → 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 (440) hide show
  1. package/README.md +11 -6
  2. package/dist/door.d.ts +4 -0
  3. package/dist/door.js +31 -0
  4. package/dist/fedipod.d.ts +16 -0
  5. package/dist/fedipod.js +13 -0
  6. package/dist/front.d.ts +3 -0
  7. package/dist/front.js +94 -0
  8. package/dist/handler.d.ts +36 -101
  9. package/dist/handler.js +85 -645
  10. package/dist/handler.jsonld +2 -30
  11. package/dist/identities.d.ts +62 -0
  12. package/dist/identities.js +236 -0
  13. package/dist/mounts.d.ts +43 -0
  14. package/dist/mounts.js +123 -0
  15. package/dist/opt-in.d.ts +46 -0
  16. package/dist/opt-in.js +176 -0
  17. package/package.json +10 -18
  18. package/lib/client/c2s.mjs +0 -636
  19. package/lib/client/localapi.mjs +0 -87
  20. package/lib/client/masto/accounts.mjs +0 -310
  21. package/lib/client/masto/body.mjs +0 -156
  22. package/lib/client/masto/index.mjs +0 -206
  23. package/lib/client/masto/instance.mjs +0 -112
  24. package/lib/client/masto/media.mjs +0 -79
  25. package/lib/client/masto/oauth.mjs +0 -656
  26. package/lib/client/masto/render.mjs +0 -518
  27. package/lib/client/masto/statuses.mjs +0 -372
  28. package/lib/client/masto/timelines.mjs +0 -438
  29. package/lib/client/oidc-auth.mjs +0 -62
  30. package/lib/client/streaming.mjs +0 -154
  31. package/lib/client/webpush.mjs +0 -98
  32. package/lib/connections/acctfeed.mjs +0 -257
  33. package/lib/connections/atproto.mjs +0 -309
  34. package/lib/connections/bskyfeed.mjs +0 -243
  35. package/lib/connections/bskygroup.mjs +0 -162
  36. package/lib/connections/fediacct.mjs +0 -253
  37. package/lib/connections/import.mjs +0 -357
  38. package/lib/connections/tagfeed.mjs +0 -212
  39. package/lib/connections/vault.mjs +0 -114
  40. package/lib/core/as2.mjs +0 -206
  41. package/lib/core/contexts/activitystreams.json +0 -379
  42. package/lib/core/contexts/anno.json +0 -126
  43. package/lib/core/contexts/did-v1.json +0 -57
  44. package/lib/core/contexts/fep-5711.json +0 -36
  45. package/lib/core/contexts/gotosocial.json +0 -86
  46. package/lib/core/contexts/identity-v1.json +0 -152
  47. package/lib/core/contexts/index.mjs +0 -63
  48. package/lib/core/contexts/join-lemmy.json +0 -33
  49. package/lib/core/contexts/joinmastodon.json +0 -28
  50. package/lib/core/contexts/litepub-0.1.json +0 -60
  51. package/lib/core/contexts/map.json +0 -19
  52. package/lib/core/contexts/miscellany.json +0 -19
  53. package/lib/core/contexts/quotes.json +0 -18
  54. package/lib/core/contexts/schemaorg.json +0 -8845
  55. package/lib/core/contexts/security-data-integrity-v1.json +0 -78
  56. package/lib/core/contexts/security-data-integrity-v2.json +0 -81
  57. package/lib/core/contexts/security-multikey-v1.json +0 -35
  58. package/lib/core/contexts/security-v1.json +0 -74
  59. package/lib/core/contexts/webfinger.json +0 -10
  60. package/lib/core/deliver.mjs +0 -387
  61. package/lib/core/graphview.mjs +0 -327
  62. package/lib/core/intake/activities.mjs +0 -649
  63. package/lib/core/intake/activity.mjs +0 -318
  64. package/lib/core/intake/channel.mjs +0 -144
  65. package/lib/core/intake/group.mjs +0 -258
  66. package/lib/core/intake/index.mjs +0 -732
  67. package/lib/core/intake/notes.mjs +0 -367
  68. package/lib/core/intake/verify.mjs +0 -142
  69. package/lib/core/keys.mjs +0 -153
  70. package/lib/core/lease.mjs +0 -206
  71. package/lib/core/place.mjs +0 -82
  72. package/lib/core/polls.mjs +0 -105
  73. package/lib/core/profile-page.mjs +0 -104
  74. package/lib/core/proof.mjs +0 -79
  75. package/lib/core/publisher/collections.mjs +0 -272
  76. package/lib/core/publisher/index.mjs +0 -522
  77. package/lib/core/publisher/notes.mjs +0 -519
  78. package/lib/core/publisher/own.mjs +0 -143
  79. package/lib/core/publisher/questions.mjs +0 -241
  80. package/lib/core/publisher/restore.mjs +0 -224
  81. package/lib/core/shapes/activitystreams.ttl +0 -157
  82. package/lib/core/shapes/index.mjs +0 -132
  83. package/lib/core/shapes/shapes-text.mjs +0 -13
  84. package/lib/core/social.mjs +0 -561
  85. package/lib/core/storage.mjs +0 -176
  86. package/lib/core/store.mjs +0 -578
  87. package/lib/core/wire-quotes.mjs +0 -71
  88. package/lib/core/wire.mjs +0 -925
  89. package/lib/device/account.mjs +0 -87
  90. package/lib/device/admin/index.mjs +0 -13
  91. package/lib/device/admin/origins.mjs +0 -35
  92. package/lib/device/admin/routes/connections.mjs +0 -144
  93. package/lib/device/admin/routes/gateway.mjs +0 -199
  94. package/lib/device/admin/routes/lifecycle.mjs +0 -200
  95. package/lib/device/admin/routes/owner.mjs +0 -322
  96. package/lib/device/admin/routes/setup.mjs +0 -428
  97. package/lib/device/admin/routes/social.mjs +0 -188
  98. package/lib/device/admin/server.mjs +0 -95
  99. package/lib/device/admin/static.mjs +0 -247
  100. package/lib/device/admin/surface.mjs +0 -315
  101. package/lib/device/certs.mjs +0 -281
  102. package/lib/device/cli/commands/account.mjs +0 -586
  103. package/lib/device/cli/commands/run.mjs +0 -278
  104. package/lib/device/cli/commands/service.mjs +0 -221
  105. package/lib/device/cli/commands/setup.mjs +0 -489
  106. package/lib/device/cli/commands/state.mjs +0 -561
  107. package/lib/device/cli/context.mjs +0 -288
  108. package/lib/device/export-collections.mjs +0 -161
  109. package/lib/device/gateway-move.mjs +0 -58
  110. package/lib/device/home.mjs +0 -161
  111. package/lib/device/migrate.mjs +0 -115
  112. package/lib/device/ports.mjs +0 -27
  113. package/lib/device/remote.mjs +0 -54
  114. package/lib/device/setup.mjs +0 -496
  115. package/lib/device/update.mjs +0 -96
  116. package/lib/gateway/caches.mjs +0 -36
  117. package/lib/gateway/directory.mjs +0 -90
  118. package/lib/gateway/front-core.mjs +0 -966
  119. package/lib/gateway/gateway-core.mjs +0 -244
  120. package/lib/gateway/headers.mjs +0 -49
  121. package/lib/gateway/httpsig.mjs +0 -159
  122. package/lib/gateway/notices.mjs +0 -84
  123. package/lib/gateway/quiet.mjs +0 -189
  124. package/lib/gateway/relay-extras.mjs +0 -89
  125. package/lib/gateway/token-claims.mjs +0 -16
  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/location.mjs +0 -52
  138. package/lib/pod/media.mjs +0 -27
  139. package/lib/pod/notes.mjs +0 -78
  140. package/lib/pod/notifications.mjs +0 -89
  141. package/lib/pod/outbox.mjs +0 -18
  142. package/lib/pod/package.json +0 -19
  143. package/lib/pod/policy.mjs +0 -58
  144. package/lib/pod/private.mjs +0 -19
  145. package/lib/pod/root.mjs +0 -104
  146. package/lib/pod/state.mjs +0 -40
  147. package/lib/pod/transport.mjs +0 -674
  148. package/lib/pod/type-index.mjs +0 -101
  149. package/lib/pod/urls.mjs +0 -91
  150. package/lib/server/embed.mjs +0 -424
  151. package/lib/session/README.md +0 -137
  152. package/lib/session/demo.html +0 -94
  153. package/lib/session/fedi-account.mjs +0 -417
  154. package/lib/session/fedi-login.mjs +0 -123
  155. package/lib/session/oidc-session.mjs +0 -233
  156. package/lib/session/package.json +0 -49
  157. package/lib/shared/guard.mjs +0 -213
  158. package/lib/shared/links.mjs +0 -9
  159. package/lib/shared/safefetch.mjs +0 -235
  160. package/lib/shared/ua.mjs +0 -29
  161. package/phanpy/dist/404.html +0 -40
  162. package/phanpy/dist/_headers +0 -2
  163. package/phanpy/dist/apple-touch-icon.png +0 -0
  164. package/phanpy/dist/assets/ICONS-DIPJeU0C.js +0 -2
  165. package/phanpy/dist/assets/ICONS-DIPJeU0C.js.map +0 -1
  166. package/phanpy/dist/assets/ScheduledAtField-D-Abfo4J.js +0 -183
  167. package/phanpy/dist/assets/ScheduledAtField-D-Abfo4J.js.map +0 -1
  168. package/phanpy/dist/assets/Temml-CCjoacWt.woff2 +0 -0
  169. package/phanpy/dist/assets/__vite-browser-external-C7iut881.js +0 -2
  170. package/phanpy/dist/assets/__vite-browser-external-C7iut881.js.map +0 -1
  171. package/phanpy/dist/assets/boosts-carousel-BiOaNdMT.jpg +0 -0
  172. package/phanpy/dist/assets/catch-up-O5q5C75Z.png +0 -0
  173. package/phanpy/dist/assets/chunk-aKtaBQYM.js +0 -1
  174. package/phanpy/dist/assets/compose-CFEx7wb4.js +0 -2
  175. package/phanpy/dist/assets/compose-CFEx7wb4.js.map +0 -1
  176. package/phanpy/dist/assets/compose-ZJN5xSb4.js +0 -35
  177. package/phanpy/dist/assets/compose-ZJN5xSb4.js.map +0 -1
  178. package/phanpy/dist/assets/debug.module-YFQgwnIr.js +0 -52
  179. package/phanpy/dist/assets/debug.module-YFQgwnIr.js.map +0 -1
  180. package/phanpy/dist/assets/dom-p1YcJ5Rw.js +0 -2
  181. package/phanpy/dist/assets/dom-p1YcJ5Rw.js.map +0 -1
  182. package/phanpy/dist/assets/home-mobile-dark@2x-Dc-J8-SM.png +0 -0
  183. package/phanpy/dist/assets/home-mobile-light@2x-Bd3fod8m.png +0 -0
  184. package/phanpy/dist/assets/home-tablet-dark@2x-BgcNI8le.png +0 -0
  185. package/phanpy/dist/assets/home-tablet-light@2x-BW2RV9T6.png +0 -0
  186. package/phanpy/dist/assets/icons/add-circle-line-WD0mwORD.js +0 -1
  187. package/phanpy/dist/assets/icons/alert-line-CpFUMf9s.js +0 -1
  188. package/phanpy/dist/assets/icons/android-2-line-Bzxnrd3S.js +0 -1
  189. package/phanpy/dist/assets/icons/announcement-line-5nwaJFJm.js +0 -1
  190. package/phanpy/dist/assets/icons/arrow-down-circle-line-a7Id9-Iv.js +0 -1
  191. package/phanpy/dist/assets/icons/arrow-down-line-B8uzXzq6.js +0 -1
  192. package/phanpy/dist/assets/icons/arrow-left-line-CGrGT9C-.js +0 -1
  193. package/phanpy/dist/assets/icons/arrow-right-line-CE-HERfX.js +0 -1
  194. package/phanpy/dist/assets/icons/arrow-to-up-line-CvdQjmcy.js +0 -1
  195. package/phanpy/dist/assets/icons/arrow-up-circle-line-DEparSNF.js +0 -1
  196. package/phanpy/dist/assets/icons/arrow-up-line-C7yBeugc.js +0 -1
  197. package/phanpy/dist/assets/icons/arrows-right-line-BtbXmJGP.js +0 -1
  198. package/phanpy/dist/assets/icons/at-line-CF66Bznk.js +0 -1
  199. package/phanpy/dist/assets/icons/attachment-line-BY8It-l9.js +0 -1
  200. package/phanpy/dist/assets/icons/blockquote-line-BS6pU_hm.js +0 -1
  201. package/phanpy/dist/assets/icons/board-line-C0UWXyA6.js +0 -1
  202. package/phanpy/dist/assets/icons/bookmark-line-1CtX5JLF.js +0 -1
  203. package/phanpy/dist/assets/icons/building-5-line-Dd59MzbO.js +0 -1
  204. package/phanpy/dist/assets/icons/bus-2-line-6_dvA0GG.js +0 -1
  205. package/phanpy/dist/assets/icons/calendar-day-line-Crpl_fmu.js +0 -1
  206. package/phanpy/dist/assets/icons/calendar-month-line-vommts2C.js +0 -1
  207. package/phanpy/dist/assets/icons/calendar-time-add-line-BU_DTlQk.js +0 -1
  208. package/phanpy/dist/assets/icons/camera-line-Cv69MEei.js +0 -1
  209. package/phanpy/dist/assets/icons/celebrate-line-B8DvIoi0.js +0 -1
  210. package/phanpy/dist/assets/icons/chart-bar-line-FK-QOmtF.js +0 -1
  211. package/phanpy/dist/assets/icons/chart-line-line-BGcJAFWw.js +0 -1
  212. package/phanpy/dist/assets/icons/chat-3-line-YFK2-hwz.js +0 -1
  213. package/phanpy/dist/assets/icons/check-circle-line-DsAxFr5-.js +0 -1
  214. package/phanpy/dist/assets/icons/clipboard-line-BBp364gJ.js +0 -1
  215. package/phanpy/dist/assets/icons/close-circle-line-CICdiuGz.js +0 -1
  216. package/phanpy/dist/assets/icons/close-line-DYrFWcjv.js +0 -1
  217. package/phanpy/dist/assets/icons/cloud-line-tiQAbah8.js +0 -1
  218. package/phanpy/dist/assets/icons/code-line-fAl8iiV3.js +0 -1
  219. package/phanpy/dist/assets/icons/comment-2-line-CR3g7WeB.js +0 -1
  220. package/phanpy/dist/assets/icons/copy-2-line-CLW69yF5.js +0 -1
  221. package/phanpy/dist/assets/icons/delete-2-line-B8xmbr4n.js +0 -1
  222. package/phanpy/dist/assets/icons/document-3-line-DBGNhAg2.js +0 -1
  223. package/phanpy/dist/assets/icons/document-line-C7mb9sb7.js +0 -1
  224. package/phanpy/dist/assets/icons/down-line-BGqSSThd.js +0 -1
  225. package/phanpy/dist/assets/icons/edit-4-line-DcgGaEhx.js +0 -1
  226. package/phanpy/dist/assets/icons/emoji-2-line-D8YgnM-y.js +0 -1
  227. package/phanpy/dist/assets/icons/exit-line-Dc07BS-x.js +0 -1
  228. package/phanpy/dist/assets/icons/external-link-line-BvpSkmjO.js +0 -1
  229. package/phanpy/dist/assets/icons/eye-2-line-CiWXx-f_.js +0 -1
  230. package/phanpy/dist/assets/icons/eye-close-line-BgTaSJ4P.js +0 -1
  231. package/phanpy/dist/assets/icons/filter-2-line-B_mR2usy.js +0 -1
  232. package/phanpy/dist/assets/icons/filter-line-C_nm-3cU.js +0 -1
  233. package/phanpy/dist/assets/icons/flag-1-line-C2HB05I0.js +0 -1
  234. package/phanpy/dist/assets/icons/forbid-circle-line-D3LTT9yd.js +0 -1
  235. package/phanpy/dist/assets/icons/formula-line-DLrt2d6j.js +0 -1
  236. package/phanpy/dist/assets/icons/grid-line-CgUmEikf.js +0 -1
  237. package/phanpy/dist/assets/icons/group-line-C5fnX0wU.js +0 -1
  238. package/phanpy/dist/assets/icons/hand-finger-2-line-BGgxbuL9.js +0 -1
  239. package/phanpy/dist/assets/icons/hashtag-line-CyTEEXBb.js +0 -1
  240. package/phanpy/dist/assets/icons/heart-crack-line-BZddbO2C.js +0 -1
  241. package/phanpy/dist/assets/icons/heart-line-CJ7K47J4.js +0 -1
  242. package/phanpy/dist/assets/icons/history-2-line-kWWvFzqv.js +0 -1
  243. package/phanpy/dist/assets/icons/history-line-bGQiwbTH.js +0 -1
  244. package/phanpy/dist/assets/icons/home-3-line-fRU1zeLG.js +0 -1
  245. package/phanpy/dist/assets/icons/information-line-iNk6-sOY.js +0 -1
  246. package/phanpy/dist/assets/icons/keyboard-line-DsCyuQNz.js +0 -1
  247. package/phanpy/dist/assets/icons/layout-4-line-CZkOX52Z.js +0 -1
  248. package/phanpy/dist/assets/icons/layout-5-line-B_znt5wO.js +0 -1
  249. package/phanpy/dist/assets/icons/left-line-eUw0tya4.js +0 -1
  250. package/phanpy/dist/assets/icons/lightning-line-DaKoDdix.js +0 -1
  251. package/phanpy/dist/assets/icons/link-2-line-DCqLmMA7.js +0 -1
  252. package/phanpy/dist/assets/icons/list-check-line-BAxosBh9.js +0 -1
  253. package/phanpy/dist/assets/icons/lock-line-CPoaXeUW.js +0 -1
  254. package/phanpy/dist/assets/icons/mail-line-B0P8aa1L.js +0 -1
  255. package/phanpy/dist/assets/icons/moon-line-CEqNAToP.js +0 -1
  256. package/phanpy/dist/assets/icons/more-1-fill-Dw2e5xab.js +0 -1
  257. package/phanpy/dist/assets/icons/more-3-line-BIiVjYTz.js +0 -1
  258. package/phanpy/dist/assets/icons/notification-line-BZZq2Gtu.js +0 -1
  259. package/phanpy/dist/assets/icons/pencil-line-CKh8-A1v.js +0 -1
  260. package/phanpy/dist/assets/icons/photo-album-line-Bnfg2u40.js +0 -1
  261. package/phanpy/dist/assets/icons/pin-line-DpTolyhs.js +0 -1
  262. package/phanpy/dist/assets/icons/play-fill-B0-G_8lX.js +0 -1
  263. package/phanpy/dist/assets/icons/qrcode-2-line-B6SPCaHn.js +0 -1
  264. package/phanpy/dist/assets/icons/quill-pen-line-B6LUrnCP.js +0 -1
  265. package/phanpy/dist/assets/icons/quote-left-fill-hQZ1cj_6.js +0 -1
  266. package/phanpy/dist/assets/icons/radar-line-CKyroTth.js +0 -1
  267. package/phanpy/dist/assets/icons/react-line-CoIKhhtK.js +0 -1
  268. package/phanpy/dist/assets/icons/refresh-2-line-D0mTF97S.js +0 -1
  269. package/phanpy/dist/assets/icons/right-line-B3KWJaqc.js +0 -1
  270. package/phanpy/dist/assets/icons/rocket-line-4KZl537D.js +0 -1
  271. package/phanpy/dist/assets/icons/round-fill-C69NTrZ0.js +0 -1
  272. package/phanpy/dist/assets/icons/round-line-B9k_1uo-.js +0 -1
  273. package/phanpy/dist/assets/icons/route-line-B28ReHcE.js +0 -1
  274. package/phanpy/dist/assets/icons/rows-4-line-CrBniagM.js +0 -1
  275. package/phanpy/dist/assets/icons/scan-line-DSf74YdP.js +0 -1
  276. package/phanpy/dist/assets/icons/search-2-line-B6geIU1z.js +0 -1
  277. package/phanpy/dist/assets/icons/settings-3-line-azJwK47w.js +0 -1
  278. package/phanpy/dist/assets/icons/settings-6-line-C_5zc5YR.js +0 -1
  279. package/phanpy/dist/assets/icons/share-2-line-D5MxWjrm.js +0 -1
  280. package/phanpy/dist/assets/icons/share-forward-line-Cp61fZzZ.js +0 -1
  281. package/phanpy/dist/assets/icons/sparkles-2-line-DYYyXTaW.js +0 -1
  282. package/phanpy/dist/assets/icons/sparkles-line-C2hY1s6I.js +0 -1
  283. package/phanpy/dist/assets/icons/time-line-BF3eet1v.js +0 -1
  284. package/phanpy/dist/assets/icons/transfer-4-line-oisfvTBw.js +0 -1
  285. package/phanpy/dist/assets/icons/translate-line-BFWol2Ce.js +0 -1
  286. package/phanpy/dist/assets/icons/unlock-line-X_x5vwv3.js +0 -1
  287. package/phanpy/dist/assets/icons/upload-3-line-DgzwUJeW.js +0 -1
  288. package/phanpy/dist/assets/icons/user-4-line-BVSynZCp.js +0 -1
  289. package/phanpy/dist/assets/icons/user-add-2-line-BlGS5Q8q.js +0 -1
  290. package/phanpy/dist/assets/icons/user-add-line-BbXdHG4z.js +0 -1
  291. package/phanpy/dist/assets/icons/user-edit-line-BFAHOV0X.js +0 -1
  292. package/phanpy/dist/assets/icons/user-follow-line-BXux0wSd.js +0 -1
  293. package/phanpy/dist/assets/icons/user-star-line-DdYlpHLU.js +0 -1
  294. package/phanpy/dist/assets/icons/user-warning-line-Cj43j0_E.js +0 -1
  295. package/phanpy/dist/assets/icons/user-x-line-DIDgNlPH.js +0 -1
  296. package/phanpy/dist/assets/icons/volume-line-Bs390KQt.js +0 -1
  297. package/phanpy/dist/assets/icons/volume-mute-line-DzlzIqFZ.js +0 -1
  298. package/phanpy/dist/assets/icons/walk-line-yNXfB-md.js +0 -1
  299. package/phanpy/dist/assets/icons/world-2-line-CenkGIwj.js +0 -1
  300. package/phanpy/dist/assets/icons/zoom-in-line-CaENUphf.js +0 -1
  301. package/phanpy/dist/assets/icons/zoom-out-line-BDNk0EWo.js +0 -1
  302. package/phanpy/dist/assets/instances-BNPptnn-.json +0 -333
  303. package/phanpy/dist/assets/locales/ar-SA-CfheRbCu.js +0 -1
  304. package/phanpy/dist/assets/locales/ca-ES-DHRW86Tz.js +0 -1
  305. package/phanpy/dist/assets/locales/cs-CZ-CKoqEuA1.js +0 -1
  306. package/phanpy/dist/assets/locales/de-DE-ByRPMRFy.js +0 -1
  307. package/phanpy/dist/assets/locales/eo-UY-CPC7wLCS.js +0 -1
  308. package/phanpy/dist/assets/locales/es-ES-dJa0Fd9t.js +0 -1
  309. package/phanpy/dist/assets/locales/eu-ES-C3djlPR2.js +0 -1
  310. package/phanpy/dist/assets/locales/fa-IR-DTT490Ck.js +0 -1
  311. package/phanpy/dist/assets/locales/fi-FI-B23MCPuz.js +0 -1
  312. package/phanpy/dist/assets/locales/fr-FR-BcaQsO5C.js +0 -1
  313. package/phanpy/dist/assets/locales/gl-ES-BR-CVKBN.js +0 -1
  314. package/phanpy/dist/assets/locales/he-IL-CfheRbCu.js +0 -1
  315. package/phanpy/dist/assets/locales/hu-HU-CfheRbCu.js +0 -1
  316. package/phanpy/dist/assets/locales/it-IT-C1136UFV.js +0 -1
  317. package/phanpy/dist/assets/locales/ja-JP-CRZmRAhD.js +0 -1
  318. package/phanpy/dist/assets/locales/kab-Cz8xH_TT.js +0 -1
  319. package/phanpy/dist/assets/locales/ko-KR-CG9UiY4o.js +0 -1
  320. package/phanpy/dist/assets/locales/lt-LT-CJcI6sHZ.js +0 -1
  321. package/phanpy/dist/assets/locales/nb-NO-CfheRbCu.js +0 -1
  322. package/phanpy/dist/assets/locales/nl-NL-DxN4Yc1t.js +0 -1
  323. package/phanpy/dist/assets/locales/oc-FR-CfheRbCu.js +0 -1
  324. package/phanpy/dist/assets/locales/pl-PL-DkwGXf6P.js +0 -1
  325. package/phanpy/dist/assets/locales/pseudo-LOCALE-CQ4XxkQ0.js +0 -1
  326. package/phanpy/dist/assets/locales/pt-BR-nGL0HdFS.js +0 -1
  327. package/phanpy/dist/assets/locales/pt-PT-Cp17aj2z.js +0 -1
  328. package/phanpy/dist/assets/locales/ru-RU-0Y8cIoaK.js +0 -1
  329. package/phanpy/dist/assets/locales/th-TH-CfheRbCu.js +0 -1
  330. package/phanpy/dist/assets/locales/tok-CfheRbCu.js +0 -1
  331. package/phanpy/dist/assets/locales/tr-TR-CfheRbCu.js +0 -1
  332. package/phanpy/dist/assets/locales/uk-UA-C5E3dFBf.js +0 -1
  333. package/phanpy/dist/assets/locales/zh-CN-BHJi7_et.js +0 -1
  334. package/phanpy/dist/assets/locales/zh-TW-CfheRbCu.js +0 -1
  335. package/phanpy/dist/assets/main-BdqNbG-a.js +0 -26
  336. package/phanpy/dist/assets/main-BdqNbG-a.js.map +0 -1
  337. package/phanpy/dist/assets/mock-home-DGhR__He.js +0 -2
  338. package/phanpy/dist/assets/mock-home-DGhR__He.js.map +0 -1
  339. package/phanpy/dist/assets/multi-column-ROck0NVt.jpg +0 -0
  340. package/phanpy/dist/assets/multi-hashtag-timeline-DH5INVHi.jpg +0 -0
  341. package/phanpy/dist/assets/nested-comments-thread-hv59kZLc.jpg +0 -0
  342. package/phanpy/dist/assets/polyfill-force-H8sUkLGp.js +0 -2
  343. package/phanpy/dist/assets/polyfill-force-H8sUkLGp.js.map +0 -1
  344. package/phanpy/dist/assets/pwa-viewport-ogKbF51K.js +0 -20
  345. package/phanpy/dist/assets/pwa-viewport-ogKbF51K.js.map +0 -1
  346. package/phanpy/dist/assets/style-B1iLPTPR.css +0 -2
  347. package/phanpy/dist/assets/temml-DuyIQMei.js +0 -97
  348. package/phanpy/dist/assets/temml-DuyIQMei.js.map +0 -1
  349. package/phanpy/dist/assets/tinyld.light.node-DtbBhLYC.js +0 -2
  350. package/phanpy/dist/assets/tinyld.light.node-DtbBhLYC.js.map +0 -1
  351. package/phanpy/dist/assets/year-in-posts-B2lywqdx.png +0 -0
  352. package/phanpy/dist/assets/year-in-posts-DJv_star.js +0 -3
  353. package/phanpy/dist/assets/year-in-posts-DJv_star.js.map +0 -1
  354. package/phanpy/dist/compose/index.html +0 -20
  355. package/phanpy/dist/favicon.ico +0 -0
  356. package/phanpy/dist/index.html +0 -73
  357. package/phanpy/dist/logo-192.png +0 -0
  358. package/phanpy/dist/logo-512.png +0 -0
  359. package/phanpy/dist/logo-badge-72.png +0 -0
  360. package/phanpy/dist/logo-maskable-512.png +0 -0
  361. package/phanpy/dist/logo-monochrome-512.png +0 -0
  362. package/phanpy/dist/logo-monochrome-maskable-512.png +0 -0
  363. package/phanpy/dist/manifest.webmanifest +0 -1
  364. package/phanpy/dist/og-image-2.jpg +0 -0
  365. package/phanpy/dist/og-image.png +0 -0
  366. package/phanpy/dist/robots.txt +0 -2
  367. package/phanpy/dist/sw.js +0 -2
  368. package/phanpy/dist/sw.js.map +0 -1
  369. package/phanpy/dist/version.json +0 -1
  370. package/run-agent.mjs +0 -899
  371. package/vendor/gate.cjs +0 -138
  372. package/vendor/idp-grant.cjs +0 -394
  373. package/vendor/jfetch.cjs +0 -16
  374. package/web/admin/actors.js +0 -147
  375. package/web/admin/bar.css +0 -126
  376. package/web/admin/bar.js +0 -99
  377. package/web/admin/client/client.js +0 -66
  378. package/web/admin/client/index.html +0 -69
  379. package/web/admin/client-pick.js +0 -79
  380. package/web/admin/common.js +0 -23
  381. package/web/admin/connections.js +0 -112
  382. package/web/admin/gateway.js +0 -133
  383. package/web/admin/group.js +0 -258
  384. package/web/admin/index.html +0 -612
  385. package/web/admin/notices-bar.js +0 -90
  386. package/web/admin/oauth-signin.mjs +0 -37
  387. package/web/admin/record.js +0 -393
  388. package/web/admin/setup/index.html +0 -257
  389. package/web/admin/setup/setup.js +0 -339
  390. package/web/admin/tokens.css +0 -68
  391. package/web/admin/upkeep.js +0 -177
  392. package/web/admin/window.css +0 -55
  393. package/web/admin/window.js +0 -136
  394. package/web/app/README.md +0 -81
  395. package/web/app/admin-facade.mjs +0 -588
  396. package/web/app/agent.mjs +0 -672
  397. package/web/app/atproto-browser.mjs +0 -86
  398. package/web/app/authorize_interaction.html +0 -56
  399. package/web/app/authorize_interaction.js +0 -69
  400. package/web/app/boot.mjs +0 -616
  401. package/web/app/deliver-relay.mjs +0 -129
  402. package/web/app/dist/boot.js +0 -34562
  403. package/web/app/dist/boot.js.map +0 -7
  404. package/web/app/dist/sw.js +0 -73456
  405. package/web/app/dist/sw.js.map +0 -7
  406. package/web/app/fediacct-browser.mjs +0 -146
  407. package/web/app/gateway-move.mjs +0 -60
  408. package/web/app/idb-kv.mjs +0 -35
  409. package/web/app/index.html +0 -258
  410. package/web/app/keys-browser.mjs +0 -71
  411. package/web/app/keystore.mjs +0 -82
  412. package/web/app/oidc-session.mjs +0 -6
  413. package/web/app/pod-auth.mjs +0 -248
  414. package/web/app/pod-remote.mjs +0 -84
  415. package/web/app/shims/_globals.mjs +0 -2
  416. package/web/app/shims/fedify-sig.mjs +0 -77
  417. package/web/app/shims/node-crypto.mjs +0 -113
  418. package/web/app/shims/node-fs.mjs +0 -17
  419. package/web/app/shims/node-path.mjs +0 -19
  420. package/web/app/shims/node-url.mjs +0 -7
  421. package/web/app/shims/prelude.js +0 -58
  422. package/web/app/shims/safefetch.mjs +0 -58
  423. package/web/app/shims/shapes-text.mjs +0 -8
  424. package/web/app/shims/web-push.mjs +0 -14
  425. package/web/app/signup.mjs +0 -357
  426. package/web/app/sw-src.mjs +0 -220
  427. package/web/app/update.js +0 -70
  428. package/web/front/admin.html +0 -55
  429. package/web/front/admin.js +0 -89
  430. package/web/front/install.sh +0 -69
  431. package/web/front/new-account.html +0 -35
  432. package/web/front/new-account.js +0 -13
  433. package/web/front/notices.html +0 -69
  434. package/web/front/notices.js +0 -107
  435. package/web/front/run.html +0 -64
  436. package/web/front/run.js +0 -221
  437. package/web/front/solid-oidc-client.js +0 -6
  438. package/web/icons/forum.svg +0 -11
  439. package/web/icons/gateway.svg +0 -14
  440. package/web/icons/local.svg +0 -11
@@ -1,522 +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
- import * as own from './own.mjs';
32
- import { recordPlace } from '../place.mjs';
33
-
34
- const AGENT_VERSION = JSON.parse(fs.readFileSync(
35
- path.join(path.dirname(fileURLToPath(import.meta.url)), '../../../package.json'), 'utf8')).version;
36
-
37
- export class Publisher {
38
- constructor({ config, remote, store, deliverer, publicKeyPem, assertionKey = null, log = console.log,
39
- probeFetch = null, resolveMention = null, resolveActor = null, clientOrigin = null,
40
- }) {
41
- this.config = config;
42
- this.remote = remote;
43
- this.store = store;
44
- this.deliverer = deliverer;
45
- // Everything this actor sends goes on the outbox its owner reads.
46
- if (deliverer) deliverer.onSent = (a) => own.recordOwn(this, a);
47
- this.publicKeyPem = publicKeyPem;
48
- this.assertionKey = assertionKey; // Ed25519 public half, multibase; null = no proofs
49
- // Where this identity's client surface answers, when that is an address a
50
- // stranger can reach. Null on a laptop, where the surface is on loopback
51
- // and advertising it to the world would name somewhere nobody can go.
52
- this.clientOrigin = clientOrigin;
53
- // A fronted identity (config.gateway.frontActor) advertises its ids on a
54
- // shared domain; the map tells RemotePod where each writes on the pod.
55
- const publicBase = config.gateway?.frontActor
56
- ? config.gateway.frontActor.replace(/ap\/actor\/?$/, '') : null;
57
- this.urls = wire.apUrls(config.remotePod, config.root, { publicBase });
58
- if (this.urls.toPod && this.remote?.setUrlMap) this.remote.setUrlMap(this.urls.toPod);
59
- // Credential-free by design — it asks what a stranger sees — but routed
60
- // through the pod's own cooldown and accounting, because it is still a
61
- // socket opened to that pod. Tests inject their own.
62
- this.probeFetch = probeFetch || ((u, i) => this.remote.probe(u, i));
63
- this.resolveMention = resolveMention;
64
- // An actor or object by its address, verified at its origin — how a reply
65
- // finds the inbox of the person it answers.
66
- this.resolveActor = resolveActor;
67
- // Per-poll rewrite windows, keyed by question id. See POLL_REWRITE_MS.
68
- this.pollTimers = new Map();
69
- this.log = log;
70
- }
71
-
72
- // Idempotent: (re)write webfinger + actor + collections + container ACLs.
73
- //
74
- // ~34 pod requests, so it does not run when it would rewrite the same bytes.
75
- // Every document below is derived from the actor doc, the handle, the host,
76
- // whether the actor is quiesced (which decides the inbox ACL) and the agent
77
- // version (which rides in nodeinfo) — so if none of those moved, there is
78
- // nothing to say. Phanpy's editor submits the whole form on every save, so
79
- // "saved without changing anything" is the common case, not a rare one.
80
- //
81
- // `force` is for the callers that publish precisely BECAUSE the pod does not
82
- // have what the digest says it has: the repair path, and the explicit
83
- // republish button. Without it, an actor lost from the pod would match the
84
- // digest, be skipped, and leave the agent reporting success while nobody can
85
- // resolve it.
86
- // The Gateway's outbox door for this account, when a Gateway is attached:
87
- // beside its inbox door, or under the fronted actor.
88
- gatewayOutbox() {
89
- const gw = this.config.gateway;
90
- if (!(gw && gw.url && gw.mode && gw.mode !== 'off')) return null;
91
- if (gw.frontActor) return String(gw.frontActor).replace(/ap\/actor\/?$/u, 'ap/outbox');
92
- return String(gw.url).replace(/ap\/inbox\/?$/u, 'ap/outbox');
93
- }
94
-
95
- // Every actor document this identity publishes, from one place: the
96
- // profile publish and a Move both write it, and a Move that rebuilt it
97
- // by hand dropped the gateway inbox, the outbox door and the collections.
98
- // `clientOrigin` is overridden only by the agent serving a client its own
99
- // local actor: standalone, the published document cannot name a loopback
100
- // address, but a client already talking to that address can be told it.
101
- actorDocFor({ priv = false, moderators = null, inbox = null, movedTo = this.config.movedTo || null,
102
- clientOrigin = this.clientOrigin } = {}) {
103
- const { urls } = this;
104
- return wire.actorDoc({
105
- urls, handle: wire.publicHandle(this.config), name: this.config.name, publicKeyPem: this.publicKeyPem,
106
- movedTo, kind: this.config.kind,
107
- approveJoins: wire.followsNeedApproval(this.config),
108
- assertionKey: this.assertionKey,
109
- summary: this.config.summary || null, icon: this.config.icon || null,
110
- image: this.config.image || null, fields: this.config.fields || [],
111
- webId: this.remote.webId || null,
112
- aliases: this.config.aliases || [],
113
- // A group says whether only its moderators may open posts (Lemmy's
114
- // term); a person's actor carries nothing of the kind.
115
- postingRestrictedToMods: this.config.kind === 'group' ? !!this.config.postingRestrictedToMods : null,
116
- moderators,
117
- pendingFollowers: priv ? urls.pendingFollowers : null,
118
- // Named only where the private folder is proved to keep it private.
119
- liked: priv ? urls.liked : null,
120
- ownerOutbox: priv ? urls.ownOutbox : null,
121
- pendingFollowing: priv ? urls.pendingFollowing : null,
122
- blocked: priv ? urls.blocked : null,
123
- inbox,
124
- // The agent's own outbox endpoint, where it is reachable: a client
125
- // following the actor must arrive somewhere that will take a write.
126
- // Otherwise the Gateway's outbox door, when one is attached.
127
- outbox: clientOrigin ? `${clientOrigin}ap/outbox` : this.gatewayOutbox(),
128
- // How a client-to-server client finds the way in with nothing configured
129
- // by hand. Advertised only where the surface is publicly reachable.
130
- oauthAuthorize: clientOrigin ? `${clientOrigin}oauth/authorize` : null,
131
- oauthToken: clientOrigin ? `${clientOrigin}oauth/token` : null,
132
- });
133
- }
134
-
135
- // The human half: a page a browser can open and follow from. The actor
136
- // document is for servers; this is the address you hand to a person. It
137
- // shows who the account is, its fields, when it joined and what it pinned;
138
- // the counts and the posts live in the collections. Written only when its
139
- // content changed.
140
- async publishProfilePage({ force = false } = {}) {
141
- const { urls } = this;
142
- const host = new URL(urls.base).host;
143
- // When the account was made. Accounts from before this was recorded take
144
- // the date of their oldest post, or today.
145
- if (!this.config.createdAt) {
146
- const oldest = this.store.getStatuses().filter(s => s.kind === 'post' && s.published)
147
- .map(s => s.published).sort()[0];
148
- this.config.createdAt = oldest || new Date().toISOString();
149
- this.store.setConfig?.({ ...this.store.getConfig?.(), createdAt: this.config.createdAt });
150
- }
151
- const pinned = this.store.getStatuses()
152
- .filter(s => s.kind === 'post' && s.pinned && s.visibility !== 'private' && s.visibility !== 'direct')
153
- .sort((a, b) => String(b.published).localeCompare(String(a.published)))
154
- .slice(0, 5)
155
- .map(s => ({ content: s.content, published: s.published, url: s.noteId }));
156
- // The address as the world knows it: at the Gateway for a fronted
157
- // identity, on the pod's host otherwise, the actor id where no host can
158
- // answer for a handle.
159
- const frontActor = this.config.gateway?.frontActor;
160
- const address = frontActor ? `@${wire.publicHandle(this.config)}@${new URL(frontActor).host}`
161
- : (wire.webfingerHost(urls.base) ? `@${this.config.handle}@${host}` : urls.actor);
162
- const html = wire.profilePageHtml({
163
- name: this.config.name || this.config.handle,
164
- address,
165
- summary: this.config.summary ? wire.contentHtml(this.config.summary) : null,
166
- icon: this.config.icon || null,
167
- image: this.config.image || null,
168
- fields: this.config.fields || [],
169
- joined: this.config.createdAt,
170
- pinned,
171
- kind: this.config.kind,
172
- });
173
- const digest = crypto.createHash('sha256').update(html).digest('hex').slice(0, 32);
174
- const seen = this.store.read('published.json', {});
175
- if (!force && seen.pageDigest === digest) return false;
176
- await podActor.writeProfilePage(this.remote, urls, html);
177
- this.store.write('published.json', { ...this.store.read('published.json', {}), pageDigest: digest });
178
- return true;
179
- }
180
-
181
- async publishProfile({ force = false } = {}) {
182
- const { urls } = this;
183
- const host = new URL(urls.base).host;
184
-
185
- // The owner-only collections (FEP-4ccd, FEP-c648) are advertised only
186
- // where "owner-only" is real — the pod must provably enforce the private
187
- // container's ACL, the same bar private posts clear.
188
- const priv = await this.privateReady() === true;
189
- const moderators = (this.config.moderators || []).length ? urls.moderators : null;
190
- // An advertised inbox gateway (config.gateway.mode past 'off') becomes the
191
- // actor's inbox; deliveries reach the pod inbox through it. Absent → today.
192
- const gw = this.config.gateway;
193
- const gwActive = gw && gw.url && gw.mode && gw.mode !== 'off';
194
- // A forum's category advertises the forum's one inbox (config.inboxUrl):
195
- // deliveries to any category land there, and the forum routes them.
196
- const actorDoc = this.actorDocFor({ priv, moderators, inbox: gwActive ? gw.url : (this.config.inboxUrl || null) });
197
- const surface = crypto.createHash('sha256').update(JSON.stringify({
198
- actor: actorDoc, handle: this.config.handle, host,
199
- quiesced: !!this.config.quiescedAt, version: AGENT_VERSION,
200
- moderators: this.config.moderators || [],
201
- })).digest('hex').slice(0, 32);
202
- // The human page has its own gate: it changes with pins and the joined
203
- // date as well as with the profile, and costs one write when it does.
204
- await this.publishProfilePage({ force });
205
- if (!force && this.store.read('published.json', {}).surfaceDigest === surface) {
206
- this.log('profile unchanged — nothing republished');
207
- return { unreachable: [], updated: 0, skipped: true };
208
- }
209
-
210
- // Discovery is the pod's, and a pod has one WebFinger document: a forum's
211
- // category (config.forum names the forum) is not the pod's identity, so it
212
- // writes none — the forum's own actor, or a Gateway, answers for it.
213
- if (!this.config.forum) {
214
- await discovery.writeWebfinger(this.remote, urls,
215
- wire.jrd({ handle: this.config.handle, host, actor: urls.actor, page: urls.profileHtml }));
216
- await discovery.writeHostMeta(this.remote, urls, wire.hostMeta(urls.base));
217
-
218
- const nodeinfoDocUrl = urls.home + 'ap/nodeinfo-2.0';
219
- const localPosts = this.store.getStatuses().filter(s => s.kind === 'post').length;
220
- await discovery.writeNodeinfo(this.remote, urls, {
221
- pointer: wire.nodeinfoPointer(nodeinfoDocUrl),
222
- doc: wire.nodeinfoDoc({ version: AGENT_VERSION, localPosts }),
223
- });
224
- }
225
-
226
- const actor = actorDoc; // built above, for the digest
227
- await podActor.write(this.remote, urls, actor);
228
-
229
- // FEP-1b12: the moderator roster, public — it is what a recipient
230
- // validates a group's announced moderation against.
231
- if (moderators) {
232
- await podFeatured.writeModerators(this.remote, urls,
233
- wire.orderedCollection(urls.moderators, this.config.moderators));
234
- }
235
- // The gateway policy doc: the PUBLIC data a keyless gateway reads to decide
236
- // what concerns this identity. Written only while a gateway is advertised.
237
- if (gwActive) await this.publishGatewayPolicy();
238
-
239
- // WebID → actor: the profile card lists the actor as a foaf:account.
240
- // Best-effort — a profile that cannot be read or edited does not stop the
241
- // publish, it is logged and the rest of the surface still goes up.
242
- if (this.config.forum) { /* a category is not the pod owner's account */ } else try {
243
- const wrote = await podActor.linkInWebIdProfile(this.remote, {
244
- actorUrl: urls.actor,
245
- accountName: `@${this.config.handle}@${host}`,
246
- kind: this.config.kind,
247
- // Where a Solid client posts: dokieli reads `as:outbox` off the WebID.
248
- outbox: this.clientOrigin ? `${this.clientOrigin}ap/outbox` : this.gatewayOutbox(),
249
- });
250
- if (wrote) this.log('WebID profile now lists the actor as a foaf:account');
251
- } catch (e) {
252
- this.log(`WebID profile not updated with the actor link: ${e.message}`);
253
- }
254
- // Where the account lives, in the owner's public type index — only where
255
- // they already have one; an index is made only on their yes, at sign-up.
256
- if (!this.config.forum) {
257
- await recordPlace(this.remote, urls.base, urls.home + 'ap/actor')
258
- .then((r) => { if (r === 'registered') this.log('the public type index now records where the account lives'); })
259
- .catch((e) => this.log(`type index not updated with the account's place: ${e.message}`));
260
- }
261
-
262
- // inbox: public may only Append; owner (the agent) reads + drains. A
263
- // quiesced actor keeps its name resolving but takes no more mail, so a
264
- // republish must not re-open the door.
265
- await podInbox.writeKeep(this.remote, urls);
266
- await podInbox.setPosture(this.remote, urls, this.config.quiescedAt ? 'closed' : 'open');
267
- if (this.config.quiescedAt) this.log('inbox left closed — this actor is quiesced');
268
-
269
- // notes live under a public-Read container (acl:default covers new notes).
270
- await podNotes.provisionContainer(this.remote, urls);
271
-
272
- await this.publishCollections({ ...ALL_COLLECTIONS, force });
273
- const updated = await this.announceProfileChange(actor, { force });
274
- await this.ensurePrivateAcls();
275
- const unreachable = await this.verifyPublicSurface();
276
- // Last, and merged: announceProfileChange writes this document too.
277
- //
278
- // Only when the surface came back readable. Recording it regardless meant a
279
- // publish that half-landed still matched the digest, so the NEXT save — the
280
- // one the operator makes because the first did not work — was skipped as a
281
- // no-op and reported success. The digest is a record of what is up there,
282
- // and an unreachable document is not up there.
283
- if (!unreachable.length) {
284
- this.store.write('published.json',
285
- { ...this.store.read('published.json', {}), surfaceDigest: surface });
286
- }
287
- // Named as the fediverse sees it: a fronted actor's name and host are the
288
- // front's, not the pod's.
289
- const pubName = wire.publicHandle(this.config);
290
- const pubHost = this.config.gateway?.frontActor ? new URL(this.config.gateway.frontActor).host : host;
291
- this.log(this.config.gateway?.frontActor || wire.webfingerHost(urls.base)
292
- ? `profile published: @${pubName}@${pubHost} → ${urls.actor}`
293
- : `profile published → ${urls.actor} — NOT discoverable as @${pubName}@${pubHost}: `
294
- + 'this pod is suffixed, and WebFinger is only answered for a subdomained pod');
295
- return { unreachable, updated };
296
- }
297
-
298
- // Fires when the document differs from the one last published — including
299
- // the first time, when there is nothing to differ from.
300
- //
301
- // Every call to publishProfile is already a DELIBERATE republish: setup, a
302
- // rename, an edit through the client or /config, `describe`, a key rotation,
303
- // or an actor document found missing from the pod. Starting the agent does not
304
- // call it. So the digest is not there to survive restarts — it is there for
305
- // the republish that changes nothing, which /config does whenever you save a
306
- // field the actor document does not carry.
307
- //
308
- // A silent first publish was considered and is WRONG here: it would spend a
309
- // real edit doing nothing but recording a digest, and that edit is exactly the
310
- // one whose invisibility this fixes.
311
- async announceProfileChange(actor, { force = false } = {}) {
312
- const digest = crypto.createHash('sha256').update(JSON.stringify(actor)).digest('hex').slice(0, 32);
313
- const seen = this.store.read('published.json', {});
314
- // A forced republish is the operator saying TELL THE WORLD — the digest
315
- // gate is for silent no-op saves, not for that.
316
- if (seen.actorDigest === digest && !force) return 0;
317
- this.store.write('published.json', { ...seen, actorDigest: digest, at: new Date().toISOString() });
318
- const inboxes = [...new Set(this.store.getContacts().followers
319
- .map(f => f.sharedInbox || f.inbox).filter(Boolean))];
320
- if (!inboxes.length) return 0;
321
- await this.deliverer.deliverToAll(inboxes,
322
- wire.updateActorActivity({ urls: this.urls, actor, serial: Date.now() }));
323
- this.log(`profile changed — Update delivered to ${inboxes.length} inbox(es)`);
324
- return inboxes.length;
325
- }
326
-
327
- // The mirror of ensurePrivateAcls, and the check this project lacked: these
328
- // documents MUST be readable by strangers or no server can see the actor.
329
- // A publish that dies half-way, or an ACL write that is accepted without
330
- // taking effect, is otherwise indistinguishable from success — the agent
331
- // reports itself configured and federating while nobody can find it.
332
- async verifyPublicSurface() {
333
- const { urls } = this;
334
- const targets = [
335
- // Discovery documents are the pod's, not a forum category's (see publishProfile).
336
- ...(this.config.forum ? [] : [['webfinger', urls.webfinger], ['host-meta', urls.base + '.well-known/host-meta']]),
337
- ['actor', urls.actor],
338
- ['notes', urls.notes],
339
- ['followers', urls.followers],
340
- ['following', urls.following],
341
- ['featured', urls.featured],
342
- ['outbox', urls.outbox],
343
- ];
344
- const unreachable = [];
345
- for (const [name, url] of targets) {
346
- if (!await this.publiclyReadable(url)) unreachable.push(name);
347
- }
348
- if (unreachable.length) {
349
- this.log(`FEDERATION: ${unreachable.join(', ')} not readable without credentials — `
350
- + 'other servers cannot resolve or fetch this actor');
351
- }
352
- return unreachable;
353
- }
354
-
355
- // An ACL write that silently failed — or was changed afterwards by anything
356
- // else touching the pod — leaves the private trees, signing keys included,
357
- // world-readable. bootstrap writes them once at setup and never returns, so
358
- // this runs on every connect: probe UNauthenticated, rewrite whatever
359
- // answers, and say so loudly if the rewrite does not take.
360
- async ensurePrivateAcls() {
361
- const findings = await containers.repairPrivateAcls(this.remote,
362
- [this.urls.home, this.urls.state],
363
- { isPublic: (u) => this.publiclyReadable(u) });
364
- // What a finding MEANS is ours to say; the library just reports.
365
- for (const f of findings) {
366
- this.log(`${f.url} was readable without credentials — rewriting its ACL`);
367
- if (f.error) { this.log(`SECURITY: ${f.url} is public and its ACL could not be rewritten: ${f.error}`); continue; }
368
- if (f.stillPublic) this.log(`SECURITY: ${f.url} is STILL readable without credentials — check the pod's ACLs`);
369
- }
370
- }
371
-
372
- // Deliberately credential-free: this asks what a stranger would see.
373
- // accept: */* matters — asking for turtle makes the server answer 501 on the
374
- // JSON documents (webfinger, actor), which reads as "unreachable" when the
375
- // world can in fact see them perfectly well.
376
- publiclyReadable(url) {
377
- return containers.probePublicReadability(this.probeFetch, url,
378
- { headers: { 'user-agent': USER_AGENT }, timeoutMs: HTTP_TIMEOUT_MS });
379
- }
380
-
381
- // Retire this identity for good: tell everyone who follows us to drop the
382
- // account, then leave a Tombstone where the actor was. The inbox stays
383
- // publicly Append-able on purpose — closing it would make deliveries 401,
384
- // which Mastodon treats as failure and retries, the opposite of the point.
385
- // A Delete stops well-behaved servers; anything that keeps delivering gets a
386
- // cheap 201 into a container we no longer read.
387
- async retireActor() {
388
- const { urls } = this;
389
- const contacts = this.store.getContacts();
390
- const inboxes = [...new Set(contacts.followers.map(f => f.sharedInbox || f.inbox).filter(Boolean))];
391
- const deletedAt = new Date().toISOString();
392
- await this.deliverer.deliverToAll(inboxes, wire.deleteActorActivity(urls, Date.parse(deletedAt)));
393
- await podActor.writeTombstone(this.remote, urls, wire.tombstoneDoc(urls, deletedAt, this.config.kind));
394
- this.store.setConfig({ ...this.store.getConfig(), retiredAt: deletedAt });
395
- await this.store.flush();
396
- this.log(`retired: Delete sent to ${inboxes.length} inbox(es), actor replaced with a Tombstone`);
397
- return { inboxes: inboxes.length, deletedAt };
398
- }
399
-
400
- // Stop accepting mail without giving up the name: deliveries get an immediate
401
- // 401 rather than a 201 into storage nobody will ever drain. WebFinger,
402
- // host-meta and the actor stay published, so the handle still resolves.
403
- async closeInbox() {
404
- await podInbox.setPosture(this.remote, this.urls, 'closed');
405
- const at = new Date().toISOString();
406
- this.store.setConfig({ ...this.store.getConfig(), quiescedAt: at });
407
- await this.store.flush();
408
- this.log(`inbox closed — @${this.config.handle} still resolves but accepts nothing`);
409
- return at;
410
- }
411
-
412
- // Undo closeInbox: mail flows again and the actor is no longer quiesced.
413
- async openInbox() {
414
- await podInbox.setPosture(this.remote, this.urls, 'open');
415
- const { quiescedAt, ...rest } = this.store.getConfig() || {};
416
- this.store.setConfig(rest);
417
- this.config.quiescedAt = undefined;
418
- await this.store.flush();
419
- this.log(`inbox re-opened — @${this.config.handle} is taking mail again`);
420
- }
421
-
422
- // Lock the inbox to a gateway: the public loses Append, so nothing reaches
423
- // the pod except through the gateway's verify-at-the-door. Reversible with
424
- // openInbox (public-Append) — the one-call rollback.
425
- async lockInboxToGateway(gatewayWebId) {
426
- await podInbox.setPosture(this.remote, this.urls, { gatewayWebId });
427
- this.log(`inbox locked to gateway ${gatewayWebId} — public delivery is refused`);
428
- }
429
-
430
- // Tell the fediverse the account lives somewhere else now. Well-behaved
431
- // servers migrate their followers to the target and stop delivering here.
432
- async publishMove(target) {
433
- const { urls } = this;
434
- const contacts = this.store.getContacts();
435
- const inboxes = [...new Set(contacts.followers.map(f => f.sharedInbox || f.inbox).filter(Boolean))];
436
- const at = new Date().toISOString();
437
- await this.deliverer.deliverToAll(inboxes, wire.moveActivity(urls, target, Date.parse(at)));
438
- this.config.movedTo = target; // so the republish below carries it
439
- this.store.setConfig({ ...this.store.getConfig(), movedTo: target, movedAt: at });
440
- const gw = this.config.gateway;
441
- const gwActive = gw && gw.url && gw.mode && gw.mode !== 'off';
442
- const priv = await this.privateReady() === true;
443
- const moderators = (this.config.moderators || []).length ? urls.moderators : null;
444
- await podActor.writeMoved(this.remote, urls, this.actorDocFor({
445
- priv, moderators, inbox: gwActive ? gw.url : null, movedTo: target,
446
- }));
447
- await this.store.flush();
448
- this.log(`moved to ${target}: Move sent to ${inboxes.length} inbox(es), actor now advertises movedTo`);
449
- return { inboxes: inboxes.length, target, movedAt: at };
450
- }
451
-
452
- // The gateway policy: a small PUBLIC document a keyless inbox gateway reads
453
- // to decide, at the edge, what to forward and what to drop. It carries only
454
- // public facts — the actor/followers URLs, the REAL pod inbox to forward to,
455
- // the accepted-following list (already public) and a mirror of the blocklist.
456
- // Publishing the blocklist here makes it public; that is a deliberate part of
457
- // running a gateway, surfaced to the operator in the admin UI.
458
- async publishGatewayPolicy() {
459
- const { urls } = this;
460
- const contacts = this.store.getContacts();
461
- const bl = this.store.getBlocklist();
462
- await podPolicy.write(this.remote, urls, {
463
- v: 1,
464
- actorUrl: urls.actor,
465
- followersUrl: urls.followers,
466
- followingUrl: urls.following,
467
- inboxUrl: urls.inbox, // the pod inbox the gateway forwards to
468
- notesPrefix: urls.notes,
469
- kind: this.config.kind || 'person',
470
- following: contacts.following.filter(f => f.accepted && !f.bsky).map(f => f.actor),
471
- blocklist: { domains: bl.domains || [], actors: bl.actors || [] },
472
- });
473
- }
474
-
475
- // collections.mjs
476
- publishOutbox(...a) { return collections.publishOutbox(this, ...a); }
477
- readPublishedOutbox(...a) { return collections.readPublishedOutbox(this, ...a); }
478
- publishFollowers(...a) { return collections.publishFollowers(this, ...a); }
479
- readPublishedFollowers(...a) { return collections.readPublishedFollowers(this, ...a); }
480
- publishCollections(...a) { return collections.publishCollections(this, ...a); }
481
- publishPending(...a) { return collections.publishPending(this, ...a); }
482
- publishBlocked(...a) { return collections.publishBlocked(this, ...a); }
483
- recordOutbox(...a) { return collections.recordOutbox(this, ...a); }
484
- unrecordOutbox(...a) { return collections.unrecordOutbox(this, ...a); }
485
- recordOwn(...a) { return own.recordOwn(this, ...a); }
486
- backfillLiked() { return own.backfillLiked(this); }
487
- inboxesFor(...a) { return notes.inboxesFor(this, ...a); }
488
- noteToSelf(...a) { return notes.noteToSelf(this, ...a); }
489
- updateObject(...a) { return notes.updateObject(this, ...a); }
490
- unrecordOwn(...a) { return own.unrecordOwn(this, ...a); }
491
- publishOwn(...a) { return own.publishOwn(this, ...a); }
492
- ownSettled() { return own.ownSettled(this); }
493
- publishFeatured(...a) { return collections.publishFeatured(this, ...a); }
494
-
495
- // restore.mjs
496
- reconcileFollowers(...a) { return restore.reconcileFollowers(this, ...a); }
497
- reconcileOutbox(...a) { return restore.reconcileOutbox(this, ...a); }
498
- rebuildStatuses(...a) { return restore.rebuildStatuses(this, ...a); }
499
- healStatuses(...a) { return restore.healStatuses(this, ...a); }
500
-
501
- // notes.mjs
502
- ensureMediaContainer(...a) { return notes.ensureMediaContainer(this, ...a); }
503
- _containerExists(...a) { return notes.containerExists(this, ...a); }
504
- ensurePrivateContainer(...a) { return notes.ensurePrivateContainer(this, ...a); }
505
- privateReady(...a) { return notes.privateReady(this, ...a); }
506
- _mentionsFor(...a) { return notes.mentionsFor(this, ...a); }
507
- publishNote(...a) { return notes.publishNote(this, ...a); }
508
- authorizeQuote(...a) { return notes.authorizeQuote(this, ...a); }
509
- withdrawQuote(...a) { return notes.withdrawQuote(this, ...a); }
510
- grantQuote(...a) { return notes.grantQuote(this, ...a); }
511
- publishObject(...a) { return notes.publishObject(this, ...a); }
512
- updateNote(...a) { return notes.updateNote(this, ...a); }
513
-
514
- // questions.mjs
515
- publishQuestion(...a) { return questions.publishQuestion(this, ...a); }
516
- _pollActivity(...a) { return questions.pollActivity(this, ...a); }
517
- recordVote(...a) { return questions.recordVote(this, ...a); }
518
- _pollDirty(...a) { return questions.pollDirty(this, ...a); }
519
- republishPoll(...a) { return questions.republishPoll(this, ...a); }
520
- closeDuePolls(...a) { return questions.closeDuePolls(this, ...a); }
521
- stopPolls(...a) { return questions.stopPolls(this, ...a); }
522
- }