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
package/vendor/gate.cjs DELETED
@@ -1,138 +0,0 @@
1
- // Shared request gate for dk's bundled local servers (pivot CSS + CORS proxy).
2
- // Plain Node — no electron imports — the spawned server processes require it.
3
- //
4
- // The servers are no-auth and loopback-bound, so the remaining attacker is a
5
- // web page in some browser on this machine. The gate shuts that out with a
6
- // per-install secret (DK_GATE_TOKEN, generated by servers.cjs). A request is
7
- // authorized when it carries the secret:
8
- // 1. header `x-dk-token` — the Electron shell injects this on app traffic, or
9
- // 2. cookie `dk-token` — a browser "blessed" via the token URL, or
10
- // 3. query `?dk-token=<secret>` — answered with a SameSite=Lax cookie and
11
- // a redirect to the same URL minus the param (the blessing flow). Lax,
12
- // not Strict: a link from another site — the pod server's own opt-in
13
- // page among them — must arrive with the cookie it was just handed, and
14
- // Lax still withholds it from cross-site POSTs and subresources; or
15
- // 4. (opt-in, proxy only) an Origin/Referer on the allowOrigins list — app
16
- // pages in a blessed browser call the proxy with plain fetch(), which
17
- // doesn't attach cookies cross-port; a hostile page can't forge Origin.
18
- // Anything else gets a bare 401 with no CORS headers. No token → gate off
19
- // (standalone dev runs of the servers stay open, as before the gate existed).
20
-
21
- 'use strict';
22
-
23
- const crypto = require('node:crypto');
24
-
25
- const HEADER = 'x-dk-token';
26
- const COOKIE = 'dk-token';
27
-
28
- // A short-lived "bless" nonce so the DURABLE token never has to appear in a URL
29
- // (which would leak it into the external browser's history / address bar). Main
30
- // hands the browser `?dk-bless=<ts>.<hmac>`; the gate recomputes the HMAC from the
31
- // token it already holds and checks the timestamp window — no shared state, and
32
- // the nonce reveals nothing about the token and expires.
33
- const BLESS_TTL_MS = 120_000;
34
- function blessNonce(token, now = Date.now()) {
35
- const ts = String(now);
36
- const mac = crypto.createHmac('sha256', token).update(ts).digest('base64url');
37
- return `${ts}.${mac}`;
38
- }
39
- function validBless(token, nonce, now = Date.now()) {
40
- if (!token || !nonce) return false;
41
- const i = String(nonce).indexOf('.');
42
- if (i < 0) return false;
43
- const ts = nonce.slice(0, i);
44
- const t = Number(ts);
45
- if (!Number.isFinite(t) || Math.abs(now - t) > BLESS_TTL_MS) return false;
46
- const expect = crypto.createHmac('sha256', token).update(ts).digest('base64url');
47
- try { return crypto.timingSafeEqual(Buffer.from(nonce.slice(i + 1)), Buffer.from(expect)); }
48
- catch { return false; } // length mismatch etc.
49
- }
50
-
51
- // Endpoints that are PUBLIC by Solid/OIDC design and must pass the gate
52
- // un-authenticated, otherwise third-party login is impossible:
53
- // - a WebID profile document is publicly dereferenceable (it's the user's
54
- // public identity; CSS's own server-side ownership check fetches it too),
55
- // - the OIDC discovery + provider endpoints are how any external Solid app
56
- // authenticates against this server.
57
- // Everything else (the rest of the pod) stays gated.
58
- function isPublicEndpoint(req) {
59
- let p;
60
- try { p = new URL(req.url, 'http://localhost').pathname; } catch { return false; }
61
- if (p === '/.well-known/openid-configuration') return true;
62
- if (p.startsWith('/.oidc/')) return true;
63
- if (req.method === 'GET' && /\/profile\/card$/.test(p)) return true; // any pod's public WebID doc
64
- return false;
65
- }
66
-
67
- // Fixed-width digest compare: neither the token's value nor its length leaks
68
- // through timing. (Ported from data-kitchen's copy of this file.)
69
- function tokenOk(given, token) {
70
- if (typeof given !== 'string' || given === '') return false;
71
- const a = crypto.createHash('sha256').update(given).digest();
72
- const b = crypto.createHash('sha256').update(String(token)).digest();
73
- return crypto.timingSafeEqual(a, b);
74
- }
75
-
76
- function cookieValue(header, name) {
77
- for (const part of String(header || '').split(';')) {
78
- const i = part.indexOf('=');
79
- if (i > 0 && part.slice(0, i).trim() === name) return part.slice(i + 1).trim();
80
- }
81
- return undefined;
82
- }
83
-
84
- // `publicEndpoints` opts back into the exemptions above. They are CSS's rules,
85
- // vendored with this file: this agent serves no WebID document and no OIDC
86
- // provider, so on it they exempt three paths that only ever 404 — and would
87
- // silently un-gate any future route underneath them. An agent exposed by
88
- // AP_ALLOWED_HOSTS has nothing but this token, so the gate has to be total.
89
- // `token` may be a function, resolved per request: an identity's secret can
90
- // rotate while the server runs, and the very next request sees the new one.
91
- function makeGate(token, { allowOrigins = [], publicEndpoints = false, secureCookie = false, cookiePath = '/' } = {}) {
92
- const tokenNow = () => (typeof token === 'function' ? token() : token);
93
- // gate(req, res) → true when the gate handled the response (caller stops).
94
- function gate(req, res) {
95
- const t = tokenNow();
96
- if (!t) return false;
97
- if (tokenOk(req.headers[HEADER], t)) return false;
98
- if (tokenOk(cookieValue(req.headers.cookie, COOKIE), t)) return false;
99
-
100
- const url = new URL(req.url, 'http://localhost');
101
- // Bless a browser via ?dk-token=<secret> (legacy) or the leak-free
102
- // ?dk-bless=<nonce> — either way, answer with the cookie and strip the param.
103
- const blessParam = url.searchParams.get('dk-bless');
104
- if (tokenOk(url.searchParams.get(COOKIE), t) || validBless(t, blessParam)) {
105
- url.searchParams.delete(COOKIE);
106
- url.searchParams.delete('dk-bless');
107
- res.writeHead(302, {
108
- // Path scopes the cookie to this identity's own door: on a shared
109
- // origin (suffix pods) two co-tenants must not clobber each other's,
110
- // and a whole-origin cookie would. Defaults to '/'.
111
- 'set-cookie': `${COOKIE}=${t}; Path=${cookiePath}; HttpOnly; SameSite=Lax; Max-Age=31536000`
112
- + (secureCookie ? '; Secure' : ''),
113
- 'location': url.pathname + url.search,
114
- });
115
- res.end();
116
- return true;
117
- }
118
-
119
- const from = req.headers.origin || req.headers.referer || '';
120
- if (allowOrigins.some((o) => from === o || from.startsWith(o + '/'))) return false;
121
-
122
- if (publicEndpoints && isPublicEndpoint(req)) return false; // public WebID / OIDC endpoints
123
-
124
- res.writeHead(401, { 'content-type': 'text/plain' });
125
- res.end('fedipod: missing or bad token\n');
126
- return true;
127
- }
128
-
129
- // Websocket upgrades have no res to answer with — just allowed or not.
130
- gate.upgradeOk = (req) => {
131
- const t = tokenNow();
132
- return !t || tokenOk(req.headers[HEADER], t) || tokenOk(cookieValue(req.headers.cookie, COOKIE), t);
133
- };
134
-
135
- return gate;
136
- }
137
-
138
- module.exports = { makeGate, blessNonce, validBless };
@@ -1,394 +0,0 @@
1
- // idp-grant.cjs — mint a CSS client-credential and run the headless DPoP
2
- // `client_credentials` grant that turns it into authenticated fetches.
3
- //
4
- // This is the durable, no-popup, no-Authorize path of "remember this IdP":
5
- // - mintCredential(): drives the CSS account API (login → controls →
6
- // create-client-credential) to obtain a long-lived, revocable {id, secret}.
7
- // The account password is used transiently here and never persisted (the
8
- // vault stores only the resulting credential — see idp-vault.cjs).
9
- // - createGrantSession(): given a stored credential, manages a DPoP key pair +
10
- // access-token lifecycle and exposes .fetch(url, init) — a Solid-OIDC
11
- // DPoP-bound fetch identical in effect to an interactive session's fetch,
12
- // but obtained with zero user interaction.
13
- //
14
- // All of this runs in the Electron MAIN process. The access token and DPoP
15
- // private key never leave it; the renderer only ever gets a proxied fetch.
16
- //
17
- // The DPoP plumbing (proof JWTs, the use_dpop_nonce retry dance) is hand-built
18
- // because @inrupt/solid-client-authn-node is not installed — jose (already in
19
- // the tree) provides the signing primitives.
20
-
21
- 'use strict';
22
-
23
- const crypto = require('node:crypto');
24
- const fs = require('node:fs');
25
- const { generateKeyPair, exportJWK, SignJWT } = require('jose');
26
- const { jfetch } = require('./jfetch.cjs');
27
-
28
- // The HTTP URI for a DPoP proof's `htu` is the request URL without query/fragment.
29
- function htuOf(url) { const u = new URL(url); return u.origin + u.pathname; }
30
-
31
- // Build a DPoP proof JWT bound to (htm, htu), optionally carrying a server nonce
32
- // and the access-token hash (`ath`, required on resource requests).
33
- async function dpopProof({ keyPair, htm, htu, nonce, accessToken }) {
34
- const jwk = await exportJWK(keyPair.publicKey);
35
- const payload = { htu, htm, jti: crypto.randomUUID() };
36
- if (nonce) payload.nonce = nonce;
37
- if (accessToken) payload.ath = crypto.createHash('sha256').update(accessToken).digest('base64url');
38
- return new SignJWT(payload)
39
- .setProtectedHeader({ alg: 'ES256', typ: 'dpop+jwt', jwk })
40
- .setIssuedAt()
41
- .sign(keyPair.privateKey);
42
- }
43
-
44
- /**
45
- * Mint a durable client-credential for an issuer via its CSS account API.
46
- * The password is used only here and discarded by the caller.
47
- * @param {object} o
48
- * @param {string} o.origin issuer origin, e.g. "https://solidcommunity.net"
49
- * @param {string} o.email account email
50
- * @param {string} o.password account password (transient — never stored)
51
- * @param {string} [o.webId] WebID to bind; discovered from the account if absent
52
- * @param {string} [o.podUrl] the pod this credential is for — picks the right
53
- * WebID when the account owns more than one
54
- * @param {string} [o.gateToken] x-dk-token, ONLY for the local gated origin
55
- * @param {string} [o.name] human label for the credential
56
- * @returns {{clientId, secret, webId, tokenEndpoint, issuerOrigin}}
57
- */
58
- async function mintCredential({ origin, email, password, webId, podUrl, gateToken, name = 'data-kitchen' }) {
59
- const accountRoot = `${origin}/.account/`;
60
-
61
- const pre = (await jfetch(accountRoot, { gateToken })).json;
62
- const loginUrl = pre?.controls?.password?.login || `${accountRoot}login/password/`;
63
- const login = await jfetch(loginUrl, { method: 'POST', gateToken, body: { email, password } });
64
- if (login.status >= 400 || !login.json?.authorization) {
65
- throw new Error(`account login failed (HTTP ${login.status})`);
66
- }
67
- const cookie = `css-account=${login.json.authorization}`;
68
-
69
- const controls = (await jfetch(accountRoot, { gateToken, cookie })).json?.controls;
70
- const ccUrl = controls?.account?.clientCredentials;
71
- if (!ccUrl) throw new Error('clientCredentials control missing — issuer is not a CSS account API');
72
-
73
- let wid = webId;
74
- if (!wid) {
75
- const linkCtl = controls?.account?.webId;
76
- const links = linkCtl ? (await jfetch(linkCtl, { gateToken, cookie })).json?.webIdLinks : null;
77
- const all = links ? Object.keys(links) : [];
78
- // An account may own several pods, each with its own WebID. Taking the
79
- // first is right only by luck: bound to another pod's WebID the credential
80
- // authenticates fine and then 403s every write, which reads as a broken
81
- // server rather than a mis-bound token.
82
- let origins = [];
83
- try { origins = podUrl ? all.filter(w => new URL(w).origin === new URL(podUrl).origin) : []; } catch {}
84
- wid = origins[0] || all[0];
85
- if (!wid) throw new Error('no WebID is linked to this account');
86
- if (podUrl && !origins.length && all.length > 1) {
87
- throw new Error(`this account has ${all.length} WebIDs and none of them is on ${new URL(podUrl).origin} `
88
- + '— the pod and the credential would not match');
89
- }
90
- }
91
-
92
- const made = await jfetch(ccUrl, { method: 'POST', gateToken, cookie, body: { name, webId: wid } });
93
- if (made.status >= 400 || !made.json?.secret) {
94
- throw new Error(`mint failed (HTTP ${made.status}): ${made.json?.message || ''}`);
95
- }
96
- const tokenEndpoint = await discoverTokenEndpoint(origin, gateToken);
97
- return { clientId: made.json.id, secret: made.json.secret, webId: wid, tokenEndpoint, resource: made.json.resource, issuerOrigin: origin };
98
- }
99
-
100
- /** Discover an issuer's OIDC token endpoint. gateToken only for the local origin. */
101
- async function discoverTokenEndpoint(origin, gateToken) {
102
- const headers = { accept: 'application/json' };
103
- if (gateToken) headers['x-dk-token'] = gateToken;
104
- const res = await fetch(`${origin}/.well-known/openid-configuration`, { headers });
105
- const cfg = await res.json().catch(() => ({}));
106
- if (!cfg.token_endpoint) throw new Error('no token_endpoint in OIDC configuration');
107
- return cfg.token_endpoint;
108
- }
109
-
110
- /**
111
- * Revoke a credential server-side (best-effort), so forgetting truly unlinks it.
112
- * Needs the account password to re-authenticate, so this is only possible where we
113
- * still hold it (the local pod). The credential's `resource` URL is returned by
114
- * mintCredential and kept in the vault for exactly this.
115
- */
116
- async function revokeCredentialViaAccount({ origin, email, password, gateToken, resource }) {
117
- if (!resource) return false;
118
- const accountRoot = `${origin}/.account/`;
119
- const pre = (await jfetch(accountRoot, { gateToken })).json;
120
- const loginUrl = pre?.controls?.password?.login || `${accountRoot}login/password/`;
121
- const login = await jfetch(loginUrl, { method: 'POST', gateToken, body: { email, password } });
122
- if (!login.json?.authorization) return false;
123
- const cookie = `css-account=${login.json.authorization}`;
124
- const res = await fetch(resource, { method: 'DELETE', headers: { cookie, ...(gateToken ? { 'x-dk-token': gateToken } : {}) } });
125
- return res.ok;
126
- }
127
-
128
- /**
129
- * Build a headless authenticated session from a stored credential. Manages one
130
- * DPoP key pair and a cached access token (re-granted on expiry/401), and
131
- * handles the use_dpop_nonce challenge on both the token and resource servers.
132
- * @param {{clientId, secret, webId, tokenEndpoint, issuerOrigin}} rec
133
- * @param {object} [o]
134
- * @param {string} [o.gateToken] x-dk-token value
135
- * @param {string} [o.gatedOrigin] the origin x-dk-token applies to (local pod only)
136
- * @returns {{webId, issuer, fetch}}
137
- */
138
- // Every token request costs the issuer an OIDC replay-detection write, and that
139
- // write takes a lock — so retrying one without backoff compounds the contention
140
- // that slowed it down. solidcommunity.net's operators measured ~30 of our token
141
- // requests in 5 seconds during their 2026-07-29 outage: one forced grant per 401
142
- // across a sweep, with nothing coalescing them. Hence one grant in flight at a
143
- // time, a breaker shut for a jittered exponential interval after each failure
144
- // (or for Retry-After when offered), and a floor on how often a 401 may force a
145
- // fresh grant.
146
- const TOKEN_BACKOFF_MIN_MS = 1_000;
147
- const TOKEN_BACKOFF_MAX_MS = 60_000;
148
- const FORCE_COOLDOWN_MS = 10_000;
149
-
150
- // Identify ourselves. An operator reading their access log should be able to
151
- // tell who we are and where to complain: solidcommunity.net's incident report
152
- // had to infer us from client-credential names because every request said only
153
- // "node", which is undici's default.
154
- const { version: AGENT_VERSION } = require('../package.json');
155
- // This is the one on the token grants, so it is the one scn actually logged.
156
- // Renamed 2026-07-31 in lockstep with lib/ua.mjs — changing only that leaves
157
- // the grants still announcing the old name.
158
- const USER_AGENT = `fedipod/${AGENT_VERSION} (+https://github.com/jeff-zucker/FediPod)`;
159
-
160
- // A ceiling no timer, retry or future bug can exceed. Steady state is ~3
161
- // requests/minute, so the default sits 20x above normal use and only engages
162
- // when something has gone wrong.
163
- const DEFAULT_MAX_PER_MIN = 60;
164
-
165
- // A crash loop would otherwise reset the breaker on every boot and force a
166
- // grant immediately — systemd restarts us on failure, so backoff has to outlive
167
- // the process. One small file, best-effort: an unreadable or unwritable one just
168
- // means we behave as before.
169
- // --- token reuse across restarts -------------------------------------------
170
- function readSavedToken(file) {
171
- if (!file) return null;
172
- try {
173
- const d = JSON.parse(fs.readFileSync(file, 'utf8'));
174
- if (!d.accessToken || !d.privateJwk) return null;
175
- if (!(Number(d.expiresAt) > Date.now() + 60_000)) return null; // too near expiry to bother
176
- return d;
177
- } catch { return null; }
178
- }
179
-
180
- async function importSavedKey(saved) {
181
- const { importJWK } = require('jose');
182
- const privateKey = await importJWK(saved.privateJwk, 'ES256');
183
- const publicKey = await importJWK(saved.publicJwk, 'ES256');
184
- return { privateKey, publicKey };
185
- }
186
-
187
- async function saveToken(file, keyPair, accessToken, expiresAt) {
188
- if (!file) return;
189
- try {
190
- const { exportJWK } = require('jose');
191
- fs.writeFileSync(file, JSON.stringify({
192
- accessToken, expiresAt,
193
- privateJwk: await exportJWK(keyPair.privateKey),
194
- publicJwk: await exportJWK(keyPair.publicKey),
195
- }) + '\n', { mode: 0o600 });
196
- } catch { /* best-effort: a fresh grant next start is the only cost */ }
197
- }
198
-
199
- function readBackoff(file) {
200
- if (!file) return { breakerUntil: 0, failures: 0 };
201
- try {
202
- const d = JSON.parse(fs.readFileSync(file, 'utf8'));
203
- return { breakerUntil: Number(d.breakerUntil) || 0, failures: Number(d.failures) || 0 };
204
- } catch { return { breakerUntil: 0, failures: 0 }; }
205
- }
206
-
207
- function writeBackoff(file, breakerUntil, failures) {
208
- if (!file) return;
209
- try { fs.writeFileSync(file, JSON.stringify({ breakerUntil, failures }) + '\n', { mode: 0o600 }); }
210
- catch { /* best-effort */ }
211
- }
212
-
213
- function createGrantSession(rec, { gateToken, gatedOrigin, backoffFile = null, tokenFile = null } = {}) {
214
- const { clientId, secret, webId, tokenEndpoint, issuerOrigin } = rec;
215
- // A DPoP token is bound to the key that requested it, so reusing one across a
216
- // restart means reusing the key too. Both go in one 0600 file beside the
217
- // credential that could mint them anyway. Any problem reading it falls back
218
- // to a fresh key and a fresh grant.
219
- const saved = readSavedToken(tokenFile);
220
- const keyPairP = saved
221
- // `extractable: true` because the DPoP key is exported two ways: as the
222
- // public JWK that rides in every proof header, and to token.json so a
223
- // restart resumes the same session instead of minting a fresh grant. jose 6
224
- // makes generated keys non-extractable by default, which turns both of
225
- // those into "non-extractable CryptoKey cannot be exported as a JWK".
226
- ? importSavedKey(saved).catch(() => generateKeyPair('ES256', { extractable: true }))
227
- : generateKeyPair('ES256', { extractable: true });
228
- let accessToken = saved?.accessToken || null;
229
- let expiresAt = saved?.expiresAt || 0;
230
- let rsNonce = null;
231
- let inFlight = null; // single-flight: N callers, one grant
232
- const carried = readBackoff(backoffFile);
233
- let failures = carried.failures, breakerUntil = carried.breakerUntil, lastForced = 0;
234
- const maxPerMin = Number(process.env.AP_MAX_REQUESTS_PER_MIN) || DEFAULT_MAX_PER_MIN;
235
- let allowance = maxPerMin, lastRefill = Date.now();
236
- // Token bucket over every request this session makes — resource and token
237
- // endpoint alike, since both cost the issuer a lock.
238
- // Accounting, so "are we the problem?" is a question the agent can answer
239
- // rather than one that takes an evening of reading. Counts every request this
240
- // session makes, by outcome, plus a rolling one-minute rate.
241
- const counts = { requests: 0, tokenGrants: 0, ok: 0, client4xx: 0, server5xx: 0, refused: 0, failed: 0 };
242
- const recent = []; // request timestamps, last 60s
243
- const note = (bucket) => { counts[bucket]++; };
244
- const rate = () => {
245
- const cutoff = Date.now() - 60_000;
246
- while (recent.length && recent[0] < cutoff) recent.shift();
247
- return recent.length;
248
- };
249
- const takeSlot = () => {
250
- const now = Date.now();
251
- allowance = Math.min(maxPerMin, allowance + ((now - lastRefill) * maxPerMin) / 60_000);
252
- lastRefill = now;
253
- if (allowance < 1) return false;
254
- allowance -= 1;
255
- counts.requests++;
256
- recent.push(now);
257
- return true;
258
- };
259
- // The ceiling is OUR politeness, not the server's refusal, so hitting it must
260
- // WAIT rather than fail: a first-run setup legitimately needs ~60 writes, and
261
- // throwing there left an account, a pod and a half-published actor behind.
262
- // Refusing outright is also not the polite option — it just moves the retry
263
- // somewhere with less information. The cap is a backstop so a genuinely
264
- // wedged bucket still surfaces instead of hanging for ever.
265
- const SLOT_WAIT_MAX_MS = Number(process.env.AP_SLOT_WAIT_MAX_MS) || 120_000;
266
- const waitForSlot = async (what) => {
267
- if (takeSlot()) return;
268
- const deadline = Date.now() + SLOT_WAIT_MAX_MS;
269
- const step = Math.max(50, Math.ceil(60_000 / maxPerMin));
270
- for (;;) {
271
- await new Promise(r => setTimeout(r, step));
272
- if (takeSlot()) return;
273
- if (Date.now() >= deadline) {
274
- note('refused');
275
- throw new Error(`local ceiling of ${maxPerMin} requests/min: no slot after `
276
- + `${Math.round(SLOT_WAIT_MAX_MS / 1000)}s — ${what}`);
277
- }
278
- }
279
- };
280
-
281
- const stats = () => ({
282
- ...counts,
283
- perMinuteNow: rate(),
284
- ceilingPerMinute: maxPerMin,
285
- backingOffFor: Math.max(0, Math.round((breakerUntil - Date.now()) / 1000)),
286
- });
287
- const gateFor = (url) => (gatedOrigin && new URL(url).origin === gatedOrigin) ? gateToken : undefined;
288
-
289
- async function requestToken(keyPair, nonce) {
290
- const proof = await dpopProof({ keyPair, htm: 'POST', htu: htuOf(tokenEndpoint), nonce });
291
- const headers = {
292
- authorization: 'Basic ' + Buffer.from(`${encodeURIComponent(clientId)}:${encodeURIComponent(secret)}`).toString('base64'),
293
- 'content-type': 'application/x-www-form-urlencoded',
294
- 'user-agent': USER_AGENT,
295
- dpop: proof,
296
- };
297
- const gate = gateFor(tokenEndpoint); if (gate) headers['x-dk-token'] = gate;
298
- // Deliberately shorter than the 60s nginx in front of a typical CSS: if a
299
- // grant has not landed in 30s the server is in trouble, and holding its
300
- // worker to the wire makes that worse. We back off and try later instead.
301
- // AP_HTTP_TIMEOUT_MS raises it for a knowingly slow issuer.
302
- await waitForSlot('token request');
303
- const res = await fetch(tokenEndpoint, {
304
- method: 'POST', headers, body: 'grant_type=client_credentials&scope=webid',
305
- signal: AbortSignal.timeout(Number(process.env.AP_HTTP_TIMEOUT_MS) || 30_000),
306
- });
307
- if ((res.status === 400 || res.status === 401) && !nonce) {
308
- const n = res.headers.get('dpop-nonce');
309
- if (n) return requestToken(keyPair, n);
310
- }
311
- // Truncated: an ailing issuer answers with a whole HTML page (a
312
- // Cloudflare 504, say) and this message goes into the log.
313
- if (!res.ok) {
314
- const body = (await res.text().catch(() => '')).replace(/\s+/g, ' ').slice(0, 200);
315
- note(res.status >= 500 ? 'server5xx' : 'client4xx');
316
- const err = new Error(`token request failed (HTTP ${res.status}): ${body}`);
317
- const ra = Number(res.headers.get('retry-after'));
318
- if (Number.isFinite(ra) && ra > 0) err.retryAfterMs = Math.min(ra * 1000, TOKEN_BACKOFF_MAX_MS);
319
- throw err;
320
- }
321
- counts.tokenGrants++;
322
- note('ok');
323
- return res.json();
324
- }
325
-
326
- async function ensureToken(force) {
327
- const keyPair = await keyPairP;
328
- if (!force && accessToken && Date.now() < expiresAt - 30_000) return keyPair;
329
- if (inFlight) { await inFlight; return keyPair; } // someone else is already asking
330
- const shut = breakerUntil - Date.now();
331
- if (shut > 0) throw new Error(`token endpoint backing off — ${Math.ceil(shut / 1000)}s left`);
332
- inFlight = (async () => {
333
- try {
334
- const tok = await requestToken(keyPair, null);
335
- accessToken = tok.access_token;
336
- expiresAt = Date.now() + (Number(tok.expires_in) || 300) * 1000;
337
- failures = 0;
338
- writeBackoff(backoffFile, 0, 0);
339
- await saveToken(tokenFile, keyPair, accessToken, expiresAt);
340
- } catch (e) {
341
- failures++;
342
- const capped = Math.min(TOKEN_BACKOFF_MIN_MS * 2 ** (failures - 1), TOKEN_BACKOFF_MAX_MS);
343
- breakerUntil = Date.now() + (e.retryAfterMs ?? Math.round(capped * (0.8 + Math.random() * 0.4)));
344
- writeBackoff(backoffFile, breakerUntil, failures);
345
- throw e;
346
- } finally { inFlight = null; }
347
- })();
348
- await inFlight;
349
- return keyPair;
350
- }
351
-
352
- async function doFetch(url, init = {}, retried = false) {
353
- const keyPair = await ensureToken(false);
354
- const method = (init.method || 'GET').toUpperCase();
355
- const proof = await dpopProof({ keyPair, htm: method, htu: htuOf(url), accessToken, nonce: rsNonce });
356
- const headers = {
357
- 'user-agent': USER_AGENT,
358
- ...(init.headers || {}),
359
- authorization: `DPoP ${accessToken}`, dpop: proof,
360
- };
361
- const gate = gateFor(url); if (gate) headers['x-dk-token'] = gate;
362
- await waitForSlot(`${method} ${url}`);
363
- const res = await fetch(url, {
364
- signal: AbortSignal.timeout(Number(process.env.AP_HTTP_TIMEOUT_MS) || 20_000), ...init, headers,
365
- });
366
- if (res.status >= 500) note('server5xx');
367
- else if (res.status >= 400) note('client4xx');
368
- else note('ok');
369
- if (res.status === 401 && !retried) {
370
- const n = res.headers.get('dpop-nonce');
371
- if (n) { rsNonce = n; return doFetch(url, init, true); } // server wants a nonce
372
- // Or the token expired — but a sweep whose every request 401s must not
373
- // mint a token per request, so force at most one grant per window and
374
- // otherwise retry with what we already hold.
375
- if (Date.now() - lastForced > FORCE_COOLDOWN_MS) {
376
- lastForced = Date.now();
377
- await ensureToken(true);
378
- }
379
- return doFetch(url, init, true);
380
- }
381
- return res;
382
- }
383
-
384
- // Confirms the credential works before the session is used — but a token we
385
- // already hold IS that confirmation, and forcing a grant here was defeating
386
- // the persisted one entirely: every restart paid the issuer for a token it
387
- // already had. Cold start still mints (and so still fails loudly on a revoked
388
- // credential); a warm one costs nothing.
389
- async function warmup() { await ensureToken(false); return webId; }
390
-
391
- return { webId, issuer: issuerOrigin, fetch: doFetch, warmup, stats };
392
- }
393
-
394
- module.exports = { mintCredential, discoverTokenEndpoint, revokeCredentialViaAccount, createGrantSession };
package/vendor/jfetch.cjs DELETED
@@ -1,16 +0,0 @@
1
- 'use strict';
2
-
3
- // JSON fetch helper for the CSS account API, shared by seed-account.cjs (owner
4
- // account provisioning) and idp-grant.cjs (the durable client-credentials grant).
5
- // Returns { status, json } with json null on a non-JSON body.
6
- async function jfetch(url, { method = 'GET', body, gateToken, cookie } = {}) {
7
- const headers = { accept: 'application/json' };
8
- if (body !== undefined) headers['content-type'] = 'application/json';
9
- if (gateToken) headers['x-dk-token'] = gateToken;
10
- if (cookie) headers.cookie = cookie;
11
- const res = await fetch(url, { method, headers, body: body === undefined ? undefined : JSON.stringify(body) });
12
- let json = null; try { json = await res.json(); } catch { /* non-JSON */ }
13
- return { status: res.status, json };
14
- }
15
-
16
- module.exports = { jfetch };