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,101 +0,0 @@
1
- // type-index.mjs — an account's place, recorded in the person's public type
2
- // index: the list Solid apps read to find what kind of thing lives where.
3
- //
4
- // The record is a registration saying there is an instance of `as:Actor` at the
5
- // account's actor on the pod:
6
- //
7
- // <#actor-…> a solid:TypeRegistration;
8
- // solid:forClass as:Actor;
9
- // solid:instance <…/ap/actor>.
10
- //
11
- // The instance is the actor at the pod, not a gateway's address for it, so the
12
- // place can be read off it. It is also how the account is found again.
13
- //
14
- // Every write goes through the transport's checked writers: valid RDF, to the
15
- // profile or a document its seeAlso names, or nothing.
16
-
17
- const SOLID = 'http://www.w3.org/ns/solid/terms#';
18
- const RDF_TYPE = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type';
19
- const AS_ACTOR = 'https://www.w3.org/ns/activitystreams#Actor';
20
- const PUBLIC_READ = ['Read'];
21
-
22
- /** Where a new public type index goes when a person has none. */
23
- export const newIndexUrl = (podBase) => `${podBase}settings/publicTypeIndex.ttl`;
24
-
25
- /** The person's public type index, or null when their profile names none. */
26
- export async function findPublicIndex(pod, podBase) {
27
- const docs = await pod.profileDocs(podBase);
28
- return pod.webIdValues(docs, SOLID + 'publicTypeIndex')[0] || null;
29
- }
30
-
31
- /** Every actor the index registers, as the instances it names. */
32
- export async function actorsIn(pod, indexUrl) {
33
- const g = await pod.readRdf(indexUrl).catch(() => null);
34
- if (!g) return [];
35
- const out = [];
36
- for (const reg of g.each(null, pod.sym(SOLID + 'forClass'), pod.sym(AS_ACTOR))) {
37
- for (const inst of g.each(reg, pod.sym(SOLID + 'instance'), null)) out.push(inst.value);
38
- }
39
- return [...new Set(out)];
40
- }
41
-
42
- // One registration per actor, named by the actor, so registering again finds
43
- // the same one.
44
- function regFor(indexUrl, actorUrl) {
45
- let h = 0;
46
- for (const c of actorUrl) h = (h * 31 + c.charCodeAt(0)) >>> 0;
47
- return `${indexUrl}#actor-${h.toString(36)}`;
48
- }
49
-
50
- /** Add the account's registration, unless the index already has one for it. */
51
- export async function register(pod, indexUrl, actorUrl) {
52
- const g = await pod.readRdf(indexUrl);
53
- if (!g) throw new Error(`no type index at ${indexUrl}`);
54
- if ((await actorsIn(pod, indexUrl)).includes(actorUrl)) return false;
55
- const reg = pod.sym(regFor(indexUrl, actorUrl));
56
- const status = await pod.writeRdfChecked(indexUrl, g, {
57
- inserts: [
58
- [reg, pod.sym(RDF_TYPE), pod.sym(SOLID + 'TypeRegistration')],
59
- [reg, pod.sym(SOLID + 'forClass'), pod.sym(AS_ACTOR)],
60
- [reg, pod.sym(SOLID + 'instance'), pod.sym(actorUrl)],
61
- ],
62
- mustDescribe: indexUrl,
63
- });
64
- if (status >= 300) throw new Error(`the type index at ${indexUrl} did not take the registration (${status})`);
65
- return true;
66
- }
67
-
68
- /**
69
- * Make a public type index and name it from the profile. Only ever called
70
- * after the person has said yes. An index already at the usual place is kept,
71
- * not overwritten; it is only named.
72
- */
73
- export async function createPublicIndex(pod, podBase) {
74
- const url = newIndexUrl(podBase);
75
- const doc = pod.sym(url);
76
- const existing = await pod.readRdf(url).catch(() => null);
77
- if (!existing) {
78
- const status = await pod.writeRdfChecked(url, null, {
79
- inserts: [[doc, pod.sym(RDF_TYPE), pod.sym(SOLID + 'TypeIndex')],
80
- [doc, pod.sym(RDF_TYPE), pod.sym(SOLID + 'ListedDocument')]],
81
- mustDescribe: url,
82
- });
83
- if (status >= 300) throw new Error(`could not make a type index at ${url} (${status})`);
84
- }
85
- // Public, as the profile is: an app finds your things by reading it.
86
- await pod.setAcl(url, PUBLIC_READ);
87
- await pod.writeAboutWebId(podBase, {
88
- inserts: [[pod.sym(pod.webId), pod.sym(SOLID + 'publicTypeIndex'), doc]],
89
- });
90
- return url;
91
- }
92
-
93
- /**
94
- * The actors this person's index says live in this pod. Empty when there is no
95
- * index or nothing registered.
96
- */
97
- export async function registeredActors(pod, podBase) {
98
- const index = await findPublicIndex(pod, podBase).catch(() => null);
99
- if (!index) return [];
100
- return (await actorsIn(pod, index)).filter(a => a.startsWith(podBase) && a.endsWith('ap/actor'));
101
- }
package/lib/pod/urls.mjs DELETED
@@ -1,91 +0,0 @@
1
- // urls.mjs — where an ActivityPub actor's documents live on a Solid pod.
2
- //
3
- // Everything the actor owns nests under ONE top-level container (`root`), so
4
- // the pod stays tidy and one pod could host several actors under different
5
- // roots. WebFinger and host-meta are the exception: fediverse discovery only
6
- // ever looks at the host root, so they sit at the pod base.
7
- //
8
- // `root` is REQUIRED. It used to default, and the default disagreed with what
9
- // one of the two callers actually passed — which is the kind of bug that puts
10
- // an actor's state in one container and its published documents in another.
11
- // A library has no business having an opinion about which container an
12
- // application uses; the application states it.
13
- //
14
- // When `publicBase` is given — an identity whose ADVERTISED ids live somewhere
15
- // else, e.g. "https://front.example/u/me/" — the ids a remote server sees build
16
- // on that instead, while the pod-side trees and every write target stay on the
17
- // pod. `toPod`/`toPublic` map between the two spaces, and a transport applies
18
- // `toPod` at its one request choke point, so callers keep passing advertised
19
- // ids and the writes still land on the pod. With no `publicBase` the returned
20
- // object is byte-identical to the unfronted form — an invariant worth keeping,
21
- // because it is what lets the fronted case be tested against the plain one.
22
-
23
- export function apUrls(remotePod, root, { publicBase = null } = {}) {
24
- if (!root) throw new Error('apUrls: a container root is required — the caller states it, this library does not guess');
25
- const base = remotePod.endsWith('/') ? remotePod : remotePod + '/';
26
- const home = base + (root.endsWith('/') ? root : root + '/');
27
- // The face a remote sees: the fronted home, or the pod home when unfronted.
28
- const face = publicBase ? (publicBase.endsWith('/') ? publicBase : publicBase + '/') : home;
29
- const urls = {
30
- base, home,
31
- webfinger: base + '.well-known/webfinger',
32
- actor: face + 'ap/actor',
33
- inbox: face + 'ap/inbox/',
34
- outbox: face + 'ap/outbox',
35
- followers: face + 'ap/followers',
36
- following: face + 'ap/following',
37
- notes: face + 'ap/notes/',
38
- privateNotes: face + 'ap/private/',
39
- featured: face + 'ap/featured',
40
- // FEP-1b12: the moderator roster a recipient validates announced
41
- // moderation against. Published only when moderators are configured.
42
- moderators: face + 'ap/moderators',
43
- // FEP-4ccd and FEP-c648: follows in limbo and the block list, as
44
- // collections. They live in the private container so the owner-only ACL
45
- // is inherited, not re-stated per document.
46
- pendingFollowers: face + 'ap/private/pending-followers',
47
- pendingFollowing: face + 'ap/private/pending-following',
48
- blocked: face + 'ap/private/blocked',
49
- // The outbox as its owner reads it (every message, §5.1) and what the
50
- // owner has liked (§5.5): the owner's alone, so they sit here too — and
51
- // at the pod's own address even for a fronted identity, because the owner
52
- // reads them there, with a credential the pod checks.
53
- ownOutbox: home + 'ap/private/outbox',
54
- liked: home + 'ap/private/liked',
55
- profileHtml: face + 'ap/profile.html',
56
- // Media stays on the pod even when fronted: attachment urls are not
57
- // identity-checked by remotes, and proxying blobs would be pure cost.
58
- media: home + 'ap/media/',
59
- state: home + 'ap-state/',
60
- };
61
- if (publicBase) {
62
- urls.podHome = home;
63
- urls.publicHome = face;
64
- urls.toPod = (u) => (typeof u === 'string' && u.startsWith(face) ? home + u.slice(face.length) : u);
65
- urls.toPublic = (u) => (typeof u === 'string' && u.startsWith(home) ? face + u.slice(home.length) : u);
66
- }
67
- return urls;
68
- }
69
-
70
- // The host a handle would resolve through, or null when no handle can point
71
- // here. @name@host is looked up at https://host/.well-known/webfinger, so only
72
- // a pod that owns the root of its host can answer for one; a pod living at
73
- // https://server/name/ may publish the document but nothing will ever ask.
74
- /**
75
- * The pod a WebID lives in: the URL up to the profile document's container.
76
- * `https://alice.pod/profile/card#me` → `https://alice.pod/`, and on a shared
77
- * host `https://server/alice/profile/card#me` → `https://server/alice/`. Taking
78
- * the origin alone named the wrong pod for the second, which is every pod on a
79
- * path.
80
- */
81
- export function podBaseOfWebId(webId) {
82
- const u = new URL(webId);
83
- u.hash = ''; u.search = '';
84
- const dir = u.pathname.replace(/profile\/card$/u, '').replace(/[^/]*$/u, '');
85
- return `${u.origin}${dir.endsWith('/') ? dir : dir + '/'}`;
86
- }
87
-
88
- export function webfingerHost(podUrl) {
89
- const u = new URL(podUrl);
90
- return u.pathname === '/' ? u.host : null;
91
- }
@@ -1,424 +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, root = 'fedipod/', log = () => {} } = {}) {
49
- const base = podBase.endsWith('/') ? podBase : podBase + '/';
50
- // Under the identity's own tree, where the gate reads it back.
51
- const url = apUrls(base, root).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, root = 'fedipod/' }) {
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,
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
- root = 'fedipod/',
273
- dataDir,
274
- session,
275
- resourceStore = null,
276
- webIdSuffix = 'profile/card#me',
277
- log = () => {},
278
- pollSeconds = null,
279
- autoAcceptFollows = true,
280
- gateToken = null,
281
- uiPath = '/fp/',
282
- }) {
283
- const base = podBase.endsWith('/') ? podBase : podBase + '/';
284
- const handle = handleFor(base);
285
- const home = path.join(dataDir, handle);
286
- fs.mkdirSync(home, { recursive: true, mode: 0o700 });
287
-
288
- const webId = base + webIdSuffix.replace(/^\//u, '');
289
- const cred = ensureCredential(home, { podBase: base, webId, root });
290
-
291
- const agent = new Agent({ home, log });
292
- agent.log = log;
293
- agent.store.log = log;
294
- // Read by connect() when it builds Intake: in-process, the pod's own change
295
- // events are the push channel.
296
- agent.embedded = true;
297
- if (pollSeconds) agent.pollSeconds = pollSeconds;
298
- agent.remote = new RemotePod(cred, { log, session });
299
- await agent.remote.warmup();
300
-
301
- // Whether this pod already carries an identity is the question connect()
302
- // assumes an answer to, so settle it first.
303
- const urls = apUrls(cred.remotePod, cred.root);
304
- agent.store.attach(agent.privateStorage(cred, 'state', urls));
305
-
306
- // A state container that is NOT THERE means a pod nobody has set up yet. A
307
- // state container we could not READ means a pod we could not ask, which is a
308
- // different answer: treating it as empty would provision a second identity
309
- // over the top of a working one.
310
- const state = await agent.remote.fetch(urls.state, { headers: { accept: 'text/turtle' } });
311
- const unprovisioned = state.status === 404 || state.status === 410;
312
- if (!unprovisioned && state.status >= 400) {
313
- throw new Error(`state at ${urls.state} → ${state.status} — cannot tell whether this pod is set up`);
314
- }
315
- if (!unprovisioned) {
316
- await agent.store.load();
317
- agent.stateLoaded = true;
318
- }
319
-
320
- if (!agent.store.getConfig()) {
321
- // The pod itself must exist first. A server creates its seeded pods after
322
- // it runs its initializers, so an agent that provisioned eagerly would
323
- // leave a container tree exactly where a pod is about to be created.
324
- const profile = await agent.remote.fetch(webId.split('#')[0], { headers: { accept: 'text/turtle' } });
325
- if (profile.status === 404 || profile.status === 410) {
326
- throw new Error(`no pod at ${base} yet — its owner profile is not there`);
327
- }
328
- log(`no identity on ${base} yet — provisioning @${handle}`);
329
- await agent.bootstrap({ handle, name: handle, kind: 'person', root: cred.root });
330
- if (autoAcceptFollows) {
331
- agent.store.setConfig({ ...agent.store.getConfig(), autoAcceptFollows: true });
332
- await agent.store.flush();
333
- }
334
- agent.stateLoaded = true;
335
- }
336
-
337
- // Before connect(), which is what looks the key and the connections up.
338
- await keyIntoPod(agent, home, log);
339
- await connectionsIntoPod(agent, home, log);
340
- await ensureInboxDoor(agent, urls, log);
341
-
342
- await agent.connect();
343
-
344
- // The client surfaces, on the pod's own origin: the Mastodon API a phone app
345
- // speaks, the write API, nodeinfo, and behind the door the admin routes and
346
- // the web client. Same code the standalone agent serves, minus the routes
347
- // that only mean something to a process of one's own.
348
- //
349
- // A pod that lives on a PATH of its host (a suffix pod, e.g.
350
- // https://server.example/aisha/) shares its origin with the front and with
351
- // every other suffix pod, so its whole surface answers UNDER that path: the
352
- // mount is the pod's own pathname, and it is stripped before a route is
353
- // matched and folded back into every self-URL. A host-root or subdomain pod
354
- // has an empty mount and everything is exactly as it was.
355
- const authorities = new FixedAuthorities(base);
356
- agent.authorities = authorities;
357
- const mount = new URL(base).pathname.replace(/\/+$/u, '');
358
- const surface = buildAdminSurface({
359
- agent,
360
- log,
361
- // The door cookie is named the same for every identity; on a shared origin
362
- // (suffix pods) it has to be scoped to this identity's own door path so two
363
- // co-tenants do not overwrite each other's. A host-root/subdomain pod keeps
364
- // the whole-origin cookie it always had.
365
- gate: makeGate(gateToken, {
366
- secureCookie: authorities.secure,
367
- cookiePath: mount ? mount + uiPath : '/',
368
- }),
369
- allowed: authorities,
370
- embedded: true,
371
- basePath: uiPath,
372
- mount,
373
- publicOrigin: base,
374
- scheme: new URL(base).protocol,
375
- });
376
-
377
- // A delivery landing in the inbox — through the door, or from any other
378
- // writer — wakes the drain at once. Only additions, and only in the inbox:
379
- // the drain's own DELETEs must not call it back.
380
- let coalesce = null;
381
- let onChanged = null;
382
- if (typeof resourceStore?.on === 'function') {
383
- const inbox = urls.inbox;
384
- onChanged = (identifier, activity) => {
385
- const kind = String(activity?.value ?? activity ?? '');
386
- if (!identifier?.path?.startsWith(inbox)) return;
387
- if (!kind.endsWith('Create') && !kind.endsWith('Add')) return;
388
- if (coalesce) return;
389
- coalesce = setTimeout(() => {
390
- coalesce = null;
391
- if (!agent.viewer) agent.intake?.drain().catch((e) => log(`drain: ${e.message}`));
392
- }, DRAIN_COALESCE_MS);
393
- coalesce.unref?.();
394
- };
395
- resourceStore.on('changed', onChanged);
396
- }
397
-
398
- const stop = async () => {
399
- if (onChanged) resourceStore.off('changed', onChanged);
400
- surface.streaming?.stop?.();
401
- clearTimeout(coalesce);
402
- agent.intake?.stop();
403
- agent.tagfeed?.stop();
404
- agent.bskyfeed?.stop();
405
- agent.deliverer?.stop();
406
- agent.importer?.stop();
407
- clearInterval(agent.schedTimer);
408
- clearInterval(agent.refreshTimer);
409
- agent.publisher?.stopPolls();
410
- // Same order the standalone agent's shutdown uses: write what is pending,
411
- // then let go of the lease so the next agent need not wait out the TTL.
412
- await Promise.allSettled([
413
- agent.store.flush(),
414
- agent.viewer ? Promise.resolve() : agent.lease?.release(),
415
- ]);
416
- };
417
-
418
- // podHome and actorUrl are the identity's own locations on the pod. They are
419
- // returned rather than rebuilt by the caller so the root name lives here.
420
- return {
421
- agent, handle, home, surface, host: authorities.host, mount,
422
- podHome: urls.home, actorUrl: urls.actor, inboxUrl: urls.inbox, stop,
423
- };
424
- }