fedipod-server 0.10.0 → 0.12.1

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 (416) hide show
  1. package/README.md +15 -6
  2. package/dist/claims.js +1 -1
  3. package/dist/handler.d.ts +7 -1
  4. package/dist/handler.js +58 -12
  5. package/dist/handler.jsonld +5 -1
  6. package/dist/store-pod.js +18 -4
  7. package/lib/{c2s.mjs → client/c2s.mjs} +8 -3
  8. package/lib/{localapi.mjs → client/localapi.mjs} +2 -2
  9. package/lib/client/masto/accounts.mjs +264 -0
  10. package/lib/client/masto/body.mjs +69 -0
  11. package/lib/client/masto/index.mjs +183 -0
  12. package/lib/client/masto/instance.mjs +104 -0
  13. package/lib/client/masto/media.mjs +133 -0
  14. package/lib/client/masto/oauth.mjs +599 -0
  15. package/lib/client/masto/render.mjs +459 -0
  16. package/lib/client/masto/statuses.mjs +331 -0
  17. package/lib/client/masto/timelines.mjs +316 -0
  18. package/lib/{streaming.mjs → client/streaming.mjs} +1 -1
  19. package/lib/{acctfeed.mjs → connections/acctfeed.mjs} +1 -1
  20. package/lib/{atproto.mjs → connections/atproto.mjs} +15 -16
  21. package/lib/{bskygroup.mjs → connections/bskygroup.mjs} +1 -1
  22. package/lib/{fediacct.mjs → connections/fediacct.mjs} +31 -35
  23. package/lib/{import.mjs → connections/import.mjs} +2 -2
  24. package/lib/{tagfeed.mjs → connections/tagfeed.mjs} +21 -4
  25. package/lib/connections/vault.mjs +114 -0
  26. package/lib/core/as2.mjs +124 -0
  27. package/lib/core/contexts/activitystreams.json +379 -0
  28. package/lib/core/contexts/did-v1.json +57 -0
  29. package/lib/core/contexts/fep-5711.json +36 -0
  30. package/lib/core/contexts/gotosocial.json +86 -0
  31. package/lib/core/contexts/identity-v1.json +152 -0
  32. package/lib/core/contexts/index.mjs +45 -0
  33. package/lib/core/contexts/join-lemmy.json +33 -0
  34. package/lib/core/contexts/joinmastodon.json +28 -0
  35. package/lib/core/contexts/map.json +16 -0
  36. package/lib/core/contexts/miscellany.json +19 -0
  37. package/lib/core/contexts/schemaorg.json +8845 -0
  38. package/lib/core/contexts/security-data-integrity-v1.json +78 -0
  39. package/lib/core/contexts/security-data-integrity-v2.json +81 -0
  40. package/lib/core/contexts/security-multikey-v1.json +35 -0
  41. package/lib/core/contexts/security-v1.json +74 -0
  42. package/lib/core/contexts/webfinger.json +10 -0
  43. package/lib/{deliver.mjs → core/deliver.mjs} +8 -2
  44. package/lib/core/intake/activities.mjs +437 -0
  45. package/lib/core/intake/activity.mjs +240 -0
  46. package/lib/core/intake/channel.mjs +144 -0
  47. package/lib/core/intake/group.mjs +222 -0
  48. package/lib/core/intake/index.mjs +629 -0
  49. package/lib/core/intake/notes.mjs +288 -0
  50. package/lib/core/intake/verify.mjs +141 -0
  51. package/lib/{keys.mjs → core/keys.mjs} +1 -1
  52. package/lib/core/publisher/collections.mjs +229 -0
  53. package/lib/core/publisher/index.mjs +421 -0
  54. package/lib/core/publisher/notes.mjs +188 -0
  55. package/lib/core/publisher/questions.mjs +233 -0
  56. package/lib/core/publisher/restore.mjs +196 -0
  57. package/lib/core/shapes/activitystreams.ttl +129 -0
  58. package/lib/core/shapes/index.mjs +107 -0
  59. package/lib/core/shapes/shapes-text.mjs +13 -0
  60. package/lib/{social.mjs → core/social.mjs} +19 -10
  61. package/lib/{storage.mjs → core/storage.mjs} +2 -2
  62. package/lib/{store.mjs → core/store.mjs} +12 -1
  63. package/lib/{wire.mjs → core/wire.mjs} +48 -67
  64. package/lib/device/admin/index.mjs +13 -0
  65. package/lib/device/admin/origins.mjs +35 -0
  66. package/lib/device/admin/routes/connections.mjs +144 -0
  67. package/lib/device/admin/routes/gateway.mjs +199 -0
  68. package/lib/device/admin/routes/lifecycle.mjs +191 -0
  69. package/lib/device/admin/routes/owner.mjs +322 -0
  70. package/lib/device/admin/routes/setup.mjs +393 -0
  71. package/lib/device/admin/routes/social.mjs +188 -0
  72. package/lib/device/admin/server.mjs +95 -0
  73. package/lib/device/admin/static.mjs +244 -0
  74. package/lib/device/admin/surface.mjs +274 -0
  75. package/lib/device/cli/commands/account.mjs +586 -0
  76. package/lib/device/cli/commands/run.mjs +278 -0
  77. package/lib/device/cli/commands/service.mjs +221 -0
  78. package/lib/device/cli/commands/setup.mjs +410 -0
  79. package/lib/device/cli/commands/state.mjs +559 -0
  80. package/lib/device/cli/context.mjs +288 -0
  81. package/lib/{export-collections.mjs → device/export-collections.mjs} +1 -1
  82. package/lib/{migrate.mjs → device/migrate.mjs} +5 -19
  83. package/lib/device/remote.mjs +54 -0
  84. package/lib/{setup.mjs → device/setup.mjs} +3 -3
  85. package/lib/{update.mjs → device/update.mjs} +1 -1
  86. package/lib/{directory.mjs → gateway/directory.mjs} +1 -1
  87. package/lib/{front-core.mjs → gateway/front-core.mjs} +272 -49
  88. package/lib/{gateway-core.mjs → gateway/gateway-core.mjs} +3 -7
  89. package/lib/{httpsig.mjs → gateway/httpsig.mjs} +34 -3
  90. package/lib/pod/README.md +61 -0
  91. package/lib/pod/actor.mjs +96 -0
  92. package/lib/pod/collection.mjs +77 -0
  93. package/lib/pod/containers.mjs +136 -0
  94. package/lib/pod/discovery.mjs +46 -0
  95. package/lib/pod/featured.mjs +18 -0
  96. package/lib/pod/followers.mjs +24 -0
  97. package/lib/pod/following.mjs +10 -0
  98. package/lib/pod/http.mjs +74 -0
  99. package/lib/pod/inbox.mjs +144 -0
  100. package/lib/{links.mjs → pod/links.mjs} +1 -1
  101. package/lib/pod/media.mjs +27 -0
  102. package/lib/pod/notes.mjs +80 -0
  103. package/lib/pod/notifications.mjs +89 -0
  104. package/lib/pod/outbox.mjs +18 -0
  105. package/lib/pod/package.json +19 -0
  106. package/lib/pod/policy.mjs +58 -0
  107. package/lib/pod/private.mjs +19 -0
  108. package/lib/pod/root.mjs +63 -0
  109. package/lib/pod/state.mjs +39 -0
  110. package/lib/{remote.mjs → pod/transport.mjs} +217 -159
  111. package/lib/pod/urls.mjs +71 -0
  112. package/lib/{embed.mjs → server/embed.mjs} +149 -22
  113. package/lib/shared/links.mjs +9 -0
  114. package/lib/{ua.mjs → shared/ua.mjs} +1 -1
  115. package/package.json +1 -1
  116. package/run-agent.mjs +47 -64
  117. package/web/admin/actors.js +145 -0
  118. package/web/admin/bar.css +9 -13
  119. package/web/admin/bar.js +2 -2
  120. package/web/admin/client/client.js +1 -1
  121. package/web/admin/client/index.html +5 -6
  122. package/web/admin/common.js +23 -0
  123. package/web/admin/connections.js +112 -0
  124. package/web/admin/gateway.js +111 -0
  125. package/web/admin/group.js +258 -0
  126. package/web/admin/index.html +60 -66
  127. package/web/admin/record.js +378 -0
  128. package/web/admin/setup/index.html +3 -1
  129. package/web/admin/setup/setup.js +2 -13
  130. package/web/admin/tokens.css +56 -0
  131. package/web/admin/upkeep.js +170 -0
  132. package/web/app/README.md +77 -0
  133. package/web/app/admin-facade.mjs +549 -0
  134. package/web/app/agent.mjs +490 -0
  135. package/web/app/atproto-browser.mjs +86 -0
  136. package/web/app/boot.mjs +340 -0
  137. package/web/app/deliver-relay.mjs +72 -0
  138. package/web/app/dist/boot.js +34067 -0
  139. package/web/app/dist/boot.js.map +7 -0
  140. package/web/app/dist/sw.js +70014 -0
  141. package/web/app/dist/sw.js.map +7 -0
  142. package/web/app/fediacct-browser.mjs +146 -0
  143. package/web/app/idb-kv.mjs +35 -0
  144. package/web/app/index.html +203 -0
  145. package/web/app/index.html~ +152 -0
  146. package/web/app/keys-browser.mjs +46 -0
  147. package/web/app/keystore.mjs +99 -0
  148. package/web/app/oidc-session.mjs +189 -0
  149. package/web/app/pod-auth.mjs +248 -0
  150. package/web/app/pod-remote.mjs +84 -0
  151. package/web/app/shims/_globals.mjs +2 -0
  152. package/web/app/shims/fedify-sig.mjs +77 -0
  153. package/web/app/shims/node-crypto.mjs +113 -0
  154. package/web/app/shims/node-fs.mjs +17 -0
  155. package/web/app/shims/node-path.mjs +19 -0
  156. package/web/app/shims/node-url.mjs +7 -0
  157. package/web/app/shims/prelude.js +58 -0
  158. package/web/app/shims/safefetch.mjs +58 -0
  159. package/web/app/shims/shapes-text.mjs +8 -0
  160. package/web/app/shims/web-push.mjs +14 -0
  161. package/web/app/signup.mjs +248 -0
  162. package/web/app/site/_headers +9 -0
  163. package/web/app/site/_redirects +21 -0
  164. package/web/app/site/admin/actors.js +145 -0
  165. package/web/app/site/admin/bar.css +60 -0
  166. package/web/app/site/admin/bar.js +61 -0
  167. package/web/app/site/admin/client/client.js +66 -0
  168. package/web/app/site/admin/client/index.html +43 -0
  169. package/web/app/site/admin/common.js +23 -0
  170. package/web/app/site/admin/connections.js +112 -0
  171. package/web/app/site/admin/gateway.js +111 -0
  172. package/web/app/site/admin/group.js +258 -0
  173. package/web/app/site/admin/index.html +562 -0
  174. package/web/app/site/admin/record.js +378 -0
  175. package/web/app/site/admin/setup/index.html +192 -0
  176. package/web/app/site/admin/setup/setup.js +297 -0
  177. package/web/app/site/admin/tokens.css +56 -0
  178. package/web/app/site/admin/upkeep.js +170 -0
  179. package/web/app/site/admin/window.css +55 -0
  180. package/web/app/site/admin/window.js +136 -0
  181. package/web/app/site/app/404.html +40 -0
  182. package/web/app/site/app/_headers +2 -0
  183. package/web/app/site/app/apple-touch-icon.png +0 -0
  184. package/web/app/site/app/assets/ICONS-DIPJeU0C.js +2 -0
  185. package/web/app/site/app/assets/ICONS-DIPJeU0C.js.map +1 -0
  186. package/web/app/site/app/assets/ScheduledAtField-D-Abfo4J.js +183 -0
  187. package/web/app/site/app/assets/ScheduledAtField-D-Abfo4J.js.map +1 -0
  188. package/web/app/site/app/assets/Temml-CCjoacWt.woff2 +0 -0
  189. package/web/app/site/app/assets/__vite-browser-external-C7iut881.js +2 -0
  190. package/web/app/site/app/assets/__vite-browser-external-C7iut881.js.map +1 -0
  191. package/web/app/site/app/assets/boosts-carousel-BiOaNdMT.jpg +0 -0
  192. package/web/app/site/app/assets/catch-up-O5q5C75Z.png +0 -0
  193. package/web/app/site/app/assets/chunk-aKtaBQYM.js +1 -0
  194. package/web/app/site/app/assets/compose-CFEx7wb4.js +2 -0
  195. package/web/app/site/app/assets/compose-CFEx7wb4.js.map +1 -0
  196. package/web/app/site/app/assets/compose-ZJN5xSb4.js +35 -0
  197. package/web/app/site/app/assets/compose-ZJN5xSb4.js.map +1 -0
  198. package/web/app/site/app/assets/debug.module-YFQgwnIr.js +52 -0
  199. package/web/app/site/app/assets/debug.module-YFQgwnIr.js.map +1 -0
  200. package/web/app/site/app/assets/dom-p1YcJ5Rw.js +2 -0
  201. package/web/app/site/app/assets/dom-p1YcJ5Rw.js.map +1 -0
  202. package/web/app/site/app/assets/home-mobile-dark@2x-Dc-J8-SM.png +0 -0
  203. package/web/app/site/app/assets/home-mobile-light@2x-Bd3fod8m.png +0 -0
  204. package/web/app/site/app/assets/home-tablet-dark@2x-BgcNI8le.png +0 -0
  205. package/web/app/site/app/assets/home-tablet-light@2x-BW2RV9T6.png +0 -0
  206. package/web/app/site/app/assets/icons/add-circle-line-WD0mwORD.js +1 -0
  207. package/web/app/site/app/assets/icons/alert-line-CpFUMf9s.js +1 -0
  208. package/web/app/site/app/assets/icons/android-2-line-Bzxnrd3S.js +1 -0
  209. package/web/app/site/app/assets/icons/announcement-line-5nwaJFJm.js +1 -0
  210. package/web/app/site/app/assets/icons/arrow-down-circle-line-a7Id9-Iv.js +1 -0
  211. package/web/app/site/app/assets/icons/arrow-down-line-B8uzXzq6.js +1 -0
  212. package/web/app/site/app/assets/icons/arrow-left-line-CGrGT9C-.js +1 -0
  213. package/web/app/site/app/assets/icons/arrow-right-line-CE-HERfX.js +1 -0
  214. package/web/app/site/app/assets/icons/arrow-to-up-line-CvdQjmcy.js +1 -0
  215. package/web/app/site/app/assets/icons/arrow-up-circle-line-DEparSNF.js +1 -0
  216. package/web/app/site/app/assets/icons/arrow-up-line-C7yBeugc.js +1 -0
  217. package/web/app/site/app/assets/icons/arrows-right-line-BtbXmJGP.js +1 -0
  218. package/web/app/site/app/assets/icons/at-line-CF66Bznk.js +1 -0
  219. package/web/app/site/app/assets/icons/attachment-line-BY8It-l9.js +1 -0
  220. package/web/app/site/app/assets/icons/blockquote-line-BS6pU_hm.js +1 -0
  221. package/web/app/site/app/assets/icons/board-line-C0UWXyA6.js +1 -0
  222. package/web/app/site/app/assets/icons/bookmark-line-1CtX5JLF.js +1 -0
  223. package/web/app/site/app/assets/icons/building-5-line-Dd59MzbO.js +1 -0
  224. package/web/app/site/app/assets/icons/bus-2-line-6_dvA0GG.js +1 -0
  225. package/web/app/site/app/assets/icons/calendar-day-line-Crpl_fmu.js +1 -0
  226. package/web/app/site/app/assets/icons/calendar-month-line-vommts2C.js +1 -0
  227. package/web/app/site/app/assets/icons/calendar-time-add-line-BU_DTlQk.js +1 -0
  228. package/web/app/site/app/assets/icons/camera-line-Cv69MEei.js +1 -0
  229. package/web/app/site/app/assets/icons/celebrate-line-B8DvIoi0.js +1 -0
  230. package/web/app/site/app/assets/icons/chart-bar-line-FK-QOmtF.js +1 -0
  231. package/web/app/site/app/assets/icons/chart-line-line-BGcJAFWw.js +1 -0
  232. package/web/app/site/app/assets/icons/chat-3-line-YFK2-hwz.js +1 -0
  233. package/web/app/site/app/assets/icons/check-circle-line-DsAxFr5-.js +1 -0
  234. package/web/app/site/app/assets/icons/clipboard-line-BBp364gJ.js +1 -0
  235. package/web/app/site/app/assets/icons/close-circle-line-CICdiuGz.js +1 -0
  236. package/web/app/site/app/assets/icons/close-line-DYrFWcjv.js +1 -0
  237. package/web/app/site/app/assets/icons/cloud-line-tiQAbah8.js +1 -0
  238. package/web/app/site/app/assets/icons/code-line-fAl8iiV3.js +1 -0
  239. package/web/app/site/app/assets/icons/comment-2-line-CR3g7WeB.js +1 -0
  240. package/web/app/site/app/assets/icons/copy-2-line-CLW69yF5.js +1 -0
  241. package/web/app/site/app/assets/icons/delete-2-line-B8xmbr4n.js +1 -0
  242. package/web/app/site/app/assets/icons/document-3-line-DBGNhAg2.js +1 -0
  243. package/web/app/site/app/assets/icons/document-line-C7mb9sb7.js +1 -0
  244. package/web/app/site/app/assets/icons/down-line-BGqSSThd.js +1 -0
  245. package/web/app/site/app/assets/icons/edit-4-line-DcgGaEhx.js +1 -0
  246. package/web/app/site/app/assets/icons/emoji-2-line-D8YgnM-y.js +1 -0
  247. package/web/app/site/app/assets/icons/exit-line-Dc07BS-x.js +1 -0
  248. package/web/app/site/app/assets/icons/external-link-line-BvpSkmjO.js +1 -0
  249. package/web/app/site/app/assets/icons/eye-2-line-CiWXx-f_.js +1 -0
  250. package/web/app/site/app/assets/icons/eye-close-line-BgTaSJ4P.js +1 -0
  251. package/web/app/site/app/assets/icons/filter-2-line-B_mR2usy.js +1 -0
  252. package/web/app/site/app/assets/icons/filter-line-C_nm-3cU.js +1 -0
  253. package/web/app/site/app/assets/icons/flag-1-line-C2HB05I0.js +1 -0
  254. package/web/app/site/app/assets/icons/forbid-circle-line-D3LTT9yd.js +1 -0
  255. package/web/app/site/app/assets/icons/formula-line-DLrt2d6j.js +1 -0
  256. package/web/app/site/app/assets/icons/grid-line-CgUmEikf.js +1 -0
  257. package/web/app/site/app/assets/icons/group-line-C5fnX0wU.js +1 -0
  258. package/web/app/site/app/assets/icons/hand-finger-2-line-BGgxbuL9.js +1 -0
  259. package/web/app/site/app/assets/icons/hashtag-line-CyTEEXBb.js +1 -0
  260. package/web/app/site/app/assets/icons/heart-crack-line-BZddbO2C.js +1 -0
  261. package/web/app/site/app/assets/icons/heart-line-CJ7K47J4.js +1 -0
  262. package/web/app/site/app/assets/icons/history-2-line-kWWvFzqv.js +1 -0
  263. package/web/app/site/app/assets/icons/history-line-bGQiwbTH.js +1 -0
  264. package/web/app/site/app/assets/icons/home-3-line-fRU1zeLG.js +1 -0
  265. package/web/app/site/app/assets/icons/information-line-iNk6-sOY.js +1 -0
  266. package/web/app/site/app/assets/icons/keyboard-line-DsCyuQNz.js +1 -0
  267. package/web/app/site/app/assets/icons/layout-4-line-CZkOX52Z.js +1 -0
  268. package/web/app/site/app/assets/icons/layout-5-line-B_znt5wO.js +1 -0
  269. package/web/app/site/app/assets/icons/left-line-eUw0tya4.js +1 -0
  270. package/web/app/site/app/assets/icons/lightning-line-DaKoDdix.js +1 -0
  271. package/web/app/site/app/assets/icons/link-2-line-DCqLmMA7.js +1 -0
  272. package/web/app/site/app/assets/icons/list-check-line-BAxosBh9.js +1 -0
  273. package/web/app/site/app/assets/icons/lock-line-CPoaXeUW.js +1 -0
  274. package/web/app/site/app/assets/icons/mail-line-B0P8aa1L.js +1 -0
  275. package/web/app/site/app/assets/icons/moon-line-CEqNAToP.js +1 -0
  276. package/web/app/site/app/assets/icons/more-1-fill-Dw2e5xab.js +1 -0
  277. package/web/app/site/app/assets/icons/more-3-line-BIiVjYTz.js +1 -0
  278. package/web/app/site/app/assets/icons/notification-line-BZZq2Gtu.js +1 -0
  279. package/web/app/site/app/assets/icons/pencil-line-CKh8-A1v.js +1 -0
  280. package/web/app/site/app/assets/icons/photo-album-line-Bnfg2u40.js +1 -0
  281. package/web/app/site/app/assets/icons/pin-line-DpTolyhs.js +1 -0
  282. package/web/app/site/app/assets/icons/play-fill-B0-G_8lX.js +1 -0
  283. package/web/app/site/app/assets/icons/qrcode-2-line-B6SPCaHn.js +1 -0
  284. package/web/app/site/app/assets/icons/quill-pen-line-B6LUrnCP.js +1 -0
  285. package/web/app/site/app/assets/icons/quote-left-fill-hQZ1cj_6.js +1 -0
  286. package/web/app/site/app/assets/icons/radar-line-CKyroTth.js +1 -0
  287. package/web/app/site/app/assets/icons/react-line-CoIKhhtK.js +1 -0
  288. package/web/app/site/app/assets/icons/refresh-2-line-D0mTF97S.js +1 -0
  289. package/web/app/site/app/assets/icons/right-line-B3KWJaqc.js +1 -0
  290. package/web/app/site/app/assets/icons/rocket-line-4KZl537D.js +1 -0
  291. package/web/app/site/app/assets/icons/round-fill-C69NTrZ0.js +1 -0
  292. package/web/app/site/app/assets/icons/round-line-B9k_1uo-.js +1 -0
  293. package/web/app/site/app/assets/icons/route-line-B28ReHcE.js +1 -0
  294. package/web/app/site/app/assets/icons/rows-4-line-CrBniagM.js +1 -0
  295. package/web/app/site/app/assets/icons/scan-line-DSf74YdP.js +1 -0
  296. package/web/app/site/app/assets/icons/search-2-line-B6geIU1z.js +1 -0
  297. package/web/app/site/app/assets/icons/settings-3-line-azJwK47w.js +1 -0
  298. package/web/app/site/app/assets/icons/settings-6-line-C_5zc5YR.js +1 -0
  299. package/web/app/site/app/assets/icons/share-2-line-D5MxWjrm.js +1 -0
  300. package/web/app/site/app/assets/icons/share-forward-line-Cp61fZzZ.js +1 -0
  301. package/web/app/site/app/assets/icons/sparkles-2-line-DYYyXTaW.js +1 -0
  302. package/web/app/site/app/assets/icons/sparkles-line-C2hY1s6I.js +1 -0
  303. package/web/app/site/app/assets/icons/time-line-BF3eet1v.js +1 -0
  304. package/web/app/site/app/assets/icons/transfer-4-line-oisfvTBw.js +1 -0
  305. package/web/app/site/app/assets/icons/translate-line-BFWol2Ce.js +1 -0
  306. package/web/app/site/app/assets/icons/unlock-line-X_x5vwv3.js +1 -0
  307. package/web/app/site/app/assets/icons/upload-3-line-DgzwUJeW.js +1 -0
  308. package/web/app/site/app/assets/icons/user-4-line-BVSynZCp.js +1 -0
  309. package/web/app/site/app/assets/icons/user-add-2-line-BlGS5Q8q.js +1 -0
  310. package/web/app/site/app/assets/icons/user-add-line-BbXdHG4z.js +1 -0
  311. package/web/app/site/app/assets/icons/user-edit-line-BFAHOV0X.js +1 -0
  312. package/web/app/site/app/assets/icons/user-follow-line-BXux0wSd.js +1 -0
  313. package/web/app/site/app/assets/icons/user-star-line-DdYlpHLU.js +1 -0
  314. package/web/app/site/app/assets/icons/user-warning-line-Cj43j0_E.js +1 -0
  315. package/web/app/site/app/assets/icons/user-x-line-DIDgNlPH.js +1 -0
  316. package/web/app/site/app/assets/icons/volume-line-Bs390KQt.js +1 -0
  317. package/web/app/site/app/assets/icons/volume-mute-line-DzlzIqFZ.js +1 -0
  318. package/web/app/site/app/assets/icons/walk-line-yNXfB-md.js +1 -0
  319. package/web/app/site/app/assets/icons/world-2-line-CenkGIwj.js +1 -0
  320. package/web/app/site/app/assets/icons/zoom-in-line-CaENUphf.js +1 -0
  321. package/web/app/site/app/assets/icons/zoom-out-line-BDNk0EWo.js +1 -0
  322. package/web/app/site/app/assets/instances-BNPptnn-.json +333 -0
  323. package/web/app/site/app/assets/locales/ar-SA-CfheRbCu.js +1 -0
  324. package/web/app/site/app/assets/locales/ca-ES-DHRW86Tz.js +1 -0
  325. package/web/app/site/app/assets/locales/cs-CZ-CKoqEuA1.js +1 -0
  326. package/web/app/site/app/assets/locales/de-DE-ByRPMRFy.js +1 -0
  327. package/web/app/site/app/assets/locales/eo-UY-CPC7wLCS.js +1 -0
  328. package/web/app/site/app/assets/locales/es-ES-dJa0Fd9t.js +1 -0
  329. package/web/app/site/app/assets/locales/eu-ES-C3djlPR2.js +1 -0
  330. package/web/app/site/app/assets/locales/fa-IR-DTT490Ck.js +1 -0
  331. package/web/app/site/app/assets/locales/fi-FI-B23MCPuz.js +1 -0
  332. package/web/app/site/app/assets/locales/fr-FR-BcaQsO5C.js +1 -0
  333. package/web/app/site/app/assets/locales/gl-ES-BR-CVKBN.js +1 -0
  334. package/web/app/site/app/assets/locales/he-IL-CfheRbCu.js +1 -0
  335. package/web/app/site/app/assets/locales/hu-HU-CfheRbCu.js +1 -0
  336. package/web/app/site/app/assets/locales/it-IT-C1136UFV.js +1 -0
  337. package/web/app/site/app/assets/locales/ja-JP-CRZmRAhD.js +1 -0
  338. package/web/app/site/app/assets/locales/kab-Cz8xH_TT.js +1 -0
  339. package/web/app/site/app/assets/locales/ko-KR-CG9UiY4o.js +1 -0
  340. package/web/app/site/app/assets/locales/lt-LT-CJcI6sHZ.js +1 -0
  341. package/web/app/site/app/assets/locales/nb-NO-CfheRbCu.js +1 -0
  342. package/web/app/site/app/assets/locales/nl-NL-DxN4Yc1t.js +1 -0
  343. package/web/app/site/app/assets/locales/oc-FR-CfheRbCu.js +1 -0
  344. package/web/app/site/app/assets/locales/pl-PL-DkwGXf6P.js +1 -0
  345. package/web/app/site/app/assets/locales/pseudo-LOCALE-CQ4XxkQ0.js +1 -0
  346. package/web/app/site/app/assets/locales/pt-BR-nGL0HdFS.js +1 -0
  347. package/web/app/site/app/assets/locales/pt-PT-Cp17aj2z.js +1 -0
  348. package/web/app/site/app/assets/locales/ru-RU-0Y8cIoaK.js +1 -0
  349. package/web/app/site/app/assets/locales/th-TH-CfheRbCu.js +1 -0
  350. package/web/app/site/app/assets/locales/tok-CfheRbCu.js +1 -0
  351. package/web/app/site/app/assets/locales/tr-TR-CfheRbCu.js +1 -0
  352. package/web/app/site/app/assets/locales/uk-UA-C5E3dFBf.js +1 -0
  353. package/web/app/site/app/assets/locales/zh-CN-BHJi7_et.js +1 -0
  354. package/web/app/site/app/assets/locales/zh-TW-CfheRbCu.js +1 -0
  355. package/web/app/site/app/assets/main-BdqNbG-a.js +26 -0
  356. package/web/app/site/app/assets/main-BdqNbG-a.js.map +1 -0
  357. package/web/app/site/app/assets/mock-home-DGhR__He.js +2 -0
  358. package/web/app/site/app/assets/mock-home-DGhR__He.js.map +1 -0
  359. package/web/app/site/app/assets/multi-column-ROck0NVt.jpg +0 -0
  360. package/web/app/site/app/assets/multi-hashtag-timeline-DH5INVHi.jpg +0 -0
  361. package/web/app/site/app/assets/nested-comments-thread-hv59kZLc.jpg +0 -0
  362. package/web/app/site/app/assets/polyfill-force-H8sUkLGp.js +2 -0
  363. package/web/app/site/app/assets/polyfill-force-H8sUkLGp.js.map +1 -0
  364. package/web/app/site/app/assets/pwa-viewport-ogKbF51K.js +20 -0
  365. package/web/app/site/app/assets/pwa-viewport-ogKbF51K.js.map +1 -0
  366. package/web/app/site/app/assets/style-B1iLPTPR.css +2 -0
  367. package/web/app/site/app/assets/temml-DuyIQMei.js +97 -0
  368. package/web/app/site/app/assets/temml-DuyIQMei.js.map +1 -0
  369. package/web/app/site/app/assets/tinyld.light.node-DtbBhLYC.js +2 -0
  370. package/web/app/site/app/assets/tinyld.light.node-DtbBhLYC.js.map +1 -0
  371. package/web/app/site/app/assets/year-in-posts-B2lywqdx.png +0 -0
  372. package/web/app/site/app/assets/year-in-posts-DJv_star.js +3 -0
  373. package/web/app/site/app/assets/year-in-posts-DJv_star.js.map +1 -0
  374. package/web/app/site/app/compose/index.html +20 -0
  375. package/web/app/site/app/favicon.ico +0 -0
  376. package/web/app/site/app/index.html +73 -0
  377. package/web/app/site/app/logo-192.png +0 -0
  378. package/web/app/site/app/logo-512.png +0 -0
  379. package/web/app/site/app/logo-badge-72.png +0 -0
  380. package/web/app/site/app/logo-maskable-512.png +0 -0
  381. package/web/app/site/app/logo-monochrome-512.png +0 -0
  382. package/web/app/site/app/logo-monochrome-maskable-512.png +0 -0
  383. package/web/app/site/app/manifest.webmanifest +1 -0
  384. package/web/app/site/app/og-image-2.jpg +0 -0
  385. package/web/app/site/app/og-image.png +0 -0
  386. package/web/app/site/app/robots.txt +2 -0
  387. package/web/app/site/app/version.json +1 -0
  388. package/web/app/site/boot.js +34067 -0
  389. package/web/app/site/index.html +203 -0
  390. package/web/app/site/sw.js +70014 -0
  391. package/web/app/sw-src.mjs +220 -0
  392. package/web/front/#new-account.html# +43 -0
  393. package/web/front/admin.html +1 -81
  394. package/web/front/admin.js +85 -0
  395. package/web/front/new-account.html +1 -9
  396. package/web/front/new-account.js +13 -0
  397. package/web/front/run.html +1 -92
  398. package/web/front/run.js +107 -0
  399. package/lib/admin.mjs +0 -1913
  400. package/lib/intake.mjs +0 -1764
  401. package/lib/mastoapi.mjs +0 -1947
  402. package/lib/podrdf.mjs +0 -132
  403. package/lib/publisher.mjs +0 -1263
  404. package/web/admin/admin.js +0 -1114
  405. /package/lib/{oidc-auth.mjs → client/oidc-auth.mjs} +0 -0
  406. /package/lib/{webpush.mjs → client/webpush.mjs} +0 -0
  407. /package/lib/{bskyfeed.mjs → connections/bskyfeed.mjs} +0 -0
  408. /package/lib/{lease.mjs → core/lease.mjs} +0 -0
  409. /package/lib/{polls.mjs → core/polls.mjs} +0 -0
  410. /package/lib/{proof.mjs → core/proof.mjs} +0 -0
  411. /package/lib/{account.mjs → device/account.mjs} +0 -0
  412. /package/lib/{certs.mjs → device/certs.mjs} +0 -0
  413. /package/lib/{home.mjs → device/home.mjs} +0 -0
  414. /package/lib/{ports.mjs → device/ports.mjs} +0 -0
  415. /package/lib/{guard.mjs → shared/guard.mjs} +0 -0
  416. /package/lib/{safefetch.mjs → shared/safefetch.mjs} +0 -0
@@ -0,0 +1,129 @@
1
+ # activitystreams.ttl — shapes for the ActivityStreams documents an identity
2
+ # reads from strangers.
3
+ #
4
+ # Shapes are identified by fragments of this document, so nothing here claims a
5
+ # namespace of its own.
6
+ #
7
+ # These are deliberately thin. They state what the ActivityPub specification
8
+ # requires and nothing more, because every fediverse implementation writes
9
+ # slightly different documents and a constraint beyond the spec is a constraint
10
+ # that fires on somebody's real traffic. Nothing here rejects anything: a
11
+ # failure is recorded and the activity is handled. A shape earns its way to
12
+ # being enforced by not firing.
13
+
14
+ @prefix sh: <http://www.w3.org/ns/shacl#> .
15
+ @prefix as: <https://www.w3.org/ns/activitystreams#> .
16
+ @prefix ldp: <http://www.w3.org/ns/ldp#> .
17
+ @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
18
+
19
+ # --- the actor document ------------------------------------------------------
20
+
21
+ <#Actor>
22
+ a sh:NodeShape ;
23
+ sh:targetClass as:Person, as:Service, as:Group, as:Organization, as:Application ;
24
+ sh:name "Actor" ;
25
+ sh:description "Someone or something that acts: it has an address activities are sent to, and a name it is addressed by." ;
26
+ sh:property [
27
+ sh:path ldp:inbox ;
28
+ sh:name "inbox" ;
29
+ sh:description "The collection an actor receives activities in." ;
30
+ sh:nodeKind sh:IRI ;
31
+ sh:minCount 1 ;
32
+ sh:maxCount 1 ;
33
+ ] ;
34
+ sh:property [
35
+ sh:path as:preferredUsername ;
36
+ sh:name "preferred username" ;
37
+ sh:description "The short name an actor is addressed by, the local part of its @handle@host address." ;
38
+ sh:datatype xsd:string ;
39
+ sh:maxCount 1 ;
40
+ ] .
41
+
42
+ # --- the activities an identity acts on --------------------------------------
43
+ #
44
+ # Each states the two things ActivityPub requires of an activity: who performed
45
+ # it, and what it is about.
46
+
47
+ <#Create>
48
+ a sh:NodeShape ;
49
+ sh:targetClass as:Create ;
50
+ sh:name "Create" ;
51
+ sh:description "The bringing into existence of the object it names." ;
52
+ sh:property [
53
+ sh:path as:actor ;
54
+ sh:name "actor" ;
55
+ sh:description "The one that performed the activity." ;
56
+ sh:nodeKind sh:BlankNodeOrIRI ;
57
+ sh:minCount 1 ;
58
+ sh:maxCount 1 ;
59
+ ] ;
60
+ sh:property [
61
+ sh:path as:object ;
62
+ sh:name "object" ;
63
+ sh:description "The thing the activity is about." ;
64
+ sh:minCount 1 ;
65
+ sh:maxCount 1 ;
66
+ ] .
67
+
68
+ <#Follow>
69
+ a sh:NodeShape ;
70
+ sh:targetClass as:Follow ;
71
+ sh:name "Follow" ;
72
+ sh:description "A request to receive what the object it names publishes." ;
73
+ sh:property [
74
+ sh:path as:actor ;
75
+ sh:name "actor" ;
76
+ sh:description "The one that performed the activity." ;
77
+ sh:nodeKind sh:BlankNodeOrIRI ;
78
+ sh:minCount 1 ;
79
+ sh:maxCount 1 ;
80
+ ] ;
81
+ sh:property [
82
+ sh:path as:object ;
83
+ sh:name "object" ;
84
+ sh:description "The thing the activity is about." ;
85
+ sh:minCount 1 ;
86
+ sh:maxCount 1 ;
87
+ ] .
88
+
89
+ <#Accept>
90
+ a sh:NodeShape ;
91
+ sh:targetClass as:Accept ;
92
+ sh:name "Accept" ;
93
+ sh:description "Assent to the activity it names." ;
94
+ sh:property [
95
+ sh:path as:actor ;
96
+ sh:name "actor" ;
97
+ sh:description "The one that performed the activity." ;
98
+ sh:nodeKind sh:BlankNodeOrIRI ;
99
+ sh:minCount 1 ;
100
+ sh:maxCount 1 ;
101
+ ] ;
102
+ sh:property [
103
+ sh:path as:object ;
104
+ sh:name "object" ;
105
+ sh:description "The thing the activity is about." ;
106
+ sh:minCount 1 ;
107
+ sh:maxCount 1 ;
108
+ ] .
109
+
110
+ <#Undo>
111
+ a sh:NodeShape ;
112
+ sh:targetClass as:Undo ;
113
+ sh:name "Undo" ;
114
+ sh:description "The retraction of the activity it names, by the one that performed it." ;
115
+ sh:property [
116
+ sh:path as:actor ;
117
+ sh:name "actor" ;
118
+ sh:description "The one that performed the activity." ;
119
+ sh:nodeKind sh:BlankNodeOrIRI ;
120
+ sh:minCount 1 ;
121
+ sh:maxCount 1 ;
122
+ ] ;
123
+ sh:property [
124
+ sh:path as:object ;
125
+ sh:name "object" ;
126
+ sh:description "The thing the activity is about." ;
127
+ sh:minCount 1 ;
128
+ sh:maxCount 1 ;
129
+ ] .
@@ -0,0 +1,107 @@
1
+ // shapes/index.mjs — validating an ActivityStreams document against the shapes
2
+ // beside this file.
3
+ //
4
+ // A shape says what a kind of document IS. Checking one against a document we
5
+ // were sent is a different question from whether we trust it: a forged actor,
6
+ // a receipt that does not bind, a reply naming a note we never wrote are each
7
+ // guarded elsewhere, and none of those guards is replaced here.
8
+ //
9
+ // NOTHING HERE REJECTS ANYTHING. A failure is recorded and the activity is
10
+ // handled exactly as it would have been. Every fediverse implementation writes
11
+ // slightly different documents, and a shape strict enough to catch something
12
+ // real is strict enough to reject somebody's quirk; which one cannot be known
13
+ // in advance, so it is learned from the record rather than guessed at. A shape
14
+ // earns enforcement by not firing.
15
+
16
+ import { Parser } from 'n3';
17
+ import Environment from '@rdfjs/environment';
18
+ import DataFactory from '@rdfjs/data-model/Factory.js';
19
+ import DatasetFactory from '@rdfjs/dataset/Factory.js';
20
+ import NamespaceFactory from '@rdfjs/namespace/Factory.js';
21
+ import TermSetFactory from '@rdfjs/term-set/Factory.js';
22
+ import TermMapFactory from '@rdfjs/term-map/Factory.js';
23
+ import ClownfaceFactory from 'clownface/Factory.js';
24
+ import SHACLValidator from 'rdf-validate-shacl';
25
+ import { SHAPES_TTL } from './shapes-text.mjs';
26
+
27
+ // The validator needs a full RDF/JS environment, not a bare factory. These
28
+ // pieces are what rdf-validate-shacl itself depends on, composed here rather
29
+ // than pulling in a packaged environment that reads files — this has to run in
30
+ // a service worker too.
31
+ const rdf = new Environment([DataFactory, DatasetFactory, NamespaceFactory, TermSetFactory, TermMapFactory, ClownfaceFactory]);
32
+
33
+ // The shapes document's own URL is the base, so `<#Create>` resolves to a
34
+ // fragment of it and no namespace is claimed.
35
+ const SHAPES_BASE = 'https://fedipod.invalid/shapes';
36
+
37
+ let validator;
38
+ let brokenReported = false;
39
+
40
+ /** The validator, built once. Reading and parsing the shapes is not per-document work. */
41
+ function shacl() {
42
+ if (!validator) {
43
+ const quads = new Parser({ baseIRI: SHAPES_BASE, format: 'text/turtle' }).parse(SHAPES_TTL);
44
+ validator = new SHACLValidator(rdf.dataset(quads.map(asTerm)), { factory: rdf });
45
+ }
46
+ return validator;
47
+ }
48
+
49
+ /** jsonld and n3 each mint their own terms; the validator's environment must mint the ones it sees. */
50
+ function asTerm(q) {
51
+ const term = (t) => {
52
+ if (t.termType === 'NamedNode') return rdf.namedNode(t.value);
53
+ if (t.termType === 'BlankNode') return rdf.blankNode(String(t.value).replace(/^_:/u, ''));
54
+ if (t.termType === 'Literal') {
55
+ return rdf.literal(t.value, t.language || (t.datatype ? rdf.namedNode(t.datatype.value) : undefined));
56
+ }
57
+ return rdf.defaultGraph();
58
+ };
59
+ return rdf.quad(term(q.subject), term(q.predicate), term(q.object));
60
+ }
61
+
62
+ /**
63
+ * Check a document's graph against the shapes.
64
+ *
65
+ * `quads` is what `parseAS2` produced. Returns null when the document
66
+ * conforms or when there was no graph to check, and otherwise a short record
67
+ * of what did not fit — the failing node, the property, and the shape's own
68
+ * words for it.
69
+ *
70
+ * Never throws. A validator that cannot run is not a reason to drop mail — but
71
+ * it is said once, because a checker that silently passes everything is the
72
+ * same silence this change exists to remove.
73
+ */
74
+ export async function checkShapes(quads) {
75
+ if (!Array.isArray(quads) || quads.length === 0) return null;
76
+ let report;
77
+ try {
78
+ report = await shacl().validate(rdf.dataset(quads.map(asTerm)));
79
+ } catch (e) {
80
+ // A validator that cannot run must not read as "everything conforms" —
81
+ // that is the same silence this whole change exists to remove. Said once,
82
+ // then quiet, because a broken validator would otherwise say it per item.
83
+ if (!brokenReported) {
84
+ brokenReported = true;
85
+ return { conforms: false, broken: true, results: [{ focus: null, path: null, message: `shape validation is not running: ${e.message}`, shape: null }] };
86
+ }
87
+ return null;
88
+ }
89
+ if (report.conforms) return null;
90
+ return {
91
+ conforms: false,
92
+ results: report.results.slice(0, 10).map((r) => ({
93
+ focus: r.focusNode?.value ?? null,
94
+ path: r.path?.value ?? null,
95
+ message: r.message?.[0]?.value ?? String(r.sourceConstraintComponent?.value ?? 'does not fit the shape'),
96
+ shape: r.sourceShape?.value ?? null,
97
+ })),
98
+ };
99
+ }
100
+
101
+ /** The failure as one line, for a log or a dead-letter record. */
102
+ export function describeShapeFailure(failure) {
103
+ if (!failure) return null;
104
+ return failure.results
105
+ .map((r) => `${r.path ? r.path.replace(/^.*[#/]/u, '') : 'node'}: ${r.message}`)
106
+ .join('; ');
107
+ }
@@ -0,0 +1,13 @@
1
+ // shapes-text.mjs — the shapes document, as text.
2
+ //
3
+ // The shapes are authored as Turtle in activitystreams.ttl, which is the one
4
+ // source of truth and opens in any RDF tool. Reading it differs by where this
5
+ // runs: Node reads the file, and the browser bundle swaps this module for a
6
+ // shim that imports the same .ttl through esbuild's text loader.
7
+ import fs from 'node:fs';
8
+ import path from 'node:path';
9
+ import { fileURLToPath } from 'node:url';
10
+
11
+ export const SHAPES_TTL = fs.readFileSync(
12
+ path.join(path.dirname(fileURLToPath(import.meta.url)), 'activitystreams.ttl'), 'utf8',
13
+ );
@@ -5,6 +5,7 @@
5
5
 
6
6
  import crypto from 'node:crypto';
7
7
  import * as wire from './wire.mjs';
8
+ import * as podNotes from '../pod/notes.mjs';
8
9
  import { dropFollower } from './store.mjs';
9
10
 
10
11
  // WebFinger, ours rather than fedify's — v2 dropped lookupWebFinger.
@@ -20,13 +21,23 @@ export async function lookupWebFinger(acct) {
20
21
  if (at < 1) return null;
21
22
  const [user, host] = [clean.slice(0, at), clean.slice(at + 1)];
22
23
  if (!host || /[/\\?#]/.test(host)) return null;
23
- const { safeFetch, readCapped } = await import('./safefetch.mjs');
24
+ const { safeFetch, readCapped } = await import('../shared/safefetch.mjs');
24
25
  const url = `https://${host}/.well-known/webfinger?resource=${encodeURIComponent(`acct:${clean}`)}`;
25
26
  const res = await safeFetch(url, {
26
27
  headers: { accept: 'application/jrd+json, application/json' },
27
28
  }).catch(() => null);
28
29
  if (!res || res.status >= 400) return null;
29
- try { return JSON.parse(await readCapped(res, 256 * 1024)); } catch { return null; }
30
+ let jrd;
31
+ try { jrd = JSON.parse(await readCapped(res, 256 * 1024)); } catch { return null; }
32
+ // The JRD says who it is about. Nothing checked it, so a host answering every
33
+ // query with one account's document made `@anyone@that.host` resolve to that
34
+ // account — and it was then stored under the handle that was asked for, not
35
+ // the one it turned out to be.
36
+ const subject = String(jrd?.subject || '').replace(/^acct:/, '').toLowerCase();
37
+ if (subject && subject !== clean.toLowerCase()) {
38
+ return null;
39
+ }
40
+ return jrd;
30
41
  }
31
42
 
32
43
  export const selfLink = (jrd) => jrd?.links?.find(l => l.rel === 'self' &&
@@ -337,8 +348,8 @@ export async function votePoll(agent, s, choices) {
337
348
  '@context': wire.AS_CTX, id: wire.createActivityId(id), type: 'Create',
338
349
  actor: urls.actor, published, to: [s.actor], object: note,
339
350
  };
340
- await agent.remote.putJson(id, note);
341
- await agent.remote.putJson(create.id, create);
351
+ await podNotes.write(agent.remote, id, note);
352
+ await podNotes.writeCreate(agent.remote, create.id, create);
342
353
  await agent.deliverer.deliverToAll([inbox], create);
343
354
  }
344
355
  agent.store.updateStatus(s.noteId, {
@@ -364,10 +375,9 @@ export async function deleteNote(agent, s) {
364
375
  const stuck = [];
365
376
  const deletedAt = new Date().toISOString();
366
377
  try {
367
- await agent.remote.putJson(s.noteId, wire.noteTombstone(s.noteId, deletedAt));
368
- await agent.remote.setAcl(s.noteId, ['Read']);
378
+ await podNotes.writeTombstone(agent.remote, s.noteId, wire.noteTombstone(s.noteId, deletedAt));
369
379
  } catch { stuck.push(s.noteId); }
370
- if (!await agent.remote.delete(wire.createActivityId(s.noteId)).catch(() => false)) {
380
+ if (!await podNotes.dropCreate(agent.remote, wire.createActivityId(s.noteId))) {
371
381
  stuck.push(wire.createActivityId(s.noteId));
372
382
  }
373
383
  if (stuck.length) {
@@ -378,7 +388,7 @@ export async function deleteNote(agent, s) {
378
388
  };
379
389
  }
380
390
  // Empty, and referenced by nothing once the note is gone.
381
- await agent.remote.delete(wire.repliesId(s.noteId)).catch(() => {});
391
+ await podNotes.dropReplies(agent.remote, wire.repliesId(s.noteId));
382
392
 
383
393
  // The Bluesky mirror goes with the post. Best-effort: a mirror the far side
384
394
  // already lost is not a reason to keep the post here.
@@ -388,9 +398,8 @@ export async function deleteNote(agent, s) {
388
398
  .catch(e => agent.log?.(`bluesky mirror not deleted (${e.message}): ${s.atproto.uri}`));
389
399
  }
390
400
 
391
- // The note, and our own boost of it if there was one.
401
+ // The note's outbox entry, and our own boost of it if there was one.
392
402
  await agent.publisher.unrecordOutbox(i => i === s.noteId || (i?.id && i.id === s.announceActivity?.id));
393
- if (s.slug) await agent.local.delete(agent.local.fedi + 'posts/' + s.slug).catch(() => {});
394
403
  agent.store.removeStatus(s.noteId);
395
404
  return { ok: true };
396
405
  }
@@ -30,8 +30,8 @@ export class HttpStorage {
30
30
  // The same jail FileStorage has, and for the same reason: a path here can
31
31
  // carry `..`, encodeURI leaves both `.` and `/` alone, and fetch normalises
32
32
  // the segments away — so a name derived from remote input could name a
33
- // resource outside the container entirely. PodRdf._rel only checks the
34
- // PREFIX, which a concatenated URL always satisfies, so it catches nothing.
33
+ // resource outside the container entirely. A prefix check is not enough on
34
+ // its own: a concatenated URL always satisfies one.
35
35
  _url(p) {
36
36
  const u = new URL(encodeURI(p), this.base);
37
37
  if (!u.href.startsWith(this.base)) throw new Error(`path escapes the container: ${p}`);
@@ -152,6 +152,10 @@ export class PodStore {
152
152
 
153
153
  has(name) { return this.cache.has(name); }
154
154
 
155
+ // The documents held here. Callers that keep one document per thing — a
156
+ // connected account, say — list them with this rather than a fixed name.
157
+ names() { return [...this.cache.keys()]; }
158
+
155
159
  read(name, fallback) {
156
160
  return this.cache.has(name) ? structuredClone(this.cache.get(name)) : fallback;
157
161
  }
@@ -487,8 +491,15 @@ export class PodStore {
487
491
  if (ids[id] === url) return id; // already known, nothing to write
488
492
  const legacy = Object.entries(ids).find(([, u]) => u === url);
489
493
  if (legacy) return legacy[0]; // pre-hash entry a client may hold
494
+ // In memory only. This runs on the RENDER path — once per status, per
495
+ // account, per attachment, every time a timeline is drawn — and `write()`
496
+ // is write-through, so on the browser build scrolling a timeline was a pod
497
+ // PUT of this whole document. Nothing durable is lost: the id IS the hash
498
+ // of the url, so idFor recomputes it identically on any later run, and the
499
+ // map exists only so urlFor can still answer for ids minted under an older
500
+ // scheme (which are loaded from the pod and never written here).
490
501
  ids[id] = url;
491
- this.write('ids.json', ids);
502
+ this.cache.set('ids.json', ids);
492
503
  return id;
493
504
  }
494
505
  urlFor(id) { return this.getIds()[id] || null; }
@@ -6,75 +6,40 @@ export const AS_CTX = 'https://www.w3.org/ns/activitystreams';
6
6
  export const SEC_CTX = 'https://w3id.org/security/v1';
7
7
  export const PUBLIC = 'https://www.w3.org/ns/activitystreams#Public';
8
8
 
9
- // URLs of the actor's documents on the remote pod. Everything the agent owns
10
- // nests under ONE top-level container (`root`) so the pod stays tidy and one
11
- // pod could host several actors under different roots. Webfinger + host-meta
12
- // stay at the pod root — fediverse discovery requires the host root.
13
- // The AP URLs for an identity. Normally everything hangs off the pod home.
14
- //
15
- // When `publicBase` is given (a fronted identity, e.g.
16
- // "https://fedipod.net/u/me/"), the ADVERTISED ids — the actor and everything
17
- // under `ap/` a remote server sees — build on the fronted home instead, while
18
- // the pod-side trees (`fediverse/`, `ap-state/`) and the write targets stay on
19
- // the pod. `toPod`/`toPublic` map between the two spaces; `RemotePod` applies
20
- // `toPod` at its one request choke point, so publisher/social/intake keep
21
- // passing advertised ids and the writes still land on the pod. With no
22
- // `publicBase` the returned object is byte-identical to before — the invariant
23
- // the smoke suite pins.
9
+ // The pod LAYOUT — which container each document lives in — moved into the pod
10
+ // library (lib/pod/urls.mjs), which knows nothing about FediPod and requires
11
+ // the container root to be stated rather than defaulted. What stays here is
12
+ // FediPod's own answer to "which root", and the handle rule, both of which read
13
+ // this application's config shape.
14
+ import { apUrls as podUrls } from '../pod/urls.mjs';
15
+
16
+ export { webfingerHost } from '../pod/urls.mjs';
17
+
18
+ // The container the Node agent has always used. Older than the rename to
19
+ // FediPod and kept because it is what every existing Node-side pod actually
20
+ // contains; the browser build states `fedipod/` on its own configs. A config
21
+ // that names a root is always believed — this is only the answer for one that
22
+ // does not.
23
+ export const DEFAULT_ROOT = 'activitypods-js/';
24
+
24
25
  // The handle the fediverse sees: a fronted identity's name is the front's.
25
26
  export function publicHandle(config) {
26
27
  return config?.gateway?.frontActor?.match(/\/u\/([^/]+)\/ap\/actor\/?$/)?.[1]
27
28
  || config?.handle || null;
28
29
  }
29
30
 
30
- export function apUrls(remotePod, root = 'activitypods-js/', { publicBase = null } = {}) {
31
- const base = remotePod.endsWith('/') ? remotePod : remotePod + '/';
32
- const home = base + (!root || root.endsWith('/') ? root : root + '/');
33
- // The face a remote sees: the fronted home, or the pod home when unfronted.
34
- const face = publicBase ? (publicBase.endsWith('/') ? publicBase : publicBase + '/') : home;
35
- const urls = {
36
- base, home,
37
- webfinger: base + '.well-known/webfinger',
38
- actor: face + 'ap/actor',
39
- inbox: face + 'ap/inbox/',
40
- outbox: face + 'ap/outbox',
41
- followers: face + 'ap/followers',
42
- following: face + 'ap/following',
43
- notes: face + 'ap/notes/',
44
- privateNotes: face + 'ap/private/',
45
- featured: face + 'ap/featured',
46
- // FEP-1b12: the moderator roster a recipient validates announced
47
- // moderation against. Published only when moderators are configured.
48
- moderators: face + 'ap/moderators',
49
- // FEP-4ccd and FEP-c648: follows in limbo and the block list, as
50
- // collections. They live in the private container so the owner-only ACL
51
- // is inherited, not re-stated per document.
52
- pendingFollowers: face + 'ap/private/pending-followers',
53
- pendingFollowing: face + 'ap/private/pending-following',
54
- blocked: face + 'ap/private/blocked',
55
- profileHtml: face + 'ap/profile.html',
56
- // Media stays on the pod even when fronted: attachment urls are not
57
- // identity-checked by remotes, and proxying blobs would be pure cost.
58
- media: home + 'ap/media/',
59
- fediverse: home + 'fediverse/',
60
- state: home + 'ap-state/',
61
- };
62
- if (publicBase) {
63
- urls.podHome = home;
64
- urls.publicHome = face;
65
- urls.toPod = (u) => (typeof u === 'string' && u.startsWith(face) ? home + u.slice(face.length) : u);
66
- urls.toPublic = (u) => (typeof u === 'string' && u.startsWith(home) ? face + u.slice(home.length) : u);
67
- }
68
- return urls;
69
- }
70
-
71
- // The host a handle would resolve through, or null when no handle can point
72
- // here. @name@host is looked up at https://host/.well-known/webfinger, so only
73
- // a pod that owns the root of its host can answer for one; a pod living at
74
- // https://server/name/ may publish the document but nothing will ever ask.
75
- export function webfingerHost(podUrl) {
76
- const u = new URL(podUrl);
77
- return u.pathname === '/' ? u.host : null;
31
+ /**
32
+ * The AP URLs for an identity, with FediPod's default root applied.
33
+ *
34
+ * The library underneath refuses to guess a root. Every caller here goes
35
+ * through this wrapper so there is exactly ONE answer to "which container",
36
+ * which is the thing that was wrong: the browser agent decided `fedipod/` for
37
+ * its own urls while the publisher, handed a config with no `root`, decided
38
+ * `activitypods-js/` — putting an identity's state in one container and its
39
+ * published documents in another.
40
+ */
41
+ export function apUrls(remotePod, root, opts = {}) {
42
+ return podUrls(remotePod, root || DEFAULT_ROOT, opts);
78
43
  }
79
44
 
80
45
  // RFC 6415 host-meta: several fediverse implementations discover WebFinger
@@ -585,15 +550,31 @@ export function titledContent(note) {
585
550
  }
586
551
 
587
552
  // Normalize a wire Note's attachment list to { url, mediaType, description }.
553
+ // Capped: this list comes from somebody else's document and lands in a
554
+ // statuses.json row, which is rewritten whole on every change. Twenty is more
555
+ // than any client shows and more than any real post carries.
556
+ const MAX_ATTACHMENTS = 20;
557
+ const MAX_ATTACHMENT_URL = 2048;
558
+ // http(s) only: an attachment URL becomes a client's <img src> or <video src>,
559
+ // and the store has guarded avatars this way all along (safeUrl).
560
+ const attachmentUrl = (u) => {
561
+ if (!u) return null;
562
+ const s = String(u).slice(0, MAX_ATTACHMENT_URL);
563
+ try {
564
+ const p = new URL(s);
565
+ return (p.protocol === 'https:' || p.protocol === 'http:') ? s : null;
566
+ } catch { return null; }
567
+ };
568
+
588
569
  export function attachmentsOf(note) {
589
570
  const list = Array.isArray(note?.attachment) ? note.attachment : note?.attachment ? [note.attachment] : [];
590
571
  // A bare Link carries `href` and no `url` — Lemmy's link posts arrive that
591
572
  // way, and reading only `url` dropped the link the post was about.
592
- return list.map(a => ({
573
+ return list.slice(0, MAX_ATTACHMENTS).map(a => ({
593
574
  url: typeof a?.url === 'string' ? a.url : a?.url?.href || (typeof a?.href === 'string' ? a.href : undefined),
594
- mediaType: a?.mediaType || '',
595
- ...(a?.name ? { description: a.name } : {}),
596
- })).filter(a => a.url);
575
+ mediaType: String(a?.mediaType || '').slice(0, 128),
576
+ ...(a?.name ? { description: String(a.name).slice(0, 1500) } : {}),
577
+ })).map(a => ({ ...a, url: attachmentUrl(a.url) })).filter(a => a.url);
597
578
  }
598
579
 
599
580
  const HTML_ESCAPES = { '&': '&amp;', '<': '&lt;', '>': '&gt;' };
@@ -0,0 +1,13 @@
1
+ // admin.mjs — loopback HTTP front: admin API + Mastodon client-API facade +
2
+ // the bundled Phanpy UI served same-origin (no CORS, no mixed content — the
3
+ // role data-kitchen's router plays for the in-app pane). Loopback-bound; the
4
+ // gate (vendor/gate.cjs) engages only when a token is configured, exactly
5
+ // its standalone behavior.
6
+ //
7
+ // The surface is surface.mjs, its routes are the modules under routes/, the
8
+ // static serving is static.mjs, the listener is server.mjs. This file is the
9
+ // door the rest of the agent imports through.
10
+
11
+ export { buildAdminSurface } from './surface.mjs';
12
+ export { startAdmin } from './server.mjs';
13
+ export { namedOrigin, secureOrigin, wsOrigins } from './origins.mjs';
@@ -0,0 +1,35 @@
1
+ // origins.mjs — the addresses an identity answers on, as a browser must be
2
+ // sent to them.
3
+
4
+ import { allowedAuthorities, hostLabel } from '../../shared/guard.mjs';
5
+
6
+ // The origin a browser should be sent to for an identity. A browser keys
7
+ // storage per ORIGIN, so linking every identity at localhost:<port> files them
8
+ // all in one bucket — which is how a client ends up holding one actor's login
9
+ // and showing it on another's page. Named whenever the handle is a legal host
10
+ // label; bare loopback when it is not, because a mangled name would be an
11
+ // origin the other agent's own guard refuses.
12
+ export function namedOrigin(handle, port) {
13
+ const label = hostLabel(handle);
14
+ return `http://${label ? label + '.' : ''}localhost:${port}`;
15
+ }
16
+
17
+ // The https origin for an identity, on the one port it was given.
18
+ // This is the advertised default — what gets printed, opened and linked; the
19
+ // http listener stays served beside it for anything that needs cleartext.
20
+ export function secureOrigin(handle, httpsPort) {
21
+ const label = hostLabel(handle);
22
+ return `https://${label ? label + '.' : ''}localhost:${httpsPort}`;
23
+ }
24
+
25
+ export function wsOrigins(port, labels = []) {
26
+ const out = new Set();
27
+ for (const a of allowedAuthorities(port, labels)) {
28
+ // No IPv6 at all. Bare `::1` cannot carry a port, and Chrome rejects the
29
+ // bracketed form inside a CSP source expression — one invalid source makes
30
+ // it drop the whole directive, which is worse than not listing the socket.
31
+ if (a.startsWith('::') || a.startsWith('[')) continue;
32
+ out.add(`wss://${/:\d+$/.test(a) ? a : `${a}:${port}`}`);
33
+ }
34
+ return [...out];
35
+ }