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,588 +0,0 @@
1
- // admin-facade.mjs — the owner's record/manage surface, answered in the browser.
2
- //
3
- // The Node agent serves web/admin over lib/admin.mjs (buildAdminSurface), which
4
- // is Node through and through — filesystem static serving, process control,
5
- // multi-actor, group moderation. The browser reuses the SAME web/admin pages
6
- // (staged static by Netlify) but cannot run that handler: it needs fs, http,
7
- // child_process. So this answers the DATA endpoints those pages call, for the
8
- // one thing a browser identity is: a single, personal, embedded actor.
9
- //
10
- // Personal only. Everything group (joins, members, moderation, mute) or local
11
- // (drain, profiles-of-siblings, start/stop, state-move, update) is either 404
12
- // here or hidden by the page on kind:'person'. What remains is reading and
13
- // editing your own identity: status, config, gateway, alias, key rotation.
14
- //
15
- // It mirrors lib/admin.mjs's handlers for those paths, calling the same agent
16
- // objects (store, publisher, intake, deliverer, remote) the Node agent does.
17
- import { publicHandle, webfingerHost } from '../../lib/core/wire.mjs';
18
- import * as podInbox from '../../lib/pod/inbox.mjs';
19
- import { normalizeImport, IMPORT_KINDS } from '../../lib/connections/import.mjs';
20
- import { followActor, followHandle } from '../../lib/core/social.mjs';
21
- import { hashPassword } from '../../lib/client/masto/index.mjs';
22
-
23
- // The identity itself — changing any means a different actor, i.e. a new setup.
24
- const PERMANENT_CONFIG = ['handle', 'remotePod', 'issuer', 'root', 'kind'];
25
- // Carried in the actor document, so a change is not live until republished.
26
- const WIRE_CONFIG = ['name', 'summary', 'icon', 'image', 'fields', 'aliases'];
27
-
28
- // The data endpoints this agent answers at the origin root (the page computes
29
- // its base as the origin, so it asks for /status, not /admin/status). Anything
30
- // not here falls through to the network as a static file or the gateway.
31
- export const ADMIN_PATHS = new Set([
32
- '/status', '/config', '/gateway', '/alias', '/rotate-key', '/import',
33
- '/deadletter', '/blocks', '/profiles', '/modqueue', '/log', '/fediacct', '/describe', '/follow',
34
- '/atproto', '/atproto/connect', '/atproto/disconnect',
35
- '/rebuild', '/move', '/retire', '/inbox/prune', '/park', '/revive', '/takeover',
36
- '/gateway/pause', '/gateway/close',
37
- '/fediacct/connect', '/fediacct/disconnect', '/fediacct/callback',
38
- ]);
39
-
40
- // The page the fediverse account's OAuth redirect lands on (a top-level
41
- // navigation, so it is HTML, not JSON). On success it steps back to the record
42
- // page on its own; either way it offers the link.
43
- function callbackHtml(ok, msg) {
44
- const esc = (s) => String(s).replace(/[&<>"]/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c]));
45
- return '<!doctype html><html lang="en"><head><meta charset="utf-8">'
46
- + '<meta name="viewport" content="width=device-width, initial-scale=1">'
47
- + (ok ? '<meta http-equiv="refresh" content="2;url=/admin/">' : '')
48
- + `<title>FediPod — ${ok ? 'connected' : 'not connected'}</title>`
49
- + '<link rel="stylesheet" href="/admin/tokens.css">'
50
- + '<style>body{font:20px/1.5 system-ui,sans-serif;margin:0;background:var(--surface);color:var(--fg);'
51
- + 'display:grid;min-height:100dvh;place-items:center}main{max-width:32rem;padding:2rem;text-align:center}'
52
- + 'h1{color:var(--heading)}a{color:var(--accent)}.err{color:var(--err)}</style></head><body><main>'
53
- + `<h1>${ok ? 'Account connected' : 'Could not connect'}</h1>`
54
- + `<p class="${ok ? '' : 'err'}">${esc(msg)}</p>`
55
- + '<p><a href="/admin/">Back to your account</a></p></main></body></html>';
56
- }
57
-
58
- export class AdminFacade {
59
- constructor({ agent, log = console.log }) { this.agent = agent; this.log = log; }
60
-
61
- // Resolve an old account (a URL, or @user@host) to its canonical actor id,
62
- // verifying by dereference the way intake does — a URL may claim any id, so
63
- // that id must answer for itself before it is stored as an alias.
64
- async resolveActor(input) {
65
- const a = this.agent;
66
- const s = String(input || '').trim().replace(/^@/, '');
67
- if (!s) return null;
68
- if (/^https?:\/\//.test(s)) {
69
- let doc = await a.intake.fetchAP(s).catch(() => null);
70
- if (doc?.id && doc.id !== s) {
71
- const own = await a.intake.fetchAP(doc.id).catch(() => null);
72
- doc = own?.id === doc.id ? own : null;
73
- }
74
- return doc?.id || null;
75
- }
76
- const at = s.indexOf('@');
77
- if (at < 1) return null;
78
- const host = s.slice(at + 1);
79
- const wf = `https://${host}/.well-known/webfinger?resource=${encodeURIComponent('acct:' + s)}`;
80
- try {
81
- const res = await a.deliverer.signedFetch(wf, { method: 'GET', headers: { accept: 'application/jrd+json' } });
82
- const jrd = await res.json();
83
- const self = (jrd?.links || []).find(l => l.rel === 'self' && /(activity|ld)\+json/.test(l.type || ''));
84
- if (!self?.href) return null;
85
- const doc = await a.intake.fetchAP(self.href).catch(() => null);
86
- return doc?.id || null;
87
- } catch { return null; }
88
- }
89
-
90
- /** Answer an admin data request. Returns true when it owns the path. */
91
- async handle(req, res, p, url, bodyText) {
92
- const a = this.agent;
93
- const json = (status, obj) => {
94
- res.writeHead(status, { 'content-type': 'application/json' });
95
- res.end(JSON.stringify(obj));
96
- return true;
97
- };
98
- if (!ADMIN_PATHS.has(p)) return false;
99
- const method = req.method;
100
- const isGroup = () => a.store.getConfig()?.kind === 'group';
101
-
102
- // ---- reads ----
103
- if (method === 'GET') {
104
- switch (p) {
105
- case '/status': return json(200, a.status());
106
- case '/config': {
107
- const cfg = a.store.getConfig();
108
- if (!cfg) return json(409, { error: 'agent not configured' });
109
- const urls = a.urls || a.publisher?.urls || null;
110
- const wfHost = urls ? new URL(urls.base).host : null;
111
- const address = cfg.gateway?.frontActor
112
- ? `@${publicHandle(cfg)}@${new URL(cfg.gateway.frontActor).host}`
113
- : (wfHost ? `@${cfg.handle}@${wfHost}` : null);
114
- return json(200, {
115
- handle: cfg.handle, remotePod: cfg.remotePod, issuer: cfg.issuer,
116
- root: cfg.root || null, kind: cfg.kind || 'person',
117
- actor: urls?.actor || null, webId: a.webId || null,
118
- accountId: urls?.actor ? a.store.idFor(urls.actor) : null,
119
- address,
120
- name: cfg.name || null, summary: cfg.summary || null, icon: cfg.icon || null,
121
- image: cfg.image || null, fields: cfg.fields || [],
122
- aliases: cfg.aliases || [],
123
- autoAcceptFollows: !!cfg.autoAcceptFollows,
124
- hasUiPassword: !!cfg.uiPassword,
125
- quiescedAt: cfg.quiescedAt || null, movedTo: cfg.movedTo || null,
126
- mode: a.status().mode,
127
- // Live from the credential's actual backend on THIS browser (a
128
- // browser-stored connection is only present where its token is);
129
- // crossPost stays a default-on config value, no longer a toggle.
130
- atproto: (() => { const s = a.atproto?.status(); return s?.connected
131
- ? { handle: s.handle, service: s.service, did: s.did, connected: true, storage: s.storage, feedPaused: s.feedPaused, crossPost: cfg.atproto?.crossPost ?? true }
132
- : null; })(),
133
- fediAccounts: a.fediaccts?.status() || [],
134
- });
135
- }
136
- case '/gateway': {
137
- const g = a.store.getConfig()?.gateway || null;
138
- return json(200, {
139
- configured: !!(g && g.url), url: g?.url || null, webId: g?.webId || null,
140
- frontActor: g?.frontActor || null, mode: g?.mode || 'off', hasSecret: !!g?.hmacSecret,
141
- stats: a.store.read('gateway-stats.json', { verified: 0, unverified: 0, lastAt: null }),
142
- // The account's standing at the gateway — paused, closed — as the
143
- // gateway last said it (agent.mjs tellGateway). The record page
144
- // shows its pause and close controls only when this is here.
145
- standing: a.gatewayStanding || null,
146
- });
147
- }
148
- case '/deadletter': return json(200, { items: a.store.getDeadLetters() });
149
- case '/blocks': return json(200, a.store.getBlocklist());
150
- // One personal identity — no siblings on a machine to list. The bar's
151
- // dropdown shows this one plus "add an account".
152
- case '/profiles': {
153
- const s = a.status();
154
- const host = s.actor ? new URL(s.actor).host : null;
155
- return json(200, {
156
- identities: [{
157
- name: s.handle, current: true, handle: s.handle,
158
- address: (s.handle && host) ? `${s.handle}@${host}` : null,
159
- admin: '/admin/', app: '/app/', kind: s.kind, mode: s.mode,
160
- }],
161
- });
162
- }
163
- case '/modqueue': return json(200, []); // person: no moderation queue
164
- case '/log': return json(200, { lines: [] }); // no local log file in the browser
165
- case '/fediacct': return json(200, { accounts: a.fediaccts?.status() || [] });
166
- case '/import':
167
- return json(200, a.importer ? a.importer.progress() : { running: false, total: 0, done: 0 });
168
- // The OAuth redirect returns here as a top-level navigation, so this one
169
- // answers with an HTML page, not JSON. It completes the connection, then
170
- // shows the result with a way back to the record page.
171
- case '/fediacct/callback': {
172
- const q = url.searchParams;
173
- const page = (ok, msg) => { res.writeHead(ok ? 200 : 400, { 'content-type': 'text/html; charset=utf-8' }); res.end(callbackHtml(ok, msg)); return true; };
174
- if (q.get('error')) return page(false, q.get('error_description') || q.get('error'));
175
- if (!q.get('code') || !q.get('state')) return page(false, 'that sign-in came back incomplete');
176
- try {
177
- const row = await a.fediaccts.complete({ state: q.get('state'), code: q.get('code') });
178
- // The roster is computed live from the backends by /config; a
179
- // browser-stored token must not put the account on the pod, so
180
- // nothing is written to pod config here.
181
- a.startAccts?.();
182
- return page(true, `${row.handle} is connected.`);
183
- } catch (e) { return page(false, e.message); }
184
- }
185
- default: return json(405, { error: 'GET not supported here' });
186
- }
187
- }
188
-
189
- if (method !== 'POST') return json(405, { error: 'POST only' });
190
- let body = {};
191
- try { body = bodyText ? JSON.parse(bodyText) : {}; }
192
- catch { return json(400, { error: 'expected JSON body' }); }
193
-
194
- // ---- writes ----
195
- switch (p) {
196
- case '/config': {
197
- const fixed = PERMANENT_CONFIG.filter(k => k in body);
198
- if (fixed.length) return json(400, { error: `${fixed.join(', ')} cannot be changed — that is the identity itself` });
199
- // Group-only knobs are not offered on a personal identity.
200
- for (const k of ['approveJoins', 'review', 'moderators']) {
201
- if (k in body) return json(404, { error: 'not a group' });
202
- }
203
- await a.requestTakeover?.();
204
- const cfg = { ...a.store.getConfig() };
205
- if ('name' in body) {
206
- if (!body.name) return json(400, { error: 'a display name is required' });
207
- cfg.name = String(body.name);
208
- }
209
- if ('summary' in body) cfg.summary = body.summary || undefined;
210
- if ('icon' in body) cfg.icon = body.icon || undefined;
211
- if ('image' in body) cfg.image = body.image || undefined;
212
- if ('fields' in body) {
213
- cfg.fields = (Array.isArray(body.fields) ? body.fields : [])
214
- .filter(f => f?.name?.trim())
215
- .map(f => ({ name: String(f.name).trim(), value: String(f.value ?? '').trim() }));
216
- }
217
- if ('autoAcceptFollows' in body) cfg.autoAcceptFollows = !!body.autoAcceptFollows;
218
- if ('password' in body) {
219
- if (body.password) cfg.uiPassword = hashPassword(body.password);
220
- else delete cfg.uiPassword;
221
- }
222
- const republish = WIRE_CONFIG.some(k => k in body);
223
- a.store.setConfig(cfg);
224
- if ('autoAcceptFollows' in body && a.publisher) a.publisher.config.autoAcceptFollows = cfg.autoAcceptFollows;
225
- if (republish && a.publisher) {
226
- Object.assign(a.publisher.config, {
227
- name: cfg.name, summary: cfg.summary, icon: cfg.icon,
228
- image: cfg.image, fields: cfg.fields, aliases: cfg.aliases,
229
- });
230
- }
231
- await a.store.flush();
232
- const pub = republish ? await a.publisher.publishProfile() : null;
233
- return json(200, {
234
- ok: true, published: republish,
235
- ...(pub?.unreachable?.length ? { unreachable: pub.unreachable } : {}),
236
- });
237
- }
238
-
239
- case '/describe': {
240
- const cfg = { ...a.store.getConfig() };
241
- if ('summary' in body) cfg.summary = body.summary || undefined;
242
- if ('icon' in body) cfg.icon = body.icon || undefined;
243
- a.store.setConfig(cfg);
244
- Object.assign(a.publisher.config, { summary: cfg.summary, icon: cfg.icon });
245
- await a.store.flush();
246
- await a.publisher.publishProfile();
247
- return json(200, { ok: true, summary: cfg.summary || null, icon: cfg.icon || null });
248
- }
249
-
250
- // Following somebody, the same call the Node agent's admin surface takes
251
- // (lib/device/admin/routes/social.mjs): by handle normally, by actor url
252
- // when WebFinger cannot answer for them. The remote-follow page
253
- // (/authorize_interaction) is what asks for it here — another server
254
- // sends its reader to that page when they press Follow and say they are
255
- // at this host.
256
- case '/follow': {
257
- await a.requestTakeover?.();
258
- if (body.actor) { await followActor(a, String(body.actor)); return json(200, { ok: true, actor: String(body.actor) }); }
259
- if (!body.handle) return json(400, { error: 'handle or actor required' });
260
- return json(200, await followHandle(a, String(body.handle)));
261
- }
262
-
263
- // Taking the account back. A viewer publishes nothing and drains
264
- // nothing, so an account whose lease is held by something that is not
265
- // running any more stays silent with no way to say so. The owner acting
266
- // HERE outranks whatever holds it.
267
- case '/takeover': {
268
- if (!await a.requestTakeover?.()) return json(503, { error: 'the lease could not be taken — the pod refused the write' });
269
- return json(200, { ok: true, mode: a.status().mode });
270
- }
271
-
272
- // The account's standing at its gateway: paused by its owner, or closed
273
- // for good (front-core: accounts that go quiet). Both go to the gateway
274
- // with the pod session as proof, and its answer is the new standing.
275
- case '/gateway/pause': {
276
- if (typeof body.paused !== 'boolean') return json(400, { error: 'paused must be true or false' });
277
- if (!a.pauseAtGateway || !a.gatewayApi) return json(501, { error: 'this account is not at a gateway' });
278
- const r = await a.pauseAtGateway(body.paused);
279
- if (!r) return json(502, { error: 'the gateway could not be reached' });
280
- return json(r.status === 200 ? 200 : r.status, r);
281
- }
282
- case '/gateway/close': {
283
- const handle = String(a.store.getConfig()?.handle || '').toLowerCase();
284
- if (!handle || String(body.confirm || '').toLowerCase() !== handle) return json(400, { error: 'type the handle to confirm' });
285
- if (!a.closeAtGateway || !a.gatewayApi) return json(501, { error: 'this account is not at a gateway' });
286
- const r = await a.closeAtGateway();
287
- if (!r) return json(502, { error: 'the gateway could not be reached' });
288
- return json(r.status === 200 ? 200 : r.status, r);
289
- }
290
-
291
- // Going quiet, and coming back. The record page's active/parked select.
292
- case '/park': {
293
- if (!await a.requestTakeover?.()) return json(503, { error: 'another device is active for this pod — park from there' });
294
- return json(200, { ok: true, ...await a.park() });
295
- }
296
- case '/revive': {
297
- if (!await a.requestTakeover?.()) return json(503, { error: 'another device is active for this pod — revive from there' });
298
- return json(200, { ok: true, ...await a.revive() });
299
- }
300
-
301
- case '/rotate-key': {
302
- await a.requestTakeover?.();
303
- const r = await a.rotateKey();
304
- return json(200, { ok: true, changed: !!r?.changed });
305
- }
306
-
307
- // Recover posts this browser lost, from what the pod still holds.
308
- case '/rebuild': {
309
- if (!await a.requestTakeover?.()) return json(503, { error: 'another device is active for this pod — recover from there' });
310
- return json(200, await a.publisher.rebuildStatuses({ fromNotes: !!body.fromNotes }));
311
- }
312
-
313
- // Catch up on a backlog: drop old inbox posts (older than `before`) but
314
- // keep applying the follows/unfollows/accepts/deletes in that window. The
315
- // browser's own Intake does the work, exactly as the Node agent's does.
316
- case '/inbox/prune': {
317
- if (!body.before) return json(400, { error: 'before (a date) required' });
318
- if (!await a.requestTakeover?.()) return json(503, { error: 'another device is active for this pod — discard from there' });
319
- return json(200, await a.intake.prune({ before: body.before, ...(body.keepConcerning ? { keepConcerning: true } : {}) }));
320
- }
321
-
322
- // Hand the account away: a federated Move migrates followers to the
323
- // target, this handle is left redirecting. The typed-handle interlock is
324
- // the same as the Node agent's — a stray click cannot produce it.
325
- case '/move': {
326
- if (!body.target) return json(400, { error: 'target required' });
327
- if (!body.confirm || body.confirm !== a.store.getConfig()?.handle) {
328
- return json(400, { error: 'type the handle to confirm' });
329
- }
330
- await a.requestTakeover?.();
331
- let target = String(body.target).trim();
332
- if (!/^https?:\/\//.test(target)) {
333
- const id = await this.resolveActor(target);
334
- if (!id) return json(400, { error: `could not resolve ${body.target}` });
335
- target = id;
336
- }
337
- return json(200, { ok: true, ...await a.moveTo(target) });
338
- }
339
-
340
- // End the identity for good: a Delete to every follower, the actor
341
- // replaced by a Tombstone that stays fetchable. Typed-handle interlock.
342
- case '/retire': {
343
- if (!body.confirm || body.confirm !== a.store.getConfig()?.handle) {
344
- return json(400, { error: 'type the handle to confirm' });
345
- }
346
- await a.requestTakeover?.();
347
- return json(200, { ok: true, ...await a.publisher.retireActor() });
348
- }
349
-
350
- case '/alias': {
351
- const urls = a.publisher?.urls;
352
- if (!urls) return json(409, { error: 'agent not connected yet' });
353
- if (!body.add && !body.remove) return json(400, { error: 'add or remove required' });
354
- await a.requestTakeover?.();
355
- const cfg = { ...a.store.getConfig() };
356
- const aliases = [...(cfg.aliases || [])];
357
- if (body.add) {
358
- if (!webfingerHost(urls.base) && !cfg.gateway?.frontActor) {
359
- return json(400, { error: 'this pod is suffixed, so other servers could never resolve it as a Move target' });
360
- }
361
- const id = await this.resolveActor(body.add);
362
- if (!id) return json(400, { error: `could not fetch the old account (${body.add}) — enter its URL or @user@host, and it must answer` });
363
- if (id === urls.actor) return json(400, { error: 'that is this account' });
364
- if (!aliases.includes(id)) aliases.push(id);
365
- } else {
366
- const target = String(body.remove).trim();
367
- if (!aliases.includes(target)) return json(404, { error: 'no such alias' });
368
- if (!body.confirm) {
369
- return json(409, { error: 'servers still retrying the Move check this alias — send confirm: true to remove it anyway' });
370
- }
371
- aliases.splice(aliases.indexOf(target), 1);
372
- }
373
- cfg.aliases = aliases;
374
- a.store.setConfig(cfg);
375
- a.publisher.config.aliases = aliases;
376
- await a.store.flush();
377
- const pub = await a.publisher.publishProfile();
378
- return json(200, { ok: true, aliases, ...(pub?.unreachable?.length ? { unreachable: pub.unreachable } : {}) });
379
- }
380
-
381
- case '/gateway': {
382
- const cfg = { ...a.store.getConfig() };
383
- const g = { ...(cfg.gateway || {}) };
384
- const inboxUrl = a.publisher?.urls?.inbox;
385
- if (body.action === 'check') {
386
- const front = String(body.front || '').replace(/\/+$/, '');
387
- try { new URL(front); } catch { return json(400, { error: 'front must be a gateway origin URL' }); }
388
- const chk = await fetch(`${front}/api/handle?handle=${encodeURIComponent(String(body.handle || '').toLowerCase())}`,
389
- { headers: { accept: 'application/json' } }).then(r => r.json()).catch(() => null);
390
- if (!chk) return json(502, { error: `${front} did not answer its handle check` });
391
- return json(200, { available: !!chk.available, reason: chk.reason || null });
392
- }
393
- await a.requestTakeover?.();
394
- const persist = async () => {
395
- cfg.gateway = g; a.store.setConfig(cfg);
396
- if (a.publisher) a.publisher.config.gateway = g;
397
- await a.store.flush();
398
- };
399
- if (body.action === 'configure') {
400
- if (!/^https:\/\/\S+$/.test(String(body.url || ''))) return json(400, { error: 'gateway url must be https' });
401
- if (!/^https?:\/\/\S+$/.test(String(body.webId || ''))) return json(400, { error: 'gateway webId must be a URL' });
402
- g.url = String(body.url); g.webId = String(body.webId);
403
- if (!g.hmacSecret) {
404
- const rnd = crypto.getRandomValues(new Uint8Array(32));
405
- g.hmacSecret = btoa(String.fromCharCode(...rnd));
406
- }
407
- g.mode = g.mode || 'off';
408
- if ('frontActor' in body) {
409
- const fa = String(body.frontActor || '');
410
- if (fa && !/^https:\/\/\S+\/ap\/actor$/.test(fa)) return json(400, { error: 'frontActor must be an https …/ap/actor URL' });
411
- g.frontActor = fa || undefined;
412
- }
413
- await persist();
414
- return json(200, { ok: true, mode: g.mode, url: g.url, webId: g.webId, hmacSecret: g.hmacSecret });
415
- }
416
- if (body.action === 'mode') {
417
- const target = body.mode;
418
- if (!['off', 'shadow', 'trust', 'locked'].includes(target)) return json(400, { error: 'mode must be off, shadow, trust or locked' });
419
- if (target !== 'off' && !g.url) return json(400, { error: 'point at a gateway first (action: configure)' });
420
- if (target === 'locked' && !g.webId) return json(400, { error: "locked needs the gateway's WebID — set it with action: configure" });
421
- const prev = g.mode || 'off';
422
- if (target === 'locked') await a.publisher?.lockInboxToGateway(g.webId);
423
- else if (prev === 'locked' && inboxUrl) await podInbox.setPosture(a.remote, a.urls, 'open');
424
- g.mode = target;
425
- await persist();
426
- if ((prev === 'off') !== (target === 'off')) await a.publisher?.publishProfile();
427
- else if (target !== 'off') await a.publisher?.publishGatewayPolicy().catch(() => {});
428
- return json(200, { ok: true, mode: g.mode });
429
- }
430
- // Attach the pod's mail through a gateway (fedipod.net and kin). The
431
- // agent proves the pod with its own DPoP session — no password — and the
432
- // front answers with the door URL and the receipt secret. Browser model
433
- // only: @you@yourpod (inbox door), never a fronted @you@front — the
434
- // latter renames every id and needs a process restart the browser has
435
- // no equivalent for.
436
- if (body.action === 'attach') {
437
- const front = String(body.front || '').replace(/\/+$/, '');
438
- let fu;
439
- try { fu = new URL(front); } catch { return json(400, { error: 'front must be the gateway origin, like https://fedipod.net' }); }
440
- if (fu.protocol !== 'https:' && !/^(localhost|127\.0\.0\.1)$|\.localhost$/.test(fu.hostname)) return json(400, { error: 'front must be https' });
441
- if (body.fronted === true) return json(400, { error: 'a fronted @you@front identity is not the browser model — the browser uses the mail-door form @you@yourpod' });
442
- const named = !!String(body.handle || '').trim();
443
- let handle = String(body.handle || cfg.handle || '').toLowerCase().trim();
444
- if (!handle) return json(400, { error: 'a name at the gateway is required' });
445
- const avail = async (h) => fetch(`${front}/api/handle?handle=${encodeURIComponent(h)}`,
446
- { headers: { accept: 'application/json' } }).then(r => r.json()).catch(() => null);
447
- let chk = await avail(handle);
448
- if (!chk) return json(502, { error: `${front} did not answer its handle check` });
449
- if (!chk.available && !named) {
450
- for (let i = 2; i <= 9 && !chk.available; i++) {
451
- const cand = `${handle}${i}`; const c = await avail(cand);
452
- if (c?.available) { handle = cand; chk = c; }
453
- }
454
- }
455
- if (!chk.available) return json(409, { error: chk.reason || `the name ${handle} is taken at ${front}` });
456
- const attach = await a.remote.session.fetch(`${front}/api/attach`, {
457
- method: 'POST', headers: { 'content-type': 'application/json' },
458
- body: JSON.stringify({ handle, podHome: a.urls.home, kind: cfg.kind || 'person', fronted: false }),
459
- }).catch(() => null);
460
- if (!attach) return json(502, { error: `${front} did not answer the attach` });
461
- const d = await attach.json().catch(() => ({}));
462
- if (attach.status !== 201) {
463
- return json(attach.status >= 400 && attach.status < 500 ? attach.status : 502, { error: d.error || `attach failed (HTTP ${attach.status})` });
464
- }
465
- g.url = String(d.doorInbox || `${front}/u/${handle}/ap/inbox/`);
466
- if (d.hmacSecret) g.hmacSecret = String(d.hmacSecret);
467
- if (!g.mode || g.mode === 'off') g.mode = 'shadow';
468
- await persist();
469
- await a.publisher?.publishProfile();
470
- await a.publisher?.publishGatewayPolicy?.().catch(() => {});
471
- return json(200, { ok: true, mode: g.mode, url: g.url });
472
- }
473
- // Detach: forget the gateway and re-advertise the pod's own inbox.
474
- if (body.action === 'forget') {
475
- if (g.frontActor) return json(400, { error: 'this identity publishes under a gateway front; detaching a front is not supported in the browser build' });
476
- const wasLocked = g.mode === 'locked';
477
- delete cfg.gateway; a.store.setConfig(cfg);
478
- if (a.publisher) a.publisher.config.gateway = undefined;
479
- await a.store.flush();
480
- if (wasLocked && inboxUrl) await podInbox.setPosture(a.remote, a.urls, 'open').catch(() => {});
481
- await a.publisher?.publishProfile();
482
- return json(200, { ok: true, mode: 'off', forgotten: true });
483
- }
484
- return json(400, { error: 'unknown gateway action' });
485
- }
486
-
487
- // Connect a Bluesky account. This is the one place an app password is
488
- // seen; it is stored owner-only on the pod (never returned), and the
489
- // mirror poll starts. Same shape as the Node agent's /atproto/connect.
490
- case '/atproto/connect': {
491
- if (!body.identifier || !body.appPassword) return json(400, { error: 'identifier and appPassword required' });
492
- await a.requestTakeover?.();
493
- let conn;
494
- try { conn = await a.atproto.connect({ service: body.service, identifier: body.identifier, appPassword: body.appPassword }); }
495
- catch (e) { return json(400, { error: e.message }); }
496
- const cfg = a.store.getConfig();
497
- a.store.setConfig({ ...cfg, atproto: { ...conn, crossPost: cfg.atproto?.crossPost ?? true } });
498
- await a.store.flush();
499
- a.startBsky?.();
500
- return json(200, { ok: true, ...a.atproto.status() });
501
- }
502
- case '/atproto/disconnect': {
503
- a.stopBsky?.();
504
- await a.atproto.disconnect();
505
- const { atproto, ...rest } = a.store.getConfig();
506
- a.store.setConfig(rest);
507
- await a.store.flush();
508
- return json(200, { ok: true });
509
- }
510
- // Bluesky settings: pause/resume the mirror, and move the credential
511
- // between this browser and the pod. Cross-post is default-on, not toggled.
512
- case '/atproto': {
513
- if (!a.atproto?.connected()) return json(400, { error: 'no bluesky account connected' });
514
- if ('feedPaused' in body) { a.atproto.setFeedPaused(!!body.feedPaused); a.restartBsky?.(); }
515
- if ('storage' in body) {
516
- if (!['pod', 'browser'].includes(body.storage)) return json(400, { error: 'storage must be pod or browser' });
517
- a.atproto.setStorage(body.storage);
518
- }
519
- return json(200, { ok: true, ...a.atproto.status() });
520
- }
521
-
522
- // Connect a fediverse account on another server: start its OAuth at that
523
- // server. The browser is then sent to `authorize`; the code comes back to
524
- // GET /fediacct/callback (above). The token never touches the pod.
525
- case '/fediacct/connect': {
526
- if (!body.host) return json(400, { error: 'the address of the server is required' });
527
- try {
528
- const { url: authorize } = await a.fediaccts.begin({ host: body.host, redirectUri: `${url.origin}/fediacct/callback` });
529
- return json(200, { ok: true, authorize });
530
- } catch (e) { return json(400, { error: e.message }); }
531
- }
532
- case '/fediacct/disconnect': {
533
- if (!body.id) return json(400, { error: 'id required' });
534
- await a.fediaccts.revoke(body.id); // best-effort revoke at the server, then forget it here
535
- if (!a.fediaccts.remove(body.id)) return json(404, { error: 'no such account' });
536
- a.restartAccts?.();
537
- return json(200, { ok: true });
538
- }
539
- // Pause/resume the mirror, or move the credential between browser and pod.
540
- case '/fediacct': {
541
- if (!body.id) return json(400, { error: 'id required' });
542
- let row;
543
- if ('storage' in body) {
544
- if (!['pod', 'browser'].includes(body.storage)) return json(400, { error: 'storage must be pod or browser' });
545
- row = a.fediaccts.setStorage(body.id, body.storage);
546
- } else row = a.fediaccts.setEnabled(body.id, !!body.enabled);
547
- if (!row) return json(404, { error: 'no such account' });
548
- a.restartAccts?.();
549
- return json(200, { ok: true, account: row });
550
- }
551
-
552
- // CSV import — follows, blocks, mutes, lists and domains, staged onto the
553
- // pod and applied by a worker over the following minutes. The same
554
- // ImportWorker the Node agent runs (lib/import.mjs); it needs only the
555
- // store and social.mjs, both of which are in this bundle.
556
- case '/import': {
557
- if (!a.importer) return json(409, { error: 'agent not connected yet' });
558
- // Staging arms a worker that keeps writing for minutes, so a device
559
- // that could not take the lease must not run one beside the device
560
- // that holds it.
561
- if (!await a.requestTakeover?.()) {
562
- return json(503, { error: 'another device is active for this pod — import from there' });
563
- }
564
- if (body.clear === true) {
565
- a.importer.clear();
566
- return json(200, { ok: true, cleared: true });
567
- }
568
- if (!IMPORT_KINDS.includes(body.kind)) {
569
- return json(400, { error: `kind must be one of: ${IMPORT_KINDS.join(', ')}` });
570
- }
571
- if (typeof body.text !== 'string' || !body.text.trim()) {
572
- return json(400, { error: 'text required — the CSV file contents' });
573
- }
574
- const { values, invalid } = normalizeImport(body.kind, body.text);
575
- const r = a.importer.stage(body.kind, values);
576
- await a.store.flush();
577
- return json(200, {
578
- ok: true, kind: body.kind, ...r,
579
- invalid: invalid.length,
580
- ...(invalid.length ? { invalidSample: invalid.slice(0, 5) } : {}),
581
- });
582
- }
583
-
584
- default:
585
- return json(404, { error: 'not available on a personal browser identity' });
586
- }
587
- }
588
- }