fedipod-server 0.29.0 → 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 (440) hide show
  1. package/README.md +11 -6
  2. package/dist/door.d.ts +4 -0
  3. package/dist/door.js +31 -0
  4. package/dist/fedipod.d.ts +16 -0
  5. package/dist/fedipod.js +13 -0
  6. package/dist/front.d.ts +3 -0
  7. package/dist/front.js +94 -0
  8. package/dist/handler.d.ts +36 -101
  9. package/dist/handler.js +85 -645
  10. package/dist/handler.jsonld +2 -30
  11. package/dist/identities.d.ts +62 -0
  12. package/dist/identities.js +236 -0
  13. package/dist/mounts.d.ts +43 -0
  14. package/dist/mounts.js +123 -0
  15. package/dist/opt-in.d.ts +46 -0
  16. package/dist/opt-in.js +176 -0
  17. package/package.json +10 -18
  18. package/lib/client/c2s.mjs +0 -636
  19. package/lib/client/localapi.mjs +0 -87
  20. package/lib/client/masto/accounts.mjs +0 -310
  21. package/lib/client/masto/body.mjs +0 -156
  22. package/lib/client/masto/index.mjs +0 -206
  23. package/lib/client/masto/instance.mjs +0 -112
  24. package/lib/client/masto/media.mjs +0 -79
  25. package/lib/client/masto/oauth.mjs +0 -656
  26. package/lib/client/masto/render.mjs +0 -518
  27. package/lib/client/masto/statuses.mjs +0 -372
  28. package/lib/client/masto/timelines.mjs +0 -438
  29. package/lib/client/oidc-auth.mjs +0 -62
  30. package/lib/client/streaming.mjs +0 -154
  31. package/lib/client/webpush.mjs +0 -98
  32. package/lib/connections/acctfeed.mjs +0 -257
  33. package/lib/connections/atproto.mjs +0 -309
  34. package/lib/connections/bskyfeed.mjs +0 -243
  35. package/lib/connections/bskygroup.mjs +0 -162
  36. package/lib/connections/fediacct.mjs +0 -253
  37. package/lib/connections/import.mjs +0 -357
  38. package/lib/connections/tagfeed.mjs +0 -212
  39. package/lib/connections/vault.mjs +0 -114
  40. package/lib/core/as2.mjs +0 -206
  41. package/lib/core/contexts/activitystreams.json +0 -379
  42. package/lib/core/contexts/anno.json +0 -126
  43. package/lib/core/contexts/did-v1.json +0 -57
  44. package/lib/core/contexts/fep-5711.json +0 -36
  45. package/lib/core/contexts/gotosocial.json +0 -86
  46. package/lib/core/contexts/identity-v1.json +0 -152
  47. package/lib/core/contexts/index.mjs +0 -63
  48. package/lib/core/contexts/join-lemmy.json +0 -33
  49. package/lib/core/contexts/joinmastodon.json +0 -28
  50. package/lib/core/contexts/litepub-0.1.json +0 -60
  51. package/lib/core/contexts/map.json +0 -19
  52. package/lib/core/contexts/miscellany.json +0 -19
  53. package/lib/core/contexts/quotes.json +0 -18
  54. package/lib/core/contexts/schemaorg.json +0 -8845
  55. package/lib/core/contexts/security-data-integrity-v1.json +0 -78
  56. package/lib/core/contexts/security-data-integrity-v2.json +0 -81
  57. package/lib/core/contexts/security-multikey-v1.json +0 -35
  58. package/lib/core/contexts/security-v1.json +0 -74
  59. package/lib/core/contexts/webfinger.json +0 -10
  60. package/lib/core/deliver.mjs +0 -387
  61. package/lib/core/graphview.mjs +0 -327
  62. package/lib/core/intake/activities.mjs +0 -649
  63. package/lib/core/intake/activity.mjs +0 -318
  64. package/lib/core/intake/channel.mjs +0 -144
  65. package/lib/core/intake/group.mjs +0 -258
  66. package/lib/core/intake/index.mjs +0 -732
  67. package/lib/core/intake/notes.mjs +0 -367
  68. package/lib/core/intake/verify.mjs +0 -142
  69. package/lib/core/keys.mjs +0 -153
  70. package/lib/core/lease.mjs +0 -206
  71. package/lib/core/place.mjs +0 -82
  72. package/lib/core/polls.mjs +0 -105
  73. package/lib/core/profile-page.mjs +0 -104
  74. package/lib/core/proof.mjs +0 -79
  75. package/lib/core/publisher/collections.mjs +0 -272
  76. package/lib/core/publisher/index.mjs +0 -522
  77. package/lib/core/publisher/notes.mjs +0 -519
  78. package/lib/core/publisher/own.mjs +0 -143
  79. package/lib/core/publisher/questions.mjs +0 -241
  80. package/lib/core/publisher/restore.mjs +0 -224
  81. package/lib/core/shapes/activitystreams.ttl +0 -157
  82. package/lib/core/shapes/index.mjs +0 -132
  83. package/lib/core/shapes/shapes-text.mjs +0 -13
  84. package/lib/core/social.mjs +0 -561
  85. package/lib/core/storage.mjs +0 -176
  86. package/lib/core/store.mjs +0 -578
  87. package/lib/core/wire-quotes.mjs +0 -71
  88. package/lib/core/wire.mjs +0 -925
  89. package/lib/device/account.mjs +0 -87
  90. package/lib/device/admin/index.mjs +0 -13
  91. package/lib/device/admin/origins.mjs +0 -35
  92. package/lib/device/admin/routes/connections.mjs +0 -144
  93. package/lib/device/admin/routes/gateway.mjs +0 -199
  94. package/lib/device/admin/routes/lifecycle.mjs +0 -200
  95. package/lib/device/admin/routes/owner.mjs +0 -322
  96. package/lib/device/admin/routes/setup.mjs +0 -428
  97. package/lib/device/admin/routes/social.mjs +0 -188
  98. package/lib/device/admin/server.mjs +0 -95
  99. package/lib/device/admin/static.mjs +0 -247
  100. package/lib/device/admin/surface.mjs +0 -315
  101. package/lib/device/certs.mjs +0 -281
  102. package/lib/device/cli/commands/account.mjs +0 -586
  103. package/lib/device/cli/commands/run.mjs +0 -278
  104. package/lib/device/cli/commands/service.mjs +0 -221
  105. package/lib/device/cli/commands/setup.mjs +0 -489
  106. package/lib/device/cli/commands/state.mjs +0 -561
  107. package/lib/device/cli/context.mjs +0 -288
  108. package/lib/device/export-collections.mjs +0 -161
  109. package/lib/device/gateway-move.mjs +0 -58
  110. package/lib/device/home.mjs +0 -161
  111. package/lib/device/migrate.mjs +0 -115
  112. package/lib/device/ports.mjs +0 -27
  113. package/lib/device/remote.mjs +0 -54
  114. package/lib/device/setup.mjs +0 -496
  115. package/lib/device/update.mjs +0 -96
  116. package/lib/gateway/caches.mjs +0 -36
  117. package/lib/gateway/directory.mjs +0 -90
  118. package/lib/gateway/front-core.mjs +0 -966
  119. package/lib/gateway/gateway-core.mjs +0 -244
  120. package/lib/gateway/headers.mjs +0 -49
  121. package/lib/gateway/httpsig.mjs +0 -159
  122. package/lib/gateway/notices.mjs +0 -84
  123. package/lib/gateway/quiet.mjs +0 -189
  124. package/lib/gateway/relay-extras.mjs +0 -89
  125. package/lib/gateway/token-claims.mjs +0 -16
  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/location.mjs +0 -52
  138. package/lib/pod/media.mjs +0 -27
  139. package/lib/pod/notes.mjs +0 -78
  140. package/lib/pod/notifications.mjs +0 -89
  141. package/lib/pod/outbox.mjs +0 -18
  142. package/lib/pod/package.json +0 -19
  143. package/lib/pod/policy.mjs +0 -58
  144. package/lib/pod/private.mjs +0 -19
  145. package/lib/pod/root.mjs +0 -104
  146. package/lib/pod/state.mjs +0 -40
  147. package/lib/pod/transport.mjs +0 -674
  148. package/lib/pod/type-index.mjs +0 -101
  149. package/lib/pod/urls.mjs +0 -91
  150. package/lib/server/embed.mjs +0 -424
  151. package/lib/session/README.md +0 -137
  152. package/lib/session/demo.html +0 -94
  153. package/lib/session/fedi-account.mjs +0 -417
  154. package/lib/session/fedi-login.mjs +0 -123
  155. package/lib/session/oidc-session.mjs +0 -233
  156. package/lib/session/package.json +0 -49
  157. package/lib/shared/guard.mjs +0 -213
  158. package/lib/shared/links.mjs +0 -9
  159. package/lib/shared/safefetch.mjs +0 -235
  160. package/lib/shared/ua.mjs +0 -29
  161. package/phanpy/dist/404.html +0 -40
  162. package/phanpy/dist/_headers +0 -2
  163. package/phanpy/dist/apple-touch-icon.png +0 -0
  164. package/phanpy/dist/assets/ICONS-DIPJeU0C.js +0 -2
  165. package/phanpy/dist/assets/ICONS-DIPJeU0C.js.map +0 -1
  166. package/phanpy/dist/assets/ScheduledAtField-D-Abfo4J.js +0 -183
  167. package/phanpy/dist/assets/ScheduledAtField-D-Abfo4J.js.map +0 -1
  168. package/phanpy/dist/assets/Temml-CCjoacWt.woff2 +0 -0
  169. package/phanpy/dist/assets/__vite-browser-external-C7iut881.js +0 -2
  170. package/phanpy/dist/assets/__vite-browser-external-C7iut881.js.map +0 -1
  171. package/phanpy/dist/assets/boosts-carousel-BiOaNdMT.jpg +0 -0
  172. package/phanpy/dist/assets/catch-up-O5q5C75Z.png +0 -0
  173. package/phanpy/dist/assets/chunk-aKtaBQYM.js +0 -1
  174. package/phanpy/dist/assets/compose-CFEx7wb4.js +0 -2
  175. package/phanpy/dist/assets/compose-CFEx7wb4.js.map +0 -1
  176. package/phanpy/dist/assets/compose-ZJN5xSb4.js +0 -35
  177. package/phanpy/dist/assets/compose-ZJN5xSb4.js.map +0 -1
  178. package/phanpy/dist/assets/debug.module-YFQgwnIr.js +0 -52
  179. package/phanpy/dist/assets/debug.module-YFQgwnIr.js.map +0 -1
  180. package/phanpy/dist/assets/dom-p1YcJ5Rw.js +0 -2
  181. package/phanpy/dist/assets/dom-p1YcJ5Rw.js.map +0 -1
  182. package/phanpy/dist/assets/home-mobile-dark@2x-Dc-J8-SM.png +0 -0
  183. package/phanpy/dist/assets/home-mobile-light@2x-Bd3fod8m.png +0 -0
  184. package/phanpy/dist/assets/home-tablet-dark@2x-BgcNI8le.png +0 -0
  185. package/phanpy/dist/assets/home-tablet-light@2x-BW2RV9T6.png +0 -0
  186. package/phanpy/dist/assets/icons/add-circle-line-WD0mwORD.js +0 -1
  187. package/phanpy/dist/assets/icons/alert-line-CpFUMf9s.js +0 -1
  188. package/phanpy/dist/assets/icons/android-2-line-Bzxnrd3S.js +0 -1
  189. package/phanpy/dist/assets/icons/announcement-line-5nwaJFJm.js +0 -1
  190. package/phanpy/dist/assets/icons/arrow-down-circle-line-a7Id9-Iv.js +0 -1
  191. package/phanpy/dist/assets/icons/arrow-down-line-B8uzXzq6.js +0 -1
  192. package/phanpy/dist/assets/icons/arrow-left-line-CGrGT9C-.js +0 -1
  193. package/phanpy/dist/assets/icons/arrow-right-line-CE-HERfX.js +0 -1
  194. package/phanpy/dist/assets/icons/arrow-to-up-line-CvdQjmcy.js +0 -1
  195. package/phanpy/dist/assets/icons/arrow-up-circle-line-DEparSNF.js +0 -1
  196. package/phanpy/dist/assets/icons/arrow-up-line-C7yBeugc.js +0 -1
  197. package/phanpy/dist/assets/icons/arrows-right-line-BtbXmJGP.js +0 -1
  198. package/phanpy/dist/assets/icons/at-line-CF66Bznk.js +0 -1
  199. package/phanpy/dist/assets/icons/attachment-line-BY8It-l9.js +0 -1
  200. package/phanpy/dist/assets/icons/blockquote-line-BS6pU_hm.js +0 -1
  201. package/phanpy/dist/assets/icons/board-line-C0UWXyA6.js +0 -1
  202. package/phanpy/dist/assets/icons/bookmark-line-1CtX5JLF.js +0 -1
  203. package/phanpy/dist/assets/icons/building-5-line-Dd59MzbO.js +0 -1
  204. package/phanpy/dist/assets/icons/bus-2-line-6_dvA0GG.js +0 -1
  205. package/phanpy/dist/assets/icons/calendar-day-line-Crpl_fmu.js +0 -1
  206. package/phanpy/dist/assets/icons/calendar-month-line-vommts2C.js +0 -1
  207. package/phanpy/dist/assets/icons/calendar-time-add-line-BU_DTlQk.js +0 -1
  208. package/phanpy/dist/assets/icons/camera-line-Cv69MEei.js +0 -1
  209. package/phanpy/dist/assets/icons/celebrate-line-B8DvIoi0.js +0 -1
  210. package/phanpy/dist/assets/icons/chart-bar-line-FK-QOmtF.js +0 -1
  211. package/phanpy/dist/assets/icons/chart-line-line-BGcJAFWw.js +0 -1
  212. package/phanpy/dist/assets/icons/chat-3-line-YFK2-hwz.js +0 -1
  213. package/phanpy/dist/assets/icons/check-circle-line-DsAxFr5-.js +0 -1
  214. package/phanpy/dist/assets/icons/clipboard-line-BBp364gJ.js +0 -1
  215. package/phanpy/dist/assets/icons/close-circle-line-CICdiuGz.js +0 -1
  216. package/phanpy/dist/assets/icons/close-line-DYrFWcjv.js +0 -1
  217. package/phanpy/dist/assets/icons/cloud-line-tiQAbah8.js +0 -1
  218. package/phanpy/dist/assets/icons/code-line-fAl8iiV3.js +0 -1
  219. package/phanpy/dist/assets/icons/comment-2-line-CR3g7WeB.js +0 -1
  220. package/phanpy/dist/assets/icons/copy-2-line-CLW69yF5.js +0 -1
  221. package/phanpy/dist/assets/icons/delete-2-line-B8xmbr4n.js +0 -1
  222. package/phanpy/dist/assets/icons/document-3-line-DBGNhAg2.js +0 -1
  223. package/phanpy/dist/assets/icons/document-line-C7mb9sb7.js +0 -1
  224. package/phanpy/dist/assets/icons/down-line-BGqSSThd.js +0 -1
  225. package/phanpy/dist/assets/icons/edit-4-line-DcgGaEhx.js +0 -1
  226. package/phanpy/dist/assets/icons/emoji-2-line-D8YgnM-y.js +0 -1
  227. package/phanpy/dist/assets/icons/exit-line-Dc07BS-x.js +0 -1
  228. package/phanpy/dist/assets/icons/external-link-line-BvpSkmjO.js +0 -1
  229. package/phanpy/dist/assets/icons/eye-2-line-CiWXx-f_.js +0 -1
  230. package/phanpy/dist/assets/icons/eye-close-line-BgTaSJ4P.js +0 -1
  231. package/phanpy/dist/assets/icons/filter-2-line-B_mR2usy.js +0 -1
  232. package/phanpy/dist/assets/icons/filter-line-C_nm-3cU.js +0 -1
  233. package/phanpy/dist/assets/icons/flag-1-line-C2HB05I0.js +0 -1
  234. package/phanpy/dist/assets/icons/forbid-circle-line-D3LTT9yd.js +0 -1
  235. package/phanpy/dist/assets/icons/formula-line-DLrt2d6j.js +0 -1
  236. package/phanpy/dist/assets/icons/grid-line-CgUmEikf.js +0 -1
  237. package/phanpy/dist/assets/icons/group-line-C5fnX0wU.js +0 -1
  238. package/phanpy/dist/assets/icons/hand-finger-2-line-BGgxbuL9.js +0 -1
  239. package/phanpy/dist/assets/icons/hashtag-line-CyTEEXBb.js +0 -1
  240. package/phanpy/dist/assets/icons/heart-crack-line-BZddbO2C.js +0 -1
  241. package/phanpy/dist/assets/icons/heart-line-CJ7K47J4.js +0 -1
  242. package/phanpy/dist/assets/icons/history-2-line-kWWvFzqv.js +0 -1
  243. package/phanpy/dist/assets/icons/history-line-bGQiwbTH.js +0 -1
  244. package/phanpy/dist/assets/icons/home-3-line-fRU1zeLG.js +0 -1
  245. package/phanpy/dist/assets/icons/information-line-iNk6-sOY.js +0 -1
  246. package/phanpy/dist/assets/icons/keyboard-line-DsCyuQNz.js +0 -1
  247. package/phanpy/dist/assets/icons/layout-4-line-CZkOX52Z.js +0 -1
  248. package/phanpy/dist/assets/icons/layout-5-line-B_znt5wO.js +0 -1
  249. package/phanpy/dist/assets/icons/left-line-eUw0tya4.js +0 -1
  250. package/phanpy/dist/assets/icons/lightning-line-DaKoDdix.js +0 -1
  251. package/phanpy/dist/assets/icons/link-2-line-DCqLmMA7.js +0 -1
  252. package/phanpy/dist/assets/icons/list-check-line-BAxosBh9.js +0 -1
  253. package/phanpy/dist/assets/icons/lock-line-CPoaXeUW.js +0 -1
  254. package/phanpy/dist/assets/icons/mail-line-B0P8aa1L.js +0 -1
  255. package/phanpy/dist/assets/icons/moon-line-CEqNAToP.js +0 -1
  256. package/phanpy/dist/assets/icons/more-1-fill-Dw2e5xab.js +0 -1
  257. package/phanpy/dist/assets/icons/more-3-line-BIiVjYTz.js +0 -1
  258. package/phanpy/dist/assets/icons/notification-line-BZZq2Gtu.js +0 -1
  259. package/phanpy/dist/assets/icons/pencil-line-CKh8-A1v.js +0 -1
  260. package/phanpy/dist/assets/icons/photo-album-line-Bnfg2u40.js +0 -1
  261. package/phanpy/dist/assets/icons/pin-line-DpTolyhs.js +0 -1
  262. package/phanpy/dist/assets/icons/play-fill-B0-G_8lX.js +0 -1
  263. package/phanpy/dist/assets/icons/qrcode-2-line-B6SPCaHn.js +0 -1
  264. package/phanpy/dist/assets/icons/quill-pen-line-B6LUrnCP.js +0 -1
  265. package/phanpy/dist/assets/icons/quote-left-fill-hQZ1cj_6.js +0 -1
  266. package/phanpy/dist/assets/icons/radar-line-CKyroTth.js +0 -1
  267. package/phanpy/dist/assets/icons/react-line-CoIKhhtK.js +0 -1
  268. package/phanpy/dist/assets/icons/refresh-2-line-D0mTF97S.js +0 -1
  269. package/phanpy/dist/assets/icons/right-line-B3KWJaqc.js +0 -1
  270. package/phanpy/dist/assets/icons/rocket-line-4KZl537D.js +0 -1
  271. package/phanpy/dist/assets/icons/round-fill-C69NTrZ0.js +0 -1
  272. package/phanpy/dist/assets/icons/round-line-B9k_1uo-.js +0 -1
  273. package/phanpy/dist/assets/icons/route-line-B28ReHcE.js +0 -1
  274. package/phanpy/dist/assets/icons/rows-4-line-CrBniagM.js +0 -1
  275. package/phanpy/dist/assets/icons/scan-line-DSf74YdP.js +0 -1
  276. package/phanpy/dist/assets/icons/search-2-line-B6geIU1z.js +0 -1
  277. package/phanpy/dist/assets/icons/settings-3-line-azJwK47w.js +0 -1
  278. package/phanpy/dist/assets/icons/settings-6-line-C_5zc5YR.js +0 -1
  279. package/phanpy/dist/assets/icons/share-2-line-D5MxWjrm.js +0 -1
  280. package/phanpy/dist/assets/icons/share-forward-line-Cp61fZzZ.js +0 -1
  281. package/phanpy/dist/assets/icons/sparkles-2-line-DYYyXTaW.js +0 -1
  282. package/phanpy/dist/assets/icons/sparkles-line-C2hY1s6I.js +0 -1
  283. package/phanpy/dist/assets/icons/time-line-BF3eet1v.js +0 -1
  284. package/phanpy/dist/assets/icons/transfer-4-line-oisfvTBw.js +0 -1
  285. package/phanpy/dist/assets/icons/translate-line-BFWol2Ce.js +0 -1
  286. package/phanpy/dist/assets/icons/unlock-line-X_x5vwv3.js +0 -1
  287. package/phanpy/dist/assets/icons/upload-3-line-DgzwUJeW.js +0 -1
  288. package/phanpy/dist/assets/icons/user-4-line-BVSynZCp.js +0 -1
  289. package/phanpy/dist/assets/icons/user-add-2-line-BlGS5Q8q.js +0 -1
  290. package/phanpy/dist/assets/icons/user-add-line-BbXdHG4z.js +0 -1
  291. package/phanpy/dist/assets/icons/user-edit-line-BFAHOV0X.js +0 -1
  292. package/phanpy/dist/assets/icons/user-follow-line-BXux0wSd.js +0 -1
  293. package/phanpy/dist/assets/icons/user-star-line-DdYlpHLU.js +0 -1
  294. package/phanpy/dist/assets/icons/user-warning-line-Cj43j0_E.js +0 -1
  295. package/phanpy/dist/assets/icons/user-x-line-DIDgNlPH.js +0 -1
  296. package/phanpy/dist/assets/icons/volume-line-Bs390KQt.js +0 -1
  297. package/phanpy/dist/assets/icons/volume-mute-line-DzlzIqFZ.js +0 -1
  298. package/phanpy/dist/assets/icons/walk-line-yNXfB-md.js +0 -1
  299. package/phanpy/dist/assets/icons/world-2-line-CenkGIwj.js +0 -1
  300. package/phanpy/dist/assets/icons/zoom-in-line-CaENUphf.js +0 -1
  301. package/phanpy/dist/assets/icons/zoom-out-line-BDNk0EWo.js +0 -1
  302. package/phanpy/dist/assets/instances-BNPptnn-.json +0 -333
  303. package/phanpy/dist/assets/locales/ar-SA-CfheRbCu.js +0 -1
  304. package/phanpy/dist/assets/locales/ca-ES-DHRW86Tz.js +0 -1
  305. package/phanpy/dist/assets/locales/cs-CZ-CKoqEuA1.js +0 -1
  306. package/phanpy/dist/assets/locales/de-DE-ByRPMRFy.js +0 -1
  307. package/phanpy/dist/assets/locales/eo-UY-CPC7wLCS.js +0 -1
  308. package/phanpy/dist/assets/locales/es-ES-dJa0Fd9t.js +0 -1
  309. package/phanpy/dist/assets/locales/eu-ES-C3djlPR2.js +0 -1
  310. package/phanpy/dist/assets/locales/fa-IR-DTT490Ck.js +0 -1
  311. package/phanpy/dist/assets/locales/fi-FI-B23MCPuz.js +0 -1
  312. package/phanpy/dist/assets/locales/fr-FR-BcaQsO5C.js +0 -1
  313. package/phanpy/dist/assets/locales/gl-ES-BR-CVKBN.js +0 -1
  314. package/phanpy/dist/assets/locales/he-IL-CfheRbCu.js +0 -1
  315. package/phanpy/dist/assets/locales/hu-HU-CfheRbCu.js +0 -1
  316. package/phanpy/dist/assets/locales/it-IT-C1136UFV.js +0 -1
  317. package/phanpy/dist/assets/locales/ja-JP-CRZmRAhD.js +0 -1
  318. package/phanpy/dist/assets/locales/kab-Cz8xH_TT.js +0 -1
  319. package/phanpy/dist/assets/locales/ko-KR-CG9UiY4o.js +0 -1
  320. package/phanpy/dist/assets/locales/lt-LT-CJcI6sHZ.js +0 -1
  321. package/phanpy/dist/assets/locales/nb-NO-CfheRbCu.js +0 -1
  322. package/phanpy/dist/assets/locales/nl-NL-DxN4Yc1t.js +0 -1
  323. package/phanpy/dist/assets/locales/oc-FR-CfheRbCu.js +0 -1
  324. package/phanpy/dist/assets/locales/pl-PL-DkwGXf6P.js +0 -1
  325. package/phanpy/dist/assets/locales/pseudo-LOCALE-CQ4XxkQ0.js +0 -1
  326. package/phanpy/dist/assets/locales/pt-BR-nGL0HdFS.js +0 -1
  327. package/phanpy/dist/assets/locales/pt-PT-Cp17aj2z.js +0 -1
  328. package/phanpy/dist/assets/locales/ru-RU-0Y8cIoaK.js +0 -1
  329. package/phanpy/dist/assets/locales/th-TH-CfheRbCu.js +0 -1
  330. package/phanpy/dist/assets/locales/tok-CfheRbCu.js +0 -1
  331. package/phanpy/dist/assets/locales/tr-TR-CfheRbCu.js +0 -1
  332. package/phanpy/dist/assets/locales/uk-UA-C5E3dFBf.js +0 -1
  333. package/phanpy/dist/assets/locales/zh-CN-BHJi7_et.js +0 -1
  334. package/phanpy/dist/assets/locales/zh-TW-CfheRbCu.js +0 -1
  335. package/phanpy/dist/assets/main-BdqNbG-a.js +0 -26
  336. package/phanpy/dist/assets/main-BdqNbG-a.js.map +0 -1
  337. package/phanpy/dist/assets/mock-home-DGhR__He.js +0 -2
  338. package/phanpy/dist/assets/mock-home-DGhR__He.js.map +0 -1
  339. package/phanpy/dist/assets/multi-column-ROck0NVt.jpg +0 -0
  340. package/phanpy/dist/assets/multi-hashtag-timeline-DH5INVHi.jpg +0 -0
  341. package/phanpy/dist/assets/nested-comments-thread-hv59kZLc.jpg +0 -0
  342. package/phanpy/dist/assets/polyfill-force-H8sUkLGp.js +0 -2
  343. package/phanpy/dist/assets/polyfill-force-H8sUkLGp.js.map +0 -1
  344. package/phanpy/dist/assets/pwa-viewport-ogKbF51K.js +0 -20
  345. package/phanpy/dist/assets/pwa-viewport-ogKbF51K.js.map +0 -1
  346. package/phanpy/dist/assets/style-B1iLPTPR.css +0 -2
  347. package/phanpy/dist/assets/temml-DuyIQMei.js +0 -97
  348. package/phanpy/dist/assets/temml-DuyIQMei.js.map +0 -1
  349. package/phanpy/dist/assets/tinyld.light.node-DtbBhLYC.js +0 -2
  350. package/phanpy/dist/assets/tinyld.light.node-DtbBhLYC.js.map +0 -1
  351. package/phanpy/dist/assets/year-in-posts-B2lywqdx.png +0 -0
  352. package/phanpy/dist/assets/year-in-posts-DJv_star.js +0 -3
  353. package/phanpy/dist/assets/year-in-posts-DJv_star.js.map +0 -1
  354. package/phanpy/dist/compose/index.html +0 -20
  355. package/phanpy/dist/favicon.ico +0 -0
  356. package/phanpy/dist/index.html +0 -73
  357. package/phanpy/dist/logo-192.png +0 -0
  358. package/phanpy/dist/logo-512.png +0 -0
  359. package/phanpy/dist/logo-badge-72.png +0 -0
  360. package/phanpy/dist/logo-maskable-512.png +0 -0
  361. package/phanpy/dist/logo-monochrome-512.png +0 -0
  362. package/phanpy/dist/logo-monochrome-maskable-512.png +0 -0
  363. package/phanpy/dist/manifest.webmanifest +0 -1
  364. package/phanpy/dist/og-image-2.jpg +0 -0
  365. package/phanpy/dist/og-image.png +0 -0
  366. package/phanpy/dist/robots.txt +0 -2
  367. package/phanpy/dist/sw.js +0 -2
  368. package/phanpy/dist/sw.js.map +0 -1
  369. package/phanpy/dist/version.json +0 -1
  370. package/run-agent.mjs +0 -899
  371. package/vendor/gate.cjs +0 -138
  372. package/vendor/idp-grant.cjs +0 -394
  373. package/vendor/jfetch.cjs +0 -16
  374. package/web/admin/actors.js +0 -147
  375. package/web/admin/bar.css +0 -126
  376. package/web/admin/bar.js +0 -99
  377. package/web/admin/client/client.js +0 -66
  378. package/web/admin/client/index.html +0 -69
  379. package/web/admin/client-pick.js +0 -79
  380. package/web/admin/common.js +0 -23
  381. package/web/admin/connections.js +0 -112
  382. package/web/admin/gateway.js +0 -133
  383. package/web/admin/group.js +0 -258
  384. package/web/admin/index.html +0 -612
  385. package/web/admin/notices-bar.js +0 -90
  386. package/web/admin/oauth-signin.mjs +0 -37
  387. package/web/admin/record.js +0 -393
  388. package/web/admin/setup/index.html +0 -257
  389. package/web/admin/setup/setup.js +0 -339
  390. package/web/admin/tokens.css +0 -68
  391. package/web/admin/upkeep.js +0 -177
  392. package/web/admin/window.css +0 -55
  393. package/web/admin/window.js +0 -136
  394. package/web/app/README.md +0 -81
  395. package/web/app/admin-facade.mjs +0 -588
  396. package/web/app/agent.mjs +0 -672
  397. package/web/app/atproto-browser.mjs +0 -86
  398. package/web/app/authorize_interaction.html +0 -56
  399. package/web/app/authorize_interaction.js +0 -69
  400. package/web/app/boot.mjs +0 -616
  401. package/web/app/deliver-relay.mjs +0 -129
  402. package/web/app/dist/boot.js +0 -34562
  403. package/web/app/dist/boot.js.map +0 -7
  404. package/web/app/dist/sw.js +0 -73456
  405. package/web/app/dist/sw.js.map +0 -7
  406. package/web/app/fediacct-browser.mjs +0 -146
  407. package/web/app/gateway-move.mjs +0 -60
  408. package/web/app/idb-kv.mjs +0 -35
  409. package/web/app/index.html +0 -258
  410. package/web/app/keys-browser.mjs +0 -71
  411. package/web/app/keystore.mjs +0 -82
  412. package/web/app/oidc-session.mjs +0 -6
  413. package/web/app/pod-auth.mjs +0 -248
  414. package/web/app/pod-remote.mjs +0 -84
  415. package/web/app/shims/_globals.mjs +0 -2
  416. package/web/app/shims/fedify-sig.mjs +0 -77
  417. package/web/app/shims/node-crypto.mjs +0 -113
  418. package/web/app/shims/node-fs.mjs +0 -17
  419. package/web/app/shims/node-path.mjs +0 -19
  420. package/web/app/shims/node-url.mjs +0 -7
  421. package/web/app/shims/prelude.js +0 -58
  422. package/web/app/shims/safefetch.mjs +0 -58
  423. package/web/app/shims/shapes-text.mjs +0 -8
  424. package/web/app/shims/web-push.mjs +0 -14
  425. package/web/app/signup.mjs +0 -357
  426. package/web/app/sw-src.mjs +0 -220
  427. package/web/app/update.js +0 -70
  428. package/web/front/admin.html +0 -55
  429. package/web/front/admin.js +0 -89
  430. package/web/front/install.sh +0 -69
  431. package/web/front/new-account.html +0 -35
  432. package/web/front/new-account.js +0 -13
  433. package/web/front/notices.html +0 -69
  434. package/web/front/notices.js +0 -107
  435. package/web/front/run.html +0 -64
  436. package/web/front/run.js +0 -221
  437. package/web/front/solid-oidc-client.js +0 -6
  438. package/web/icons/forum.svg +0 -11
  439. package/web/icons/gateway.svg +0 -14
  440. package/web/icons/local.svg +0 -11
@@ -1,189 +0,0 @@
1
- // quiet.mjs — accounts that go quiet: what the front knows about an account
2
- // nobody opens, and what its owner may say about it. The router in
3
- // front-core.mjs asks closedState/accountState at the door and on every
4
- // public read, stamps noteOpened from the relay, counts noteReceived after
5
- // a forwarded delivery, and hands /api/open, /api/pause and /api/close here.
6
-
7
- // ---- accounts that go quiet -------------------------------------------------
8
- //
9
- // The gateway holds no mail: every delivery it takes is two writes into the
10
- // owner's pod inbox, read only when their browser is open. An account nobody
11
- // opens grows on its pod without limit, and its owner comes back to a drain
12
- // of everything at once. So the front keeps two facts about each browser
13
- // account — when its owner was last here, and how much content has arrived
14
- // since — and acts on them.
15
- //
16
- // `openedAt` is written when the owner signs in (POST /api/open, always) and
17
- // while they act through the relay (at most hourly). The count of content
18
- // deliveries since then lives in its own record, keyed by handle AND
19
- // openedAt: a fresh stamp starts a fresh count with nothing to reset, and a
20
- // delivery's write can never overwrite a sign-in's. Two deliveries landing
21
- // together may each read the same total and one increment is lost — the cap
22
- // comes out slightly soft, never too strict.
23
- //
24
- // PAUSED: the count has reached the cap, or the owner said so. Content is
25
- // accepted and discarded, control (follows, unfollows, moves, deletions,
26
- // blocks) still lands. The owner's next sign-in ends a cap pause by itself; a
27
- // pause they set lasts until they lift it.
28
- //
29
- // CLOSED: the owner said so, or nothing has opened the account for the close
30
- // window. Found closed by time, it is written down so it stays closed. From
31
- // then on the door, the actor and the handle answer 410 — never a failure
32
- // code, which senders retry and count against this whole host, and never a
33
- // success, which would keep them sending. Nothing on the pod is touched. An
34
- // address that moved (see /api/move) keeps answering as moved; closing does
35
- // not take that away.
36
- //
37
- // Only an account whose owner has signed in from a browser carries
38
- // `openedAt`. A DeviceAgent behind this door never does: it drains its own
39
- // inbox as it runs, so it is never counted, never paused and never closed by
40
- // time. Accounts from before this was built are counted from their next
41
- // sign-in.
42
- export const DEFAULT_PAUSE_ITEMS = 5000;
43
- export const DEFAULT_CLOSE_DAYS = 183; // six months
44
- const OPEN_STAMP_EVERY_MS = 60 * 60_000; // the relay's stamp, at most hourly
45
- const pauseItemsOf = (ctx) => (Number(ctx.pauseItems) > 0 ? Number(ctx.pauseItems) : DEFAULT_PAUSE_ITEMS);
46
- const closeDaysOf = (ctx) => (Number(ctx.closeDays) > 0 ? Number(ctx.closeDays) : DEFAULT_CLOSE_DAYS);
47
- const receivedKey = (key, rec) => `${key}/${rec.openedAt}`;
48
-
49
- export async function receivedSince(ctx, key, rec) {
50
- if (!ctx.readReceived || !rec.openedAt) return { items: 0, bytes: 0 };
51
- const got = await ctx.readReceived(receivedKey(key, rec)).catch(() => null);
52
- return { items: Number(got?.items) || 0, bytes: Number(got?.bytes) || 0 };
53
- }
54
-
55
- // One more content delivery reached the pod. Nothing to count against until
56
- // the owner has been here once.
57
- export async function noteReceived(ctx, key, rec, bytes) {
58
- if (!ctx.readReceived || !ctx.writeReceived || !rec.openedAt) return;
59
- const so = await receivedSince(ctx, key, rec);
60
- await ctx.writeReceived(receivedKey(key, rec), { items: so.items + 1, bytes: so.bytes + (Number(bytes) || 0) })
61
- .catch((e) => console.log(`front @${key}: count not written: ${e?.message || e}`));
62
- }
63
-
64
- // The owner is here. `always` is a sign-in; the relay stamps at most hourly,
65
- // so a busy hour of posting is one write. The count that went with the old
66
- // stamp is dropped: it is over, and a store should not keep one per hour.
67
- export async function noteOpened(ctx, key, rec, { always = false } = {}) {
68
- if (!ctx.putDirectory) return rec;
69
- if (!always && rec.openedAt && Date.now() - Date.parse(rec.openedAt) < OPEN_STAMP_EVERY_MS) return rec;
70
- const next = { ...rec, openedAt: new Date().toISOString() };
71
- await ctx.putDirectory(key, next);
72
- if (rec.openedAt && ctx.dropReceived) await ctx.dropReceived(receivedKey(key, rec)).catch(() => {});
73
- return next;
74
- }
75
-
76
- // Closed, and why. Reads no count: this is asked on every public read.
77
- export async function closedState(ctx, key, rec) {
78
- if (rec.movedTo) return { closed: false };
79
- if (rec.closedAt) return { closed: true, closedAt: rec.closedAt, closedBy: rec.closedBy || 'owner' };
80
- if (rec.openedAt && Date.now() - Date.parse(rec.openedAt) > closeDaysOf(ctx) * 86400_000) {
81
- const closedAt = new Date().toISOString();
82
- if (ctx.putDirectory) await ctx.putDirectory(key, { ...rec, closedAt, closedBy: 'quiet' });
83
- console.log(`front @${key}: closed — nothing opened it since ${rec.openedAt}`);
84
- return { closed: true, closedAt, closedBy: 'quiet' };
85
- }
86
- return { closed: false };
87
- }
88
-
89
- // The whole standing of an account, for the door and for its owner's page.
90
- export async function accountState(ctx, key, rec) {
91
- const c = await closedState(ctx, key, rec);
92
- const received = c.closed ? { items: 0, bytes: 0 } : await receivedSince(ctx, key, rec);
93
- const cap = pauseItemsOf(ctx);
94
- const pausedBy = c.closed ? null : rec.pausedAt ? 'owner' : received.items >= cap ? 'quiet' : null;
95
- return {
96
- closed: c.closed, closedAt: c.closedAt || null, closedBy: c.closedBy || null,
97
- paused: !!pausedBy, pausedBy, pausedAt: rec.pausedAt || null,
98
- openedAt: rec.openedAt || null, received, pauseItems: cap, closeDays: closeDaysOf(ctx),
99
- };
100
- }
101
-
102
- // A 410 with a reason, the shape every closed or moved id answers with.
103
- export const closedAnswer = (headers = {}, why = 'this address is closed') => ({
104
- status: 410, headers: { 'cache-control': 'no-store', ...headers, 'content-type': 'application/json' },
105
- body: JSON.stringify({ error: why }) });
106
-
107
- // The owner of a row, proved the way the relay proves them: a pod token whose
108
- // WebID is the row's, or lives on the row's pod for a row from before WebIDs
109
- // were recorded.
110
- async function provedOwner(request, pathname, ctx, rec, { j, verifyPodToken, webidUnderPod }) {
111
- const webid = await verifyPodToken(request, pathname, ctx.verifier);
112
- if (!webid) return { error: j(401, { error: 'a Solid-OIDC token proving the pod is required' }) };
113
- const owner = rec.webId ? webid === rec.webId : webidUnderPod(webid, rec.podHome);
114
- if (!owner) return { error: j(403, { error: "the token proves a different pod than this account's" }) };
115
- return { webid };
116
- }
117
-
118
- // The owner's three routes. `deps` are front-core's own JSON reply and
119
- // token check, handed in so this module stays free of the router's privates.
120
- // Returns a response, or null when the path is not one of these.
121
- export async function routeQuietApi(request, pathname, ctx, deps) {
122
- const { j } = deps;
123
- if (!['/api/open', '/api/pause', '/api/close'].includes(pathname)) return null;
124
- // A page at another origin asks first whether it may call, and is answered
125
- // as the relay answers it. Unanswered, the browser never sends the call and
126
- // some pages asked again every minute.
127
- if (request.method === 'OPTIONS') return deps.apiPreflight();
128
- if (request.method !== 'POST') return null;
129
- // The owner is here: their browser says so as it opens the account. The
130
- // answer is the account's standing at this gateway, which the manage page
131
- // shows. A closed address is told so and nothing is written.
132
- if (pathname === '/api/open' && request.method === 'POST') {
133
- if (!ctx.putDirectory) return j(501, { error: 'this front keeps no directory' });
134
- let body;
135
- try { body = JSON.parse(await request.clone().text()); } catch { return j(400, { error: 'bad JSON' }); }
136
- const handle = String(body.handle || '').toLowerCase();
137
- let rec = await ctx.lookup(handle);
138
- if (!rec) return j(404, { error: 'no such account' });
139
- const who = await provedOwner(request, pathname, ctx, rec, deps);
140
- if (who.error) return who.error;
141
- const before = await closedState(ctx, handle, rec);
142
- if (before.closed) return j(410, { error: 'this address is closed', closedAt: before.closedAt, closedBy: before.closedBy });
143
- rec = await noteOpened(ctx, handle, rec, { always: true });
144
- return j(200, { ok: true, handle, ...await accountState(ctx, handle, rec) });
145
- }
146
-
147
- // The owner pausing their own account, or lifting the pause they set.
148
- if (pathname === '/api/pause' && request.method === 'POST') {
149
- if (!ctx.putDirectory) return j(501, { error: 'this front keeps no directory' });
150
- let body;
151
- try { body = JSON.parse(await request.clone().text()); } catch { return j(400, { error: 'bad JSON' }); }
152
- const handle = String(body.handle || '').toLowerCase();
153
- let rec = await ctx.lookup(handle);
154
- if (!rec) return j(404, { error: 'no such account' });
155
- const who = await provedOwner(request, pathname, ctx, rec, deps);
156
- if (who.error) return who.error;
157
- const before = await closedState(ctx, handle, rec);
158
- if (before.closed) return j(410, { error: 'this address is closed', closedAt: before.closedAt });
159
- if (typeof body.paused !== 'boolean') return j(400, { error: 'paused must be true or false' });
160
- if (body.paused && !rec.pausedAt) rec = { ...rec, pausedAt: new Date().toISOString() };
161
- if (!body.paused && rec.pausedAt) { rec = { ...rec }; delete rec.pausedAt; }
162
- // Lifting a pause is a sign-in's worth of "I am here": the count starts over.
163
- if (!body.paused) rec = { ...rec, openedAt: new Date().toISOString() };
164
- await ctx.putDirectory(handle, rec);
165
- console.log(`front @${handle}: ${body.paused ? 'paused' : 'resumed'} by its owner`);
166
- return j(200, { ok: true, handle, ...await accountState(ctx, handle, rec) });
167
- }
168
-
169
- // The owner closing their address for good. Repeating it changes nothing.
170
- if (pathname === '/api/close' && request.method === 'POST') {
171
- if (!ctx.putDirectory) return j(501, { error: 'this front keeps no directory' });
172
- let body;
173
- try { body = JSON.parse(await request.clone().text()); } catch { return j(400, { error: 'bad JSON' }); }
174
- const handle = String(body.handle || '').toLowerCase();
175
- let rec = await ctx.lookup(handle);
176
- if (!rec) return j(404, { error: 'no such account' });
177
- const who = await provedOwner(request, pathname, ctx, rec, deps);
178
- if (who.error) return who.error;
179
- if (body.confirm !== true) return j(400, { error: 'closing is for good — send confirm: true' });
180
- if (!rec.closedAt) {
181
- rec = { ...rec, closedAt: new Date().toISOString(), closedBy: 'owner' };
182
- await ctx.putDirectory(handle, rec);
183
- console.log(`front @${handle}: closed by its owner`);
184
- }
185
- return j(200, { ok: true, handle, ...await accountState(ctx, handle, rec) });
186
- }
187
-
188
- return null;
189
- }
@@ -1,89 +0,0 @@
1
- // relay-extras.mjs — the relay's single request, and what the relay notices
2
- // about the requests it carries.
3
- //
4
- // relayOne(): sends one request a browser-run agent has already signed, with
5
- // exactly the headers that were signed. It forwards a signature and cannot
6
- // make one: a POST must carry one, by this account's own key, over the body
7
- // its Digest names.
8
- //
9
- // withdrawn(): a relayed delivery that withdraws something of the account's —
10
- // a Delete, or an Undo — means the copies the edge holds of that account's
11
- // documents are out of date; the front purges them rather than serving a
12
- // deleted post for the rest of its hold.
13
- //
14
- // overLimit(): how many requests one account may have the relay carry. Counted
15
- // per running instance, which bounds a runaway agent or a stolen token without
16
- // any shared state; a real account sends nowhere near it.
17
-
18
- import crypto from 'node:crypto';
19
- import { readCapped, safeFetch } from '../shared/safefetch.mjs';
20
-
21
- const RELAY_MAX_BODY = 1024 * 1024;
22
- const RELAY_TIMEOUT_MS = 8_000;
23
- // The only headers a relayed request may carry to the remote server. Host
24
- // comes from the URL; the user agent from safeFetch.
25
- const RELAY_HEADERS = new Set(['date', 'digest', 'signature', 'content-type', 'accept']);
26
- const keyIdOf = (signature) => (/keyId="([^"]+)"/.exec(signature || '') || [])[1] || null;
27
-
28
- export async function relayOne(item, rec, fetchImpl) {
29
- const url = String(item?.url || '');
30
- const method = String(item?.method || 'POST').toUpperCase();
31
- if (method !== 'GET' && method !== 'POST') return { url, status: 0, error: 'method must be GET or POST' };
32
- let u;
33
- try { u = new URL(url); } catch { return { url, status: 0, error: 'not a URL' }; }
34
- if (u.protocol !== 'https:' && !(u.protocol === 'http:' && process.env.AP_ALLOW_PRIVATE_TARGETS === '1')) {
35
- return { url, status: 0, error: 'https only' };
36
- }
37
- const headers = {};
38
- for (const [k, v] of Object.entries(item?.headers || {})) {
39
- const name = k.toLowerCase();
40
- if (RELAY_HEADERS.has(name) && typeof v === 'string') headers[name] = v;
41
- }
42
- const body = method === 'POST' ? String(item?.body ?? '') : undefined;
43
- if (body !== undefined && Buffer.byteLength(body) > RELAY_MAX_BODY) return { url, status: 0, error: 'body too large' };
44
- const keyId = keyIdOf(headers.signature);
45
- if (method === 'POST' && !keyId) return { url, status: 0, error: 'a delivery must be signed' };
46
- if (keyId && !keyId.startsWith(rec.actorUrl + '#')) return { url, status: 0, error: "signed with a key that is not this account's" };
47
- if (headers.digest) {
48
- const want = 'SHA-256=' + crypto.createHash('sha256').update(body || '').digest('base64');
49
- if (headers.digest !== want) return { url, status: 0, error: 'digest does not match the body' };
50
- }
51
- try {
52
- const res = await safeFetch(url, { method, headers, body, signal: AbortSignal.timeout(RELAY_TIMEOUT_MS) }, fetchImpl);
53
- const out = { url, method, status: res.status };
54
- // The far server asking to be left alone has to reach the agent that will
55
- // do the asking again. Without this the browser build could not honour a
56
- // Retry-After at all — every delivery it makes goes through here — and fell
57
- // back to its own ladder against a server that had already said how long.
58
- const retryAfter = res.headers.get('retry-after');
59
- if (retryAfter) out.retryAfter = retryAfter;
60
- if (method === 'GET') {
61
- out.contentType = res.headers.get('content-type') || null;
62
- out.body = await readCapped(res, RELAY_MAX_BODY);
63
- }
64
- return out;
65
- } catch (e) { return { url, method, status: 0, error: e.message }; }
66
- }
67
-
68
- const WINDOW_MS = 10 * 60 * 1000;
69
- const PER_WINDOW = 600;
70
- const counts = new Map(); // handle → { since, n }
71
-
72
- export function withdrawn(items, actorUrl) {
73
- return items.some((it) => {
74
- if (String(it?.method || 'POST').toUpperCase() !== 'POST') return false;
75
- try {
76
- const a = JSON.parse(String(it?.body ?? ''));
77
- return a?.actor === actorUrl && (a.type === 'Delete' || a.type === 'Undo');
78
- } catch { return false; }
79
- });
80
- }
81
-
82
- export function overLimit(handle, n, now = Date.now()) {
83
- const c = counts.get(handle);
84
- const fresh = !c || now - c.since > WINDOW_MS ? { since: now, n: 0 } : c;
85
- fresh.n += n;
86
- counts.set(handle, fresh);
87
- if (counts.size > 10_000) counts.clear();
88
- return fresh.n > PER_WINDOW;
89
- }
@@ -1,16 +0,0 @@
1
- // token-claims.mjs — who a Solid access token says it is for, unchecked.
2
- //
3
- // Only for choosing where to send a read that the pod checks anyway: the
4
- // owner's full outbox lives on the pod under the owner's own rule, so reading
5
- // the claim decides nothing but the address. Verifying it here would let
6
- // anyone make the front fetch signing keys from a server they name, on every
7
- // read.
8
-
9
- export function claimedWebId(request) {
10
- const token = /^(?:DPoP|Bearer)\s+([\w-]+)\.([\w-]+)\./u.exec(request.headers.get('authorization') || '');
11
- if (!token) return null;
12
- try {
13
- const claims = JSON.parse(Buffer.from(token[2], 'base64url').toString('utf8'));
14
- return typeof claims?.webid === 'string' ? claims.webid : null;
15
- } catch { return null; }
16
- }
package/lib/pod/README.md DELETED
@@ -1,61 +0,0 @@
1
- # solid-ap-pod
2
-
3
- Reading and writing an ActivityPub actor's documents on a Solid pod.
4
-
5
- It knows **where each document lives and how it lands safely** — the container
6
- layout, the transport, the WAC access-control documents, the deletion deny-list,
7
- the byte budgets. It does not know **what is in them**: every AS2 document is
8
- built by the caller and passed in. That line is why this can be lifted out of
9
- the application it grew up in.
10
-
11
- It currently lives inside FediPod at `lib/pod/`. Extracting it is a directory
12
- move plus this `package.json`; `scripts/check-pod-calls.mjs` in the parent repo
13
- exists to keep that true.
14
-
15
- ## Rules it holds itself to
16
-
17
- 1. **No upward imports.** Nothing here reaches outside this directory.
18
- 2. **No Node built-ins.** It runs unmodified in a service worker, which is why
19
- it carries its own `readCapped`/`retryAfterMs` in `http.mjs` rather than
20
- using a host application's SSRF-guarding fetch.
21
- 3. **One runtime dependency, rdflib**, reached only by `transport.mjs` (listings,
22
- ACL documents, ACP detection, the WebID patch). Resource modules must stay
23
- free of it so a size-sensitive host — a serverless function answering every
24
- request — can import one without paying for a parser.
25
- 4. **Everything injected.** It never builds a session, mints a credential, or
26
- reads config or environment.
27
- 5. **No opinions with defaults.** `apUrls` requires the container root rather
28
- than guessing one, because a guessed root put an identity's state in one
29
- container and its published documents in another.
30
-
31
- ## Layout
32
-
33
- | file | what |
34
- |---|---|
35
- | `urls.mjs` | the container layout; `apUrls(remotePod, root, { publicBase })` |
36
- | `transport.mjs` | `PodTransport` — verbs, ACLs, listings, cooldown, deny-list |
37
- | `http.mjs` | `readCapped`, `retryAfterMs` |
38
- | `links.mjs` | RFC 8288 `Link` header parsing |
39
-
40
- ## The transport
41
-
42
- `PodTransport` takes a session — `{ fetch(url, init) }` — and never makes one.
43
- A Node agent hands it a DPoP client-credentials grant, a browser hands it a
44
- Solid-OIDC session, a pod server hands it a shim straight onto its own store.
45
-
46
- Three manners hold for every request, and they are in `fetch()` so nothing can
47
- route around them:
48
-
49
- - **The cooldown.** A 429 or 503 arms a pod-wide pause. `cooldownMode: 'refuse'`
50
- fails fast for the window (right for a daemon with timers behind it);
51
- `'wait'` sleeps it out (right for a tab with a person in front of it).
52
- - **The url map.** A fronted identity's advertised ids are mapped to their pod
53
- locations at this one choke point, so callers pass advertised ids throughout.
54
- - **The deletion deny-list.** A pod's profile, settings, discovery documents,
55
- access-control documents and lease are never deletable. A pod was crippled
56
- exactly this way; the list is a DENY-list rather than an allow-prefix because
57
- the next caller will have a different prefix and the same things it must
58
- never touch.
59
-
60
- Subclasses override **`_send`**, not `fetch` — an override of `_send` cannot
61
- skip any of the three.
package/lib/pod/actor.mjs DELETED
@@ -1,96 +0,0 @@
1
- // actor.mjs — ap/actor and the documents that speak for it.
2
- //
3
- // "Actor" here always means the ActivityPub actor DOCUMENT — the thing remote
4
- // servers fetch to learn this identity's inbox, keys and collections. It is
5
- // not the party performing an operation; a transport carries that as its
6
- // `role`.
7
- //
8
- // The actor is public, and its Read rule travels with every write for the same
9
- // reason it does in discovery.mjs: an actor nobody can fetch is an identity
10
- // nobody can follow, and the failure is silent from this side.
11
-
12
- const PUBLIC_READ = ['Read'];
13
-
14
- // What ActivityPub §3.2 requires a client to send: the profiled JSON-LD type is
15
- // the MUST, activity+json only the SHOULD a server ought to treat as equivalent.
16
- // Asking for both is the only form every implementation answers.
17
- const ACCEPT_AP = 'application/activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams"';
18
-
19
- // ---- the owner's agent ----
20
-
21
- /** Publish the actor document. */
22
- export async function write(pod, urls, doc) {
23
- await pod.putJson(urls.actor, doc);
24
- await pod.setAcl(urls.actor, PUBLIC_READ);
25
- }
26
-
27
- /** What this pod currently publishes as its actor, or null if nothing does. */
28
- export function read(pod, urls) {
29
- return pod.getJson(urls.actor);
30
- }
31
-
32
- /**
33
- * Replace the actor with a Tombstone.
34
- *
35
- * Still public-Read, deliberately: a retired actor that 404s reads to a remote
36
- * server as a temporary failure worth retrying, where a Tombstone reads as an
37
- * answer.
38
- */
39
- export async function writeTombstone(pod, urls, doc) {
40
- await pod.putJson(urls.actor, doc);
41
- await pod.setAcl(urls.actor, PUBLIC_READ);
42
- }
43
-
44
- /**
45
- * Publish the actor carrying `movedTo`.
46
- *
47
- * The same document in the same place — but named apart from `write` because
48
- * what it means is not "the profile changed", it is "this identity is now
49
- * somewhere else", and a caller should have to say which it intends.
50
- */
51
- export async function writeMoved(pod, urls, doc) {
52
- await pod.putJson(urls.actor, doc);
53
- await pod.setAcl(urls.actor, PUBLIC_READ);
54
- }
55
-
56
- /** The human half: a page a person can open and follow from. */
57
- export async function writeProfilePage(pod, urls, html) {
58
- await pod.put(urls.profileHtml, html, 'text/html');
59
- await pod.setAcl(urls.profileHtml, PUBLIC_READ);
60
- }
61
-
62
- /**
63
- * Record the actor in the pod owner's WebID profile, as `foaf:account`.
64
- *
65
- * The inverse of the actor's own `alsoKnownAs`: together they let a reader of
66
- * either document verify the other, rather than taking one side's word.
67
- *
68
- * Delegated to the transport, which reads the profile, refuses to write if the
69
- * parsed graph does not mention the WebID, and patches exactly the statements
70
- * involved rather than rewriting a document full of things that are not ours.
71
- */
72
- export function linkInWebIdProfile(pod, { actorUrl, accountName, kind = 'person', outbox = null }) {
73
- return pod.linkAccountInProfile({ actorUrl, accountName, kind, outbox });
74
- }
75
-
76
- // ---- anyone at all ----
77
-
78
- /**
79
- * The OIDC issuer a published actor names, read without credentials.
80
- *
81
- * Asked of a stranger's pod during sign-in, before there is any session to ask
82
- * with — so a plain fetch, and a null rather than a throw when the actor is
83
- * not there or says nothing.
84
- */
85
- export async function readIssuer(actorUrl, fetchImpl = fetch) {
86
- try {
87
- // Both media types, and the profile with it. `application/activity+json`
88
- // alone is what ActivityPub calls the SHOULD, not the MUST — and a pod that
89
- // stores its actor as JSON-LD answers 501 to it, which would read here as
90
- // "this pod names no issuer" and send the caller off to guess one.
91
- const res = await fetchImpl(actorUrl, { headers: { accept: ACCEPT_AP } });
92
- if (res.status >= 400) return null;
93
- const doc = await res.json();
94
- return doc?.endpoints?.oauthAuthorizationEndpoint || null;
95
- } catch { return null; }
96
- }
@@ -1,77 +0,0 @@
1
- // collection.mjs — the mechanics every AS2 collection on a pod shares.
2
- //
3
- // Not a resource itself: the outbox, the followers list and the block list are
4
- // each their own document with their own meaning, and each has a module that
5
- // says so. What they have in common is how a collection is written and walked,
6
- // and that is here so it is written once.
7
- //
8
- // A paged collection is a HEAD carrying counts and bounds, plus page documents
9
- // carrying the items. A remote server reads the small head and walks only as
10
- // far as it needs, instead of pulling one enormous document.
11
-
12
- const PUBLIC_READ = ['Read'];
13
-
14
- /**
15
- * One page.
16
- *
17
- * `publicRead` is false for a page that already exists: the ACL is set when a
18
- * page is first created and not rewritten on every republish, because a page
19
- * is written on every change and its rule is not what changed.
20
- */
21
- export async function writePage(pod, pageUrl, doc, { publicRead = false } = {}) {
22
- await pod.putJson(pageUrl, doc);
23
- if (publicRead) await pod.setAcl(pageUrl, PUBLIC_READ);
24
- }
25
-
26
- /** The head — counts, bounds, and the pointer at the first page. */
27
- export async function writeHead(pod, url, doc, { publicRead = false } = {}) {
28
- await pod.putJson(url, doc);
29
- if (publicRead) await pod.setAcl(url, PUBLIC_READ);
30
- }
31
-
32
- /**
33
- * Remove a page that is no longer within bounds.
34
- *
35
- * Best-effort: a surplus page left behind is stale but harmless — the head no
36
- * longer points at it — where a failed publish over a delete is not.
37
- */
38
- export async function dropPage(pod, pageUrl) {
39
- await pod.delete(pageUrl).catch(() => {});
40
- }
41
-
42
- /** A whole flat collection, written as one document. */
43
- export async function writeFlat(pod, url, doc, { publicRead = false } = {}) {
44
- await pod.putJson(url, doc);
45
- if (publicRead) await pod.setAcl(url, PUBLIC_READ);
46
- }
47
-
48
- /**
49
- * Every item in a published collection, walking pages.
50
- *
51
- * Also reads a FLAT collection, so a collection published before this project
52
- * paged them is still readable — which matters because a rebuild reads these
53
- * to recover what a lost machine no longer has.
54
- *
55
- * `seen` guards a cycle and `max` a collection that lies about its own size;
56
- * both are the pod's data but not necessarily ours, since a restored backup or
57
- * a half-finished write can leave either.
58
- *
59
- * @returns items, or null when there is no collection there at all
60
- */
61
- export async function readPaged(pod, headUrl, { max = 10_000, alsoItems = false } = {}) {
62
- const head = await pod.getJson(headUrl).catch(() => null);
63
- if (!head) return null;
64
- if (Array.isArray(head.orderedItems) && head.orderedItems.length) return head.orderedItems;
65
- if (alsoItems && Array.isArray(head.items) && head.items.length) return head.items;
66
- const items = [];
67
- const seen = new Set();
68
- let next = head.first;
69
- while (next && !seen.has(next) && items.length < max) {
70
- seen.add(next);
71
- const page = await pod.getJson(next).catch(() => null);
72
- if (!page) break;
73
- items.push(...(page.orderedItems || []));
74
- next = page.next;
75
- }
76
- return items;
77
- }
@@ -1,140 +0,0 @@
1
- // containers.mjs — making a container exist, and making its access right.
2
- //
3
- // A container on a Solid pod is not created; it is implied by something being
4
- // in it. So every provisioning here writes one small canary document and then
5
- // states the access rule the container is supposed to have. The two are one
6
- // operation on purpose: a container that exists without its ACL is either
7
- // world-readable when it should not be, or unreachable when it should not be,
8
- // and both have happened by writing one and not the other.
9
- //
10
- // The probes at the bottom take a `probe` function rather than a transport,
11
- // and that is the whole point of them: they ask what a STRANGER sees. Handing
12
- // them a credential would answer a different question, and answer it wrongly.
13
-
14
- const KEEP = { keep: true };
15
- const KEEP_CT = 'application/json';
16
-
17
- /** The canary this module writes, and reads back to ask whether it was here. */
18
- const keepUrl = (base) => `${base}.keep`;
19
-
20
- // ---- the owner's agent, holding the pod's own credential ----
21
-
22
- /**
23
- * Whether this container has already been provisioned.
24
- *
25
- * Only a definite "yes" counts: anything else falls through to the write,
26
- * which is idempotent anyway, so a failed probe costs a request and never
27
- * correctness. Worth asking because the alternative is re-provisioning — and
28
- * rewriting an ACL — every time a service worker restarts, which is whenever
29
- * the browser feels like it.
30
- */
31
- export async function exists(pod, base) {
32
- try {
33
- const r = await pod.fetch(keepUrl(base), { method: 'HEAD' });
34
- return r?.status >= 200 && r.status < 300;
35
- } catch { return false; }
36
- }
37
-
38
- /** Bring a container into being with an owner-only rule. */
39
- export async function provisionOwnerOnly(pod, base) {
40
- await pod.putJson(keepUrl(base), KEEP, KEEP_CT);
41
- await pod.setAcl(base, []);
42
- }
43
-
44
- /** Bring a container into being, world-readable — a published tree. */
45
- export async function provisionPublic(pod, base) {
46
- await pod.putJson(keepUrl(base), KEEP, KEEP_CT);
47
- await pod.setAcl(base, ['Read']);
48
- }
49
-
50
- /**
51
- * The private half: the home itself and the state container.
52
- *
53
- * Idempotent, so two devices doing it at once is harmless — which is what
54
- * makes it safe to run on every boot rather than only at setup. Asked before
55
- * written: a browser's worker boots whenever the browser likes, and each boot
56
- * used to write the canary and both rules again. Returns whether it wrote.
57
- */
58
- export async function provisionPrivate(pod, urls) {
59
- if (await exists(pod, urls.state)) return false;
60
- await pod.putJson(keepUrl(urls.state), KEEP, KEEP_CT);
61
- await pod.setAcl(urls.state, []);
62
- await pod.setAcl(urls.home, []);
63
- return true;
64
- }
65
-
66
- /**
67
- * Check the private trees are actually private, and put back any that are not.
68
- *
69
- * An ACL write that silently failed — or that something else changed
70
- * afterwards — leaves the private trees, signing keys among them, readable by
71
- * anyone. Setup writes them once and never returns, so this is what makes it a
72
- * standing property rather than a one-time hope.
73
- *
74
- * Returns what it found rather than logging: what a finding MEANS, and how
75
- * loudly to say it, belongs to the application.
76
- *
77
- * `isPublic` is supplied rather than assumed: how you decide a tree is
78
- * world-readable — which headers, which timeout — is the caller's business.
79
- *
80
- * @returns {Promise<Array<{url, rewritten, stillPublic, error}>>} one entry per
81
- * tree that was readable without credentials; empty when all is well.
82
- */
83
- export async function repairPrivateAcls(pod, trees, { isPublic } = {}) {
84
- const findings = [];
85
- for (const url of trees) {
86
- if (!await isPublic(url)) continue;
87
- const finding = { url, rewritten: false, stillPublic: false, error: null };
88
- findings.push(finding);
89
- try {
90
- await pod.setAcl(url, []);
91
- finding.rewritten = true;
92
- } catch (e) {
93
- finding.error = e.message;
94
- continue;
95
- }
96
- finding.stillPublic = await isPublic(url);
97
- }
98
- return findings;
99
- }
100
-
101
- // ---- anyone at all: what a stranger can see ----
102
-
103
- /**
104
- * Is this readable with no credentials?
105
- *
106
- * `accept: *\/*` matters. Asking for turtle makes a server answer 501 on the
107
- * JSON documents — webfinger, the actor — which reads as "unreachable" when
108
- * the world can in fact see them perfectly well.
109
- *
110
- * Unreachable is not a finding: a pod that failed to answer has not been shown
111
- * to be public, and treating that as one would rewrite ACLs on a network blip.
112
- */
113
- export async function probePublicReadability(probe, url, { headers = {}, timeoutMs = 20_000 } = {}) {
114
- try {
115
- const res = await probe(url, {
116
- headers: { accept: '*/*', ...headers },
117
- signal: AbortSignal.timeout(timeoutMs),
118
- });
119
- return res.status < 400;
120
- } catch { return false; }
121
- }
122
-
123
- /**
124
- * Does this pod actually ENFORCE an owner-only rule?
125
- *
126
- * A bare, unauthenticated read of the private container's canary must be
127
- * refused. Anything else — including a redirect to a login page, which is why
128
- * redirects are not followed — means documents meant to be private would not
129
- * be, and the caller should decline to put private things here.
130
- *
131
- * Takes a POD-SPACE url: an identity whose ids are advertised elsewhere must
132
- * map back to the pod first, because the rule being tested is the pod's.
133
- *
134
- * @returns {Promise<true|string>} true, or why not
135
- */
136
- export async function probePrivateEnforcement(probe, podKeepUrl) {
137
- const r = await probe(podKeepUrl, { redirect: 'manual' });
138
- if (r.status === 401 || r.status === 403) return true;
139
- return `this pod serves private documents to strangers (HTTP ${r.status})`;
140
- }