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.
- package/README.md +15 -6
- package/dist/claims.js +1 -1
- package/dist/handler.d.ts +7 -1
- package/dist/handler.js +58 -12
- package/dist/handler.jsonld +5 -1
- package/dist/store-pod.js +18 -4
- package/lib/{c2s.mjs → client/c2s.mjs} +8 -3
- package/lib/{localapi.mjs → client/localapi.mjs} +2 -2
- package/lib/client/masto/accounts.mjs +264 -0
- package/lib/client/masto/body.mjs +69 -0
- package/lib/client/masto/index.mjs +183 -0
- package/lib/client/masto/instance.mjs +104 -0
- package/lib/client/masto/media.mjs +133 -0
- package/lib/client/masto/oauth.mjs +599 -0
- package/lib/client/masto/render.mjs +459 -0
- package/lib/client/masto/statuses.mjs +331 -0
- package/lib/client/masto/timelines.mjs +316 -0
- package/lib/{streaming.mjs → client/streaming.mjs} +1 -1
- package/lib/{acctfeed.mjs → connections/acctfeed.mjs} +1 -1
- package/lib/{atproto.mjs → connections/atproto.mjs} +15 -16
- package/lib/{bskygroup.mjs → connections/bskygroup.mjs} +1 -1
- package/lib/{fediacct.mjs → connections/fediacct.mjs} +31 -35
- package/lib/{import.mjs → connections/import.mjs} +2 -2
- package/lib/{tagfeed.mjs → connections/tagfeed.mjs} +21 -4
- package/lib/connections/vault.mjs +114 -0
- package/lib/core/as2.mjs +124 -0
- package/lib/core/contexts/activitystreams.json +379 -0
- package/lib/core/contexts/did-v1.json +57 -0
- package/lib/core/contexts/fep-5711.json +36 -0
- package/lib/core/contexts/gotosocial.json +86 -0
- package/lib/core/contexts/identity-v1.json +152 -0
- package/lib/core/contexts/index.mjs +45 -0
- package/lib/core/contexts/join-lemmy.json +33 -0
- package/lib/core/contexts/joinmastodon.json +28 -0
- package/lib/core/contexts/map.json +16 -0
- package/lib/core/contexts/miscellany.json +19 -0
- package/lib/core/contexts/schemaorg.json +8845 -0
- package/lib/core/contexts/security-data-integrity-v1.json +78 -0
- package/lib/core/contexts/security-data-integrity-v2.json +81 -0
- package/lib/core/contexts/security-multikey-v1.json +35 -0
- package/lib/core/contexts/security-v1.json +74 -0
- package/lib/core/contexts/webfinger.json +10 -0
- package/lib/{deliver.mjs → core/deliver.mjs} +8 -2
- package/lib/core/intake/activities.mjs +437 -0
- package/lib/core/intake/activity.mjs +240 -0
- package/lib/core/intake/channel.mjs +144 -0
- package/lib/core/intake/group.mjs +222 -0
- package/lib/core/intake/index.mjs +629 -0
- package/lib/core/intake/notes.mjs +288 -0
- package/lib/core/intake/verify.mjs +141 -0
- package/lib/{keys.mjs → core/keys.mjs} +1 -1
- package/lib/core/publisher/collections.mjs +229 -0
- package/lib/core/publisher/index.mjs +421 -0
- package/lib/core/publisher/notes.mjs +188 -0
- package/lib/core/publisher/questions.mjs +233 -0
- package/lib/core/publisher/restore.mjs +196 -0
- package/lib/core/shapes/activitystreams.ttl +129 -0
- package/lib/core/shapes/index.mjs +107 -0
- package/lib/core/shapes/shapes-text.mjs +13 -0
- package/lib/{social.mjs → core/social.mjs} +19 -10
- package/lib/{storage.mjs → core/storage.mjs} +2 -2
- package/lib/{store.mjs → core/store.mjs} +12 -1
- package/lib/{wire.mjs → core/wire.mjs} +48 -67
- package/lib/device/admin/index.mjs +13 -0
- package/lib/device/admin/origins.mjs +35 -0
- package/lib/device/admin/routes/connections.mjs +144 -0
- package/lib/device/admin/routes/gateway.mjs +199 -0
- package/lib/device/admin/routes/lifecycle.mjs +191 -0
- package/lib/device/admin/routes/owner.mjs +322 -0
- package/lib/device/admin/routes/setup.mjs +393 -0
- package/lib/device/admin/routes/social.mjs +188 -0
- package/lib/device/admin/server.mjs +95 -0
- package/lib/device/admin/static.mjs +244 -0
- package/lib/device/admin/surface.mjs +274 -0
- package/lib/device/cli/commands/account.mjs +586 -0
- package/lib/device/cli/commands/run.mjs +278 -0
- package/lib/device/cli/commands/service.mjs +221 -0
- package/lib/device/cli/commands/setup.mjs +410 -0
- package/lib/device/cli/commands/state.mjs +559 -0
- package/lib/device/cli/context.mjs +288 -0
- package/lib/{export-collections.mjs → device/export-collections.mjs} +1 -1
- package/lib/{migrate.mjs → device/migrate.mjs} +5 -19
- package/lib/device/remote.mjs +54 -0
- package/lib/{setup.mjs → device/setup.mjs} +3 -3
- package/lib/{update.mjs → device/update.mjs} +1 -1
- package/lib/{directory.mjs → gateway/directory.mjs} +1 -1
- package/lib/{front-core.mjs → gateway/front-core.mjs} +272 -49
- package/lib/{gateway-core.mjs → gateway/gateway-core.mjs} +3 -7
- package/lib/{httpsig.mjs → gateway/httpsig.mjs} +34 -3
- package/lib/pod/README.md +61 -0
- package/lib/pod/actor.mjs +96 -0
- package/lib/pod/collection.mjs +77 -0
- package/lib/pod/containers.mjs +136 -0
- package/lib/pod/discovery.mjs +46 -0
- package/lib/pod/featured.mjs +18 -0
- package/lib/pod/followers.mjs +24 -0
- package/lib/pod/following.mjs +10 -0
- package/lib/pod/http.mjs +74 -0
- package/lib/pod/inbox.mjs +144 -0
- package/lib/{links.mjs → pod/links.mjs} +1 -1
- package/lib/pod/media.mjs +27 -0
- package/lib/pod/notes.mjs +80 -0
- package/lib/pod/notifications.mjs +89 -0
- package/lib/pod/outbox.mjs +18 -0
- package/lib/pod/package.json +19 -0
- package/lib/pod/policy.mjs +58 -0
- package/lib/pod/private.mjs +19 -0
- package/lib/pod/root.mjs +63 -0
- package/lib/pod/state.mjs +39 -0
- package/lib/{remote.mjs → pod/transport.mjs} +217 -159
- package/lib/pod/urls.mjs +71 -0
- package/lib/{embed.mjs → server/embed.mjs} +149 -22
- package/lib/shared/links.mjs +9 -0
- package/lib/{ua.mjs → shared/ua.mjs} +1 -1
- package/package.json +1 -1
- package/run-agent.mjs +47 -64
- package/web/admin/actors.js +145 -0
- package/web/admin/bar.css +9 -13
- package/web/admin/bar.js +2 -2
- package/web/admin/client/client.js +1 -1
- package/web/admin/client/index.html +5 -6
- package/web/admin/common.js +23 -0
- package/web/admin/connections.js +112 -0
- package/web/admin/gateway.js +111 -0
- package/web/admin/group.js +258 -0
- package/web/admin/index.html +60 -66
- package/web/admin/record.js +378 -0
- package/web/admin/setup/index.html +3 -1
- package/web/admin/setup/setup.js +2 -13
- package/web/admin/tokens.css +56 -0
- package/web/admin/upkeep.js +170 -0
- package/web/app/README.md +77 -0
- package/web/app/admin-facade.mjs +549 -0
- package/web/app/agent.mjs +490 -0
- package/web/app/atproto-browser.mjs +86 -0
- package/web/app/boot.mjs +340 -0
- package/web/app/deliver-relay.mjs +72 -0
- package/web/app/dist/boot.js +34067 -0
- package/web/app/dist/boot.js.map +7 -0
- package/web/app/dist/sw.js +70014 -0
- package/web/app/dist/sw.js.map +7 -0
- package/web/app/fediacct-browser.mjs +146 -0
- package/web/app/idb-kv.mjs +35 -0
- package/web/app/index.html +203 -0
- package/web/app/index.html~ +152 -0
- package/web/app/keys-browser.mjs +46 -0
- package/web/app/keystore.mjs +99 -0
- package/web/app/oidc-session.mjs +189 -0
- package/web/app/pod-auth.mjs +248 -0
- package/web/app/pod-remote.mjs +84 -0
- package/web/app/shims/_globals.mjs +2 -0
- package/web/app/shims/fedify-sig.mjs +77 -0
- package/web/app/shims/node-crypto.mjs +113 -0
- package/web/app/shims/node-fs.mjs +17 -0
- package/web/app/shims/node-path.mjs +19 -0
- package/web/app/shims/node-url.mjs +7 -0
- package/web/app/shims/prelude.js +58 -0
- package/web/app/shims/safefetch.mjs +58 -0
- package/web/app/shims/shapes-text.mjs +8 -0
- package/web/app/shims/web-push.mjs +14 -0
- package/web/app/signup.mjs +248 -0
- package/web/app/site/_headers +9 -0
- package/web/app/site/_redirects +21 -0
- package/web/app/site/admin/actors.js +145 -0
- package/web/app/site/admin/bar.css +60 -0
- package/web/app/site/admin/bar.js +61 -0
- package/web/app/site/admin/client/client.js +66 -0
- package/web/app/site/admin/client/index.html +43 -0
- package/web/app/site/admin/common.js +23 -0
- package/web/app/site/admin/connections.js +112 -0
- package/web/app/site/admin/gateway.js +111 -0
- package/web/app/site/admin/group.js +258 -0
- package/web/app/site/admin/index.html +562 -0
- package/web/app/site/admin/record.js +378 -0
- package/web/app/site/admin/setup/index.html +192 -0
- package/web/app/site/admin/setup/setup.js +297 -0
- package/web/app/site/admin/tokens.css +56 -0
- package/web/app/site/admin/upkeep.js +170 -0
- package/web/app/site/admin/window.css +55 -0
- package/web/app/site/admin/window.js +136 -0
- package/web/app/site/app/404.html +40 -0
- package/web/app/site/app/_headers +2 -0
- package/web/app/site/app/apple-touch-icon.png +0 -0
- package/web/app/site/app/assets/ICONS-DIPJeU0C.js +2 -0
- package/web/app/site/app/assets/ICONS-DIPJeU0C.js.map +1 -0
- package/web/app/site/app/assets/ScheduledAtField-D-Abfo4J.js +183 -0
- package/web/app/site/app/assets/ScheduledAtField-D-Abfo4J.js.map +1 -0
- package/web/app/site/app/assets/Temml-CCjoacWt.woff2 +0 -0
- package/web/app/site/app/assets/__vite-browser-external-C7iut881.js +2 -0
- package/web/app/site/app/assets/__vite-browser-external-C7iut881.js.map +1 -0
- package/web/app/site/app/assets/boosts-carousel-BiOaNdMT.jpg +0 -0
- package/web/app/site/app/assets/catch-up-O5q5C75Z.png +0 -0
- package/web/app/site/app/assets/chunk-aKtaBQYM.js +1 -0
- package/web/app/site/app/assets/compose-CFEx7wb4.js +2 -0
- package/web/app/site/app/assets/compose-CFEx7wb4.js.map +1 -0
- package/web/app/site/app/assets/compose-ZJN5xSb4.js +35 -0
- package/web/app/site/app/assets/compose-ZJN5xSb4.js.map +1 -0
- package/web/app/site/app/assets/debug.module-YFQgwnIr.js +52 -0
- package/web/app/site/app/assets/debug.module-YFQgwnIr.js.map +1 -0
- package/web/app/site/app/assets/dom-p1YcJ5Rw.js +2 -0
- package/web/app/site/app/assets/dom-p1YcJ5Rw.js.map +1 -0
- package/web/app/site/app/assets/home-mobile-dark@2x-Dc-J8-SM.png +0 -0
- package/web/app/site/app/assets/home-mobile-light@2x-Bd3fod8m.png +0 -0
- package/web/app/site/app/assets/home-tablet-dark@2x-BgcNI8le.png +0 -0
- package/web/app/site/app/assets/home-tablet-light@2x-BW2RV9T6.png +0 -0
- package/web/app/site/app/assets/icons/add-circle-line-WD0mwORD.js +1 -0
- package/web/app/site/app/assets/icons/alert-line-CpFUMf9s.js +1 -0
- package/web/app/site/app/assets/icons/android-2-line-Bzxnrd3S.js +1 -0
- package/web/app/site/app/assets/icons/announcement-line-5nwaJFJm.js +1 -0
- package/web/app/site/app/assets/icons/arrow-down-circle-line-a7Id9-Iv.js +1 -0
- package/web/app/site/app/assets/icons/arrow-down-line-B8uzXzq6.js +1 -0
- package/web/app/site/app/assets/icons/arrow-left-line-CGrGT9C-.js +1 -0
- package/web/app/site/app/assets/icons/arrow-right-line-CE-HERfX.js +1 -0
- package/web/app/site/app/assets/icons/arrow-to-up-line-CvdQjmcy.js +1 -0
- package/web/app/site/app/assets/icons/arrow-up-circle-line-DEparSNF.js +1 -0
- package/web/app/site/app/assets/icons/arrow-up-line-C7yBeugc.js +1 -0
- package/web/app/site/app/assets/icons/arrows-right-line-BtbXmJGP.js +1 -0
- package/web/app/site/app/assets/icons/at-line-CF66Bznk.js +1 -0
- package/web/app/site/app/assets/icons/attachment-line-BY8It-l9.js +1 -0
- package/web/app/site/app/assets/icons/blockquote-line-BS6pU_hm.js +1 -0
- package/web/app/site/app/assets/icons/board-line-C0UWXyA6.js +1 -0
- package/web/app/site/app/assets/icons/bookmark-line-1CtX5JLF.js +1 -0
- package/web/app/site/app/assets/icons/building-5-line-Dd59MzbO.js +1 -0
- package/web/app/site/app/assets/icons/bus-2-line-6_dvA0GG.js +1 -0
- package/web/app/site/app/assets/icons/calendar-day-line-Crpl_fmu.js +1 -0
- package/web/app/site/app/assets/icons/calendar-month-line-vommts2C.js +1 -0
- package/web/app/site/app/assets/icons/calendar-time-add-line-BU_DTlQk.js +1 -0
- package/web/app/site/app/assets/icons/camera-line-Cv69MEei.js +1 -0
- package/web/app/site/app/assets/icons/celebrate-line-B8DvIoi0.js +1 -0
- package/web/app/site/app/assets/icons/chart-bar-line-FK-QOmtF.js +1 -0
- package/web/app/site/app/assets/icons/chart-line-line-BGcJAFWw.js +1 -0
- package/web/app/site/app/assets/icons/chat-3-line-YFK2-hwz.js +1 -0
- package/web/app/site/app/assets/icons/check-circle-line-DsAxFr5-.js +1 -0
- package/web/app/site/app/assets/icons/clipboard-line-BBp364gJ.js +1 -0
- package/web/app/site/app/assets/icons/close-circle-line-CICdiuGz.js +1 -0
- package/web/app/site/app/assets/icons/close-line-DYrFWcjv.js +1 -0
- package/web/app/site/app/assets/icons/cloud-line-tiQAbah8.js +1 -0
- package/web/app/site/app/assets/icons/code-line-fAl8iiV3.js +1 -0
- package/web/app/site/app/assets/icons/comment-2-line-CR3g7WeB.js +1 -0
- package/web/app/site/app/assets/icons/copy-2-line-CLW69yF5.js +1 -0
- package/web/app/site/app/assets/icons/delete-2-line-B8xmbr4n.js +1 -0
- package/web/app/site/app/assets/icons/document-3-line-DBGNhAg2.js +1 -0
- package/web/app/site/app/assets/icons/document-line-C7mb9sb7.js +1 -0
- package/web/app/site/app/assets/icons/down-line-BGqSSThd.js +1 -0
- package/web/app/site/app/assets/icons/edit-4-line-DcgGaEhx.js +1 -0
- package/web/app/site/app/assets/icons/emoji-2-line-D8YgnM-y.js +1 -0
- package/web/app/site/app/assets/icons/exit-line-Dc07BS-x.js +1 -0
- package/web/app/site/app/assets/icons/external-link-line-BvpSkmjO.js +1 -0
- package/web/app/site/app/assets/icons/eye-2-line-CiWXx-f_.js +1 -0
- package/web/app/site/app/assets/icons/eye-close-line-BgTaSJ4P.js +1 -0
- package/web/app/site/app/assets/icons/filter-2-line-B_mR2usy.js +1 -0
- package/web/app/site/app/assets/icons/filter-line-C_nm-3cU.js +1 -0
- package/web/app/site/app/assets/icons/flag-1-line-C2HB05I0.js +1 -0
- package/web/app/site/app/assets/icons/forbid-circle-line-D3LTT9yd.js +1 -0
- package/web/app/site/app/assets/icons/formula-line-DLrt2d6j.js +1 -0
- package/web/app/site/app/assets/icons/grid-line-CgUmEikf.js +1 -0
- package/web/app/site/app/assets/icons/group-line-C5fnX0wU.js +1 -0
- package/web/app/site/app/assets/icons/hand-finger-2-line-BGgxbuL9.js +1 -0
- package/web/app/site/app/assets/icons/hashtag-line-CyTEEXBb.js +1 -0
- package/web/app/site/app/assets/icons/heart-crack-line-BZddbO2C.js +1 -0
- package/web/app/site/app/assets/icons/heart-line-CJ7K47J4.js +1 -0
- package/web/app/site/app/assets/icons/history-2-line-kWWvFzqv.js +1 -0
- package/web/app/site/app/assets/icons/history-line-bGQiwbTH.js +1 -0
- package/web/app/site/app/assets/icons/home-3-line-fRU1zeLG.js +1 -0
- package/web/app/site/app/assets/icons/information-line-iNk6-sOY.js +1 -0
- package/web/app/site/app/assets/icons/keyboard-line-DsCyuQNz.js +1 -0
- package/web/app/site/app/assets/icons/layout-4-line-CZkOX52Z.js +1 -0
- package/web/app/site/app/assets/icons/layout-5-line-B_znt5wO.js +1 -0
- package/web/app/site/app/assets/icons/left-line-eUw0tya4.js +1 -0
- package/web/app/site/app/assets/icons/lightning-line-DaKoDdix.js +1 -0
- package/web/app/site/app/assets/icons/link-2-line-DCqLmMA7.js +1 -0
- package/web/app/site/app/assets/icons/list-check-line-BAxosBh9.js +1 -0
- package/web/app/site/app/assets/icons/lock-line-CPoaXeUW.js +1 -0
- package/web/app/site/app/assets/icons/mail-line-B0P8aa1L.js +1 -0
- package/web/app/site/app/assets/icons/moon-line-CEqNAToP.js +1 -0
- package/web/app/site/app/assets/icons/more-1-fill-Dw2e5xab.js +1 -0
- package/web/app/site/app/assets/icons/more-3-line-BIiVjYTz.js +1 -0
- package/web/app/site/app/assets/icons/notification-line-BZZq2Gtu.js +1 -0
- package/web/app/site/app/assets/icons/pencil-line-CKh8-A1v.js +1 -0
- package/web/app/site/app/assets/icons/photo-album-line-Bnfg2u40.js +1 -0
- package/web/app/site/app/assets/icons/pin-line-DpTolyhs.js +1 -0
- package/web/app/site/app/assets/icons/play-fill-B0-G_8lX.js +1 -0
- package/web/app/site/app/assets/icons/qrcode-2-line-B6SPCaHn.js +1 -0
- package/web/app/site/app/assets/icons/quill-pen-line-B6LUrnCP.js +1 -0
- package/web/app/site/app/assets/icons/quote-left-fill-hQZ1cj_6.js +1 -0
- package/web/app/site/app/assets/icons/radar-line-CKyroTth.js +1 -0
- package/web/app/site/app/assets/icons/react-line-CoIKhhtK.js +1 -0
- package/web/app/site/app/assets/icons/refresh-2-line-D0mTF97S.js +1 -0
- package/web/app/site/app/assets/icons/right-line-B3KWJaqc.js +1 -0
- package/web/app/site/app/assets/icons/rocket-line-4KZl537D.js +1 -0
- package/web/app/site/app/assets/icons/round-fill-C69NTrZ0.js +1 -0
- package/web/app/site/app/assets/icons/round-line-B9k_1uo-.js +1 -0
- package/web/app/site/app/assets/icons/route-line-B28ReHcE.js +1 -0
- package/web/app/site/app/assets/icons/rows-4-line-CrBniagM.js +1 -0
- package/web/app/site/app/assets/icons/scan-line-DSf74YdP.js +1 -0
- package/web/app/site/app/assets/icons/search-2-line-B6geIU1z.js +1 -0
- package/web/app/site/app/assets/icons/settings-3-line-azJwK47w.js +1 -0
- package/web/app/site/app/assets/icons/settings-6-line-C_5zc5YR.js +1 -0
- package/web/app/site/app/assets/icons/share-2-line-D5MxWjrm.js +1 -0
- package/web/app/site/app/assets/icons/share-forward-line-Cp61fZzZ.js +1 -0
- package/web/app/site/app/assets/icons/sparkles-2-line-DYYyXTaW.js +1 -0
- package/web/app/site/app/assets/icons/sparkles-line-C2hY1s6I.js +1 -0
- package/web/app/site/app/assets/icons/time-line-BF3eet1v.js +1 -0
- package/web/app/site/app/assets/icons/transfer-4-line-oisfvTBw.js +1 -0
- package/web/app/site/app/assets/icons/translate-line-BFWol2Ce.js +1 -0
- package/web/app/site/app/assets/icons/unlock-line-X_x5vwv3.js +1 -0
- package/web/app/site/app/assets/icons/upload-3-line-DgzwUJeW.js +1 -0
- package/web/app/site/app/assets/icons/user-4-line-BVSynZCp.js +1 -0
- package/web/app/site/app/assets/icons/user-add-2-line-BlGS5Q8q.js +1 -0
- package/web/app/site/app/assets/icons/user-add-line-BbXdHG4z.js +1 -0
- package/web/app/site/app/assets/icons/user-edit-line-BFAHOV0X.js +1 -0
- package/web/app/site/app/assets/icons/user-follow-line-BXux0wSd.js +1 -0
- package/web/app/site/app/assets/icons/user-star-line-DdYlpHLU.js +1 -0
- package/web/app/site/app/assets/icons/user-warning-line-Cj43j0_E.js +1 -0
- package/web/app/site/app/assets/icons/user-x-line-DIDgNlPH.js +1 -0
- package/web/app/site/app/assets/icons/volume-line-Bs390KQt.js +1 -0
- package/web/app/site/app/assets/icons/volume-mute-line-DzlzIqFZ.js +1 -0
- package/web/app/site/app/assets/icons/walk-line-yNXfB-md.js +1 -0
- package/web/app/site/app/assets/icons/world-2-line-CenkGIwj.js +1 -0
- package/web/app/site/app/assets/icons/zoom-in-line-CaENUphf.js +1 -0
- package/web/app/site/app/assets/icons/zoom-out-line-BDNk0EWo.js +1 -0
- package/web/app/site/app/assets/instances-BNPptnn-.json +333 -0
- package/web/app/site/app/assets/locales/ar-SA-CfheRbCu.js +1 -0
- package/web/app/site/app/assets/locales/ca-ES-DHRW86Tz.js +1 -0
- package/web/app/site/app/assets/locales/cs-CZ-CKoqEuA1.js +1 -0
- package/web/app/site/app/assets/locales/de-DE-ByRPMRFy.js +1 -0
- package/web/app/site/app/assets/locales/eo-UY-CPC7wLCS.js +1 -0
- package/web/app/site/app/assets/locales/es-ES-dJa0Fd9t.js +1 -0
- package/web/app/site/app/assets/locales/eu-ES-C3djlPR2.js +1 -0
- package/web/app/site/app/assets/locales/fa-IR-DTT490Ck.js +1 -0
- package/web/app/site/app/assets/locales/fi-FI-B23MCPuz.js +1 -0
- package/web/app/site/app/assets/locales/fr-FR-BcaQsO5C.js +1 -0
- package/web/app/site/app/assets/locales/gl-ES-BR-CVKBN.js +1 -0
- package/web/app/site/app/assets/locales/he-IL-CfheRbCu.js +1 -0
- package/web/app/site/app/assets/locales/hu-HU-CfheRbCu.js +1 -0
- package/web/app/site/app/assets/locales/it-IT-C1136UFV.js +1 -0
- package/web/app/site/app/assets/locales/ja-JP-CRZmRAhD.js +1 -0
- package/web/app/site/app/assets/locales/kab-Cz8xH_TT.js +1 -0
- package/web/app/site/app/assets/locales/ko-KR-CG9UiY4o.js +1 -0
- package/web/app/site/app/assets/locales/lt-LT-CJcI6sHZ.js +1 -0
- package/web/app/site/app/assets/locales/nb-NO-CfheRbCu.js +1 -0
- package/web/app/site/app/assets/locales/nl-NL-DxN4Yc1t.js +1 -0
- package/web/app/site/app/assets/locales/oc-FR-CfheRbCu.js +1 -0
- package/web/app/site/app/assets/locales/pl-PL-DkwGXf6P.js +1 -0
- package/web/app/site/app/assets/locales/pseudo-LOCALE-CQ4XxkQ0.js +1 -0
- package/web/app/site/app/assets/locales/pt-BR-nGL0HdFS.js +1 -0
- package/web/app/site/app/assets/locales/pt-PT-Cp17aj2z.js +1 -0
- package/web/app/site/app/assets/locales/ru-RU-0Y8cIoaK.js +1 -0
- package/web/app/site/app/assets/locales/th-TH-CfheRbCu.js +1 -0
- package/web/app/site/app/assets/locales/tok-CfheRbCu.js +1 -0
- package/web/app/site/app/assets/locales/tr-TR-CfheRbCu.js +1 -0
- package/web/app/site/app/assets/locales/uk-UA-C5E3dFBf.js +1 -0
- package/web/app/site/app/assets/locales/zh-CN-BHJi7_et.js +1 -0
- package/web/app/site/app/assets/locales/zh-TW-CfheRbCu.js +1 -0
- package/web/app/site/app/assets/main-BdqNbG-a.js +26 -0
- package/web/app/site/app/assets/main-BdqNbG-a.js.map +1 -0
- package/web/app/site/app/assets/mock-home-DGhR__He.js +2 -0
- package/web/app/site/app/assets/mock-home-DGhR__He.js.map +1 -0
- package/web/app/site/app/assets/multi-column-ROck0NVt.jpg +0 -0
- package/web/app/site/app/assets/multi-hashtag-timeline-DH5INVHi.jpg +0 -0
- package/web/app/site/app/assets/nested-comments-thread-hv59kZLc.jpg +0 -0
- package/web/app/site/app/assets/polyfill-force-H8sUkLGp.js +2 -0
- package/web/app/site/app/assets/polyfill-force-H8sUkLGp.js.map +1 -0
- package/web/app/site/app/assets/pwa-viewport-ogKbF51K.js +20 -0
- package/web/app/site/app/assets/pwa-viewport-ogKbF51K.js.map +1 -0
- package/web/app/site/app/assets/style-B1iLPTPR.css +2 -0
- package/web/app/site/app/assets/temml-DuyIQMei.js +97 -0
- package/web/app/site/app/assets/temml-DuyIQMei.js.map +1 -0
- package/web/app/site/app/assets/tinyld.light.node-DtbBhLYC.js +2 -0
- package/web/app/site/app/assets/tinyld.light.node-DtbBhLYC.js.map +1 -0
- package/web/app/site/app/assets/year-in-posts-B2lywqdx.png +0 -0
- package/web/app/site/app/assets/year-in-posts-DJv_star.js +3 -0
- package/web/app/site/app/assets/year-in-posts-DJv_star.js.map +1 -0
- package/web/app/site/app/compose/index.html +20 -0
- package/web/app/site/app/favicon.ico +0 -0
- package/web/app/site/app/index.html +73 -0
- package/web/app/site/app/logo-192.png +0 -0
- package/web/app/site/app/logo-512.png +0 -0
- package/web/app/site/app/logo-badge-72.png +0 -0
- package/web/app/site/app/logo-maskable-512.png +0 -0
- package/web/app/site/app/logo-monochrome-512.png +0 -0
- package/web/app/site/app/logo-monochrome-maskable-512.png +0 -0
- package/web/app/site/app/manifest.webmanifest +1 -0
- package/web/app/site/app/og-image-2.jpg +0 -0
- package/web/app/site/app/og-image.png +0 -0
- package/web/app/site/app/robots.txt +2 -0
- package/web/app/site/app/version.json +1 -0
- package/web/app/site/boot.js +34067 -0
- package/web/app/site/index.html +203 -0
- package/web/app/site/sw.js +70014 -0
- package/web/app/sw-src.mjs +220 -0
- package/web/front/#new-account.html# +43 -0
- package/web/front/admin.html +1 -81
- package/web/front/admin.js +85 -0
- package/web/front/new-account.html +1 -9
- package/web/front/new-account.js +13 -0
- package/web/front/run.html +1 -92
- package/web/front/run.js +107 -0
- package/lib/admin.mjs +0 -1913
- package/lib/intake.mjs +0 -1764
- package/lib/mastoapi.mjs +0 -1947
- package/lib/podrdf.mjs +0 -132
- package/lib/publisher.mjs +0 -1263
- package/web/admin/admin.js +0 -1114
- /package/lib/{oidc-auth.mjs → client/oidc-auth.mjs} +0 -0
- /package/lib/{webpush.mjs → client/webpush.mjs} +0 -0
- /package/lib/{bskyfeed.mjs → connections/bskyfeed.mjs} +0 -0
- /package/lib/{lease.mjs → core/lease.mjs} +0 -0
- /package/lib/{polls.mjs → core/polls.mjs} +0 -0
- /package/lib/{proof.mjs → core/proof.mjs} +0 -0
- /package/lib/{account.mjs → device/account.mjs} +0 -0
- /package/lib/{certs.mjs → device/certs.mjs} +0 -0
- /package/lib/{home.mjs → device/home.mjs} +0 -0
- /package/lib/{ports.mjs → device/ports.mjs} +0 -0
- /package/lib/{guard.mjs → shared/guard.mjs} +0 -0
- /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('
|
|
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
|
-
|
|
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
|
|
341
|
-
await agent.remote
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
34
|
-
//
|
|
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.
|
|
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
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
// the
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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 = { '&': '&', '<': '<', '>': '>' };
|
|
@@ -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
|
+
}
|