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
package/dist/handler.js
CHANGED
|
@@ -1,79 +1,58 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// handler.ts — the CSS HttpHandler. Thin by design: claim only the front's
|
|
3
|
-
// routes (canHandle),
|
|
4
|
-
//
|
|
5
|
-
//
|
|
3
|
+
// routes and the identities' (canHandle), and hand each request to the part
|
|
4
|
+
// that answers it: an identity's own surface, the door for a delivery to its
|
|
5
|
+
// inbox, or the Gateway's front. The state every part shares lives here; the
|
|
6
|
+
// parts are mounts.ts (which pod a request belongs to), identities.ts
|
|
7
|
+
// (starting and stopping them), opt-in.ts (what an owner asks), door.ts and
|
|
8
|
+
// front.ts.
|
|
6
9
|
//
|
|
7
10
|
// componentsjs-generator reads FediPodServerArgs to emit one component
|
|
8
11
|
// parameter per field, so the config injects each by name.
|
|
12
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
15
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
16
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
17
|
+
}
|
|
18
|
+
Object.defineProperty(o, k2, desc);
|
|
19
|
+
}) : (function(o, m, k, k2) {
|
|
20
|
+
if (k2 === undefined) k2 = k;
|
|
21
|
+
o[k2] = m[k];
|
|
22
|
+
}));
|
|
23
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
24
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
25
|
+
}) : function(o, v) {
|
|
26
|
+
o["default"] = v;
|
|
27
|
+
});
|
|
28
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
29
|
+
var ownKeys = function(o) {
|
|
30
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
31
|
+
var ar = [];
|
|
32
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
33
|
+
return ar;
|
|
34
|
+
};
|
|
35
|
+
return ownKeys(o);
|
|
36
|
+
};
|
|
37
|
+
return function (mod) {
|
|
38
|
+
if (mod && mod.__esModule) return mod;
|
|
39
|
+
var result = {};
|
|
40
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
41
|
+
__setModuleDefault(result, mod);
|
|
42
|
+
return result;
|
|
43
|
+
};
|
|
44
|
+
})();
|
|
9
45
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
46
|
exports.FediPodServerHandler = void 0;
|
|
11
|
-
const node_fs_1 = require("node:fs");
|
|
12
|
-
const node_path_1 = require("node:path");
|
|
13
|
-
const node_crypto_1 = require("node:crypto");
|
|
14
47
|
const community_server_1 = require("@solid/community-server");
|
|
15
48
|
const claims_1 = require("./claims");
|
|
16
|
-
const adapt_1 = require("./adapt");
|
|
17
49
|
const store_css_1 = require("./store-css");
|
|
18
|
-
const store_pod_1 = require("./store-pod");
|
|
19
50
|
const directory_1 = require("./directory");
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
function pathContains(a, b) {
|
|
26
|
-
return a === b || b.startsWith(a + '/') || a === '';
|
|
27
|
-
}
|
|
28
|
-
// The JS front-core is FediPod's own ESM tree, reached at runtime. A real
|
|
29
|
-
// dynamic import() built via Function keeps tsc from downleveling it to
|
|
30
|
-
// require() — which cannot load an ESM module with top-level await under a
|
|
31
|
-
// CommonJS build.
|
|
32
|
-
//
|
|
33
|
-
// Two layouts carry that tree: the published package ships its own copy of
|
|
34
|
-
// lib/ beside dist/ (prepack puts it there), and a repo checkout reaches the
|
|
35
|
-
// repo's lib/ three levels up. Prefer the package's own copy when it exists.
|
|
36
|
-
const LIB_ROOT = (0, node_fs_1.existsSync)((0, node_path_1.join)(__dirname, '../lib/server/embed.mjs')) ? '../lib' : '../../../lib';
|
|
37
|
-
const FRONT_CORE = `${LIB_ROOT}/gateway/front-core.mjs`;
|
|
38
|
-
const EMBED = `${LIB_ROOT}/server/embed.mjs`;
|
|
39
|
-
const PLACE = `${LIB_ROOT}/core/place.mjs`;
|
|
40
|
-
const TRANSPORT = `${LIB_ROOT}/pod/transport.mjs`;
|
|
41
|
-
const esmImport = new Function('s', 'return import(s)');
|
|
42
|
-
// The front's pages and the files they load, carried in the same two layouts
|
|
43
|
-
// as lib/. A missing file is not fatal: the route it feeds answers 404.
|
|
44
|
-
const WEB_ROOT = (0, node_fs_1.existsSync)((0, node_path_1.join)(__dirname, '../web/front/run.html'))
|
|
45
|
-
? (0, node_path_1.join)(__dirname, '../web/front') : (0, node_path_1.join)(__dirname, '../../../web/front');
|
|
46
|
-
const webFile = (name) => {
|
|
47
|
-
try {
|
|
48
|
-
return (0, node_fs_1.readFileSync)((0, node_path_1.join)(WEB_ROOT, name), 'utf8');
|
|
49
|
-
}
|
|
50
|
-
catch {
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
// A pod that will not come up yet is usually a pod still being created by the
|
|
55
|
-
// server that is booting. Keep asking, slower each time, up to a few minutes.
|
|
56
|
-
const START_RETRY_MS = [2_000, 5_000, 15_000, 60_000, 300_000];
|
|
57
|
-
/** The identity's name, from its pod URL. Mirrors handleFor in lib/embed.mjs. */
|
|
58
|
-
function deriveHandle(podBase) {
|
|
59
|
-
const u = new URL(podBase);
|
|
60
|
-
const segments = u.pathname.split('/').filter((seg) => seg.length > 0);
|
|
61
|
-
return segments.length > 0 ? segments[segments.length - 1] : u.hostname.split('.')[0];
|
|
62
|
-
}
|
|
63
|
-
/** The opt-in page's path: leading slash, no trailing one, the default when unset. */
|
|
64
|
-
function normalizeRunPath(raw) {
|
|
65
|
-
const said = String(raw ?? '').trim().replace(/\/+$/u, '');
|
|
66
|
-
if (!said)
|
|
67
|
-
return claims_1.DEFAULT_RUN_PATH;
|
|
68
|
-
return said.startsWith('/') ? said : `/${said}`;
|
|
69
|
-
}
|
|
70
|
-
/** A door path always has both slashes, so claiming and stripping agree. */
|
|
71
|
-
function normalizeUiPath(raw) {
|
|
72
|
-
if (raw === '')
|
|
73
|
-
return '';
|
|
74
|
-
const path = raw ?? '/fp/';
|
|
75
|
-
return `/${path.replace(/^\/+|\/+$/gu, '')}/`;
|
|
76
|
-
}
|
|
51
|
+
const mounts_1 = require("./mounts");
|
|
52
|
+
const identities = __importStar(require("./identities"));
|
|
53
|
+
const optIn = __importStar(require("./opt-in"));
|
|
54
|
+
const door_1 = require("./door");
|
|
55
|
+
const front_1 = require("./front");
|
|
77
56
|
class FediPodServerHandler extends community_server_1.HttpHandler {
|
|
78
57
|
args;
|
|
79
58
|
io;
|
|
@@ -120,288 +99,18 @@ class FediPodServerHandler extends community_server_1.HttpHandler {
|
|
|
120
99
|
this.frontHost = args.frontHost || new URL(args.frontOrigin).hostname;
|
|
121
100
|
// One answer for both the claim and the route, so a server cannot answer
|
|
122
101
|
// at one path and refuse at another.
|
|
123
|
-
this.runPath = normalizeRunPath(args.runPath);
|
|
124
|
-
this.uiPath = normalizeUiPath(args.agentUiPath);
|
|
102
|
+
this.runPath = (0, mounts_1.normalizeRunPath)(args.runPath);
|
|
103
|
+
this.uiPath = (0, mounts_1.normalizeUiPath)(args.agentUiPath);
|
|
125
104
|
this.registry = args.agentRuntimeOptIn
|
|
126
105
|
? (0, directory_1.makeAgentRegistry)(this.io, absolute(args.agentRegistryContainer ?? '/.internal/fedipod/agents/'))
|
|
127
106
|
: null;
|
|
128
107
|
}
|
|
129
|
-
/**
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
*
|
|
134
|
-
* A HOST-ROOT or subdomain pod needs an origin of its own, and it may not be
|
|
135
|
-
* the front's host: the whole surface answers at the origin root, so two of
|
|
136
|
-
* them, or one sharing the front, would collide. A SUFFIX pod lives on a path
|
|
137
|
-
* (`server/aisha/`), so it may share its host — with the front and with other
|
|
138
|
-
* suffix pods — provided no claim already contains or nests under its path.
|
|
139
|
-
*/
|
|
140
|
-
validateAgentPod(podBase) {
|
|
141
|
-
let host;
|
|
142
|
-
let mount;
|
|
143
|
-
try {
|
|
144
|
-
const u = new URL(podBase);
|
|
145
|
-
host = u.host.toLowerCase();
|
|
146
|
-
mount = mountOf(podBase);
|
|
147
|
-
}
|
|
148
|
-
catch {
|
|
149
|
-
throw new Error(`not a pod URL: ${podBase}`);
|
|
150
|
-
}
|
|
151
|
-
if (mount === '') {
|
|
152
|
-
// A pod at the root of its host: it owns the whole origin, so it cannot
|
|
153
|
-
// share it with the front or with any other claim.
|
|
154
|
-
if (host.split(':')[0] === String(this.frontHost).toLowerCase()) {
|
|
155
|
-
throw new Error(`${podBase} is on the front's own host — give the identity its own origin, or host it on a path`);
|
|
156
|
-
}
|
|
157
|
-
for (const c of this.claimed.values()) {
|
|
158
|
-
if (c.host === host) {
|
|
159
|
-
throw new Error(`the host ${host} already carries an identity — a host-root identity needs an origin of its own`);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
else {
|
|
164
|
-
// A pod on a path: it owns only its subtree. Refuse anything that already
|
|
165
|
-
// contains it or that it would contain, so no identity can answer under
|
|
166
|
-
// another's path (and none straddles the front's own routes underneath).
|
|
167
|
-
for (const c of this.claimed.values()) {
|
|
168
|
-
if (c.host !== host)
|
|
169
|
-
continue;
|
|
170
|
-
if (pathContains(c.mount, mount) || pathContains(mount, c.mount)) {
|
|
171
|
-
throw new Error(`${podBase} nests with the identity already at ${c.podBase} — a suffixed pod owns only its own subtree`);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
const handle = deriveHandle(podBase);
|
|
176
|
-
const holder = this.agentHandles.get(handle);
|
|
177
|
-
if (holder && holder !== podBase) {
|
|
178
|
-
throw new Error(`the name ${handle} already belongs to ${holder} — two identities cannot share it`);
|
|
179
|
-
}
|
|
180
|
-
return { host, mount, podBase, handle };
|
|
181
|
-
}
|
|
182
|
-
/**
|
|
183
|
-
* The claim a request belongs to, or null. A request matches when its host is
|
|
184
|
-
* the claim's host and its path is at or under the claim's mount; the deepest
|
|
185
|
-
* mount wins, so a suffix pod's own routes are never swallowed by a shallower
|
|
186
|
-
* claim on the same host.
|
|
187
|
-
*/
|
|
188
|
-
resolveClaim(host, pathname) {
|
|
189
|
-
let best = null;
|
|
190
|
-
for (const c of this.claimed.values()) {
|
|
191
|
-
if (c.host !== host)
|
|
192
|
-
continue;
|
|
193
|
-
if (c.mount === '' || pathname === c.mount || pathname.startsWith(c.mount + '/')) {
|
|
194
|
-
if (!best || c.mount.length > best.mount.length)
|
|
195
|
-
best = c;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
return best;
|
|
199
|
-
}
|
|
200
|
-
/** A request path relative to a mount: `/aisha/ap/actor` under `/aisha` → `/ap/actor`. */
|
|
201
|
-
stripMount(pathname, mount) {
|
|
202
|
-
if (!mount)
|
|
203
|
-
return pathname;
|
|
204
|
-
return pathname.slice(mount.length) || '/';
|
|
205
|
-
}
|
|
206
|
-
/**
|
|
207
|
-
* Start an agent for each opted-in pod. Runs before the server listens, so
|
|
208
|
-
* the identities come up in the background and boot is never held on a pod.
|
|
209
|
-
*/
|
|
210
|
-
async initialize() {
|
|
211
|
-
if (!this.registry)
|
|
212
|
-
return;
|
|
213
|
-
const runs = this.runsIdentities();
|
|
214
|
-
// The stock CSS CLI installs no signal handlers, so a SIGTERM (systemd
|
|
215
|
-
// stop, docker stop, Ctrl+C) killed the process with agent state
|
|
216
|
-
// unflushed and the lease held for its whole TTL. Flush first, bounded,
|
|
217
|
-
// then re-raise so the process still dies the way it was asked to.
|
|
218
|
-
if (runs && !this.onSignal) {
|
|
219
|
-
this.onSignal = (signal) => {
|
|
220
|
-
const timeout = new Promise((resolve) => { setTimeout(resolve, 5_000).unref?.(); });
|
|
221
|
-
void Promise.race([this.finalize(), timeout]).then(() => {
|
|
222
|
-
process.kill(process.pid, signal);
|
|
223
|
-
});
|
|
224
|
-
};
|
|
225
|
-
process.once('SIGTERM', this.onSignal);
|
|
226
|
-
process.once('SIGINT', this.onSignal);
|
|
227
|
-
}
|
|
228
|
-
// Identities run in the primary, and requests are answered by the workers,
|
|
229
|
-
// so with more than one worker an identity federates but cannot be reached:
|
|
230
|
-
// its client API, its owner pages and its live feed are in a process no
|
|
231
|
-
// request arrives at. Said once, by the process that has them.
|
|
232
|
-
if (runs && this.args.clusterManager && !this.args.clusterManager.isSingleThreaded()) {
|
|
233
|
-
this.logger.warn('FediPod is running in a multi-worker server. Deliveries are picked up by the inbox '
|
|
234
|
-
+ 'sweep rather than as they land, and no identity can answer its client API, its live feed or its '
|
|
235
|
-
+ "owner's pages while requests are served by other processes. Run with --workers 1.");
|
|
236
|
-
}
|
|
237
|
-
// Awaited, and BEFORE the server listens: a pod whose owner opted in must
|
|
238
|
-
// have its routes claimed from the first request after a restart, never
|
|
239
|
-
// served briefly by LDP. A failed load must not fail the boot — the rows
|
|
240
|
-
// persist, and the next start recovers them.
|
|
241
|
-
const pods = [];
|
|
242
|
-
try {
|
|
243
|
-
const keys = await this.registry.listKeys();
|
|
244
|
-
for (const key of keys) {
|
|
245
|
-
const row = await this.registry.get(key);
|
|
246
|
-
if (!row)
|
|
247
|
-
continue;
|
|
248
|
-
if (this.claimed.has(row.podBase) || this.identities.has(row.podBase))
|
|
249
|
-
continue; // already claimed
|
|
250
|
-
let claim;
|
|
251
|
-
try {
|
|
252
|
-
claim = this.validateAgentPod(row.podBase);
|
|
253
|
-
}
|
|
254
|
-
catch (e) {
|
|
255
|
-
this.logger.error(`opted-in pod ${row.podBase} no longer valid: ${e.message}`);
|
|
256
|
-
continue;
|
|
257
|
-
}
|
|
258
|
-
this.claimed.set(row.podBase, claim);
|
|
259
|
-
this.agentHandles.set(row.handle, row.podBase);
|
|
260
|
-
this.roots.set(row.podBase, row.root || 'fedipod/');
|
|
261
|
-
pods.push(row.podBase);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
catch (e) {
|
|
265
|
-
this.logger.error(`could not read the opt-in registry — opted-in identities are absent this boot: ${e.message}`);
|
|
266
|
-
}
|
|
267
|
-
if (pods.length > 0) {
|
|
268
|
-
this.logger.info(runs
|
|
269
|
-
? `FediPod agent enabled for ${pods.length} opted-in pod(s)`
|
|
270
|
-
: `FediPod claimed the routes of ${pods.length} opted-in pod(s); they are run elsewhere`);
|
|
271
|
-
}
|
|
272
|
-
// Claimed everywhere, run in one place. A process that does not run them
|
|
273
|
-
// must still not let a pod answer for a path that belongs to an identity.
|
|
274
|
-
if (runs)
|
|
275
|
-
for (const pod of pods)
|
|
276
|
-
void this.startIdentity(pod);
|
|
277
|
-
}
|
|
108
|
+
/** The shared state, for the parts beside this file. */
|
|
109
|
+
internals() { return this; }
|
|
110
|
+
/** Start an agent for each opted-in pod, before the server listens. */
|
|
111
|
+
initialize() { return identities.initialize(this); }
|
|
278
112
|
/** Stop every identity: timers cleared, state written, lease let go. */
|
|
279
|
-
|
|
280
|
-
this.stopping = true;
|
|
281
|
-
if (this.onSignal) {
|
|
282
|
-
process.removeListener('SIGTERM', this.onSignal);
|
|
283
|
-
process.removeListener('SIGINT', this.onSignal);
|
|
284
|
-
this.onSignal = null;
|
|
285
|
-
}
|
|
286
|
-
const running = [...this.identities.values()];
|
|
287
|
-
this.identities.clear();
|
|
288
|
-
this.surfaces.clear();
|
|
289
|
-
this.doorSecrets.clear();
|
|
290
|
-
this.starting.clear();
|
|
291
|
-
await Promise.allSettled(running.map(async (identity) => {
|
|
292
|
-
await identity.stop();
|
|
293
|
-
this.logger.info(`FediPod agent @${identity.handle} stopped`);
|
|
294
|
-
}));
|
|
295
|
-
}
|
|
296
|
-
/**
|
|
297
|
-
* One identity's door secret, in its own pod's state. `agentDataDir` is
|
|
298
|
-
* handed in as the place an identity set up before this kept it, so its
|
|
299
|
-
* owner's existing door link survives the move.
|
|
300
|
-
*/
|
|
301
|
-
async doorSecretFor(podBase, session, opts = {}) {
|
|
302
|
-
const { ensureDoorSecret } = await esmImport(EMBED);
|
|
303
|
-
return ensureDoorSecret(session ?? (0, store_pod_1.makeStoreSession)(this.args.resourceStore, podBase), podBase, {
|
|
304
|
-
...opts,
|
|
305
|
-
root: this.roots.get(podBase) ?? 'fedipod/',
|
|
306
|
-
dataDir: this.args.agentDataDir,
|
|
307
|
-
handle: deriveHandle(podBase),
|
|
308
|
-
log: (message) => { this.logger.info(`@${deriveHandle(podBase)}: ${message}`); },
|
|
309
|
-
});
|
|
310
|
-
}
|
|
311
|
-
async startIdentity(podBase) {
|
|
312
|
-
if (this.starting.has(podBase) || this.identities.has(podBase))
|
|
313
|
-
return;
|
|
314
|
-
this.starting.add(podBase);
|
|
315
|
-
this.startCancelled.delete(podBase);
|
|
316
|
-
const session = (0, store_pod_1.makeStoreSession)(this.args.resourceStore, podBase);
|
|
317
|
-
try {
|
|
318
|
-
for (let attempt = 0; !this.stopping && !this.startCancelled.has(podBase); attempt++) {
|
|
319
|
-
try {
|
|
320
|
-
const { startEmbeddedAgent } = await esmImport(EMBED);
|
|
321
|
-
// The secret is in the map BEFORE the surface can exist, so the
|
|
322
|
-
// gate's resolver never comes up empty — empty would mean gate-off.
|
|
323
|
-
if (!this.doorSecrets.has(podBase)) {
|
|
324
|
-
const door = await this.doorSecretFor(podBase, session);
|
|
325
|
-
this.doorSecrets.set(podBase, door.secret);
|
|
326
|
-
this.logger.info(`door secret for @${deriveHandle(podBase)} is in its pod at ${door.url}`);
|
|
327
|
-
}
|
|
328
|
-
const identity = await startEmbeddedAgent({
|
|
329
|
-
podBase,
|
|
330
|
-
root: this.roots.get(podBase) ?? 'fedipod/',
|
|
331
|
-
dataDir: this.args.agentDataDir,
|
|
332
|
-
session,
|
|
333
|
-
resourceStore: this.args.resourceStore,
|
|
334
|
-
webIdSuffix: this.args.agentWebIdSuffix ?? 'profile/card#me',
|
|
335
|
-
pollSeconds: this.args.agentPollSeconds ?? null,
|
|
336
|
-
autoAcceptFollows: this.args.agentAutoAcceptFollows !== false,
|
|
337
|
-
gateToken: () => this.doorSecrets.get(podBase),
|
|
338
|
-
uiPath: this.uiPath,
|
|
339
|
-
log: (message) => {
|
|
340
|
-
this.logger.info(message);
|
|
341
|
-
},
|
|
342
|
-
});
|
|
343
|
-
// Stopped, or opted out, while this one was still coming up.
|
|
344
|
-
if (this.stopping || this.startCancelled.has(podBase)) {
|
|
345
|
-
await identity.stop();
|
|
346
|
-
return;
|
|
347
|
-
}
|
|
348
|
-
this.identities.set(podBase, identity);
|
|
349
|
-
this.surfaces.set(podBase, identity);
|
|
350
|
-
this.logger.info(`FediPod agent @${identity.handle} running on ${podBase}`);
|
|
351
|
-
// A suffix pod cannot answer WebFinger for itself — its host root is
|
|
352
|
-
// the front's — so it is followable ONLY through the door's apex
|
|
353
|
-
// dispatch. Front it whether or not auto-front is on: the row is the
|
|
354
|
-
// whole of what makes @handle@host resolve to it.
|
|
355
|
-
if (this.args.agentAutoFront || this.claimed.get(podBase)?.mount) {
|
|
356
|
-
await this.frontIdentity(podBase, identity);
|
|
357
|
-
}
|
|
358
|
-
return;
|
|
359
|
-
}
|
|
360
|
-
catch (e) {
|
|
361
|
-
const wait = START_RETRY_MS[Math.min(attempt, START_RETRY_MS.length - 1)];
|
|
362
|
-
this.logger.warn(`FediPod agent for ${podBase} did not start: ${e.message} — retrying in ${Math.round(wait / 1000)}s`);
|
|
363
|
-
await new Promise((resolve) => {
|
|
364
|
-
setTimeout(resolve, wait).unref?.();
|
|
365
|
-
});
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
finally {
|
|
370
|
-
this.starting.delete(podBase);
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
/**
|
|
374
|
-
* When this server is also the door, give a running identity a
|
|
375
|
-
* @handle@<frontHost> address: one inbox-only directory row so the
|
|
376
|
-
* shared-domain handle resolves and the door can take verified delivery for
|
|
377
|
-
* it. The row names the identity's own tree, which is where its inbox and
|
|
378
|
-
* its actor are — the door writes deliveries there and the agent watches
|
|
379
|
-
* that container. A row written by an attach belongs to its owner and is
|
|
380
|
-
* left alone; one this server wrote itself is corrected. The identity keeps
|
|
381
|
-
* its own ids on the pod; nothing is moved.
|
|
382
|
-
*/
|
|
383
|
-
async frontIdentity(podBase, identity) {
|
|
384
|
-
const { handle, podHome, actorUrl } = identity;
|
|
385
|
-
try {
|
|
386
|
-
const existing = await this.dir.lookup(handle);
|
|
387
|
-
const row = (0, directory_1.frontRow)(existing, {
|
|
388
|
-
handle, podHome, actorUrl,
|
|
389
|
-
kind: identity.agent?.store?.getConfig?.()?.kind === 'group' ? 'group' : 'person',
|
|
390
|
-
gatewayWebId: this.args.gatewayWebId ?? null,
|
|
391
|
-
hmacSecret: (0, node_crypto_1.randomBytes)(32).toString('base64'),
|
|
392
|
-
inboxOnly: true,
|
|
393
|
-
}, podBase);
|
|
394
|
-
if (!row)
|
|
395
|
-
return;
|
|
396
|
-
await this.dir.putDirectory(handle, row);
|
|
397
|
-
this.logger.info(existing
|
|
398
|
-
? `FediPod: the door's record of @${handle} now names ${podHome}`
|
|
399
|
-
: `FediPod: @${handle}@${this.frontHost} now resolves to ${podBase}`);
|
|
400
|
-
}
|
|
401
|
-
catch (e) {
|
|
402
|
-
this.logger.warn(`FediPod: could not front @${handle}: ${e.message}`);
|
|
403
|
-
}
|
|
404
|
-
}
|
|
113
|
+
finalize() { return identities.finalize(this); }
|
|
405
114
|
async canHandle({ request }) {
|
|
406
115
|
const host = String(request.headers.host ?? '').toLowerCase();
|
|
407
116
|
const pathname = new URL(request.url ?? '/', `https://${host}`).pathname;
|
|
@@ -415,22 +124,11 @@ class FediPodServerHandler extends community_server_1.HttpHandler {
|
|
|
415
124
|
// starting, and then stop being served once it has. The path is matched
|
|
416
125
|
// relative to the claim's mount, so a suffix pod's `/aisha/ap/actor` is
|
|
417
126
|
// judged as `/ap/actor`.
|
|
418
|
-
const c =
|
|
419
|
-
if (c && (0, claims_1.agentClaims)({ pathname:
|
|
127
|
+
const c = (0, mounts_1.resolveClaim)(this, host, pathname);
|
|
128
|
+
if (c && (0, claims_1.agentClaims)({ pathname: (0, mounts_1.stripMount)(pathname, c.mount), method: request.method }, this.uiPath))
|
|
420
129
|
return;
|
|
421
130
|
throw new Error('not a gateway route'); // reject → CSS's LDP handler takes it
|
|
422
131
|
}
|
|
423
|
-
/**
|
|
424
|
-
* Whether identities run in this process. They run in one: the lease admits
|
|
425
|
-
* a single drainer, and the state each one holds is in memory. With workers,
|
|
426
|
-
* that process is the primary — which serves no requests, so every process
|
|
427
|
-
* still has to know which hosts belong to an identity even though only one
|
|
428
|
-
* of them can answer for it.
|
|
429
|
-
*/
|
|
430
|
-
runsIdentities() {
|
|
431
|
-
const cluster = this.args.clusterManager;
|
|
432
|
-
return !cluster || cluster.isSingleThreaded() || cluster.isPrimary();
|
|
433
|
-
}
|
|
434
132
|
/**
|
|
435
133
|
* The identity a request belongs to (matched by host and mount) and the
|
|
436
134
|
* request path relative to that identity's mount, or null. The identity is
|
|
@@ -438,195 +136,23 @@ class FediPodServerHandler extends community_server_1.HttpHandler {
|
|
|
438
136
|
* upgrade, which has only the request to go on.
|
|
439
137
|
*/
|
|
440
138
|
matchIdentity(host, pathname = '/') {
|
|
441
|
-
const c =
|
|
139
|
+
const c = (0, mounts_1.resolveClaim)(this, String(host ?? '').toLowerCase(), pathname);
|
|
442
140
|
if (!c)
|
|
443
141
|
return null;
|
|
444
|
-
return { identity: this.surfaces.get(c.podBase), rel:
|
|
142
|
+
return { identity: this.surfaces.get(c.podBase), rel: (0, mounts_1.stripMount)(pathname, c.mount) };
|
|
445
143
|
}
|
|
446
|
-
/**
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
* reply and nowhere else. Re-opting-in rotates the secret — that is how a
|
|
450
|
-
* lost one is recovered.
|
|
451
|
-
*/
|
|
452
|
-
/**
|
|
453
|
-
* The WebIDs behind the account session in the request, or none. The server
|
|
454
|
-
* keeps that session in a `css-account` cookie; its own account API is what
|
|
455
|
-
* says which WebIDs the session owns, and it takes the same cookie. Nothing
|
|
456
|
-
* here trusts the cookie's presence — the account API does the deciding, and
|
|
457
|
-
* the caller picks the WebID that owns the pod being claimed.
|
|
458
|
-
*/
|
|
459
|
-
async webIdsFromSession(request) {
|
|
460
|
-
const cookie = request.headers.get('cookie');
|
|
461
|
-
if (!cookie || !/(?:^|;\s*)css-account=/u.test(cookie))
|
|
462
|
-
return [];
|
|
463
|
-
const base = this.args.frontOrigin.replace(/\/$/u, '');
|
|
464
|
-
const asJson = { cookie, accept: 'application/json' };
|
|
465
|
-
try {
|
|
466
|
-
const index = await fetch(`${base}/.account/`, { headers: asJson });
|
|
467
|
-
if (!index.ok)
|
|
468
|
-
return [];
|
|
469
|
-
const controls = (await index.json()).controls;
|
|
470
|
-
const webIdLink = controls?.account?.webId;
|
|
471
|
-
if (!webIdLink)
|
|
472
|
-
return [];
|
|
473
|
-
const linked = await fetch(webIdLink, { headers: asJson });
|
|
474
|
-
if (!linked.ok)
|
|
475
|
-
return [];
|
|
476
|
-
const links = (await linked.json()).webIdLinks ?? {};
|
|
477
|
-
return Object.keys(links);
|
|
478
|
-
}
|
|
479
|
-
catch {
|
|
480
|
-
return [];
|
|
481
|
-
}
|
|
144
|
+
/** What a pod owner asks of this server; see opt-in.ts. */
|
|
145
|
+
webIdsFromSession(request) {
|
|
146
|
+
return optIn.webIdsFromSession(this, request);
|
|
482
147
|
}
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
return { httpStatus: 501, error: 'this server does not offer runtime opt-in' };
|
|
486
|
-
if (this.args.clusterManager && !this.args.clusterManager.isSingleThreaded()) {
|
|
487
|
-
return { httpStatus: 503, error: 'runtime opt-in needs a single-worker server (--workers 1)' };
|
|
488
|
-
}
|
|
489
|
-
const base = podBase.endsWith('/') ? podBase : `${podBase}/`;
|
|
490
|
-
const handle = deriveHandle(base);
|
|
491
|
-
// Already running here from an earlier opt-in: proving pod
|
|
492
|
-
// control again buys a fresh secret, nothing else.
|
|
493
|
-
if (this.agentHandles.get(handle) === base) {
|
|
494
|
-
const door = await this.doorSecretFor(base, undefined, { rotate: true });
|
|
495
|
-
this.doorSecrets.set(base, door.secret);
|
|
496
|
-
return { httpStatus: 201, ok: true, handle, host: new URL(base).host.toLowerCase(),
|
|
497
|
-
doorSecret: door.secret, doorPath: this.uiPath, status: 'rotated' };
|
|
498
|
-
}
|
|
499
|
-
let claim;
|
|
500
|
-
try {
|
|
501
|
-
claim = this.validateAgentPod(base);
|
|
502
|
-
}
|
|
503
|
-
catch (e) {
|
|
504
|
-
return { httpStatus: 409, error: e.message };
|
|
505
|
-
}
|
|
506
|
-
const { host } = claim;
|
|
507
|
-
// Where on the pod: the container its owner named holds `fedipod/`. The
|
|
508
|
-
// place is recorded in their public type index — made only on their yes —
|
|
509
|
-
// before anything else is written, and read here with the server's own
|
|
510
|
-
// access to the pod.
|
|
511
|
-
const place = await esmImport(PLACE);
|
|
512
|
-
const chosen = place.chosenRoot(base, container);
|
|
513
|
-
if (chosen.problem || !chosen.root)
|
|
514
|
-
return { httpStatus: 400, error: `Where to store it: ${chosen.problem}.` };
|
|
515
|
-
const root = chosen.root;
|
|
516
|
-
const { PodTransport } = await esmImport(TRANSPORT);
|
|
517
|
-
const pod = new PodTransport((0, store_pod_1.makeStoreSession)(this.args.resourceStore, base), { webId });
|
|
518
|
-
if (!createIndex && !await place.hasPublicIndex(pod, base)) {
|
|
519
|
-
return { httpStatus: 409, code: 'needs-index',
|
|
520
|
-
error: 'Your pod has no public type index, the list Solid apps use to find your things. '
|
|
521
|
-
+ 'FediPod needs one to record where your account lives.' };
|
|
522
|
-
}
|
|
523
|
-
try {
|
|
524
|
-
await place.recordPlace(pod, base, `${base}${root}ap/actor`, { create: createIndex });
|
|
525
|
-
}
|
|
526
|
-
catch (e) {
|
|
527
|
-
return { httpStatus: 500, error: `could not record where the account lives in your type index (${e.message})` };
|
|
528
|
-
}
|
|
529
|
-
this.roots.set(base, root);
|
|
530
|
-
try {
|
|
531
|
-
await this.registry.add({ podBase: base, handle, host, webId, root, optedInAt: new Date().toISOString() });
|
|
532
|
-
}
|
|
533
|
-
catch (e) {
|
|
534
|
-
this.logger.error(`opt-in row for ${base} could not be written: ${e.message}`);
|
|
535
|
-
return { httpStatus: 500, error: 'could not record the opt-in' };
|
|
536
|
-
}
|
|
537
|
-
// From this instant the pod's identity routes answer 503 instead of LDP,
|
|
538
|
-
// until the agent registers its surface.
|
|
539
|
-
this.claimed.set(base, claim);
|
|
540
|
-
this.agentHandles.set(handle, base);
|
|
541
|
-
const door = await this.doorSecretFor(base, undefined, { rotate: true });
|
|
542
|
-
this.doorSecrets.set(base, door.secret);
|
|
543
|
-
void this.startIdentity(base);
|
|
544
|
-
this.logger.info(`runtime opt-in: @${handle} on ${base} (door secret in its pod at ${door.url})`);
|
|
545
|
-
return { httpStatus: 201, ok: true, handle, host,
|
|
546
|
-
doorSecret: door.secret, doorPath: this.uiPath, status: 'starting' };
|
|
148
|
+
optInPod(a) {
|
|
149
|
+
return optIn.optInPod(this, a);
|
|
547
150
|
}
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
* handle, the host its address carries, and whether it runs here already.
|
|
551
|
-
*/
|
|
552
|
-
async describePod({ podBase }) {
|
|
553
|
-
const base = podBase.endsWith('/') ? podBase : `${podBase}/`;
|
|
554
|
-
const handle = deriveHandle(base);
|
|
555
|
-
let host;
|
|
556
|
-
try {
|
|
557
|
-
host = this.validateAgentPod(base).host;
|
|
558
|
-
}
|
|
559
|
-
catch {
|
|
560
|
-
host = new URL(base).host.toLowerCase();
|
|
561
|
-
}
|
|
562
|
-
const running = this.agentHandles.get(handle) === base;
|
|
563
|
-
// The management page's door takes its key once in the address and keeps
|
|
564
|
-
// the browser in with a cookie. Only the owner's own page ever asks for
|
|
565
|
-
// this description, so the key rides on the link for a running account.
|
|
566
|
-
// Read from the pod, where the key always is, rather than from what this
|
|
567
|
-
// process happens to hold: an account still starting has none in memory.
|
|
568
|
-
let key;
|
|
569
|
-
if (running) {
|
|
570
|
-
try {
|
|
571
|
-
key = (await this.doorSecretFor(base)).secret;
|
|
572
|
-
}
|
|
573
|
-
catch {
|
|
574
|
-
key = this.doorSecrets.get(base);
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
const door = base.replace(/\/$/u, '') + this.uiPath;
|
|
578
|
-
return { handle, host, address: `@${handle}@${host}`, running,
|
|
579
|
-
manage: key ? `${door}?dk-token=${encodeURIComponent(key)}` : door };
|
|
151
|
+
describePod(a) {
|
|
152
|
+
return optIn.describePod(this, a);
|
|
580
153
|
}
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
if (!this.registry)
|
|
584
|
-
return { httpStatus: 501, error: 'this server does not offer runtime opt-in' };
|
|
585
|
-
const base = podBase.endsWith('/') ? podBase : `${podBase}/`;
|
|
586
|
-
const host = new URL(base).host.toLowerCase();
|
|
587
|
-
const key = (0, directory_1.agentKey)(host, base);
|
|
588
|
-
const row = await this.registry.get(key);
|
|
589
|
-
if (!row || row.podBase !== base)
|
|
590
|
-
return { httpStatus: 404, error: 'this pod has not opted in' };
|
|
591
|
-
this.claimed.delete(base); // routes fall to LDP now
|
|
592
|
-
this.startCancelled.add(base); // a pending start stands down
|
|
593
|
-
const identity = this.identities.get(base);
|
|
594
|
-
this.identities.delete(base);
|
|
595
|
-
this.surfaces.delete(base);
|
|
596
|
-
this.agentHandles.delete(row.handle);
|
|
597
|
-
this.doorSecrets.delete(base);
|
|
598
|
-
if (identity)
|
|
599
|
-
await identity.stop();
|
|
600
|
-
await this.registry.remove(key);
|
|
601
|
-
this.logger.info(`runtime opt-out: @${row.handle} on ${base} — the pod serves plain LDP again`);
|
|
602
|
-
return { httpStatus: 200, ok: true, stopped: Boolean(identity) };
|
|
603
|
-
}
|
|
604
|
-
/**
|
|
605
|
-
* A delivery to an identity's own inbox, verified here — the server that
|
|
606
|
-
* stores the inbox is the one the request reached, so the signature is
|
|
607
|
-
* checked while its headers exist and the receipt is written beside the
|
|
608
|
-
* activity. The same door code the front runs; nothing is renamed.
|
|
609
|
-
*/
|
|
610
|
-
async deliverAtDoor(identity, request, response) {
|
|
611
|
-
const { deliverToInbox } = await esmImport(EMBED);
|
|
612
|
-
let whatwg;
|
|
613
|
-
try {
|
|
614
|
-
// The pod's own origin: the signature covers the path and the Host header, and both are the pod's.
|
|
615
|
-
whatwg = await (0, adapt_1.nodeToWhatwg)(request, new URL(identity.podHome).origin);
|
|
616
|
-
}
|
|
617
|
-
catch (e) {
|
|
618
|
-
const status = e.statusCode === 413 ? 413 : 400;
|
|
619
|
-
response.writeHead(status, { 'content-type': 'application/json' });
|
|
620
|
-
response.end(JSON.stringify({ error: e.message }));
|
|
621
|
-
return;
|
|
622
|
-
}
|
|
623
|
-
const out = await deliverToInbox(identity.agent, whatwg, {
|
|
624
|
-
podPut: (url, body, ct) => this.podPut(url, body, ct),
|
|
625
|
-
gatewayWebId: this.args.gatewayWebId ?? null,
|
|
626
|
-
});
|
|
627
|
-
this.logger.info(`FediPod: delivery for @${identity.handle} at the door — ${out.reason} (${out.status})`);
|
|
628
|
-
response.writeHead(out.status, { 'content-type': 'application/json' });
|
|
629
|
-
response.end(JSON.stringify({ reason: out.reason }));
|
|
154
|
+
optOutPod(a) {
|
|
155
|
+
return optIn.optOutPod(this, a);
|
|
630
156
|
}
|
|
631
157
|
async handle({ request, response }) {
|
|
632
158
|
const host = String(request.headers.host ?? '').toLowerCase();
|
|
@@ -634,118 +160,32 @@ class FediPodServerHandler extends community_server_1.HttpHandler {
|
|
|
634
160
|
// The front's own routes win first — its dispatch, WebFinger and API sit at
|
|
635
161
|
// the apex above every suffix pod — so a claim is consulted only where the
|
|
636
162
|
// front does not answer.
|
|
637
|
-
const claimed = (0, claims_1.claims)({ host, pathname }, this.frontHost, this.runPath) ? null :
|
|
638
|
-
if (claimed)
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
}
|
|
651
|
-
response.writeHead(503, { 'content-type': 'application/json', 'retry-after': '5' });
|
|
652
|
-
response.end(JSON.stringify({ error: 'this identity is still starting' }));
|
|
653
|
-
return;
|
|
654
|
-
}
|
|
655
|
-
// This identity's own inbox path — <mount>/<root>/ap/inbox/, from its
|
|
656
|
-
// actor, so it already carries the mount for a suffix pod.
|
|
657
|
-
const inboxPath = new URL(identity.actorUrl).pathname.replace(/ap\/actor$/u, 'ap/inbox/');
|
|
658
|
-
if (pathname === inboxPath && String(request.method).toUpperCase() === 'POST') {
|
|
659
|
-
await this.deliverAtDoor(identity, request, response);
|
|
163
|
+
const claimed = (0, claims_1.claims)({ host, pathname }, this.frontHost, this.runPath) ? null : (0, mounts_1.resolveClaim)(this, host, pathname);
|
|
164
|
+
if (!claimed)
|
|
165
|
+
return (0, front_1.serveFront)(this, request, response);
|
|
166
|
+
const identity = this.surfaces.get(claimed.podBase);
|
|
167
|
+
if (!identity) {
|
|
168
|
+
// Claimed, but nothing here can answer for it. Saying which of the two
|
|
169
|
+
// reasons it is beats letting the pod answer for a route that is not
|
|
170
|
+
// the pod's, and beats telling a client to try again when trying again
|
|
171
|
+
// will reach another process just as unable to help.
|
|
172
|
+
if (!identities.runsIdentities(this)) {
|
|
173
|
+
response.writeHead(503, { 'content-type': 'application/json' });
|
|
174
|
+
response.end(JSON.stringify({ error: 'this identity is not reachable on a server running more '
|
|
175
|
+
+ 'than one worker: it runs in the process that serves no requests. Run with --workers 1.' }));
|
|
660
176
|
return;
|
|
661
177
|
}
|
|
662
|
-
|
|
178
|
+
response.writeHead(503, { 'content-type': 'application/json', 'retry-after': '5' });
|
|
179
|
+
response.end(JSON.stringify({ error: 'this identity is still starting' }));
|
|
663
180
|
return;
|
|
664
181
|
}
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
catch (e) {
|
|
671
|
-
const status = e.statusCode === 413 ? 413 : 400;
|
|
672
|
-
response.writeHead(status, { 'content-type': 'application/json' });
|
|
673
|
-
response.end(JSON.stringify({ error: e.message }));
|
|
674
|
-
return;
|
|
182
|
+
// This identity's own inbox path — <mount>/<root>/ap/inbox/, from its
|
|
183
|
+
// actor, so it already carries the mount for a suffix pod.
|
|
184
|
+
const inboxPath = new URL(identity.actorUrl).pathname.replace(/ap\/actor$/u, 'ap/inbox/');
|
|
185
|
+
if (pathname === inboxPath && String(request.method).toUpperCase() === 'POST') {
|
|
186
|
+
return (0, door_1.deliverAtDoor)(this, identity, request, response);
|
|
675
187
|
}
|
|
676
|
-
|
|
677
|
-
host: this.frontHost,
|
|
678
|
-
frontOrigin: this.args.frontOrigin,
|
|
679
|
-
// This server issued the session the reader is already using, so it can
|
|
680
|
-
// read it rather than sending them to an identity provider that has just
|
|
681
|
-
// told them who they are. Same-origin only; front-core decides that.
|
|
682
|
-
webIdsFromSession: (req) => this.webIdsFromSession(req),
|
|
683
|
-
gatewayWebId: this.args.gatewayWebId,
|
|
684
|
-
offersPods: !!this.args.offersPods,
|
|
685
|
-
signupPage: this.args.signupPage || webFile('new-account.html'),
|
|
686
|
-
runPage: this.args.runPage || webFile('run.html'),
|
|
687
|
-
runPath: this.runPath,
|
|
688
|
-
adminPage: this.args.adminPage || webFile('admin.html'),
|
|
689
|
-
// The notices page renders here too, and says the server keeps none:
|
|
690
|
-
// a notices store is the Netlify front's, not this component's.
|
|
691
|
-
noticesPage: webFile('notices.html'),
|
|
692
|
-
// The opt-in and roster pages load the sign-in library, and the installer
|
|
693
|
-
// command is handed out with it; without these the pages render but
|
|
694
|
-
// cannot be used.
|
|
695
|
-
authBundle: webFile('solid-oidc-client.js'),
|
|
696
|
-
// Each page's own script — inline until 2026-09-09, so that the pages can
|
|
697
|
-
// be served under `script-src 'self'` (see lib/front-core.mjs).
|
|
698
|
-
pageScripts: {
|
|
699
|
-
'new-account.js': webFile('new-account.js'),
|
|
700
|
-
'run.js': webFile('run.js'),
|
|
701
|
-
'admin.js': webFile('admin.js'),
|
|
702
|
-
'notices.js': webFile('notices.js'),
|
|
703
|
-
},
|
|
704
|
-
installScript: webFile('install.sh'),
|
|
705
|
-
lookup: (h) => this.dir.lookup(h),
|
|
706
|
-
putDirectory: (h, rec) => this.dir.putDirectory(h, rec),
|
|
707
|
-
podPut: (_handle, url, body, ct) => this.podPut(url, body, ct),
|
|
708
|
-
// Reads for the public proxy. `this.io.read` goes STRAIGHT into the
|
|
709
|
-
// resource store, which applies no access control of its own — so this is
|
|
710
|
-
// the one place that has to say what may be read, and it says: only
|
|
711
|
-
// inside the pod of the handle being served, and never a server-internal
|
|
712
|
-
// tree. Without it a row could name any location and this would fetch it
|
|
713
|
-
// (the directory, with every user's receipt secret, included).
|
|
714
|
-
//
|
|
715
|
-
// The front now refuses to attach such a row at all (podHomeProblem in
|
|
716
|
-
// lib/front-core.mjs); this is the same refusal at the other end, because
|
|
717
|
-
// a row can also arrive from a seed or an older deploy.
|
|
718
|
-
podGet: async (url, opts) => {
|
|
719
|
-
const denied = { status: 403, text: async () => '', headers: { get: () => null } };
|
|
720
|
-
let target;
|
|
721
|
-
try {
|
|
722
|
-
target = new URL(url);
|
|
723
|
-
}
|
|
724
|
-
catch {
|
|
725
|
-
return denied;
|
|
726
|
-
}
|
|
727
|
-
if (/(^|\/)\.internal(\/|$)/u.test(target.pathname))
|
|
728
|
-
return denied;
|
|
729
|
-
// Which pod asked: routeFront reads `rec.podHome + rest` and passes
|
|
730
|
-
// that podHome here, so the confinement is a value on the call rather
|
|
731
|
-
// than state on the handler. No podHome, no read.
|
|
732
|
-
const home = opts?.podHome;
|
|
733
|
-
if (!home || !url.startsWith(home))
|
|
734
|
-
return denied;
|
|
735
|
-
const raw = await this.io.read(url);
|
|
736
|
-
return raw == null
|
|
737
|
-
? { status: 404, text: async () => '', headers: { get: () => null } }
|
|
738
|
-
: { status: 200, text: async () => raw, headers: { get: () => null } };
|
|
739
|
-
},
|
|
740
|
-
agentControl: this.registry
|
|
741
|
-
? {
|
|
742
|
-
optIn: (a) => this.optInPod(a),
|
|
743
|
-
optOut: (a) => this.optOutPod(a),
|
|
744
|
-
describe: (a) => this.describePod(a),
|
|
745
|
-
}
|
|
746
|
-
: undefined,
|
|
747
|
-
});
|
|
748
|
-
await (0, adapt_1.applyToNode)(response, out);
|
|
188
|
+
await identity.surface.handler(request, response);
|
|
749
189
|
}
|
|
750
190
|
}
|
|
751
191
|
exports.FediPodServerHandler = FediPodServerHandler;
|