fedipod-server 0.29.0 → 0.31.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 +16 -6
- package/dist/claims.d.ts +2 -0
- package/dist/claims.js +13 -4
- package/dist/door.d.ts +4 -0
- package/dist/door.js +31 -0
- package/dist/fedipod.d.ts +15 -0
- package/dist/fedipod.js +13 -0
- package/dist/front.d.ts +3 -0
- package/dist/front.js +92 -0
- package/dist/handler.d.ts +36 -101
- package/dist/handler.js +93 -644
- package/dist/handler.jsonld +2 -30
- package/dist/identities.d.ts +71 -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/dist/private-read.d.ts +4 -0
- package/dist/private-read.js +34 -0
- package/package.json +12 -20
- 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,522 +0,0 @@
|
|
|
1
|
-
// publisher.mjs — builds/maintains the actor's public face on the remote pod:
|
|
2
|
-
// webfinger, actor doc, collections, notes. The /ap/ tree is disposable:
|
|
3
|
-
// publishProfile() rebuilds it.
|
|
4
|
-
//
|
|
5
|
-
// This file is the actor's public face — the actor document, discovery,
|
|
6
|
-
// ACLs, the inbox posture, Move and retire. The rest is in the modules
|
|
7
|
-
// beside it: collections.mjs (the published collections and the outbox
|
|
8
|
-
// record), restore.mjs (catching up with the pod), notes.mjs (a note going
|
|
9
|
-
// up), questions.mjs (polls) — each a set of functions taking the Publisher
|
|
10
|
-
// as their first argument, reached here through one-line delegations.
|
|
11
|
-
|
|
12
|
-
import fs from 'node:fs';
|
|
13
|
-
import path from 'node:path';
|
|
14
|
-
import { fileURLToPath } from 'node:url';
|
|
15
|
-
import crypto from 'node:crypto';
|
|
16
|
-
import * as wire from '../wire.mjs';
|
|
17
|
-
import { USER_AGENT } from '../../shared/ua.mjs';
|
|
18
|
-
import { HTTP_TIMEOUT_MS } from '../../shared/safefetch.mjs';
|
|
19
|
-
import * as containers from '../../pod/containers.mjs';
|
|
20
|
-
import * as discovery from '../../pod/discovery.mjs';
|
|
21
|
-
import * as podActor from '../../pod/actor.mjs';
|
|
22
|
-
import * as podInbox from '../../pod/inbox.mjs';
|
|
23
|
-
import * as podFeatured from '../../pod/featured.mjs';
|
|
24
|
-
import * as podPolicy from '../../pod/policy.mjs';
|
|
25
|
-
import * as podNotes from '../../pod/notes.mjs';
|
|
26
|
-
import * as collections from './collections.mjs';
|
|
27
|
-
import { ALL_COLLECTIONS } from './collections.mjs';
|
|
28
|
-
import * as restore from './restore.mjs';
|
|
29
|
-
import * as notes from './notes.mjs';
|
|
30
|
-
import * as questions from './questions.mjs';
|
|
31
|
-
import * as own from './own.mjs';
|
|
32
|
-
import { recordPlace } from '../place.mjs';
|
|
33
|
-
|
|
34
|
-
const AGENT_VERSION = JSON.parse(fs.readFileSync(
|
|
35
|
-
path.join(path.dirname(fileURLToPath(import.meta.url)), '../../../package.json'), 'utf8')).version;
|
|
36
|
-
|
|
37
|
-
export class Publisher {
|
|
38
|
-
constructor({ config, remote, store, deliverer, publicKeyPem, assertionKey = null, log = console.log,
|
|
39
|
-
probeFetch = null, resolveMention = null, resolveActor = null, clientOrigin = null,
|
|
40
|
-
}) {
|
|
41
|
-
this.config = config;
|
|
42
|
-
this.remote = remote;
|
|
43
|
-
this.store = store;
|
|
44
|
-
this.deliverer = deliverer;
|
|
45
|
-
// Everything this actor sends goes on the outbox its owner reads.
|
|
46
|
-
if (deliverer) deliverer.onSent = (a) => own.recordOwn(this, a);
|
|
47
|
-
this.publicKeyPem = publicKeyPem;
|
|
48
|
-
this.assertionKey = assertionKey; // Ed25519 public half, multibase; null = no proofs
|
|
49
|
-
// Where this identity's client surface answers, when that is an address a
|
|
50
|
-
// stranger can reach. Null on a laptop, where the surface is on loopback
|
|
51
|
-
// and advertising it to the world would name somewhere nobody can go.
|
|
52
|
-
this.clientOrigin = clientOrigin;
|
|
53
|
-
// A fronted identity (config.gateway.frontActor) advertises its ids on a
|
|
54
|
-
// shared domain; the map tells RemotePod where each writes on the pod.
|
|
55
|
-
const publicBase = config.gateway?.frontActor
|
|
56
|
-
? config.gateway.frontActor.replace(/ap\/actor\/?$/, '') : null;
|
|
57
|
-
this.urls = wire.apUrls(config.remotePod, config.root, { publicBase });
|
|
58
|
-
if (this.urls.toPod && this.remote?.setUrlMap) this.remote.setUrlMap(this.urls.toPod);
|
|
59
|
-
// Credential-free by design — it asks what a stranger sees — but routed
|
|
60
|
-
// through the pod's own cooldown and accounting, because it is still a
|
|
61
|
-
// socket opened to that pod. Tests inject their own.
|
|
62
|
-
this.probeFetch = probeFetch || ((u, i) => this.remote.probe(u, i));
|
|
63
|
-
this.resolveMention = resolveMention;
|
|
64
|
-
// An actor or object by its address, verified at its origin — how a reply
|
|
65
|
-
// finds the inbox of the person it answers.
|
|
66
|
-
this.resolveActor = resolveActor;
|
|
67
|
-
// Per-poll rewrite windows, keyed by question id. See POLL_REWRITE_MS.
|
|
68
|
-
this.pollTimers = new Map();
|
|
69
|
-
this.log = log;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Idempotent: (re)write webfinger + actor + collections + container ACLs.
|
|
73
|
-
//
|
|
74
|
-
// ~34 pod requests, so it does not run when it would rewrite the same bytes.
|
|
75
|
-
// Every document below is derived from the actor doc, the handle, the host,
|
|
76
|
-
// whether the actor is quiesced (which decides the inbox ACL) and the agent
|
|
77
|
-
// version (which rides in nodeinfo) — so if none of those moved, there is
|
|
78
|
-
// nothing to say. Phanpy's editor submits the whole form on every save, so
|
|
79
|
-
// "saved without changing anything" is the common case, not a rare one.
|
|
80
|
-
//
|
|
81
|
-
// `force` is for the callers that publish precisely BECAUSE the pod does not
|
|
82
|
-
// have what the digest says it has: the repair path, and the explicit
|
|
83
|
-
// republish button. Without it, an actor lost from the pod would match the
|
|
84
|
-
// digest, be skipped, and leave the agent reporting success while nobody can
|
|
85
|
-
// resolve it.
|
|
86
|
-
// The Gateway's outbox door for this account, when a Gateway is attached:
|
|
87
|
-
// beside its inbox door, or under the fronted actor.
|
|
88
|
-
gatewayOutbox() {
|
|
89
|
-
const gw = this.config.gateway;
|
|
90
|
-
if (!(gw && gw.url && gw.mode && gw.mode !== 'off')) return null;
|
|
91
|
-
if (gw.frontActor) return String(gw.frontActor).replace(/ap\/actor\/?$/u, 'ap/outbox');
|
|
92
|
-
return String(gw.url).replace(/ap\/inbox\/?$/u, 'ap/outbox');
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Every actor document this identity publishes, from one place: the
|
|
96
|
-
// profile publish and a Move both write it, and a Move that rebuilt it
|
|
97
|
-
// by hand dropped the gateway inbox, the outbox door and the collections.
|
|
98
|
-
// `clientOrigin` is overridden only by the agent serving a client its own
|
|
99
|
-
// local actor: standalone, the published document cannot name a loopback
|
|
100
|
-
// address, but a client already talking to that address can be told it.
|
|
101
|
-
actorDocFor({ priv = false, moderators = null, inbox = null, movedTo = this.config.movedTo || null,
|
|
102
|
-
clientOrigin = this.clientOrigin } = {}) {
|
|
103
|
-
const { urls } = this;
|
|
104
|
-
return wire.actorDoc({
|
|
105
|
-
urls, handle: wire.publicHandle(this.config), name: this.config.name, publicKeyPem: this.publicKeyPem,
|
|
106
|
-
movedTo, kind: this.config.kind,
|
|
107
|
-
approveJoins: wire.followsNeedApproval(this.config),
|
|
108
|
-
assertionKey: this.assertionKey,
|
|
109
|
-
summary: this.config.summary || null, icon: this.config.icon || null,
|
|
110
|
-
image: this.config.image || null, fields: this.config.fields || [],
|
|
111
|
-
webId: this.remote.webId || null,
|
|
112
|
-
aliases: this.config.aliases || [],
|
|
113
|
-
// A group says whether only its moderators may open posts (Lemmy's
|
|
114
|
-
// term); a person's actor carries nothing of the kind.
|
|
115
|
-
postingRestrictedToMods: this.config.kind === 'group' ? !!this.config.postingRestrictedToMods : null,
|
|
116
|
-
moderators,
|
|
117
|
-
pendingFollowers: priv ? urls.pendingFollowers : null,
|
|
118
|
-
// Named only where the private folder is proved to keep it private.
|
|
119
|
-
liked: priv ? urls.liked : null,
|
|
120
|
-
ownerOutbox: priv ? urls.ownOutbox : null,
|
|
121
|
-
pendingFollowing: priv ? urls.pendingFollowing : null,
|
|
122
|
-
blocked: priv ? urls.blocked : null,
|
|
123
|
-
inbox,
|
|
124
|
-
// The agent's own outbox endpoint, where it is reachable: a client
|
|
125
|
-
// following the actor must arrive somewhere that will take a write.
|
|
126
|
-
// Otherwise the Gateway's outbox door, when one is attached.
|
|
127
|
-
outbox: clientOrigin ? `${clientOrigin}ap/outbox` : this.gatewayOutbox(),
|
|
128
|
-
// How a client-to-server client finds the way in with nothing configured
|
|
129
|
-
// by hand. Advertised only where the surface is publicly reachable.
|
|
130
|
-
oauthAuthorize: clientOrigin ? `${clientOrigin}oauth/authorize` : null,
|
|
131
|
-
oauthToken: clientOrigin ? `${clientOrigin}oauth/token` : null,
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// The human half: a page a browser can open and follow from. The actor
|
|
136
|
-
// document is for servers; this is the address you hand to a person. It
|
|
137
|
-
// shows who the account is, its fields, when it joined and what it pinned;
|
|
138
|
-
// the counts and the posts live in the collections. Written only when its
|
|
139
|
-
// content changed.
|
|
140
|
-
async publishProfilePage({ force = false } = {}) {
|
|
141
|
-
const { urls } = this;
|
|
142
|
-
const host = new URL(urls.base).host;
|
|
143
|
-
// When the account was made. Accounts from before this was recorded take
|
|
144
|
-
// the date of their oldest post, or today.
|
|
145
|
-
if (!this.config.createdAt) {
|
|
146
|
-
const oldest = this.store.getStatuses().filter(s => s.kind === 'post' && s.published)
|
|
147
|
-
.map(s => s.published).sort()[0];
|
|
148
|
-
this.config.createdAt = oldest || new Date().toISOString();
|
|
149
|
-
this.store.setConfig?.({ ...this.store.getConfig?.(), createdAt: this.config.createdAt });
|
|
150
|
-
}
|
|
151
|
-
const pinned = this.store.getStatuses()
|
|
152
|
-
.filter(s => s.kind === 'post' && s.pinned && s.visibility !== 'private' && s.visibility !== 'direct')
|
|
153
|
-
.sort((a, b) => String(b.published).localeCompare(String(a.published)))
|
|
154
|
-
.slice(0, 5)
|
|
155
|
-
.map(s => ({ content: s.content, published: s.published, url: s.noteId }));
|
|
156
|
-
// The address as the world knows it: at the Gateway for a fronted
|
|
157
|
-
// identity, on the pod's host otherwise, the actor id where no host can
|
|
158
|
-
// answer for a handle.
|
|
159
|
-
const frontActor = this.config.gateway?.frontActor;
|
|
160
|
-
const address = frontActor ? `@${wire.publicHandle(this.config)}@${new URL(frontActor).host}`
|
|
161
|
-
: (wire.webfingerHost(urls.base) ? `@${this.config.handle}@${host}` : urls.actor);
|
|
162
|
-
const html = wire.profilePageHtml({
|
|
163
|
-
name: this.config.name || this.config.handle,
|
|
164
|
-
address,
|
|
165
|
-
summary: this.config.summary ? wire.contentHtml(this.config.summary) : null,
|
|
166
|
-
icon: this.config.icon || null,
|
|
167
|
-
image: this.config.image || null,
|
|
168
|
-
fields: this.config.fields || [],
|
|
169
|
-
joined: this.config.createdAt,
|
|
170
|
-
pinned,
|
|
171
|
-
kind: this.config.kind,
|
|
172
|
-
});
|
|
173
|
-
const digest = crypto.createHash('sha256').update(html).digest('hex').slice(0, 32);
|
|
174
|
-
const seen = this.store.read('published.json', {});
|
|
175
|
-
if (!force && seen.pageDigest === digest) return false;
|
|
176
|
-
await podActor.writeProfilePage(this.remote, urls, html);
|
|
177
|
-
this.store.write('published.json', { ...this.store.read('published.json', {}), pageDigest: digest });
|
|
178
|
-
return true;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
async publishProfile({ force = false } = {}) {
|
|
182
|
-
const { urls } = this;
|
|
183
|
-
const host = new URL(urls.base).host;
|
|
184
|
-
|
|
185
|
-
// The owner-only collections (FEP-4ccd, FEP-c648) are advertised only
|
|
186
|
-
// where "owner-only" is real — the pod must provably enforce the private
|
|
187
|
-
// container's ACL, the same bar private posts clear.
|
|
188
|
-
const priv = await this.privateReady() === true;
|
|
189
|
-
const moderators = (this.config.moderators || []).length ? urls.moderators : null;
|
|
190
|
-
// An advertised inbox gateway (config.gateway.mode past 'off') becomes the
|
|
191
|
-
// actor's inbox; deliveries reach the pod inbox through it. Absent → today.
|
|
192
|
-
const gw = this.config.gateway;
|
|
193
|
-
const gwActive = gw && gw.url && gw.mode && gw.mode !== 'off';
|
|
194
|
-
// A forum's category advertises the forum's one inbox (config.inboxUrl):
|
|
195
|
-
// deliveries to any category land there, and the forum routes them.
|
|
196
|
-
const actorDoc = this.actorDocFor({ priv, moderators, inbox: gwActive ? gw.url : (this.config.inboxUrl || null) });
|
|
197
|
-
const surface = crypto.createHash('sha256').update(JSON.stringify({
|
|
198
|
-
actor: actorDoc, handle: this.config.handle, host,
|
|
199
|
-
quiesced: !!this.config.quiescedAt, version: AGENT_VERSION,
|
|
200
|
-
moderators: this.config.moderators || [],
|
|
201
|
-
})).digest('hex').slice(0, 32);
|
|
202
|
-
// The human page has its own gate: it changes with pins and the joined
|
|
203
|
-
// date as well as with the profile, and costs one write when it does.
|
|
204
|
-
await this.publishProfilePage({ force });
|
|
205
|
-
if (!force && this.store.read('published.json', {}).surfaceDigest === surface) {
|
|
206
|
-
this.log('profile unchanged — nothing republished');
|
|
207
|
-
return { unreachable: [], updated: 0, skipped: true };
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Discovery is the pod's, and a pod has one WebFinger document: a forum's
|
|
211
|
-
// category (config.forum names the forum) is not the pod's identity, so it
|
|
212
|
-
// writes none — the forum's own actor, or a Gateway, answers for it.
|
|
213
|
-
if (!this.config.forum) {
|
|
214
|
-
await discovery.writeWebfinger(this.remote, urls,
|
|
215
|
-
wire.jrd({ handle: this.config.handle, host, actor: urls.actor, page: urls.profileHtml }));
|
|
216
|
-
await discovery.writeHostMeta(this.remote, urls, wire.hostMeta(urls.base));
|
|
217
|
-
|
|
218
|
-
const nodeinfoDocUrl = urls.home + 'ap/nodeinfo-2.0';
|
|
219
|
-
const localPosts = this.store.getStatuses().filter(s => s.kind === 'post').length;
|
|
220
|
-
await discovery.writeNodeinfo(this.remote, urls, {
|
|
221
|
-
pointer: wire.nodeinfoPointer(nodeinfoDocUrl),
|
|
222
|
-
doc: wire.nodeinfoDoc({ version: AGENT_VERSION, localPosts }),
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
const actor = actorDoc; // built above, for the digest
|
|
227
|
-
await podActor.write(this.remote, urls, actor);
|
|
228
|
-
|
|
229
|
-
// FEP-1b12: the moderator roster, public — it is what a recipient
|
|
230
|
-
// validates a group's announced moderation against.
|
|
231
|
-
if (moderators) {
|
|
232
|
-
await podFeatured.writeModerators(this.remote, urls,
|
|
233
|
-
wire.orderedCollection(urls.moderators, this.config.moderators));
|
|
234
|
-
}
|
|
235
|
-
// The gateway policy doc: the PUBLIC data a keyless gateway reads to decide
|
|
236
|
-
// what concerns this identity. Written only while a gateway is advertised.
|
|
237
|
-
if (gwActive) await this.publishGatewayPolicy();
|
|
238
|
-
|
|
239
|
-
// WebID → actor: the profile card lists the actor as a foaf:account.
|
|
240
|
-
// Best-effort — a profile that cannot be read or edited does not stop the
|
|
241
|
-
// publish, it is logged and the rest of the surface still goes up.
|
|
242
|
-
if (this.config.forum) { /* a category is not the pod owner's account */ } else try {
|
|
243
|
-
const wrote = await podActor.linkInWebIdProfile(this.remote, {
|
|
244
|
-
actorUrl: urls.actor,
|
|
245
|
-
accountName: `@${this.config.handle}@${host}`,
|
|
246
|
-
kind: this.config.kind,
|
|
247
|
-
// Where a Solid client posts: dokieli reads `as:outbox` off the WebID.
|
|
248
|
-
outbox: this.clientOrigin ? `${this.clientOrigin}ap/outbox` : this.gatewayOutbox(),
|
|
249
|
-
});
|
|
250
|
-
if (wrote) this.log('WebID profile now lists the actor as a foaf:account');
|
|
251
|
-
} catch (e) {
|
|
252
|
-
this.log(`WebID profile not updated with the actor link: ${e.message}`);
|
|
253
|
-
}
|
|
254
|
-
// Where the account lives, in the owner's public type index — only where
|
|
255
|
-
// they already have one; an index is made only on their yes, at sign-up.
|
|
256
|
-
if (!this.config.forum) {
|
|
257
|
-
await recordPlace(this.remote, urls.base, urls.home + 'ap/actor')
|
|
258
|
-
.then((r) => { if (r === 'registered') this.log('the public type index now records where the account lives'); })
|
|
259
|
-
.catch((e) => this.log(`type index not updated with the account's place: ${e.message}`));
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// inbox: public may only Append; owner (the agent) reads + drains. A
|
|
263
|
-
// quiesced actor keeps its name resolving but takes no more mail, so a
|
|
264
|
-
// republish must not re-open the door.
|
|
265
|
-
await podInbox.writeKeep(this.remote, urls);
|
|
266
|
-
await podInbox.setPosture(this.remote, urls, this.config.quiescedAt ? 'closed' : 'open');
|
|
267
|
-
if (this.config.quiescedAt) this.log('inbox left closed — this actor is quiesced');
|
|
268
|
-
|
|
269
|
-
// notes live under a public-Read container (acl:default covers new notes).
|
|
270
|
-
await podNotes.provisionContainer(this.remote, urls);
|
|
271
|
-
|
|
272
|
-
await this.publishCollections({ ...ALL_COLLECTIONS, force });
|
|
273
|
-
const updated = await this.announceProfileChange(actor, { force });
|
|
274
|
-
await this.ensurePrivateAcls();
|
|
275
|
-
const unreachable = await this.verifyPublicSurface();
|
|
276
|
-
// Last, and merged: announceProfileChange writes this document too.
|
|
277
|
-
//
|
|
278
|
-
// Only when the surface came back readable. Recording it regardless meant a
|
|
279
|
-
// publish that half-landed still matched the digest, so the NEXT save — the
|
|
280
|
-
// one the operator makes because the first did not work — was skipped as a
|
|
281
|
-
// no-op and reported success. The digest is a record of what is up there,
|
|
282
|
-
// and an unreachable document is not up there.
|
|
283
|
-
if (!unreachable.length) {
|
|
284
|
-
this.store.write('published.json',
|
|
285
|
-
{ ...this.store.read('published.json', {}), surfaceDigest: surface });
|
|
286
|
-
}
|
|
287
|
-
// Named as the fediverse sees it: a fronted actor's name and host are the
|
|
288
|
-
// front's, not the pod's.
|
|
289
|
-
const pubName = wire.publicHandle(this.config);
|
|
290
|
-
const pubHost = this.config.gateway?.frontActor ? new URL(this.config.gateway.frontActor).host : host;
|
|
291
|
-
this.log(this.config.gateway?.frontActor || wire.webfingerHost(urls.base)
|
|
292
|
-
? `profile published: @${pubName}@${pubHost} → ${urls.actor}`
|
|
293
|
-
: `profile published → ${urls.actor} — NOT discoverable as @${pubName}@${pubHost}: `
|
|
294
|
-
+ 'this pod is suffixed, and WebFinger is only answered for a subdomained pod');
|
|
295
|
-
return { unreachable, updated };
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// Fires when the document differs from the one last published — including
|
|
299
|
-
// the first time, when there is nothing to differ from.
|
|
300
|
-
//
|
|
301
|
-
// Every call to publishProfile is already a DELIBERATE republish: setup, a
|
|
302
|
-
// rename, an edit through the client or /config, `describe`, a key rotation,
|
|
303
|
-
// or an actor document found missing from the pod. Starting the agent does not
|
|
304
|
-
// call it. So the digest is not there to survive restarts — it is there for
|
|
305
|
-
// the republish that changes nothing, which /config does whenever you save a
|
|
306
|
-
// field the actor document does not carry.
|
|
307
|
-
//
|
|
308
|
-
// A silent first publish was considered and is WRONG here: it would spend a
|
|
309
|
-
// real edit doing nothing but recording a digest, and that edit is exactly the
|
|
310
|
-
// one whose invisibility this fixes.
|
|
311
|
-
async announceProfileChange(actor, { force = false } = {}) {
|
|
312
|
-
const digest = crypto.createHash('sha256').update(JSON.stringify(actor)).digest('hex').slice(0, 32);
|
|
313
|
-
const seen = this.store.read('published.json', {});
|
|
314
|
-
// A forced republish is the operator saying TELL THE WORLD — the digest
|
|
315
|
-
// gate is for silent no-op saves, not for that.
|
|
316
|
-
if (seen.actorDigest === digest && !force) return 0;
|
|
317
|
-
this.store.write('published.json', { ...seen, actorDigest: digest, at: new Date().toISOString() });
|
|
318
|
-
const inboxes = [...new Set(this.store.getContacts().followers
|
|
319
|
-
.map(f => f.sharedInbox || f.inbox).filter(Boolean))];
|
|
320
|
-
if (!inboxes.length) return 0;
|
|
321
|
-
await this.deliverer.deliverToAll(inboxes,
|
|
322
|
-
wire.updateActorActivity({ urls: this.urls, actor, serial: Date.now() }));
|
|
323
|
-
this.log(`profile changed — Update delivered to ${inboxes.length} inbox(es)`);
|
|
324
|
-
return inboxes.length;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// The mirror of ensurePrivateAcls, and the check this project lacked: these
|
|
328
|
-
// documents MUST be readable by strangers or no server can see the actor.
|
|
329
|
-
// A publish that dies half-way, or an ACL write that is accepted without
|
|
330
|
-
// taking effect, is otherwise indistinguishable from success — the agent
|
|
331
|
-
// reports itself configured and federating while nobody can find it.
|
|
332
|
-
async verifyPublicSurface() {
|
|
333
|
-
const { urls } = this;
|
|
334
|
-
const targets = [
|
|
335
|
-
// Discovery documents are the pod's, not a forum category's (see publishProfile).
|
|
336
|
-
...(this.config.forum ? [] : [['webfinger', urls.webfinger], ['host-meta', urls.base + '.well-known/host-meta']]),
|
|
337
|
-
['actor', urls.actor],
|
|
338
|
-
['notes', urls.notes],
|
|
339
|
-
['followers', urls.followers],
|
|
340
|
-
['following', urls.following],
|
|
341
|
-
['featured', urls.featured],
|
|
342
|
-
['outbox', urls.outbox],
|
|
343
|
-
];
|
|
344
|
-
const unreachable = [];
|
|
345
|
-
for (const [name, url] of targets) {
|
|
346
|
-
if (!await this.publiclyReadable(url)) unreachable.push(name);
|
|
347
|
-
}
|
|
348
|
-
if (unreachable.length) {
|
|
349
|
-
this.log(`FEDERATION: ${unreachable.join(', ')} not readable without credentials — `
|
|
350
|
-
+ 'other servers cannot resolve or fetch this actor');
|
|
351
|
-
}
|
|
352
|
-
return unreachable;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
// An ACL write that silently failed — or was changed afterwards by anything
|
|
356
|
-
// else touching the pod — leaves the private trees, signing keys included,
|
|
357
|
-
// world-readable. bootstrap writes them once at setup and never returns, so
|
|
358
|
-
// this runs on every connect: probe UNauthenticated, rewrite whatever
|
|
359
|
-
// answers, and say so loudly if the rewrite does not take.
|
|
360
|
-
async ensurePrivateAcls() {
|
|
361
|
-
const findings = await containers.repairPrivateAcls(this.remote,
|
|
362
|
-
[this.urls.home, this.urls.state],
|
|
363
|
-
{ isPublic: (u) => this.publiclyReadable(u) });
|
|
364
|
-
// What a finding MEANS is ours to say; the library just reports.
|
|
365
|
-
for (const f of findings) {
|
|
366
|
-
this.log(`${f.url} was readable without credentials — rewriting its ACL`);
|
|
367
|
-
if (f.error) { this.log(`SECURITY: ${f.url} is public and its ACL could not be rewritten: ${f.error}`); continue; }
|
|
368
|
-
if (f.stillPublic) this.log(`SECURITY: ${f.url} is STILL readable without credentials — check the pod's ACLs`);
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
// Deliberately credential-free: this asks what a stranger would see.
|
|
373
|
-
// accept: */* matters — asking for turtle makes the server answer 501 on the
|
|
374
|
-
// JSON documents (webfinger, actor), which reads as "unreachable" when the
|
|
375
|
-
// world can in fact see them perfectly well.
|
|
376
|
-
publiclyReadable(url) {
|
|
377
|
-
return containers.probePublicReadability(this.probeFetch, url,
|
|
378
|
-
{ headers: { 'user-agent': USER_AGENT }, timeoutMs: HTTP_TIMEOUT_MS });
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
// Retire this identity for good: tell everyone who follows us to drop the
|
|
382
|
-
// account, then leave a Tombstone where the actor was. The inbox stays
|
|
383
|
-
// publicly Append-able on purpose — closing it would make deliveries 401,
|
|
384
|
-
// which Mastodon treats as failure and retries, the opposite of the point.
|
|
385
|
-
// A Delete stops well-behaved servers; anything that keeps delivering gets a
|
|
386
|
-
// cheap 201 into a container we no longer read.
|
|
387
|
-
async retireActor() {
|
|
388
|
-
const { urls } = this;
|
|
389
|
-
const contacts = this.store.getContacts();
|
|
390
|
-
const inboxes = [...new Set(contacts.followers.map(f => f.sharedInbox || f.inbox).filter(Boolean))];
|
|
391
|
-
const deletedAt = new Date().toISOString();
|
|
392
|
-
await this.deliverer.deliverToAll(inboxes, wire.deleteActorActivity(urls, Date.parse(deletedAt)));
|
|
393
|
-
await podActor.writeTombstone(this.remote, urls, wire.tombstoneDoc(urls, deletedAt, this.config.kind));
|
|
394
|
-
this.store.setConfig({ ...this.store.getConfig(), retiredAt: deletedAt });
|
|
395
|
-
await this.store.flush();
|
|
396
|
-
this.log(`retired: Delete sent to ${inboxes.length} inbox(es), actor replaced with a Tombstone`);
|
|
397
|
-
return { inboxes: inboxes.length, deletedAt };
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
// Stop accepting mail without giving up the name: deliveries get an immediate
|
|
401
|
-
// 401 rather than a 201 into storage nobody will ever drain. WebFinger,
|
|
402
|
-
// host-meta and the actor stay published, so the handle still resolves.
|
|
403
|
-
async closeInbox() {
|
|
404
|
-
await podInbox.setPosture(this.remote, this.urls, 'closed');
|
|
405
|
-
const at = new Date().toISOString();
|
|
406
|
-
this.store.setConfig({ ...this.store.getConfig(), quiescedAt: at });
|
|
407
|
-
await this.store.flush();
|
|
408
|
-
this.log(`inbox closed — @${this.config.handle} still resolves but accepts nothing`);
|
|
409
|
-
return at;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
// Undo closeInbox: mail flows again and the actor is no longer quiesced.
|
|
413
|
-
async openInbox() {
|
|
414
|
-
await podInbox.setPosture(this.remote, this.urls, 'open');
|
|
415
|
-
const { quiescedAt, ...rest } = this.store.getConfig() || {};
|
|
416
|
-
this.store.setConfig(rest);
|
|
417
|
-
this.config.quiescedAt = undefined;
|
|
418
|
-
await this.store.flush();
|
|
419
|
-
this.log(`inbox re-opened — @${this.config.handle} is taking mail again`);
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
// Lock the inbox to a gateway: the public loses Append, so nothing reaches
|
|
423
|
-
// the pod except through the gateway's verify-at-the-door. Reversible with
|
|
424
|
-
// openInbox (public-Append) — the one-call rollback.
|
|
425
|
-
async lockInboxToGateway(gatewayWebId) {
|
|
426
|
-
await podInbox.setPosture(this.remote, this.urls, { gatewayWebId });
|
|
427
|
-
this.log(`inbox locked to gateway ${gatewayWebId} — public delivery is refused`);
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
// Tell the fediverse the account lives somewhere else now. Well-behaved
|
|
431
|
-
// servers migrate their followers to the target and stop delivering here.
|
|
432
|
-
async publishMove(target) {
|
|
433
|
-
const { urls } = this;
|
|
434
|
-
const contacts = this.store.getContacts();
|
|
435
|
-
const inboxes = [...new Set(contacts.followers.map(f => f.sharedInbox || f.inbox).filter(Boolean))];
|
|
436
|
-
const at = new Date().toISOString();
|
|
437
|
-
await this.deliverer.deliverToAll(inboxes, wire.moveActivity(urls, target, Date.parse(at)));
|
|
438
|
-
this.config.movedTo = target; // so the republish below carries it
|
|
439
|
-
this.store.setConfig({ ...this.store.getConfig(), movedTo: target, movedAt: at });
|
|
440
|
-
const gw = this.config.gateway;
|
|
441
|
-
const gwActive = gw && gw.url && gw.mode && gw.mode !== 'off';
|
|
442
|
-
const priv = await this.privateReady() === true;
|
|
443
|
-
const moderators = (this.config.moderators || []).length ? urls.moderators : null;
|
|
444
|
-
await podActor.writeMoved(this.remote, urls, this.actorDocFor({
|
|
445
|
-
priv, moderators, inbox: gwActive ? gw.url : null, movedTo: target,
|
|
446
|
-
}));
|
|
447
|
-
await this.store.flush();
|
|
448
|
-
this.log(`moved to ${target}: Move sent to ${inboxes.length} inbox(es), actor now advertises movedTo`);
|
|
449
|
-
return { inboxes: inboxes.length, target, movedAt: at };
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
// The gateway policy: a small PUBLIC document a keyless inbox gateway reads
|
|
453
|
-
// to decide, at the edge, what to forward and what to drop. It carries only
|
|
454
|
-
// public facts — the actor/followers URLs, the REAL pod inbox to forward to,
|
|
455
|
-
// the accepted-following list (already public) and a mirror of the blocklist.
|
|
456
|
-
// Publishing the blocklist here makes it public; that is a deliberate part of
|
|
457
|
-
// running a gateway, surfaced to the operator in the admin UI.
|
|
458
|
-
async publishGatewayPolicy() {
|
|
459
|
-
const { urls } = this;
|
|
460
|
-
const contacts = this.store.getContacts();
|
|
461
|
-
const bl = this.store.getBlocklist();
|
|
462
|
-
await podPolicy.write(this.remote, urls, {
|
|
463
|
-
v: 1,
|
|
464
|
-
actorUrl: urls.actor,
|
|
465
|
-
followersUrl: urls.followers,
|
|
466
|
-
followingUrl: urls.following,
|
|
467
|
-
inboxUrl: urls.inbox, // the pod inbox the gateway forwards to
|
|
468
|
-
notesPrefix: urls.notes,
|
|
469
|
-
kind: this.config.kind || 'person',
|
|
470
|
-
following: contacts.following.filter(f => f.accepted && !f.bsky).map(f => f.actor),
|
|
471
|
-
blocklist: { domains: bl.domains || [], actors: bl.actors || [] },
|
|
472
|
-
});
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
// collections.mjs
|
|
476
|
-
publishOutbox(...a) { return collections.publishOutbox(this, ...a); }
|
|
477
|
-
readPublishedOutbox(...a) { return collections.readPublishedOutbox(this, ...a); }
|
|
478
|
-
publishFollowers(...a) { return collections.publishFollowers(this, ...a); }
|
|
479
|
-
readPublishedFollowers(...a) { return collections.readPublishedFollowers(this, ...a); }
|
|
480
|
-
publishCollections(...a) { return collections.publishCollections(this, ...a); }
|
|
481
|
-
publishPending(...a) { return collections.publishPending(this, ...a); }
|
|
482
|
-
publishBlocked(...a) { return collections.publishBlocked(this, ...a); }
|
|
483
|
-
recordOutbox(...a) { return collections.recordOutbox(this, ...a); }
|
|
484
|
-
unrecordOutbox(...a) { return collections.unrecordOutbox(this, ...a); }
|
|
485
|
-
recordOwn(...a) { return own.recordOwn(this, ...a); }
|
|
486
|
-
backfillLiked() { return own.backfillLiked(this); }
|
|
487
|
-
inboxesFor(...a) { return notes.inboxesFor(this, ...a); }
|
|
488
|
-
noteToSelf(...a) { return notes.noteToSelf(this, ...a); }
|
|
489
|
-
updateObject(...a) { return notes.updateObject(this, ...a); }
|
|
490
|
-
unrecordOwn(...a) { return own.unrecordOwn(this, ...a); }
|
|
491
|
-
publishOwn(...a) { return own.publishOwn(this, ...a); }
|
|
492
|
-
ownSettled() { return own.ownSettled(this); }
|
|
493
|
-
publishFeatured(...a) { return collections.publishFeatured(this, ...a); }
|
|
494
|
-
|
|
495
|
-
// restore.mjs
|
|
496
|
-
reconcileFollowers(...a) { return restore.reconcileFollowers(this, ...a); }
|
|
497
|
-
reconcileOutbox(...a) { return restore.reconcileOutbox(this, ...a); }
|
|
498
|
-
rebuildStatuses(...a) { return restore.rebuildStatuses(this, ...a); }
|
|
499
|
-
healStatuses(...a) { return restore.healStatuses(this, ...a); }
|
|
500
|
-
|
|
501
|
-
// notes.mjs
|
|
502
|
-
ensureMediaContainer(...a) { return notes.ensureMediaContainer(this, ...a); }
|
|
503
|
-
_containerExists(...a) { return notes.containerExists(this, ...a); }
|
|
504
|
-
ensurePrivateContainer(...a) { return notes.ensurePrivateContainer(this, ...a); }
|
|
505
|
-
privateReady(...a) { return notes.privateReady(this, ...a); }
|
|
506
|
-
_mentionsFor(...a) { return notes.mentionsFor(this, ...a); }
|
|
507
|
-
publishNote(...a) { return notes.publishNote(this, ...a); }
|
|
508
|
-
authorizeQuote(...a) { return notes.authorizeQuote(this, ...a); }
|
|
509
|
-
withdrawQuote(...a) { return notes.withdrawQuote(this, ...a); }
|
|
510
|
-
grantQuote(...a) { return notes.grantQuote(this, ...a); }
|
|
511
|
-
publishObject(...a) { return notes.publishObject(this, ...a); }
|
|
512
|
-
updateNote(...a) { return notes.updateNote(this, ...a); }
|
|
513
|
-
|
|
514
|
-
// questions.mjs
|
|
515
|
-
publishQuestion(...a) { return questions.publishQuestion(this, ...a); }
|
|
516
|
-
_pollActivity(...a) { return questions.pollActivity(this, ...a); }
|
|
517
|
-
recordVote(...a) { return questions.recordVote(this, ...a); }
|
|
518
|
-
_pollDirty(...a) { return questions.pollDirty(this, ...a); }
|
|
519
|
-
republishPoll(...a) { return questions.republishPoll(this, ...a); }
|
|
520
|
-
closeDuePolls(...a) { return questions.closeDuePolls(this, ...a); }
|
|
521
|
-
stopPolls(...a) { return questions.stopPolls(this, ...a); }
|
|
522
|
-
}
|