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
package/lib/device/setup.mjs
DELETED
|
@@ -1,496 +0,0 @@
|
|
|
1
|
-
// setup.mjs — the setup flow itself, lifted out of the CLI so a browser page
|
|
2
|
-
// can drive it. Same order, same decisions as `fedipod setup` always had:
|
|
3
|
-
// create the account and pod (or use one you already have), mint a revocable
|
|
4
|
-
// credential, provision the pod, publish the actor, check it is really public.
|
|
5
|
-
//
|
|
6
|
-
// The caller owns the run record and polls it; runSetup only fills it in. That
|
|
7
|
-
// is deliberate. The credential a CSS server mints is shown once and cannot be
|
|
8
|
-
// recovered, so the durable record of a run has to be the credential file on
|
|
9
|
-
// disk, never an HTTP response a closed tab can take with it.
|
|
10
|
-
|
|
11
|
-
import fs from 'node:fs';
|
|
12
|
-
import path from 'node:path';
|
|
13
|
-
import { pathToFileURL } from 'node:url';
|
|
14
|
-
import * as $rdf from 'rdflib';
|
|
15
|
-
|
|
16
|
-
import { createAccountWithPod as realCreateAccount } from './account.mjs';
|
|
17
|
-
import { mintCredential as realMint, RemotePod } from './remote.mjs';
|
|
18
|
-
import { hashPassword } from '../client/masto/index.mjs';
|
|
19
|
-
import { webfingerHost, apUrls, DEFAULT_ROOT } from '../core/wire.mjs';
|
|
20
|
-
import { rootOf, recordLastUsed, writeJsonAtomic } from './home.mjs';
|
|
21
|
-
import { insecureUrlReason } from '../shared/safefetch.mjs';
|
|
22
|
-
import { resourceExists } from '../pod/root.mjs';
|
|
23
|
-
import { CURRENT_LAYOUT, isCurrent } from './migrate.mjs';
|
|
24
|
-
import { completeGatewayMove } from './gateway-move.mjs';
|
|
25
|
-
import { chosenRoot, chosenRootInNewPod, publicIndexKnown, recordPlace, needsIndex } from '../core/place.mjs';
|
|
26
|
-
|
|
27
|
-
const SOLID = $rdf.Namespace('http://www.w3.org/ns/solid/terms#');
|
|
28
|
-
|
|
29
|
-
export const STEPS = ['account', 'credential', 'bootstrap', 'connect', 'publish', 'verify'];
|
|
30
|
-
|
|
31
|
-
// Read-only pre-check on the pod behind an existing-pod setup, before a
|
|
32
|
-
// credential is minted or the first write is attempted. Two failures it turns
|
|
33
|
-
// from a bare "PUT … → 401" into a plain sentence:
|
|
34
|
-
// 1. the pod host is unreachable — no account should be built on a pod that
|
|
35
|
-
// is not there;
|
|
36
|
-
// 2. the pod's WebID document declares no OIDC issuer. A pod verifies a token
|
|
37
|
-
// by dereferencing its WebID and looking for solid:oidcIssuer; an empty or
|
|
38
|
-
// issuer-less profile makes it reject every write with 401, so setup would
|
|
39
|
-
// stall on the first one. Only judged when the document is publicly
|
|
40
|
-
// readable — a protected one cannot be read here and is left to bootstrap.
|
|
41
|
-
export async function checkPodUsable(pod, { webId, fetch: doFetch = globalThis.fetch } = {}) {
|
|
42
|
-
const base = pod.endsWith('/') ? pod : pod + '/';
|
|
43
|
-
const wid = webId || new URL('profile/card#me', base).href;
|
|
44
|
-
const cardUrl = wid.split('#')[0];
|
|
45
|
-
|
|
46
|
-
let root;
|
|
47
|
-
try { root = await doFetch(base, { headers: { accept: 'text/turtle' } }); }
|
|
48
|
-
catch (e) { return { ok: false, error: `${base} could not be reached (${e.message}). Check the address and that the server is running.` }; }
|
|
49
|
-
if (root.status === 404) return { ok: false, error: `there is no pod at ${base} — the server answered 404.` };
|
|
50
|
-
if (root.status >= 500) return { ok: false, error: `the pod host at ${base} is not responding (HTTP ${root.status}).` };
|
|
51
|
-
|
|
52
|
-
let card;
|
|
53
|
-
try { card = await doFetch(cardUrl, { headers: { accept: 'text/turtle' } }); }
|
|
54
|
-
catch (e) { return { ok: false, error: `the pod's WebID document ${cardUrl} could not be read (${e.message}).` }; }
|
|
55
|
-
if (card.status === 404) return { ok: false, error: `the pod has no WebID document at ${cardUrl} — the identity it needs is missing.` };
|
|
56
|
-
if (card.status >= 500) return { ok: false, error: `the pod host is not serving ${cardUrl} (HTTP ${card.status}).` };
|
|
57
|
-
if (card.status === 401 || card.status === 403) return { ok: true }; // cannot read unauthenticated; leave it to bootstrap
|
|
58
|
-
|
|
59
|
-
const body = await card.text().catch(() => '');
|
|
60
|
-
const g = $rdf.graph();
|
|
61
|
-
try { $rdf.parse(body, g, cardUrl, (card.headers.get('content-type') || 'text/turtle').split(';')[0].trim()); }
|
|
62
|
-
catch { return { ok: false, error: `the pod's WebID document ${cardUrl} could not be parsed as RDF.` }; }
|
|
63
|
-
if (!g.each($rdf.sym(wid), SOLID('oidcIssuer'), null).length) {
|
|
64
|
-
return { ok: false, error: `the pod's WebID document ${cardUrl} declares no OIDC issuer, so the pod will reject every write. It looks empty or incomplete — restore it, or set up with a fresh pod.` };
|
|
65
|
-
}
|
|
66
|
-
return { ok: true };
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export const credentialPath = (home) => path.join(home, 'credential.json');
|
|
70
|
-
export const hasCredential = (home) => fs.existsSync(credentialPath(home));
|
|
71
|
-
|
|
72
|
-
export function newRun() {
|
|
73
|
-
return {
|
|
74
|
-
phase: 'running',
|
|
75
|
-
startedAt: new Date().toISOString(),
|
|
76
|
-
steps: STEPS.map(key => ({ key, state: 'waiting', note: null })),
|
|
77
|
-
error: null,
|
|
78
|
-
result: null,
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// What must be answered before a run can start. `resuming` is a run that
|
|
83
|
-
// already has its credential: the account and the pod are settled, and only
|
|
84
|
-
// the actor's own details are still open.
|
|
85
|
-
export function setupInputError(a, resuming = false) {
|
|
86
|
-
if (!a.handle) return 'a handle is required';
|
|
87
|
-
if (a.kind && a.kind !== 'person' && a.kind !== 'group') return 'kind must be person or group';
|
|
88
|
-
if (a.privateRoot) {
|
|
89
|
-
try { new URL(a.privateRoot); } catch { return `"${a.privateRoot}" is not a container address`; }
|
|
90
|
-
// A pod one, at least. A file: private root is a directory and has no
|
|
91
|
-
// transport to secure.
|
|
92
|
-
if (/^https?:/i.test(a.privateRoot)) {
|
|
93
|
-
const bad = insecureUrlReason(a.privateRoot, 'private-data address');
|
|
94
|
-
if (bad) return bad;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
if (a.gateway) {
|
|
98
|
-
if (typeof a.gateway.url !== 'string') return 'a gateway needs its door URL';
|
|
99
|
-
const badGw = insecureUrlReason(a.gateway.url, 'gateway address');
|
|
100
|
-
if (badGw) return badGw;
|
|
101
|
-
}
|
|
102
|
-
if (a.shape && a.shape !== 'pod' && a.shape !== 'front') return 'shape must be "pod" or "front"';
|
|
103
|
-
if (a.gatewayOrigin) {
|
|
104
|
-
const badO = insecureUrlReason(a.gatewayOrigin, 'gateway address');
|
|
105
|
-
if (badO) return badO;
|
|
106
|
-
}
|
|
107
|
-
if (resuming) return null;
|
|
108
|
-
if (a.mode !== 'new' && a.mode !== 'existing') return 'mode must be "new" or "existing"';
|
|
109
|
-
if (!a.issuer) return 'an identity provider is required';
|
|
110
|
-
// Checked BEFORE the password is asked for, let alone sent: the issuer is
|
|
111
|
-
// where it goes, and the pod is where the credential it buys is used.
|
|
112
|
-
const badIssuer = insecureUrlReason(a.issuer, 'identity provider address');
|
|
113
|
-
if (badIssuer) return badIssuer;
|
|
114
|
-
if (!a.email) return 'an account email is required';
|
|
115
|
-
if (!a.password) return 'the account password is required';
|
|
116
|
-
if (a.mode === 'existing' && !a.pod) return 'a pod address is required';
|
|
117
|
-
const badPod = insecureUrlReason(a.pod, 'pod address');
|
|
118
|
-
if (badPod) return badPod;
|
|
119
|
-
return null;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Where an identity's address lives. A pod on a suffix-based host cannot
|
|
123
|
-
// answer WebFinger for a handle, so its address must live at the Gateway
|
|
124
|
-
// whatever was asked; a pod at its own host root takes the shape chosen.
|
|
125
|
-
export function frontedAddress({ pod, shape }) {
|
|
126
|
-
const pathPod = pod ? !webfingerHost(pod) : false;
|
|
127
|
-
return pathPod || shape === 'front';
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// The account a pod already holds, read as its owner: the config and the key
|
|
131
|
-
// record in its state container. Both null when there is none, or when the
|
|
132
|
-
// credential is not the owner's. Injected for tests.
|
|
133
|
-
export async function readPodAccount({ home, credential, pod, root, log = () => {} }) {
|
|
134
|
-
const remote = new RemotePod(credential, { home, log });
|
|
135
|
-
await remote.warmup?.();
|
|
136
|
-
const urls = apUrls(pod, root || DEFAULT_ROOT);
|
|
137
|
-
const config = await remote.getJson(urls.state + 'config.json').catch(() => null);
|
|
138
|
-
const keys = await remote.getJson(urls.state + 'keys.json').catch(() => null);
|
|
139
|
-
return { config, keys };
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Take an address at a Gateway: attach the pod to it, fronted, and return the
|
|
143
|
-
// gateway config bootstrap writes and connect reads. The pod session proves
|
|
144
|
-
// the pod — no password reaches the Gateway. Injected for tests.
|
|
145
|
-
export async function takeGatewayAddress({ home, credential, gatewayOrigin, handle, podHome, actorUrl, kind, log = () => {} }) {
|
|
146
|
-
const remote = new RemotePod(credential, { home, log });
|
|
147
|
-
await remote.warmup?.();
|
|
148
|
-
const origin = String(gatewayOrigin).replace(/\/$/, '');
|
|
149
|
-
const res = await remote.session.fetch(`${origin}/api/attach`, {
|
|
150
|
-
method: 'POST', headers: { 'content-type': 'application/json' },
|
|
151
|
-
body: JSON.stringify({ handle, podHome, actorUrl, kind: kind === 'group' ? 'group' : 'person', fronted: true }),
|
|
152
|
-
});
|
|
153
|
-
const d = await res.json().catch(() => ({}));
|
|
154
|
-
if (res.status !== 201 || !d.hmacSecret) {
|
|
155
|
-
throw new Error(`could not take a gateway address at ${new URL(origin).host} (HTTP ${res.status})${d.error ? ': ' + d.error : ''}`);
|
|
156
|
-
}
|
|
157
|
-
return {
|
|
158
|
-
url: `${origin}/u/${handle}/ap/inbox/`,
|
|
159
|
-
frontActor: String(d.frontActor || `${origin}/u/${handle}/ap/actor`),
|
|
160
|
-
hmacSecret: String(d.hmacSecret), mode: 'trust',
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// What the CLI printed before asking "create pod and fediverse account?"
|
|
165
|
-
// (bin/fedipod.mjs, the address preview) — as data, so the page can show
|
|
166
|
-
// the same warnings. Pure: no network, so it can answer while you type.
|
|
167
|
-
export function preflight({ mode, pod, issuer, podName, handle, kind, shape = 'pod', gatewayOrigin = 'https://fedipod.net' }) {
|
|
168
|
-
const warnings = [];
|
|
169
|
-
if (!handle) return { ok: false, error: 'a handle is required' };
|
|
170
|
-
let issuerHost;
|
|
171
|
-
try { issuerHost = new URL(issuer || 'https://solidcommunity.net').host; }
|
|
172
|
-
catch { return { ok: false, error: `"${issuer}" is not a URL` }; }
|
|
173
|
-
|
|
174
|
-
let gwHost; try { gwHost = new URL(gatewayOrigin).host; } catch { gwHost = 'fedipod.net'; }
|
|
175
|
-
|
|
176
|
-
if (mode === 'new') {
|
|
177
|
-
// Whether the provider puts the pod on its own subdomain or on a path is
|
|
178
|
-
// the provider's call, learned only once the pod is made. If the address
|
|
179
|
-
// was asked to live at the Gateway, it does. Otherwise the pod's own host
|
|
180
|
-
// is the address, and the run moves it to the Gateway if the provider
|
|
181
|
-
// turned out to use paths.
|
|
182
|
-
if (shape === 'front') {
|
|
183
|
-
if (kind === 'group') return { ok: false, mode, handle, kind, error: 'group-needs-host-root', refusal: 'group-needs-host-root', warnings };
|
|
184
|
-
return { ok: true, mode, handle, kind, fronted: true, forced: false, gatewayHost: gwHost, shape: 'front',
|
|
185
|
-
address: `@${handle}@${gwHost}`, webfingerHost: null, resolvable: true, warnings, refusal: null };
|
|
186
|
-
}
|
|
187
|
-
return {
|
|
188
|
-
ok: true, mode, handle, kind, fronted: false, shape: 'pod',
|
|
189
|
-
address: `@${handle}@${podName || handle}.${issuerHost}`,
|
|
190
|
-
webfingerHost: null, resolvable: null, warnings, refusal: null,
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
let podUrl;
|
|
195
|
-
try { podUrl = new URL(pod); }
|
|
196
|
-
catch { return { ok: false, error: `"${pod}" is not a pod address` }; }
|
|
197
|
-
// A handle resolves through <host>/.well-known/webfinger, so it only works
|
|
198
|
-
// when the pod owns the root of its host. A pod that does not can still be
|
|
199
|
-
// an account, with its address at the Gateway.
|
|
200
|
-
const wfHost = webfingerHost(podUrl.href);
|
|
201
|
-
const fronted = !wfHost || shape === 'front';
|
|
202
|
-
if (fronted) {
|
|
203
|
-
// A person's address moves to the Gateway; a group cannot, yet — a group
|
|
204
|
-
// needs a pod at its own host until fronted groups are proven.
|
|
205
|
-
if (kind === 'group') {
|
|
206
|
-
return { ok: false, mode: 'existing', handle, kind, refusal: 'group-needs-host-root', warnings,
|
|
207
|
-
address: `@${handle}@${podUrl.host}`, webfingerHost: null, resolvable: false };
|
|
208
|
-
}
|
|
209
|
-
return {
|
|
210
|
-
ok: true, mode: 'existing', handle, kind, fronted: true, forced: !wfHost, gatewayHost: gwHost, shape: 'front',
|
|
211
|
-
address: `@${handle}@${gwHost}`, webfingerHost: null, resolvable: true, warnings, refusal: null,
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
return {
|
|
215
|
-
ok: true, mode: 'existing', handle, kind, fronted: false, shape: 'pod',
|
|
216
|
-
address: `@${handle}@${wfHost}`,
|
|
217
|
-
webfingerHost: wfHost, resolvable: true, warnings, refusal: null,
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// Fills `run` in as it goes. Never throws: a failure lands in run.error, so a
|
|
222
|
-
// page that polls sees it. `deps` is for tests — the real functions otherwise.
|
|
223
|
-
export async function runSetup({ home, agent, answers, run, deps = {}, log = () => {} }) {
|
|
224
|
-
const createAccount = deps.createAccountWithPod || realCreateAccount;
|
|
225
|
-
const mint = deps.mintCredential || realMint;
|
|
226
|
-
const checkPod = deps.checkPodUsable || checkPodUsable;
|
|
227
|
-
const attachGateway = deps.attachGateway || takeGatewayAddress;
|
|
228
|
-
|
|
229
|
-
const at = (key) => run.steps.find(s => s.key === key);
|
|
230
|
-
const begin = (key) => { at(key).state = 'running'; };
|
|
231
|
-
const done = (key, note = null) => { at(key).state = 'ok'; at(key).note = note; };
|
|
232
|
-
const skip = (key, note = null) => { at(key).state = 'skipped'; at(key).note = note; };
|
|
233
|
-
|
|
234
|
-
const {
|
|
235
|
-
mode, issuer, email, password, handle, name, podName,
|
|
236
|
-
kind = 'person', approveJoins = false, summary, icon, keys, uiPassword,
|
|
237
|
-
gateway = null, shape = 'pod', gatewayOrigin = 'https://fedipod.net',
|
|
238
|
-
// The container that holds `fedipod/` (blank: the pod's root), and the
|
|
239
|
-
// person's yes to a new public type index when their profile names none.
|
|
240
|
-
container = '', createIndex = false,
|
|
241
|
-
} = answers;
|
|
242
|
-
let { pod, root } = answers;
|
|
243
|
-
const rootGiven = !!root;
|
|
244
|
-
if (!root) root = 'fedipod/'; // until the pod is known; a resuming run
|
|
245
|
-
// overwrites this with the credential's own root below.
|
|
246
|
-
let accountWebId = null; // what createAccountWithPod reported, when it ran
|
|
247
|
-
// The private half always starts here, beside the credential and the keys —
|
|
248
|
-
// not on the pod. Every activity you receive would otherwise cost the pod
|
|
249
|
-
// several writes, and that pod is a server somebody runs. `state --to` moves
|
|
250
|
-
// it afterwards; starting on the pod and moving later is strictly worse,
|
|
251
|
-
// because the copy left behind was on the pod the whole time.
|
|
252
|
-
let privateRoot = answers.privateRoot
|
|
253
|
-
|| pathToFileURL(path.join(home, 'private')).href + '/';
|
|
254
|
-
// A pod that already holds an account at ANOTHER gateway, set up with an
|
|
255
|
-
// address at this one: not a second account but the same one moving in.
|
|
256
|
-
// Its state and key are on the pod, and stay there.
|
|
257
|
-
let movingIn = false;
|
|
258
|
-
let movedFrom = null;
|
|
259
|
-
// Everything that leaves this function — notes, errors, the log — goes
|
|
260
|
-
// through here. Nothing today echoes a password back, but /setup/progress is
|
|
261
|
-
// polled repeatedly and would re-serve one forever if anything ever did.
|
|
262
|
-
const scrub = (s) => (password ? String(s).split(password).join('••••••') : String(s));
|
|
263
|
-
|
|
264
|
-
const credPath = credentialPath(home);
|
|
265
|
-
const resuming = fs.existsSync(credPath);
|
|
266
|
-
|
|
267
|
-
try {
|
|
268
|
-
// --- account: create it, or use the pod you already have ---
|
|
269
|
-
if (resuming) {
|
|
270
|
-
skip('account', 'the credential is already on disk');
|
|
271
|
-
} else if (mode === 'new') {
|
|
272
|
-
begin('account');
|
|
273
|
-
// A new pod has no public type index: without a yes to making one,
|
|
274
|
-
// nothing is created at all.
|
|
275
|
-
if (!createIndex) throw needsIndex('A new pod has no public type index.');
|
|
276
|
-
const made = await createAccount({ issuer, email, password, podName: podName || handle });
|
|
277
|
-
pod = made.pod;
|
|
278
|
-
accountWebId = made.webId || null;
|
|
279
|
-
log(`account + pod created: ${pod}`);
|
|
280
|
-
// The server put the pod on a path rather than its own subdomain, so
|
|
281
|
-
// nobody could ever find this group. A person was warned before we got
|
|
282
|
-
// here and chose to continue; a group cannot.
|
|
283
|
-
if (kind === 'group' && !webfingerHost(pod)) {
|
|
284
|
-
throw new Error(`${issuer} created the pod at ${pod} — a suffixed pod. `
|
|
285
|
-
+ 'WebFinger is only answered for a subdomained pod, so nobody '
|
|
286
|
-
+ 'could find this group. The pod exists; no actor was published.');
|
|
287
|
-
}
|
|
288
|
-
if (!rootGiven) {
|
|
289
|
-
const place = chosenRootInNewPod(pod.endsWith('/') ? pod : pod + '/', container);
|
|
290
|
-
if (place.problem) throw new Error(`Where to store it: ${place.problem}.`);
|
|
291
|
-
root = place.root;
|
|
292
|
-
}
|
|
293
|
-
done('account', pod);
|
|
294
|
-
} else {
|
|
295
|
-
// A pod you bring must be there and carry a usable WebID before a
|
|
296
|
-
// credential is minted against it — no account on a pod that is not
|
|
297
|
-
// reachable, and no silent 401 later on a pod whose profile is empty.
|
|
298
|
-
const usable = await checkPod(pod);
|
|
299
|
-
if (!usable.ok) throw new Error(usable.error);
|
|
300
|
-
const podBase = pod.endsWith('/') ? pod : pod + '/';
|
|
301
|
-
if (!rootGiven) {
|
|
302
|
-
const place = chosenRoot(podBase, container);
|
|
303
|
-
if (place.problem) throw new Error(`Where to store it: ${place.problem}.`);
|
|
304
|
-
root = place.root;
|
|
305
|
-
}
|
|
306
|
-
// An account at the chosen place, or an older one where accounts used to go.
|
|
307
|
-
const exists = (r) => (deps.resourceExists || resourceExists)(deps.fetch || fetch, apUrls(pod, r).actor);
|
|
308
|
-
const held = await exists(root) || (root !== DEFAULT_ROOT && await exists(DEFAULT_ROOT));
|
|
309
|
-
if (held && !frontedAddress({ pod, shape })) {
|
|
310
|
-
throw new Error('The pod already hosts a FediPod account. If you want a second account, put it on a different pod. '
|
|
311
|
-
+ 'To move an account whose address is at another gateway to this one, ask for an address at this gateway.');
|
|
312
|
-
}
|
|
313
|
-
if (held && kind === 'group') throw new Error('The pod already hosts a FediPod account, and a group cannot move between gateways.');
|
|
314
|
-
movingIn = held;
|
|
315
|
-
if (movingIn) { privateRoot = null; }
|
|
316
|
-
// Asked before anything is minted: a profile with no public type index
|
|
317
|
-
// gets one only on its person's yes.
|
|
318
|
-
if (!movingIn && !createIndex
|
|
319
|
-
&& await (deps.publicIndexKnown || publicIndexKnown)(deps.fetch || fetch, podBase) !== true) {
|
|
320
|
-
throw needsIndex('Your profile names no public type index.');
|
|
321
|
-
}
|
|
322
|
-
skip('account', movingIn ? 'the pod already holds the account — its address moves here' : 'using the pod you already have');
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// --- credential: the point of no return, and the durability boundary ---
|
|
326
|
-
let resumeWebId = null;
|
|
327
|
-
if (resuming) {
|
|
328
|
-
const rec = JSON.parse(fs.readFileSync(credPath, 'utf8'));
|
|
329
|
-
pod = rec.remotePod;
|
|
330
|
-
root = rec.root;
|
|
331
|
-
resumeWebId = rec.webId || null;
|
|
332
|
-
skip('credential', `already minted — ${credPath}`);
|
|
333
|
-
} else {
|
|
334
|
-
begin('credential');
|
|
335
|
-
// The WebID matters as much as the pod: an account with more than one
|
|
336
|
-
// pod has more than one, and a credential bound to the wrong one 403s
|
|
337
|
-
// every write. Account creation knows which; otherwise the pod's origin
|
|
338
|
-
// picks it out.
|
|
339
|
-
const credential = await mint({
|
|
340
|
-
origin: issuer, email, password, name: 'fedipod',
|
|
341
|
-
webId: accountWebId || undefined, podUrl: pod,
|
|
342
|
-
});
|
|
343
|
-
const rec = {
|
|
344
|
-
...credential,
|
|
345
|
-
remotePod: pod.endsWith('/') ? pod : pod + '/',
|
|
346
|
-
createdAt: new Date().toISOString(),
|
|
347
|
-
...(root ? { root } : {}),
|
|
348
|
-
...(keys === 'pod' || movingIn ? { keysMode: 'pod' } : {}),
|
|
349
|
-
// Where the private half lives. Per-machine, like keysMode. A move-in
|
|
350
|
-
// keeps it on the pod, where the account already is, and says so.
|
|
351
|
-
...(privateRoot ? { privateRoot } : {}),
|
|
352
|
-
...(movingIn ? { stateOnPod: true } : {}),
|
|
353
|
-
// What shape this install is, so `upgrade` can tell an old one from a
|
|
354
|
-
// new one without guessing from the fields.
|
|
355
|
-
...(isCurrent({ privateRoot, stateOnPod: movingIn }) ? { layout: CURRENT_LAYOUT } : {}),
|
|
356
|
-
};
|
|
357
|
-
fs.mkdirSync(home, { recursive: true, mode: 0o700 });
|
|
358
|
-
writeJsonAtomic(credPath, rec);
|
|
359
|
-
// The identity you just made is the one you are using, so it becomes what
|
|
360
|
-
// a plain command means. Here rather than earlier: the credential landing
|
|
361
|
-
// is what makes this an identity at all.
|
|
362
|
-
// NOT `root` — that name is already taken by the answers' AP container
|
|
363
|
-
// root, destructured above, and shadowing it is a temporal dead zone.
|
|
364
|
-
const apHomeRoot = rootOf(home);
|
|
365
|
-
if (apHomeRoot !== home) recordLastUsed(apHomeRoot, path.basename(home));
|
|
366
|
-
pod = rec.remotePod;
|
|
367
|
-
log(`credential minted and saved to ${credPath}`);
|
|
368
|
-
done('credential', credPath);
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
// --- a move-in: the account on the pod, read with the credential just
|
|
372
|
-
// minted, has to be one whose address lives at another gateway ---
|
|
373
|
-
if (movingIn) {
|
|
374
|
-
const cred = JSON.parse(fs.readFileSync(credPath, 'utf8'));
|
|
375
|
-
const account = await (deps.readPodAccount || readPodAccount)({ home, credential: cred, pod, root: root || DEFAULT_ROOT, log });
|
|
376
|
-
if (!account?.config) throw new Error('the pod holds an account, but its record could not be read with this credential — is this account the pod\'s owner?');
|
|
377
|
-
const frontActor = account.config.gateway?.frontActor || null;
|
|
378
|
-
const gwHost = new URL(gatewayOrigin).host;
|
|
379
|
-
if (!frontActor) throw new Error('The pod already hosts a FediPod account whose address lives on the pod: nothing to move. Sign in to it instead.');
|
|
380
|
-
if (new URL(frontActor).host === gwHost) throw new Error(`The pod already hosts a FediPod account at ${gwHost}: sign in to it instead of setting up again.`);
|
|
381
|
-
if ((account.config.kind || 'person') === 'group') throw new Error('a group cannot move between gateways');
|
|
382
|
-
if (!account.keys?.rsa || account.keys.ct) {
|
|
383
|
-
throw new Error(`this account's key is still under a password from an earlier version — sign in once at ${new URL(frontActor).host} first, then set up here`);
|
|
384
|
-
}
|
|
385
|
-
movedFrom = { actor: frontActor, gateway: new URL(frontActor).origin, handle: account.config.handle, at: new Date().toISOString() };
|
|
386
|
-
log(`moving @${account.config.handle}@${new URL(frontActor).host} here, as @${handle}@${gwHost}`);
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
// --- take an address at the Gateway, if this pod needs one or asked for
|
|
390
|
-
// one --- before bootstrap, so the config it writes carries the Gateway
|
|
391
|
-
// ids and the key connect mints below is stamped to the Gateway actor from
|
|
392
|
-
// the start. A signup-arranged gateway (the installer carry-over) wins.
|
|
393
|
-
let gatewayCfg = gateway;
|
|
394
|
-
if (!gatewayCfg && frontedAddress({ pod, shape })) {
|
|
395
|
-
if (kind === 'group') {
|
|
396
|
-
throw new Error(`a group needs a subdomained pod — a Gateway address for a group is not supported yet`
|
|
397
|
-
+ (webfingerHost(pod) ? '' : `; ${pod} is suffixed`) + '.');
|
|
398
|
-
}
|
|
399
|
-
const urls = apUrls(pod, root || DEFAULT_ROOT);
|
|
400
|
-
const cred = JSON.parse(fs.readFileSync(credPath, 'utf8'));
|
|
401
|
-
log(`taking a gateway address at ${new URL(gatewayOrigin).host}`);
|
|
402
|
-
gatewayCfg = await attachGateway({
|
|
403
|
-
home, credential: cred, gatewayOrigin, handle,
|
|
404
|
-
podHome: urls.home, actorUrl: urls.actor, kind, log,
|
|
405
|
-
});
|
|
406
|
-
log(`gateway address @${handle}@${new URL(gatewayOrigin).host}`);
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
// --- provision the pod and bring federation up ---
|
|
410
|
-
begin('bootstrap');
|
|
411
|
-
// Resuming skipped the checks the fresh paths ran, and the credential it
|
|
412
|
-
// carries may be the one bound to a pod whose profile is empty — the write
|
|
413
|
-
// below would 401. Catch it here, as this step, with a sentence.
|
|
414
|
-
if (resuming) {
|
|
415
|
-
const ready = await checkPod(pod, { webId: resumeWebId || undefined });
|
|
416
|
-
if (!ready.ok) throw new Error(ready.error);
|
|
417
|
-
}
|
|
418
|
-
await agent.bootstrap({ handle, name: name || handle, root, kind, approveJoins, summary, icon, gateway: gatewayCfg });
|
|
419
|
-
if (movingIn && movedFrom) {
|
|
420
|
-
// The old actor becomes an alias, which is what a server checks on the
|
|
421
|
-
// new actor before it honours a Move; the Move itself is pending until
|
|
422
|
-
// the new actor is published (completeGatewayMove, below and at every
|
|
423
|
-
// start). The key stays; its stamp follows the actor.
|
|
424
|
-
const cfg = agent.store.getConfig() || {};
|
|
425
|
-
agent.store.setConfig({ ...cfg, aliases: [...new Set([...(cfg.aliases || []), movedFrom.actor])], movedFrom });
|
|
426
|
-
const keyRec = agent.store.read?.('keys.json', null);
|
|
427
|
-
if (keyRec?.mintedFor && gatewayCfg?.frontActor) agent.store.write?.('keys.json', { ...keyRec, mintedFor: gatewayCfg.frontActor });
|
|
428
|
-
await agent.store.flush();
|
|
429
|
-
}
|
|
430
|
-
done('bootstrap');
|
|
431
|
-
|
|
432
|
-
begin('connect');
|
|
433
|
-
// Direct, not connectWithRetry: setup wants to hear about a failure now,
|
|
434
|
-
// not back off for an hour with a browser tab waiting on it.
|
|
435
|
-
await agent.connect({ repair: false }); // publish below is the publish
|
|
436
|
-
done('connect');
|
|
437
|
-
|
|
438
|
-
begin('publish');
|
|
439
|
-
if (uiPassword) {
|
|
440
|
-
agent.store.setConfig({ ...agent.store.getConfig(), uiPassword: hashPassword(uiPassword) });
|
|
441
|
-
}
|
|
442
|
-
const published = await agent.publisher.publishProfile();
|
|
443
|
-
await agent.store.flush();
|
|
444
|
-
// Where the account lives, in the owner's public type index — made first
|
|
445
|
-
// if they said yes to one.
|
|
446
|
-
if (!movingIn) {
|
|
447
|
-
const urlsAt = apUrls(pod, root);
|
|
448
|
-
try {
|
|
449
|
-
await (deps.recordPlace || recordPlace)(agent.publisher?.remote || agent.remote, urlsAt.base, urlsAt.home + 'ap/actor', { create: !!createIndex });
|
|
450
|
-
} catch (e) {
|
|
451
|
-
throw new Error(`could not record where the account lives in your type index (${e.message})`);
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
if (movingIn) {
|
|
455
|
-
// Tell the old gateway and the followers. Not fatal: left pending, it
|
|
456
|
-
// is tried again when the agent next starts acting.
|
|
457
|
-
const moved = await (deps.completeGatewayMove || completeGatewayMove)(agent).catch((e) => { log(`gateway move: ${e.message}`); return null; });
|
|
458
|
-
if (moved?.completedAt) log(`Move sent from ${movedFrom.actor} to ${moved.moveSent} inbox(es)`);
|
|
459
|
-
}
|
|
460
|
-
done('publish');
|
|
461
|
-
|
|
462
|
-
// --- and say plainly whether the world can actually see it ---
|
|
463
|
-
begin('verify');
|
|
464
|
-
const unreachable = published?.unreachable || [];
|
|
465
|
-
const wfHost = webfingerHost(pod);
|
|
466
|
-
done('verify', unreachable.length
|
|
467
|
-
? `not readable without credentials: ${unreachable.join(', ')}`
|
|
468
|
-
: 'the public surface is reachable');
|
|
469
|
-
|
|
470
|
-
// A fronted identity resolves at the Gateway, not the pod host.
|
|
471
|
-
const frontActor = gatewayCfg?.frontActor || agent.store.getConfig()?.gateway?.frontActor || null;
|
|
472
|
-
const gwHost = frontActor ? new URL(frontActor).host : null;
|
|
473
|
-
run.result = {
|
|
474
|
-
kind,
|
|
475
|
-
pod,
|
|
476
|
-
handle,
|
|
477
|
-
actor: agent.urls?.actor || null,
|
|
478
|
-
fronted: !!frontActor,
|
|
479
|
-
webfingerHost: gwHost || wfHost,
|
|
480
|
-
resolvable: !!(gwHost || wfHost),
|
|
481
|
-
address: gwHost ? `@${handle}@${gwHost}` : (wfHost ? `@${handle}@${wfHost}` : null),
|
|
482
|
-
unreachable,
|
|
483
|
-
};
|
|
484
|
-
run.phase = 'done';
|
|
485
|
-
log(wfHost
|
|
486
|
-
? `${kind === 'group' ? 'group' : 'actor'} published: @${handle}@${wfHost}`
|
|
487
|
-
: `${kind === 'group' ? 'group' : 'actor'} published, but not reachable as a handle — `
|
|
488
|
-
+ `${pod} is a suffixed pod`);
|
|
489
|
-
} catch (e) {
|
|
490
|
-
for (const s of run.steps) if (s.state === 'running') s.state = 'error';
|
|
491
|
-
run.error = scrub(e.message);
|
|
492
|
-
run.phase = 'error';
|
|
493
|
-
log(`setup failed: ${run.error}`);
|
|
494
|
-
}
|
|
495
|
-
return run;
|
|
496
|
-
}
|
package/lib/device/update.mjs
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
// update.mjs — is a newer FediPod published, and the update itself.
|
|
2
|
-
//
|
|
3
|
-
// "Latest" is the version field of package.json on the repo's main branch —
|
|
4
|
-
// the same file a clone reads to know what it is running. The check is one
|
|
5
|
-
// GET of that file; nothing else leaves the machine, and AP_UPDATE_CHECK=0
|
|
6
|
-
// turns it off. The update is what re-running the installer does: fast-forward
|
|
7
|
-
// the checkout, reinstall dependencies, restart the agents.
|
|
8
|
-
|
|
9
|
-
import fs from 'node:fs';
|
|
10
|
-
import path from 'node:path';
|
|
11
|
-
import { execFileSync, spawn } from 'node:child_process';
|
|
12
|
-
import { fileURLToPath } from 'node:url';
|
|
13
|
-
|
|
14
|
-
export const LATEST_URL = 'https://raw.githubusercontent.com/jeff-zucker/FediPod/main/package.json';
|
|
15
|
-
|
|
16
|
-
export const repoRoot = () => path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
|
|
17
|
-
|
|
18
|
-
export const localVersion = (root = repoRoot()) => {
|
|
19
|
-
try { return JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')).version || null; }
|
|
20
|
-
catch { return null; }
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
// a newer than b, for dotted numeric versions.
|
|
24
|
-
export const newerThan = (a, b) => {
|
|
25
|
-
const pa = String(a).split('.').map(Number);
|
|
26
|
-
const pb = String(b).split('.').map(Number);
|
|
27
|
-
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
28
|
-
const d = (pa[i] || 0) - (pb[i] || 0);
|
|
29
|
-
if (d) return d > 0;
|
|
30
|
-
}
|
|
31
|
-
return false;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export async function checkLatest({ current = localVersion(), fetchImpl = fetch } = {}) {
|
|
35
|
-
if (process.env.AP_UPDATE_CHECK === '0') return null;
|
|
36
|
-
try {
|
|
37
|
-
const res = await fetchImpl(LATEST_URL, { signal: AbortSignal.timeout(10_000) });
|
|
38
|
-
if (!res.ok) return null;
|
|
39
|
-
const latest = (await res.json()).version || null;
|
|
40
|
-
if (!latest || !current) return null;
|
|
41
|
-
return { current, latest, available: newerThan(latest, current), checkedAt: new Date().toISOString() };
|
|
42
|
-
} catch { return null; }
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// The npm half, injectable — a test asking what runUpdate DOES must never be
|
|
46
|
-
// able to install software on the machine running it.
|
|
47
|
-
const npmInstallLatest = () =>
|
|
48
|
-
execFileSync('npm', ['install', '-g', 'fedipod@latest'], { stdio: 'pipe' });
|
|
49
|
-
|
|
50
|
-
// Fast-forward only: a checkout with local changes is somebody's work, and an
|
|
51
|
-
// update must refuse rather than eat it.
|
|
52
|
-
export function runUpdate({ root = repoRoot(), log = () => {}, install = npmInstallLatest } = {}) {
|
|
53
|
-
// The ordinary install comes from npm, where updating is npm's job and the
|
|
54
|
-
// files under us are not ours to move. A checkout is somebody working on it,
|
|
55
|
-
// and gets the fast-forward below.
|
|
56
|
-
if (!fs.existsSync(path.join(root, '.git'))) {
|
|
57
|
-
try {
|
|
58
|
-
log('updating from npm…');
|
|
59
|
-
install();
|
|
60
|
-
return { ok: true, note: 'updated from npm — restart the agent to serve it' };
|
|
61
|
-
} catch (e) {
|
|
62
|
-
return { ok: false,
|
|
63
|
-
note: `npm install -g fedipod@latest failed: ${String(e.stderr || e.message).slice(0, 300).trim()}` };
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
try {
|
|
67
|
-
execFileSync('git', ['-C', root, 'pull', '--ff-only'], { stdio: 'pipe' });
|
|
68
|
-
} catch (e) {
|
|
69
|
-
return { ok: false, note: `git pull refused: ${String(e.stderr || e.message).slice(0, 300).trim()}` };
|
|
70
|
-
}
|
|
71
|
-
try {
|
|
72
|
-
execFileSync('npm', ['install', '--omit=dev', '--no-audit', '--no-fund'], { cwd: root, stdio: 'pipe' });
|
|
73
|
-
} catch (e) {
|
|
74
|
-
return { ok: false, note: `npm install failed: ${String(e.stderr || e.message).slice(0, 300).trim()}` };
|
|
75
|
-
}
|
|
76
|
-
const version = localVersion(root);
|
|
77
|
-
log(`updated the checkout to ${version}`);
|
|
78
|
-
return { ok: true, note: `updated to ${version}`, version };
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// New code runs only after a restart. Where systemd units exist they are all
|
|
82
|
-
// restarted by name; otherwise the caller re-execs its own process.
|
|
83
|
-
export function restartAgents({ log = () => {} } = {}) {
|
|
84
|
-
try {
|
|
85
|
-
const list = execFileSync('systemctl',
|
|
86
|
-
['--user', 'list-units', '--plain', '--no-legend', 'fedipod-*'], { stdio: 'pipe' })
|
|
87
|
-
.toString().trim();
|
|
88
|
-
if (list) {
|
|
89
|
-
const child = spawn('systemctl', ['--user', 'restart', 'fedipod-*'], { detached: true, stdio: 'ignore' });
|
|
90
|
-
child.unref();
|
|
91
|
-
log('restarting the fedipod units');
|
|
92
|
-
return 'units';
|
|
93
|
-
}
|
|
94
|
-
} catch { /* no systemd here */ }
|
|
95
|
-
return 'self';
|
|
96
|
-
}
|
package/lib/gateway/caches.mjs
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
// caches.mjs — what one warm gateway process keeps between requests: the
|
|
2
|
-
// pod-token verifier, and the public keys of the servers that deliver here.
|
|
3
|
-
// Built per request, each costs round trips to somebody else's server for
|
|
4
|
-
// every call; built once, a process pays them once.
|
|
5
|
-
|
|
6
|
-
// The Solid-OIDC verifier remembers the issuers' key sets it has seen. A
|
|
7
|
-
// fresh one per call re-read the caller's WebID document, the issuer's
|
|
8
|
-
// discovery document and its keys on every signed-in call.
|
|
9
|
-
let verifier = null;
|
|
10
|
-
export async function podTokenVerifier() {
|
|
11
|
-
if (!verifier) verifier = (await import('@solid/access-token-verifier')).createSolidTokenVerifier();
|
|
12
|
-
return verifier;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// The keys deliveries are signed with, by key id, in the shape Fedify's
|
|
16
|
-
// verifier asks for. A key is held an hour; a key that could not be fetched
|
|
17
|
-
// is held five minutes, so a server that was down is asked again soon.
|
|
18
|
-
// Fedify re-fetches on its own when a held key no longer verifies, so a
|
|
19
|
-
// rotated key costs one failed check, not an hour of refused mail.
|
|
20
|
-
const KEY_TTL_MS = 60 * 60_000;
|
|
21
|
-
const MISS_TTL_MS = 5 * 60_000;
|
|
22
|
-
const KEY_CACHE_MAX = 500;
|
|
23
|
-
const keys = new Map(); // key id → { key, until }
|
|
24
|
-
export const senderKeys = {
|
|
25
|
-
async get(keyId) {
|
|
26
|
-
const hit = keys.get(keyId.href);
|
|
27
|
-
if (!hit) return undefined;
|
|
28
|
-
if (hit.until < Date.now()) { keys.delete(keyId.href); return undefined; }
|
|
29
|
-
return hit.key;
|
|
30
|
-
},
|
|
31
|
-
async set(keyId, key) {
|
|
32
|
-
if (keys.size >= KEY_CACHE_MAX) keys.delete(keys.keys().next().value);
|
|
33
|
-
keys.set(keyId.href, { key, until: Date.now() + (key ? KEY_TTL_MS : MISS_TTL_MS) });
|
|
34
|
-
},
|
|
35
|
-
size: () => keys.size,
|
|
36
|
-
};
|