fedipod-server 0.29.0 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (444) hide show
  1. package/README.md +16 -6
  2. package/dist/claims.d.ts +2 -0
  3. package/dist/claims.js +13 -4
  4. package/dist/door.d.ts +4 -0
  5. package/dist/door.js +31 -0
  6. package/dist/fedipod.d.ts +15 -0
  7. package/dist/fedipod.js +13 -0
  8. package/dist/front.d.ts +3 -0
  9. package/dist/front.js +92 -0
  10. package/dist/handler.d.ts +36 -101
  11. package/dist/handler.js +93 -644
  12. package/dist/handler.jsonld +2 -30
  13. package/dist/identities.d.ts +71 -0
  14. package/dist/identities.js +236 -0
  15. package/dist/mounts.d.ts +43 -0
  16. package/dist/mounts.js +123 -0
  17. package/dist/opt-in.d.ts +46 -0
  18. package/dist/opt-in.js +176 -0
  19. package/dist/private-read.d.ts +4 -0
  20. package/dist/private-read.js +34 -0
  21. package/package.json +12 -20
  22. package/lib/client/c2s.mjs +0 -636
  23. package/lib/client/localapi.mjs +0 -87
  24. package/lib/client/masto/accounts.mjs +0 -310
  25. package/lib/client/masto/body.mjs +0 -156
  26. package/lib/client/masto/index.mjs +0 -206
  27. package/lib/client/masto/instance.mjs +0 -112
  28. package/lib/client/masto/media.mjs +0 -79
  29. package/lib/client/masto/oauth.mjs +0 -656
  30. package/lib/client/masto/render.mjs +0 -518
  31. package/lib/client/masto/statuses.mjs +0 -372
  32. package/lib/client/masto/timelines.mjs +0 -438
  33. package/lib/client/oidc-auth.mjs +0 -62
  34. package/lib/client/streaming.mjs +0 -154
  35. package/lib/client/webpush.mjs +0 -98
  36. package/lib/connections/acctfeed.mjs +0 -257
  37. package/lib/connections/atproto.mjs +0 -309
  38. package/lib/connections/bskyfeed.mjs +0 -243
  39. package/lib/connections/bskygroup.mjs +0 -162
  40. package/lib/connections/fediacct.mjs +0 -253
  41. package/lib/connections/import.mjs +0 -357
  42. package/lib/connections/tagfeed.mjs +0 -212
  43. package/lib/connections/vault.mjs +0 -114
  44. package/lib/core/as2.mjs +0 -206
  45. package/lib/core/contexts/activitystreams.json +0 -379
  46. package/lib/core/contexts/anno.json +0 -126
  47. package/lib/core/contexts/did-v1.json +0 -57
  48. package/lib/core/contexts/fep-5711.json +0 -36
  49. package/lib/core/contexts/gotosocial.json +0 -86
  50. package/lib/core/contexts/identity-v1.json +0 -152
  51. package/lib/core/contexts/index.mjs +0 -63
  52. package/lib/core/contexts/join-lemmy.json +0 -33
  53. package/lib/core/contexts/joinmastodon.json +0 -28
  54. package/lib/core/contexts/litepub-0.1.json +0 -60
  55. package/lib/core/contexts/map.json +0 -19
  56. package/lib/core/contexts/miscellany.json +0 -19
  57. package/lib/core/contexts/quotes.json +0 -18
  58. package/lib/core/contexts/schemaorg.json +0 -8845
  59. package/lib/core/contexts/security-data-integrity-v1.json +0 -78
  60. package/lib/core/contexts/security-data-integrity-v2.json +0 -81
  61. package/lib/core/contexts/security-multikey-v1.json +0 -35
  62. package/lib/core/contexts/security-v1.json +0 -74
  63. package/lib/core/contexts/webfinger.json +0 -10
  64. package/lib/core/deliver.mjs +0 -387
  65. package/lib/core/graphview.mjs +0 -327
  66. package/lib/core/intake/activities.mjs +0 -649
  67. package/lib/core/intake/activity.mjs +0 -318
  68. package/lib/core/intake/channel.mjs +0 -144
  69. package/lib/core/intake/group.mjs +0 -258
  70. package/lib/core/intake/index.mjs +0 -732
  71. package/lib/core/intake/notes.mjs +0 -367
  72. package/lib/core/intake/verify.mjs +0 -142
  73. package/lib/core/keys.mjs +0 -153
  74. package/lib/core/lease.mjs +0 -206
  75. package/lib/core/place.mjs +0 -82
  76. package/lib/core/polls.mjs +0 -105
  77. package/lib/core/profile-page.mjs +0 -104
  78. package/lib/core/proof.mjs +0 -79
  79. package/lib/core/publisher/collections.mjs +0 -272
  80. package/lib/core/publisher/index.mjs +0 -522
  81. package/lib/core/publisher/notes.mjs +0 -519
  82. package/lib/core/publisher/own.mjs +0 -143
  83. package/lib/core/publisher/questions.mjs +0 -241
  84. package/lib/core/publisher/restore.mjs +0 -224
  85. package/lib/core/shapes/activitystreams.ttl +0 -157
  86. package/lib/core/shapes/index.mjs +0 -132
  87. package/lib/core/shapes/shapes-text.mjs +0 -13
  88. package/lib/core/social.mjs +0 -561
  89. package/lib/core/storage.mjs +0 -176
  90. package/lib/core/store.mjs +0 -578
  91. package/lib/core/wire-quotes.mjs +0 -71
  92. package/lib/core/wire.mjs +0 -925
  93. package/lib/device/account.mjs +0 -87
  94. package/lib/device/admin/index.mjs +0 -13
  95. package/lib/device/admin/origins.mjs +0 -35
  96. package/lib/device/admin/routes/connections.mjs +0 -144
  97. package/lib/device/admin/routes/gateway.mjs +0 -199
  98. package/lib/device/admin/routes/lifecycle.mjs +0 -200
  99. package/lib/device/admin/routes/owner.mjs +0 -322
  100. package/lib/device/admin/routes/setup.mjs +0 -428
  101. package/lib/device/admin/routes/social.mjs +0 -188
  102. package/lib/device/admin/server.mjs +0 -95
  103. package/lib/device/admin/static.mjs +0 -247
  104. package/lib/device/admin/surface.mjs +0 -315
  105. package/lib/device/certs.mjs +0 -281
  106. package/lib/device/cli/commands/account.mjs +0 -586
  107. package/lib/device/cli/commands/run.mjs +0 -278
  108. package/lib/device/cli/commands/service.mjs +0 -221
  109. package/lib/device/cli/commands/setup.mjs +0 -489
  110. package/lib/device/cli/commands/state.mjs +0 -561
  111. package/lib/device/cli/context.mjs +0 -288
  112. package/lib/device/export-collections.mjs +0 -161
  113. package/lib/device/gateway-move.mjs +0 -58
  114. package/lib/device/home.mjs +0 -161
  115. package/lib/device/migrate.mjs +0 -115
  116. package/lib/device/ports.mjs +0 -27
  117. package/lib/device/remote.mjs +0 -54
  118. package/lib/device/setup.mjs +0 -496
  119. package/lib/device/update.mjs +0 -96
  120. package/lib/gateway/caches.mjs +0 -36
  121. package/lib/gateway/directory.mjs +0 -90
  122. package/lib/gateway/front-core.mjs +0 -966
  123. package/lib/gateway/gateway-core.mjs +0 -244
  124. package/lib/gateway/headers.mjs +0 -49
  125. package/lib/gateway/httpsig.mjs +0 -159
  126. package/lib/gateway/notices.mjs +0 -84
  127. package/lib/gateway/quiet.mjs +0 -189
  128. package/lib/gateway/relay-extras.mjs +0 -89
  129. package/lib/gateway/token-claims.mjs +0 -16
  130. package/lib/pod/README.md +0 -61
  131. package/lib/pod/actor.mjs +0 -96
  132. package/lib/pod/collection.mjs +0 -77
  133. package/lib/pod/containers.mjs +0 -140
  134. package/lib/pod/discovery.mjs +0 -46
  135. package/lib/pod/featured.mjs +0 -18
  136. package/lib/pod/followers.mjs +0 -24
  137. package/lib/pod/following.mjs +0 -10
  138. package/lib/pod/http.mjs +0 -74
  139. package/lib/pod/inbox.mjs +0 -166
  140. package/lib/pod/links.mjs +0 -35
  141. package/lib/pod/location.mjs +0 -52
  142. package/lib/pod/media.mjs +0 -27
  143. package/lib/pod/notes.mjs +0 -78
  144. package/lib/pod/notifications.mjs +0 -89
  145. package/lib/pod/outbox.mjs +0 -18
  146. package/lib/pod/package.json +0 -19
  147. package/lib/pod/policy.mjs +0 -58
  148. package/lib/pod/private.mjs +0 -19
  149. package/lib/pod/root.mjs +0 -104
  150. package/lib/pod/state.mjs +0 -40
  151. package/lib/pod/transport.mjs +0 -674
  152. package/lib/pod/type-index.mjs +0 -101
  153. package/lib/pod/urls.mjs +0 -91
  154. package/lib/server/embed.mjs +0 -424
  155. package/lib/session/README.md +0 -137
  156. package/lib/session/demo.html +0 -94
  157. package/lib/session/fedi-account.mjs +0 -417
  158. package/lib/session/fedi-login.mjs +0 -123
  159. package/lib/session/oidc-session.mjs +0 -233
  160. package/lib/session/package.json +0 -49
  161. package/lib/shared/guard.mjs +0 -213
  162. package/lib/shared/links.mjs +0 -9
  163. package/lib/shared/safefetch.mjs +0 -235
  164. package/lib/shared/ua.mjs +0 -29
  165. package/phanpy/dist/404.html +0 -40
  166. package/phanpy/dist/_headers +0 -2
  167. package/phanpy/dist/apple-touch-icon.png +0 -0
  168. package/phanpy/dist/assets/ICONS-DIPJeU0C.js +0 -2
  169. package/phanpy/dist/assets/ICONS-DIPJeU0C.js.map +0 -1
  170. package/phanpy/dist/assets/ScheduledAtField-D-Abfo4J.js +0 -183
  171. package/phanpy/dist/assets/ScheduledAtField-D-Abfo4J.js.map +0 -1
  172. package/phanpy/dist/assets/Temml-CCjoacWt.woff2 +0 -0
  173. package/phanpy/dist/assets/__vite-browser-external-C7iut881.js +0 -2
  174. package/phanpy/dist/assets/__vite-browser-external-C7iut881.js.map +0 -1
  175. package/phanpy/dist/assets/boosts-carousel-BiOaNdMT.jpg +0 -0
  176. package/phanpy/dist/assets/catch-up-O5q5C75Z.png +0 -0
  177. package/phanpy/dist/assets/chunk-aKtaBQYM.js +0 -1
  178. package/phanpy/dist/assets/compose-CFEx7wb4.js +0 -2
  179. package/phanpy/dist/assets/compose-CFEx7wb4.js.map +0 -1
  180. package/phanpy/dist/assets/compose-ZJN5xSb4.js +0 -35
  181. package/phanpy/dist/assets/compose-ZJN5xSb4.js.map +0 -1
  182. package/phanpy/dist/assets/debug.module-YFQgwnIr.js +0 -52
  183. package/phanpy/dist/assets/debug.module-YFQgwnIr.js.map +0 -1
  184. package/phanpy/dist/assets/dom-p1YcJ5Rw.js +0 -2
  185. package/phanpy/dist/assets/dom-p1YcJ5Rw.js.map +0 -1
  186. package/phanpy/dist/assets/home-mobile-dark@2x-Dc-J8-SM.png +0 -0
  187. package/phanpy/dist/assets/home-mobile-light@2x-Bd3fod8m.png +0 -0
  188. package/phanpy/dist/assets/home-tablet-dark@2x-BgcNI8le.png +0 -0
  189. package/phanpy/dist/assets/home-tablet-light@2x-BW2RV9T6.png +0 -0
  190. package/phanpy/dist/assets/icons/add-circle-line-WD0mwORD.js +0 -1
  191. package/phanpy/dist/assets/icons/alert-line-CpFUMf9s.js +0 -1
  192. package/phanpy/dist/assets/icons/android-2-line-Bzxnrd3S.js +0 -1
  193. package/phanpy/dist/assets/icons/announcement-line-5nwaJFJm.js +0 -1
  194. package/phanpy/dist/assets/icons/arrow-down-circle-line-a7Id9-Iv.js +0 -1
  195. package/phanpy/dist/assets/icons/arrow-down-line-B8uzXzq6.js +0 -1
  196. package/phanpy/dist/assets/icons/arrow-left-line-CGrGT9C-.js +0 -1
  197. package/phanpy/dist/assets/icons/arrow-right-line-CE-HERfX.js +0 -1
  198. package/phanpy/dist/assets/icons/arrow-to-up-line-CvdQjmcy.js +0 -1
  199. package/phanpy/dist/assets/icons/arrow-up-circle-line-DEparSNF.js +0 -1
  200. package/phanpy/dist/assets/icons/arrow-up-line-C7yBeugc.js +0 -1
  201. package/phanpy/dist/assets/icons/arrows-right-line-BtbXmJGP.js +0 -1
  202. package/phanpy/dist/assets/icons/at-line-CF66Bznk.js +0 -1
  203. package/phanpy/dist/assets/icons/attachment-line-BY8It-l9.js +0 -1
  204. package/phanpy/dist/assets/icons/blockquote-line-BS6pU_hm.js +0 -1
  205. package/phanpy/dist/assets/icons/board-line-C0UWXyA6.js +0 -1
  206. package/phanpy/dist/assets/icons/bookmark-line-1CtX5JLF.js +0 -1
  207. package/phanpy/dist/assets/icons/building-5-line-Dd59MzbO.js +0 -1
  208. package/phanpy/dist/assets/icons/bus-2-line-6_dvA0GG.js +0 -1
  209. package/phanpy/dist/assets/icons/calendar-day-line-Crpl_fmu.js +0 -1
  210. package/phanpy/dist/assets/icons/calendar-month-line-vommts2C.js +0 -1
  211. package/phanpy/dist/assets/icons/calendar-time-add-line-BU_DTlQk.js +0 -1
  212. package/phanpy/dist/assets/icons/camera-line-Cv69MEei.js +0 -1
  213. package/phanpy/dist/assets/icons/celebrate-line-B8DvIoi0.js +0 -1
  214. package/phanpy/dist/assets/icons/chart-bar-line-FK-QOmtF.js +0 -1
  215. package/phanpy/dist/assets/icons/chart-line-line-BGcJAFWw.js +0 -1
  216. package/phanpy/dist/assets/icons/chat-3-line-YFK2-hwz.js +0 -1
  217. package/phanpy/dist/assets/icons/check-circle-line-DsAxFr5-.js +0 -1
  218. package/phanpy/dist/assets/icons/clipboard-line-BBp364gJ.js +0 -1
  219. package/phanpy/dist/assets/icons/close-circle-line-CICdiuGz.js +0 -1
  220. package/phanpy/dist/assets/icons/close-line-DYrFWcjv.js +0 -1
  221. package/phanpy/dist/assets/icons/cloud-line-tiQAbah8.js +0 -1
  222. package/phanpy/dist/assets/icons/code-line-fAl8iiV3.js +0 -1
  223. package/phanpy/dist/assets/icons/comment-2-line-CR3g7WeB.js +0 -1
  224. package/phanpy/dist/assets/icons/copy-2-line-CLW69yF5.js +0 -1
  225. package/phanpy/dist/assets/icons/delete-2-line-B8xmbr4n.js +0 -1
  226. package/phanpy/dist/assets/icons/document-3-line-DBGNhAg2.js +0 -1
  227. package/phanpy/dist/assets/icons/document-line-C7mb9sb7.js +0 -1
  228. package/phanpy/dist/assets/icons/down-line-BGqSSThd.js +0 -1
  229. package/phanpy/dist/assets/icons/edit-4-line-DcgGaEhx.js +0 -1
  230. package/phanpy/dist/assets/icons/emoji-2-line-D8YgnM-y.js +0 -1
  231. package/phanpy/dist/assets/icons/exit-line-Dc07BS-x.js +0 -1
  232. package/phanpy/dist/assets/icons/external-link-line-BvpSkmjO.js +0 -1
  233. package/phanpy/dist/assets/icons/eye-2-line-CiWXx-f_.js +0 -1
  234. package/phanpy/dist/assets/icons/eye-close-line-BgTaSJ4P.js +0 -1
  235. package/phanpy/dist/assets/icons/filter-2-line-B_mR2usy.js +0 -1
  236. package/phanpy/dist/assets/icons/filter-line-C_nm-3cU.js +0 -1
  237. package/phanpy/dist/assets/icons/flag-1-line-C2HB05I0.js +0 -1
  238. package/phanpy/dist/assets/icons/forbid-circle-line-D3LTT9yd.js +0 -1
  239. package/phanpy/dist/assets/icons/formula-line-DLrt2d6j.js +0 -1
  240. package/phanpy/dist/assets/icons/grid-line-CgUmEikf.js +0 -1
  241. package/phanpy/dist/assets/icons/group-line-C5fnX0wU.js +0 -1
  242. package/phanpy/dist/assets/icons/hand-finger-2-line-BGgxbuL9.js +0 -1
  243. package/phanpy/dist/assets/icons/hashtag-line-CyTEEXBb.js +0 -1
  244. package/phanpy/dist/assets/icons/heart-crack-line-BZddbO2C.js +0 -1
  245. package/phanpy/dist/assets/icons/heart-line-CJ7K47J4.js +0 -1
  246. package/phanpy/dist/assets/icons/history-2-line-kWWvFzqv.js +0 -1
  247. package/phanpy/dist/assets/icons/history-line-bGQiwbTH.js +0 -1
  248. package/phanpy/dist/assets/icons/home-3-line-fRU1zeLG.js +0 -1
  249. package/phanpy/dist/assets/icons/information-line-iNk6-sOY.js +0 -1
  250. package/phanpy/dist/assets/icons/keyboard-line-DsCyuQNz.js +0 -1
  251. package/phanpy/dist/assets/icons/layout-4-line-CZkOX52Z.js +0 -1
  252. package/phanpy/dist/assets/icons/layout-5-line-B_znt5wO.js +0 -1
  253. package/phanpy/dist/assets/icons/left-line-eUw0tya4.js +0 -1
  254. package/phanpy/dist/assets/icons/lightning-line-DaKoDdix.js +0 -1
  255. package/phanpy/dist/assets/icons/link-2-line-DCqLmMA7.js +0 -1
  256. package/phanpy/dist/assets/icons/list-check-line-BAxosBh9.js +0 -1
  257. package/phanpy/dist/assets/icons/lock-line-CPoaXeUW.js +0 -1
  258. package/phanpy/dist/assets/icons/mail-line-B0P8aa1L.js +0 -1
  259. package/phanpy/dist/assets/icons/moon-line-CEqNAToP.js +0 -1
  260. package/phanpy/dist/assets/icons/more-1-fill-Dw2e5xab.js +0 -1
  261. package/phanpy/dist/assets/icons/more-3-line-BIiVjYTz.js +0 -1
  262. package/phanpy/dist/assets/icons/notification-line-BZZq2Gtu.js +0 -1
  263. package/phanpy/dist/assets/icons/pencil-line-CKh8-A1v.js +0 -1
  264. package/phanpy/dist/assets/icons/photo-album-line-Bnfg2u40.js +0 -1
  265. package/phanpy/dist/assets/icons/pin-line-DpTolyhs.js +0 -1
  266. package/phanpy/dist/assets/icons/play-fill-B0-G_8lX.js +0 -1
  267. package/phanpy/dist/assets/icons/qrcode-2-line-B6SPCaHn.js +0 -1
  268. package/phanpy/dist/assets/icons/quill-pen-line-B6LUrnCP.js +0 -1
  269. package/phanpy/dist/assets/icons/quote-left-fill-hQZ1cj_6.js +0 -1
  270. package/phanpy/dist/assets/icons/radar-line-CKyroTth.js +0 -1
  271. package/phanpy/dist/assets/icons/react-line-CoIKhhtK.js +0 -1
  272. package/phanpy/dist/assets/icons/refresh-2-line-D0mTF97S.js +0 -1
  273. package/phanpy/dist/assets/icons/right-line-B3KWJaqc.js +0 -1
  274. package/phanpy/dist/assets/icons/rocket-line-4KZl537D.js +0 -1
  275. package/phanpy/dist/assets/icons/round-fill-C69NTrZ0.js +0 -1
  276. package/phanpy/dist/assets/icons/round-line-B9k_1uo-.js +0 -1
  277. package/phanpy/dist/assets/icons/route-line-B28ReHcE.js +0 -1
  278. package/phanpy/dist/assets/icons/rows-4-line-CrBniagM.js +0 -1
  279. package/phanpy/dist/assets/icons/scan-line-DSf74YdP.js +0 -1
  280. package/phanpy/dist/assets/icons/search-2-line-B6geIU1z.js +0 -1
  281. package/phanpy/dist/assets/icons/settings-3-line-azJwK47w.js +0 -1
  282. package/phanpy/dist/assets/icons/settings-6-line-C_5zc5YR.js +0 -1
  283. package/phanpy/dist/assets/icons/share-2-line-D5MxWjrm.js +0 -1
  284. package/phanpy/dist/assets/icons/share-forward-line-Cp61fZzZ.js +0 -1
  285. package/phanpy/dist/assets/icons/sparkles-2-line-DYYyXTaW.js +0 -1
  286. package/phanpy/dist/assets/icons/sparkles-line-C2hY1s6I.js +0 -1
  287. package/phanpy/dist/assets/icons/time-line-BF3eet1v.js +0 -1
  288. package/phanpy/dist/assets/icons/transfer-4-line-oisfvTBw.js +0 -1
  289. package/phanpy/dist/assets/icons/translate-line-BFWol2Ce.js +0 -1
  290. package/phanpy/dist/assets/icons/unlock-line-X_x5vwv3.js +0 -1
  291. package/phanpy/dist/assets/icons/upload-3-line-DgzwUJeW.js +0 -1
  292. package/phanpy/dist/assets/icons/user-4-line-BVSynZCp.js +0 -1
  293. package/phanpy/dist/assets/icons/user-add-2-line-BlGS5Q8q.js +0 -1
  294. package/phanpy/dist/assets/icons/user-add-line-BbXdHG4z.js +0 -1
  295. package/phanpy/dist/assets/icons/user-edit-line-BFAHOV0X.js +0 -1
  296. package/phanpy/dist/assets/icons/user-follow-line-BXux0wSd.js +0 -1
  297. package/phanpy/dist/assets/icons/user-star-line-DdYlpHLU.js +0 -1
  298. package/phanpy/dist/assets/icons/user-warning-line-Cj43j0_E.js +0 -1
  299. package/phanpy/dist/assets/icons/user-x-line-DIDgNlPH.js +0 -1
  300. package/phanpy/dist/assets/icons/volume-line-Bs390KQt.js +0 -1
  301. package/phanpy/dist/assets/icons/volume-mute-line-DzlzIqFZ.js +0 -1
  302. package/phanpy/dist/assets/icons/walk-line-yNXfB-md.js +0 -1
  303. package/phanpy/dist/assets/icons/world-2-line-CenkGIwj.js +0 -1
  304. package/phanpy/dist/assets/icons/zoom-in-line-CaENUphf.js +0 -1
  305. package/phanpy/dist/assets/icons/zoom-out-line-BDNk0EWo.js +0 -1
  306. package/phanpy/dist/assets/instances-BNPptnn-.json +0 -333
  307. package/phanpy/dist/assets/locales/ar-SA-CfheRbCu.js +0 -1
  308. package/phanpy/dist/assets/locales/ca-ES-DHRW86Tz.js +0 -1
  309. package/phanpy/dist/assets/locales/cs-CZ-CKoqEuA1.js +0 -1
  310. package/phanpy/dist/assets/locales/de-DE-ByRPMRFy.js +0 -1
  311. package/phanpy/dist/assets/locales/eo-UY-CPC7wLCS.js +0 -1
  312. package/phanpy/dist/assets/locales/es-ES-dJa0Fd9t.js +0 -1
  313. package/phanpy/dist/assets/locales/eu-ES-C3djlPR2.js +0 -1
  314. package/phanpy/dist/assets/locales/fa-IR-DTT490Ck.js +0 -1
  315. package/phanpy/dist/assets/locales/fi-FI-B23MCPuz.js +0 -1
  316. package/phanpy/dist/assets/locales/fr-FR-BcaQsO5C.js +0 -1
  317. package/phanpy/dist/assets/locales/gl-ES-BR-CVKBN.js +0 -1
  318. package/phanpy/dist/assets/locales/he-IL-CfheRbCu.js +0 -1
  319. package/phanpy/dist/assets/locales/hu-HU-CfheRbCu.js +0 -1
  320. package/phanpy/dist/assets/locales/it-IT-C1136UFV.js +0 -1
  321. package/phanpy/dist/assets/locales/ja-JP-CRZmRAhD.js +0 -1
  322. package/phanpy/dist/assets/locales/kab-Cz8xH_TT.js +0 -1
  323. package/phanpy/dist/assets/locales/ko-KR-CG9UiY4o.js +0 -1
  324. package/phanpy/dist/assets/locales/lt-LT-CJcI6sHZ.js +0 -1
  325. package/phanpy/dist/assets/locales/nb-NO-CfheRbCu.js +0 -1
  326. package/phanpy/dist/assets/locales/nl-NL-DxN4Yc1t.js +0 -1
  327. package/phanpy/dist/assets/locales/oc-FR-CfheRbCu.js +0 -1
  328. package/phanpy/dist/assets/locales/pl-PL-DkwGXf6P.js +0 -1
  329. package/phanpy/dist/assets/locales/pseudo-LOCALE-CQ4XxkQ0.js +0 -1
  330. package/phanpy/dist/assets/locales/pt-BR-nGL0HdFS.js +0 -1
  331. package/phanpy/dist/assets/locales/pt-PT-Cp17aj2z.js +0 -1
  332. package/phanpy/dist/assets/locales/ru-RU-0Y8cIoaK.js +0 -1
  333. package/phanpy/dist/assets/locales/th-TH-CfheRbCu.js +0 -1
  334. package/phanpy/dist/assets/locales/tok-CfheRbCu.js +0 -1
  335. package/phanpy/dist/assets/locales/tr-TR-CfheRbCu.js +0 -1
  336. package/phanpy/dist/assets/locales/uk-UA-C5E3dFBf.js +0 -1
  337. package/phanpy/dist/assets/locales/zh-CN-BHJi7_et.js +0 -1
  338. package/phanpy/dist/assets/locales/zh-TW-CfheRbCu.js +0 -1
  339. package/phanpy/dist/assets/main-BdqNbG-a.js +0 -26
  340. package/phanpy/dist/assets/main-BdqNbG-a.js.map +0 -1
  341. package/phanpy/dist/assets/mock-home-DGhR__He.js +0 -2
  342. package/phanpy/dist/assets/mock-home-DGhR__He.js.map +0 -1
  343. package/phanpy/dist/assets/multi-column-ROck0NVt.jpg +0 -0
  344. package/phanpy/dist/assets/multi-hashtag-timeline-DH5INVHi.jpg +0 -0
  345. package/phanpy/dist/assets/nested-comments-thread-hv59kZLc.jpg +0 -0
  346. package/phanpy/dist/assets/polyfill-force-H8sUkLGp.js +0 -2
  347. package/phanpy/dist/assets/polyfill-force-H8sUkLGp.js.map +0 -1
  348. package/phanpy/dist/assets/pwa-viewport-ogKbF51K.js +0 -20
  349. package/phanpy/dist/assets/pwa-viewport-ogKbF51K.js.map +0 -1
  350. package/phanpy/dist/assets/style-B1iLPTPR.css +0 -2
  351. package/phanpy/dist/assets/temml-DuyIQMei.js +0 -97
  352. package/phanpy/dist/assets/temml-DuyIQMei.js.map +0 -1
  353. package/phanpy/dist/assets/tinyld.light.node-DtbBhLYC.js +0 -2
  354. package/phanpy/dist/assets/tinyld.light.node-DtbBhLYC.js.map +0 -1
  355. package/phanpy/dist/assets/year-in-posts-B2lywqdx.png +0 -0
  356. package/phanpy/dist/assets/year-in-posts-DJv_star.js +0 -3
  357. package/phanpy/dist/assets/year-in-posts-DJv_star.js.map +0 -1
  358. package/phanpy/dist/compose/index.html +0 -20
  359. package/phanpy/dist/favicon.ico +0 -0
  360. package/phanpy/dist/index.html +0 -73
  361. package/phanpy/dist/logo-192.png +0 -0
  362. package/phanpy/dist/logo-512.png +0 -0
  363. package/phanpy/dist/logo-badge-72.png +0 -0
  364. package/phanpy/dist/logo-maskable-512.png +0 -0
  365. package/phanpy/dist/logo-monochrome-512.png +0 -0
  366. package/phanpy/dist/logo-monochrome-maskable-512.png +0 -0
  367. package/phanpy/dist/manifest.webmanifest +0 -1
  368. package/phanpy/dist/og-image-2.jpg +0 -0
  369. package/phanpy/dist/og-image.png +0 -0
  370. package/phanpy/dist/robots.txt +0 -2
  371. package/phanpy/dist/sw.js +0 -2
  372. package/phanpy/dist/sw.js.map +0 -1
  373. package/phanpy/dist/version.json +0 -1
  374. package/run-agent.mjs +0 -899
  375. package/vendor/gate.cjs +0 -138
  376. package/vendor/idp-grant.cjs +0 -394
  377. package/vendor/jfetch.cjs +0 -16
  378. package/web/admin/actors.js +0 -147
  379. package/web/admin/bar.css +0 -126
  380. package/web/admin/bar.js +0 -99
  381. package/web/admin/client/client.js +0 -66
  382. package/web/admin/client/index.html +0 -69
  383. package/web/admin/client-pick.js +0 -79
  384. package/web/admin/common.js +0 -23
  385. package/web/admin/connections.js +0 -112
  386. package/web/admin/gateway.js +0 -133
  387. package/web/admin/group.js +0 -258
  388. package/web/admin/index.html +0 -612
  389. package/web/admin/notices-bar.js +0 -90
  390. package/web/admin/oauth-signin.mjs +0 -37
  391. package/web/admin/record.js +0 -393
  392. package/web/admin/setup/index.html +0 -257
  393. package/web/admin/setup/setup.js +0 -339
  394. package/web/admin/tokens.css +0 -68
  395. package/web/admin/upkeep.js +0 -177
  396. package/web/admin/window.css +0 -55
  397. package/web/admin/window.js +0 -136
  398. package/web/app/README.md +0 -81
  399. package/web/app/admin-facade.mjs +0 -588
  400. package/web/app/agent.mjs +0 -672
  401. package/web/app/atproto-browser.mjs +0 -86
  402. package/web/app/authorize_interaction.html +0 -56
  403. package/web/app/authorize_interaction.js +0 -69
  404. package/web/app/boot.mjs +0 -616
  405. package/web/app/deliver-relay.mjs +0 -129
  406. package/web/app/dist/boot.js +0 -34562
  407. package/web/app/dist/boot.js.map +0 -7
  408. package/web/app/dist/sw.js +0 -73456
  409. package/web/app/dist/sw.js.map +0 -7
  410. package/web/app/fediacct-browser.mjs +0 -146
  411. package/web/app/gateway-move.mjs +0 -60
  412. package/web/app/idb-kv.mjs +0 -35
  413. package/web/app/index.html +0 -258
  414. package/web/app/keys-browser.mjs +0 -71
  415. package/web/app/keystore.mjs +0 -82
  416. package/web/app/oidc-session.mjs +0 -6
  417. package/web/app/pod-auth.mjs +0 -248
  418. package/web/app/pod-remote.mjs +0 -84
  419. package/web/app/shims/_globals.mjs +0 -2
  420. package/web/app/shims/fedify-sig.mjs +0 -77
  421. package/web/app/shims/node-crypto.mjs +0 -113
  422. package/web/app/shims/node-fs.mjs +0 -17
  423. package/web/app/shims/node-path.mjs +0 -19
  424. package/web/app/shims/node-url.mjs +0 -7
  425. package/web/app/shims/prelude.js +0 -58
  426. package/web/app/shims/safefetch.mjs +0 -58
  427. package/web/app/shims/shapes-text.mjs +0 -8
  428. package/web/app/shims/web-push.mjs +0 -14
  429. package/web/app/signup.mjs +0 -357
  430. package/web/app/sw-src.mjs +0 -220
  431. package/web/app/update.js +0 -70
  432. package/web/front/admin.html +0 -55
  433. package/web/front/admin.js +0 -89
  434. package/web/front/install.sh +0 -69
  435. package/web/front/new-account.html +0 -35
  436. package/web/front/new-account.js +0 -13
  437. package/web/front/notices.html +0 -69
  438. package/web/front/notices.js +0 -107
  439. package/web/front/run.html +0 -64
  440. package/web/front/run.js +0 -221
  441. package/web/front/solid-oidc-client.js +0 -6
  442. package/web/icons/forum.svg +0 -11
  443. package/web/icons/gateway.svg +0 -14
  444. package/web/icons/local.svg +0 -11
@@ -1,428 +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 { chosenRoot, publicIndexKnown } from '../../../core/place.mjs';
17
- import { portFree, freePortFrom } from '../../ports.mjs';
18
- import { yieldDirectory } from '../../../gateway/directory.mjs';
19
- import { localFetch } from '../../../client/localapi.mjs';
20
- import { projectRoot, SETUP_PAGE } from '../static.mjs';
21
- import { secureOrigin } from '../origins.mjs';
22
-
23
- // The signup carry-over: gateway and identity parameters a signup page
24
- // recorded for this install (written by the installer, consumed by the first
25
- // setup run). Read fresh each time — the file is deleted once consumed.
26
- function readFirstRun() {
27
- const file = process.env.AP_FIRST_RUN;
28
- if (!file) return null;
29
- try {
30
- const fr = JSON.parse(fs.readFileSync(file, 'utf8'));
31
- return fr && typeof fr === 'object' ? fr : null;
32
- } catch { return null; }
33
- }
34
-
35
- // The same config shape the `gateway` CLI command writes.
36
- function gatewayConfigFrom(fr) {
37
- if (!fr?.gateway || !fr?.secret) return null;
38
- const target = String(fr.gateway);
39
- return fr.fronted
40
- ? { url: target.replace(/ap\/actor$/, 'ap/inbox/'), frontActor: target,
41
- mode: 'shadow', hmacSecret: String(fr.secret) }
42
- : { url: target.replace(/\/?$/, '/'), mode: 'shadow', hmacSecret: String(fr.secret) };
43
- }
44
-
45
- export async function get(p, ctx, req, res, url) { // eslint-disable-line no-unused-vars
46
- const { agent, log, allowed, embedded, port, publicOrigin, versionOnDisk, isGroup } = ctx; // eslint-disable-line no-unused-vars
47
- const json = (res, status, obj) => { ctx.json(res, status, obj); return true; };
48
-
49
- // ---- setup and configuration, for the pages under /admin/ ----
50
- // These answer for a group too: a group is set up in the browser like
51
- // anything else, and it has a display name to change.
52
- if (req.method === 'GET' && p === '/setup/state') {
53
- const home = agent.home || null;
54
- const held = home && hasCredential(home) ? agent.readCredential?.() : null;
55
- return json(res, 200, {
56
- hasCredential: !!(home && hasCredential(home)),
57
- configured: agent.configured(),
58
- // Not the same question: a crashed setup leaves a credential with no
59
- // actor behind it, and that is finishable without minting a second.
60
- resumable: !!(home && hasCredential(home)) && !agent.configured(),
61
- running: ctx.setup.run?.phase === 'running',
62
- phase: ctx.setup.run?.phase || 'idle',
63
- home,
64
- port,
65
- handle: agent.store.getConfig()?.handle || allowed.label || null,
66
- kind: agent.store.getConfig()?.kind || null,
67
- origins: {
68
- loopback: publicOrigin
69
- || `https://localhost:${port}/`,
70
- named: publicOrigin
71
- ? null
72
- : allowed.label
73
- ? (true
74
- ? `https://${allowed.label}.localhost:${port}/`
75
- : `http://${allowed.label}.localhost:${port}/`)
76
- : null,
77
- },
78
- // Set in the environment, the password never needs to reach the page.
79
- passwordSupplied: !!process.env.AP_PASSWORD,
80
- identity: held ? { pod: held.remotePod, issuer: held.issuerOrigin, root: held.root || null } : null,
81
- defaults: { issuer: 'https://solidcommunity.net', port: 8030 },
82
- // What a signup page arranged for this install, so the form opens
83
- // pre-filled. The receipt secret stays out of it — the /setup run
84
- // reads that from the file itself.
85
- firstRun: (() => {
86
- if (agent.configured()) return null;
87
- const fr = readFirstRun();
88
- if (!fr?.handle) return null;
89
- let gatewayHost = null;
90
- try { gatewayHost = new URL(fr.gateway).host; } catch { /* no gateway named */ }
91
- return { pod: fr.pod || null, issuer: fr.issuer || null, handle: String(fr.handle),
92
- kind: fr.kind === 'group' ? 'group' : 'person', fronted: !!fr.fronted, gatewayHost };
93
- })(),
94
- });
95
- }
96
- if (req.method === 'GET' && p === '/setup/progress') {
97
- return json(res, 200, ctx.setup.run || { phase: 'idle', steps: [], error: null, result: null });
98
- }
99
-
100
- return false;
101
- }
102
-
103
- export async function post(p, body, ctx, req, res) { // eslint-disable-line no-unused-vars
104
- const { agent, log, allowed, embedded, port, publicOrigin, versionOnDisk, isGroup } = ctx; // eslint-disable-line no-unused-vars
105
- const json = (res, status, obj) => { ctx.json(res, status, obj); return true; };
106
- switch (p) {
107
- // Stop an agent whose pidfile is gone and which no terminal owns
108
- // (backgrounded, orphaned by a closed shell). It used to sit ABOVE both
109
- // the configured() gate and the isLocal one, so it was the only
110
- // state-changing route that needed neither — reachable from any host
111
- // AP_ALLOWED_HOSTS named, on an agent that had never been set up.
112
- case '/shutdown': {
113
- json(res, 200, { ok: true, stopping: process.pid });
114
- setTimeout(() => process.kill(process.pid, 'SIGTERM'), 50);
115
- return true;
116
- }
117
- // ---- setup, driven by the page at /admin/setup/ ----
118
- case '/setup/check': {
119
- const pre = { ...preflight(body), ...await placeCheck(body) };
120
- // For a new pod, ask the provider where it puts pods, so the page can
121
- // show the Gateway address before the pod exists. A provider on paths
122
- // means the address must live at the Gateway; the run decides for real.
123
- if (pre.ok && body.mode === 'new' && body.shape !== 'front') {
124
- const layout = await podLayout(fetch, body.issuer || '').catch(() => null);
125
- if (layout === 'path') {
126
- if ((body.kind || 'person') === 'group') {
127
- return json(res, 200, { ...pre, ok: false, layout, refusal: 'group-needs-host-root' });
128
- }
129
- let gwHost; try { gwHost = new URL(body.gatewayOrigin || 'https://fedipod.net').host; } catch { gwHost = 'fedipod.net'; }
130
- return json(res, 200, { ...pre, layout, fronted: true, forced: true, shape: 'front', gatewayHost: gwHost,
131
- address: `@${body.handle}@${gwHost}`, resolvable: true });
132
- }
133
- return json(res, 200, { ...pre, layout });
134
- }
135
- return json(res, 200, pre);
136
- }
137
- // Discard a credential that never finished setup, so the account and
138
- // pod can be entered again. The credential a CSS server mints is shown
139
- // once, so a setup that stops after the mint (a wrong pod answers 401
140
- // to the first write) leaves the form in "finish" mode with no way to
141
- // re-enter what was wrong. This removes it locally and reopens the full
142
- // form. It does NOT revoke server-side — that needs the account
143
- // password (`fedipod revoke-credential`); the old credential is left on
144
- // the account, revocable from its dashboard.
145
- case '/setup/reset': {
146
- if (isCrossSiteNavigation(req)) return json(res, 403, { error: 'cross-site request' });
147
- // A working identity is never swapped out this way — that is a
148
- // teardown (`fedipod retire`), not a half-finished setup.
149
- if (agent.configured()) {
150
- return json(res, 409, { error: 'this home holds a working identity — retire it, do not reset' });
151
- }
152
- if (ctx.setup.run?.phase === 'running') {
153
- return json(res, 409, { error: 'setup is running — let it finish or stop it first', phase: 'running' });
154
- }
155
- const home = agent.home;
156
- if (!home) return json(res, 500, { error: 'this agent has no AP_HOME to reset' });
157
- const removed = hasCredential(home);
158
- if (removed) fs.rmSync(credentialPath(home), { force: true });
159
- // Drop the pod handle too, so configured() cannot flicker true off a
160
- // stale in-memory session while the fresh form is filled in.
161
- agent.remote = null;
162
- ctx.setup.run = null;
163
- return json(res, 200, { ok: true, removed });
164
- }
165
- case '/setup': {
166
- // A visited page must not be able to navigate this into existence.
167
- if (isCrossSiteNavigation(req)) return json(res, 403, { error: 'cross-site request' });
168
- if (agent.configured()) {
169
- return json(res, 409, {
170
- error: 'this home already holds an identity',
171
- pod: agent.store.getConfig()?.remotePod || null,
172
- });
173
- }
174
- if (ctx.setup.run?.phase === 'running') {
175
- return json(res, 409, { error: 'setup is already running', phase: 'running' });
176
- }
177
- const home = agent.home;
178
- if (!home) return json(res, 500, { error: 'this agent has no AP_HOME to set up' });
179
- // A crashed setup left a credential with no actor behind it. That is
180
- // finishable, and must NOT mint a second credential — the first is
181
- // unrecoverable and would be orphaned.
182
- const resuming = hasCredential(home);
183
- const answers = { ...body, password: body.password || process.env.AP_PASSWORD || '' };
184
- // The signup carry-over: attach to the gateway the signup arranged,
185
- // set before the first publish so the actor never advertises the pod
186
- // inbox first. The secret comes from the installer's file, never the
187
- // page.
188
- const gatewayCfg = gatewayConfigFrom(readFirstRun());
189
- if (gatewayCfg) answers.gateway = gatewayCfg;
190
- const bad = setupInputError(answers, resuming);
191
- if (bad) return json(res, 400, { error: bad });
192
- if (!resuming) {
193
- const pre = preflight(answers);
194
- if (!pre.ok) return json(res, 400, { ...pre, error: pre.error || pre.refusal });
195
- }
196
- // Widen the allowlist now, not at the end: the page offers to move to
197
- // the named origin, which has to answer before it is offered.
198
- allowed.setHandle(answers.handle);
199
- ctx.setup.run = newRun();
200
- // Deliberately not awaited. The credential a CSS server mints is
201
- // shown once, so what a run depends on is the file it writes, never
202
- // a connection a closed tab can take with it.
203
- runSetup({ home, agent, answers, run: ctx.setup.run, log })
204
- .then((r) => {
205
- // The carried secret is consumed; a finished setup deletes it.
206
- if (r?.phase === 'done' && process.env.AP_FIRST_RUN) {
207
- try { fs.unlinkSync(process.env.AP_FIRST_RUN); } catch { /* already gone */ }
208
- }
209
- })
210
- .catch(e => { ctx.setup.run.phase = 'error'; ctx.setup.run.error = e.message; });
211
- return json(res, 202, { ok: true, running: true, resuming });
212
- }
213
- // One identity per home, so a new actor means a new home, a free port
214
- // and a process there — then its own setup, run with the answers this
215
- // page collected. The reply is the address of the new actor's page,
216
- // which is where the progress of that setup is reported.
217
- case '/new-actor': {
218
- if (isCrossSiteNavigation(req)) return json(res, 403, { error: 'cross-site request' });
219
- const handle = String(body.handle || '').trim().toLowerCase();
220
- // The name first, and before anything is created: it is what becomes
221
- // a directory, and "mode must be new or existing" is a poor answer to
222
- // a handle that could climb out of profiles/.
223
- // Named after the handle rather than generated, so `profiles` and
224
- // `--profile <name>` stay legible a year from now.
225
- if (!/^[a-z0-9][a-z0-9_-]{0,30}$/.test(handle)) {
226
- return json(res, 400, { error: 'a handle is letters, digits, hyphens and underscores' });
227
- }
228
- const dir = path.join(rootOf(path.resolve(agent.home || '')), 'profiles', handle);
229
- if (fs.existsSync(path.join(dir, 'credential.json'))) {
230
- return json(res, 409, { error: `${handle} already exists — it is in the list above` });
231
- }
232
- // Then everything else setup needs, so a missing password is refused
233
- // here rather than after a process has been started for it.
234
- const bad = setupInputError({ ...body, handle });
235
- if (bad) return json(res, 400, { error: bad });
236
- const newPort = await freePortFrom(port + 1);
237
- if (!newPort) return json(res, 503, { error: 'no free port in the next 50' });
238
- fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
239
- const child = spawn(process.execPath, [path.join(projectRoot, 'run-agent.mjs')], {
240
- detached: true, stdio: 'ignore',
241
- env: { ...process.env, AP_HOME: dir, AP_PORT: String(newPort), AP_PROFILE: '' },
242
- });
243
- child.unref();
244
- // Answer only once it is listening: handing back a URL that is not
245
- // up yet shows a connection error instead of the setup form.
246
- let listening = false;
247
- for (let i = 0; i < 60 && !listening; i++) {
248
- await new Promise(r => setTimeout(r, 250));
249
- listening = await localFetch(agent.home, newPort, '/status', { timeout: 1000 })
250
- .then(r => r.ok).catch(() => false);
251
- }
252
- if (!listening) return json(res, 504, { error: `started it on ${newPort} but it never answered` });
253
-
254
- // Its own /setup owns the work — minting the credential, provisioning
255
- // the pod, publishing. It answers 202 and runs in the background, so
256
- // the page that opens next is the one reporting progress.
257
- const started = await localFetch(agent.home, newPort, '/setup', {
258
- method: 'POST',
259
- headers: { 'content-type': 'application/json' },
260
- body: JSON.stringify({ ...body, handle }),
261
- }).then(async r => ({ status: r.status, json: await r.json().catch(() => null) }))
262
- .catch(e => ({ status: 0, json: { error: e.message } }));
263
- if (started.status !== 202) {
264
- return json(res, started.status || 502, {
265
- error: started.json?.error || `the new agent refused setup (HTTP ${started.status})`,
266
- port: newPort, url: `${secureOrigin(handle, newPort)}${SETUP_PAGE}`,
267
- });
268
- }
269
- return json(res, 200,
270
- { ok: true, handle, port: newPort,
271
- url: `${secureOrigin(handle, newPort)}${SETUP_PAGE}` });
272
- }
273
- // Start an actor that is not running, so its page can be visited. The
274
- // same spawn as /new-actor, minus the making: this one has a home
275
- // already. Its recorded port is preferred so its address stays what it
276
- // was, but a port something else has taken is walked past, not fought.
277
- case '/start-actor': {
278
- if (isCrossSiteNavigation(req)) return json(res, 403, { error: 'cross-site request' });
279
- const name = String(body.name || '');
280
- const found = identityHomes(rootOf(path.resolve(agent.home || ''))).find(h => h.name === name);
281
- if (!found) return json(res, 404, { error: `no identity called ${name}` });
282
-
283
- let want = null;
284
- try { want = JSON.parse(fs.readFileSync(path.join(found.dir, 'agent.json'), 'utf8')).port || null; } catch {}
285
- if (want) {
286
- // Its own handle, not the profile directory's name: usually the
287
- // same, but a renamed profile makes them differ and the origin has
288
- // to be one that agent's own guard will accept.
289
- const live = await localFetch(agent.home, want, '/status', { timeout: 1000 })
290
- .then(r => (r.ok ? r.json() : null)).catch(() => null);
291
- if (live) {
292
- return json(res, 200, {
293
- ok: true, name, port: want, already: true,
294
- url: `${secureOrigin(live.handle || name, want)}/`,
295
- });
296
- }
297
- }
298
- // The port is not the only evidence it is running. An agent from
299
- // before agent.json was written at startup has a live pidfile and no
300
- // recorded port, and spawning past that gives one home two agents —
301
- // which is exactly how this machine ended up with two on profiles/jeff.
302
- let held = null;
303
- try { held = Number(fs.readFileSync(path.join(found.dir, 'agent.pid'), 'utf8').trim()) || null; } catch {}
304
- if (held) {
305
- let alive = false;
306
- try { process.kill(held, 0); alive = true; } catch { /* gone */ }
307
- if (alive) {
308
- return json(res, 409, {
309
- error: `${name} is already running as pid ${held}, on a port it never recorded. `
310
- + `Stop it first: kill ${held}`,
311
- });
312
- }
313
- }
314
- // A recorded port held by the directory door is still this
315
- // identity's port — the door steps aside for its owner.
316
- if (want && !await portFree(want)) await yieldDirectory(want, { portFree });
317
- const on = (want && await portFree(want)) ? want : await freePortFrom(port + 1);
318
- if (!on) return json(res, 503, { error: 'no free port in the next 50' });
319
-
320
- const started = spawn(process.execPath, [path.join(projectRoot, 'run-agent.mjs')], {
321
- detached: true, stdio: 'ignore',
322
- env: { ...process.env, AP_HOME: found.dir, AP_PORT: String(on), AP_PROFILE: '' },
323
- });
324
- started.unref();
325
- for (let i = 0; i < 60; i++) {
326
- await new Promise(r => setTimeout(r, 250));
327
- const up = await localFetch(agent.home, on, '/status', { timeout: 1000 })
328
- .then(r => (r.ok ? r.json() : null)).catch(() => null);
329
- if (up) {
330
- return json(res, 200, { ok: true, name, port: on,
331
- url: `${secureOrigin(up.handle || name, on)}/` });
332
- }
333
- }
334
- return json(res, 504, { error: `started ${name} on ${on} but it never answered` });
335
- }
336
- // Pull the latest published FediPod and restart the agents. What the
337
- // installer does on a re-run, offered where the version line is.
338
- case '/update': {
339
- if (isCrossSiteNavigation(req)) return json(res, 403, { error: 'cross-site request' });
340
- const { runUpdate, restartAgents, repoRoot } = await import('../../update.mjs');
341
- const r = runUpdate({ log });
342
- if (!r.ok) return json(res, 409, { error: r.note });
343
- json(res, 200, { ok: true, note: r.note, restarting: true });
344
- // The response first, the restart a beat later — a self-restart must
345
- // not take the answer down with it.
346
- setTimeout(() => {
347
- if (restartAgents({ log }) === 'self') {
348
- const child = spawn(process.execPath, [path.join(repoRoot(), 'run-agent.mjs')],
349
- { detached: true, stdio: 'ignore', env: process.env });
350
- child.unref();
351
- setTimeout(() => process.exit(0), 300);
352
- }
353
- }, 200);
354
- return true;
355
- }
356
- // Move the private half — every state document and the RDF tree — while
357
- // the agent runs: quiesce this process's writers, flush, copy, verify,
358
- // repoint the credential, then reconnect on the new location. The old
359
- // copy is left where it was, exactly as the CLI move leaves it.
360
- case '/state-move': {
361
- const cred = agent.readCredential?.();
362
- if (!cred) return json(res, 409, { error: 'no credential — run setup first' });
363
- const to = String(body.to || '').trim();
364
- if (!to) return json(res, 400, { error: 'say where it should go' });
365
- let target = null;
366
- if (to !== 'pod') {
367
- // A path or a URL — two chars before the colon, so a Windows drive
368
- // letter reads as a path rather than a scheme.
369
- const asPath = !/^[a-z][a-z0-9+.-]+:/i.test(to);
370
- const raw = asPath
371
- ? pathToFileURL(path.resolve(to.replace(/^~(?=[/\\]|$)/, os.homedir()))).href
372
- : to;
373
- target = raw.endsWith('/') ? raw : raw + '/';
374
- try { new URL(target); } catch { return json(res, 400, { error: `"${to}" is not a container URL or a path` }); }
375
- if (/^https?:/i.test(target)) {
376
- const bad = insecureUrlReason(target, 'private-data address');
377
- if (bad) return json(res, 400, { error: bad });
378
- }
379
- }
380
- const whereState = (c) => (c.privateRoot ? tildify(c.privateRoot) : 'on the pod');
381
- if ((cred.privateRoot || null) === target) {
382
- return json(res, 200, { ok: true, docs: 0, notes: 0, unchanged: true, now: whereState(cred) });
383
- }
384
- await agent.requestTakeover?.();
385
- agent.intake?.stop(); agent.deliverer?.stop(); agent.tagfeed?.stop(); agent.importer?.stop();
386
- try {
387
- await agent.store.flush();
388
- const destCred = { ...cred, privateRoot: target };
389
- const copied = await copyPrivateHalf({
390
- from: { state: agent.privateStorage(cred, 'state') },
391
- to: { state: agent.privateStorage(destCred, 'state') },
392
- log,
393
- });
394
- if (target) cred.privateRoot = target; else delete cred.privateRoot;
395
- if (isCurrent(cred)) cred.layout = CURRENT_LAYOUT; else delete cred.layout;
396
- writeJsonAtomic(path.join(agent.home, 'credential.json'), cred);
397
- // connect() rebuilds everything that pointed at the old location —
398
- // store, publisher, intake — and restarts them.
399
- agent.store.attach(agent.privateStorage(cred, 'state'));
400
- agent.stateLoaded = false;
401
- await agent.connect();
402
- return json(res, 200, { ok: true, ...copied, now: whereState(cred) });
403
- } catch (e) {
404
- // The credential is only repointed after a verified copy, so on any
405
- // failure reconnect puts the agent back to work where it was.
406
- agent.stateLoaded = false;
407
- await agent.connect().catch(() => {});
408
- return json(res, 502, { error: e.message });
409
- }
410
- }
411
- default: return false;
412
- }
413
- }
414
-
415
- // Where the data will go and whether the type index question has to be asked:
416
- // a pod you bring is read as a stranger would (its profile is public); a new
417
- // pod has no index, so the question is always asked for one.
418
- async function placeCheck(body) {
419
- if (body.mode === 'new') return { typeIndex: false };
420
- let base;
421
- try { base = new URL(String(body.pod || '')).href; } catch { return {}; }
422
- if (!base.endsWith('/')) base += '/';
423
- const place = chosenRoot(base, body.container);
424
- return {
425
- typeIndex: await publicIndexKnown(fetch, base),
426
- ...(place.problem ? { placeProblem: place.problem } : { place: base + place.root }),
427
- };
428
- }
@@ -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
- }