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,115 +0,0 @@
1
- // Keeping every identity on this machine at the current shape of an install.
2
- //
3
- // bbba587 fixed the DEFAULT: a new install keeps its private half — timeline,
4
- // contacts, blocklist, notifications, masto-tokens.json — on this machine.
5
- // It did nothing for the installs that already existed, and `privateRoot`
6
- // absent still means "on the pod" (run-agent.mjs privateUrls). So an identity
7
- // set up before that commit still writes every state change to a pod, and
8
- // still keeps its private data on a server.
9
- //
10
- // A new default only ever fixes the installs that do not exist yet. That is the
11
- // reason this file exists rather than a one-off script: a change to install
12
- // shape ships as a numbered step HERE, and `fedipod upgrade` runs the
13
- // pending ones over every identity under the root, not just the one you
14
- // happened to be in.
15
-
16
- // Bump when a step is added, and add the step to pendingSteps below.
17
- export const CURRENT_LAYOUT = 1;
18
-
19
- // 0 is "never stamped", which is every install made before this file existed.
20
- // It is not the same as "wrong": an old install may already be in the right
21
- // shape, and pendingSteps is what decides, per step.
22
- export const layoutOf = (cred) => Number(cred?.layout) || 0;
23
-
24
- // Layout 1 — the private half lives on this machine.
25
- //
26
- // Only when `privateRoot` is ABSENT. An operator who pointed it somewhere
27
- // deliberately, with `--private-root`, has said where they want it; a migration
28
- // that overrode that would be moving data on its own initiative.
29
- export function needsStateMove(cred) {
30
- // `stateOnPod` is a move-in from a browser account (setup.mjs): the state
31
- // is on the pod because that is where the account already lives.
32
- return !cred?.privateRoot && !cred?.stateOnPod;
33
- }
34
-
35
- export function pendingSteps(cred) {
36
- const steps = [];
37
- if (needsStateMove(cred)) {
38
- steps.push({
39
- id: 'state-off-pod',
40
- what: 'move the private half off the pod, onto this machine',
41
- why: 'receiving a post costs pod writes, and the timeline lives on a server',
42
- });
43
- }
44
- return steps;
45
- }
46
-
47
- // True when there is nothing left to do and the stamp can be written.
48
- export const isCurrent = (cred) => pendingSteps(cred).length === 0;
49
-
50
- // The documents on the pod that the state tree consists of — everything the
51
- // move copied. Deliberately NOT "everything in the container":
52
- //
53
- // lease.json stays. It is the single-active-agent lock, and it is on the pod
54
- // precisely BECAUSE the private half is not: a lease only one machine can
55
- // reach coordinates nothing. run-agent builds it from urls.state, never from
56
- // privateRoot, so it goes on being used after the move.
57
- //
58
- // .keep and .acl belong to the container, which also stays — the container is
59
- // what carries the owner-only ACL that lease.json sits behind.
60
- //
61
- // RemotePod.delete's deny-list is the backstop under all of this; this list is
62
- // what we affirmatively claim, and the two disagreeing is a bug worth hearing
63
- // about rather than a file quietly surviving.
64
- export const MOVED_STATE_DOCS = new Set([
65
- 'config.json', 'queue.json', 'blocklist.json', 'contacts.json', 'muted.json',
66
- 'pending.json', 'requests.json', 'deadletter.json', 'statuses.json',
67
- 'notifications.json', 'media.json', 'actors.json', 'ids.json', 'published.json',
68
- 'outbox.json', 'outbox-removed.json', 'parked.json', 'tagfeed.json',
69
- 'intake-attempts.json', 'inbox-channel.json', 'masto-tokens.json', 'masto-markers.json',
70
- 'lists.json', 'filters.json', 'scheduled.json', 'webpush.json',
71
- 'import-state.json',
72
- // Present only in pod-keys mode; a state move must carry it or the moved
73
- // store cannot sign.
74
- 'keys.json',
75
- ]);
76
-
77
- export const KEPT_ON_POD = new Set(['lease.json', '.keep']);
78
-
79
- // What `state --drop-remote` would remove, given a container listing. Split out
80
- // so the decision can be tested without a pod, and so the caller can show it
81
- // before doing anything.
82
- export function classifyRemoteState(urls, base) {
83
- const drop = [];
84
- const keep = [];
85
- for (const u of urls) {
86
- const name = String(u).slice(String(base).length).replace(/^\/+/, '');
87
- if (!name || name.includes('/')) { keep.push({ url: u, name, why: 'not a document of this container' }); continue; }
88
- if (KEPT_ON_POD.has(name)) {
89
- keep.push({ url: u, name, why: name === 'lease.json' ? 'the lease stays on the pod by design' : 'the container needs it' });
90
- } else if (MOVED_STATE_DOCS.has(name)) {
91
- drop.push({ url: u, name });
92
- } else {
93
- keep.push({ url: u, name, why: 'not one of ours' });
94
- }
95
- }
96
- return { drop, keep };
97
- }
98
-
99
- // Copy the private half — every state document — from one storage to the
100
- // other. Copy and verify only: nothing is repointed and nothing is deleted, so
101
- // a failure part-way leaves the source authoritative.
102
- // Used by both `state --to` and the record page's Move private data.
103
- export async function copyPrivateHalf({ from, to, log = () => {} }) {
104
- const { PodStore } = await import('../core/store.mjs');
105
- const src = new PodStore({ log: () => {} });
106
- src.attach(from.state);
107
- await src.load();
108
- const dst = new PodStore({ log });
109
- dst.attach(to.state);
110
- for (const [name, value] of src.cache) dst.write(name, value);
111
- if (!await dst.commit()) {
112
- throw new Error('some state documents did not land — nothing was repointed, nothing was deleted');
113
- }
114
- return { docs: src.cache.size };
115
- }
@@ -1,27 +0,0 @@
1
- // ports.mjs — finding a port nothing else is on.
2
- //
3
- // This lived twice, in bin/fedipod.mjs and lib/admin.mjs, and the two
4
- // copies had already drifted: one returned null when it ran out of ports and
5
- // the other threw. Both spawn agents, so which one you got decided whether a
6
- // full range produced an error message or a silent undefined.
7
-
8
- import net from 'node:net';
9
-
10
- // Bind to find out. Something holding a port without answering HTTP reads as
11
- // free to a probe that only asks for a response, and the new agent then dies on
12
- // EADDRINUSE with nobody watching.
13
- export function portFree(port) {
14
- return new Promise((resolve) => {
15
- const probe = net.createServer();
16
- probe.once('error', () => resolve(false));
17
- probe.once('listening', () => probe.close(() => resolve(true)));
18
- probe.listen(port, '127.0.0.1');
19
- });
20
- }
21
-
22
- // null when the whole span is taken. Callers that want an error say so in their
23
- // own words — the CLI has a person reading it, the admin API has a status code.
24
- export async function freePortFrom(first, span = 50) {
25
- for (let p = first; p < first + span; p++) if (await portFree(p)) return p;
26
- return null;
27
- }
@@ -1,54 +0,0 @@
1
- // remote.mjs — the Node agent's pod transport: CSS client-credentials + DPoP
2
- // (vendor/idp-grant.cjs, extracted from data-kitchen's "remember this IdP"
3
- // machinery — plain Node, no Electron).
4
- //
5
- // Everything about talking to a pod — the verbs, the ACL documents, the
6
- // deletion deny-list, the Retry-After cooldown, the container listing — lives
7
- // in lib/pod/transport.mjs, which knows nothing about Node and is shared with
8
- // the browser. What is left here is the one thing that genuinely differs: how
9
- // the session is obtained. That top-level `require` of the vendor grant is also
10
- // exactly why the split exists — it cannot be bundled for a service worker.
11
-
12
- import { createRequire } from 'node:module';
13
- import path from 'node:path';
14
- import { fileURLToPath } from 'node:url';
15
- import { PodTransport } from '../pod/transport.mjs';
16
-
17
- const require = createRequire(import.meta.url);
18
- const vendorDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../vendor');
19
- const { mintCredential, discoverTokenEndpoint, createGrantSession, revokeCredentialViaAccount } =
20
- require(path.join(vendorDir, 'idp-grant.cjs'));
21
-
22
- export { mintCredential, discoverTokenEndpoint, revokeCredentialViaAccount };
23
-
24
- // Re-exported so the callers that guard their own deletions — run-agent, the
25
- // server package's live test, the smoke suite — keep importing it from here.
26
- export { protectedFromDeletion } from '../pod/transport.mjs';
27
-
28
- export class RemotePod extends PodTransport {
29
- constructor(credential, { log = () => {}, home = null, session = null, role = 'agent' } = {}) {
30
- // An injected session replaces the credential-backed one, and everything
31
- // above it — the cooldown, the deny-list, the url map — is unchanged. That
32
- // is how the agent runs inside a pod server: same class, no token to mint.
33
- super(session || createGrantSession(credential, home ? {
34
- backoffFile: path.join(home, 'backoff.json'),
35
- tokenFile: path.join(home, 'token.json'),
36
- } : {}), {
37
- webId: credential.webId,
38
- log,
39
- role,
40
- runtime: 'node',
41
- // Fail fast for the window rather than sleeping it out: a daemon has
42
- // timers behind it, and a request that waits is a timer that does not
43
- // fire. The callers' existing retry paths take it from there.
44
- cooldownMode: 'refuse',
45
- });
46
- }
47
-
48
- async warmup() { return this.session.warmup(); }
49
-
50
- /** The grant's own counters, under the transport's. */
51
- stats() {
52
- return { ...(this.session.stats?.() || {}), ...super.stats() };
53
- }
54
- }
@@ -1,459 +0,0 @@
1
- // setup.mjs — the setup flow itself, lifted out of the CLI so a browser page
2
- // can drive it. Same order, same decisions as `fedipod setup` always had:
3
- // create the account and pod (or use one you already have), mint a revocable
4
- // credential, provision the pod, publish the actor, check it is really public.
5
- //
6
- // The caller owns the run record and polls it; runSetup only fills it in. That
7
- // is deliberate. The credential a CSS server mints is shown once and cannot be
8
- // recovered, so the durable record of a run has to be the credential file on
9
- // disk, never an HTTP response a closed tab can take with it.
10
-
11
- import fs from 'node:fs';
12
- import path from 'node:path';
13
- import { pathToFileURL } from 'node:url';
14
- import * as $rdf from 'rdflib';
15
-
16
- import { createAccountWithPod as realCreateAccount } from './account.mjs';
17
- import { mintCredential as realMint, RemotePod } from './remote.mjs';
18
- import { hashPassword } from '../client/masto/index.mjs';
19
- import { webfingerHost, apUrls, DEFAULT_ROOT } from '../core/wire.mjs';
20
- import { rootOf, recordLastUsed, writeJsonAtomic } from './home.mjs';
21
- import { insecureUrlReason } from '../shared/safefetch.mjs';
22
- import { resourceExists } from '../pod/root.mjs';
23
- import { CURRENT_LAYOUT, isCurrent } from './migrate.mjs';
24
- import { completeGatewayMove } from './gateway-move.mjs';
25
-
26
- const SOLID = $rdf.Namespace('http://www.w3.org/ns/solid/terms#');
27
-
28
- export const STEPS = ['account', 'credential', 'bootstrap', 'connect', 'publish', 'verify'];
29
-
30
- // Read-only pre-check on the pod behind an existing-pod setup, before a
31
- // credential is minted or the first write is attempted. Two failures it turns
32
- // from a bare "PUT … → 401" into a plain sentence:
33
- // 1. the pod host is unreachable — no account should be built on a pod that
34
- // is not there;
35
- // 2. the pod's WebID document declares no OIDC issuer. A pod verifies a token
36
- // by dereferencing its WebID and looking for solid:oidcIssuer; an empty or
37
- // issuer-less profile makes it reject every write with 401, so setup would
38
- // stall on the first one. Only judged when the document is publicly
39
- // readable — a protected one cannot be read here and is left to bootstrap.
40
- export async function checkPodUsable(pod, { webId, fetch: doFetch = globalThis.fetch } = {}) {
41
- const base = pod.endsWith('/') ? pod : pod + '/';
42
- const wid = webId || new URL('profile/card#me', base).href;
43
- const cardUrl = wid.split('#')[0];
44
-
45
- let root;
46
- try { root = await doFetch(base, { headers: { accept: 'text/turtle' } }); }
47
- catch (e) { return { ok: false, error: `${base} could not be reached (${e.message}). Check the address and that the server is running.` }; }
48
- if (root.status === 404) return { ok: false, error: `there is no pod at ${base} — the server answered 404.` };
49
- if (root.status >= 500) return { ok: false, error: `the pod host at ${base} is not responding (HTTP ${root.status}).` };
50
-
51
- let card;
52
- try { card = await doFetch(cardUrl, { headers: { accept: 'text/turtle' } }); }
53
- catch (e) { return { ok: false, error: `the pod's WebID document ${cardUrl} could not be read (${e.message}).` }; }
54
- if (card.status === 404) return { ok: false, error: `the pod has no WebID document at ${cardUrl} — the identity it needs is missing.` };
55
- if (card.status >= 500) return { ok: false, error: `the pod host is not serving ${cardUrl} (HTTP ${card.status}).` };
56
- if (card.status === 401 || card.status === 403) return { ok: true }; // cannot read unauthenticated; leave it to bootstrap
57
-
58
- const body = await card.text().catch(() => '');
59
- const g = $rdf.graph();
60
- try { $rdf.parse(body, g, cardUrl, (card.headers.get('content-type') || 'text/turtle').split(';')[0].trim()); }
61
- catch { return { ok: false, error: `the pod's WebID document ${cardUrl} could not be parsed as RDF.` }; }
62
- if (!g.each($rdf.sym(wid), SOLID('oidcIssuer'), null).length) {
63
- return { ok: false, error: `the pod's WebID document ${cardUrl} declares no OIDC issuer, so the pod will reject every write. It looks empty or incomplete — restore it, or set up with a fresh pod.` };
64
- }
65
- return { ok: true };
66
- }
67
-
68
- export const credentialPath = (home) => path.join(home, 'credential.json');
69
- export const hasCredential = (home) => fs.existsSync(credentialPath(home));
70
-
71
- export function newRun() {
72
- return {
73
- phase: 'running',
74
- startedAt: new Date().toISOString(),
75
- steps: STEPS.map(key => ({ key, state: 'waiting', note: null })),
76
- error: null,
77
- result: null,
78
- };
79
- }
80
-
81
- // What must be answered before a run can start. `resuming` is a run that
82
- // already has its credential: the account and the pod are settled, and only
83
- // the actor's own details are still open.
84
- export function setupInputError(a, resuming = false) {
85
- if (!a.handle) return 'a handle is required';
86
- if (a.kind && a.kind !== 'person' && a.kind !== 'group') return 'kind must be person or group';
87
- if (a.privateRoot) {
88
- try { new URL(a.privateRoot); } catch { return `"${a.privateRoot}" is not a container address`; }
89
- // A pod one, at least. A file: private root is a directory and has no
90
- // transport to secure.
91
- if (/^https?:/i.test(a.privateRoot)) {
92
- const bad = insecureUrlReason(a.privateRoot, 'private-data address');
93
- if (bad) return bad;
94
- }
95
- }
96
- if (a.gateway) {
97
- if (typeof a.gateway.url !== 'string') return 'a gateway needs its door URL';
98
- const badGw = insecureUrlReason(a.gateway.url, 'gateway address');
99
- if (badGw) return badGw;
100
- }
101
- if (a.shape && a.shape !== 'pod' && a.shape !== 'front') return 'shape must be "pod" or "front"';
102
- if (a.gatewayOrigin) {
103
- const badO = insecureUrlReason(a.gatewayOrigin, 'gateway address');
104
- if (badO) return badO;
105
- }
106
- if (resuming) return null;
107
- if (a.mode !== 'new' && a.mode !== 'existing') return 'mode must be "new" or "existing"';
108
- if (!a.issuer) return 'an identity provider is required';
109
- // Checked BEFORE the password is asked for, let alone sent: the issuer is
110
- // where it goes, and the pod is where the credential it buys is used.
111
- const badIssuer = insecureUrlReason(a.issuer, 'identity provider address');
112
- if (badIssuer) return badIssuer;
113
- if (!a.email) return 'an account email is required';
114
- if (!a.password) return 'the account password is required';
115
- if (a.mode === 'existing' && !a.pod) return 'a pod address is required';
116
- const badPod = insecureUrlReason(a.pod, 'pod address');
117
- if (badPod) return badPod;
118
- return null;
119
- }
120
-
121
- // Where an identity's address lives. A pod on a suffix-based host cannot
122
- // answer WebFinger for a handle, so its address must live at the Gateway
123
- // whatever was asked; a pod at its own host root takes the shape chosen.
124
- export function frontedAddress({ pod, shape }) {
125
- const pathPod = pod ? !webfingerHost(pod) : false;
126
- return pathPod || shape === 'front';
127
- }
128
-
129
- // The account a pod already holds, read as its owner: the config and the key
130
- // record in its state container. Both null when there is none, or when the
131
- // credential is not the owner's. Injected for tests.
132
- export async function readPodAccount({ home, credential, pod, root, log = () => {} }) {
133
- const remote = new RemotePod(credential, { home, log });
134
- await remote.warmup?.();
135
- const urls = apUrls(pod, root || DEFAULT_ROOT);
136
- const config = await remote.getJson(urls.state + 'config.json').catch(() => null);
137
- const keys = await remote.getJson(urls.state + 'keys.json').catch(() => null);
138
- return { config, keys };
139
- }
140
-
141
- // Take an address at a Gateway: attach the pod to it, fronted, and return the
142
- // gateway config bootstrap writes and connect reads. The pod session proves
143
- // the pod — no password reaches the Gateway. Injected for tests.
144
- export async function takeGatewayAddress({ home, credential, gatewayOrigin, handle, podHome, actorUrl, kind, log = () => {} }) {
145
- const remote = new RemotePod(credential, { home, log });
146
- await remote.warmup?.();
147
- const origin = String(gatewayOrigin).replace(/\/$/, '');
148
- const res = await remote.session.fetch(`${origin}/api/attach`, {
149
- method: 'POST', headers: { 'content-type': 'application/json' },
150
- body: JSON.stringify({ handle, podHome, actorUrl, kind: kind === 'group' ? 'group' : 'person', fronted: true }),
151
- });
152
- const d = await res.json().catch(() => ({}));
153
- if (res.status !== 201 || !d.hmacSecret) {
154
- throw new Error(`could not take a gateway address at ${new URL(origin).host} (HTTP ${res.status})${d.error ? ': ' + d.error : ''}`);
155
- }
156
- return {
157
- url: `${origin}/u/${handle}/ap/inbox/`,
158
- frontActor: String(d.frontActor || `${origin}/u/${handle}/ap/actor`),
159
- hmacSecret: String(d.hmacSecret), mode: 'trust',
160
- };
161
- }
162
-
163
- // What the CLI printed before asking "create pod and fediverse account?"
164
- // (bin/fedipod.mjs, the address preview) — as data, so the page can show
165
- // the same warnings. Pure: no network, so it can answer while you type.
166
- export function preflight({ mode, pod, issuer, podName, handle, kind, shape = 'pod', gatewayOrigin = 'https://fedipod.net' }) {
167
- const warnings = [];
168
- if (!handle) return { ok: false, error: 'a handle is required' };
169
- let issuerHost;
170
- try { issuerHost = new URL(issuer || 'https://solidcommunity.net').host; }
171
- catch { return { ok: false, error: `"${issuer}" is not a URL` }; }
172
-
173
- let gwHost; try { gwHost = new URL(gatewayOrigin).host; } catch { gwHost = 'fedipod.net'; }
174
-
175
- if (mode === 'new') {
176
- // Whether the provider puts the pod on its own subdomain or on a path is
177
- // the provider's call, learned only once the pod is made. If the address
178
- // was asked to live at the Gateway, it does. Otherwise the pod's own host
179
- // is the address, and the run moves it to the Gateway if the provider
180
- // turned out to use paths.
181
- if (shape === 'front') {
182
- if (kind === 'group') return { ok: false, mode, handle, kind, error: 'group-needs-host-root', refusal: 'group-needs-host-root', warnings };
183
- return { ok: true, mode, handle, kind, fronted: true, forced: false, gatewayHost: gwHost, shape: 'front',
184
- address: `@${handle}@${gwHost}`, webfingerHost: null, resolvable: true, warnings, refusal: null };
185
- }
186
- return {
187
- ok: true, mode, handle, kind, fronted: false, shape: 'pod',
188
- address: `@${handle}@${podName || handle}.${issuerHost}`,
189
- webfingerHost: null, resolvable: null, warnings, refusal: null,
190
- };
191
- }
192
-
193
- let podUrl;
194
- try { podUrl = new URL(pod); }
195
- catch { return { ok: false, error: `"${pod}" is not a pod address` }; }
196
- // A handle resolves through <host>/.well-known/webfinger, so it only works
197
- // when the pod owns the root of its host. A pod that does not can still be
198
- // an account, with its address at the Gateway.
199
- const wfHost = webfingerHost(podUrl.href);
200
- const fronted = !wfHost || shape === 'front';
201
- if (fronted) {
202
- // A person's address moves to the Gateway; a group cannot, yet — a group
203
- // needs a pod at its own host until fronted groups are proven.
204
- if (kind === 'group') {
205
- return { ok: false, mode: 'existing', handle, kind, refusal: 'group-needs-host-root', warnings,
206
- address: `@${handle}@${podUrl.host}`, webfingerHost: null, resolvable: false };
207
- }
208
- return {
209
- ok: true, mode: 'existing', handle, kind, fronted: true, forced: !wfHost, gatewayHost: gwHost, shape: 'front',
210
- address: `@${handle}@${gwHost}`, webfingerHost: null, resolvable: true, warnings, refusal: null,
211
- };
212
- }
213
- return {
214
- ok: true, mode: 'existing', handle, kind, fronted: false, shape: 'pod',
215
- address: `@${handle}@${wfHost}`,
216
- webfingerHost: wfHost, resolvable: true, warnings, refusal: null,
217
- };
218
- }
219
-
220
- // Fills `run` in as it goes. Never throws: a failure lands in run.error, so a
221
- // page that polls sees it. `deps` is for tests — the real functions otherwise.
222
- export async function runSetup({ home, agent, answers, run, deps = {}, log = () => {} }) {
223
- const createAccount = deps.createAccountWithPod || realCreateAccount;
224
- const mint = deps.mintCredential || realMint;
225
- const checkPod = deps.checkPodUsable || checkPodUsable;
226
- const attachGateway = deps.attachGateway || takeGatewayAddress;
227
-
228
- const at = (key) => run.steps.find(s => s.key === key);
229
- const begin = (key) => { at(key).state = 'running'; };
230
- const done = (key, note = null) => { at(key).state = 'ok'; at(key).note = note; };
231
- const skip = (key, note = null) => { at(key).state = 'skipped'; at(key).note = note; };
232
-
233
- const {
234
- mode, issuer, email, password, handle, name, podName,
235
- kind = 'person', approveJoins = false, summary, icon, keys, uiPassword,
236
- gateway = null, shape = 'pod', gatewayOrigin = 'https://fedipod.net',
237
- } = answers;
238
- let { pod, root } = answers;
239
- if (!root) root = 'fedipod/'; // new installs default to the fedipod/ container; a
240
- // resuming run overwrites this with the credential's own root below.
241
- let accountWebId = null; // what createAccountWithPod reported, when it ran
242
- // The private half always starts here, beside the credential and the keys —
243
- // not on the pod. Every activity you receive would otherwise cost the pod
244
- // several writes, and that pod is a server somebody runs. `state --to` moves
245
- // it afterwards; starting on the pod and moving later is strictly worse,
246
- // because the copy left behind was on the pod the whole time.
247
- let privateRoot = answers.privateRoot
248
- || pathToFileURL(path.join(home, 'private')).href + '/';
249
- // A pod that already holds an account at ANOTHER gateway, set up with an
250
- // address at this one: not a second account but the same one moving in.
251
- // Its state and key are on the pod, and stay there.
252
- let movingIn = false;
253
- let movedFrom = null;
254
- // Everything that leaves this function — notes, errors, the log — goes
255
- // through here. Nothing today echoes a password back, but /setup/progress is
256
- // polled repeatedly and would re-serve one forever if anything ever did.
257
- const scrub = (s) => (password ? String(s).split(password).join('••••••') : String(s));
258
-
259
- const credPath = credentialPath(home);
260
- const resuming = fs.existsSync(credPath);
261
-
262
- try {
263
- // --- account: create it, or use the pod you already have ---
264
- if (resuming) {
265
- skip('account', 'the credential is already on disk');
266
- } else if (mode === 'new') {
267
- begin('account');
268
- const made = await createAccount({ issuer, email, password, podName: podName || handle });
269
- pod = made.pod;
270
- accountWebId = made.webId || null;
271
- log(`account + pod created: ${pod}`);
272
- // The server put the pod on a path rather than its own subdomain, so
273
- // nobody could ever find this group. A person was warned before we got
274
- // here and chose to continue; a group cannot.
275
- if (kind === 'group' && !webfingerHost(pod)) {
276
- throw new Error(`${issuer} created the pod at ${pod} — a suffix-based host, `
277
- + 'not a host root. WebFinger is only answered at a host root, so nobody '
278
- + 'could find this group. The pod exists; no actor was published.');
279
- }
280
- done('account', pod);
281
- } else {
282
- // A pod you bring must be there and carry a usable WebID before a
283
- // credential is minted against it — no account on a pod that is not
284
- // reachable, and no silent 401 later on a pod whose profile is empty.
285
- const usable = await checkPod(pod);
286
- if (!usable.ok) throw new Error(usable.error);
287
- const held = await (deps.resourceExists || resourceExists)(deps.fetch || fetch, apUrls(pod, root || DEFAULT_ROOT).actor);
288
- if (held && !frontedAddress({ pod, shape })) {
289
- throw new Error('The pod already hosts a FediPod account. If you want a second account, put it on a different pod. '
290
- + 'To move an account whose address is at another gateway to this one, ask for an address at this gateway.');
291
- }
292
- if (held && kind === 'group') throw new Error('The pod already hosts a FediPod account, and a group cannot move between gateways.');
293
- movingIn = held;
294
- if (movingIn) { privateRoot = null; }
295
- skip('account', movingIn ? 'the pod already holds the account — its address moves here' : 'using the pod you already have');
296
- }
297
-
298
- // --- credential: the point of no return, and the durability boundary ---
299
- let resumeWebId = null;
300
- if (resuming) {
301
- const rec = JSON.parse(fs.readFileSync(credPath, 'utf8'));
302
- pod = rec.remotePod;
303
- root = rec.root;
304
- resumeWebId = rec.webId || null;
305
- skip('credential', `already minted — ${credPath}`);
306
- } else {
307
- begin('credential');
308
- // The WebID matters as much as the pod: an account with more than one
309
- // pod has more than one, and a credential bound to the wrong one 403s
310
- // every write. Account creation knows which; otherwise the pod's origin
311
- // picks it out.
312
- const credential = await mint({
313
- origin: issuer, email, password, name: 'fedipod',
314
- webId: accountWebId || undefined, podUrl: pod,
315
- });
316
- const rec = {
317
- ...credential,
318
- remotePod: pod.endsWith('/') ? pod : pod + '/',
319
- createdAt: new Date().toISOString(),
320
- ...(root ? { root } : {}),
321
- ...(keys === 'pod' || movingIn ? { keysMode: 'pod' } : {}),
322
- // Where the private half lives. Per-machine, like keysMode. A move-in
323
- // keeps it on the pod, where the account already is, and says so.
324
- ...(privateRoot ? { privateRoot } : {}),
325
- ...(movingIn ? { stateOnPod: true } : {}),
326
- // What shape this install is, so `upgrade` can tell an old one from a
327
- // new one without guessing from the fields.
328
- ...(isCurrent({ privateRoot, stateOnPod: movingIn }) ? { layout: CURRENT_LAYOUT } : {}),
329
- };
330
- fs.mkdirSync(home, { recursive: true, mode: 0o700 });
331
- writeJsonAtomic(credPath, rec);
332
- // The identity you just made is the one you are using, so it becomes what
333
- // a plain command means. Here rather than earlier: the credential landing
334
- // is what makes this an identity at all.
335
- // NOT `root` — that name is already taken by the answers' AP container
336
- // root, destructured above, and shadowing it is a temporal dead zone.
337
- const apHomeRoot = rootOf(home);
338
- if (apHomeRoot !== home) recordLastUsed(apHomeRoot, path.basename(home));
339
- pod = rec.remotePod;
340
- log(`credential minted and saved to ${credPath}`);
341
- done('credential', credPath);
342
- }
343
-
344
- // --- a move-in: the account on the pod, read with the credential just
345
- // minted, has to be one whose address lives at another gateway ---
346
- if (movingIn) {
347
- const cred = JSON.parse(fs.readFileSync(credPath, 'utf8'));
348
- const account = await (deps.readPodAccount || readPodAccount)({ home, credential: cred, pod, root: root || DEFAULT_ROOT, log });
349
- if (!account?.config) throw new Error('the pod holds an account, but its record could not be read with this credential — is this account the pod\'s owner?');
350
- const frontActor = account.config.gateway?.frontActor || null;
351
- const gwHost = new URL(gatewayOrigin).host;
352
- if (!frontActor) throw new Error('The pod already hosts a FediPod account whose address lives on the pod: nothing to move. Sign in to it instead.');
353
- if (new URL(frontActor).host === gwHost) throw new Error(`The pod already hosts a FediPod account at ${gwHost}: sign in to it instead of setting up again.`);
354
- if ((account.config.kind || 'person') === 'group') throw new Error('a group cannot move between gateways');
355
- if (!account.keys?.rsa || account.keys.ct) {
356
- throw new Error(`this account's key is still under a password from an earlier version — sign in once at ${new URL(frontActor).host} first, then set up here`);
357
- }
358
- movedFrom = { actor: frontActor, gateway: new URL(frontActor).origin, handle: account.config.handle, at: new Date().toISOString() };
359
- log(`moving @${account.config.handle}@${new URL(frontActor).host} here, as @${handle}@${gwHost}`);
360
- }
361
-
362
- // --- take an address at the Gateway, if this pod needs one or asked for
363
- // one --- before bootstrap, so the config it writes carries the Gateway
364
- // ids and the key connect mints below is stamped to the Gateway actor from
365
- // the start. A signup-arranged gateway (the installer carry-over) wins.
366
- let gatewayCfg = gateway;
367
- if (!gatewayCfg && frontedAddress({ pod, shape })) {
368
- if (kind === 'group') {
369
- throw new Error(`a group needs a pod at its own host — a Gateway address for a group is not supported yet`
370
- + (webfingerHost(pod) ? '' : `; ${pod} is a suffix-based host`) + '.');
371
- }
372
- const urls = apUrls(pod, root || DEFAULT_ROOT);
373
- const cred = JSON.parse(fs.readFileSync(credPath, 'utf8'));
374
- log(`taking a gateway address at ${new URL(gatewayOrigin).host}`);
375
- gatewayCfg = await attachGateway({
376
- home, credential: cred, gatewayOrigin, handle,
377
- podHome: urls.home, actorUrl: urls.actor, kind, log,
378
- });
379
- log(`gateway address @${handle}@${new URL(gatewayOrigin).host}`);
380
- }
381
-
382
- // --- provision the pod and bring federation up ---
383
- begin('bootstrap');
384
- // Resuming skipped the checks the fresh paths ran, and the credential it
385
- // carries may be the one bound to a pod whose profile is empty — the write
386
- // below would 401. Catch it here, as this step, with a sentence.
387
- if (resuming) {
388
- const ready = await checkPod(pod, { webId: resumeWebId || undefined });
389
- if (!ready.ok) throw new Error(ready.error);
390
- }
391
- await agent.bootstrap({ handle, name: name || handle, root, kind, approveJoins, summary, icon, gateway: gatewayCfg });
392
- if (movingIn && movedFrom) {
393
- // The old actor becomes an alias, which is what a server checks on the
394
- // new actor before it honours a Move; the Move itself is pending until
395
- // the new actor is published (completeGatewayMove, below and at every
396
- // start). The key stays; its stamp follows the actor.
397
- const cfg = agent.store.getConfig() || {};
398
- agent.store.setConfig({ ...cfg, aliases: [...new Set([...(cfg.aliases || []), movedFrom.actor])], movedFrom });
399
- const keyRec = agent.store.read?.('keys.json', null);
400
- if (keyRec?.mintedFor && gatewayCfg?.frontActor) agent.store.write?.('keys.json', { ...keyRec, mintedFor: gatewayCfg.frontActor });
401
- await agent.store.flush();
402
- }
403
- done('bootstrap');
404
-
405
- begin('connect');
406
- // Direct, not connectWithRetry: setup wants to hear about a failure now,
407
- // not back off for an hour with a browser tab waiting on it.
408
- await agent.connect({ repair: false }); // publish below is the publish
409
- done('connect');
410
-
411
- begin('publish');
412
- if (uiPassword) {
413
- agent.store.setConfig({ ...agent.store.getConfig(), uiPassword: hashPassword(uiPassword) });
414
- }
415
- const published = await agent.publisher.publishProfile();
416
- await agent.store.flush();
417
- if (movingIn) {
418
- // Tell the old gateway and the followers. Not fatal: left pending, it
419
- // is tried again when the agent next starts acting.
420
- const moved = await (deps.completeGatewayMove || completeGatewayMove)(agent).catch((e) => { log(`gateway move: ${e.message}`); return null; });
421
- if (moved?.completedAt) log(`Move sent from ${movedFrom.actor} to ${moved.moveSent} inbox(es)`);
422
- }
423
- done('publish');
424
-
425
- // --- and say plainly whether the world can actually see it ---
426
- begin('verify');
427
- const unreachable = published?.unreachable || [];
428
- const wfHost = webfingerHost(pod);
429
- done('verify', unreachable.length
430
- ? `not readable without credentials: ${unreachable.join(', ')}`
431
- : 'the public surface is reachable');
432
-
433
- // A fronted identity resolves at the Gateway, not the pod host.
434
- const frontActor = gatewayCfg?.frontActor || agent.store.getConfig()?.gateway?.frontActor || null;
435
- const gwHost = frontActor ? new URL(frontActor).host : null;
436
- run.result = {
437
- kind,
438
- pod,
439
- handle,
440
- actor: agent.urls?.actor || null,
441
- fronted: !!frontActor,
442
- webfingerHost: gwHost || wfHost,
443
- resolvable: !!(gwHost || wfHost),
444
- address: gwHost ? `@${handle}@${gwHost}` : (wfHost ? `@${handle}@${wfHost}` : null),
445
- unreachable,
446
- };
447
- run.phase = 'done';
448
- log(wfHost
449
- ? `${kind === 'group' ? 'group' : 'actor'} published: @${handle}@${wfHost}`
450
- : `${kind === 'group' ? 'group' : 'actor'} published, but not reachable as a handle — `
451
- + `${pod} is not a host root`);
452
- } catch (e) {
453
- for (const s of run.steps) if (s.state === 'running') s.state = 'error';
454
- run.error = scrub(e.message);
455
- run.phase = 'error';
456
- log(`setup failed: ${run.error}`);
457
- }
458
- return run;
459
- }