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,412 +0,0 @@
1
- // setup.mjs — the first run and the process: setup driven from the page,
2
- // a new actor in a new home, starting a stopped one, updating and
3
- // restarting, moving the private half, and stopping this process.
4
-
5
- import fs from 'node:fs';
6
- import path from 'node:path';
7
- import os from 'node:os';
8
- import { spawn } from 'node:child_process';
9
- import { pathToFileURL } from 'node:url';
10
- import { isCrossSiteNavigation } from '../../../shared/guard.mjs';
11
- import { identityHomes, rootOf, tildify, writeJsonAtomic } from '../../home.mjs';
12
- import { copyPrivateHalf, isCurrent, CURRENT_LAYOUT } from '../../migrate.mjs';
13
- import { insecureUrlReason } from '../../../shared/safefetch.mjs';
14
- import { newRun, preflight, runSetup, setupInputError, hasCredential, credentialPath } from '../../setup.mjs';
15
- import { podLayout } from '../../../pod/root.mjs';
16
- import { portFree, freePortFrom } from '../../ports.mjs';
17
- import { yieldDirectory } from '../../../gateway/directory.mjs';
18
- import { localFetch } from '../../../client/localapi.mjs';
19
- import { projectRoot, SETUP_PAGE } from '../static.mjs';
20
- import { secureOrigin } from '../origins.mjs';
21
-
22
- // The signup carry-over: gateway and identity parameters a signup page
23
- // recorded for this install (written by the installer, consumed by the first
24
- // setup run). Read fresh each time — the file is deleted once consumed.
25
- function readFirstRun() {
26
- const file = process.env.AP_FIRST_RUN;
27
- if (!file) return null;
28
- try {
29
- const fr = JSON.parse(fs.readFileSync(file, 'utf8'));
30
- return fr && typeof fr === 'object' ? fr : null;
31
- } catch { return null; }
32
- }
33
-
34
- // The same config shape the `gateway` CLI command writes.
35
- function gatewayConfigFrom(fr) {
36
- if (!fr?.gateway || !fr?.secret) return null;
37
- const target = String(fr.gateway);
38
- return fr.fronted
39
- ? { url: target.replace(/ap\/actor$/, 'ap/inbox/'), frontActor: target,
40
- mode: 'shadow', hmacSecret: String(fr.secret) }
41
- : { url: target.replace(/\/?$/, '/'), mode: 'shadow', hmacSecret: String(fr.secret) };
42
- }
43
-
44
- export async function get(p, ctx, req, res, url) { // eslint-disable-line no-unused-vars
45
- const { agent, log, allowed, embedded, port, publicOrigin, versionOnDisk, isGroup } = ctx; // eslint-disable-line no-unused-vars
46
- const json = (res, status, obj) => { ctx.json(res, status, obj); return true; };
47
-
48
- // ---- setup and configuration, for the pages under /admin/ ----
49
- // These answer for a group too: a group is set up in the browser like
50
- // anything else, and it has a display name to change.
51
- if (req.method === 'GET' && p === '/setup/state') {
52
- const home = agent.home || null;
53
- const held = home && hasCredential(home) ? agent.readCredential?.() : null;
54
- return json(res, 200, {
55
- hasCredential: !!(home && hasCredential(home)),
56
- configured: agent.configured(),
57
- // Not the same question: a crashed setup leaves a credential with no
58
- // actor behind it, and that is finishable without minting a second.
59
- resumable: !!(home && hasCredential(home)) && !agent.configured(),
60
- running: ctx.setup.run?.phase === 'running',
61
- phase: ctx.setup.run?.phase || 'idle',
62
- home,
63
- port,
64
- handle: agent.store.getConfig()?.handle || allowed.label || null,
65
- kind: agent.store.getConfig()?.kind || null,
66
- origins: {
67
- loopback: publicOrigin
68
- || `https://localhost:${port}/`,
69
- named: publicOrigin
70
- ? null
71
- : allowed.label
72
- ? (true
73
- ? `https://${allowed.label}.localhost:${port}/`
74
- : `http://${allowed.label}.localhost:${port}/`)
75
- : null,
76
- },
77
- // Set in the environment, the password never needs to reach the page.
78
- passwordSupplied: !!process.env.AP_PASSWORD,
79
- identity: held ? { pod: held.remotePod, issuer: held.issuerOrigin, root: held.root || null } : null,
80
- defaults: { issuer: 'https://solidcommunity.net', port: 8030 },
81
- // What a signup page arranged for this install, so the form opens
82
- // pre-filled. The receipt secret stays out of it — the /setup run
83
- // reads that from the file itself.
84
- firstRun: (() => {
85
- if (agent.configured()) return null;
86
- const fr = readFirstRun();
87
- if (!fr?.handle) return null;
88
- let gatewayHost = null;
89
- try { gatewayHost = new URL(fr.gateway).host; } catch { /* no gateway named */ }
90
- return { pod: fr.pod || null, issuer: fr.issuer || null, handle: String(fr.handle),
91
- kind: fr.kind === 'group' ? 'group' : 'person', fronted: !!fr.fronted, gatewayHost };
92
- })(),
93
- });
94
- }
95
- if (req.method === 'GET' && p === '/setup/progress') {
96
- return json(res, 200, ctx.setup.run || { phase: 'idle', steps: [], error: null, result: null });
97
- }
98
-
99
- return false;
100
- }
101
-
102
- export async function post(p, body, ctx, req, res) { // eslint-disable-line no-unused-vars
103
- const { agent, log, allowed, embedded, port, publicOrigin, versionOnDisk, isGroup } = ctx; // eslint-disable-line no-unused-vars
104
- const json = (res, status, obj) => { ctx.json(res, status, obj); return true; };
105
- switch (p) {
106
- // Stop an agent whose pidfile is gone and which no terminal owns
107
- // (backgrounded, orphaned by a closed shell). It used to sit ABOVE both
108
- // the configured() gate and the isLocal one, so it was the only
109
- // state-changing route that needed neither — reachable from any host
110
- // AP_ALLOWED_HOSTS named, on an agent that had never been set up.
111
- case '/shutdown': {
112
- json(res, 200, { ok: true, stopping: process.pid });
113
- setTimeout(() => process.kill(process.pid, 'SIGTERM'), 50);
114
- return true;
115
- }
116
- // ---- setup, driven by the page at /admin/setup/ ----
117
- case '/setup/check': {
118
- const pre = preflight(body);
119
- // For a new pod, ask the provider where it puts pods, so the page can
120
- // show the Gateway address before the pod exists. A provider on paths
121
- // means the address must live at the Gateway; the run decides for real.
122
- if (pre.ok && body.mode === 'new' && body.shape !== 'front') {
123
- const layout = await podLayout(fetch, body.issuer || '').catch(() => null);
124
- if (layout === 'path') {
125
- if ((body.kind || 'person') === 'group') {
126
- return json(res, 200, { ...pre, ok: false, layout, refusal: 'group-needs-host-root' });
127
- }
128
- let gwHost; try { gwHost = new URL(body.gatewayOrigin || 'https://fedipod.net').host; } catch { gwHost = 'fedipod.net'; }
129
- return json(res, 200, { ...pre, layout, fronted: true, forced: true, shape: 'front', gatewayHost: gwHost,
130
- address: `@${body.handle}@${gwHost}`, resolvable: true });
131
- }
132
- return json(res, 200, { ...pre, layout });
133
- }
134
- return json(res, 200, pre);
135
- }
136
- // Discard a credential that never finished setup, so the account and
137
- // pod can be entered again. The credential a CSS server mints is shown
138
- // once, so a setup that stops after the mint (a wrong pod answers 401
139
- // to the first write) leaves the form in "finish" mode with no way to
140
- // re-enter what was wrong. This removes it locally and reopens the full
141
- // form. It does NOT revoke server-side — that needs the account
142
- // password (`fedipod revoke-credential`); the old credential is left on
143
- // the account, revocable from its dashboard.
144
- case '/setup/reset': {
145
- if (isCrossSiteNavigation(req)) return json(res, 403, { error: 'cross-site request' });
146
- // A working identity is never swapped out this way — that is a
147
- // teardown (`fedipod retire`), not a half-finished setup.
148
- if (agent.configured()) {
149
- return json(res, 409, { error: 'this home holds a working identity — retire it, do not reset' });
150
- }
151
- if (ctx.setup.run?.phase === 'running') {
152
- return json(res, 409, { error: 'setup is running — let it finish or stop it first', phase: 'running' });
153
- }
154
- const home = agent.home;
155
- if (!home) return json(res, 500, { error: 'this agent has no AP_HOME to reset' });
156
- const removed = hasCredential(home);
157
- if (removed) fs.rmSync(credentialPath(home), { force: true });
158
- // Drop the pod handle too, so configured() cannot flicker true off a
159
- // stale in-memory session while the fresh form is filled in.
160
- agent.remote = null;
161
- ctx.setup.run = null;
162
- return json(res, 200, { ok: true, removed });
163
- }
164
- case '/setup': {
165
- // A visited page must not be able to navigate this into existence.
166
- if (isCrossSiteNavigation(req)) return json(res, 403, { error: 'cross-site request' });
167
- if (agent.configured()) {
168
- return json(res, 409, {
169
- error: 'this home already holds an identity',
170
- pod: agent.store.getConfig()?.remotePod || null,
171
- });
172
- }
173
- if (ctx.setup.run?.phase === 'running') {
174
- return json(res, 409, { error: 'setup is already running', phase: 'running' });
175
- }
176
- const home = agent.home;
177
- if (!home) return json(res, 500, { error: 'this agent has no AP_HOME to set up' });
178
- // A crashed setup left a credential with no actor behind it. That is
179
- // finishable, and must NOT mint a second credential — the first is
180
- // unrecoverable and would be orphaned.
181
- const resuming = hasCredential(home);
182
- const answers = { ...body, password: body.password || process.env.AP_PASSWORD || '' };
183
- // The signup carry-over: attach to the gateway the signup arranged,
184
- // set before the first publish so the actor never advertises the pod
185
- // inbox first. The secret comes from the installer's file, never the
186
- // page.
187
- const gatewayCfg = gatewayConfigFrom(readFirstRun());
188
- if (gatewayCfg) answers.gateway = gatewayCfg;
189
- const bad = setupInputError(answers, resuming);
190
- if (bad) return json(res, 400, { error: bad });
191
- if (!resuming) {
192
- const pre = preflight(answers);
193
- if (!pre.ok) return json(res, 400, { ...pre, error: pre.error || pre.refusal });
194
- }
195
- // Widen the allowlist now, not at the end: the page offers to move to
196
- // the named origin, which has to answer before it is offered.
197
- allowed.setHandle(answers.handle);
198
- ctx.setup.run = newRun();
199
- // Deliberately not awaited. The credential a CSS server mints is
200
- // shown once, so what a run depends on is the file it writes, never
201
- // a connection a closed tab can take with it.
202
- runSetup({ home, agent, answers, run: ctx.setup.run, log })
203
- .then((r) => {
204
- // The carried secret is consumed; a finished setup deletes it.
205
- if (r?.phase === 'done' && process.env.AP_FIRST_RUN) {
206
- try { fs.unlinkSync(process.env.AP_FIRST_RUN); } catch { /* already gone */ }
207
- }
208
- })
209
- .catch(e => { ctx.setup.run.phase = 'error'; ctx.setup.run.error = e.message; });
210
- return json(res, 202, { ok: true, running: true, resuming });
211
- }
212
- // One identity per home, so a new actor means a new home, a free port
213
- // and a process there — then its own setup, run with the answers this
214
- // page collected. The reply is the address of the new actor's page,
215
- // which is where the progress of that setup is reported.
216
- case '/new-actor': {
217
- if (isCrossSiteNavigation(req)) return json(res, 403, { error: 'cross-site request' });
218
- const handle = String(body.handle || '').trim().toLowerCase();
219
- // The name first, and before anything is created: it is what becomes
220
- // a directory, and "mode must be new or existing" is a poor answer to
221
- // a handle that could climb out of profiles/.
222
- // Named after the handle rather than generated, so `profiles` and
223
- // `--profile <name>` stay legible a year from now.
224
- if (!/^[a-z0-9][a-z0-9_-]{0,30}$/.test(handle)) {
225
- return json(res, 400, { error: 'a handle is letters, digits, hyphens and underscores' });
226
- }
227
- const dir = path.join(rootOf(path.resolve(agent.home || '')), 'profiles', handle);
228
- if (fs.existsSync(path.join(dir, 'credential.json'))) {
229
- return json(res, 409, { error: `${handle} already exists — it is in the list above` });
230
- }
231
- // Then everything else setup needs, so a missing password is refused
232
- // here rather than after a process has been started for it.
233
- const bad = setupInputError({ ...body, handle });
234
- if (bad) return json(res, 400, { error: bad });
235
- const newPort = await freePortFrom(port + 1);
236
- if (!newPort) return json(res, 503, { error: 'no free port in the next 50' });
237
- fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
238
- const child = spawn(process.execPath, [path.join(projectRoot, 'run-agent.mjs')], {
239
- detached: true, stdio: 'ignore',
240
- env: { ...process.env, AP_HOME: dir, AP_PORT: String(newPort), AP_PROFILE: '' },
241
- });
242
- child.unref();
243
- // Answer only once it is listening: handing back a URL that is not
244
- // up yet shows a connection error instead of the setup form.
245
- let listening = false;
246
- for (let i = 0; i < 60 && !listening; i++) {
247
- await new Promise(r => setTimeout(r, 250));
248
- listening = await localFetch(agent.home, newPort, '/status', { timeout: 1000 })
249
- .then(r => r.ok).catch(() => false);
250
- }
251
- if (!listening) return json(res, 504, { error: `started it on ${newPort} but it never answered` });
252
-
253
- // Its own /setup owns the work — minting the credential, provisioning
254
- // the pod, publishing. It answers 202 and runs in the background, so
255
- // the page that opens next is the one reporting progress.
256
- const started = await localFetch(agent.home, newPort, '/setup', {
257
- method: 'POST',
258
- headers: { 'content-type': 'application/json' },
259
- body: JSON.stringify({ ...body, handle }),
260
- }).then(async r => ({ status: r.status, json: await r.json().catch(() => null) }))
261
- .catch(e => ({ status: 0, json: { error: e.message } }));
262
- if (started.status !== 202) {
263
- return json(res, started.status || 502, {
264
- error: started.json?.error || `the new agent refused setup (HTTP ${started.status})`,
265
- port: newPort, url: `${secureOrigin(handle, newPort)}${SETUP_PAGE}`,
266
- });
267
- }
268
- return json(res, 200,
269
- { ok: true, handle, port: newPort,
270
- url: `${secureOrigin(handle, newPort)}${SETUP_PAGE}` });
271
- }
272
- // Start an actor that is not running, so its page can be visited. The
273
- // same spawn as /new-actor, minus the making: this one has a home
274
- // already. Its recorded port is preferred so its address stays what it
275
- // was, but a port something else has taken is walked past, not fought.
276
- case '/start-actor': {
277
- if (isCrossSiteNavigation(req)) return json(res, 403, { error: 'cross-site request' });
278
- const name = String(body.name || '');
279
- const found = identityHomes(rootOf(path.resolve(agent.home || ''))).find(h => h.name === name);
280
- if (!found) return json(res, 404, { error: `no identity called ${name}` });
281
-
282
- let want = null;
283
- try { want = JSON.parse(fs.readFileSync(path.join(found.dir, 'agent.json'), 'utf8')).port || null; } catch {}
284
- if (want) {
285
- // Its own handle, not the profile directory's name: usually the
286
- // same, but a renamed profile makes them differ and the origin has
287
- // to be one that agent's own guard will accept.
288
- const live = await localFetch(agent.home, want, '/status', { timeout: 1000 })
289
- .then(r => (r.ok ? r.json() : null)).catch(() => null);
290
- if (live) {
291
- return json(res, 200, {
292
- ok: true, name, port: want, already: true,
293
- url: `${secureOrigin(live.handle || name, want)}/`,
294
- });
295
- }
296
- }
297
- // The port is not the only evidence it is running. An agent from
298
- // before agent.json was written at startup has a live pidfile and no
299
- // recorded port, and spawning past that gives one home two agents —
300
- // which is exactly how this machine ended up with two on profiles/jeff.
301
- let held = null;
302
- try { held = Number(fs.readFileSync(path.join(found.dir, 'agent.pid'), 'utf8').trim()) || null; } catch {}
303
- if (held) {
304
- let alive = false;
305
- try { process.kill(held, 0); alive = true; } catch { /* gone */ }
306
- if (alive) {
307
- return json(res, 409, {
308
- error: `${name} is already running as pid ${held}, on a port it never recorded. `
309
- + `Stop it first: kill ${held}`,
310
- });
311
- }
312
- }
313
- // A recorded port held by the directory door is still this
314
- // identity's port — the door steps aside for its owner.
315
- if (want && !await portFree(want)) await yieldDirectory(want, { portFree });
316
- const on = (want && await portFree(want)) ? want : await freePortFrom(port + 1);
317
- if (!on) return json(res, 503, { error: 'no free port in the next 50' });
318
-
319
- const started = spawn(process.execPath, [path.join(projectRoot, 'run-agent.mjs')], {
320
- detached: true, stdio: 'ignore',
321
- env: { ...process.env, AP_HOME: found.dir, AP_PORT: String(on), AP_PROFILE: '' },
322
- });
323
- started.unref();
324
- for (let i = 0; i < 60; i++) {
325
- await new Promise(r => setTimeout(r, 250));
326
- const up = await localFetch(agent.home, on, '/status', { timeout: 1000 })
327
- .then(r => (r.ok ? r.json() : null)).catch(() => null);
328
- if (up) {
329
- return json(res, 200, { ok: true, name, port: on,
330
- url: `${secureOrigin(up.handle || name, on)}/` });
331
- }
332
- }
333
- return json(res, 504, { error: `started ${name} on ${on} but it never answered` });
334
- }
335
- // Pull the latest published FediPod and restart the agents. What the
336
- // installer does on a re-run, offered where the version line is.
337
- case '/update': {
338
- if (isCrossSiteNavigation(req)) return json(res, 403, { error: 'cross-site request' });
339
- const { runUpdate, restartAgents, repoRoot } = await import('../../update.mjs');
340
- const r = runUpdate({ log });
341
- if (!r.ok) return json(res, 409, { error: r.note });
342
- json(res, 200, { ok: true, note: r.note, restarting: true });
343
- // The response first, the restart a beat later — a self-restart must
344
- // not take the answer down with it.
345
- setTimeout(() => {
346
- if (restartAgents({ log }) === 'self') {
347
- const child = spawn(process.execPath, [path.join(repoRoot(), 'run-agent.mjs')],
348
- { detached: true, stdio: 'ignore', env: process.env });
349
- child.unref();
350
- setTimeout(() => process.exit(0), 300);
351
- }
352
- }, 200);
353
- return true;
354
- }
355
- // Move the private half — every state document and the RDF tree — while
356
- // the agent runs: quiesce this process's writers, flush, copy, verify,
357
- // repoint the credential, then reconnect on the new location. The old
358
- // copy is left where it was, exactly as the CLI move leaves it.
359
- case '/state-move': {
360
- const cred = agent.readCredential?.();
361
- if (!cred) return json(res, 409, { error: 'no credential — run setup first' });
362
- const to = String(body.to || '').trim();
363
- if (!to) return json(res, 400, { error: 'say where it should go' });
364
- let target = null;
365
- if (to !== 'pod') {
366
- // A path or a URL — two chars before the colon, so a Windows drive
367
- // letter reads as a path rather than a scheme.
368
- const asPath = !/^[a-z][a-z0-9+.-]+:/i.test(to);
369
- const raw = asPath
370
- ? pathToFileURL(path.resolve(to.replace(/^~(?=[/\\]|$)/, os.homedir()))).href
371
- : to;
372
- target = raw.endsWith('/') ? raw : raw + '/';
373
- try { new URL(target); } catch { return json(res, 400, { error: `"${to}" is not a container URL or a path` }); }
374
- if (/^https?:/i.test(target)) {
375
- const bad = insecureUrlReason(target, 'private-data address');
376
- if (bad) return json(res, 400, { error: bad });
377
- }
378
- }
379
- const whereState = (c) => (c.privateRoot ? tildify(c.privateRoot) : 'on the pod');
380
- if ((cred.privateRoot || null) === target) {
381
- return json(res, 200, { ok: true, docs: 0, notes: 0, unchanged: true, now: whereState(cred) });
382
- }
383
- await agent.requestTakeover?.();
384
- agent.intake?.stop(); agent.deliverer?.stop(); agent.tagfeed?.stop(); agent.importer?.stop();
385
- try {
386
- await agent.store.flush();
387
- const destCred = { ...cred, privateRoot: target };
388
- const copied = await copyPrivateHalf({
389
- from: { state: agent.privateStorage(cred, 'state') },
390
- to: { state: agent.privateStorage(destCred, 'state') },
391
- log,
392
- });
393
- if (target) cred.privateRoot = target; else delete cred.privateRoot;
394
- if (isCurrent(cred)) cred.layout = CURRENT_LAYOUT; else delete cred.layout;
395
- writeJsonAtomic(path.join(agent.home, 'credential.json'), cred);
396
- // connect() rebuilds everything that pointed at the old location —
397
- // store, publisher, intake — and restarts them.
398
- agent.store.attach(agent.privateStorage(cred, 'state'));
399
- agent.stateLoaded = false;
400
- await agent.connect();
401
- return json(res, 200, { ok: true, ...copied, now: whereState(cred) });
402
- } catch (e) {
403
- // The credential is only repointed after a verified copy, so on any
404
- // failure reconnect puts the agent back to work where it was.
405
- agent.stateLoaded = false;
406
- await agent.connect().catch(() => {});
407
- return json(res, 502, { error: e.message });
408
- }
409
- }
410
- default: return false;
411
- }
412
- }
@@ -1,188 +0,0 @@
1
- // social.mjs — the owner acting on the social graph, and a group operator
2
- // moderating: posting, the tag feed, follows, mutes, blocks, ejecting,
3
- // retracting, review, joins, the moderation queue.
4
-
5
- import { followHandle, followActor, unfollowActor, ejectFollower, retractAnnouncement, admitRequest, refuseRequest, applyModeration } from '../../../core/social.mjs';
6
-
7
- export async function get(p, ctx, req, res, url) { // eslint-disable-line no-unused-vars
8
- const { agent, log, allowed, embedded, port, publicOrigin, versionOnDisk, isGroup } = ctx; // eslint-disable-line no-unused-vars
9
- const json = (res, status, obj) => { ctx.json(res, status, obj); return true; };
10
-
11
- if (req.method === 'GET' && p === '/tagfeed') {
12
- return json(res, 200, agent.tagfeed
13
- ? { ...agent.tagfeed.config(), lastSweep: agent.tagfeed.lastSweep, lastAdded: agent.tagfeed.lastAdded }
14
- : { error: 'agent not configured' });
15
- }
16
-
17
- return false;
18
- }
19
-
20
- export async function post(p, body, ctx, req, res) { // eslint-disable-line no-unused-vars
21
- const { agent, log, allowed, embedded, port, publicOrigin, versionOnDisk, isGroup } = ctx; // eslint-disable-line no-unused-vars
22
- const json = (res, status, obj) => { ctx.json(res, status, obj); return true; };
23
- switch (p) {
24
- case '/post': {
25
- if (!body.content) return json(res, 400, { error: 'content required' });
26
- const note = await agent.publisher.publishNote(body.content, { inReplyTo: body.inReplyTo });
27
- return json(res, 200, { ok: true, id: note.id });
28
- }
29
- case '/tagfeed': return json(res, 200, agent.tagfeed.setConfig(body));
30
- case '/archive': {
31
- // Whether drained mail's original bytes are kept in the private
32
- // half's inbox-archive/. Absent means on.
33
- const cfg = agent.store.getConfig();
34
- agent.store.setConfig({ ...cfg, archiveInbox: !!body.on });
35
- await agent.store.flush();
36
- return json(res, 200, { ok: true, archiveInbox: !!body.on });
37
- }
38
- case '/follow': {
39
- // By handle normally; by actor URL when there is no handle to resolve —
40
- // a pod on a path, or anything WebFinger cannot answer for. Pasting an
41
- // actor URL is a normal way to follow in Mastodon too.
42
- if (body.actor) { await followActor(agent, body.actor); return json(res, 200, { ok: true, actor: body.actor }); }
43
- if (!body.handle) return json(res, 400, { error: 'handle or actor required' });
44
- return json(res, 200, await followHandle(agent, body.handle));
45
- }
46
- case '/unfollow': return json(res, 200, await unfollowActor(agent, body.actor));
47
- case '/mute':
48
- case '/unmute': {
49
- if (!isGroup()) return json(res, 404, { error: 'not a group' });
50
- if (!body.actor) return json(res, 400, { error: 'actor required' });
51
- const m = agent.store.getMuted();
52
- m.actors = p === '/mute'
53
- ? [...new Set([...m.actors, body.actor])]
54
- : m.actors.filter(a => a !== body.actor);
55
- agent.store.setMuted(m);
56
- await agent.store.flush();
57
- return json(res, 200, { ok: true, actors: m.actors });
58
- }
59
- case '/eject': {
60
- if (!isGroup()) return json(res, 404, { error: 'not a group' });
61
- if (!body.actor) return json(res, 400, { error: 'actor required' });
62
- const r = await ejectFollower(agent, body.actor);
63
- await agent.store.flush();
64
- return json(res, 200, r);
65
- }
66
- case '/retract': {
67
- if (!isGroup()) return json(res, 404, { error: 'not a group' });
68
- if (!body.noteId) return json(res, 400, { error: 'noteId required' });
69
- const r = await retractAnnouncement(agent, body.noteId);
70
- await agent.store.flush();
71
- return json(res, 200, r);
72
- }
73
- case '/review': {
74
- if (!isGroup()) return json(res, 404, { error: 'not a group' });
75
- agent.store.setConfig({ ...agent.store.getConfig(), review: !!body.on });
76
- await agent.store.flush();
77
- return json(res, 200, { ok: true, review: !!body.on });
78
- }
79
- case '/describe': {
80
- const cfg = { ...agent.store.getConfig() };
81
- if ('summary' in body) cfg.summary = body.summary || undefined;
82
- if ('icon' in body) cfg.icon = body.icon || undefined;
83
- agent.store.setConfig(cfg);
84
- Object.assign(agent.publisher.config, { summary: cfg.summary, icon: cfg.icon });
85
- await agent.store.flush();
86
- await agent.publisher.publishProfile(); // the bio and avatar are on the wire
87
- return json(res, 200, { ok: true, summary: cfg.summary || null, icon: cfg.icon || null });
88
- }
89
- case '/joins': {
90
- if (!isGroup()) return json(res, 404, { error: 'not a group' });
91
- const cfg = { ...agent.store.getConfig(), approveJoins: !!body.approve };
92
- agent.store.setConfig(cfg);
93
- agent.publisher.config.approveJoins = !!body.approve;
94
- await agent.store.flush();
95
- // Unlike review, this one is visible to the fediverse: the actor
96
- // document carries manuallyApprovesFollowers, so nothing changes for
97
- // anyone until it is republished.
98
- await agent.publisher.publishProfile();
99
- return json(res, 200, { ok: true, approveJoins: !!body.approve });
100
- }
101
- case '/admit':
102
- case '/refuse': {
103
- // Kind-agnostic, like the queue they act on: admitting adds a
104
- // follower and Accepts, refusing Rejects. A person needs both.
105
- // `all` answers the whole queue — the shape a migration wave arrives
106
- // in — with one collections republish for the lot.
107
- if (p === '/admit' && body.all === true) {
108
- const waiting = agent.store.getRequests().map(r => r.actor);
109
- let admitted = 0;
110
- for (const actor of waiting) {
111
- try { await admitRequest(agent, actor, { publish: false }); admitted++; }
112
- catch (e) { log(`admit ${actor} failed: ${e.message}`); }
113
- }
114
- if (admitted) await agent.publisher.publishCollections({ followers: true, pending: true });
115
- await agent.store.flush();
116
- return json(res, 200, { ok: true, admitted, requests: agent.store.getRequests().length });
117
- }
118
- if (!body.actor) return json(res, 400, { error: 'actor required' });
119
- const r = p === '/admit' ? await admitRequest(agent, body.actor)
120
- : await refuseRequest(agent, body.actor);
121
- await agent.store.flush();
122
- return json(res, 200, { ...r, requests: agent.store.getRequests().length });
123
- }
124
- case '/approve':
125
- case '/decline': {
126
- if (!isGroup()) return json(res, 404, { error: 'not a group' });
127
- if (!body.noteId) return json(res, 400, { error: 'noteId required' });
128
- const held = agent.store.getPending().some(x => x.noteId === body.noteId);
129
- if (!held) return json(res, 404, { error: 'not held for review' });
130
- if (p === '/approve') await agent.intake.amplify(body.noteId, { approved: true });
131
- else agent.store.setPending(agent.store.getPending().filter(x => x.noteId !== body.noteId));
132
- await agent.store.flush();
133
- return json(res, 200, { ok: true, noteId: body.noteId, pending: agent.store.getPending().length });
134
- }
135
- // Answer one queued moderation ask: apply it (the operator vouching
136
- // for a delivery nothing else can vouch for) or dismiss it.
137
- case '/modqueue': {
138
- if (!isGroup()) return json(res, 404, { error: 'not a group' });
139
- const q = agent.store.read('modqueue.json', []);
140
- const entry = q.find(e => e.id === body.id);
141
- if (!entry) return json(res, 404, { error: 'no such queued action' });
142
- if (body.action !== 'apply' && body.action !== 'dismiss') {
143
- return json(res, 400, { error: 'action must be apply or dismiss' });
144
- }
145
- let applied = null;
146
- if (body.action === 'apply') applied = await applyModeration(agent, entry);
147
- agent.store.write('modqueue.json', q.filter(e => e.id !== entry.id));
148
- await agent.store.flush();
149
- return json(res, 200, {
150
- ok: true, action: body.action, type: entry.type, moderator: entry.moderator,
151
- ...(applied && typeof applied === 'object' ? { result: applied } : {}),
152
- remaining: agent.store.read('modqueue.json', []).length,
153
- });
154
- }
155
- // Symmetrical with /block, and open for the same reason: a block made
156
- // by mistake is worth undoing before federation is even configured.
157
- case '/unblock': {
158
- const b = agent.store.getBlocklist();
159
- const before = b.domains.length + b.actors.length;
160
- if (body.actor) b.actors = b.actors.filter(a => a !== body.actor);
161
- else if (body.domain) b.domains = b.domains.filter(d => d !== body.domain);
162
- else return json(res, 400, { error: 'domain or actor required' });
163
- agent.store.setBlocklist(b);
164
- // The published blocked collection (FEP-c648) follows, best-effort.
165
- Promise.resolve(agent.publisher?.publishCollections?.({ blocked: true })).catch(() => {});
166
- return json(res, 200, {
167
- ok: true, removed: before - (b.domains.length + b.actors.length),
168
- domains: b.domains, actors: b.actors,
169
- });
170
- }
171
- case '/block': {
172
- const b = agent.store.getBlocklist();
173
- if (body.actor) {
174
- if (!/^https?:\/\/\S+$/.test(body.actor)) return json(res, 400, { error: 'actor must be a URL' });
175
- if (!b.actors.includes(body.actor)) b.actors.push(body.actor);
176
- } else if (body.domain) {
177
- if (!b.domains.includes(body.domain)) b.domains.push(body.domain);
178
- } else {
179
- return json(res, 400, { error: 'domain or actor required' });
180
- }
181
- agent.store.setBlocklist(b);
182
- // The published blocked collection (FEP-c648) follows, best-effort.
183
- Promise.resolve(agent.publisher?.publishCollections?.({ blocked: true })).catch(() => {});
184
- return json(res, 200, { ok: true, domains: b.domains, actors: b.actors });
185
- }
186
- default: return false;
187
- }
188
- }