fedipod-server 0.28.1 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (438) hide show
  1. package/README.md +38 -34
  2. package/config/server.json +22 -0
  3. package/dist/claims.js +3 -2
  4. package/dist/directory.d.ts +2 -0
  5. package/dist/door.d.ts +4 -0
  6. package/dist/door.js +31 -0
  7. package/dist/fedipod.d.ts +16 -0
  8. package/dist/fedipod.js +13 -0
  9. package/dist/front.d.ts +3 -0
  10. package/dist/front.js +94 -0
  11. package/dist/handler.d.ts +39 -101
  12. package/dist/handler.js +86 -617
  13. package/dist/handler.jsonld +6 -30
  14. package/dist/identities.d.ts +62 -0
  15. package/dist/identities.js +236 -0
  16. package/dist/mounts.d.ts +43 -0
  17. package/dist/mounts.js +123 -0
  18. package/dist/opt-in.d.ts +46 -0
  19. package/dist/opt-in.js +176 -0
  20. package/dist/store-pod.js +37 -0
  21. package/package.json +10 -18
  22. package/lib/client/c2s.mjs +0 -582
  23. package/lib/client/localapi.mjs +0 -87
  24. package/lib/client/masto/accounts.mjs +0 -309
  25. package/lib/client/masto/body.mjs +0 -156
  26. package/lib/client/masto/index.mjs +0 -206
  27. package/lib/client/masto/instance.mjs +0 -112
  28. package/lib/client/masto/media.mjs +0 -79
  29. package/lib/client/masto/oauth.mjs +0 -656
  30. package/lib/client/masto/render.mjs +0 -518
  31. package/lib/client/masto/statuses.mjs +0 -372
  32. package/lib/client/masto/timelines.mjs +0 -438
  33. package/lib/client/oidc-auth.mjs +0 -62
  34. package/lib/client/streaming.mjs +0 -154
  35. package/lib/client/webpush.mjs +0 -98
  36. package/lib/connections/acctfeed.mjs +0 -257
  37. package/lib/connections/atproto.mjs +0 -309
  38. package/lib/connections/bskyfeed.mjs +0 -243
  39. package/lib/connections/bskygroup.mjs +0 -162
  40. package/lib/connections/fediacct.mjs +0 -253
  41. package/lib/connections/import.mjs +0 -357
  42. package/lib/connections/tagfeed.mjs +0 -212
  43. package/lib/connections/vault.mjs +0 -114
  44. package/lib/core/as2.mjs +0 -206
  45. package/lib/core/contexts/activitystreams.json +0 -379
  46. package/lib/core/contexts/anno.json +0 -126
  47. package/lib/core/contexts/did-v1.json +0 -57
  48. package/lib/core/contexts/fep-5711.json +0 -36
  49. package/lib/core/contexts/gotosocial.json +0 -86
  50. package/lib/core/contexts/identity-v1.json +0 -152
  51. package/lib/core/contexts/index.mjs +0 -63
  52. package/lib/core/contexts/join-lemmy.json +0 -33
  53. package/lib/core/contexts/joinmastodon.json +0 -28
  54. package/lib/core/contexts/litepub-0.1.json +0 -60
  55. package/lib/core/contexts/map.json +0 -19
  56. package/lib/core/contexts/miscellany.json +0 -19
  57. package/lib/core/contexts/quotes.json +0 -18
  58. package/lib/core/contexts/schemaorg.json +0 -8845
  59. package/lib/core/contexts/security-data-integrity-v1.json +0 -78
  60. package/lib/core/contexts/security-data-integrity-v2.json +0 -81
  61. package/lib/core/contexts/security-multikey-v1.json +0 -35
  62. package/lib/core/contexts/security-v1.json +0 -74
  63. package/lib/core/contexts/webfinger.json +0 -10
  64. package/lib/core/deliver.mjs +0 -371
  65. package/lib/core/graphview.mjs +0 -327
  66. package/lib/core/intake/activities.mjs +0 -633
  67. package/lib/core/intake/activity.mjs +0 -318
  68. package/lib/core/intake/channel.mjs +0 -144
  69. package/lib/core/intake/group.mjs +0 -256
  70. package/lib/core/intake/index.mjs +0 -701
  71. package/lib/core/intake/notes.mjs +0 -360
  72. package/lib/core/intake/verify.mjs +0 -142
  73. package/lib/core/keys.mjs +0 -153
  74. package/lib/core/lease.mjs +0 -206
  75. package/lib/core/polls.mjs +0 -105
  76. package/lib/core/profile-page.mjs +0 -104
  77. package/lib/core/proof.mjs +0 -79
  78. package/lib/core/publisher/collections.mjs +0 -237
  79. package/lib/core/publisher/index.mjs +0 -500
  80. package/lib/core/publisher/notes.mjs +0 -453
  81. package/lib/core/publisher/questions.mjs +0 -239
  82. package/lib/core/publisher/restore.mjs +0 -224
  83. package/lib/core/shapes/activitystreams.ttl +0 -157
  84. package/lib/core/shapes/index.mjs +0 -132
  85. package/lib/core/shapes/shapes-text.mjs +0 -13
  86. package/lib/core/social.mjs +0 -531
  87. package/lib/core/storage.mjs +0 -176
  88. package/lib/core/store.mjs +0 -568
  89. package/lib/core/wire-quotes.mjs +0 -71
  90. package/lib/core/wire.mjs +0 -889
  91. package/lib/device/account.mjs +0 -87
  92. package/lib/device/admin/index.mjs +0 -13
  93. package/lib/device/admin/origins.mjs +0 -35
  94. package/lib/device/admin/routes/connections.mjs +0 -144
  95. package/lib/device/admin/routes/gateway.mjs +0 -199
  96. package/lib/device/admin/routes/lifecycle.mjs +0 -200
  97. package/lib/device/admin/routes/owner.mjs +0 -322
  98. package/lib/device/admin/routes/setup.mjs +0 -412
  99. package/lib/device/admin/routes/social.mjs +0 -188
  100. package/lib/device/admin/server.mjs +0 -95
  101. package/lib/device/admin/static.mjs +0 -247
  102. package/lib/device/admin/surface.mjs +0 -315
  103. package/lib/device/certs.mjs +0 -281
  104. package/lib/device/cli/commands/account.mjs +0 -586
  105. package/lib/device/cli/commands/run.mjs +0 -278
  106. package/lib/device/cli/commands/service.mjs +0 -221
  107. package/lib/device/cli/commands/setup.mjs +0 -447
  108. package/lib/device/cli/commands/state.mjs +0 -561
  109. package/lib/device/cli/context.mjs +0 -288
  110. package/lib/device/export-collections.mjs +0 -161
  111. package/lib/device/gateway-move.mjs +0 -58
  112. package/lib/device/home.mjs +0 -161
  113. package/lib/device/migrate.mjs +0 -115
  114. package/lib/device/ports.mjs +0 -27
  115. package/lib/device/remote.mjs +0 -54
  116. package/lib/device/setup.mjs +0 -459
  117. package/lib/device/update.mjs +0 -96
  118. package/lib/gateway/caches.mjs +0 -36
  119. package/lib/gateway/directory.mjs +0 -90
  120. package/lib/gateway/front-core.mjs +0 -970
  121. package/lib/gateway/gateway-core.mjs +0 -165
  122. package/lib/gateway/headers.mjs +0 -49
  123. package/lib/gateway/httpsig.mjs +0 -159
  124. package/lib/gateway/notices.mjs +0 -84
  125. package/lib/gateway/quiet.mjs +0 -189
  126. package/lib/pod/README.md +0 -61
  127. package/lib/pod/actor.mjs +0 -96
  128. package/lib/pod/collection.mjs +0 -77
  129. package/lib/pod/containers.mjs +0 -140
  130. package/lib/pod/discovery.mjs +0 -46
  131. package/lib/pod/featured.mjs +0 -18
  132. package/lib/pod/followers.mjs +0 -24
  133. package/lib/pod/following.mjs +0 -10
  134. package/lib/pod/http.mjs +0 -74
  135. package/lib/pod/inbox.mjs +0 -166
  136. package/lib/pod/links.mjs +0 -35
  137. package/lib/pod/media.mjs +0 -27
  138. package/lib/pod/notes.mjs +0 -80
  139. package/lib/pod/notifications.mjs +0 -89
  140. package/lib/pod/outbox.mjs +0 -18
  141. package/lib/pod/package.json +0 -19
  142. package/lib/pod/policy.mjs +0 -58
  143. package/lib/pod/private.mjs +0 -19
  144. package/lib/pod/root.mjs +0 -104
  145. package/lib/pod/state.mjs +0 -40
  146. package/lib/pod/transport.mjs +0 -525
  147. package/lib/pod/urls.mjs +0 -85
  148. package/lib/server/embed.mjs +0 -423
  149. package/lib/session/README.md +0 -137
  150. package/lib/session/demo.html +0 -94
  151. package/lib/session/fedi-account.mjs +0 -408
  152. package/lib/session/fedi-login.mjs +0 -123
  153. package/lib/session/oidc-session.mjs +0 -233
  154. package/lib/session/package.json +0 -49
  155. package/lib/shared/guard.mjs +0 -213
  156. package/lib/shared/links.mjs +0 -9
  157. package/lib/shared/safefetch.mjs +0 -235
  158. package/lib/shared/ua.mjs +0 -29
  159. package/phanpy/dist/404.html +0 -40
  160. package/phanpy/dist/_headers +0 -2
  161. package/phanpy/dist/apple-touch-icon.png +0 -0
  162. package/phanpy/dist/assets/ICONS-DIPJeU0C.js +0 -2
  163. package/phanpy/dist/assets/ICONS-DIPJeU0C.js.map +0 -1
  164. package/phanpy/dist/assets/ScheduledAtField-D-Abfo4J.js +0 -183
  165. package/phanpy/dist/assets/ScheduledAtField-D-Abfo4J.js.map +0 -1
  166. package/phanpy/dist/assets/Temml-CCjoacWt.woff2 +0 -0
  167. package/phanpy/dist/assets/__vite-browser-external-C7iut881.js +0 -2
  168. package/phanpy/dist/assets/__vite-browser-external-C7iut881.js.map +0 -1
  169. package/phanpy/dist/assets/boosts-carousel-BiOaNdMT.jpg +0 -0
  170. package/phanpy/dist/assets/catch-up-O5q5C75Z.png +0 -0
  171. package/phanpy/dist/assets/chunk-aKtaBQYM.js +0 -1
  172. package/phanpy/dist/assets/compose-CFEx7wb4.js +0 -2
  173. package/phanpy/dist/assets/compose-CFEx7wb4.js.map +0 -1
  174. package/phanpy/dist/assets/compose-ZJN5xSb4.js +0 -35
  175. package/phanpy/dist/assets/compose-ZJN5xSb4.js.map +0 -1
  176. package/phanpy/dist/assets/debug.module-YFQgwnIr.js +0 -52
  177. package/phanpy/dist/assets/debug.module-YFQgwnIr.js.map +0 -1
  178. package/phanpy/dist/assets/dom-p1YcJ5Rw.js +0 -2
  179. package/phanpy/dist/assets/dom-p1YcJ5Rw.js.map +0 -1
  180. package/phanpy/dist/assets/home-mobile-dark@2x-Dc-J8-SM.png +0 -0
  181. package/phanpy/dist/assets/home-mobile-light@2x-Bd3fod8m.png +0 -0
  182. package/phanpy/dist/assets/home-tablet-dark@2x-BgcNI8le.png +0 -0
  183. package/phanpy/dist/assets/home-tablet-light@2x-BW2RV9T6.png +0 -0
  184. package/phanpy/dist/assets/icons/add-circle-line-WD0mwORD.js +0 -1
  185. package/phanpy/dist/assets/icons/alert-line-CpFUMf9s.js +0 -1
  186. package/phanpy/dist/assets/icons/android-2-line-Bzxnrd3S.js +0 -1
  187. package/phanpy/dist/assets/icons/announcement-line-5nwaJFJm.js +0 -1
  188. package/phanpy/dist/assets/icons/arrow-down-circle-line-a7Id9-Iv.js +0 -1
  189. package/phanpy/dist/assets/icons/arrow-down-line-B8uzXzq6.js +0 -1
  190. package/phanpy/dist/assets/icons/arrow-left-line-CGrGT9C-.js +0 -1
  191. package/phanpy/dist/assets/icons/arrow-right-line-CE-HERfX.js +0 -1
  192. package/phanpy/dist/assets/icons/arrow-to-up-line-CvdQjmcy.js +0 -1
  193. package/phanpy/dist/assets/icons/arrow-up-circle-line-DEparSNF.js +0 -1
  194. package/phanpy/dist/assets/icons/arrow-up-line-C7yBeugc.js +0 -1
  195. package/phanpy/dist/assets/icons/arrows-right-line-BtbXmJGP.js +0 -1
  196. package/phanpy/dist/assets/icons/at-line-CF66Bznk.js +0 -1
  197. package/phanpy/dist/assets/icons/attachment-line-BY8It-l9.js +0 -1
  198. package/phanpy/dist/assets/icons/blockquote-line-BS6pU_hm.js +0 -1
  199. package/phanpy/dist/assets/icons/board-line-C0UWXyA6.js +0 -1
  200. package/phanpy/dist/assets/icons/bookmark-line-1CtX5JLF.js +0 -1
  201. package/phanpy/dist/assets/icons/building-5-line-Dd59MzbO.js +0 -1
  202. package/phanpy/dist/assets/icons/bus-2-line-6_dvA0GG.js +0 -1
  203. package/phanpy/dist/assets/icons/calendar-day-line-Crpl_fmu.js +0 -1
  204. package/phanpy/dist/assets/icons/calendar-month-line-vommts2C.js +0 -1
  205. package/phanpy/dist/assets/icons/calendar-time-add-line-BU_DTlQk.js +0 -1
  206. package/phanpy/dist/assets/icons/camera-line-Cv69MEei.js +0 -1
  207. package/phanpy/dist/assets/icons/celebrate-line-B8DvIoi0.js +0 -1
  208. package/phanpy/dist/assets/icons/chart-bar-line-FK-QOmtF.js +0 -1
  209. package/phanpy/dist/assets/icons/chart-line-line-BGcJAFWw.js +0 -1
  210. package/phanpy/dist/assets/icons/chat-3-line-YFK2-hwz.js +0 -1
  211. package/phanpy/dist/assets/icons/check-circle-line-DsAxFr5-.js +0 -1
  212. package/phanpy/dist/assets/icons/clipboard-line-BBp364gJ.js +0 -1
  213. package/phanpy/dist/assets/icons/close-circle-line-CICdiuGz.js +0 -1
  214. package/phanpy/dist/assets/icons/close-line-DYrFWcjv.js +0 -1
  215. package/phanpy/dist/assets/icons/cloud-line-tiQAbah8.js +0 -1
  216. package/phanpy/dist/assets/icons/code-line-fAl8iiV3.js +0 -1
  217. package/phanpy/dist/assets/icons/comment-2-line-CR3g7WeB.js +0 -1
  218. package/phanpy/dist/assets/icons/copy-2-line-CLW69yF5.js +0 -1
  219. package/phanpy/dist/assets/icons/delete-2-line-B8xmbr4n.js +0 -1
  220. package/phanpy/dist/assets/icons/document-3-line-DBGNhAg2.js +0 -1
  221. package/phanpy/dist/assets/icons/document-line-C7mb9sb7.js +0 -1
  222. package/phanpy/dist/assets/icons/down-line-BGqSSThd.js +0 -1
  223. package/phanpy/dist/assets/icons/edit-4-line-DcgGaEhx.js +0 -1
  224. package/phanpy/dist/assets/icons/emoji-2-line-D8YgnM-y.js +0 -1
  225. package/phanpy/dist/assets/icons/exit-line-Dc07BS-x.js +0 -1
  226. package/phanpy/dist/assets/icons/external-link-line-BvpSkmjO.js +0 -1
  227. package/phanpy/dist/assets/icons/eye-2-line-CiWXx-f_.js +0 -1
  228. package/phanpy/dist/assets/icons/eye-close-line-BgTaSJ4P.js +0 -1
  229. package/phanpy/dist/assets/icons/filter-2-line-B_mR2usy.js +0 -1
  230. package/phanpy/dist/assets/icons/filter-line-C_nm-3cU.js +0 -1
  231. package/phanpy/dist/assets/icons/flag-1-line-C2HB05I0.js +0 -1
  232. package/phanpy/dist/assets/icons/forbid-circle-line-D3LTT9yd.js +0 -1
  233. package/phanpy/dist/assets/icons/formula-line-DLrt2d6j.js +0 -1
  234. package/phanpy/dist/assets/icons/grid-line-CgUmEikf.js +0 -1
  235. package/phanpy/dist/assets/icons/group-line-C5fnX0wU.js +0 -1
  236. package/phanpy/dist/assets/icons/hand-finger-2-line-BGgxbuL9.js +0 -1
  237. package/phanpy/dist/assets/icons/hashtag-line-CyTEEXBb.js +0 -1
  238. package/phanpy/dist/assets/icons/heart-crack-line-BZddbO2C.js +0 -1
  239. package/phanpy/dist/assets/icons/heart-line-CJ7K47J4.js +0 -1
  240. package/phanpy/dist/assets/icons/history-2-line-kWWvFzqv.js +0 -1
  241. package/phanpy/dist/assets/icons/history-line-bGQiwbTH.js +0 -1
  242. package/phanpy/dist/assets/icons/home-3-line-fRU1zeLG.js +0 -1
  243. package/phanpy/dist/assets/icons/information-line-iNk6-sOY.js +0 -1
  244. package/phanpy/dist/assets/icons/keyboard-line-DsCyuQNz.js +0 -1
  245. package/phanpy/dist/assets/icons/layout-4-line-CZkOX52Z.js +0 -1
  246. package/phanpy/dist/assets/icons/layout-5-line-B_znt5wO.js +0 -1
  247. package/phanpy/dist/assets/icons/left-line-eUw0tya4.js +0 -1
  248. package/phanpy/dist/assets/icons/lightning-line-DaKoDdix.js +0 -1
  249. package/phanpy/dist/assets/icons/link-2-line-DCqLmMA7.js +0 -1
  250. package/phanpy/dist/assets/icons/list-check-line-BAxosBh9.js +0 -1
  251. package/phanpy/dist/assets/icons/lock-line-CPoaXeUW.js +0 -1
  252. package/phanpy/dist/assets/icons/mail-line-B0P8aa1L.js +0 -1
  253. package/phanpy/dist/assets/icons/moon-line-CEqNAToP.js +0 -1
  254. package/phanpy/dist/assets/icons/more-1-fill-Dw2e5xab.js +0 -1
  255. package/phanpy/dist/assets/icons/more-3-line-BIiVjYTz.js +0 -1
  256. package/phanpy/dist/assets/icons/notification-line-BZZq2Gtu.js +0 -1
  257. package/phanpy/dist/assets/icons/pencil-line-CKh8-A1v.js +0 -1
  258. package/phanpy/dist/assets/icons/photo-album-line-Bnfg2u40.js +0 -1
  259. package/phanpy/dist/assets/icons/pin-line-DpTolyhs.js +0 -1
  260. package/phanpy/dist/assets/icons/play-fill-B0-G_8lX.js +0 -1
  261. package/phanpy/dist/assets/icons/qrcode-2-line-B6SPCaHn.js +0 -1
  262. package/phanpy/dist/assets/icons/quill-pen-line-B6LUrnCP.js +0 -1
  263. package/phanpy/dist/assets/icons/quote-left-fill-hQZ1cj_6.js +0 -1
  264. package/phanpy/dist/assets/icons/radar-line-CKyroTth.js +0 -1
  265. package/phanpy/dist/assets/icons/react-line-CoIKhhtK.js +0 -1
  266. package/phanpy/dist/assets/icons/refresh-2-line-D0mTF97S.js +0 -1
  267. package/phanpy/dist/assets/icons/right-line-B3KWJaqc.js +0 -1
  268. package/phanpy/dist/assets/icons/rocket-line-4KZl537D.js +0 -1
  269. package/phanpy/dist/assets/icons/round-fill-C69NTrZ0.js +0 -1
  270. package/phanpy/dist/assets/icons/round-line-B9k_1uo-.js +0 -1
  271. package/phanpy/dist/assets/icons/route-line-B28ReHcE.js +0 -1
  272. package/phanpy/dist/assets/icons/rows-4-line-CrBniagM.js +0 -1
  273. package/phanpy/dist/assets/icons/scan-line-DSf74YdP.js +0 -1
  274. package/phanpy/dist/assets/icons/search-2-line-B6geIU1z.js +0 -1
  275. package/phanpy/dist/assets/icons/settings-3-line-azJwK47w.js +0 -1
  276. package/phanpy/dist/assets/icons/settings-6-line-C_5zc5YR.js +0 -1
  277. package/phanpy/dist/assets/icons/share-2-line-D5MxWjrm.js +0 -1
  278. package/phanpy/dist/assets/icons/share-forward-line-Cp61fZzZ.js +0 -1
  279. package/phanpy/dist/assets/icons/sparkles-2-line-DYYyXTaW.js +0 -1
  280. package/phanpy/dist/assets/icons/sparkles-line-C2hY1s6I.js +0 -1
  281. package/phanpy/dist/assets/icons/time-line-BF3eet1v.js +0 -1
  282. package/phanpy/dist/assets/icons/transfer-4-line-oisfvTBw.js +0 -1
  283. package/phanpy/dist/assets/icons/translate-line-BFWol2Ce.js +0 -1
  284. package/phanpy/dist/assets/icons/unlock-line-X_x5vwv3.js +0 -1
  285. package/phanpy/dist/assets/icons/upload-3-line-DgzwUJeW.js +0 -1
  286. package/phanpy/dist/assets/icons/user-4-line-BVSynZCp.js +0 -1
  287. package/phanpy/dist/assets/icons/user-add-2-line-BlGS5Q8q.js +0 -1
  288. package/phanpy/dist/assets/icons/user-add-line-BbXdHG4z.js +0 -1
  289. package/phanpy/dist/assets/icons/user-edit-line-BFAHOV0X.js +0 -1
  290. package/phanpy/dist/assets/icons/user-follow-line-BXux0wSd.js +0 -1
  291. package/phanpy/dist/assets/icons/user-star-line-DdYlpHLU.js +0 -1
  292. package/phanpy/dist/assets/icons/user-warning-line-Cj43j0_E.js +0 -1
  293. package/phanpy/dist/assets/icons/user-x-line-DIDgNlPH.js +0 -1
  294. package/phanpy/dist/assets/icons/volume-line-Bs390KQt.js +0 -1
  295. package/phanpy/dist/assets/icons/volume-mute-line-DzlzIqFZ.js +0 -1
  296. package/phanpy/dist/assets/icons/walk-line-yNXfB-md.js +0 -1
  297. package/phanpy/dist/assets/icons/world-2-line-CenkGIwj.js +0 -1
  298. package/phanpy/dist/assets/icons/zoom-in-line-CaENUphf.js +0 -1
  299. package/phanpy/dist/assets/icons/zoom-out-line-BDNk0EWo.js +0 -1
  300. package/phanpy/dist/assets/instances-BNPptnn-.json +0 -333
  301. package/phanpy/dist/assets/locales/ar-SA-CfheRbCu.js +0 -1
  302. package/phanpy/dist/assets/locales/ca-ES-DHRW86Tz.js +0 -1
  303. package/phanpy/dist/assets/locales/cs-CZ-CKoqEuA1.js +0 -1
  304. package/phanpy/dist/assets/locales/de-DE-ByRPMRFy.js +0 -1
  305. package/phanpy/dist/assets/locales/eo-UY-CPC7wLCS.js +0 -1
  306. package/phanpy/dist/assets/locales/es-ES-dJa0Fd9t.js +0 -1
  307. package/phanpy/dist/assets/locales/eu-ES-C3djlPR2.js +0 -1
  308. package/phanpy/dist/assets/locales/fa-IR-DTT490Ck.js +0 -1
  309. package/phanpy/dist/assets/locales/fi-FI-B23MCPuz.js +0 -1
  310. package/phanpy/dist/assets/locales/fr-FR-BcaQsO5C.js +0 -1
  311. package/phanpy/dist/assets/locales/gl-ES-BR-CVKBN.js +0 -1
  312. package/phanpy/dist/assets/locales/he-IL-CfheRbCu.js +0 -1
  313. package/phanpy/dist/assets/locales/hu-HU-CfheRbCu.js +0 -1
  314. package/phanpy/dist/assets/locales/it-IT-C1136UFV.js +0 -1
  315. package/phanpy/dist/assets/locales/ja-JP-CRZmRAhD.js +0 -1
  316. package/phanpy/dist/assets/locales/kab-Cz8xH_TT.js +0 -1
  317. package/phanpy/dist/assets/locales/ko-KR-CG9UiY4o.js +0 -1
  318. package/phanpy/dist/assets/locales/lt-LT-CJcI6sHZ.js +0 -1
  319. package/phanpy/dist/assets/locales/nb-NO-CfheRbCu.js +0 -1
  320. package/phanpy/dist/assets/locales/nl-NL-DxN4Yc1t.js +0 -1
  321. package/phanpy/dist/assets/locales/oc-FR-CfheRbCu.js +0 -1
  322. package/phanpy/dist/assets/locales/pl-PL-DkwGXf6P.js +0 -1
  323. package/phanpy/dist/assets/locales/pseudo-LOCALE-CQ4XxkQ0.js +0 -1
  324. package/phanpy/dist/assets/locales/pt-BR-nGL0HdFS.js +0 -1
  325. package/phanpy/dist/assets/locales/pt-PT-Cp17aj2z.js +0 -1
  326. package/phanpy/dist/assets/locales/ru-RU-0Y8cIoaK.js +0 -1
  327. package/phanpy/dist/assets/locales/th-TH-CfheRbCu.js +0 -1
  328. package/phanpy/dist/assets/locales/tok-CfheRbCu.js +0 -1
  329. package/phanpy/dist/assets/locales/tr-TR-CfheRbCu.js +0 -1
  330. package/phanpy/dist/assets/locales/uk-UA-C5E3dFBf.js +0 -1
  331. package/phanpy/dist/assets/locales/zh-CN-BHJi7_et.js +0 -1
  332. package/phanpy/dist/assets/locales/zh-TW-CfheRbCu.js +0 -1
  333. package/phanpy/dist/assets/main-BdqNbG-a.js +0 -26
  334. package/phanpy/dist/assets/main-BdqNbG-a.js.map +0 -1
  335. package/phanpy/dist/assets/mock-home-DGhR__He.js +0 -2
  336. package/phanpy/dist/assets/mock-home-DGhR__He.js.map +0 -1
  337. package/phanpy/dist/assets/multi-column-ROck0NVt.jpg +0 -0
  338. package/phanpy/dist/assets/multi-hashtag-timeline-DH5INVHi.jpg +0 -0
  339. package/phanpy/dist/assets/nested-comments-thread-hv59kZLc.jpg +0 -0
  340. package/phanpy/dist/assets/polyfill-force-H8sUkLGp.js +0 -2
  341. package/phanpy/dist/assets/polyfill-force-H8sUkLGp.js.map +0 -1
  342. package/phanpy/dist/assets/pwa-viewport-ogKbF51K.js +0 -20
  343. package/phanpy/dist/assets/pwa-viewport-ogKbF51K.js.map +0 -1
  344. package/phanpy/dist/assets/style-B1iLPTPR.css +0 -2
  345. package/phanpy/dist/assets/temml-DuyIQMei.js +0 -97
  346. package/phanpy/dist/assets/temml-DuyIQMei.js.map +0 -1
  347. package/phanpy/dist/assets/tinyld.light.node-DtbBhLYC.js +0 -2
  348. package/phanpy/dist/assets/tinyld.light.node-DtbBhLYC.js.map +0 -1
  349. package/phanpy/dist/assets/year-in-posts-B2lywqdx.png +0 -0
  350. package/phanpy/dist/assets/year-in-posts-DJv_star.js +0 -3
  351. package/phanpy/dist/assets/year-in-posts-DJv_star.js.map +0 -1
  352. package/phanpy/dist/compose/index.html +0 -20
  353. package/phanpy/dist/favicon.ico +0 -0
  354. package/phanpy/dist/index.html +0 -73
  355. package/phanpy/dist/logo-192.png +0 -0
  356. package/phanpy/dist/logo-512.png +0 -0
  357. package/phanpy/dist/logo-badge-72.png +0 -0
  358. package/phanpy/dist/logo-maskable-512.png +0 -0
  359. package/phanpy/dist/logo-monochrome-512.png +0 -0
  360. package/phanpy/dist/logo-monochrome-maskable-512.png +0 -0
  361. package/phanpy/dist/manifest.webmanifest +0 -1
  362. package/phanpy/dist/og-image-2.jpg +0 -0
  363. package/phanpy/dist/og-image.png +0 -0
  364. package/phanpy/dist/robots.txt +0 -2
  365. package/phanpy/dist/sw.js +0 -2
  366. package/phanpy/dist/sw.js.map +0 -1
  367. package/phanpy/dist/version.json +0 -1
  368. package/run-agent.mjs +0 -897
  369. package/vendor/gate.cjs +0 -138
  370. package/vendor/idp-grant.cjs +0 -394
  371. package/vendor/jfetch.cjs +0 -16
  372. package/web/admin/actors.js +0 -145
  373. package/web/admin/bar.css +0 -126
  374. package/web/admin/bar.js +0 -99
  375. package/web/admin/client/client.js +0 -66
  376. package/web/admin/client/index.html +0 -69
  377. package/web/admin/client-pick.js +0 -79
  378. package/web/admin/common.js +0 -23
  379. package/web/admin/connections.js +0 -112
  380. package/web/admin/gateway.js +0 -133
  381. package/web/admin/group.js +0 -258
  382. package/web/admin/index.html +0 -603
  383. package/web/admin/notices-bar.js +0 -90
  384. package/web/admin/oauth-signin.mjs +0 -37
  385. package/web/admin/record.js +0 -390
  386. package/web/admin/setup/index.html +0 -241
  387. package/web/admin/setup/setup.js +0 -326
  388. package/web/admin/tokens.css +0 -68
  389. package/web/admin/upkeep.js +0 -177
  390. package/web/admin/window.css +0 -55
  391. package/web/admin/window.js +0 -136
  392. package/web/app/README.md +0 -81
  393. package/web/app/admin-facade.mjs +0 -588
  394. package/web/app/agent.mjs +0 -663
  395. package/web/app/atproto-browser.mjs +0 -86
  396. package/web/app/authorize_interaction.html +0 -56
  397. package/web/app/authorize_interaction.js +0 -69
  398. package/web/app/boot.mjs +0 -576
  399. package/web/app/deliver-relay.mjs +0 -129
  400. package/web/app/dist/boot.js +0 -34218
  401. package/web/app/dist/boot.js.map +0 -7
  402. package/web/app/dist/sw.js +0 -72810
  403. package/web/app/dist/sw.js.map +0 -7
  404. package/web/app/fediacct-browser.mjs +0 -146
  405. package/web/app/gateway-move.mjs +0 -60
  406. package/web/app/idb-kv.mjs +0 -35
  407. package/web/app/index.html +0 -242
  408. package/web/app/keys-browser.mjs +0 -71
  409. package/web/app/keystore.mjs +0 -82
  410. package/web/app/oidc-session.mjs +0 -6
  411. package/web/app/pod-auth.mjs +0 -248
  412. package/web/app/pod-remote.mjs +0 -84
  413. package/web/app/shims/_globals.mjs +0 -2
  414. package/web/app/shims/fedify-sig.mjs +0 -77
  415. package/web/app/shims/node-crypto.mjs +0 -113
  416. package/web/app/shims/node-fs.mjs +0 -17
  417. package/web/app/shims/node-path.mjs +0 -19
  418. package/web/app/shims/node-url.mjs +0 -7
  419. package/web/app/shims/prelude.js +0 -58
  420. package/web/app/shims/safefetch.mjs +0 -58
  421. package/web/app/shims/shapes-text.mjs +0 -8
  422. package/web/app/shims/web-push.mjs +0 -14
  423. package/web/app/signup.mjs +0 -318
  424. package/web/app/sw-src.mjs +0 -220
  425. package/web/app/update.js +0 -70
  426. package/web/front/admin.html +0 -55
  427. package/web/front/admin.js +0 -89
  428. package/web/front/install.sh +0 -69
  429. package/web/front/new-account.html +0 -35
  430. package/web/front/new-account.js +0 -13
  431. package/web/front/notices.html +0 -69
  432. package/web/front/notices.js +0 -107
  433. package/web/front/run.html +0 -58
  434. package/web/front/run.js +0 -195
  435. package/web/front/solid-oidc-client.js +0 -6
  436. package/web/icons/forum.svg +0 -11
  437. package/web/icons/gateway.svg +0 -14
  438. package/web/icons/local.svg +0 -11
@@ -1,423 +0,0 @@
1
- // embed.mjs — the agent, running inside the pod server that holds its pod.
2
- //
3
- // Nothing here reimplements the agent: it is the same Agent class, the same
4
- // intake, deliverer, publisher and lease. What changes is the transport. The
5
- // caller hands in a session that reaches the pod through the server's own
6
- // store, and RemotePod takes it instead of minting a credential — so an
7
- // identity acts on its pod with no token, no socket, and no second process.
8
- //
9
- // The one thing a pod server can do that a laptop cannot is notice a write the
10
- // moment it happens, so the notification socket is replaced by the store's own
11
- // change events.
12
-
13
- import crypto from 'node:crypto';
14
- import fs from 'node:fs';
15
- import path from 'node:path';
16
-
17
- import { createRequire } from 'node:module';
18
-
19
- import { Agent } from '../../run-agent.mjs';
20
- import { RemotePod } from '../device/remote.mjs';
21
- import { apUrls, DEFAULT_ROOT } from '../core/wire.mjs';
22
- import { handleDelivery } from '../gateway/gateway-core.mjs';
23
- import { writeJsonAtomic } from '../device/home.mjs';
24
- import { buildAdminSurface } from '../device/admin/index.mjs';
25
- import { FixedAuthorities } from '../shared/guard.mjs';
26
- import { CONNECTION_PREFIX, fileVault, moveVault, podVault } from '../connections/vault.mjs';
27
-
28
- const require = createRequire(import.meta.url);
29
- const { makeGate } = require('../../vendor/gate.cjs');
30
-
31
- // How long to gather store events before draining, so a delivery of several
32
- // items costs one sweep rather than one each.
33
- const DRAIN_COALESCE_MS = 250;
34
-
35
- // How every secret in this project is minted (the attach flow's recipe).
36
- const mintSecret = () => crypto.randomBytes(32).toString('base64');
37
-
38
- /**
39
- * The secret guarding one identity's owner door, kept in its pod's state
40
- * beside its signing key. Minted when absent; `rotate` re-mints over an
41
- * existing one — which is how a lost secret is recovered, by proving pod
42
- * control again. Never logged.
43
- *
44
- * `dataDir`, when given, is where an identity set up before this kept it: the
45
- * secret is moved into the pod and the host's copy removed, so the owner's
46
- * existing door link goes on working.
47
- */
48
- export async function ensureDoorSecret(session, podBase, { rotate = false, dataDir = null, handle = null, log = () => {} } = {}) {
49
- const base = podBase.endsWith('/') ? podBase : podBase + '/';
50
- // Under the identity's own tree (fedipod/), where the gate reads it back.
51
- const url = apUrls(base, 'fedipod/').state + 'door-secret.json';
52
- const onHost = dataDir && handle ? path.join(dataDir, handle, 'door-secret.json') : null;
53
-
54
- if (!rotate) {
55
- const res = await session.fetch(url, { headers: { accept: 'application/json' } })
56
- .catch(() => ({ ok: false }));
57
- if (res.ok) {
58
- try {
59
- const rec = JSON.parse(await res.text());
60
- if (rec?.secret) {
61
- if (onHost) fs.rmSync(onHost, { force: true });
62
- return { secret: rec.secret, url, rotated: false };
63
- }
64
- } catch { /* unreadable: mint below */ }
65
- }
66
- // Set up before the secret lived on the pod: keep the one the owner has.
67
- if (onHost && fs.existsSync(onHost)) {
68
- try {
69
- const rec = JSON.parse(fs.readFileSync(onHost, 'utf8'));
70
- if (rec?.secret && await putSecret(session, url, rec)) {
71
- fs.rmSync(onHost, { force: true });
72
- log('door secret moved into the pod');
73
- return { secret: rec.secret, url, rotated: false };
74
- }
75
- } catch { /* unreadable: mint below */ }
76
- }
77
- }
78
-
79
- const rec = { secret: mintSecret(), mintedAt: new Date().toISOString() };
80
- if (await putSecret(session, url, rec)) {
81
- if (onHost) fs.rmSync(onHost, { force: true });
82
- return { secret: rec.secret, url, rotated: rotate };
83
- }
84
- // The pod would not take it. Opting in must not fail for that: a pod that
85
- // cannot be written to right now is a pod whose owner still asked for an
86
- // identity, and the secret has somewhere else to live — where it lived
87
- // before. The next start moves it onto the pod.
88
- if (onHost) {
89
- writeJsonAtomic(onHost, rec, { mode: 0o600 });
90
- log(`the pod would not take the door secret; it is on this host at ${onHost} for now`);
91
- return { secret: rec.secret, url: onHost, rotated: rotate };
92
- }
93
- throw new Error(`could not write the door secret to ${url}, and there is no directory to keep it in`);
94
- }
95
-
96
- async function putSecret(session, url, rec) {
97
- try {
98
- const res = await session.fetch(url, {
99
- method: 'PUT',
100
- headers: { 'content-type': 'application/json' },
101
- body: JSON.stringify(rec, null, 2) + '\n',
102
- });
103
- return res.ok;
104
- } catch {
105
- // A store that throws rather than answering is a store that did not take
106
- // it, which is the same answer.
107
- return false;
108
- }
109
- }
110
-
111
- /** The identity's name: a subdomain pod is its label, a path pod its last segment. */
112
- export function handleFor(podBase) {
113
- const u = new URL(podBase);
114
- const segments = u.pathname.split('/').filter(Boolean);
115
- return segments.length ? segments[segments.length - 1] : u.hostname.split('.')[0];
116
- }
117
-
118
- /**
119
- * The credential file an embedded identity runs on. It names the pod and
120
- * nothing else: there is no client id and no secret, because there is nobody
121
- * to authenticate to. Absent `privateRoot`, so the state tree stays on the pod.
122
- *
123
- * `keysMode: 'pod'` because here the pod's server and the agent are the same
124
- * process: keeping the signing key on the host's disk puts it somewhere the
125
- * pod does not travel to, so handing the pod over hands over an identity that
126
- * cannot sign. An identity set up before this gets the mode added.
127
- */
128
- function ensureCredential(home, { podBase, webId }) {
129
- const file = path.join(home, 'credential.json');
130
- try {
131
- const rec = JSON.parse(fs.readFileSync(file, 'utf8'));
132
- if (rec.keysMode === 'pod') return rec;
133
- const updated = { ...rec, keysMode: 'pod' };
134
- writeJsonAtomic(file, updated, { mode: 0o600 });
135
- return updated;
136
- } catch { /* first run for this identity */ }
137
- const rec = {
138
- webId,
139
- remotePod: podBase.endsWith('/') ? podBase : podBase + '/',
140
- createdAt: new Date().toISOString(),
141
- root: 'fedipod/',
142
- keysMode: 'pod',
143
- };
144
- writeJsonAtomic(file, rec, { mode: 0o600 });
145
- return rec;
146
- }
147
-
148
- /**
149
- * Move an identity's signing key off the host's disk and into its pod.
150
- *
151
- * Only after the pod has it: the private key is the one thing here that
152
- * nothing can rebuild, and remote servers have its public half cached, so a
153
- * copy lost between the two places is an identity that can never sign again
154
- * under the name it already published.
155
- */
156
- async function keyIntoPod(agent, home, log) {
157
- const local = path.join(home, 'keys.json');
158
- if (!fs.existsSync(local)) return;
159
- if (agent.store.read('keys.json', null)) {
160
- fs.rmSync(local, { force: true });
161
- log('this identity signs with the key in its pod; removed the copy on this host');
162
- return;
163
- }
164
- let rec;
165
- try {
166
- rec = JSON.parse(fs.readFileSync(local, 'utf8'));
167
- } catch (e) {
168
- log(`the local signing key could not be read (${e.message}) — leaving it where it is`);
169
- return;
170
- }
171
- agent.store.write('keys.json', rec);
172
- if (!await agent.store.commit()) {
173
- log('could not write the signing key to the pod — it stays on this host for now');
174
- return;
175
- }
176
- fs.rmSync(local, { force: true });
177
- log('signing key moved into the pod');
178
- }
179
-
180
- /**
181
- * Move an identity's credentials for accounts it holds on OTHER servers off
182
- * the host's disk and into its pod. A token here is full access to somebody's
183
- * account on a server this project does not run, so the host's copy goes only
184
- * after the pod has taken it.
185
- */
186
- async function connectionsIntoPod(agent, home, log) {
187
- const moves = [
188
- [fileVault(path.join(home, 'fediaccts')), podVault(agent.store, `${CONNECTION_PREFIX}fedi-`)],
189
- [fileVault(path.join(home, 'fediaccts', '_apps')), podVault(agent.store, `${CONNECTION_PREFIX}fediapp-`)],
190
- [fileVault(home), podVault(agent.store, CONNECTION_PREFIX)],
191
- ];
192
- // The last pair is the whole home directory, where only the Bluesky
193
- // credential is ours to move.
194
- for (const [from, to] of moves.slice(0, 2)) await moveVault(from, to, log);
195
- const bluesky = moves[2][0].read('atproto');
196
- if (bluesky && !moves[2][1].read('bluesky')) {
197
- moves[2][1].write('bluesky', bluesky);
198
- if (await moves[2][1].commit()) {
199
- await moves[2][0].remove('atproto');
200
- log('the Bluesky connection moved into the pod');
201
- } else {
202
- log('could not write the Bluesky connection to the pod — it stays on this host for now');
203
- }
204
- }
205
- }
206
-
207
- /**
208
- * The pod's own inbox is a verifying door here.
209
- *
210
- * The server that stores the inbox is the server the delivery arrives at, so
211
- * the signature is checked while the headers still exist and the receipt is
212
- * written beside the activity — the same door code a standalone gateway runs,
213
- * with nothing renamed and nothing advertised differently. The identity's
214
- * config names its own inbox as the door and carries the receipt secret, which
215
- * is what makes the drain read receipts at all; `trust` because a verified
216
- * sender is one the identity may act for.
217
- *
218
- * An identity attached to an outside door keeps that door: only the secret
219
- * is ensured, so receipts from either door verify against the one value.
220
- */
221
- export async function ensureInboxDoor(agent, urls, log = () => {}) {
222
- const cfg = agent.store.getConfig();
223
- if (!cfg) return;
224
- const g = { ...(cfg.gateway || {}) };
225
- const fresh = !g.url || !g.mode || g.mode === 'off';
226
- if (fresh) Object.assign(g, { url: urls.inbox, mode: 'trust' });
227
- if (!g.hmacSecret) g.hmacSecret = crypto.randomBytes(32).toString('base64');
228
- if (fresh || !cfg.gateway?.hmacSecret) {
229
- agent.store.setConfig({ ...cfg, gateway: g });
230
- await agent.store.flush();
231
- log(fresh ? 'the pod inbox verifies deliveries at the door' : 'receipt secret added for the inbox door');
232
- }
233
- }
234
-
235
- /**
236
- * One delivery to a running identity's inbox, verified at the door.
237
- *
238
- * `request` is the WHATWG form of the POST. `podPut` writes through the
239
- * server's store. Returns { status, reason } for the caller to answer with.
240
- * The policy is read from the identity's live state rather than its
241
- * published policy document, because both are in this process.
242
- */
243
- export async function deliverToInbox(agent, request, { podPut, gatewayWebId = null, fetchImpl = fetch } = {}) {
244
- const cfg = agent.store.getConfig() || {};
245
- const contacts = agent.store.getContacts();
246
- const bl = agent.store.getBlocklist();
247
- const u = agent.urls;
248
- const toPod = (x) => (u.toPod ? u.toPod(x) : x);
249
- const ident = {
250
- inboxUrl: toPod(u.inbox),
251
- actorUrl: u.actor,
252
- followersUrl: u.followers,
253
- notesPrefix: u.notes,
254
- following: contacts.following.filter((f) => f.accepted && !f.bsky).map((f) => f.actor),
255
- blocklist: { domains: bl.domains || [], actors: bl.actors || [] },
256
- kind: cfg.kind || 'person',
257
- gatewayWebId,
258
- hmacSecret: cfg.gateway?.hmacSecret || null,
259
- };
260
- return handleDelivery(request, ident, { podPut, fetchImpl });
261
- }
262
-
263
- /**
264
- * Bring one identity up inside the server.
265
- *
266
- * `session` is the store-backed transport ({ fetch, warmup, stats }).
267
- * `resourceStore` is optional and used only to watch for inbox writes.
268
- * Returns the running agent and a stop() that leaves the pod tidy.
269
- */
270
- export async function startEmbeddedAgent({
271
- podBase,
272
- dataDir,
273
- session,
274
- resourceStore = null,
275
- webIdSuffix = 'profile/card#me',
276
- log = () => {},
277
- pollSeconds = null,
278
- autoAcceptFollows = true,
279
- gateToken = null,
280
- uiPath = '/fp/',
281
- }) {
282
- const base = podBase.endsWith('/') ? podBase : podBase + '/';
283
- const handle = handleFor(base);
284
- const home = path.join(dataDir, handle);
285
- fs.mkdirSync(home, { recursive: true, mode: 0o700 });
286
-
287
- const webId = base + webIdSuffix.replace(/^\//u, '');
288
- const cred = ensureCredential(home, { podBase: base, webId });
289
-
290
- const agent = new Agent({ home, log });
291
- agent.log = log;
292
- agent.store.log = log;
293
- // Read by connect() when it builds Intake: in-process, the pod's own change
294
- // events are the push channel.
295
- agent.embedded = true;
296
- if (pollSeconds) agent.pollSeconds = pollSeconds;
297
- agent.remote = new RemotePod(cred, { log, session });
298
- await agent.remote.warmup();
299
-
300
- // Whether this pod already carries an identity is the question connect()
301
- // assumes an answer to, so settle it first.
302
- const urls = apUrls(cred.remotePod, cred.root);
303
- agent.store.attach(agent.privateStorage(cred, 'state', urls));
304
-
305
- // A state container that is NOT THERE means a pod nobody has set up yet. A
306
- // state container we could not READ means a pod we could not ask, which is a
307
- // different answer: treating it as empty would provision a second identity
308
- // over the top of a working one.
309
- const state = await agent.remote.fetch(urls.state, { headers: { accept: 'text/turtle' } });
310
- const unprovisioned = state.status === 404 || state.status === 410;
311
- if (!unprovisioned && state.status >= 400) {
312
- throw new Error(`state at ${urls.state} → ${state.status} — cannot tell whether this pod is set up`);
313
- }
314
- if (!unprovisioned) {
315
- await agent.store.load();
316
- agent.stateLoaded = true;
317
- }
318
-
319
- if (!agent.store.getConfig()) {
320
- // The pod itself must exist first. A server creates its seeded pods after
321
- // it runs its initializers, so an agent that provisioned eagerly would
322
- // leave a container tree exactly where a pod is about to be created.
323
- const profile = await agent.remote.fetch(webId.split('#')[0], { headers: { accept: 'text/turtle' } });
324
- if (profile.status === 404 || profile.status === 410) {
325
- throw new Error(`no pod at ${base} yet — its owner profile is not there`);
326
- }
327
- log(`no identity on ${base} yet — provisioning @${handle}`);
328
- await agent.bootstrap({ handle, name: handle, kind: 'person', root: cred.root });
329
- if (autoAcceptFollows) {
330
- agent.store.setConfig({ ...agent.store.getConfig(), autoAcceptFollows: true });
331
- await agent.store.flush();
332
- }
333
- agent.stateLoaded = true;
334
- }
335
-
336
- // Before connect(), which is what looks the key and the connections up.
337
- await keyIntoPod(agent, home, log);
338
- await connectionsIntoPod(agent, home, log);
339
- await ensureInboxDoor(agent, urls, log);
340
-
341
- await agent.connect();
342
-
343
- // The client surfaces, on the pod's own origin: the Mastodon API a phone app
344
- // speaks, the write API, nodeinfo, and behind the door the admin routes and
345
- // the web client. Same code the standalone agent serves, minus the routes
346
- // that only mean something to a process of one's own.
347
- //
348
- // A pod that lives on a PATH of its host (a suffix pod, e.g.
349
- // https://server.example/aisha/) shares its origin with the front and with
350
- // every other suffix pod, so its whole surface answers UNDER that path: the
351
- // mount is the pod's own pathname, and it is stripped before a route is
352
- // matched and folded back into every self-URL. A host-root or subdomain pod
353
- // has an empty mount and everything is exactly as it was.
354
- const authorities = new FixedAuthorities(base);
355
- agent.authorities = authorities;
356
- const mount = new URL(base).pathname.replace(/\/+$/u, '');
357
- const surface = buildAdminSurface({
358
- agent,
359
- log,
360
- // The door cookie is named the same for every identity; on a shared origin
361
- // (suffix pods) it has to be scoped to this identity's own door path so two
362
- // co-tenants do not overwrite each other's. A host-root/subdomain pod keeps
363
- // the whole-origin cookie it always had.
364
- gate: makeGate(gateToken, {
365
- secureCookie: authorities.secure,
366
- cookiePath: mount ? mount + uiPath : '/',
367
- }),
368
- allowed: authorities,
369
- embedded: true,
370
- basePath: uiPath,
371
- mount,
372
- publicOrigin: base,
373
- scheme: new URL(base).protocol,
374
- });
375
-
376
- // A delivery landing in the inbox — through the door, or from any other
377
- // writer — wakes the drain at once. Only additions, and only in the inbox:
378
- // the drain's own DELETEs must not call it back.
379
- let coalesce = null;
380
- let onChanged = null;
381
- if (typeof resourceStore?.on === 'function') {
382
- const inbox = urls.inbox;
383
- onChanged = (identifier, activity) => {
384
- const kind = String(activity?.value ?? activity ?? '');
385
- if (!identifier?.path?.startsWith(inbox)) return;
386
- if (!kind.endsWith('Create') && !kind.endsWith('Add')) return;
387
- if (coalesce) return;
388
- coalesce = setTimeout(() => {
389
- coalesce = null;
390
- if (!agent.viewer) agent.intake?.drain().catch((e) => log(`drain: ${e.message}`));
391
- }, DRAIN_COALESCE_MS);
392
- coalesce.unref?.();
393
- };
394
- resourceStore.on('changed', onChanged);
395
- }
396
-
397
- const stop = async () => {
398
- if (onChanged) resourceStore.off('changed', onChanged);
399
- surface.streaming?.stop?.();
400
- clearTimeout(coalesce);
401
- agent.intake?.stop();
402
- agent.tagfeed?.stop();
403
- agent.bskyfeed?.stop();
404
- agent.deliverer?.stop();
405
- agent.importer?.stop();
406
- clearInterval(agent.schedTimer);
407
- clearInterval(agent.refreshTimer);
408
- agent.publisher?.stopPolls();
409
- // Same order the standalone agent's shutdown uses: write what is pending,
410
- // then let go of the lease so the next agent need not wait out the TTL.
411
- await Promise.allSettled([
412
- agent.store.flush(),
413
- agent.viewer ? Promise.resolve() : agent.lease?.release(),
414
- ]);
415
- };
416
-
417
- // podHome and actorUrl are the identity's own locations on the pod. They are
418
- // returned rather than rebuilt by the caller so the root name lives here.
419
- return {
420
- agent, handle, home, surface, host: authorities.host, mount,
421
- podHome: urls.home, actorUrl: urls.actor, inboxUrl: urls.inbox, stop,
422
- };
423
- }
@@ -1,137 +0,0 @@
1
- # fediverse-account
2
-
3
- The library lets an app take a Fediverse handle or a WebID and act as that
4
- account.
5
-
6
- - **`login`** — sends the person to wherever their account signs in, their
7
- Mastodon server or their pod, and brings them back to where they were.
8
- - **`resume`** — on every page load, finishes a sign-in they are coming back
9
- from. Otherwise does nothing.
10
- - **`current`** — the signed-in account, or nothing.
11
- - **`signOut`** — signs them out, at their server or pod as well as in the
12
- browser.
13
- - **`describe`** — says what an address is without signing in: a Mastodon
14
- account or a pod account, and its handle.
15
-
16
- Once signed in, the account gives you:
17
-
18
- - **`post`** — publishes a post as them, public, unlisted or followers-only.
19
- - **`reply`** — answers a post.
20
- - **`timeline`** — their home timeline, newest first.
21
- - **`outbox`** — their own posts, newest first. Pass `rdf: true` to also get
22
- the real thing as RDF, for an app that wants to work with it as linked
23
- data rather than as this library's own shape.
24
- - **`follow`** — follows someone by handle.
25
- - **`favourite`**, **`boost`** — as they say.
26
- - **`profile`** — who they are as their server or pod shows them: name,
27
- handle, picture, bio, counts, profile page, and the WebID for a pod
28
- account.
29
- - **`notice`** — a sentence to show them, or nothing. A browser-based
30
- FediPod account gets: "Because you have a browser-based account, your
31
- posts and interactions will only go out to the Fediverse when your browser
32
- is opened to your account."
33
- - **`handle`, `name`, `kind`** — who they are, and whether the account is on
34
- Mastodon or a pod, for an app that wants to know.
35
- - **`signOut`** — the same as above.
36
-
37
- The app never asks which kind it has. A Mastodon account acts at once. A
38
- pod account's actions are carried out by its own agent, at once for a
39
- DeviceAgent or Server account, and when fedipod.net is next open for a
40
- browser-based one.
41
-
42
- Three files, no dependencies. It runs in a page and in a service worker.
43
- The one exception: `outbox({ rdf: true })` needs the `jsonld` package, and
44
- only loads it when that flag is actually used.
45
-
46
- [The demo](https://jeff-zucker.github.io/FediPod/) shows the sign-in and
47
- the profile that comes back.
48
-
49
- ## Get it
50
-
51
- From a CDN, with nothing to install:
52
-
53
- ```js
54
- import { fediAccount } from 'https://cdn.jsdelivr.net/npm/fediverse-account@0.1.0/fedi-account.mjs';
55
- ```
56
-
57
- Or from npm:
58
-
59
- ```bash
60
- npm install fediverse-account
61
- ```
62
-
63
- ```js
64
- import { fediAccount } from 'fediverse-account';
65
- ```
66
-
67
- ## Use
68
-
69
- ```js
70
- import { fediAccount } from 'fediverse-account';
71
- const accounts = fediAccount({ dbName: 'my-app', clientName: 'My app' });
72
-
73
- await accounts.resume(); // on every page load
74
-
75
- signInButton.onclick = () => accounts.login(addressField.value);
76
-
77
- const me = await accounts.current(); // null when nobody is signed in
78
- if (me?.notice) show(me.notice);
79
- await me.post({ text: 'Hello from my app' });
80
- for (const p of await me.timeline({ limit: 20 })) render(p);
81
- for (const p of await me.outbox({ limit: 20 })) render(p);
82
- await me.follow('@aisha@her.server');
83
- await me.reply(p.url, 'Well said');
84
- await me.signOut();
85
- ```
86
-
87
- ## The details
88
-
89
- - **`fediAccount({ dbName, clientName })`** — sets the library up for your
90
- app, once. `dbName` keeps this app's sign-ins apart from another app's on
91
- the same origin. `clientName` is what the person sees on the consent
92
- screen.
93
- - **`login(address, { returnTo, redirectUri })`** — `@kwame@mastodon.social`
94
- goes to Mastodon; `@mei@fedipod.net`, or a WebID, goes to the pod. They
95
- come back to this page unless `redirectUri` says otherwise, and are
96
- returned to where they were unless `returnTo` says otherwise.
97
- - **`post({ text, inReplyTo, visibility })`** — `visibility` is `public`,
98
- `unlisted` or `followers`.
99
- - **`reply(post, text)`** — the post is named by its address or its id.
100
- - **`timeline({ limit })`** — each post as `{ id, url, author: { id, handle,
101
- name }, html, published, inReplyTo }`.
102
- - **`outbox({ limit, rdf })`** — the same shape as `timeline`, but only this
103
- account's own posts. With `rdf: true`, the returned array also carries
104
- `.rdf`: the real outbox parsed into an RDF/JS quad array — for a Mastodon
105
- account too, since a Mastodon server is itself an ActivityPub server with
106
- a real actor and outbox, same as a pod's. It comes back empty on a server
107
- that requires a signed request just to read that document, which some
108
- do. Reading it needs the `jsonld` package available to your app; without
109
- `rdf: true` nothing changes and nothing extra loads.
110
- - **`fetch`** — the raw authenticated fetch, for anything the above does not
111
- cover.
112
- - **`actor`** — the account's ActivityPub id.
113
-
114
- Every action answers `{ sent: true }` when the server did it, or `{ queued:
115
- true }` when the account's own agent will carry it out.
116
-
117
- ## What it cannot do
118
-
119
- - **An address whose server is neither a Mastodon-family server nor a Solid
120
- pod** is refused with a sentence naming the host.
121
- - **A browser-based FediPod account acts when its browser is open.** Posts,
122
- follows and replies wait at the account's outbox until fedipod.net is next
123
- open in some browser, and its timeline is the one that browser last built.
124
- The account's `notice` says so; show it.
125
- - **A Mastodon account has no pod.** An app that keeps things on a pod has
126
- nowhere to keep them for such a person.
127
-
128
- ## Where the sign-in goes, and what stays in the browser
129
-
130
- A Mastodon account signs in at its own server, which asks the person to
131
- approve your app once. The token it grants stays in their browser and is
132
- handed back to the server at sign-out.
133
-
134
- A pod account signs in at the pod's login provider. The key that proves the
135
- sign-in is made in the browser and cannot be read out of it, even by a
136
- script on the page. The person stays signed in across browser restarts
137
- until they sign out, and sign-out is reported to the provider.
@@ -1,94 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>Act as a Fediverse account</title>
7
- <link rel="stylesheet" href="../../web/admin/tokens.css">
8
- <style>
9
- body { font-family: var(--font, Arial, Helvetica, sans-serif); background: var(--bg); color: var(--fg); margin: 0; padding: .75rem 1rem 2rem; }
10
- main { max-width: 44rem; margin: 0 auto; }
11
- h1 { margin-top: .5rem; }
12
- form { display: flex; gap: .75rem; flex-wrap: wrap; align-items: end; margin: 1.25rem 0; }
13
- label { display: flex; flex-direction: column; gap: .35rem; flex: 1 1 18rem; }
14
- input { font: inherit; padding: .6rem .8rem; background: var(--field-bg); color: var(--field-text); border: 1px solid var(--field-border); border-radius: .4rem; }
15
- button { font: inherit; padding: .6rem 1.2rem; background: var(--btn); color: var(--btn-text); border: 1px solid var(--btn-edge); border-radius: .4rem; cursor: pointer; }
16
- button:hover { background: var(--btn-hover); }
17
- a { color: var(--link); }
18
- .kinds { padding-left: 1.4rem; margin: .75rem 0 1.25rem; } .kinds li { margin: .4rem 0; }
19
- :focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
20
- .skip { position: absolute; left: -999px; } .skip:focus { left: 1rem; top: 1rem; }
21
- #status, #profile { padding: 1rem 1.2rem; border: 1px solid var(--line); border-radius: .4rem; background: var(--surface); margin: 1rem 0; }
22
- #status { min-height: 1.5em; }
23
- #status[data-kind="error"] { border-color: var(--err); }
24
- #status[hidden] { display: none; }
25
- #profile { display: grid; grid-template-columns: auto 1fr; gap: .5rem 1.2rem; align-items: start; }
26
- #profile img { width: 5rem; height: 5rem; border-radius: .6rem; object-fit: cover; background: var(--raised); }
27
- #profile dl { margin: 0; display: grid; grid-template-columns: max-content 1fr; gap: .3rem 1rem; }
28
- #profile dt { font-weight: bold; } #profile dd { margin: 0; overflow-wrap: anywhere; }
29
- section[hidden] { display: none; }
30
- </style>
31
- </head>
32
- <body>
33
- <a href="#main" class="skip">Skip to content</a>
34
- <main id="main">
35
- <h1>Act as a Fediverse account</h1>
36
- <p>Type an address and you are sent to wherever that account signs in, then come back here signed in as it. Any of these works:</p>
37
- <ul class="kinds">
38
- <li><strong>A FediPod handle</strong> — <code>@jeff@fedipod.net</code> — signs in at your Solid pod</li>
39
- <li><strong>Other Fediverse handle</strong> — <code>@you@mastodon.social</code> — signs in at your server</li>
40
- <li><strong>A WebID</strong> — <code>https://you.solidcommunity.net/profile/card#me</code> — signs in at your Solid pod</li>
41
- </ul>
42
- <p>This demo is created with the <a href="https://github.com/jeff-zucker/FediPod/tree/main/lib/session">fediverse-account</a> library. Once signed in, the demo shows you your profile. The library itself returns an account session with methods to post, reply, like, etc.</p>
43
- <form id="form">
44
- <label>Your address
45
- <input id="address" name="address" type="text" autocomplete="username" placeholder="@you@your.server" required>
46
- </label>
47
- <button type="submit" id="go">Sign in</button>
48
- <button type="button" id="out" hidden>Sign out</button>
49
- </form>
50
- <p id="status" role="status" aria-live="polite">Not signed in.</p>
51
- <section id="acting" hidden aria-labelledby="profile-h">
52
- <h2 id="profile-h">Profile</h2>
53
- <div id="profile"></div>
54
- </section>
55
- </main>
56
- <script type="module">
57
- import { fediAccount } from './fedi-account.mjs';
58
- const accounts = fediAccount({ dbName: 'fedi-account-demo', clientName: 'Fediverse account demo' });
59
- const $ = (id) => document.getElementById(id);
60
- const say = (text, kind = '') => { $('status').hidden = false; $('status').textContent = text; $('status').dataset.kind = kind; $('status').setAttribute('role', kind === 'error' ? 'alert' : 'status'); };
61
- const esc = (s) => String(s ?? '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
62
- const link = (u) => (u ? `<a href="${esc(u)}">${esc(u)}</a>` : '—');
63
- const showProfile = async (me) => {
64
- try {
65
- const p = await me.profile();
66
- $('profile').innerHTML = `${p.avatar ? `<img src="${esc(p.avatar)}" alt="">` : '<span></span>'}
67
- <dl>
68
- <dt>Name</dt><dd>${esc(p.name || '—')}</dd>
69
- <dt>Handle</dt><dd>${esc(p.handle || '—')}</dd>
70
- <dt>Kind</dt><dd>${me.kind === 'pod' ? 'a FediPod account on a Solid pod' : 'a Mastodon account'}</dd>
71
- <dt>Bio</dt><dd>${p.bio || '—'}</dd>
72
- <dt>Counts</dt><dd>${p.posts ?? '?'} posts, ${p.followers ?? '?'} followers, ${p.following ?? '?'} following</dd>
73
- <dt>Profile page</dt><dd>${link(p.url)}</dd>
74
- <dt>Actor</dt><dd>${link(p.actor)}</dd>
75
- ${p.webId ? `<dt>WebID</dt><dd>${link(p.webId)}</dd>` : ''}
76
- </dl>`;
77
- } catch (e) { $('profile').textContent = `The profile could not be read: ${e.message}`; }
78
- };
79
- const show = async () => {
80
- const me = await accounts.current();
81
- $('out').hidden = !me; $('acting').hidden = !me; $('status').hidden = !!me;
82
- if (!me) { say('Not signed in.'); $('profile').innerHTML = ''; return; }
83
- await showProfile(me);
84
- };
85
- try { await accounts.resume(); } catch (e) { say(e.message, 'error'); }
86
- await show();
87
- $('form').addEventListener('submit', async (e) => {
88
- e.preventDefault(); say('Finding that account…');
89
- try { await accounts.login($('address').value); } catch (err) { say(err.message, 'error'); }
90
- });
91
- $('out').addEventListener('click', async () => { await accounts.signOut(); await show(); });
92
- </script>
93
- </body>
94
- </html>