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,582 +0,0 @@
1
- // c2s.mjs — ActivityPub client-to-server (§6): the spec's own write API,
2
- // beside the Mastodon facade. A client POSTs an activity — or a bare object,
3
- // which is wrapped in a Create (§6.2.1) — to this actor's outbox; the agent
4
- // assigns ids, applies the side-effects and delivers. Every verb lands on the
5
- // SAME helper the facade and admin surfaces use — this module re-implements
6
- // no persistence and no delivery, so one write path stays one.
7
- //
8
- // GETs on the actor and outbox are redirects to the pod's documents wherever
9
- // those are reachable, which is the Server. Standalone they are not: the actor
10
- // published there names no address a local client could write to, and on a
11
- // fronted account the published outbox address is the Gateway's write door,
12
- // which does not answer a read. So on that build both are served here, to the
13
- // client already talking to this agent.
14
- //
15
- // `dispatch` takes an activity and answers with { status, body, headers }; it
16
- // touches no request or response. The HTTP handler below is one caller. The
17
- // other is the inbox drain, handing over an activity the Gateway took at the
18
- // outbox door on the owner's behalf and stamped as theirs.
19
- //
20
- // The inbox is the exception, and has to be. Deliveries land in a container on
21
- // the pod which the drain empties as it handles each item, so reading that
22
- // container tells the owner only what has not been dealt with yet. What was
23
- // actually received is whole only in the archive, so §5.2's "the owner can
24
- // read their own inbox" is served from there, by this agent, to the owner
25
- // alone.
26
-
27
- import * as social from '../core/social.mjs';
28
- import * as wire from '../core/wire.mjs';
29
- import { readLenient } from '../core/as2.mjs';
30
- import { safeSlug } from '../core/publisher/notes.mjs';
31
-
32
- const MAX_BODY = 512 * 1024; // same ceiling the inbox drain enforces
33
-
34
- // How many archived items one page of the inbox will read. A page is one
35
- // month, and a month with more than this is served short rather than costing
36
- // the pod an unbounded read; the log says when that happened.
37
- const MAX_INBOX_PAGE = 500;
38
-
39
- // How many recorded activities one page of the locally-served outbox carries.
40
- const MAX_OUTBOX_PAGE = 50;
41
-
42
- // §6 names activities; anything else with a type is an object to wrap.
43
- const ACTIVITY_TYPES = new Set([
44
- 'Create', 'Update', 'Delete', 'Follow', 'Like', 'Announce', 'Undo',
45
- 'Block', 'Add', 'Remove', 'Accept', 'Reject', 'Move',
46
- ]);
47
-
48
- const arr = (v) => (v === undefined || v === null ? [] : Array.isArray(v) ? v : [v]);
49
- const idOf = (v) => (typeof v === 'string' ? v : v?.id || null);
50
-
51
- function readBody(req) {
52
- return new Promise((resolve, reject) => {
53
- let size = 0;
54
- const chunks = [];
55
- req.on('data', (c) => {
56
- size += c.length;
57
- if (size > MAX_BODY) { reject(new Error('body too large')); req.destroy(); return; }
58
- chunks.push(c);
59
- });
60
- req.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
61
- req.on('error', reject);
62
- });
63
- }
64
-
65
- export class C2S {
66
- constructor({ agent, log = console.log, auth, scheme = null, mount = '' }) {
67
- this.agent = agent;
68
- this.log = log;
69
- this.auth = auth;
70
- // What this agent calls itself, for the actor it serves a local client.
71
- // The same two values oidc-auth rebuilds the DPoP `htu` from, so the two
72
- // surfaces agree on the address a client just used.
73
- this.scheme = scheme;
74
- this.mount = mount;
75
- }
76
-
77
- get store() { return this.agent.store; }
78
- get urls() { return this.agent.publisher?.urls; }
79
-
80
- send(res, status, obj, headers = {}) {
81
- res.writeHead(status, {
82
- 'content-type': 'application/json; charset=utf-8',
83
- 'cache-control': 'no-store',
84
- ...headers,
85
- });
86
- res.end(JSON.stringify(obj));
87
- return true;
88
- }
89
-
90
- byIri(iri) {
91
- return iri ? this.store.getStatuses().find((s) => s.noteId === iri) : null;
92
- }
93
-
94
- /** The months the archive holds, newest first. One container listing. */
95
- async archiveMonths() {
96
- const archive = this.agent.intake?.archive;
97
- if (!archive) return [];
98
- const { names } = await archive.list('');
99
- return (names || [])
100
- .map((n) => n.replace(/\/$/u, ''))
101
- .filter((n) => /^\d{4}-\d{2}$/u.test(n))
102
- .sort()
103
- .reverse();
104
- }
105
-
106
- /**
107
- * The owner's own inbox, §5.2. Paged by month because that is how the
108
- * archive is stored, so a page costs one listing and a read per item and no
109
- * page is dearer for another month being large.
110
- */
111
- async sendInbox(res, url) {
112
- const id = `${this.urls.base}ap/inbox`;
113
- const page = url?.searchParams?.get('page') || null;
114
- let months;
115
- try {
116
- months = await this.archiveMonths();
117
- } catch (e) {
118
- return this.send(res, 502, { error: `the archive could not be read: ${e.message}` });
119
- }
120
-
121
- if (!page) {
122
- if (!months.length && this.store.getConfig()?.archiveInbox === false) {
123
- this.log('inbox read: nothing to show — this identity does not keep what it receives');
124
- }
125
- return this.send(res, 200, {
126
- '@context': wire.AS_CTX, id, type: 'OrderedCollection',
127
- ...(months.length ? { first: `${id}?page=${months[0]}` } : { orderedItems: [] }),
128
- });
129
- }
130
- if (!/^\d{4}-\d{2}$/u.test(page)) {
131
- return this.send(res, 400, { error: 'page names a month, written 2026-09' });
132
- }
133
-
134
- const archive = this.agent.intake?.archive;
135
- let names = [];
136
- try {
137
- // The trailing slash matters: without it this names a document, not the
138
- // container, and a pod answers about the wrong thing.
139
- ({ names } = await archive.list(`${page}/`));
140
- } catch (e) {
141
- return this.send(res, 502, { error: `the archive could not be read: ${e.message}` });
142
- }
143
- const files = (names || []).filter((n) => n.endsWith('.json')).sort();
144
- if (files.length > MAX_INBOX_PAGE) {
145
- this.log(`inbox read: ${page} holds ${files.length} items; serving the first ${MAX_INBOX_PAGE}`);
146
- }
147
- const kept = [];
148
- for (const file of files.slice(0, MAX_INBOX_PAGE)) {
149
- // Read as written: these records are JSON-LD, and the default read asks
150
- // turtle-first, which a server is free to answer with turtle.
151
- const read = await archive.read(`${page}/${file}`, { accept: '*/*' });
152
- if (!read?.ok || !read.body) continue;
153
- try {
154
- const record = JSON.parse(read.body);
155
- // The record wraps the bytes as they arrived; the activity is those
156
- // bytes, not a retelling of them.
157
- kept.push({ at: record.receivedAt || '', activity: JSON.parse(record.raw) });
158
- } catch { /* a record that will not parse is not one that can be served */ }
159
- }
160
- kept.sort((a, b) => String(b.at).localeCompare(String(a.at)));
161
- const older = months.filter((m) => m < page)[0] || null;
162
- return this.send(res, 200, {
163
- '@context': wire.AS_CTX,
164
- id: `${id}?page=${page}`,
165
- type: 'OrderedCollectionPage',
166
- partOf: id,
167
- ...(older ? { next: `${id}?page=${older}` } : {}),
168
- orderedItems: kept.map((k) => k.activity),
169
- });
170
- }
171
-
172
- // The actor a client-to-server client on this machine reads. The same
173
- // document the pod holds, but for the fields naming where a write goes —
174
- // the outbox and the two OAuth endpoints — and for `id`, which names this
175
- // address so the client files its credential here and comes back. The pod's
176
- // id stays on as an alias; the two are one identity.
177
- // What this agent calls itself, to the client that just asked. The Host
178
- // passed the Authorities firewall before any route ran, so whichever alias
179
- // the client used is one this agent answers on. The scheme is read off the
180
- // connection unless the caller named one: only the Server passes a scheme,
181
- // and standalone this listener is its own TLS.
182
- localOrigin(req) {
183
- const host = req.headers.host;
184
- if (!host) return null;
185
- const scheme = this.scheme
186
- || (req.socket?.encrypted || req.headers['x-forwarded-proto'] === 'https' ? 'https' : 'http');
187
- return `${scheme}://${host}${this.mount}/`;
188
- }
189
-
190
- /**
191
- * The owner's own outbox, from what this agent recorded. The pod holds the
192
- * published copy and it stays the canonical one; this is served only where
193
- * that copy cannot be read by the client asking.
194
- */
195
- sendLocalOutbox(res, url, origin) {
196
- const id = `${origin}ap/outbox`;
197
- const outbox = this.store.read('outbox.json', []); // newest first
198
- const page = url?.searchParams?.get('page') || null;
199
- const ct = { 'content-type': 'application/activity+json; charset=utf-8' };
200
- if (!page) {
201
- return this.send(res, 200, {
202
- '@context': wire.AS_CTX, id, type: 'OrderedCollection', totalItems: outbox.length,
203
- ...(outbox.length ? { first: `${id}?page=1` } : { orderedItems: [] }),
204
- }, ct);
205
- }
206
- const n = Number(page);
207
- if (!Number.isInteger(n) || n < 1) {
208
- return this.send(res, 400, { error: 'page is a whole number from 1' });
209
- }
210
- const start = (n - 1) * MAX_OUTBOX_PAGE;
211
- const items = outbox.slice(start, start + MAX_OUTBOX_PAGE);
212
- return this.send(res, 200, {
213
- '@context': wire.AS_CTX,
214
- id: `${id}?page=${n}`,
215
- type: 'OrderedCollectionPage',
216
- partOf: id,
217
- ...(start + items.length < outbox.length ? { next: `${id}?page=${n + 1}` } : {}),
218
- // A post is recorded by its object id and published as the Create beside
219
- // it — the same mapping the pod's copy uses, from the same helper.
220
- orderedItems: items.map(wire.outboxWireItem),
221
- }, ct);
222
- }
223
-
224
- sendLocalActor(req, res, origin) {
225
- const pub = this.agent.publisher;
226
- const cfg = pub.config || {};
227
- const gw = cfg.gateway;
228
- const gwActive = gw && gw.url && gw.mode && gw.mode !== 'off';
229
- const doc = pub.actorDocFor({
230
- inbox: gwActive ? gw.url : (cfg.inboxUrl || null),
231
- clientOrigin: origin,
232
- });
233
- // A client files its credential under the id it reads here and comes back
234
- // to that address for everything after, so this document has to name
235
- // itself. The pod's id stays on as an alias rather than being dropped —
236
- // the two are one identity. Nothing published ever carries this id: every
237
- // activity the dispatcher builds names the pod's actor.
238
- const canonical = doc.id;
239
- doc.id = `${origin}ap/actor`;
240
- doc.alsoKnownAs = [...new Set([...(doc.alsoKnownAs || []), canonical])];
241
- const body = JSON.stringify(doc);
242
- res.writeHead(200, {
243
- 'content-type': 'application/activity+json; charset=utf-8',
244
- 'cache-control': 'no-store',
245
- 'content-length': Buffer.byteLength(body),
246
- });
247
- res.end(req.method === 'HEAD' ? undefined : body);
248
- return true;
249
- }
250
-
251
- async handle(req, res, pathname, url) { // eslint-disable-line no-unused-vars
252
- if (pathname !== '/ap/outbox' && pathname !== '/ap/actor' && pathname !== '/ap/inbox') return false;
253
- if (req.method === 'OPTIONS') {
254
- // Accept-Post is what a client such as dokieli reads to choose a format;
255
- // naming JSON only is what makes it send JSON-LD rather than HTML.
256
- res.writeHead(204, pathname === '/ap/inbox' ? { allow: 'GET, OPTIONS' }
257
- : { allow: 'GET, POST, OPTIONS', 'accept-post': 'application/ld+json, application/activity+json' });
258
- res.end(); return true;
259
- }
260
- if (!this.agent.configured() || !this.urls) {
261
- return this.send(res, 409, { error: 'agent not configured' });
262
- }
263
- if (pathname === '/ap/inbox') {
264
- if (req.method !== 'GET' && req.method !== 'HEAD') {
265
- return this.send(res, 405, { error: "deliveries go to this actor's inbox on the pod, which the "
266
- + 'actor document names; this address is the owner reading their own' });
267
- }
268
- const reader = await this.auth(req, pathname);
269
- if (!reader.ok) return this.send(res, reader.status, { error: reader.error }, reader.headers || {});
270
- return this.sendInbox(res, url);
271
- }
272
- if (req.method === 'GET' || req.method === 'HEAD') {
273
- // Where the client surface answers on an address a stranger can reach,
274
- // the pod's copies already name it and are the documents. Standalone
275
- // they are not reachable by the client asking, so it is answered here.
276
- const local = this.agent.publisher?.clientOrigin ? null : this.localOrigin(req);
277
- if (local && pathname === '/ap/actor') return this.sendLocalActor(req, res, local);
278
- if (local && pathname === '/ap/outbox') return this.sendLocalOutbox(res, url, local);
279
- // The pod's copy is the document; send the reader there.
280
- const target = pathname === '/ap/actor' ? this.urls.actor : this.urls.outbox;
281
- res.writeHead(303, { location: target, 'cache-control': 'no-store' });
282
- res.end();
283
- return true;
284
- }
285
- if (pathname !== '/ap/outbox' || req.method !== 'POST') {
286
- return this.send(res, 405, { error: 'POST the outbox; GET redirects to the pod' });
287
- }
288
-
289
- const who = await this.auth(req, pathname);
290
- if (!who.ok) return this.send(res, who.status, { error: who.error }, who.headers || {});
291
-
292
- // A viewer (another agent holds the lease) may not act — but the user
293
- // acting HERE outranks an idle active agent elsewhere, exactly as on the
294
- // facade: a write attempt claims the lease, and only a failed claim 503s.
295
- if (this.agent.viewer) {
296
- const took = await this.agent.requestTakeover?.();
297
- if (!took) return this.send(res, 503, { error: 'another agent is active for this pod — takeover failed, try again' });
298
- }
299
-
300
- // Read as JSON-LD, so a client may send its activity with whatever context
301
- // it likes and still be understood. What is read is the GRAPH: `dispatch`
302
- // takes decisions from it and the publisher builds the document that is
303
- // actually posted, so nothing a client sent is republished verbatim and a
304
- // term it aliased still means what it says.
305
- // `raw` is the document as the client wrote it. The graph is what
306
- // decisions are taken from; the bytes are what an object that is stored
307
- // as sent (publishObject) is stored from — a read against our contexts
308
- // rewrites a stranger's terms to full IRIs, which is right for reading and
309
- // wrong for keeping.
310
- let activity; let raw = null;
311
- try {
312
- const body = await readBody(req);
313
- try { raw = JSON.parse(body); } catch { raw = null; }
314
- const read = await readLenient(body);
315
- activity = read.view ?? read.doc;
316
- } catch (e) {
317
- return this.send(res, 400, { error: `unreadable body: ${e.message}` });
318
- }
319
- if (!activity || typeof activity !== 'object' || Array.isArray(activity) || !activity.type) {
320
- return this.send(res, 400, { error: 'a typed ActivityStreams object is required' });
321
- }
322
- // The name the client asks for its new document (LDP's Slug), taken when
323
- // it is plain and free — it is what lets the client know the address of
324
- // what it made before anything answers.
325
- const slug = safeSlug(req.headers.slug) || null;
326
- const r = await this.dispatch(activity, { slug, raw });
327
- return this.send(res, r.status, r.body, r.headers);
328
- }
329
-
330
- // Addressing → the facade's four visibilities, inverting the table the
331
- // composer writes (wire.addressing). Nothing stated is a public post — what
332
- // every client means by a post with no audience chosen, and what a client
333
- // that never addresses (dokieli's annotations) needs.
334
- visibilityOf(activity, object) {
335
- const to = arr(activity.to ?? object?.to).map(idOf);
336
- const cc = arr(activity.cc ?? object?.cc).map(idOf);
337
- if (!to.length && !cc.length) return 'public';
338
- if (to.includes(wire.PUBLIC)) return 'public';
339
- if (cc.includes(wire.PUBLIC)) return 'unlisted';
340
- if (to.includes(this.urls.followers)) return 'private';
341
- return 'direct';
342
- }
343
-
344
- async dispatch(activity, { slug = null, raw = null } = {}) {
345
- const reply = (status, body, headers = {}) => ({ status, body, headers });
346
- if (!activity || typeof activity !== 'object' || Array.isArray(activity) || !activity.type) {
347
- return reply(400, { error: 'a typed ActivityStreams object is required' });
348
- }
349
- // A bare object arrives without an activity around it; the server supplies
350
- // the Create (§6.2.1), carrying the object's own addressing up onto it.
351
- if (!ACTIVITY_TYPES.has(activity.type)) {
352
- activity = { type: 'Create', object: activity, to: activity.to, cc: activity.cc };
353
- }
354
- try {
355
- return await this._dispatch(activity, { slug, raw, reply });
356
- } catch (e) {
357
- this.log(`c2s ${activity?.type}: ${e.message}`);
358
- return reply(422, { error: e.message || String(e) });
359
- }
360
- }
361
-
362
- // The actors a client addressed by id, beyond the audience words: the ones
363
- // in to/cc are listed and delivered to; the ones in bto/bcc are delivered
364
- // to and never listed (§6).
365
- addressedActors(activity, object) {
366
- const pick = (...fields) => [...new Set(fields.flatMap((f) => arr(activity[f] ?? object?.[f]).map(idOf)))]
367
- .filter((a) => typeof a === 'string' && /^https?:\/\//u.test(a)
368
- && a !== wire.PUBLIC && a !== this.urls.followers && a !== this.urls.actor);
369
- return { also: pick('to', 'cc'), deliverTo: pick('bto', 'bcc') };
370
- }
371
-
372
- async _dispatch(activity, { slug, raw, reply }) {
373
- const agent = this.agent;
374
- const object = typeof activity.object === 'object' && activity.object !== null
375
- ? activity.object : null;
376
- const objectId = idOf(activity.object);
377
-
378
- switch (activity.type) {
379
- case 'Create': {
380
- const makes = object?.type || 'Note';
381
- if (!object) return reply(422, { error: 'a Create carries the object it creates' });
382
- const visibility = this.visibilityOf(activity, object);
383
- const { also, deliverTo } = this.addressedActors(activity, object);
384
- // Not a Note and not a poll: stored as sent, under this actor, and
385
- // the Create around it delivered — a Web Annotation, for one.
386
- if (makes !== 'Note' && makes !== 'Question') {
387
- const asSent = raw && typeof raw === 'object' && !Array.isArray(raw)
388
- ? (ACTIVITY_TYPES.has(raw.type) ? (raw.object && typeof raw.object === 'object' ? raw.object : null) : raw)
389
- : null;
390
- const made = await agent.publisher.publishObject(asSent || object, { visibility, slug, also, deliverTo });
391
- return reply(201, { id: made.createId, object: made.id }, { location: made.createId });
392
- }
393
- // `source.content` is the client's plain text when it sends one; bare
394
- // `content` is TREATED as plain text and escaped — markup survives as
395
- // visible characters rather than as markup. Documented v1 limit.
396
- const text = String(object.source?.content ?? object.content ?? '');
397
- if (!text.trim()) return reply(422, { error: 'the note has no content' });
398
-
399
- // A Question is a poll: the choices are in oneOf (pick one) or anyOf
400
- // (pick several), each naming itself, and endTime is when it shuts.
401
- if (makes === 'Question') {
402
- const one = arr(object.oneOf);
403
- const many = arr(object.anyOf);
404
- const titles = (one.length ? one : many).map((c) => String(c?.name ?? '').trim()).filter(Boolean);
405
- try {
406
- const question = await agent.publisher.publishQuestion(text, {
407
- options: titles,
408
- multiple: !one.length && many.length > 0,
409
- expiresAt: object.endTime || null,
410
- inReplyTo: idOf(object.inReplyTo) || undefined,
411
- visibility,
412
- spoilerText: object.summary || null,
413
- sensitive: object.sensitive === true,
414
- });
415
- return reply(201,
416
- { id: wire.createActivityId(question.id), object: question.id },
417
- { location: wire.createActivityId(question.id) });
418
- } catch (e) {
419
- return reply(422, { error: e.message });
420
- }
421
- }
422
-
423
- const attachments = arr(object.attachment).map((a) => ({
424
- url: a?.url, mediaType: a?.mediaType,
425
- ...(a?.name ? { description: a.name } : {}),
426
- })).filter((a) => a.url);
427
- const note = await agent.publisher.publishNote(text, {
428
- inReplyTo: idOf(object.inReplyTo) || undefined,
429
- attachments,
430
- visibility,
431
- spoilerText: object.summary || null,
432
- sensitive: object.sensitive === true,
433
- slug, also, deliverTo,
434
- });
435
- return reply(201, { id: wire.createActivityId(note.id), object: note.id },
436
- { location: wire.createActivityId(note.id) });
437
- }
438
-
439
- case 'Update': {
440
- if (objectId === this.urls.actor) {
441
- return reply(422, { error: 'edit the profile on the admin surface; actor updates are not taken here' });
442
- }
443
- const s = this.byIri(objectId);
444
- if (!s) return reply(404, { error: 'no such note here' });
445
- if (s.actor !== this.urls.actor || s.kind !== 'post') {
446
- return reply(403, { error: 'not your note' });
447
- }
448
- const text = String(object?.source?.content ?? object?.content ?? '');
449
- if (!text.trim()) return reply(422, { error: 'the edit has no content' });
450
- const attachments = object?.attachment !== undefined
451
- ? arr(object.attachment).map((a) => ({
452
- url: a?.url, mediaType: a?.mediaType,
453
- ...(a?.name ? { description: a.name } : {}),
454
- })).filter((a) => a.url)
455
- : null;
456
- await agent.publisher.updateNote(s, {
457
- content: text, spoilerText: object?.summary || null,
458
- sensitive: object?.sensitive === undefined ? null : object.sensitive === true, attachments,
459
- });
460
- return reply(200, { ok: true, object: s.noteId });
461
- }
462
-
463
- case 'Delete': {
464
- if (objectId === this.urls.actor) {
465
- return reply(422, { error: 'retiring the actor is done on the admin surface, where it asks twice' });
466
- }
467
- const s = this.byIri(objectId);
468
- if (!s) return reply(404, { error: 'no such note here' });
469
- if (s.actor !== this.urls.actor || s.kind !== 'post') {
470
- return reply(403, { error: 'not your note' });
471
- }
472
- const r = await social.deleteNote(agent, s);
473
- if (!r.ok) return reply(502, { error: r.error, stillPublished: r.stillPublished });
474
- return reply(200, { ok: true });
475
- }
476
-
477
- case 'Follow': {
478
- if (!objectId) return reply(400, { error: 'whom? object must name an actor' });
479
- // An acct: form or bare handle resolves through WebFinger; an https
480
- // IRI is fetched directly.
481
- if (/^acct:|^@|^[^/@]+@[^/@]+$/.test(objectId) && !/^https?:/.test(objectId)) {
482
- const r = await social.followHandle(agent, objectId.replace(/^acct:/, ''));
483
- const rec = this.store.getContacts().following.find((f) => f.actor === r.actor);
484
- return reply(201, { id: rec?.followActivity?.id, object: r.actor },
485
- rec?.followActivity?.id ? { location: rec.followActivity.id } : {});
486
- }
487
- const doc = await social.followActor(agent, objectId);
488
- const rec = this.store.getContacts().following.find((f) => f.actor === doc.id);
489
- return reply(201, { id: rec?.followActivity?.id, object: doc.id },
490
- rec?.followActivity?.id ? { location: rec.followActivity.id } : {});
491
- }
492
-
493
- case 'Like': {
494
- const s = this.byIri(objectId);
495
- if (!s) return reply(422, { error: 'that note is not held here — like what the timeline holds' });
496
- const updated = await social.favourite(agent, s);
497
- return reply(201, { id: updated.likeActivity?.id, object: s.noteId },
498
- updated.likeActivity?.id ? { location: updated.likeActivity.id } : {});
499
- }
500
-
501
- case 'Announce': {
502
- const s = this.byIri(objectId);
503
- if (!s) return reply(422, { error: 'that note is not held here — boost what the timeline holds' });
504
- const updated = await social.reblog(agent, s);
505
- return reply(201, { id: updated.announceActivity?.id, object: s.noteId },
506
- updated.announceActivity?.id ? { location: updated.announceActivity.id } : {});
507
- }
508
-
509
- case 'Undo': {
510
- // What is being unsaid — by the inner activity's id when the client
511
- // sends one, by its type+object when it re-states it instead.
512
- const inner = object;
513
- const innerId = idOf(activity.object);
514
- if (inner?.type === 'Block') {
515
- const target = idOf(inner.object);
516
- if (!target) return reply(400, { error: 'unblock whom?' });
517
- await social.unblockActor(agent, target);
518
- return reply(200, { ok: true, object: target });
519
- }
520
- const statuses = this.store.getStatuses();
521
- let s = innerId ? statuses.find((x) => x.likeActivity?.id === innerId) : null;
522
- if (!s && inner?.type === 'Like') s = this.byIri(idOf(inner.object));
523
- if (s?.favourited) {
524
- const updated = await social.unfavourite(agent, s);
525
- return reply(200, { ok: true, object: updated.noteId });
526
- }
527
- s = innerId ? statuses.find((x) => x.announceActivity?.id === innerId) : null;
528
- if (!s && inner?.type === 'Announce') s = this.byIri(idOf(inner.object));
529
- if (s?.reblogged) {
530
- const updated = await social.unreblog(agent, s);
531
- return reply(200, { ok: true, object: updated.noteId });
532
- }
533
- const following = this.store.getContacts().following;
534
- const rec = following.find((f) => f.followActivity?.id === innerId)
535
- || (inner?.type === 'Follow' ? following.find((f) => f.actor === idOf(inner.object)) : null);
536
- if (rec) {
537
- await social.unfollowActor(agent, rec.actor);
538
- return reply(200, { ok: true, object: rec.actor });
539
- }
540
- return reply(422, { error: 'nothing here matches what that Undo names' });
541
- }
542
-
543
- case 'Block': {
544
- if (!objectId) return reply(400, { error: 'block whom? object must name an actor' });
545
- await social.blockActor(agent, objectId);
546
- return reply(200, { ok: true, object: objectId });
547
- }
548
-
549
- case 'Add':
550
- case 'Remove': {
551
- // The one collection a client may edit is the pins (§7.6/§7.9 in the
552
- // other direction): target must be the featured collection.
553
- if (idOf(activity.target) !== this.urls.featured) {
554
- return reply(422, { error: 'the featured collection is the one Add/Remove edits here' });
555
- }
556
- const s = this.byIri(objectId);
557
- if (!s) return reply(404, { error: 'no such note here' });
558
- const updated = await social.pinStatus(agent, s, activity.type === 'Add');
559
- return reply(200, { ok: true, object: updated.noteId, pinned: !!updated.pinned });
560
- }
561
-
562
- case 'Accept':
563
- case 'Reject': {
564
- // Answering a held follow request: the object is the Follow (or the
565
- // requester). Which request is meant comes from the Follow's actor.
566
- const requester = object?.actor ? idOf(object.actor) : objectId;
567
- if (!requester) return reply(400, { error: 'whose request? object must name the Follow or its actor' });
568
- const r = activity.type === 'Accept'
569
- ? await social.admitRequest(agent, requester).catch((e) => ({ error: e.message }))
570
- : await social.refuseRequest(agent, requester).catch((e) => ({ error: e.message }));
571
- if (r.error) return reply(404, { error: r.error });
572
- return reply(200, { ok: true, object: requester });
573
- }
574
-
575
- case 'Move':
576
- return reply(422, { error: 'moving the account is done on the admin surface, where it asks twice' });
577
-
578
- default:
579
- return reply(422, { error: `no handler for ${activity.type}` });
580
- }
581
- }
582
- }
@@ -1,87 +0,0 @@
1
- // localapi.mjs — talking to an agent on this machine.
2
- //
3
- // The agent serves https and nothing else, on the one port it was given. Its
4
- // certificate is signed by this install's own certificate authority, which no
5
- // public trust store knows about, so a plain fetch() would refuse it. Every
6
- // caller here is on loopback reaching a listener this install started, so the
7
- // answer is to trust that one authority — never to skip verification.
8
- //
9
- // Returns a small fetch-shaped result ({ status, ok, text(), json() }) so call
10
- // sites read the way they did when this was cleartext http.
11
-
12
- import https from 'node:https';
13
- import fs from 'node:fs';
14
- import path from 'node:path';
15
- import { rootOf, apRoot } from '../device/home.mjs';
16
- import { certPaths } from '../device/certs.mjs';
17
-
18
- /**
19
- * The authorities worth offering for a loopback call: this install's, and the
20
- * machine's default install's. The well-known door on 8030 is held by
21
- * whichever agent got there first, which may belong to a different root than
22
- * the one this command is acting in — so both are candidates. An empty list
23
- * means no certificate has been minted anywhere, and the call will fail the
24
- * way any unreachable agent does.
25
- */
26
- export function localCa(home) {
27
- const read = (root) => {
28
- try { return fs.readFileSync(certPaths(path.join(root, 'certs')).caCert); } catch { return null; }
29
- };
30
- // This install's own authority decides, and alone: every install names its
31
- // authority the same thing, so offering two lets the verifier match by name
32
- // and then fail on the signature. Only an install with no authority of its
33
- // own falls back to the machine's default — that is the case of a command
34
- // run outside any install reaching the well-known door.
35
- const mine = home ? read(rootOf(home)) : null;
36
- if (mine) return [mine];
37
- try { const fallback = read(apRoot()); if (fallback) return [fallback]; } catch { /* no home dir */ }
38
- return null;
39
- }
40
-
41
- /**
42
- * One request to an agent on this machine. `home` names any identity under the
43
- * install, which is how the CA is found; `port` is the agent's own port.
44
- */
45
- export async function localFetch(home, port, pathname, opts = {}) {
46
- const res = await once(home, port, pathname, opts);
47
- // The well-known door answers with a redirect to whichever agent holds it,
48
- // so a probe that lands there has to follow it the way fetch() would.
49
- if (opts.redirect === 'manual' || res.status < 300 || res.status >= 400) return res;
50
- const to = res.headers?.location;
51
- if (!to) return res;
52
- const target = new URL(to, `https://localhost:${port}`);
53
- return once(home, Number(target.port) || 443, target.pathname + target.search, opts);
54
- }
55
-
56
- function once(home, port, pathname, { method = 'GET', headers = {}, body, timeout = 0 } = {}) {
57
- const ca = localCa(home);
58
- return new Promise((resolve, reject) => {
59
- const req = https.request({
60
- host: 'localhost', port, path: pathname, method,
61
- // One-shot calls: a kept-alive socket holds the agent's listener open, so
62
- // a shutdown asked for over this channel would never finish.
63
- headers: { connection: 'close', ...headers },
64
- ...(ca ? { ca } : {}),
65
- // The certificate carries localhost and *.localhost; on some systems
66
- // "localhost" resolves to ::1 first, which the name check still covers.
67
- servername: 'localhost',
68
- }, (res) => {
69
- const chunks = [];
70
- res.on('data', (c) => chunks.push(c));
71
- res.on('end', () => {
72
- const text = Buffer.concat(chunks).toString('utf8');
73
- resolve({
74
- status: res.statusCode,
75
- ok: res.statusCode >= 200 && res.statusCode < 300,
76
- headers: res.headers,
77
- text: async () => text,
78
- json: async () => JSON.parse(text),
79
- });
80
- });
81
- });
82
- req.on('error', reject);
83
- if (timeout) req.setTimeout(timeout, () => req.destroy(new Error('timed out')));
84
- if (body !== undefined && body !== null) req.write(body);
85
- req.end();
86
- });
87
- }