fedipod-server 0.28.1 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (438) hide show
  1. package/README.md +38 -34
  2. package/config/server.json +22 -0
  3. package/dist/claims.js +3 -2
  4. package/dist/directory.d.ts +2 -0
  5. package/dist/door.d.ts +4 -0
  6. package/dist/door.js +31 -0
  7. package/dist/fedipod.d.ts +16 -0
  8. package/dist/fedipod.js +13 -0
  9. package/dist/front.d.ts +3 -0
  10. package/dist/front.js +94 -0
  11. package/dist/handler.d.ts +39 -101
  12. package/dist/handler.js +86 -617
  13. package/dist/handler.jsonld +6 -30
  14. package/dist/identities.d.ts +62 -0
  15. package/dist/identities.js +236 -0
  16. package/dist/mounts.d.ts +43 -0
  17. package/dist/mounts.js +123 -0
  18. package/dist/opt-in.d.ts +46 -0
  19. package/dist/opt-in.js +176 -0
  20. package/dist/store-pod.js +37 -0
  21. package/package.json +10 -18
  22. package/lib/client/c2s.mjs +0 -582
  23. package/lib/client/localapi.mjs +0 -87
  24. package/lib/client/masto/accounts.mjs +0 -309
  25. package/lib/client/masto/body.mjs +0 -156
  26. package/lib/client/masto/index.mjs +0 -206
  27. package/lib/client/masto/instance.mjs +0 -112
  28. package/lib/client/masto/media.mjs +0 -79
  29. package/lib/client/masto/oauth.mjs +0 -656
  30. package/lib/client/masto/render.mjs +0 -518
  31. package/lib/client/masto/statuses.mjs +0 -372
  32. package/lib/client/masto/timelines.mjs +0 -438
  33. package/lib/client/oidc-auth.mjs +0 -62
  34. package/lib/client/streaming.mjs +0 -154
  35. package/lib/client/webpush.mjs +0 -98
  36. package/lib/connections/acctfeed.mjs +0 -257
  37. package/lib/connections/atproto.mjs +0 -309
  38. package/lib/connections/bskyfeed.mjs +0 -243
  39. package/lib/connections/bskygroup.mjs +0 -162
  40. package/lib/connections/fediacct.mjs +0 -253
  41. package/lib/connections/import.mjs +0 -357
  42. package/lib/connections/tagfeed.mjs +0 -212
  43. package/lib/connections/vault.mjs +0 -114
  44. package/lib/core/as2.mjs +0 -206
  45. package/lib/core/contexts/activitystreams.json +0 -379
  46. package/lib/core/contexts/anno.json +0 -126
  47. package/lib/core/contexts/did-v1.json +0 -57
  48. package/lib/core/contexts/fep-5711.json +0 -36
  49. package/lib/core/contexts/gotosocial.json +0 -86
  50. package/lib/core/contexts/identity-v1.json +0 -152
  51. package/lib/core/contexts/index.mjs +0 -63
  52. package/lib/core/contexts/join-lemmy.json +0 -33
  53. package/lib/core/contexts/joinmastodon.json +0 -28
  54. package/lib/core/contexts/litepub-0.1.json +0 -60
  55. package/lib/core/contexts/map.json +0 -19
  56. package/lib/core/contexts/miscellany.json +0 -19
  57. package/lib/core/contexts/quotes.json +0 -18
  58. package/lib/core/contexts/schemaorg.json +0 -8845
  59. package/lib/core/contexts/security-data-integrity-v1.json +0 -78
  60. package/lib/core/contexts/security-data-integrity-v2.json +0 -81
  61. package/lib/core/contexts/security-multikey-v1.json +0 -35
  62. package/lib/core/contexts/security-v1.json +0 -74
  63. package/lib/core/contexts/webfinger.json +0 -10
  64. package/lib/core/deliver.mjs +0 -371
  65. package/lib/core/graphview.mjs +0 -327
  66. package/lib/core/intake/activities.mjs +0 -633
  67. package/lib/core/intake/activity.mjs +0 -318
  68. package/lib/core/intake/channel.mjs +0 -144
  69. package/lib/core/intake/group.mjs +0 -256
  70. package/lib/core/intake/index.mjs +0 -701
  71. package/lib/core/intake/notes.mjs +0 -360
  72. package/lib/core/intake/verify.mjs +0 -142
  73. package/lib/core/keys.mjs +0 -153
  74. package/lib/core/lease.mjs +0 -206
  75. package/lib/core/polls.mjs +0 -105
  76. package/lib/core/profile-page.mjs +0 -104
  77. package/lib/core/proof.mjs +0 -79
  78. package/lib/core/publisher/collections.mjs +0 -237
  79. package/lib/core/publisher/index.mjs +0 -500
  80. package/lib/core/publisher/notes.mjs +0 -453
  81. package/lib/core/publisher/questions.mjs +0 -239
  82. package/lib/core/publisher/restore.mjs +0 -224
  83. package/lib/core/shapes/activitystreams.ttl +0 -157
  84. package/lib/core/shapes/index.mjs +0 -132
  85. package/lib/core/shapes/shapes-text.mjs +0 -13
  86. package/lib/core/social.mjs +0 -531
  87. package/lib/core/storage.mjs +0 -176
  88. package/lib/core/store.mjs +0 -568
  89. package/lib/core/wire-quotes.mjs +0 -71
  90. package/lib/core/wire.mjs +0 -889
  91. package/lib/device/account.mjs +0 -87
  92. package/lib/device/admin/index.mjs +0 -13
  93. package/lib/device/admin/origins.mjs +0 -35
  94. package/lib/device/admin/routes/connections.mjs +0 -144
  95. package/lib/device/admin/routes/gateway.mjs +0 -199
  96. package/lib/device/admin/routes/lifecycle.mjs +0 -200
  97. package/lib/device/admin/routes/owner.mjs +0 -322
  98. package/lib/device/admin/routes/setup.mjs +0 -412
  99. package/lib/device/admin/routes/social.mjs +0 -188
  100. package/lib/device/admin/server.mjs +0 -95
  101. package/lib/device/admin/static.mjs +0 -247
  102. package/lib/device/admin/surface.mjs +0 -315
  103. package/lib/device/certs.mjs +0 -281
  104. package/lib/device/cli/commands/account.mjs +0 -586
  105. package/lib/device/cli/commands/run.mjs +0 -278
  106. package/lib/device/cli/commands/service.mjs +0 -221
  107. package/lib/device/cli/commands/setup.mjs +0 -447
  108. package/lib/device/cli/commands/state.mjs +0 -561
  109. package/lib/device/cli/context.mjs +0 -288
  110. package/lib/device/export-collections.mjs +0 -161
  111. package/lib/device/gateway-move.mjs +0 -58
  112. package/lib/device/home.mjs +0 -161
  113. package/lib/device/migrate.mjs +0 -115
  114. package/lib/device/ports.mjs +0 -27
  115. package/lib/device/remote.mjs +0 -54
  116. package/lib/device/setup.mjs +0 -459
  117. package/lib/device/update.mjs +0 -96
  118. package/lib/gateway/caches.mjs +0 -36
  119. package/lib/gateway/directory.mjs +0 -90
  120. package/lib/gateway/front-core.mjs +0 -970
  121. package/lib/gateway/gateway-core.mjs +0 -165
  122. package/lib/gateway/headers.mjs +0 -49
  123. package/lib/gateway/httpsig.mjs +0 -159
  124. package/lib/gateway/notices.mjs +0 -84
  125. package/lib/gateway/quiet.mjs +0 -189
  126. package/lib/pod/README.md +0 -61
  127. package/lib/pod/actor.mjs +0 -96
  128. package/lib/pod/collection.mjs +0 -77
  129. package/lib/pod/containers.mjs +0 -140
  130. package/lib/pod/discovery.mjs +0 -46
  131. package/lib/pod/featured.mjs +0 -18
  132. package/lib/pod/followers.mjs +0 -24
  133. package/lib/pod/following.mjs +0 -10
  134. package/lib/pod/http.mjs +0 -74
  135. package/lib/pod/inbox.mjs +0 -166
  136. package/lib/pod/links.mjs +0 -35
  137. package/lib/pod/media.mjs +0 -27
  138. package/lib/pod/notes.mjs +0 -80
  139. package/lib/pod/notifications.mjs +0 -89
  140. package/lib/pod/outbox.mjs +0 -18
  141. package/lib/pod/package.json +0 -19
  142. package/lib/pod/policy.mjs +0 -58
  143. package/lib/pod/private.mjs +0 -19
  144. package/lib/pod/root.mjs +0 -104
  145. package/lib/pod/state.mjs +0 -40
  146. package/lib/pod/transport.mjs +0 -525
  147. package/lib/pod/urls.mjs +0 -85
  148. package/lib/server/embed.mjs +0 -423
  149. package/lib/session/README.md +0 -137
  150. package/lib/session/demo.html +0 -94
  151. package/lib/session/fedi-account.mjs +0 -408
  152. package/lib/session/fedi-login.mjs +0 -123
  153. package/lib/session/oidc-session.mjs +0 -233
  154. package/lib/session/package.json +0 -49
  155. package/lib/shared/guard.mjs +0 -213
  156. package/lib/shared/links.mjs +0 -9
  157. package/lib/shared/safefetch.mjs +0 -235
  158. package/lib/shared/ua.mjs +0 -29
  159. package/phanpy/dist/404.html +0 -40
  160. package/phanpy/dist/_headers +0 -2
  161. package/phanpy/dist/apple-touch-icon.png +0 -0
  162. package/phanpy/dist/assets/ICONS-DIPJeU0C.js +0 -2
  163. package/phanpy/dist/assets/ICONS-DIPJeU0C.js.map +0 -1
  164. package/phanpy/dist/assets/ScheduledAtField-D-Abfo4J.js +0 -183
  165. package/phanpy/dist/assets/ScheduledAtField-D-Abfo4J.js.map +0 -1
  166. package/phanpy/dist/assets/Temml-CCjoacWt.woff2 +0 -0
  167. package/phanpy/dist/assets/__vite-browser-external-C7iut881.js +0 -2
  168. package/phanpy/dist/assets/__vite-browser-external-C7iut881.js.map +0 -1
  169. package/phanpy/dist/assets/boosts-carousel-BiOaNdMT.jpg +0 -0
  170. package/phanpy/dist/assets/catch-up-O5q5C75Z.png +0 -0
  171. package/phanpy/dist/assets/chunk-aKtaBQYM.js +0 -1
  172. package/phanpy/dist/assets/compose-CFEx7wb4.js +0 -2
  173. package/phanpy/dist/assets/compose-CFEx7wb4.js.map +0 -1
  174. package/phanpy/dist/assets/compose-ZJN5xSb4.js +0 -35
  175. package/phanpy/dist/assets/compose-ZJN5xSb4.js.map +0 -1
  176. package/phanpy/dist/assets/debug.module-YFQgwnIr.js +0 -52
  177. package/phanpy/dist/assets/debug.module-YFQgwnIr.js.map +0 -1
  178. package/phanpy/dist/assets/dom-p1YcJ5Rw.js +0 -2
  179. package/phanpy/dist/assets/dom-p1YcJ5Rw.js.map +0 -1
  180. package/phanpy/dist/assets/home-mobile-dark@2x-Dc-J8-SM.png +0 -0
  181. package/phanpy/dist/assets/home-mobile-light@2x-Bd3fod8m.png +0 -0
  182. package/phanpy/dist/assets/home-tablet-dark@2x-BgcNI8le.png +0 -0
  183. package/phanpy/dist/assets/home-tablet-light@2x-BW2RV9T6.png +0 -0
  184. package/phanpy/dist/assets/icons/add-circle-line-WD0mwORD.js +0 -1
  185. package/phanpy/dist/assets/icons/alert-line-CpFUMf9s.js +0 -1
  186. package/phanpy/dist/assets/icons/android-2-line-Bzxnrd3S.js +0 -1
  187. package/phanpy/dist/assets/icons/announcement-line-5nwaJFJm.js +0 -1
  188. package/phanpy/dist/assets/icons/arrow-down-circle-line-a7Id9-Iv.js +0 -1
  189. package/phanpy/dist/assets/icons/arrow-down-line-B8uzXzq6.js +0 -1
  190. package/phanpy/dist/assets/icons/arrow-left-line-CGrGT9C-.js +0 -1
  191. package/phanpy/dist/assets/icons/arrow-right-line-CE-HERfX.js +0 -1
  192. package/phanpy/dist/assets/icons/arrow-to-up-line-CvdQjmcy.js +0 -1
  193. package/phanpy/dist/assets/icons/arrow-up-circle-line-DEparSNF.js +0 -1
  194. package/phanpy/dist/assets/icons/arrow-up-line-C7yBeugc.js +0 -1
  195. package/phanpy/dist/assets/icons/arrows-right-line-BtbXmJGP.js +0 -1
  196. package/phanpy/dist/assets/icons/at-line-CF66Bznk.js +0 -1
  197. package/phanpy/dist/assets/icons/attachment-line-BY8It-l9.js +0 -1
  198. package/phanpy/dist/assets/icons/blockquote-line-BS6pU_hm.js +0 -1
  199. package/phanpy/dist/assets/icons/board-line-C0UWXyA6.js +0 -1
  200. package/phanpy/dist/assets/icons/bookmark-line-1CtX5JLF.js +0 -1
  201. package/phanpy/dist/assets/icons/building-5-line-Dd59MzbO.js +0 -1
  202. package/phanpy/dist/assets/icons/bus-2-line-6_dvA0GG.js +0 -1
  203. package/phanpy/dist/assets/icons/calendar-day-line-Crpl_fmu.js +0 -1
  204. package/phanpy/dist/assets/icons/calendar-month-line-vommts2C.js +0 -1
  205. package/phanpy/dist/assets/icons/calendar-time-add-line-BU_DTlQk.js +0 -1
  206. package/phanpy/dist/assets/icons/camera-line-Cv69MEei.js +0 -1
  207. package/phanpy/dist/assets/icons/celebrate-line-B8DvIoi0.js +0 -1
  208. package/phanpy/dist/assets/icons/chart-bar-line-FK-QOmtF.js +0 -1
  209. package/phanpy/dist/assets/icons/chart-line-line-BGcJAFWw.js +0 -1
  210. package/phanpy/dist/assets/icons/chat-3-line-YFK2-hwz.js +0 -1
  211. package/phanpy/dist/assets/icons/check-circle-line-DsAxFr5-.js +0 -1
  212. package/phanpy/dist/assets/icons/clipboard-line-BBp364gJ.js +0 -1
  213. package/phanpy/dist/assets/icons/close-circle-line-CICdiuGz.js +0 -1
  214. package/phanpy/dist/assets/icons/close-line-DYrFWcjv.js +0 -1
  215. package/phanpy/dist/assets/icons/cloud-line-tiQAbah8.js +0 -1
  216. package/phanpy/dist/assets/icons/code-line-fAl8iiV3.js +0 -1
  217. package/phanpy/dist/assets/icons/comment-2-line-CR3g7WeB.js +0 -1
  218. package/phanpy/dist/assets/icons/copy-2-line-CLW69yF5.js +0 -1
  219. package/phanpy/dist/assets/icons/delete-2-line-B8xmbr4n.js +0 -1
  220. package/phanpy/dist/assets/icons/document-3-line-DBGNhAg2.js +0 -1
  221. package/phanpy/dist/assets/icons/document-line-C7mb9sb7.js +0 -1
  222. package/phanpy/dist/assets/icons/down-line-BGqSSThd.js +0 -1
  223. package/phanpy/dist/assets/icons/edit-4-line-DcgGaEhx.js +0 -1
  224. package/phanpy/dist/assets/icons/emoji-2-line-D8YgnM-y.js +0 -1
  225. package/phanpy/dist/assets/icons/exit-line-Dc07BS-x.js +0 -1
  226. package/phanpy/dist/assets/icons/external-link-line-BvpSkmjO.js +0 -1
  227. package/phanpy/dist/assets/icons/eye-2-line-CiWXx-f_.js +0 -1
  228. package/phanpy/dist/assets/icons/eye-close-line-BgTaSJ4P.js +0 -1
  229. package/phanpy/dist/assets/icons/filter-2-line-B_mR2usy.js +0 -1
  230. package/phanpy/dist/assets/icons/filter-line-C_nm-3cU.js +0 -1
  231. package/phanpy/dist/assets/icons/flag-1-line-C2HB05I0.js +0 -1
  232. package/phanpy/dist/assets/icons/forbid-circle-line-D3LTT9yd.js +0 -1
  233. package/phanpy/dist/assets/icons/formula-line-DLrt2d6j.js +0 -1
  234. package/phanpy/dist/assets/icons/grid-line-CgUmEikf.js +0 -1
  235. package/phanpy/dist/assets/icons/group-line-C5fnX0wU.js +0 -1
  236. package/phanpy/dist/assets/icons/hand-finger-2-line-BGgxbuL9.js +0 -1
  237. package/phanpy/dist/assets/icons/hashtag-line-CyTEEXBb.js +0 -1
  238. package/phanpy/dist/assets/icons/heart-crack-line-BZddbO2C.js +0 -1
  239. package/phanpy/dist/assets/icons/heart-line-CJ7K47J4.js +0 -1
  240. package/phanpy/dist/assets/icons/history-2-line-kWWvFzqv.js +0 -1
  241. package/phanpy/dist/assets/icons/history-line-bGQiwbTH.js +0 -1
  242. package/phanpy/dist/assets/icons/home-3-line-fRU1zeLG.js +0 -1
  243. package/phanpy/dist/assets/icons/information-line-iNk6-sOY.js +0 -1
  244. package/phanpy/dist/assets/icons/keyboard-line-DsCyuQNz.js +0 -1
  245. package/phanpy/dist/assets/icons/layout-4-line-CZkOX52Z.js +0 -1
  246. package/phanpy/dist/assets/icons/layout-5-line-B_znt5wO.js +0 -1
  247. package/phanpy/dist/assets/icons/left-line-eUw0tya4.js +0 -1
  248. package/phanpy/dist/assets/icons/lightning-line-DaKoDdix.js +0 -1
  249. package/phanpy/dist/assets/icons/link-2-line-DCqLmMA7.js +0 -1
  250. package/phanpy/dist/assets/icons/list-check-line-BAxosBh9.js +0 -1
  251. package/phanpy/dist/assets/icons/lock-line-CPoaXeUW.js +0 -1
  252. package/phanpy/dist/assets/icons/mail-line-B0P8aa1L.js +0 -1
  253. package/phanpy/dist/assets/icons/moon-line-CEqNAToP.js +0 -1
  254. package/phanpy/dist/assets/icons/more-1-fill-Dw2e5xab.js +0 -1
  255. package/phanpy/dist/assets/icons/more-3-line-BIiVjYTz.js +0 -1
  256. package/phanpy/dist/assets/icons/notification-line-BZZq2Gtu.js +0 -1
  257. package/phanpy/dist/assets/icons/pencil-line-CKh8-A1v.js +0 -1
  258. package/phanpy/dist/assets/icons/photo-album-line-Bnfg2u40.js +0 -1
  259. package/phanpy/dist/assets/icons/pin-line-DpTolyhs.js +0 -1
  260. package/phanpy/dist/assets/icons/play-fill-B0-G_8lX.js +0 -1
  261. package/phanpy/dist/assets/icons/qrcode-2-line-B6SPCaHn.js +0 -1
  262. package/phanpy/dist/assets/icons/quill-pen-line-B6LUrnCP.js +0 -1
  263. package/phanpy/dist/assets/icons/quote-left-fill-hQZ1cj_6.js +0 -1
  264. package/phanpy/dist/assets/icons/radar-line-CKyroTth.js +0 -1
  265. package/phanpy/dist/assets/icons/react-line-CoIKhhtK.js +0 -1
  266. package/phanpy/dist/assets/icons/refresh-2-line-D0mTF97S.js +0 -1
  267. package/phanpy/dist/assets/icons/right-line-B3KWJaqc.js +0 -1
  268. package/phanpy/dist/assets/icons/rocket-line-4KZl537D.js +0 -1
  269. package/phanpy/dist/assets/icons/round-fill-C69NTrZ0.js +0 -1
  270. package/phanpy/dist/assets/icons/round-line-B9k_1uo-.js +0 -1
  271. package/phanpy/dist/assets/icons/route-line-B28ReHcE.js +0 -1
  272. package/phanpy/dist/assets/icons/rows-4-line-CrBniagM.js +0 -1
  273. package/phanpy/dist/assets/icons/scan-line-DSf74YdP.js +0 -1
  274. package/phanpy/dist/assets/icons/search-2-line-B6geIU1z.js +0 -1
  275. package/phanpy/dist/assets/icons/settings-3-line-azJwK47w.js +0 -1
  276. package/phanpy/dist/assets/icons/settings-6-line-C_5zc5YR.js +0 -1
  277. package/phanpy/dist/assets/icons/share-2-line-D5MxWjrm.js +0 -1
  278. package/phanpy/dist/assets/icons/share-forward-line-Cp61fZzZ.js +0 -1
  279. package/phanpy/dist/assets/icons/sparkles-2-line-DYYyXTaW.js +0 -1
  280. package/phanpy/dist/assets/icons/sparkles-line-C2hY1s6I.js +0 -1
  281. package/phanpy/dist/assets/icons/time-line-BF3eet1v.js +0 -1
  282. package/phanpy/dist/assets/icons/transfer-4-line-oisfvTBw.js +0 -1
  283. package/phanpy/dist/assets/icons/translate-line-BFWol2Ce.js +0 -1
  284. package/phanpy/dist/assets/icons/unlock-line-X_x5vwv3.js +0 -1
  285. package/phanpy/dist/assets/icons/upload-3-line-DgzwUJeW.js +0 -1
  286. package/phanpy/dist/assets/icons/user-4-line-BVSynZCp.js +0 -1
  287. package/phanpy/dist/assets/icons/user-add-2-line-BlGS5Q8q.js +0 -1
  288. package/phanpy/dist/assets/icons/user-add-line-BbXdHG4z.js +0 -1
  289. package/phanpy/dist/assets/icons/user-edit-line-BFAHOV0X.js +0 -1
  290. package/phanpy/dist/assets/icons/user-follow-line-BXux0wSd.js +0 -1
  291. package/phanpy/dist/assets/icons/user-star-line-DdYlpHLU.js +0 -1
  292. package/phanpy/dist/assets/icons/user-warning-line-Cj43j0_E.js +0 -1
  293. package/phanpy/dist/assets/icons/user-x-line-DIDgNlPH.js +0 -1
  294. package/phanpy/dist/assets/icons/volume-line-Bs390KQt.js +0 -1
  295. package/phanpy/dist/assets/icons/volume-mute-line-DzlzIqFZ.js +0 -1
  296. package/phanpy/dist/assets/icons/walk-line-yNXfB-md.js +0 -1
  297. package/phanpy/dist/assets/icons/world-2-line-CenkGIwj.js +0 -1
  298. package/phanpy/dist/assets/icons/zoom-in-line-CaENUphf.js +0 -1
  299. package/phanpy/dist/assets/icons/zoom-out-line-BDNk0EWo.js +0 -1
  300. package/phanpy/dist/assets/instances-BNPptnn-.json +0 -333
  301. package/phanpy/dist/assets/locales/ar-SA-CfheRbCu.js +0 -1
  302. package/phanpy/dist/assets/locales/ca-ES-DHRW86Tz.js +0 -1
  303. package/phanpy/dist/assets/locales/cs-CZ-CKoqEuA1.js +0 -1
  304. package/phanpy/dist/assets/locales/de-DE-ByRPMRFy.js +0 -1
  305. package/phanpy/dist/assets/locales/eo-UY-CPC7wLCS.js +0 -1
  306. package/phanpy/dist/assets/locales/es-ES-dJa0Fd9t.js +0 -1
  307. package/phanpy/dist/assets/locales/eu-ES-C3djlPR2.js +0 -1
  308. package/phanpy/dist/assets/locales/fa-IR-DTT490Ck.js +0 -1
  309. package/phanpy/dist/assets/locales/fi-FI-B23MCPuz.js +0 -1
  310. package/phanpy/dist/assets/locales/fr-FR-BcaQsO5C.js +0 -1
  311. package/phanpy/dist/assets/locales/gl-ES-BR-CVKBN.js +0 -1
  312. package/phanpy/dist/assets/locales/he-IL-CfheRbCu.js +0 -1
  313. package/phanpy/dist/assets/locales/hu-HU-CfheRbCu.js +0 -1
  314. package/phanpy/dist/assets/locales/it-IT-C1136UFV.js +0 -1
  315. package/phanpy/dist/assets/locales/ja-JP-CRZmRAhD.js +0 -1
  316. package/phanpy/dist/assets/locales/kab-Cz8xH_TT.js +0 -1
  317. package/phanpy/dist/assets/locales/ko-KR-CG9UiY4o.js +0 -1
  318. package/phanpy/dist/assets/locales/lt-LT-CJcI6sHZ.js +0 -1
  319. package/phanpy/dist/assets/locales/nb-NO-CfheRbCu.js +0 -1
  320. package/phanpy/dist/assets/locales/nl-NL-DxN4Yc1t.js +0 -1
  321. package/phanpy/dist/assets/locales/oc-FR-CfheRbCu.js +0 -1
  322. package/phanpy/dist/assets/locales/pl-PL-DkwGXf6P.js +0 -1
  323. package/phanpy/dist/assets/locales/pseudo-LOCALE-CQ4XxkQ0.js +0 -1
  324. package/phanpy/dist/assets/locales/pt-BR-nGL0HdFS.js +0 -1
  325. package/phanpy/dist/assets/locales/pt-PT-Cp17aj2z.js +0 -1
  326. package/phanpy/dist/assets/locales/ru-RU-0Y8cIoaK.js +0 -1
  327. package/phanpy/dist/assets/locales/th-TH-CfheRbCu.js +0 -1
  328. package/phanpy/dist/assets/locales/tok-CfheRbCu.js +0 -1
  329. package/phanpy/dist/assets/locales/tr-TR-CfheRbCu.js +0 -1
  330. package/phanpy/dist/assets/locales/uk-UA-C5E3dFBf.js +0 -1
  331. package/phanpy/dist/assets/locales/zh-CN-BHJi7_et.js +0 -1
  332. package/phanpy/dist/assets/locales/zh-TW-CfheRbCu.js +0 -1
  333. package/phanpy/dist/assets/main-BdqNbG-a.js +0 -26
  334. package/phanpy/dist/assets/main-BdqNbG-a.js.map +0 -1
  335. package/phanpy/dist/assets/mock-home-DGhR__He.js +0 -2
  336. package/phanpy/dist/assets/mock-home-DGhR__He.js.map +0 -1
  337. package/phanpy/dist/assets/multi-column-ROck0NVt.jpg +0 -0
  338. package/phanpy/dist/assets/multi-hashtag-timeline-DH5INVHi.jpg +0 -0
  339. package/phanpy/dist/assets/nested-comments-thread-hv59kZLc.jpg +0 -0
  340. package/phanpy/dist/assets/polyfill-force-H8sUkLGp.js +0 -2
  341. package/phanpy/dist/assets/polyfill-force-H8sUkLGp.js.map +0 -1
  342. package/phanpy/dist/assets/pwa-viewport-ogKbF51K.js +0 -20
  343. package/phanpy/dist/assets/pwa-viewport-ogKbF51K.js.map +0 -1
  344. package/phanpy/dist/assets/style-B1iLPTPR.css +0 -2
  345. package/phanpy/dist/assets/temml-DuyIQMei.js +0 -97
  346. package/phanpy/dist/assets/temml-DuyIQMei.js.map +0 -1
  347. package/phanpy/dist/assets/tinyld.light.node-DtbBhLYC.js +0 -2
  348. package/phanpy/dist/assets/tinyld.light.node-DtbBhLYC.js.map +0 -1
  349. package/phanpy/dist/assets/year-in-posts-B2lywqdx.png +0 -0
  350. package/phanpy/dist/assets/year-in-posts-DJv_star.js +0 -3
  351. package/phanpy/dist/assets/year-in-posts-DJv_star.js.map +0 -1
  352. package/phanpy/dist/compose/index.html +0 -20
  353. package/phanpy/dist/favicon.ico +0 -0
  354. package/phanpy/dist/index.html +0 -73
  355. package/phanpy/dist/logo-192.png +0 -0
  356. package/phanpy/dist/logo-512.png +0 -0
  357. package/phanpy/dist/logo-badge-72.png +0 -0
  358. package/phanpy/dist/logo-maskable-512.png +0 -0
  359. package/phanpy/dist/logo-monochrome-512.png +0 -0
  360. package/phanpy/dist/logo-monochrome-maskable-512.png +0 -0
  361. package/phanpy/dist/manifest.webmanifest +0 -1
  362. package/phanpy/dist/og-image-2.jpg +0 -0
  363. package/phanpy/dist/og-image.png +0 -0
  364. package/phanpy/dist/robots.txt +0 -2
  365. package/phanpy/dist/sw.js +0 -2
  366. package/phanpy/dist/sw.js.map +0 -1
  367. package/phanpy/dist/version.json +0 -1
  368. package/run-agent.mjs +0 -897
  369. package/vendor/gate.cjs +0 -138
  370. package/vendor/idp-grant.cjs +0 -394
  371. package/vendor/jfetch.cjs +0 -16
  372. package/web/admin/actors.js +0 -145
  373. package/web/admin/bar.css +0 -126
  374. package/web/admin/bar.js +0 -99
  375. package/web/admin/client/client.js +0 -66
  376. package/web/admin/client/index.html +0 -69
  377. package/web/admin/client-pick.js +0 -79
  378. package/web/admin/common.js +0 -23
  379. package/web/admin/connections.js +0 -112
  380. package/web/admin/gateway.js +0 -133
  381. package/web/admin/group.js +0 -258
  382. package/web/admin/index.html +0 -603
  383. package/web/admin/notices-bar.js +0 -90
  384. package/web/admin/oauth-signin.mjs +0 -37
  385. package/web/admin/record.js +0 -390
  386. package/web/admin/setup/index.html +0 -241
  387. package/web/admin/setup/setup.js +0 -326
  388. package/web/admin/tokens.css +0 -68
  389. package/web/admin/upkeep.js +0 -177
  390. package/web/admin/window.css +0 -55
  391. package/web/admin/window.js +0 -136
  392. package/web/app/README.md +0 -81
  393. package/web/app/admin-facade.mjs +0 -588
  394. package/web/app/agent.mjs +0 -663
  395. package/web/app/atproto-browser.mjs +0 -86
  396. package/web/app/authorize_interaction.html +0 -56
  397. package/web/app/authorize_interaction.js +0 -69
  398. package/web/app/boot.mjs +0 -576
  399. package/web/app/deliver-relay.mjs +0 -129
  400. package/web/app/dist/boot.js +0 -34218
  401. package/web/app/dist/boot.js.map +0 -7
  402. package/web/app/dist/sw.js +0 -72810
  403. package/web/app/dist/sw.js.map +0 -7
  404. package/web/app/fediacct-browser.mjs +0 -146
  405. package/web/app/gateway-move.mjs +0 -60
  406. package/web/app/idb-kv.mjs +0 -35
  407. package/web/app/index.html +0 -242
  408. package/web/app/keys-browser.mjs +0 -71
  409. package/web/app/keystore.mjs +0 -82
  410. package/web/app/oidc-session.mjs +0 -6
  411. package/web/app/pod-auth.mjs +0 -248
  412. package/web/app/pod-remote.mjs +0 -84
  413. package/web/app/shims/_globals.mjs +0 -2
  414. package/web/app/shims/fedify-sig.mjs +0 -77
  415. package/web/app/shims/node-crypto.mjs +0 -113
  416. package/web/app/shims/node-fs.mjs +0 -17
  417. package/web/app/shims/node-path.mjs +0 -19
  418. package/web/app/shims/node-url.mjs +0 -7
  419. package/web/app/shims/prelude.js +0 -58
  420. package/web/app/shims/safefetch.mjs +0 -58
  421. package/web/app/shims/shapes-text.mjs +0 -8
  422. package/web/app/shims/web-push.mjs +0 -14
  423. package/web/app/signup.mjs +0 -318
  424. package/web/app/sw-src.mjs +0 -220
  425. package/web/app/update.js +0 -70
  426. package/web/front/admin.html +0 -55
  427. package/web/front/admin.js +0 -89
  428. package/web/front/install.sh +0 -69
  429. package/web/front/new-account.html +0 -35
  430. package/web/front/new-account.js +0 -13
  431. package/web/front/notices.html +0 -69
  432. package/web/front/notices.js +0 -107
  433. package/web/front/run.html +0 -58
  434. package/web/front/run.js +0 -195
  435. package/web/front/solid-oidc-client.js +0 -6
  436. package/web/icons/forum.svg +0 -11
  437. package/web/icons/gateway.svg +0 -14
  438. package/web/icons/local.svg +0 -11
@@ -1,206 +0,0 @@
1
- // lease.mjs — single-active-agent coordination. The inbox drain is a
2
- // destructive read and the state store is write-through-cached, so exactly
3
- // one agent may ACT on a pod at a time; later arrivals run as read-only
4
- // viewers. The lease is one JSON doc in ap-state, always read and written
5
- // with FRESH fetches (never through the cached store), renewed while held.
6
- // Best-effort by design: a lost renewal just means the other agent takes
7
- // over at the next expiry.
8
-
9
- import crypto from 'node:crypto';
10
-
11
- // Renewal is the one thing that writes even when nothing is happening: at 30s
12
- // it was 120 PUTs an hour per agent, each taking a write lock on the pod; at
13
- // 90s, 40. Five minutes against a 15-minute TTL leaves two missed renewals of
14
- // headroom and makes it 12. The cost is automatic promotion after a crash
15
- // waiting up to the TTL — a user acting on a second device does not wait,
16
- // since takeover() claims the lease outright.
17
- const TTL_MS = 900_000;
18
- const RENEW_MS = 300_000;
19
- const JITTER = () => 0.85 + Math.random() * 0.3;
20
- // Distinct from null: null is "nobody holds it", this is "we could not ask".
21
- const UNREADABLE = Symbol('lease-unreadable');
22
-
23
- export class Lease {
24
- // `id` names the HOLDER, and acquire() lets a holder reclaim its own lease
25
- // without waiting for the TTL — so whether it is stable decides whether a
26
- // restart is the same agent coming back or a second one arriving. A process
27
- // that is gone for good wants a new one each time; a browser does not, since
28
- // its agent lives in a service worker the browser is free to kill and restart
29
- // whenever it likes. Left to the default, every one of those restarts looked
30
- // like another device and asked the owner to take over their own account —
31
- // which is what switching between the two clients did, because that is a
32
- // navigation and a navigation can take the worker with it. The browser build
33
- // passes one it keeps.
34
- constructor({ url, fetchImpl, log = console.log, id = null }) {
35
- this.url = url;
36
- this.fetchImpl = fetchImpl;
37
- this.log = log;
38
- this.id = id || crypto.randomUUID();
39
- this.stopped = false;
40
- this.timer = null;
41
- this.heldUntil = 0; // wall-clock end of the lease we last wrote
42
- this.denied = null; // why the last acquire() said no: 'unreadable' | 'held'
43
- }
44
-
45
- // null means the lease document is NOT THERE — nobody holds it. UNREADABLE
46
- // means we could not ask, which is a different answer and must not be read as
47
- // "free": acquire() treated both as free, so a pod hiccup during acquire
48
- // could hand a second agent a lease the first still holds, and both would
49
- // drain the same inbox destructively.
50
- async readFresh() {
51
- let res, body;
52
- try {
53
- res = await this.fetchImpl(this.url, { headers: { accept: 'application/json' } });
54
- if (res.status === 404 || res.status === 410) return null;
55
- if (res.status >= 400) return UNREADABLE;
56
- body = await res.text();
57
- } catch { return UNREADABLE; }
58
- this.etag = res.headers?.get?.('etag') || null;
59
- // A 200 carrying garbage — a half-written PUT, a proxy error page served
60
- // as 200 — is not a lease anybody holds, and overwriting it is the repair.
61
- // Calling that UNREADABLE would leave the agent a viewer forever, because
62
- // acquire() refuses to write over what it could not read.
63
- try { return JSON.parse(body); }
64
- catch { this.log('lease document is unparsable — treating it as unheld'); return null; }
65
- }
66
-
67
- // Returns false only for a refused If-Match — that is not an error, it is the
68
- // answer. Anything else throws.
69
- async write(doc, { ifMatch = null } = {}) {
70
- const res = await this.fetchImpl(this.url, {
71
- method: 'PUT',
72
- headers: { 'content-type': 'application/json', ...(ifMatch ? { 'if-match': ifMatch } : {}) },
73
- body: JSON.stringify(doc),
74
- });
75
- if (ifMatch && res.status === 412) return false;
76
- if (res.status >= 400) throw new Error(`lease PUT → ${res.status}`);
77
- this.etag = res.headers?.get?.('etag') || null;
78
- return true;
79
- }
80
-
81
- // True when this agent now holds the lease. Write-then-confirm narrows the
82
- // last-writer-wins race to a small window — acceptable for a human-scale
83
- // "laptop vs phone" situation.
84
- async acquire() {
85
- const cur = await this.readFresh();
86
- // Cannot read it: stay a viewer. A viewer that should have been active is
87
- // an inconvenience; two agents both draining is the destructive one.
88
- if (cur === UNREADABLE) {
89
- this.denied = 'unreadable';
90
- this.log('lease unreadable — staying a viewer'); return false;
91
- }
92
- if (cur && cur.holder !== this.id && Date.now() < cur.expiresAt) { this.denied = 'held'; return false; }
93
- const doc = { holder: this.id, expiresAt: Date.now() + TTL_MS };
94
- if (!await this.write(doc)) { this.denied = 'held'; return false; }
95
- const confirm = await this.readFresh();
96
- if (confirm === UNREADABLE) { this.denied = 'unreadable'; return false; }
97
- if (confirm?.holder !== this.id) { this.denied = 'held'; return false; }
98
- // From the document we WROTE, and only once the confirm agrees. Recomputing
99
- // it here would claim a lease that outlives what the pod's copy grants, by
100
- // however long the PUT and the confirming GET took.
101
- this.heldUntil = doc.expiresAt;
102
- this.denied = null;
103
- return true;
104
- }
105
-
106
- // Renewal checks ownership first: a takeover by another agent must win,
107
- // not be clobbered by a blind rewrite. Losing the lease fires onLost once
108
- // and stops renewing.
109
- async renewOnce() {
110
- // The lease we hold has a wall-clock end. If renewals keep failing — the
111
- // pod unreachable, refusing, or slow — the TTL passes, another agent is
112
- // entitled to take over, and this one went on draining as though nothing
113
- // had happened. Two agents on one destructive inbox read is exactly what
114
- // the lease exists to prevent, so an expiry we could not extend is a loss.
115
- if (this.heldUntil && Date.now() > this.heldUntil) {
116
- this.stopRenewal();
117
- this.etag = null;
118
- this.log('lease expired and could not be renewed — standing down');
119
- this.onLost?.();
120
- return false;
121
- }
122
- const doc = { holder: this.id, expiresAt: Date.now() + TTL_MS };
123
- // The steady path is a single conditional PUT. A 412 means somebody else
124
- // wrote the lease — the only thing the preceding GET was ever there to
125
- // detect — so the read is paid for exactly when it matters instead of every
126
- // 90 seconds forever. No etag (a server that does not return one) falls
127
- // back to read-then-write.
128
- if (this.etag && await this.write(doc, { ifMatch: this.etag })) { this.heldUntil = doc.expiresAt; return true; }
129
- const cur = await this.readFresh();
130
- // Before the ownership test, or the sentinel answers it: a Symbol is truthy
131
- // and its .holder is undefined, so "could not ask" read as "somebody else
132
- // holds it" and stood a perfectly healthy agent down on one 500.
133
- if (cur === UNREADABLE) return true; // keep it; the next tick retries
134
- if (cur && cur.holder !== this.id) {
135
- // clearTimeout alone was a no-op here — the timer being cleared is the one
136
- // whose own callback is running, and the chain re-armed in `finally`
137
- // regardless. Stopping has to outlive this call, so it is a flag.
138
- this.stopRenewal();
139
- // readFresh just cached the NEW holder's ETag. Keeping it would let the
140
- // next conditional PUT succeed and take the lease back, silently, from an
141
- // agent that has already stood down.
142
- this.etag = null;
143
- this.log('lease taken over by another agent');
144
- this.onLost?.();
145
- return false;
146
- }
147
- await this.write(doc);
148
- this.heldUntil = doc.expiresAt;
149
- return true;
150
- }
151
-
152
- // Self-scheduling rather than setInterval, so each agent's renewals drift
153
- // apart instead of several beating in lockstep against one pod.
154
- // Idempotent, like Deliverer.startQueue: startActive reaches this twice on the
155
- // parked path, and two chains against one pod is the doubling this whole file
156
- // is written to avoid.
157
- startRenewal() {
158
- if (this.timer) return;
159
- this.stopped = false; // restartable across demote/takeover
160
- const tick = () => {
161
- if (this.stopped) return;
162
- this.timer = setTimeout(() => {
163
- this.renewOnce()
164
- .catch(e => this.log(`lease renewal: ${e.message}`))
165
- .finally(() => { if (!this.stopped) tick(); });
166
- }, Math.round(RENEW_MS * JITTER()));
167
- this.timer.unref?.();
168
- };
169
- tick();
170
- }
171
-
172
- // Stop renewing WITHOUT giving the lease up — a demoted agent stands down, it
173
- // does not release. Unstopped, the chain outlives demotion for the life of the
174
- // process: ~80 requests an hour from an agent that is no longer acting.
175
- stopRenewal() {
176
- this.stopped = true;
177
- clearTimeout(this.timer);
178
- this.timer = null;
179
- }
180
-
181
- // Forcibly claim the lease (a user acting on a viewer device outranks the
182
- // idle active agent). Write-then-confirm with one retry against the small
183
- // window where the current holder's renewal lands in between.
184
- async takeover() {
185
- for (let attempt = 0; attempt < 2; attempt++) {
186
- const doc = { holder: this.id, expiresAt: Date.now() + TTL_MS };
187
- await this.write(doc);
188
- const confirm = await this.readFresh();
189
- if (confirm !== UNREADABLE && confirm?.holder === this.id) {
190
- this.heldUntil = doc.expiresAt;
191
- return true;
192
- }
193
- }
194
- return false;
195
- }
196
-
197
- // Asked by the destructive path before it acts. The stand-down otherwise
198
- // waits for the next renewal tick, which is up to ~117s away, and the whole
199
- // point of the TTL is that after it another agent may start draining.
200
- stillHeld() { return !this.stopped && (!this.heldUntil || Date.now() < this.heldUntil); }
201
-
202
- async release() {
203
- this.stopRenewal();
204
- await this.write({ holder: this.id, expiresAt: 0 }).catch(() => {});
205
- }
206
- }
@@ -1,105 +0,0 @@
1
- // polls.mjs — the count behind a Question.
2
- //
3
- // A poll travels as a Question whose options live in `oneOf` (pick one) or
4
- // `anyOf` (pick several). A vote is not a special activity: it arrives as an
5
- // ordinary reply carrying the option's `name` and no content. So the fediverse
6
- // carries the votes and the author's server keeps the count — which means when
7
- // the poll is ours, the roster of who chose what is ours to hold.
8
- //
9
- // Everything here is pure. The roster is a plain object the caller stores and
10
- // hands back; the tallies are always DERIVED from it rather than kept beside
11
- // it, so a count cannot drift away from the votes it is meant to summarize.
12
-
13
- import crypto from 'node:crypto';
14
-
15
- /** Where the roster lives in agent state. */
16
- export const VOTES_DOC = 'poll-votes.json';
17
-
18
- // What a poll may be. These are Mastodon's own limits, and they are the ones
19
- // clients read out of the instance document to draw their poll composer, so a
20
- // number we chose differently would be a number the composer then lets someone
21
- // exceed. They live here rather than beside the client API because every way
22
- // in has to honour them: an option is matched BY NAME when the vote comes
23
- // back, so a long title is one something else in the network may truncate and
24
- // hand back unrecognizable.
25
- export const MAX_OPTIONS = 4;
26
- export const MAX_OPTION_CHARS = 50;
27
- export const MIN_SECONDS = 5 * 60;
28
- export const MAX_SECONDS = 2629746; // a month, as Mastodon counts one
29
-
30
- // A voter is recorded as a hash of their actor id rather than the id itself.
31
- // The roster is rewritten on every vote inside a document we serialize whole,
32
- // and a poll that travels would otherwise grow a list of everyone who took
33
- // part. The hash is stable, which is all a duplicate check needs.
34
- export function voterKey(actor) {
35
- return crypto.createHash('sha256').update(String(actor)).digest('hex').slice(0, 16);
36
- }
37
-
38
- /**
39
- * Does this note have the shape of a vote? A name, and no content: that is the
40
- * whole convention. Anything carrying prose is a reply somebody wrote, and
41
- * swallowing it into a tally would lose it.
42
- */
43
- export function isVoteShape(note) {
44
- if (!note?.name || typeof note.name !== 'string') return false;
45
- const text = String(note.content ?? '').replace(/<[^>]*>/gu, '').trim();
46
- return text === '';
47
- }
48
-
49
- /** Which option is this the name of? -1 when it names none of them. */
50
- export function optionIndex(poll, name) {
51
- const opts = poll?.options || [];
52
- return opts.findIndex(o => o.title === String(name));
53
- }
54
-
55
- /** Closed outright, or past its end time. */
56
- export function pollClosed(poll, now = Date.now()) {
57
- if (!poll) return false;
58
- if (poll.closed) return true;
59
- return !!poll.expiresAt && Date.parse(poll.expiresAt) <= now;
60
- }
61
-
62
- /**
63
- * Record one choice. Returns the roster to store and whether anything changed
64
- * — an unchanged roster must not cost a rewrite of the Question, which is the
65
- * one expense a busy poll can run up on the pod.
66
- *
67
- * A single-choice poll takes the first answer and ignores the rest, which is
68
- * how a voter changing their mind is refused rather than counted twice. A
69
- * multiple-choice poll unions the answers, because each choice arrives as its
70
- * own reply.
71
- */
72
- export function addVote(roster, key, index, { multiple = false } = {}) {
73
- const had = roster[key] || [];
74
- if (had.length && !multiple) return { roster, changed: false };
75
- if (had.includes(index)) return { roster, changed: false };
76
- return { roster: { ...roster, [key]: [...had, index] }, changed: true };
77
- }
78
-
79
- /**
80
- * The counts, derived. `votes` is answers given, `voters` is people who gave
81
- * them — the same number until a poll takes several answers each.
82
- */
83
- export function tallyOf(roster, optionCount) {
84
- const counts = new Array(optionCount).fill(0);
85
- let voters = 0;
86
- for (const picks of Object.values(roster || {})) {
87
- let counted = false;
88
- for (const i of picks) {
89
- if (i >= 0 && i < optionCount) { counts[i]++; counted = true; }
90
- }
91
- if (counted) voters++;
92
- }
93
- return { counts, voters, votes: counts.reduce((n, c) => n + c, 0) };
94
- }
95
-
96
- /** The poll record with its counts brought up to date from the roster. */
97
- export function withTally(poll, roster) {
98
- const opts = poll?.options || [];
99
- const { counts, voters } = tallyOf(roster, opts.length);
100
- return {
101
- ...poll,
102
- options: opts.map((o, i) => ({ ...o, votes: counts[i] })),
103
- votersCount: voters,
104
- };
105
- }
@@ -1,104 +0,0 @@
1
- // profile-page.mjs — the human page a browser opens for an actor: who the
2
- // account is, its fields, when it joined, what it pinned, and a remote-follow
3
- // control. The actor document is for servers; this is the address you hand
4
- // to a person. The agent writes it beside the actor (publisher
5
- // publishProfilePage) only when its content changed.
6
-
7
- const HTML_ESCAPES = { '&': '&amp;', '<': '&lt;', '>': '&gt;' };
8
-
9
- // `fields` are the profile's name/value rows, `joined` an ISO date, `pinned`
10
- // the pinned posts as { content (sanitized HTML), published, url }.
11
- export function profilePageHtml({ name, address, summary = null, icon = null, image = null,
12
- fields = [], joined = null, pinned = [], kind = 'person' }) {
13
- const esc = (s) => String(s ?? '').replace(/[&<>"]/g, c => ({ ...HTML_ESCAPES, '"': '&quot;' }[c]));
14
- const what = kind === 'group' ? 'a group' : 'an account';
15
- const month = (iso) => {
16
- const d = new Date(iso);
17
- return Number.isNaN(d.getTime()) ? null
18
- : d.toLocaleDateString('en', { month: 'long', year: 'numeric', timeZone: 'UTC' });
19
- };
20
- const day = (iso) => {
21
- const d = new Date(iso);
22
- return Number.isNaN(d.getTime()) ? '' : d.toISOString().slice(0, 10);
23
- };
24
- const fieldRows = fields.filter(f => f?.name && f?.value !== undefined && f?.value !== null && String(f.value) !== '');
25
- const joinedText = joined ? month(joined) : null;
26
- const pins = pinned.filter(x => x && typeof x.content === 'string');
27
- return `<!doctype html>
28
- <html lang="en">
29
- <head>
30
- <meta charset="utf-8">
31
- <meta name="viewport" content="width=device-width, initial-scale=1">
32
- <title>${esc(name)} (${esc(address)})</title>
33
- <style>
34
- :root { color-scheme: light dark; }
35
- body { font: 112.5%/1.5 system-ui, sans-serif; max-width: 34rem; margin: 3rem auto; padding: 0 1rem; }
36
- img.header { width: 100%; max-height: 12rem; object-fit: cover; border-radius: 1rem; }
37
- img.avatar { width: 6rem; height: 6rem; border-radius: 1rem; object-fit: cover; margin-top: 1rem; }
38
- h1 { margin: .5rem 0 0; }
39
- .address { font-size: 1.1rem; user-select: all; }
40
- dl.fields { display: grid; grid-template-columns: max-content 1fr; gap: .4rem 1rem; margin: 1.5rem 0; }
41
- dl.fields dt { font-weight: 600; }
42
- dl.fields dd { margin: 0; overflow-wrap: anywhere; }
43
- .joined { margin: 1rem 0; }
44
- section.pinned h2 { font-size: 1.1rem; margin: 2rem 0 .5rem; }
45
- article.pin { border: 1px solid #8884; border-radius: 1rem; padding: 1rem 1.2rem; margin: 1rem 0; }
46
- article.pin p:last-child { margin-bottom: 0; }
47
- article.pin .when { font-size: 1rem; }
48
- form { margin-top: 2rem; }
49
- label { display: block; margin-bottom: .3rem; }
50
- input { font: inherit; padding: .5rem; width: 14rem; max-width: 100%; }
51
- button { font: inherit; padding: .5rem 1rem; }
52
- .hint { color: #666; font-size: 1rem; }
53
- .err { color: #b00020; }
54
- .err:empty { display: none; }
55
- @media (prefers-color-scheme: dark) {
56
- .hint { color: #aaa; }
57
- .err { color: #ff8a80; }
58
- }
59
- </style>
60
- </head>
61
- <body>
62
- <main>
63
- ${image ? `<img class="header" src="${esc(image)}" alt="">` : ''}
64
- ${icon ? `<img class="avatar" src="${esc(icon)}" alt="">` : ''}
65
- <h1>${esc(name)}</h1>
66
- <p class="address">${esc(address)}</p>
67
- ${summary ? `<div>${summary}</div>` : ''}
68
- ${fieldRows.length ? `<dl class="fields">
69
- ${fieldRows.map(f => `<dt>${esc(f.name)}</dt><dd>${esc(f.value)}</dd>`).join('\n')}
70
- </dl>` : ''}
71
- ${joinedText ? `<p class="joined">Joined ${esc(joinedText)}</p>` : ''}
72
- ${pins.length ? `<section class="pinned">
73
- <h2>Pinned</h2>
74
- ${pins.map(x => `<article class="pin">
75
- ${x.content}
76
- <p class="when">${x.url ? `<a href="${esc(x.url)}">${esc(day(x.published))}</a>` : esc(day(x.published))}</p>
77
- </article>`).join('\n')}
78
- </section>` : ''}
79
- <p>This is ${what} on the Fediverse. To follow it, paste the address above
80
- into the search box of Mastodon or any Fediverse app — or use the form.</p>
81
- <form id="follow">
82
- <label for="server">your server</label>
83
- <input id="server" type="text" placeholder="mastodon.social" autocomplete="off"
84
- required aria-describedby="follow-err">
85
- <button type="submit">Follow</button>
86
- <p class="err" id="follow-err" role="alert"></p>
87
- </form>
88
- <p class="hint">The form sends you to your own server's follow screen.</p>
89
- </main>
90
- <script>
91
- document.getElementById('follow').addEventListener('submit', (ev) => {
92
- ev.preventDefault();
93
- const s = document.getElementById('server').value.trim().replace(/^https?:\\/\\//, '').replace(/\\/.*$/, '');
94
- const err = document.getElementById('follow-err');
95
- const addr = document.querySelector('.address').textContent.trim();
96
- if (!s) { err.textContent = 'Enter your server, e.g. mastodon.social'; return; }
97
- err.textContent = '';
98
- location.href = 'https://' + s + '/authorize_interaction?uri=' + encodeURIComponent(addr);
99
- });
100
- </script>
101
- </body>
102
- </html>
103
- `;
104
- }
@@ -1,79 +0,0 @@
1
- // proof.mjs — FEP-8b32 object integrity proofs (Data Integrity, eddsa-jcs-2022).
2
- //
3
- // An HTTP signature covers one delivery and says nothing about who wrote what
4
- // was inside it. A proof travels with the activity, so a server that receives
5
- // one of ours second-hand — carried by a group, or forwarded by a follower's
6
- // server — can tell it is ours without asking us.
7
- //
8
- // The RSA `#main-key` and its HTTP signatures are untouched; this is a second
9
- // key alongside, which is what the Data Integrity suites require.
10
-
11
- import crypto from 'node:crypto';
12
- import serialize from 'json-canon';
13
-
14
- // multicodec: an Ed25519 public key is its 32 raw bytes behind 0xed 0x01.
15
- const ED25519_PREFIX = Buffer.from([0xed, 0x01]);
16
- const B58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
17
-
18
- export function base58btc(bytes) {
19
- const b = Buffer.from(bytes);
20
- let n = 0n;
21
- for (const byte of b) n = (n << 8n) + BigInt(byte);
22
- let out = '';
23
- while (n > 0n) { out = B58[Number(n % 58n)] + out; n /= 58n; }
24
- for (const byte of b) { if (byte !== 0) break; out = B58[0] + out; }
25
- return out;
26
- }
27
-
28
- /** The `publicKeyMultibase` form of an Ed25519 public key in PEM. */
29
- export function multibaseEd25519(publicPem) {
30
- const raw = Buffer.from(crypto.createPublicKey(publicPem).export({ format: 'jwk' }).x, 'base64url');
31
- return 'z' + base58btc(Buffer.concat([ED25519_PREFIX, raw]));
32
- }
33
-
34
- /** The signing key, as WebCrypto wants it. */
35
- export function edPrivateKey(privatePem) {
36
- const jwk = crypto.createPrivateKey(privatePem).export({ format: 'jwk' });
37
- return crypto.subtle.importKey('jwk', { ...jwk, key_ops: ['sign'] }, { name: 'Ed25519' }, true, ['sign']);
38
- }
39
-
40
- const sha256 = (s) => crypto.createHash('sha256').update(s).digest();
41
-
42
- // The proof's own terms have to be declared in the document that carries it,
43
- // or a receiver that processes JSON-LD expands the proof away before it ever
44
- // looks at it. Declared BEFORE signing, because the proof covers the document
45
- // exactly as sent, `@context` included.
46
- export const DI_CTX = 'https://w3id.org/security/data-integrity/v1';
47
-
48
- function withProofContext(doc) {
49
- const ctx = doc['@context'];
50
- const list = Array.isArray(ctx) ? ctx : ctx ? [ctx] : [];
51
- if (list.includes(DI_CTX)) return doc;
52
- return { ...doc, '@context': [...list, DI_CTX] };
53
- }
54
-
55
- /**
56
- * Return the activity with a `proof` attached. The proof covers the activity
57
- * exactly as it goes on the wire, minus the proof itself — so it must be the
58
- * last thing added, and nothing may edit the activity afterwards.
59
- *
60
- * Whole seconds on `created`: a verifier re-serializes the timestamp it was
61
- * given, and a fractional one has more than one spelling.
62
- */
63
- export async function attachProof(activity, { privateKey, verificationMethod, created = new Date() } = {}) {
64
- if (!privateKey || !verificationMethod || !activity || typeof activity !== 'object') return activity;
65
- const { proof: _existing, ...bare } = activity;
66
- const doc = withProofContext(bare);
67
- const config = {
68
- '@context': doc['@context'],
69
- type: 'DataIntegrityProof',
70
- cryptosuite: 'eddsa-jcs-2022',
71
- verificationMethod,
72
- proofPurpose: 'assertionMethod',
73
- created: new Date(created).toISOString().replace(/\.\d+Z$/, 'Z'),
74
- };
75
- const digest = Buffer.concat([sha256(serialize(config)), sha256(serialize(doc))]);
76
- const sig = await crypto.subtle.sign('Ed25519', privateKey, digest);
77
- const { '@context': _ctx, ...emitted } = config;
78
- return { ...doc, proof: { ...emitted, proofValue: 'z' + base58btc(Buffer.from(sig)) } };
79
- }