fedipod-server 0.29.0 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -6
- package/dist/door.d.ts +4 -0
- package/dist/door.js +31 -0
- package/dist/fedipod.d.ts +16 -0
- package/dist/fedipod.js +13 -0
- package/dist/front.d.ts +3 -0
- package/dist/front.js +94 -0
- package/dist/handler.d.ts +36 -101
- package/dist/handler.js +85 -645
- package/dist/handler.jsonld +2 -30
- package/dist/identities.d.ts +62 -0
- package/dist/identities.js +236 -0
- package/dist/mounts.d.ts +43 -0
- package/dist/mounts.js +123 -0
- package/dist/opt-in.d.ts +46 -0
- package/dist/opt-in.js +176 -0
- package/package.json +10 -18
- package/lib/client/c2s.mjs +0 -636
- package/lib/client/localapi.mjs +0 -87
- package/lib/client/masto/accounts.mjs +0 -310
- package/lib/client/masto/body.mjs +0 -156
- package/lib/client/masto/index.mjs +0 -206
- package/lib/client/masto/instance.mjs +0 -112
- package/lib/client/masto/media.mjs +0 -79
- package/lib/client/masto/oauth.mjs +0 -656
- package/lib/client/masto/render.mjs +0 -518
- package/lib/client/masto/statuses.mjs +0 -372
- package/lib/client/masto/timelines.mjs +0 -438
- package/lib/client/oidc-auth.mjs +0 -62
- package/lib/client/streaming.mjs +0 -154
- package/lib/client/webpush.mjs +0 -98
- package/lib/connections/acctfeed.mjs +0 -257
- package/lib/connections/atproto.mjs +0 -309
- package/lib/connections/bskyfeed.mjs +0 -243
- package/lib/connections/bskygroup.mjs +0 -162
- package/lib/connections/fediacct.mjs +0 -253
- package/lib/connections/import.mjs +0 -357
- package/lib/connections/tagfeed.mjs +0 -212
- package/lib/connections/vault.mjs +0 -114
- package/lib/core/as2.mjs +0 -206
- package/lib/core/contexts/activitystreams.json +0 -379
- package/lib/core/contexts/anno.json +0 -126
- package/lib/core/contexts/did-v1.json +0 -57
- package/lib/core/contexts/fep-5711.json +0 -36
- package/lib/core/contexts/gotosocial.json +0 -86
- package/lib/core/contexts/identity-v1.json +0 -152
- package/lib/core/contexts/index.mjs +0 -63
- package/lib/core/contexts/join-lemmy.json +0 -33
- package/lib/core/contexts/joinmastodon.json +0 -28
- package/lib/core/contexts/litepub-0.1.json +0 -60
- package/lib/core/contexts/map.json +0 -19
- package/lib/core/contexts/miscellany.json +0 -19
- package/lib/core/contexts/quotes.json +0 -18
- package/lib/core/contexts/schemaorg.json +0 -8845
- package/lib/core/contexts/security-data-integrity-v1.json +0 -78
- package/lib/core/contexts/security-data-integrity-v2.json +0 -81
- package/lib/core/contexts/security-multikey-v1.json +0 -35
- package/lib/core/contexts/security-v1.json +0 -74
- package/lib/core/contexts/webfinger.json +0 -10
- package/lib/core/deliver.mjs +0 -387
- package/lib/core/graphview.mjs +0 -327
- package/lib/core/intake/activities.mjs +0 -649
- package/lib/core/intake/activity.mjs +0 -318
- package/lib/core/intake/channel.mjs +0 -144
- package/lib/core/intake/group.mjs +0 -258
- package/lib/core/intake/index.mjs +0 -732
- package/lib/core/intake/notes.mjs +0 -367
- package/lib/core/intake/verify.mjs +0 -142
- package/lib/core/keys.mjs +0 -153
- package/lib/core/lease.mjs +0 -206
- package/lib/core/place.mjs +0 -82
- package/lib/core/polls.mjs +0 -105
- package/lib/core/profile-page.mjs +0 -104
- package/lib/core/proof.mjs +0 -79
- package/lib/core/publisher/collections.mjs +0 -272
- package/lib/core/publisher/index.mjs +0 -522
- package/lib/core/publisher/notes.mjs +0 -519
- package/lib/core/publisher/own.mjs +0 -143
- package/lib/core/publisher/questions.mjs +0 -241
- package/lib/core/publisher/restore.mjs +0 -224
- package/lib/core/shapes/activitystreams.ttl +0 -157
- package/lib/core/shapes/index.mjs +0 -132
- package/lib/core/shapes/shapes-text.mjs +0 -13
- package/lib/core/social.mjs +0 -561
- package/lib/core/storage.mjs +0 -176
- package/lib/core/store.mjs +0 -578
- package/lib/core/wire-quotes.mjs +0 -71
- package/lib/core/wire.mjs +0 -925
- package/lib/device/account.mjs +0 -87
- package/lib/device/admin/index.mjs +0 -13
- package/lib/device/admin/origins.mjs +0 -35
- package/lib/device/admin/routes/connections.mjs +0 -144
- package/lib/device/admin/routes/gateway.mjs +0 -199
- package/lib/device/admin/routes/lifecycle.mjs +0 -200
- package/lib/device/admin/routes/owner.mjs +0 -322
- package/lib/device/admin/routes/setup.mjs +0 -428
- package/lib/device/admin/routes/social.mjs +0 -188
- package/lib/device/admin/server.mjs +0 -95
- package/lib/device/admin/static.mjs +0 -247
- package/lib/device/admin/surface.mjs +0 -315
- package/lib/device/certs.mjs +0 -281
- package/lib/device/cli/commands/account.mjs +0 -586
- package/lib/device/cli/commands/run.mjs +0 -278
- package/lib/device/cli/commands/service.mjs +0 -221
- package/lib/device/cli/commands/setup.mjs +0 -489
- package/lib/device/cli/commands/state.mjs +0 -561
- package/lib/device/cli/context.mjs +0 -288
- package/lib/device/export-collections.mjs +0 -161
- package/lib/device/gateway-move.mjs +0 -58
- package/lib/device/home.mjs +0 -161
- package/lib/device/migrate.mjs +0 -115
- package/lib/device/ports.mjs +0 -27
- package/lib/device/remote.mjs +0 -54
- package/lib/device/setup.mjs +0 -496
- package/lib/device/update.mjs +0 -96
- package/lib/gateway/caches.mjs +0 -36
- package/lib/gateway/directory.mjs +0 -90
- package/lib/gateway/front-core.mjs +0 -966
- package/lib/gateway/gateway-core.mjs +0 -244
- package/lib/gateway/headers.mjs +0 -49
- package/lib/gateway/httpsig.mjs +0 -159
- package/lib/gateway/notices.mjs +0 -84
- package/lib/gateway/quiet.mjs +0 -189
- package/lib/gateway/relay-extras.mjs +0 -89
- package/lib/gateway/token-claims.mjs +0 -16
- package/lib/pod/README.md +0 -61
- package/lib/pod/actor.mjs +0 -96
- package/lib/pod/collection.mjs +0 -77
- package/lib/pod/containers.mjs +0 -140
- package/lib/pod/discovery.mjs +0 -46
- package/lib/pod/featured.mjs +0 -18
- package/lib/pod/followers.mjs +0 -24
- package/lib/pod/following.mjs +0 -10
- package/lib/pod/http.mjs +0 -74
- package/lib/pod/inbox.mjs +0 -166
- package/lib/pod/links.mjs +0 -35
- package/lib/pod/location.mjs +0 -52
- package/lib/pod/media.mjs +0 -27
- package/lib/pod/notes.mjs +0 -78
- package/lib/pod/notifications.mjs +0 -89
- package/lib/pod/outbox.mjs +0 -18
- package/lib/pod/package.json +0 -19
- package/lib/pod/policy.mjs +0 -58
- package/lib/pod/private.mjs +0 -19
- package/lib/pod/root.mjs +0 -104
- package/lib/pod/state.mjs +0 -40
- package/lib/pod/transport.mjs +0 -674
- package/lib/pod/type-index.mjs +0 -101
- package/lib/pod/urls.mjs +0 -91
- package/lib/server/embed.mjs +0 -424
- package/lib/session/README.md +0 -137
- package/lib/session/demo.html +0 -94
- package/lib/session/fedi-account.mjs +0 -417
- package/lib/session/fedi-login.mjs +0 -123
- package/lib/session/oidc-session.mjs +0 -233
- package/lib/session/package.json +0 -49
- package/lib/shared/guard.mjs +0 -213
- package/lib/shared/links.mjs +0 -9
- package/lib/shared/safefetch.mjs +0 -235
- package/lib/shared/ua.mjs +0 -29
- package/phanpy/dist/404.html +0 -40
- package/phanpy/dist/_headers +0 -2
- package/phanpy/dist/apple-touch-icon.png +0 -0
- package/phanpy/dist/assets/ICONS-DIPJeU0C.js +0 -2
- package/phanpy/dist/assets/ICONS-DIPJeU0C.js.map +0 -1
- package/phanpy/dist/assets/ScheduledAtField-D-Abfo4J.js +0 -183
- package/phanpy/dist/assets/ScheduledAtField-D-Abfo4J.js.map +0 -1
- package/phanpy/dist/assets/Temml-CCjoacWt.woff2 +0 -0
- package/phanpy/dist/assets/__vite-browser-external-C7iut881.js +0 -2
- package/phanpy/dist/assets/__vite-browser-external-C7iut881.js.map +0 -1
- package/phanpy/dist/assets/boosts-carousel-BiOaNdMT.jpg +0 -0
- package/phanpy/dist/assets/catch-up-O5q5C75Z.png +0 -0
- package/phanpy/dist/assets/chunk-aKtaBQYM.js +0 -1
- package/phanpy/dist/assets/compose-CFEx7wb4.js +0 -2
- package/phanpy/dist/assets/compose-CFEx7wb4.js.map +0 -1
- package/phanpy/dist/assets/compose-ZJN5xSb4.js +0 -35
- package/phanpy/dist/assets/compose-ZJN5xSb4.js.map +0 -1
- package/phanpy/dist/assets/debug.module-YFQgwnIr.js +0 -52
- package/phanpy/dist/assets/debug.module-YFQgwnIr.js.map +0 -1
- package/phanpy/dist/assets/dom-p1YcJ5Rw.js +0 -2
- package/phanpy/dist/assets/dom-p1YcJ5Rw.js.map +0 -1
- package/phanpy/dist/assets/home-mobile-dark@2x-Dc-J8-SM.png +0 -0
- package/phanpy/dist/assets/home-mobile-light@2x-Bd3fod8m.png +0 -0
- package/phanpy/dist/assets/home-tablet-dark@2x-BgcNI8le.png +0 -0
- package/phanpy/dist/assets/home-tablet-light@2x-BW2RV9T6.png +0 -0
- package/phanpy/dist/assets/icons/add-circle-line-WD0mwORD.js +0 -1
- package/phanpy/dist/assets/icons/alert-line-CpFUMf9s.js +0 -1
- package/phanpy/dist/assets/icons/android-2-line-Bzxnrd3S.js +0 -1
- package/phanpy/dist/assets/icons/announcement-line-5nwaJFJm.js +0 -1
- package/phanpy/dist/assets/icons/arrow-down-circle-line-a7Id9-Iv.js +0 -1
- package/phanpy/dist/assets/icons/arrow-down-line-B8uzXzq6.js +0 -1
- package/phanpy/dist/assets/icons/arrow-left-line-CGrGT9C-.js +0 -1
- package/phanpy/dist/assets/icons/arrow-right-line-CE-HERfX.js +0 -1
- package/phanpy/dist/assets/icons/arrow-to-up-line-CvdQjmcy.js +0 -1
- package/phanpy/dist/assets/icons/arrow-up-circle-line-DEparSNF.js +0 -1
- package/phanpy/dist/assets/icons/arrow-up-line-C7yBeugc.js +0 -1
- package/phanpy/dist/assets/icons/arrows-right-line-BtbXmJGP.js +0 -1
- package/phanpy/dist/assets/icons/at-line-CF66Bznk.js +0 -1
- package/phanpy/dist/assets/icons/attachment-line-BY8It-l9.js +0 -1
- package/phanpy/dist/assets/icons/blockquote-line-BS6pU_hm.js +0 -1
- package/phanpy/dist/assets/icons/board-line-C0UWXyA6.js +0 -1
- package/phanpy/dist/assets/icons/bookmark-line-1CtX5JLF.js +0 -1
- package/phanpy/dist/assets/icons/building-5-line-Dd59MzbO.js +0 -1
- package/phanpy/dist/assets/icons/bus-2-line-6_dvA0GG.js +0 -1
- package/phanpy/dist/assets/icons/calendar-day-line-Crpl_fmu.js +0 -1
- package/phanpy/dist/assets/icons/calendar-month-line-vommts2C.js +0 -1
- package/phanpy/dist/assets/icons/calendar-time-add-line-BU_DTlQk.js +0 -1
- package/phanpy/dist/assets/icons/camera-line-Cv69MEei.js +0 -1
- package/phanpy/dist/assets/icons/celebrate-line-B8DvIoi0.js +0 -1
- package/phanpy/dist/assets/icons/chart-bar-line-FK-QOmtF.js +0 -1
- package/phanpy/dist/assets/icons/chart-line-line-BGcJAFWw.js +0 -1
- package/phanpy/dist/assets/icons/chat-3-line-YFK2-hwz.js +0 -1
- package/phanpy/dist/assets/icons/check-circle-line-DsAxFr5-.js +0 -1
- package/phanpy/dist/assets/icons/clipboard-line-BBp364gJ.js +0 -1
- package/phanpy/dist/assets/icons/close-circle-line-CICdiuGz.js +0 -1
- package/phanpy/dist/assets/icons/close-line-DYrFWcjv.js +0 -1
- package/phanpy/dist/assets/icons/cloud-line-tiQAbah8.js +0 -1
- package/phanpy/dist/assets/icons/code-line-fAl8iiV3.js +0 -1
- package/phanpy/dist/assets/icons/comment-2-line-CR3g7WeB.js +0 -1
- package/phanpy/dist/assets/icons/copy-2-line-CLW69yF5.js +0 -1
- package/phanpy/dist/assets/icons/delete-2-line-B8xmbr4n.js +0 -1
- package/phanpy/dist/assets/icons/document-3-line-DBGNhAg2.js +0 -1
- package/phanpy/dist/assets/icons/document-line-C7mb9sb7.js +0 -1
- package/phanpy/dist/assets/icons/down-line-BGqSSThd.js +0 -1
- package/phanpy/dist/assets/icons/edit-4-line-DcgGaEhx.js +0 -1
- package/phanpy/dist/assets/icons/emoji-2-line-D8YgnM-y.js +0 -1
- package/phanpy/dist/assets/icons/exit-line-Dc07BS-x.js +0 -1
- package/phanpy/dist/assets/icons/external-link-line-BvpSkmjO.js +0 -1
- package/phanpy/dist/assets/icons/eye-2-line-CiWXx-f_.js +0 -1
- package/phanpy/dist/assets/icons/eye-close-line-BgTaSJ4P.js +0 -1
- package/phanpy/dist/assets/icons/filter-2-line-B_mR2usy.js +0 -1
- package/phanpy/dist/assets/icons/filter-line-C_nm-3cU.js +0 -1
- package/phanpy/dist/assets/icons/flag-1-line-C2HB05I0.js +0 -1
- package/phanpy/dist/assets/icons/forbid-circle-line-D3LTT9yd.js +0 -1
- package/phanpy/dist/assets/icons/formula-line-DLrt2d6j.js +0 -1
- package/phanpy/dist/assets/icons/grid-line-CgUmEikf.js +0 -1
- package/phanpy/dist/assets/icons/group-line-C5fnX0wU.js +0 -1
- package/phanpy/dist/assets/icons/hand-finger-2-line-BGgxbuL9.js +0 -1
- package/phanpy/dist/assets/icons/hashtag-line-CyTEEXBb.js +0 -1
- package/phanpy/dist/assets/icons/heart-crack-line-BZddbO2C.js +0 -1
- package/phanpy/dist/assets/icons/heart-line-CJ7K47J4.js +0 -1
- package/phanpy/dist/assets/icons/history-2-line-kWWvFzqv.js +0 -1
- package/phanpy/dist/assets/icons/history-line-bGQiwbTH.js +0 -1
- package/phanpy/dist/assets/icons/home-3-line-fRU1zeLG.js +0 -1
- package/phanpy/dist/assets/icons/information-line-iNk6-sOY.js +0 -1
- package/phanpy/dist/assets/icons/keyboard-line-DsCyuQNz.js +0 -1
- package/phanpy/dist/assets/icons/layout-4-line-CZkOX52Z.js +0 -1
- package/phanpy/dist/assets/icons/layout-5-line-B_znt5wO.js +0 -1
- package/phanpy/dist/assets/icons/left-line-eUw0tya4.js +0 -1
- package/phanpy/dist/assets/icons/lightning-line-DaKoDdix.js +0 -1
- package/phanpy/dist/assets/icons/link-2-line-DCqLmMA7.js +0 -1
- package/phanpy/dist/assets/icons/list-check-line-BAxosBh9.js +0 -1
- package/phanpy/dist/assets/icons/lock-line-CPoaXeUW.js +0 -1
- package/phanpy/dist/assets/icons/mail-line-B0P8aa1L.js +0 -1
- package/phanpy/dist/assets/icons/moon-line-CEqNAToP.js +0 -1
- package/phanpy/dist/assets/icons/more-1-fill-Dw2e5xab.js +0 -1
- package/phanpy/dist/assets/icons/more-3-line-BIiVjYTz.js +0 -1
- package/phanpy/dist/assets/icons/notification-line-BZZq2Gtu.js +0 -1
- package/phanpy/dist/assets/icons/pencil-line-CKh8-A1v.js +0 -1
- package/phanpy/dist/assets/icons/photo-album-line-Bnfg2u40.js +0 -1
- package/phanpy/dist/assets/icons/pin-line-DpTolyhs.js +0 -1
- package/phanpy/dist/assets/icons/play-fill-B0-G_8lX.js +0 -1
- package/phanpy/dist/assets/icons/qrcode-2-line-B6SPCaHn.js +0 -1
- package/phanpy/dist/assets/icons/quill-pen-line-B6LUrnCP.js +0 -1
- package/phanpy/dist/assets/icons/quote-left-fill-hQZ1cj_6.js +0 -1
- package/phanpy/dist/assets/icons/radar-line-CKyroTth.js +0 -1
- package/phanpy/dist/assets/icons/react-line-CoIKhhtK.js +0 -1
- package/phanpy/dist/assets/icons/refresh-2-line-D0mTF97S.js +0 -1
- package/phanpy/dist/assets/icons/right-line-B3KWJaqc.js +0 -1
- package/phanpy/dist/assets/icons/rocket-line-4KZl537D.js +0 -1
- package/phanpy/dist/assets/icons/round-fill-C69NTrZ0.js +0 -1
- package/phanpy/dist/assets/icons/round-line-B9k_1uo-.js +0 -1
- package/phanpy/dist/assets/icons/route-line-B28ReHcE.js +0 -1
- package/phanpy/dist/assets/icons/rows-4-line-CrBniagM.js +0 -1
- package/phanpy/dist/assets/icons/scan-line-DSf74YdP.js +0 -1
- package/phanpy/dist/assets/icons/search-2-line-B6geIU1z.js +0 -1
- package/phanpy/dist/assets/icons/settings-3-line-azJwK47w.js +0 -1
- package/phanpy/dist/assets/icons/settings-6-line-C_5zc5YR.js +0 -1
- package/phanpy/dist/assets/icons/share-2-line-D5MxWjrm.js +0 -1
- package/phanpy/dist/assets/icons/share-forward-line-Cp61fZzZ.js +0 -1
- package/phanpy/dist/assets/icons/sparkles-2-line-DYYyXTaW.js +0 -1
- package/phanpy/dist/assets/icons/sparkles-line-C2hY1s6I.js +0 -1
- package/phanpy/dist/assets/icons/time-line-BF3eet1v.js +0 -1
- package/phanpy/dist/assets/icons/transfer-4-line-oisfvTBw.js +0 -1
- package/phanpy/dist/assets/icons/translate-line-BFWol2Ce.js +0 -1
- package/phanpy/dist/assets/icons/unlock-line-X_x5vwv3.js +0 -1
- package/phanpy/dist/assets/icons/upload-3-line-DgzwUJeW.js +0 -1
- package/phanpy/dist/assets/icons/user-4-line-BVSynZCp.js +0 -1
- package/phanpy/dist/assets/icons/user-add-2-line-BlGS5Q8q.js +0 -1
- package/phanpy/dist/assets/icons/user-add-line-BbXdHG4z.js +0 -1
- package/phanpy/dist/assets/icons/user-edit-line-BFAHOV0X.js +0 -1
- package/phanpy/dist/assets/icons/user-follow-line-BXux0wSd.js +0 -1
- package/phanpy/dist/assets/icons/user-star-line-DdYlpHLU.js +0 -1
- package/phanpy/dist/assets/icons/user-warning-line-Cj43j0_E.js +0 -1
- package/phanpy/dist/assets/icons/user-x-line-DIDgNlPH.js +0 -1
- package/phanpy/dist/assets/icons/volume-line-Bs390KQt.js +0 -1
- package/phanpy/dist/assets/icons/volume-mute-line-DzlzIqFZ.js +0 -1
- package/phanpy/dist/assets/icons/walk-line-yNXfB-md.js +0 -1
- package/phanpy/dist/assets/icons/world-2-line-CenkGIwj.js +0 -1
- package/phanpy/dist/assets/icons/zoom-in-line-CaENUphf.js +0 -1
- package/phanpy/dist/assets/icons/zoom-out-line-BDNk0EWo.js +0 -1
- package/phanpy/dist/assets/instances-BNPptnn-.json +0 -333
- package/phanpy/dist/assets/locales/ar-SA-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/locales/ca-ES-DHRW86Tz.js +0 -1
- package/phanpy/dist/assets/locales/cs-CZ-CKoqEuA1.js +0 -1
- package/phanpy/dist/assets/locales/de-DE-ByRPMRFy.js +0 -1
- package/phanpy/dist/assets/locales/eo-UY-CPC7wLCS.js +0 -1
- package/phanpy/dist/assets/locales/es-ES-dJa0Fd9t.js +0 -1
- package/phanpy/dist/assets/locales/eu-ES-C3djlPR2.js +0 -1
- package/phanpy/dist/assets/locales/fa-IR-DTT490Ck.js +0 -1
- package/phanpy/dist/assets/locales/fi-FI-B23MCPuz.js +0 -1
- package/phanpy/dist/assets/locales/fr-FR-BcaQsO5C.js +0 -1
- package/phanpy/dist/assets/locales/gl-ES-BR-CVKBN.js +0 -1
- package/phanpy/dist/assets/locales/he-IL-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/locales/hu-HU-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/locales/it-IT-C1136UFV.js +0 -1
- package/phanpy/dist/assets/locales/ja-JP-CRZmRAhD.js +0 -1
- package/phanpy/dist/assets/locales/kab-Cz8xH_TT.js +0 -1
- package/phanpy/dist/assets/locales/ko-KR-CG9UiY4o.js +0 -1
- package/phanpy/dist/assets/locales/lt-LT-CJcI6sHZ.js +0 -1
- package/phanpy/dist/assets/locales/nb-NO-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/locales/nl-NL-DxN4Yc1t.js +0 -1
- package/phanpy/dist/assets/locales/oc-FR-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/locales/pl-PL-DkwGXf6P.js +0 -1
- package/phanpy/dist/assets/locales/pseudo-LOCALE-CQ4XxkQ0.js +0 -1
- package/phanpy/dist/assets/locales/pt-BR-nGL0HdFS.js +0 -1
- package/phanpy/dist/assets/locales/pt-PT-Cp17aj2z.js +0 -1
- package/phanpy/dist/assets/locales/ru-RU-0Y8cIoaK.js +0 -1
- package/phanpy/dist/assets/locales/th-TH-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/locales/tok-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/locales/tr-TR-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/locales/uk-UA-C5E3dFBf.js +0 -1
- package/phanpy/dist/assets/locales/zh-CN-BHJi7_et.js +0 -1
- package/phanpy/dist/assets/locales/zh-TW-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/main-BdqNbG-a.js +0 -26
- package/phanpy/dist/assets/main-BdqNbG-a.js.map +0 -1
- package/phanpy/dist/assets/mock-home-DGhR__He.js +0 -2
- package/phanpy/dist/assets/mock-home-DGhR__He.js.map +0 -1
- package/phanpy/dist/assets/multi-column-ROck0NVt.jpg +0 -0
- package/phanpy/dist/assets/multi-hashtag-timeline-DH5INVHi.jpg +0 -0
- package/phanpy/dist/assets/nested-comments-thread-hv59kZLc.jpg +0 -0
- package/phanpy/dist/assets/polyfill-force-H8sUkLGp.js +0 -2
- package/phanpy/dist/assets/polyfill-force-H8sUkLGp.js.map +0 -1
- package/phanpy/dist/assets/pwa-viewport-ogKbF51K.js +0 -20
- package/phanpy/dist/assets/pwa-viewport-ogKbF51K.js.map +0 -1
- package/phanpy/dist/assets/style-B1iLPTPR.css +0 -2
- package/phanpy/dist/assets/temml-DuyIQMei.js +0 -97
- package/phanpy/dist/assets/temml-DuyIQMei.js.map +0 -1
- package/phanpy/dist/assets/tinyld.light.node-DtbBhLYC.js +0 -2
- package/phanpy/dist/assets/tinyld.light.node-DtbBhLYC.js.map +0 -1
- package/phanpy/dist/assets/year-in-posts-B2lywqdx.png +0 -0
- package/phanpy/dist/assets/year-in-posts-DJv_star.js +0 -3
- package/phanpy/dist/assets/year-in-posts-DJv_star.js.map +0 -1
- package/phanpy/dist/compose/index.html +0 -20
- package/phanpy/dist/favicon.ico +0 -0
- package/phanpy/dist/index.html +0 -73
- package/phanpy/dist/logo-192.png +0 -0
- package/phanpy/dist/logo-512.png +0 -0
- package/phanpy/dist/logo-badge-72.png +0 -0
- package/phanpy/dist/logo-maskable-512.png +0 -0
- package/phanpy/dist/logo-monochrome-512.png +0 -0
- package/phanpy/dist/logo-monochrome-maskable-512.png +0 -0
- package/phanpy/dist/manifest.webmanifest +0 -1
- package/phanpy/dist/og-image-2.jpg +0 -0
- package/phanpy/dist/og-image.png +0 -0
- package/phanpy/dist/robots.txt +0 -2
- package/phanpy/dist/sw.js +0 -2
- package/phanpy/dist/sw.js.map +0 -1
- package/phanpy/dist/version.json +0 -1
- package/run-agent.mjs +0 -899
- package/vendor/gate.cjs +0 -138
- package/vendor/idp-grant.cjs +0 -394
- package/vendor/jfetch.cjs +0 -16
- package/web/admin/actors.js +0 -147
- package/web/admin/bar.css +0 -126
- package/web/admin/bar.js +0 -99
- package/web/admin/client/client.js +0 -66
- package/web/admin/client/index.html +0 -69
- package/web/admin/client-pick.js +0 -79
- package/web/admin/common.js +0 -23
- package/web/admin/connections.js +0 -112
- package/web/admin/gateway.js +0 -133
- package/web/admin/group.js +0 -258
- package/web/admin/index.html +0 -612
- package/web/admin/notices-bar.js +0 -90
- package/web/admin/oauth-signin.mjs +0 -37
- package/web/admin/record.js +0 -393
- package/web/admin/setup/index.html +0 -257
- package/web/admin/setup/setup.js +0 -339
- package/web/admin/tokens.css +0 -68
- package/web/admin/upkeep.js +0 -177
- package/web/admin/window.css +0 -55
- package/web/admin/window.js +0 -136
- package/web/app/README.md +0 -81
- package/web/app/admin-facade.mjs +0 -588
- package/web/app/agent.mjs +0 -672
- package/web/app/atproto-browser.mjs +0 -86
- package/web/app/authorize_interaction.html +0 -56
- package/web/app/authorize_interaction.js +0 -69
- package/web/app/boot.mjs +0 -616
- package/web/app/deliver-relay.mjs +0 -129
- package/web/app/dist/boot.js +0 -34562
- package/web/app/dist/boot.js.map +0 -7
- package/web/app/dist/sw.js +0 -73456
- package/web/app/dist/sw.js.map +0 -7
- package/web/app/fediacct-browser.mjs +0 -146
- package/web/app/gateway-move.mjs +0 -60
- package/web/app/idb-kv.mjs +0 -35
- package/web/app/index.html +0 -258
- package/web/app/keys-browser.mjs +0 -71
- package/web/app/keystore.mjs +0 -82
- package/web/app/oidc-session.mjs +0 -6
- package/web/app/pod-auth.mjs +0 -248
- package/web/app/pod-remote.mjs +0 -84
- package/web/app/shims/_globals.mjs +0 -2
- package/web/app/shims/fedify-sig.mjs +0 -77
- package/web/app/shims/node-crypto.mjs +0 -113
- package/web/app/shims/node-fs.mjs +0 -17
- package/web/app/shims/node-path.mjs +0 -19
- package/web/app/shims/node-url.mjs +0 -7
- package/web/app/shims/prelude.js +0 -58
- package/web/app/shims/safefetch.mjs +0 -58
- package/web/app/shims/shapes-text.mjs +0 -8
- package/web/app/shims/web-push.mjs +0 -14
- package/web/app/signup.mjs +0 -357
- package/web/app/sw-src.mjs +0 -220
- package/web/app/update.js +0 -70
- package/web/front/admin.html +0 -55
- package/web/front/admin.js +0 -89
- package/web/front/install.sh +0 -69
- package/web/front/new-account.html +0 -35
- package/web/front/new-account.js +0 -13
- package/web/front/notices.html +0 -69
- package/web/front/notices.js +0 -107
- package/web/front/run.html +0 -64
- package/web/front/run.js +0 -221
- package/web/front/solid-oidc-client.js +0 -6
- package/web/icons/forum.svg +0 -11
- package/web/icons/gateway.svg +0 -14
- package/web/icons/local.svg +0 -11
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"@context": {
|
|
3
|
-
"id": "@id",
|
|
4
|
-
"type": "@type",
|
|
5
|
-
"@protected": true,
|
|
6
|
-
"digestMultibase": {
|
|
7
|
-
"@id": "https://w3id.org/security#digestMultibase",
|
|
8
|
-
"@type": "https://w3id.org/security#multibase"
|
|
9
|
-
},
|
|
10
|
-
"proof": {
|
|
11
|
-
"@id": "https://w3id.org/security#proof",
|
|
12
|
-
"@type": "@id",
|
|
13
|
-
"@container": "@graph"
|
|
14
|
-
},
|
|
15
|
-
"DataIntegrityProof": {
|
|
16
|
-
"@id": "https://w3id.org/security#DataIntegrityProof",
|
|
17
|
-
"@context": {
|
|
18
|
-
"@protected": true,
|
|
19
|
-
"id": "@id",
|
|
20
|
-
"type": "@type",
|
|
21
|
-
"challenge": "https://w3id.org/security#challenge",
|
|
22
|
-
"created": {
|
|
23
|
-
"@id": "http://purl.org/dc/terms/created",
|
|
24
|
-
"@type": "http://www.w3.org/2001/XMLSchema#dateTime"
|
|
25
|
-
},
|
|
26
|
-
"domain": "https://w3id.org/security#domain",
|
|
27
|
-
"expires": {
|
|
28
|
-
"@id": "https://w3id.org/security#expiration",
|
|
29
|
-
"@type": "http://www.w3.org/2001/XMLSchema#dateTime"
|
|
30
|
-
},
|
|
31
|
-
"nonce": "https://w3id.org/security#nonce",
|
|
32
|
-
"proofPurpose": {
|
|
33
|
-
"@id": "https://w3id.org/security#proofPurpose",
|
|
34
|
-
"@type": "@vocab",
|
|
35
|
-
"@context": {
|
|
36
|
-
"@protected": true,
|
|
37
|
-
"id": "@id",
|
|
38
|
-
"type": "@type",
|
|
39
|
-
"assertionMethod": {
|
|
40
|
-
"@id": "https://w3id.org/security#assertionMethod",
|
|
41
|
-
"@type": "@id",
|
|
42
|
-
"@container": "@set"
|
|
43
|
-
},
|
|
44
|
-
"authentication": {
|
|
45
|
-
"@id": "https://w3id.org/security#authenticationMethod",
|
|
46
|
-
"@type": "@id",
|
|
47
|
-
"@container": "@set"
|
|
48
|
-
},
|
|
49
|
-
"capabilityInvocation": {
|
|
50
|
-
"@id": "https://w3id.org/security#capabilityInvocationMethod",
|
|
51
|
-
"@type": "@id",
|
|
52
|
-
"@container": "@set"
|
|
53
|
-
},
|
|
54
|
-
"capabilityDelegation": {
|
|
55
|
-
"@id": "https://w3id.org/security#capabilityDelegationMethod",
|
|
56
|
-
"@type": "@id",
|
|
57
|
-
"@container": "@set"
|
|
58
|
-
},
|
|
59
|
-
"keyAgreement": {
|
|
60
|
-
"@id": "https://w3id.org/security#keyAgreementMethod",
|
|
61
|
-
"@type": "@id",
|
|
62
|
-
"@container": "@set"
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
},
|
|
66
|
-
"cryptosuite": "https://w3id.org/security#cryptosuite",
|
|
67
|
-
"proofValue": {
|
|
68
|
-
"@id": "https://w3id.org/security#proofValue",
|
|
69
|
-
"@type": "https://w3id.org/security#multibase"
|
|
70
|
-
},
|
|
71
|
-
"verificationMethod": {
|
|
72
|
-
"@id": "https://w3id.org/security#verificationMethod",
|
|
73
|
-
"@type": "@id"
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"@context": {
|
|
3
|
-
"id": "@id",
|
|
4
|
-
"type": "@type",
|
|
5
|
-
"@protected": true,
|
|
6
|
-
"proof": {
|
|
7
|
-
"@id": "https://w3id.org/security#proof",
|
|
8
|
-
"@type": "@id",
|
|
9
|
-
"@container": "@graph"
|
|
10
|
-
},
|
|
11
|
-
"DataIntegrityProof": {
|
|
12
|
-
"@id": "https://w3id.org/security#DataIntegrityProof",
|
|
13
|
-
"@context": {
|
|
14
|
-
"@protected": true,
|
|
15
|
-
"id": "@id",
|
|
16
|
-
"type": "@type",
|
|
17
|
-
"challenge": "https://w3id.org/security#challenge",
|
|
18
|
-
"created": {
|
|
19
|
-
"@id": "http://purl.org/dc/terms/created",
|
|
20
|
-
"@type": "http://www.w3.org/2001/XMLSchema#dateTime"
|
|
21
|
-
},
|
|
22
|
-
"domain": "https://w3id.org/security#domain",
|
|
23
|
-
"expires": {
|
|
24
|
-
"@id": "https://w3id.org/security#expiration",
|
|
25
|
-
"@type": "http://www.w3.org/2001/XMLSchema#dateTime"
|
|
26
|
-
},
|
|
27
|
-
"nonce": "https://w3id.org/security#nonce",
|
|
28
|
-
"previousProof": {
|
|
29
|
-
"@id": "https://w3id.org/security#previousProof",
|
|
30
|
-
"@type": "@id"
|
|
31
|
-
},
|
|
32
|
-
"proofPurpose": {
|
|
33
|
-
"@id": "https://w3id.org/security#proofPurpose",
|
|
34
|
-
"@type": "@vocab",
|
|
35
|
-
"@context": {
|
|
36
|
-
"@protected": true,
|
|
37
|
-
"id": "@id",
|
|
38
|
-
"type": "@type",
|
|
39
|
-
"assertionMethod": {
|
|
40
|
-
"@id": "https://w3id.org/security#assertionMethod",
|
|
41
|
-
"@type": "@id",
|
|
42
|
-
"@container": "@set"
|
|
43
|
-
},
|
|
44
|
-
"authentication": {
|
|
45
|
-
"@id": "https://w3id.org/security#authenticationMethod",
|
|
46
|
-
"@type": "@id",
|
|
47
|
-
"@container": "@set"
|
|
48
|
-
},
|
|
49
|
-
"capabilityInvocation": {
|
|
50
|
-
"@id": "https://w3id.org/security#capabilityInvocationMethod",
|
|
51
|
-
"@type": "@id",
|
|
52
|
-
"@container": "@set"
|
|
53
|
-
},
|
|
54
|
-
"capabilityDelegation": {
|
|
55
|
-
"@id": "https://w3id.org/security#capabilityDelegationMethod",
|
|
56
|
-
"@type": "@id",
|
|
57
|
-
"@container": "@set"
|
|
58
|
-
},
|
|
59
|
-
"keyAgreement": {
|
|
60
|
-
"@id": "https://w3id.org/security#keyAgreementMethod",
|
|
61
|
-
"@type": "@id",
|
|
62
|
-
"@container": "@set"
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
},
|
|
66
|
-
"cryptosuite": {
|
|
67
|
-
"@id": "https://w3id.org/security#cryptosuite",
|
|
68
|
-
"@type": "https://w3id.org/security#cryptosuiteString"
|
|
69
|
-
},
|
|
70
|
-
"proofValue": {
|
|
71
|
-
"@id": "https://w3id.org/security#proofValue",
|
|
72
|
-
"@type": "https://w3id.org/security#multibase"
|
|
73
|
-
},
|
|
74
|
-
"verificationMethod": {
|
|
75
|
-
"@id": "https://w3id.org/security#verificationMethod",
|
|
76
|
-
"@type": "@id"
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"@context": {
|
|
3
|
-
"id": "@id",
|
|
4
|
-
"type": "@type",
|
|
5
|
-
"@protected": true,
|
|
6
|
-
"Multikey": {
|
|
7
|
-
"@id": "https://w3id.org/security#Multikey",
|
|
8
|
-
"@context": {
|
|
9
|
-
"@protected": true,
|
|
10
|
-
"id": "@id",
|
|
11
|
-
"type": "@type",
|
|
12
|
-
"controller": {
|
|
13
|
-
"@id": "https://w3id.org/security#controller",
|
|
14
|
-
"@type": "@id"
|
|
15
|
-
},
|
|
16
|
-
"revoked": {
|
|
17
|
-
"@id": "https://w3id.org/security#revoked",
|
|
18
|
-
"@type": "http://www.w3.org/2001/XMLSchema#dateTime"
|
|
19
|
-
},
|
|
20
|
-
"expires": {
|
|
21
|
-
"@id": "https://w3id.org/security#expiration",
|
|
22
|
-
"@type": "http://www.w3.org/2001/XMLSchema#dateTime"
|
|
23
|
-
},
|
|
24
|
-
"publicKeyMultibase": {
|
|
25
|
-
"@id": "https://w3id.org/security#publicKeyMultibase",
|
|
26
|
-
"@type": "https://w3id.org/security#multibase"
|
|
27
|
-
},
|
|
28
|
-
"secretKeyMultibase": {
|
|
29
|
-
"@id": "https://w3id.org/security#secretKeyMultibase",
|
|
30
|
-
"@type": "https://w3id.org/security#multibase"
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"@context": {
|
|
3
|
-
"id": "@id",
|
|
4
|
-
"type": "@type",
|
|
5
|
-
"dc": "http://purl.org/dc/terms/",
|
|
6
|
-
"sec": "https://w3id.org/security#",
|
|
7
|
-
"xsd": "http://www.w3.org/2001/XMLSchema#",
|
|
8
|
-
"EcdsaKoblitzSignature2016": "sec:EcdsaKoblitzSignature2016",
|
|
9
|
-
"Ed25519Signature2018": "sec:Ed25519Signature2018",
|
|
10
|
-
"EncryptedMessage": "sec:EncryptedMessage",
|
|
11
|
-
"GraphSignature2012": "sec:GraphSignature2012",
|
|
12
|
-
"LinkedDataSignature2015": "sec:LinkedDataSignature2015",
|
|
13
|
-
"LinkedDataSignature2016": "sec:LinkedDataSignature2016",
|
|
14
|
-
"CryptographicKey": "sec:Key",
|
|
15
|
-
"authenticationTag": "sec:authenticationTag",
|
|
16
|
-
"canonicalizationAlgorithm": "sec:canonicalizationAlgorithm",
|
|
17
|
-
"cipherAlgorithm": "sec:cipherAlgorithm",
|
|
18
|
-
"cipherData": "sec:cipherData",
|
|
19
|
-
"cipherKey": "sec:cipherKey",
|
|
20
|
-
"created": {
|
|
21
|
-
"@id": "dc:created",
|
|
22
|
-
"@type": "xsd:dateTime"
|
|
23
|
-
},
|
|
24
|
-
"creator": {
|
|
25
|
-
"@id": "dc:creator",
|
|
26
|
-
"@type": "@id"
|
|
27
|
-
},
|
|
28
|
-
"digestAlgorithm": "sec:digestAlgorithm",
|
|
29
|
-
"digestValue": "sec:digestValue",
|
|
30
|
-
"domain": "sec:domain",
|
|
31
|
-
"encryptionKey": "sec:encryptionKey",
|
|
32
|
-
"expiration": {
|
|
33
|
-
"@id": "sec:expiration",
|
|
34
|
-
"@type": "xsd:dateTime"
|
|
35
|
-
},
|
|
36
|
-
"expires": {
|
|
37
|
-
"@id": "sec:expiration",
|
|
38
|
-
"@type": "xsd:dateTime"
|
|
39
|
-
},
|
|
40
|
-
"initializationVector": "sec:initializationVector",
|
|
41
|
-
"iterationCount": "sec:iterationCount",
|
|
42
|
-
"nonce": "sec:nonce",
|
|
43
|
-
"normalizationAlgorithm": "sec:normalizationAlgorithm",
|
|
44
|
-
"owner": {
|
|
45
|
-
"@id": "sec:owner",
|
|
46
|
-
"@type": "@id"
|
|
47
|
-
},
|
|
48
|
-
"password": "sec:password",
|
|
49
|
-
"privateKey": {
|
|
50
|
-
"@id": "sec:privateKey",
|
|
51
|
-
"@type": "@id"
|
|
52
|
-
},
|
|
53
|
-
"privateKeyPem": "sec:privateKeyPem",
|
|
54
|
-
"publicKey": {
|
|
55
|
-
"@id": "sec:publicKey",
|
|
56
|
-
"@type": "@id"
|
|
57
|
-
},
|
|
58
|
-
"publicKeyBase58": "sec:publicKeyBase58",
|
|
59
|
-
"publicKeyPem": "sec:publicKeyPem",
|
|
60
|
-
"publicKeyWif": "sec:publicKeyWif",
|
|
61
|
-
"publicKeyService": {
|
|
62
|
-
"@id": "sec:publicKeyService",
|
|
63
|
-
"@type": "@id"
|
|
64
|
-
},
|
|
65
|
-
"revoked": {
|
|
66
|
-
"@id": "sec:revoked",
|
|
67
|
-
"@type": "xsd:dateTime"
|
|
68
|
-
},
|
|
69
|
-
"salt": "sec:salt",
|
|
70
|
-
"signature": "sec:signature",
|
|
71
|
-
"signatureAlgorithm": "sec:signingAlgorithm",
|
|
72
|
-
"signatureValue": "sec:signatureValue"
|
|
73
|
-
}
|
|
74
|
-
}
|
package/lib/core/deliver.mjs
DELETED
|
@@ -1,387 +0,0 @@
|
|
|
1
|
-
// deliver.mjs — signed outbound delivery + retry queue. Fedify's signRequest
|
|
2
|
-
// does draft-cavage HTTP Signatures (what Mastodon verifies). Failures go to
|
|
3
|
-
// a JSON-file queue with exponential backoff; peers that stay down are
|
|
4
|
-
// dropped after MAX_ATTEMPTS (~3 days).
|
|
5
|
-
|
|
6
|
-
import { signRequest } from '@fedify/fedify/sig';
|
|
7
|
-
import { webcrypto } from 'node:crypto';
|
|
8
|
-
import { attachProof } from './proof.mjs';
|
|
9
|
-
import { pinnedFor, retryAfterMs, HTTP_TIMEOUT_MS } from '../shared/safefetch.mjs';
|
|
10
|
-
import { USER_AGENT } from '../shared/ua.mjs';
|
|
11
|
-
import { dropFollower } from './store.mjs';
|
|
12
|
-
|
|
13
|
-
const MAX_REDIRECTS = 3;
|
|
14
|
-
|
|
15
|
-
// draft-cavage signs `(request-target): <method> <path><query>`. Fedify 2.3.4
|
|
16
|
-
// builds that line from the pathname alone, so a signature over any URL
|
|
17
|
-
// carrying a query is one a correct verifier cannot reconstruct — a paged
|
|
18
|
-
// collection at `?page=2`, a WordPress inbox at `?rest_route=…`. Re-sign just
|
|
19
|
-
// that line, over the header list Fedify already chose; a URL with no query
|
|
20
|
-
// comes back untouched, and anything unexpected is left exactly as signed.
|
|
21
|
-
export async function withQueryInTarget(signed, privateKey) {
|
|
22
|
-
const url = new URL(signed.url);
|
|
23
|
-
if (!url.search) return signed;
|
|
24
|
-
const header = signed.headers.get('signature');
|
|
25
|
-
if (!header) return signed;
|
|
26
|
-
const fields = Object.fromEntries(
|
|
27
|
-
[...header.matchAll(/([A-Za-z]+)="([^"]*)"/g)].map(m => [m[1], m[2]]));
|
|
28
|
-
const names = fields.headers?.split(/\s+/g) || [];
|
|
29
|
-
if (!fields.signature || !names.includes('(request-target)')) return signed;
|
|
30
|
-
if (names.some(n => n.startsWith('(') && n !== '(request-target)')) return signed;
|
|
31
|
-
const message = names.map(n => n === '(request-target)'
|
|
32
|
-
? `(request-target): ${signed.method.toLowerCase()} ${url.pathname}${url.search}`
|
|
33
|
-
: `${n}: ${(n === 'host' ? signed.headers.get('host') || url.host : signed.headers.get(n)) ?? ''}`)
|
|
34
|
-
.join('\n');
|
|
35
|
-
const sig = await webcrypto.subtle.sign('RSASSA-PKCS1-v1_5', privateKey,
|
|
36
|
-
new TextEncoder().encode(message));
|
|
37
|
-
const headers = new Headers(signed.headers);
|
|
38
|
-
headers.set('signature',
|
|
39
|
-
header.replace(/signature="[^"]*"/, `signature="${Buffer.from(sig).toString('base64')}"`));
|
|
40
|
-
const init = { method: signed.method, headers, signal: signed.signal };
|
|
41
|
-
if (signed.method !== 'GET' && signed.method !== 'HEAD') init.body = await signed.arrayBuffer();
|
|
42
|
-
return new Request(signed.url, init);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const MAX_ATTEMPTS = 12; // 2^12 min ≈ 68h of backoff
|
|
46
|
-
const TICK_MS = 60_000;
|
|
47
|
-
const MAX_QUEUE = 2000; // beyond it, overflow dead-letters
|
|
48
|
-
|
|
49
|
-
// Is this failure about the SERVER, or about this one recipient? A 410 Gone,
|
|
50
|
-
// a 403, a 401 from an authorized-fetch instance are answers about one inbox;
|
|
51
|
-
// cooling the whole host for them starves every other recipient on it — which
|
|
52
|
-
// on a big instance is most of your followers. Only a refusal that is about
|
|
53
|
-
// the server, or no answer at all, is grounds to leave it alone.
|
|
54
|
-
const hostOf = (inbox) => { try { return new URL(inbox).host; } catch { return inbox; } };
|
|
55
|
-
const aboutTheHost = (e) => !e.status || e.status >= 500 || e.status === 429;
|
|
56
|
-
// A 410 says the inbox is gone for good and a 404 that nothing answers there:
|
|
57
|
-
// neither earns a second POST. Gone also means whoever read there has left.
|
|
58
|
-
const unsalvageable = (e) => e.status === 404 || e.status === 410;
|
|
59
|
-
|
|
60
|
-
export class Deliverer {
|
|
61
|
-
// passive: signing-only (viewer-mode agents) — no queue drain timer, so a
|
|
62
|
-
// read-only agent never mutates shared delivery state. startQueue() flips
|
|
63
|
-
// it live when a viewer is promoted to active.
|
|
64
|
-
constructor({ store, keyId, rsaPrivate, actorId = null, edPrivate = null, proofKeyId = null,
|
|
65
|
-
log = console.log, passive = false, onGone = null }) {
|
|
66
|
-
this.store = store;
|
|
67
|
-
// Called after followers at a gone inbox are dropped: the agent republishes
|
|
68
|
-
// its followers collection.
|
|
69
|
-
this.onGone = onGone;
|
|
70
|
-
this.keyId = keyId;
|
|
71
|
-
this.rsaPrivate = rsaPrivate;
|
|
72
|
-
this.actorId = actorId;
|
|
73
|
-
this.edPrivate = edPrivate;
|
|
74
|
-
this.proofKeyId = proofKeyId;
|
|
75
|
-
this.log = log;
|
|
76
|
-
// How many fresh deliveries one attempt may carry. The Node deliverer
|
|
77
|
-
// opens a socket per recipient; the browser's relay deliverer sends a
|
|
78
|
-
// list, and sets this to what the relay accepts.
|
|
79
|
-
this.batchSize = 1;
|
|
80
|
-
if (!passive) this.startQueue();
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
startQueue() {
|
|
84
|
-
if (this.timer) return;
|
|
85
|
-
this.timer = setInterval(() => this.drainQueue().catch(e => this.log(`queue: ${e.message}`)), TICK_MS);
|
|
86
|
-
this.timer.unref?.();
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Signed fetch — also used for GETs so authorized-fetch instances answer
|
|
90
|
-
// us. Targets come from attacker-supplied documents, so every hop is
|
|
91
|
-
// address-checked and the connection is pinned to the checked address.
|
|
92
|
-
// Redirects are followed by hand because each hop needs its own signature
|
|
93
|
-
// (the signature covers the request target).
|
|
94
|
-
async signedFetch(url, init = {}) {
|
|
95
|
-
let current = url;
|
|
96
|
-
// Named on every hop: a remote operator seeing our deliveries or our
|
|
97
|
-
// verify-by-dereference GETs should know what we are.
|
|
98
|
-
//
|
|
99
|
-
// And bounded. This was the one remote-facing path in the project without a
|
|
100
|
-
// deadline — publisher, intake and the token grant all set one — so a peer
|
|
101
|
-
// that accepted the connection and then stalled held us for undici's 300s
|
|
102
|
-
// default. Against a 60s queue tick that is five overlapping drains
|
|
103
|
-
// re-POSTing the same activity at a server already in trouble. One signal,
|
|
104
|
-
// built once, so it covers all four redirect hops together rather than
|
|
105
|
-
// giving each hop a fresh deadline.
|
|
106
|
-
const withUa = {
|
|
107
|
-
...init,
|
|
108
|
-
signal: init.signal || AbortSignal.timeout(HTTP_TIMEOUT_MS),
|
|
109
|
-
headers: { 'user-agent': USER_AGENT, ...(init.headers || {}) },
|
|
110
|
-
};
|
|
111
|
-
for (let hop = 0; hop <= MAX_REDIRECTS; hop++) {
|
|
112
|
-
const dispatcher = await pinnedFor(current);
|
|
113
|
-
const req = new Request(current, withUa);
|
|
114
|
-
const signed = await withQueryInTarget(
|
|
115
|
-
await signRequest(req, this.rsaPrivate, new URL(this.keyId)), this.rsaPrivate);
|
|
116
|
-
const res = await fetch(signed, { ...(dispatcher ? { dispatcher } : {}), redirect: 'manual' });
|
|
117
|
-
if (res.status >= 300 && res.status < 400 && res.headers.get('location')) {
|
|
118
|
-
current = new URL(res.headers.get('location'), current).href;
|
|
119
|
-
continue;
|
|
120
|
-
}
|
|
121
|
-
// Where this ended up, which is not always where it was asked to go.
|
|
122
|
-
// Callers compare the document's own id against the URL they REQUESTED,
|
|
123
|
-
// so without this an open redirector on a victim's origin let an
|
|
124
|
-
// attacker's server answer for a victim's URL and be believed.
|
|
125
|
-
try { Object.defineProperty(res, 'finalUrl', { value: current, configurable: true }); }
|
|
126
|
-
catch { /* a frozen Response: the caller falls back to the requested URL */ }
|
|
127
|
-
return res;
|
|
128
|
-
}
|
|
129
|
-
throw new Error(`too many redirects from ${url}`);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
async deliverNow(inbox, activity) {
|
|
133
|
-
const res = await this.signedFetch(inbox, {
|
|
134
|
-
method: 'POST',
|
|
135
|
-
headers: { 'content-type': 'application/activity+json' },
|
|
136
|
-
body: JSON.stringify(activity),
|
|
137
|
-
});
|
|
138
|
-
if (res.status >= 400) {
|
|
139
|
-
// Carry what the server told us instead of flattening it to a string.
|
|
140
|
-
// The Response used to be discarded here, headers and all, which is why
|
|
141
|
-
// a `Retry-After: 3600` was answered with four more POSTs inside the
|
|
142
|
-
// hour it asked for.
|
|
143
|
-
const err = new Error(`POST ${inbox} → ${res.status}`);
|
|
144
|
-
err.status = res.status;
|
|
145
|
-
// Only when the header is ACTUALLY there. Defaulting made every 503
|
|
146
|
-
// without one — which is most of them — collapse the exponential ladder
|
|
147
|
-
// to a flat 60s, the opposite of the intent. A remote's own instruction
|
|
148
|
-
// is honoured up to a day rather than the pod path's 30-minute ceiling:
|
|
149
|
-
// MAX_ATTEMPTS bounds the wait anyway.
|
|
150
|
-
const ra = retryAfterMs(res, 24 * 60 * 60_000);
|
|
151
|
-
if (ra != null) err.retryAfterMs = ra;
|
|
152
|
-
throw err;
|
|
153
|
-
}
|
|
154
|
-
return res;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Deliver, queueing on failure.
|
|
158
|
-
// FEP-8b32: a proof on everything WE author, so a server that meets one of
|
|
159
|
-
// our activities second-hand can verify it without asking us.
|
|
160
|
-
//
|
|
161
|
-
// Only ours. An activity we forward on someone else's behalf keeps whatever
|
|
162
|
-
// proof its author gave it — signing that one as an assertion of ours would
|
|
163
|
-
// be claiming to have written it.
|
|
164
|
-
async proofed(activity) {
|
|
165
|
-
if (!this.edPrivate || !this.proofKeyId || !activity || typeof activity !== 'object') return activity;
|
|
166
|
-
if (activity.proof) return activity;
|
|
167
|
-
const actor = typeof activity.actor === 'string' ? activity.actor : activity.actor?.id;
|
|
168
|
-
if (!this.actorId || actor !== this.actorId) return activity;
|
|
169
|
-
try {
|
|
170
|
-
return await attachProof(activity,
|
|
171
|
-
{ privateKey: this.edPrivate, verificationMethod: this.proofKeyId });
|
|
172
|
-
} catch (e) {
|
|
173
|
-
this.log(`proof: ${e.message}`); // an unproved activity still federates
|
|
174
|
-
return activity;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// One attempt per target, answered in order: `{ ok: true }` or `{ error }`
|
|
179
|
-
// with the error deliverNow would have thrown. Here one at a time; the
|
|
180
|
-
// relay deliverer sends the whole list in one call.
|
|
181
|
-
async deliverManyNow(targets) {
|
|
182
|
-
const out = [];
|
|
183
|
-
for (const t of targets) {
|
|
184
|
-
try { await this.deliverNow(t.inbox, t.activity); out.push({ ok: true }); }
|
|
185
|
-
catch (error) { out.push({ error }); }
|
|
186
|
-
}
|
|
187
|
-
return out;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// A fresh activity of this actor's, for the outbox its owner reads. Never
|
|
191
|
-
// allowed to stop a delivery; a retry from the queue is not fresh.
|
|
192
|
-
_sent(activity) {
|
|
193
|
-
try { this.onSent?.(activity); } catch (e) { this.log(`owner's outbox record: ${e.message}`); }
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// An inbox on a blocked domain is never delivered to, whoever still lists it.
|
|
197
|
-
_blockedInbox(inbox) {
|
|
198
|
-
try { return !!this.store.getBlocklist?.().domains.length && this.store.isBlocked?.(new URL(inbox).origin + '/'); }
|
|
199
|
-
catch { return false; }
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
async deliver(inbox, activity) {
|
|
203
|
-
this._sent(activity);
|
|
204
|
-
if (this._blockedInbox(inbox)) return;
|
|
205
|
-
// Proved before anything else, so the copy that goes on the queue is the
|
|
206
|
-
// copy that was signed — a retry days later must not post a bare activity.
|
|
207
|
-
const signed = await this.proofed(activity);
|
|
208
|
-
if (this._queueIfCooling(inbox, signed)) return;
|
|
209
|
-
const [result] = await this.deliverManyNow([{ inbox, activity: signed }]);
|
|
210
|
-
await this._settle(inbox, signed, result);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// A host we already know is refusing: queue without asking again. This is
|
|
214
|
-
// the path a FRESH activity takes, so without it a fan-out to a struggling
|
|
215
|
-
// server opened one socket per follower before any of this applied.
|
|
216
|
-
_queueIfCooling(inbox, signed) {
|
|
217
|
-
const host = hostOf(inbox);
|
|
218
|
-
const until = this._cooling?.get(host);
|
|
219
|
-
if (!until || until <= Date.now()) return false;
|
|
220
|
-
this.log(`${host} is cooling — queueing ${signed.type} rather than asking again`);
|
|
221
|
-
this._enqueue({ inbox, activity: signed, attempts: 1, nextAt: until });
|
|
222
|
-
return true;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// What one attempt's outcome means for the queue.
|
|
226
|
-
async _settle(inbox, signed, result) {
|
|
227
|
-
if (result.ok) { this.log(`delivered ${signed.type} → ${inbox}`); return; }
|
|
228
|
-
const e = result.error;
|
|
229
|
-
if (unsalvageable(e)) { await this._unsalvageable(inbox, e); return; }
|
|
230
|
-
this.log(`delivery failed (${e.message}) — queued`);
|
|
231
|
-
const wait = e.retryAfterMs || 60_000;
|
|
232
|
-
if (aboutTheHost(e)) {
|
|
233
|
-
this._cooling ||= new Map();
|
|
234
|
-
this._cooling.set(hostOf(inbox), Date.now() + wait);
|
|
235
|
-
}
|
|
236
|
-
this._enqueue({ inbox, activity: signed, attempts: 1, nextAt: Date.now() + wait });
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Not retried, and on a 410 the followers who received there are dropped,
|
|
240
|
-
// so the next fan-out stops asking; the followers collection is republished
|
|
241
|
-
// through onGone. A 404 drops only this delivery.
|
|
242
|
-
async _unsalvageable(inbox, e) {
|
|
243
|
-
if (e.status !== 410) { this.log(`delivery dropped (${e.message}) — nothing answers there`); return; }
|
|
244
|
-
const contacts = this.store.getContacts?.();
|
|
245
|
-
const left = (contacts?.followers || []).filter(f => f.inbox === inbox || f.sharedInbox === inbox);
|
|
246
|
-
this.log(`${inbox} is gone (410) — delivery dropped${left.length ? `, ${left.length} follower(s) there forgotten` : ''}`);
|
|
247
|
-
if (!left.length) return;
|
|
248
|
-
for (const f of left) dropFollower(contacts, f.actor, 'inbox-gone');
|
|
249
|
-
this.store.setContacts(contacts);
|
|
250
|
-
try { await this.onGone?.(); } catch (err) { this.log(`followers republish after 410: ${err.message}`); }
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// The queue has a ceiling: a fan-out into a sea of down servers must not grow
|
|
254
|
-
// queue.json without bound. Overflow keeps the evidence as a dead letter.
|
|
255
|
-
_enqueue(item) {
|
|
256
|
-
const q = this.store.getQueue();
|
|
257
|
-
if (q.length >= MAX_QUEUE) {
|
|
258
|
-
this.log(`delivery queue is full (${MAX_QUEUE}) — dead-lettering ${item.activity?.type} → ${item.inbox}`);
|
|
259
|
-
this.store.addDeadLetter({ inboxUrl: item.inbox, reason: 'delivery queue full', activity: item.activity });
|
|
260
|
-
return;
|
|
261
|
-
}
|
|
262
|
-
q.push(item);
|
|
263
|
-
this.store.setQueue(q);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
async deliverToAll(inboxes, activity) {
|
|
267
|
-
this._sent(activity);
|
|
268
|
-
// Proved once for the whole fan-out: every recipient gets the same bytes,
|
|
269
|
-
// and one signature is computed rather than one per follower.
|
|
270
|
-
const signed = await this.proofed(activity);
|
|
271
|
-
// Shared inboxes deduplicate fan-out to the same server. Sent in batches
|
|
272
|
-
// of batchSize, and a host found cooling by one batch is not asked again
|
|
273
|
-
// by the next.
|
|
274
|
-
const targets = [...new Set(inboxes)].filter((inbox) => !this._blockedInbox(inbox))
|
|
275
|
-
.map((inbox) => ({ inbox, activity: signed }));
|
|
276
|
-
for (let i = 0; i < targets.length; i += this.batchSize) {
|
|
277
|
-
const chunk = targets.slice(i, i + this.batchSize).filter((t) => !this._queueIfCooling(t.inbox, signed));
|
|
278
|
-
if (!chunk.length) continue;
|
|
279
|
-
const results = await this.deliverManyNow(chunk);
|
|
280
|
-
for (let k = 0; k < chunk.length; k++) await this._settle(chunk[k].inbox, signed, results[k]);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// Serialized, for the same reason Intake.drain is: the tick is 60s and a
|
|
285
|
-
// drain over slow peers outlasts it, so a second run started on top of the
|
|
286
|
-
// first. Both read their own clone of queue.json, so both re-POSTed every due
|
|
287
|
-
// activity — duplicate deliveries at a server that is already failing — and
|
|
288
|
-
// both then wrote `keep` back from a stale snapshot, losing the attempt
|
|
289
|
-
// counts that are supposed to make the backoff converge.
|
|
290
|
-
async drainQueue() {
|
|
291
|
-
if (this._draining) { this._drainAgain = true; return this._draining; }
|
|
292
|
-
this._draining = this._drainQueueOnce().finally(() => {
|
|
293
|
-
this._draining = null;
|
|
294
|
-
if (this._drainAgain) { this._drainAgain = false; this.drainQueue().catch(e => this.log(`queue: ${e.message}`)); }
|
|
295
|
-
});
|
|
296
|
-
return this._draining;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
// The queue is walked per HOST, not per item.
|
|
300
|
-
//
|
|
301
|
-
// It used to be a flat sequential loop, which had two costs against a server
|
|
302
|
-
// that was down. Every due item for that host was POSTed — the first failure
|
|
303
|
-
// told us nothing the code could act on — and because each request carries a
|
|
304
|
-
// 15s deadline, K items for one dead host held the drain for up to 15K
|
|
305
|
-
// seconds while every healthy peer's activity waited behind them. Compounding
|
|
306
|
-
// requests at a struggling server, and head-of-line blocking of everyone
|
|
307
|
-
// else: the shape of the incident this project has a postmortem for.
|
|
308
|
-
//
|
|
309
|
-
// One failure now cools the whole host for the rest of this drain, and a
|
|
310
|
-
// Retry-After it sent decides for how long. The breaker is in memory; what
|
|
311
|
-
// persists is nextAt, which the queue already carried.
|
|
312
|
-
async _drainQueueOnce() {
|
|
313
|
-
const q = this.store.getQueue();
|
|
314
|
-
if (!q.length) return;
|
|
315
|
-
const now = Date.now();
|
|
316
|
-
const keep = [];
|
|
317
|
-
// Whether anything actually moved. The only early return above is on an
|
|
318
|
-
// EMPTY queue, so one item with a far-future nextAt used to rewrite
|
|
319
|
-
// queue.json on all 1440 ticks of a day with byte-identical content —
|
|
320
|
-
// PodStore.write has no equality check, and the ordinary ladder reaches
|
|
321
|
-
// hours-long waits by attempt 6, so any peer that fails a few times got
|
|
322
|
-
// there. A length compare is not enough: getQueue() hands back a clone, so
|
|
323
|
-
// attempts/nextAt are mutated in place and the count stays the same.
|
|
324
|
-
let changed = false;
|
|
325
|
-
// Outlives the drain: a Retry-After only reached items that happened to be
|
|
326
|
-
// due in the same pass, so a sibling due a minute later ignored it entirely.
|
|
327
|
-
this._cooling ||= new Map(); // host → until
|
|
328
|
-
for (const [h, until] of this._cooling) if (until <= now) this._cooling.delete(h);
|
|
329
|
-
|
|
330
|
-
const ladderFor = (item) => {
|
|
331
|
-
const base = Math.min(2 ** item.attempts, 2 ** MAX_ATTEMPTS) * 60_000;
|
|
332
|
-
return Math.round(base * (0.85 + Math.random() * 0.3)); // jittered, not lockstep
|
|
333
|
-
};
|
|
334
|
-
|
|
335
|
-
for (const item of q) {
|
|
336
|
-
// Due first, THEN cooling: an item that is not yet due must not have its
|
|
337
|
-
// ladder advanced on every 60s tick of a 15-minute cooldown. Once
|
|
338
|
-
// deferred its nextAt IS the cooldown, so it advances exactly once per
|
|
339
|
-
// cooldown — the same rate as the item that opened the socket.
|
|
340
|
-
if (item.nextAt > now) { keep.push(item); continue; }
|
|
341
|
-
const host = hostOf(item.inbox);
|
|
342
|
-
const until = this._cooling.get(host);
|
|
343
|
-
if (until && until > now) {
|
|
344
|
-
// A sibling already failed against this host. Defer without opening a
|
|
345
|
-
// socket — but ADVANCE THE LADDER, or only the one item that actually
|
|
346
|
-
// made a request ever climbs it and a queue of K items takes K times
|
|
347
|
-
// as long to give up as the module promises.
|
|
348
|
-
item.attempts += 1;
|
|
349
|
-
changed = true;
|
|
350
|
-
if (item.attempts > MAX_ATTEMPTS) {
|
|
351
|
-
this.log(`giving up on ${item.inbox} after ${MAX_ATTEMPTS} attempts`);
|
|
352
|
-
continue;
|
|
353
|
-
}
|
|
354
|
-
item.nextAt = Math.max(item.nextAt, until);
|
|
355
|
-
keep.push(item);
|
|
356
|
-
continue;
|
|
357
|
-
}
|
|
358
|
-
try {
|
|
359
|
-
await this.deliverNow(item.inbox, item.activity);
|
|
360
|
-
changed = true;
|
|
361
|
-
this.log(`retry ok: ${item.activity.type} → ${item.inbox}`);
|
|
362
|
-
} catch (e) {
|
|
363
|
-
if (unsalvageable(e)) { changed = true; await this._unsalvageable(item.inbox, e); continue; }
|
|
364
|
-
item.attempts += 1;
|
|
365
|
-
changed = true;
|
|
366
|
-
const wait = e.retryAfterMs || ladderFor(item);
|
|
367
|
-
// Cool the host BEFORE the give-up test: a failure is evidence about
|
|
368
|
-
// the server whether or not the item that produced it survives, and
|
|
369
|
-
// skipping this left the flat behaviour intact for exactly the backlog
|
|
370
|
-
// that has just aged out.
|
|
371
|
-
if (aboutTheHost(e)) {
|
|
372
|
-
this._cooling.set(host, now + wait);
|
|
373
|
-
if (e.retryAfterMs) this.log(`${host} asked for ${Math.round(wait / 1000)}s — honouring it`);
|
|
374
|
-
}
|
|
375
|
-
if (item.attempts > MAX_ATTEMPTS) {
|
|
376
|
-
this.log(`giving up on ${item.inbox} after ${MAX_ATTEMPTS} attempts`);
|
|
377
|
-
continue;
|
|
378
|
-
}
|
|
379
|
-
item.nextAt = now + wait;
|
|
380
|
-
keep.push(item);
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
if (changed) this.store.setQueue(keep);
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
stop() { clearInterval(this.timer); this.timer = null; }
|
|
387
|
-
}
|