@undefineds.co/xpod 0.4.14 → 0.4.15
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/config/cloud.json +0 -3
- package/config/local.json +0 -3
- package/config/xpod.base.json +0 -24
- package/dist/api/ai-config/AiConfigStore.d.ts +2 -2
- package/dist/api/ai-config/AiConfigStore.js +4 -1
- package/dist/api/ai-gateway/auth/CallerPodAccess.d.ts +2 -5
- package/dist/api/ai-gateway/auth/CallerPodAccess.js +3 -42
- package/dist/api/ai-gateway/auth/GatewayApiKeyAuthenticator.d.ts +2 -1
- package/dist/api/ai-gateway/auth/GatewayApiKeyAuthenticator.js +1 -1
- package/dist/api/ai-gateway/auth/PodGatewayAccessKeyRepository.d.ts +3 -8
- package/dist/api/ai-gateway/auth/PodGatewayAccessKeyRepository.js +9 -32
- package/dist/api/ai-gateway/connect/index.d.ts +3 -3
- package/dist/api/ai-gateway/connect/index.js +9 -38
- package/dist/api/ai-gateway/errors.js +2 -1
- package/dist/api/ai-gateway/models/PodModelSelectionRepository.d.ts +3 -3
- package/dist/api/ai-gateway/models/PodModelSelectionRepository.js +7 -11
- package/dist/api/ai-gateway/pod/HostedPodRoute.d.ts +27 -0
- package/dist/api/ai-gateway/pod/HostedPodRoute.js +47 -0
- package/dist/api/ai-gateway/pod/OwnerPodAccess.d.ts +79 -0
- package/dist/api/ai-gateway/pod/OwnerPodAccess.js +192 -0
- package/dist/api/ai-gateway/pod/PodInterfaceKeyStore.d.ts +52 -0
- package/dist/api/ai-gateway/pod/PodInterfaceKeyStore.js +61 -0
- package/dist/api/ai-gateway/quota/ProviderQuotaAdapter.d.ts +3 -3
- package/dist/api/ai-gateway/quota/ProviderQuotaAdapter.js +7 -32
- package/dist/api/auth/ClientCredentialsAuthenticator.js +5 -28
- package/dist/api/auth/TokenEndpointRoute.d.ts +18 -0
- package/dist/api/auth/TokenEndpointRoute.js +36 -0
- package/dist/api/chatkit/pod-store.d.ts +4 -4
- package/dist/api/chatkit/pod-store.js +24 -95
- package/dist/api/container/common.js +36 -23
- package/dist/api/container/local.js +62 -29
- package/dist/api/container/routes.js +39 -7
- package/dist/api/container/types.d.ts +2 -2
- package/dist/api/handlers/AiGatewayManagementHandler.d.ts +6 -0
- package/dist/api/handlers/AiGatewayManagementHandler.js +17 -4
- package/dist/api/handlers/NetworkSettingsHandler.d.ts +7 -0
- package/dist/api/handlers/NetworkSettingsHandler.js +7 -0
- package/dist/api/handlers/PodSettingsHandler.d.ts +3 -3
- package/dist/api/handlers/PodSettingsHandler.js +3 -3
- package/dist/api/handlers/ProvisionHandler.d.ts +29 -0
- package/dist/api/handlers/ProvisionHandler.js +46 -0
- package/dist/api/handlers/ReachabilityHandler.js +3 -1
- package/dist/api/runtime.d.ts +9 -0
- package/dist/api/runtime.js +16 -3
- package/dist/cli/commands/start.d.ts +9 -2
- package/dist/cli/commands/start.js +15 -17
- package/dist/components/components.jsonld +1 -1
- package/dist/components/context.jsonld +5 -42
- package/dist/edge/EdgeNodeAgent.d.ts +10 -0
- package/dist/edge/EdgeNodeAgent.js +55 -0
- package/dist/edge/EdgeNodeAgent.jsonld +8 -0
- package/dist/edge/reachability/ReachabilitySessionService.js +4 -1
- package/dist/edge/reachability/RouteSetBuilder.d.ts +18 -1
- package/dist/edge/reachability/RouteSetBuilder.js +47 -0
- package/dist/edge/reachability/RouteSetBuilder.jsonld +29 -0
- package/dist/edge/reachability/types.d.ts +8 -2
- package/dist/http/SubgraphSparqlHttpHandler.d.ts +1 -25
- package/dist/http/SubgraphSparqlHttpHandler.js +0 -100
- package/dist/http/SubgraphSparqlHttpHandler.jsonld +0 -8
- package/dist/identity/drizzle/PodInterfaceKeyRepository.d.ts +33 -0
- package/dist/identity/drizzle/PodInterfaceKeyRepository.js +78 -0
- package/dist/identity/drizzle/db.d.ts +7 -0
- package/dist/identity/drizzle/db.js +30 -0
- package/dist/identity/drizzle/schema.pg.d.ts +9 -0
- package/dist/identity/drizzle/schema.pg.js +16 -1
- package/dist/identity/drizzle/schema.sqlite.d.ts +106 -0
- package/dist/identity/drizzle/schema.sqlite.js +16 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.js +3 -5
- package/dist/runtime/GatewayAdminProxyAuth.d.ts +1 -16
- package/dist/runtime/GatewayAdminProxyAuth.js +2 -71
- package/dist/runtime/Proxy.d.ts +10 -1
- package/dist/runtime/Proxy.js +95 -28
- package/dist/runtime/bootstrap.d.ts +10 -0
- package/dist/runtime/bootstrap.js +24 -0
- package/dist/runtime/host/node/NodeRuntimeHost.js +5 -3
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.js +2 -3
- package/dist/runtime/js-runtime.js +10 -3
- package/dist/runtime/lifecycle.js +5 -4
- package/dist/runtime/port-finder.d.ts +9 -13
- package/dist/runtime/port-finder.js +18 -37
- package/dist/tunnel/AutoTunnelProvider.d.ts +56 -0
- package/dist/tunnel/AutoTunnelProvider.js +181 -0
- package/dist/tunnel/index.d.ts +1 -0
- package/dist/tunnel/index.js +3 -1
- package/node_modules/@undefineds.co/ai-connections/dist/error-wording.js +7 -2
- package/node_modules/@undefineds.co/solid-sdk/dist/access-route.cjs +230 -0
- package/node_modules/@undefineds.co/solid-sdk/dist/access-route.d.ts +76 -0
- package/node_modules/@undefineds.co/solid-sdk/dist/access-route.js +115 -0
- package/node_modules/@undefineds.co/solid-sdk/dist/index.cjs +89 -0
- package/node_modules/@undefineds.co/solid-sdk/dist/index.d.ts +1 -0
- package/node_modules/@undefineds.co/solid-sdk/dist/index.js +1 -0
- package/node_modules/@undefineds.co/solid-sdk/package.json +6 -1
- package/package.json +2 -2
- package/static/app/assets/inrupt-smoke.js +23 -23
- package/static/auth-callback/assets/{AiConfigContext-DCXpjzpj.js → AiConfigContext-Bx0HCAW5.js} +1 -1
- package/static/auth-callback/assets/{AiConfigPage-fe-xMIXa.js → AiConfigPage-ChQImMbm.js} +1 -1
- package/static/auth-callback/assets/{DocumentProcessingPanel-BGnkfMuU.js → DocumentProcessingPanel-D830H4SH.js} +1 -1
- package/static/auth-callback/assets/{IndexLifecyclePanel-42Jups0j.js → IndexLifecyclePanel-CnlkjMF8.js} +1 -1
- package/static/auth-callback/assets/{IndexSubjectPanel-CYyLOlQU.js → IndexSubjectPanel-CdEfg_CO.js} +1 -1
- package/static/auth-callback/assets/{ModelAssignmentsPanel-BOvUH4yr.js → ModelAssignmentsPanel-D-8A8dZx.js} +1 -1
- package/static/auth-callback/assets/{ModelsPage-DwyITxt7.js → ModelsPage-Cim7cubp.js} +3 -3
- package/static/auth-callback/assets/{NetworkPage-CG9RelJA.js → NetworkPage-CfK3x-pb.js} +2 -2
- package/static/auth-callback/assets/{PaneListHeader-yvd5ohs6.js → PaneListHeader-Bf_kEd4U.js} +1 -1
- package/static/auth-callback/assets/{SearchIndexingPanel-2_a6ezzj.js → SearchIndexingPanel-4kD4RBEQ.js} +1 -1
- package/static/auth-callback/assets/{StatusSubjectPanel-8qtVxnzC.js → StatusSubjectPanel-Bw9AiTSf.js} +1 -1
- package/static/auth-callback/assets/{StatusWorkspace-CBdRahCF.js → StatusWorkspace-C-301H-0.js} +1 -1
- package/static/auth-callback/assets/{SystemSettingsPage-BZJ6vWkg.js → SystemSettingsPage-BbWn-eG6.js} +1 -1
- package/static/auth-callback/assets/{SystemSettingsSubjectPanel-xm08UhXP.js → SystemSettingsSubjectPanel-DA4wvNSm.js} +1 -1
- package/static/auth-callback/assets/{XpodAiConnectionsPodStore-V6vgGfuw.js → XpodAiConnectionsPodStore-CohdyLoX.js} +1 -1
- package/static/auth-callback/assets/{activity-CeM7xly8.js → activity-CCsb9Oam.js} +1 -1
- package/static/auth-callback/assets/ai-connections-DexTPyfC.js +1 -0
- package/static/auth-callback/assets/{auth-callback-DTG8cSj7.js → auth-callback-Ds-c62W3.js} +5 -5
- package/static/auth-callback/assets/{badge-qJo2Zr4d.js → badge-CQm5OyzR.js} +1 -1
- package/static/auth-callback/assets/{collection-runtime-Bztsz31a.js → collection-runtime-DlAuU5TW.js} +1 -1
- package/static/auth-callback/assets/{dialog-D-h6x5x0.js → dialog-CnGzg5Ff.js} +1 -1
- package/static/auth-callback/assets/{download-C2ir4lmR.js → download-DwMONJvm.js} +1 -1
- package/static/auth-callback/assets/{external-link-THLDL4On.js → external-link-Bu5AGqCO.js} +1 -1
- package/static/auth-callback/assets/{index-BlVZb9A7.js → index-B0T-1YO8.js} +1 -1
- package/static/auth-callback/assets/{index-BRpv4yO3.js → index-C8QZf_4W.js} +1 -1
- package/static/auth-callback/assets/{index-D983R1FV.js → index-DwIlrIDG.js} +1 -1
- package/static/auth-callback/assets/{index-browser-3FvR0aaf.js → index-browser-lRsMcP_7.js} +1 -1
- package/static/auth-callback/assets/{key-round-C9U5j1Ly.js → key-round-BDMtU57P.js} +1 -1
- package/static/auth-callback/assets/{rotate-ccw-DP45cD4o.js → rotate-ccw-CoKIGCsQ.js} +1 -1
- package/static/auth-callback/assets/{skeleton-Dn4EiSCS.js → skeleton-CPoSWHDr.js} +1 -1
- package/static/auth-callback/assets/{waypoints-uqFQ3uEG.js → waypoints-BeYHxqHv.js} +1 -1
- package/static/auth-callback/assets/{workspace-layout-DLYK0Wwu.js → workspace-layout-xA5YxiK3.js} +1 -1
- package/static/auth-callback/auth-callback.html +1 -1
- package/static/dashboard/assets/{AiConfigContext-DJcMECNw.js → AiConfigContext-rWx4TyiH.js} +1 -1
- package/static/dashboard/assets/{AiConfigPage-BvE7dOTy.js → AiConfigPage-StVJpDFT.js} +1 -1
- package/static/dashboard/assets/{DocumentProcessingPanel-D9780krf.js → DocumentProcessingPanel-DJ5yul2O.js} +1 -1
- package/static/dashboard/assets/{IndexLifecyclePanel-SjsUcnbM.js → IndexLifecyclePanel-CpjtgezK.js} +1 -1
- package/static/dashboard/assets/{IndexSubjectPanel-C-ym2Tqi.js → IndexSubjectPanel-BVKcot1_.js} +1 -1
- package/static/dashboard/assets/{ModelAssignmentsPanel-BRjZ-arB.js → ModelAssignmentsPanel-BxVgWZPc.js} +1 -1
- package/static/dashboard/assets/{ModelsPage-BtTU6du3.js → ModelsPage-CECBwmeO.js} +3 -3
- package/static/dashboard/assets/{NetworkPage-CV9qpkDg.js → NetworkPage-zQgFscEW.js} +2 -2
- package/static/dashboard/assets/{PaneListHeader-KyPTTBsv.js → PaneListHeader-DIPe8pS6.js} +1 -1
- package/static/dashboard/assets/{SearchIndexingPanel-DolHGawg.js → SearchIndexingPanel-lalFJ3c8.js} +1 -1
- package/static/dashboard/assets/{StatusSubjectPanel-_YZvKiYi.js → StatusSubjectPanel-CpY_KU-G.js} +1 -1
- package/static/dashboard/assets/{StatusWorkspace-Dlbh_G40.js → StatusWorkspace-BwWCii3A.js} +1 -1
- package/static/dashboard/assets/{SystemSettingsPage-Be7QSIoz.js → SystemSettingsPage-BKZxX-_n.js} +1 -1
- package/static/dashboard/assets/{SystemSettingsSubjectPanel-DBp0gja7.js → SystemSettingsSubjectPanel-BZrqb2C5.js} +1 -1
- package/static/dashboard/assets/{XpodAiConnectionsPodStore-ClpSjzpP.js → XpodAiConnectionsPodStore-Bbpx9dTN.js} +1 -1
- package/static/dashboard/assets/{activity-lsNbTXJu.js → activity-BJ15TLVP.js} +1 -1
- package/static/dashboard/assets/ai-connections-GG6qkTJt.js +1 -0
- package/static/dashboard/assets/{badge-CVatKjaG.js → badge-BBX5FlaB.js} +1 -1
- package/static/dashboard/assets/{collection-runtime-Ec4UnY28.js → collection-runtime-UBbHBzdf.js} +1 -1
- package/static/dashboard/assets/{dashboard-BEzsRsvF.js → dashboard-7Y6BHyV9.js} +62 -62
- package/static/dashboard/assets/{dialog-BuyelQ4V.js → dialog-QrOJ-hB0.js} +1 -1
- package/static/dashboard/assets/{download-B4Nf678F.js → download-DK9x-VOP.js} +1 -1
- package/static/dashboard/assets/{external-link-PYv9CvKL.js → external-link-Cnkn87mJ.js} +1 -1
- package/static/dashboard/assets/{index-C9XPE-mI.js → index-B1z84it5.js} +1 -1
- package/static/dashboard/assets/{index-awr7ulur.js → index-DzjauX3N.js} +1 -1
- package/static/dashboard/assets/{index-Cx69bTNh.js → index-cIa8_efI.js} +1 -1
- package/static/dashboard/assets/{key-round-VsY80U1Z.js → key-round-BoS6UqKz.js} +1 -1
- package/static/dashboard/assets/{rotate-ccw-Cz1K4vE9.js → rotate-ccw-BeWV76i0.js} +1 -1
- package/static/dashboard/assets/{skeleton-BhNv_8tO.js → skeleton-CypcJL07.js} +1 -1
- package/static/dashboard/assets/{waypoints-DYS9XFYV.js → waypoints-zHOOqfhi.js} +1 -1
- package/static/dashboard/assets/{workspace-layout-OZJAFx5V.js → workspace-layout-DZ9JuxnM.js} +1 -1
- package/static/dashboard/dashboard.html +1 -1
- package/static/settings/assets/{AiConfigContext-B8JS3MXF.js → AiConfigContext-D3b1l_Kw.js} +1 -1
- package/static/settings/assets/{AiConfigPage-C3Zl8LUl.js → AiConfigPage-DYKw-atP.js} +1 -1
- package/static/settings/assets/{DocumentProcessingPanel-DpDZAQvf.js → DocumentProcessingPanel-DguRfsIV.js} +1 -1
- package/static/settings/assets/{IndexLifecyclePanel-Ec1XF88t.js → IndexLifecyclePanel-B85qJJA4.js} +1 -1
- package/static/settings/assets/{IndexSubjectPanel-BulT9BmO.js → IndexSubjectPanel-CsN7IxaV.js} +1 -1
- package/static/settings/assets/{ModelAssignmentsPanel-BcGSmYjb.js → ModelAssignmentsPanel-DEp9byI6.js} +1 -1
- package/static/settings/assets/{ModelsPage-CvTCKy1T.js → ModelsPage-CzTnPs_G.js} +3 -3
- package/static/settings/assets/{NetworkPage-DQF5u9Bn.js → NetworkPage-BDVnmUU0.js} +2 -2
- package/static/settings/assets/{PaneListHeader-D40hqcVl.js → PaneListHeader-Cpe39N76.js} +1 -1
- package/static/settings/assets/{SearchIndexingPanel-iCUPOx_0.js → SearchIndexingPanel-DA9w8lLL.js} +1 -1
- package/static/settings/assets/{StatusSubjectPanel-E--ZHSql.js → StatusSubjectPanel-BU0Aq0CS.js} +1 -1
- package/static/settings/assets/{StatusWorkspace-YjDAx1II.js → StatusWorkspace-D1AC1i5N.js} +1 -1
- package/static/settings/assets/{SystemSettingsPage-CDdV3mC9.js → SystemSettingsPage-ChKvOioO.js} +1 -1
- package/static/settings/assets/{SystemSettingsSubjectPanel-Br8WazDq.js → SystemSettingsSubjectPanel-DyTpOnD2.js} +1 -1
- package/static/settings/assets/{XpodAiConnectionsPodStore-CLr38P_Z.js → XpodAiConnectionsPodStore-VU5Bsz1a.js} +1 -1
- package/static/settings/assets/{activity-C5DAHHTE.js → activity-DGpLfsQB.js} +1 -1
- package/static/settings/assets/ai-connections-DexTPyfC.js +1 -0
- package/static/settings/assets/{badge-D8eIyxKt.js → badge-_hCrrWRP.js} +1 -1
- package/static/settings/assets/{collection-runtime-DLc9zil8.js → collection-runtime-E40TZ8Cl.js} +1 -1
- package/static/settings/assets/{dialog-CyuR7Va4.js → dialog-DTr2JkHn.js} +1 -1
- package/static/settings/assets/{download-c0oxZrCz.js → download-B_D21DDX.js} +1 -1
- package/static/settings/assets/{external-link-D3Z34fP_.js → external-link-BATYaM13.js} +1 -1
- package/static/settings/assets/{index-CSr3-2Cj.js → index-BAlAVi-J.js} +1 -1
- package/static/settings/assets/{index-ComidxAm.js → index-Ckh4h9jj.js} +1 -1
- package/static/settings/assets/{index-CKGbRFlc.js → index-DOiwLF4B.js} +1 -1
- package/static/settings/assets/{index-browser-CAscEbae.js → index-browser-DFFWN3Kr.js} +1 -1
- package/static/settings/assets/{key-round-DyjLmXuB.js → key-round-ZTVU_1O1.js} +1 -1
- package/static/settings/assets/{rotate-ccw-679HF_ec.js → rotate-ccw-DDdw8PTU.js} +1 -1
- package/static/settings/assets/{settings-DKknqu4C.js → settings-CyLnISOv.js} +62 -62
- package/static/settings/assets/{skeleton-CkopNkPB.js → skeleton-DEzA3hc2.js} +1 -1
- package/static/settings/assets/{waypoints-CrqfAftE.js → waypoints-BSdfNvEb.js} +1 -1
- package/static/settings/assets/{workspace-layout-UN6c4qTG.js → workspace-layout-BOQnl_3d.js} +1 -1
- package/static/settings/settings.html +1 -1
- package/dist/api/ai-gateway/pod/HostedPodDataAccess.d.ts +0 -31
- package/dist/api/ai-gateway/pod/HostedPodDataAccess.js +0 -330
- package/dist/http/InternalPodDataHttpHandler.d.ts +0 -57
- package/dist/http/InternalPodDataHttpHandler.js +0 -551
- package/dist/http/InternalPodDataHttpHandler.jsonld +0 -243
- package/static/auth-callback/assets/ai-connections-C6yaE6WQ.js +0 -1
- package/static/dashboard/assets/ai-connections-CXp5gd1J.js +0 -1
- package/static/settings/assets/ai-connections-C6yaE6WQ.js +0 -1
|
@@ -1,24 +1,21 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.GATEWAY_ADMIN_PROXY_HEADERS = exports.GATEWAY_ADMIN_PROXY_NONCE_HEADER = exports.
|
|
3
|
+
exports.GATEWAY_ADMIN_PROXY_HEADERS = exports.GATEWAY_ADMIN_PROXY_NONCE_HEADER = exports.GATEWAY_ADMIN_PROXY_SIGNATURE_HEADER = exports.GATEWAY_ADMIN_PROXY_TIMESTAMP_HEADER = exports.GATEWAY_ADMIN_PROXY_LOOPBACK_HEADER = void 0;
|
|
4
4
|
exports.createGatewayAdminProxyAuthSecret = createGatewayAdminProxyAuthSecret;
|
|
5
5
|
exports.createGatewayAdminProxyHeaders = createGatewayAdminProxyHeaders;
|
|
6
6
|
exports.stripGatewayAdminProxyHeaders = stripGatewayAdminProxyHeaders;
|
|
7
7
|
exports.isLoopbackRemoteAddress = isLoopbackRemoteAddress;
|
|
8
8
|
exports.verifyGatewayAdminProxyHeaders = verifyGatewayAdminProxyHeaders;
|
|
9
|
-
exports.canonicalGatewayAdminProxyIntent = canonicalGatewayAdminProxyIntent;
|
|
10
9
|
const node_crypto_1 = require("node:crypto");
|
|
11
10
|
const node_net_1 = require("node:net");
|
|
12
11
|
exports.GATEWAY_ADMIN_PROXY_LOOPBACK_HEADER = 'x-xpod-admin-proxy-loopback';
|
|
13
12
|
exports.GATEWAY_ADMIN_PROXY_TIMESTAMP_HEADER = 'x-xpod-admin-proxy-timestamp';
|
|
14
13
|
exports.GATEWAY_ADMIN_PROXY_SIGNATURE_HEADER = 'x-xpod-admin-proxy-signature';
|
|
15
|
-
exports.GATEWAY_ADMIN_PROXY_INTENT_HEADER = 'x-xpod-admin-proxy-intent';
|
|
16
14
|
exports.GATEWAY_ADMIN_PROXY_NONCE_HEADER = 'x-xpod-admin-proxy-nonce';
|
|
17
15
|
exports.GATEWAY_ADMIN_PROXY_HEADERS = [
|
|
18
16
|
exports.GATEWAY_ADMIN_PROXY_LOOPBACK_HEADER,
|
|
19
17
|
exports.GATEWAY_ADMIN_PROXY_TIMESTAMP_HEADER,
|
|
20
18
|
exports.GATEWAY_ADMIN_PROXY_SIGNATURE_HEADER,
|
|
21
|
-
exports.GATEWAY_ADMIN_PROXY_INTENT_HEADER,
|
|
22
19
|
exports.GATEWAY_ADMIN_PROXY_NONCE_HEADER,
|
|
23
20
|
];
|
|
24
21
|
const MAX_CLOCK_SKEW_MS = 60_000;
|
|
@@ -28,12 +25,10 @@ function createGatewayAdminProxyAuthSecret() {
|
|
|
28
25
|
function createGatewayAdminProxyHeaders(input) {
|
|
29
26
|
const issuedAt = input.issuedAt ?? Date.now();
|
|
30
27
|
const loopback = input.originalClientLoopback ? '1' : '0';
|
|
31
|
-
const intent = input.intent ? canonicalGatewayAdminProxyIntent(input.intent) : undefined;
|
|
32
28
|
return {
|
|
33
29
|
[exports.GATEWAY_ADMIN_PROXY_LOOPBACK_HEADER]: loopback,
|
|
34
30
|
[exports.GATEWAY_ADMIN_PROXY_TIMESTAMP_HEADER]: String(issuedAt),
|
|
35
31
|
...(input.nonce ? { [exports.GATEWAY_ADMIN_PROXY_NONCE_HEADER]: input.nonce } : {}),
|
|
36
|
-
...(intent ? { [exports.GATEWAY_ADMIN_PROXY_INTENT_HEADER]: intent } : {}),
|
|
37
32
|
[exports.GATEWAY_ADMIN_PROXY_SIGNATURE_HEADER]: signGatewayAdminProxyMarker({
|
|
38
33
|
secret: input.secret,
|
|
39
34
|
method: input.method,
|
|
@@ -41,7 +36,6 @@ function createGatewayAdminProxyHeaders(input) {
|
|
|
41
36
|
loopback,
|
|
42
37
|
issuedAt,
|
|
43
38
|
nonce: input.nonce,
|
|
44
|
-
intent,
|
|
45
39
|
}),
|
|
46
40
|
};
|
|
47
41
|
}
|
|
@@ -66,7 +60,6 @@ function verifyGatewayAdminProxyHeaders(input) {
|
|
|
66
60
|
const timestamp = firstHeader(input.headers[exports.GATEWAY_ADMIN_PROXY_TIMESTAMP_HEADER]);
|
|
67
61
|
const signature = firstHeader(input.headers[exports.GATEWAY_ADMIN_PROXY_SIGNATURE_HEADER]);
|
|
68
62
|
const nonce = firstHeader(input.headers[exports.GATEWAY_ADMIN_PROXY_NONCE_HEADER]);
|
|
69
|
-
const intentHeader = firstHeader(input.headers[exports.GATEWAY_ADMIN_PROXY_INTENT_HEADER]);
|
|
70
63
|
const present = loopback !== undefined || timestamp !== undefined || signature !== undefined;
|
|
71
64
|
if (!present) {
|
|
72
65
|
return { present: false, valid: false, originalClientLoopback: false };
|
|
@@ -87,10 +80,6 @@ function verifyGatewayAdminProxyHeaders(input) {
|
|
|
87
80
|
if (Math.abs((input.now ?? Date.now()) - issuedAt) > MAX_CLOCK_SKEW_MS) {
|
|
88
81
|
return { present: true, valid: false, originalClientLoopback: loopback === '1', reason: 'expired_marker' };
|
|
89
82
|
}
|
|
90
|
-
const intent = parseGatewayAdminProxyIntent(intentHeader);
|
|
91
|
-
if (intentHeader !== undefined && !intent) {
|
|
92
|
-
return { present: true, valid: false, originalClientLoopback: loopback === '1', reason: 'invalid_intent' };
|
|
93
|
-
}
|
|
94
83
|
const expected = signGatewayAdminProxyMarker({
|
|
95
84
|
secret: input.secret,
|
|
96
85
|
method: input.method,
|
|
@@ -98,12 +87,11 @@ function verifyGatewayAdminProxyHeaders(input) {
|
|
|
98
87
|
loopback,
|
|
99
88
|
issuedAt,
|
|
100
89
|
nonce,
|
|
101
|
-
intent: intentHeader,
|
|
102
90
|
});
|
|
103
91
|
if (!safeEqual(signature, expected)) {
|
|
104
92
|
return { present: true, valid: false, originalClientLoopback: loopback === '1', reason: 'bad_signature' };
|
|
105
93
|
}
|
|
106
|
-
return { present: true, valid: true, originalClientLoopback: loopback === '1', nonce
|
|
94
|
+
return { present: true, valid: true, originalClientLoopback: loopback === '1', nonce };
|
|
107
95
|
}
|
|
108
96
|
function signGatewayAdminProxyMarker(input) {
|
|
109
97
|
const payload = [
|
|
@@ -113,66 +101,9 @@ function signGatewayAdminProxyMarker(input) {
|
|
|
113
101
|
input.loopback,
|
|
114
102
|
String(input.issuedAt),
|
|
115
103
|
input.nonce ?? '',
|
|
116
|
-
input.intent ?? '',
|
|
117
104
|
].join('\n');
|
|
118
105
|
return (0, node_crypto_1.createHmac)('sha256', input.secret).update(payload).digest('base64url');
|
|
119
106
|
}
|
|
120
|
-
function canonicalGatewayAdminProxyIntent(intent) {
|
|
121
|
-
return JSON.stringify({
|
|
122
|
-
ownerWebId: intent.ownerWebId,
|
|
123
|
-
...(intent.podBaseUrl ? { podBaseUrl: intent.podBaseUrl } : {}),
|
|
124
|
-
method: intent.method,
|
|
125
|
-
resourceUrl: intent.resourceUrl,
|
|
126
|
-
principalKind: intent.principalKind,
|
|
127
|
-
scopes: [...intent.scopes].sort(),
|
|
128
|
-
...(intent.bodyDigest ? { bodyDigest: intent.bodyDigest } : {}),
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
function parseGatewayAdminProxyIntent(value) {
|
|
132
|
-
if (!value) {
|
|
133
|
-
return undefined;
|
|
134
|
-
}
|
|
135
|
-
try {
|
|
136
|
-
const parsed = JSON.parse(value);
|
|
137
|
-
if (typeof parsed.ownerWebId !== 'string' ||
|
|
138
|
-
typeof parsed.resourceUrl !== 'string' ||
|
|
139
|
-
(parsed.podBaseUrl !== undefined && !isHttpUrl(parsed.podBaseUrl)) ||
|
|
140
|
-
(parsed.method !== 'GET' && parsed.method !== 'HEAD' && parsed.method !== 'POST' && parsed.method !== 'PUT' && parsed.method !== 'PATCH' && parsed.method !== 'DELETE') ||
|
|
141
|
-
(parsed.principalKind !== 'solid-user' && parsed.principalKind !== 'gateway-key-verifier') ||
|
|
142
|
-
!Array.isArray(parsed.scopes) ||
|
|
143
|
-
!parsed.scopes.every((scope) => typeof scope === 'string') ||
|
|
144
|
-
(parsed.bodyDigest !== undefined && !isSha256Hex(parsed.bodyDigest)) ||
|
|
145
|
-
(parsed.method === 'POST' && !isSha256Hex(parsed.bodyDigest))) {
|
|
146
|
-
return undefined;
|
|
147
|
-
}
|
|
148
|
-
return {
|
|
149
|
-
ownerWebId: parsed.ownerWebId,
|
|
150
|
-
...(parsed.podBaseUrl ? { podBaseUrl: parsed.podBaseUrl } : {}),
|
|
151
|
-
method: parsed.method,
|
|
152
|
-
resourceUrl: parsed.resourceUrl,
|
|
153
|
-
principalKind: parsed.principalKind,
|
|
154
|
-
scopes: parsed.scopes,
|
|
155
|
-
...(parsed.bodyDigest ? { bodyDigest: parsed.bodyDigest } : {}),
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
catch {
|
|
159
|
-
return undefined;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
function isHttpUrl(value) {
|
|
163
|
-
if (typeof value !== 'string')
|
|
164
|
-
return false;
|
|
165
|
-
try {
|
|
166
|
-
const url = new URL(value);
|
|
167
|
-
return url.protocol === 'http:' || url.protocol === 'https:';
|
|
168
|
-
}
|
|
169
|
-
catch {
|
|
170
|
-
return false;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
function isSha256Hex(value) {
|
|
174
|
-
return typeof value === 'string' && /^[a-f0-9]{64}$/u.test(value);
|
|
175
|
-
}
|
|
176
107
|
function safeEqual(actual, expected) {
|
|
177
108
|
const actualBuffer = Buffer.from(actual);
|
|
178
109
|
const expectedBuffer = Buffer.from(expected);
|
package/dist/runtime/Proxy.d.ts
CHANGED
|
@@ -73,7 +73,6 @@ export declare class GatewayProxy {
|
|
|
73
73
|
private shouldRouteToApi;
|
|
74
74
|
private pathnameFromRequestUrl;
|
|
75
75
|
private applyInternalAdminProxyHeaders;
|
|
76
|
-
private verifiedInternalPodProxyHeaders;
|
|
77
76
|
private isApiHost;
|
|
78
77
|
private configuredApiHosts;
|
|
79
78
|
private hostsFromUrlList;
|
|
@@ -94,6 +93,15 @@ export declare class GatewayProxy {
|
|
|
94
93
|
* Add CORS headers matching CSS CorsHandler configuration
|
|
95
94
|
*/
|
|
96
95
|
private addCorsHeaders;
|
|
96
|
+
/**
|
|
97
|
+
* Whether this request carries operator authority.
|
|
98
|
+
*
|
|
99
|
+
* `/service/*` exposes logs and service control, so a caller that only reached the Gateway
|
|
100
|
+
* through a tunnel must not get it - whatever address it presents. A local caller counts, and
|
|
101
|
+
* so does a signed gateway admin proxy header, which is how the same machine proves it is the
|
|
102
|
+
* operator through a local forwarder.
|
|
103
|
+
*/
|
|
104
|
+
private hasOperatorAuthority;
|
|
97
105
|
private handleInternalApi;
|
|
98
106
|
private isCssReady;
|
|
99
107
|
private normalizeTarget;
|
|
@@ -125,3 +133,4 @@ export interface GatewayProxyOptions {
|
|
|
125
133
|
*/
|
|
126
134
|
ingressPort?: number;
|
|
127
135
|
}
|
|
136
|
+
export declare function forwardedFromOutside(req: http.IncomingMessage): boolean;
|
package/dist/runtime/Proxy.js
CHANGED
|
@@ -4,8 +4,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.GatewayProxy = void 0;
|
|
7
|
+
exports.forwardedFromOutside = forwardedFromOutside;
|
|
7
8
|
const http_proxy_1 = __importDefault(require("http-proxy"));
|
|
8
9
|
const http_1 = __importDefault(require("http"));
|
|
10
|
+
const node_net_1 = require("node:net");
|
|
9
11
|
const global_logger_factory_1 = require("global-logger-factory");
|
|
10
12
|
const NodeRuntimeHost_1 = require("./host/node/NodeRuntimeHost");
|
|
11
13
|
const GatewayAdminProxyAuth_1 = require("./GatewayAdminProxyAuth");
|
|
@@ -214,15 +216,15 @@ class GatewayProxy {
|
|
|
214
216
|
const pathname = url.split('?')[0];
|
|
215
217
|
const origin = req.headers.origin;
|
|
216
218
|
const originalRemoteAddress = this.clientRemoteAddressResolver?.(req) ?? req.socket.remoteAddress;
|
|
217
|
-
// A loopback peer address only proves the connection came from this machine
|
|
218
|
-
//
|
|
219
|
-
//
|
|
220
|
-
|
|
221
|
-
|
|
219
|
+
// A loopback peer address only proves the connection came from this machine: tunnels and
|
|
220
|
+
// the P2P data plane terminate here too. Forwarded-for is what separates them - a local
|
|
221
|
+
// dev proxy forwards this machine's address, while a provider edge appends the address it
|
|
222
|
+
// saw, so a request whose right-most forwarder is not loopback came from outside and is
|
|
223
|
+
// never local, whatever its peer address or headers say.
|
|
224
|
+
const originalClientLoopback = !untrustedIngress
|
|
225
|
+
&& !forwardedFromOutside(req)
|
|
226
|
+
&& (0, GatewayAdminProxyAuth_1.isLoopbackRemoteAddress)(originalRemoteAddress);
|
|
222
227
|
(0, GatewayAdminProxyAuth_1.stripGatewayAdminProxyHeaders)(req.headers);
|
|
223
|
-
if (internalPodProxyHeaders) {
|
|
224
|
-
Object.assign(req.headers, internalPodProxyHeaders);
|
|
225
|
-
}
|
|
226
228
|
// Store public host for routing before any CSS canonical-host rewrites.
|
|
227
229
|
// External gateways pass the original domain through X-Forwarded-Host;
|
|
228
230
|
// direct/local requests use Host.
|
|
@@ -302,7 +304,7 @@ class GatewayProxy {
|
|
|
302
304
|
if (origin) {
|
|
303
305
|
this.addCorsHeaders(res, origin);
|
|
304
306
|
}
|
|
305
|
-
void this.handleInternalApi(req, res);
|
|
307
|
+
void this.handleInternalApi(req, res, this.hasOperatorAuthority(req, originalClientLoopback));
|
|
306
308
|
return;
|
|
307
309
|
}
|
|
308
310
|
// 2. API Server Routing.
|
|
@@ -387,24 +389,6 @@ class GatewayProxy {
|
|
|
387
389
|
originalClientLoopback,
|
|
388
390
|
}));
|
|
389
391
|
}
|
|
390
|
-
verifiedInternalPodProxyHeaders(req, originalClientLoopback) {
|
|
391
|
-
if (!originalClientLoopback || req.url !== '/.internal/pod-data') {
|
|
392
|
-
return undefined;
|
|
393
|
-
}
|
|
394
|
-
const verification = (0, GatewayAdminProxyAuth_1.verifyGatewayAdminProxyHeaders)({
|
|
395
|
-
headers: req.headers,
|
|
396
|
-
secret: this.internalAdminAuthSecret,
|
|
397
|
-
method: req.method,
|
|
398
|
-
url: req.url,
|
|
399
|
-
});
|
|
400
|
-
if (!verification.valid || !verification.originalClientLoopback || !verification.intent || !verification.nonce) {
|
|
401
|
-
return undefined;
|
|
402
|
-
}
|
|
403
|
-
return Object.fromEntries(GatewayAdminProxyAuth_1.GATEWAY_ADMIN_PROXY_HEADERS.flatMap((header) => {
|
|
404
|
-
const value = req.headers[header];
|
|
405
|
-
return value === undefined ? [] : [[header, value]];
|
|
406
|
-
}));
|
|
407
|
-
}
|
|
408
392
|
isApiHost(hostHeader) {
|
|
409
393
|
const host = this.normalizeHost(hostHeader);
|
|
410
394
|
if (!host) {
|
|
@@ -579,11 +563,35 @@ class GatewayProxy {
|
|
|
579
563
|
res.setHeader('Access-Control-Allow-Headers', CORS_CONFIG.allowedHeaders.join(', '));
|
|
580
564
|
res.setHeader('Access-Control-Expose-Headers', CORS_CONFIG.exposedHeaders.join(', '));
|
|
581
565
|
}
|
|
582
|
-
|
|
566
|
+
/**
|
|
567
|
+
* Whether this request carries operator authority.
|
|
568
|
+
*
|
|
569
|
+
* `/service/*` exposes logs and service control, so a caller that only reached the Gateway
|
|
570
|
+
* through a tunnel must not get it - whatever address it presents. A local caller counts, and
|
|
571
|
+
* so does a signed gateway admin proxy header, which is how the same machine proves it is the
|
|
572
|
+
* operator through a local forwarder.
|
|
573
|
+
*/
|
|
574
|
+
hasOperatorAuthority(req, originalClientLoopback) {
|
|
575
|
+
if (originalClientLoopback) {
|
|
576
|
+
return true;
|
|
577
|
+
}
|
|
578
|
+
if (!this.internalAdminAuthSecret) {
|
|
579
|
+
return false;
|
|
580
|
+
}
|
|
581
|
+
const verification = (0, GatewayAdminProxyAuth_1.verifyGatewayAdminProxyHeaders)({
|
|
582
|
+
headers: req.headers,
|
|
583
|
+
secret: this.internalAdminAuthSecret,
|
|
584
|
+
method: req.method,
|
|
585
|
+
url: req.url,
|
|
586
|
+
});
|
|
587
|
+
return verification.valid;
|
|
588
|
+
}
|
|
589
|
+
async handleInternalApi(req, res, operatorAuthorized) {
|
|
583
590
|
try {
|
|
584
591
|
const reqUrl = req.url ?? '/';
|
|
585
592
|
const parsed = new URL(reqUrl, 'http://localhost');
|
|
586
593
|
const pathname = parsed.pathname;
|
|
594
|
+
// Status is the entry probe every client uses, and it carries no secrets.
|
|
587
595
|
if (pathname === '/service/status') {
|
|
588
596
|
const status = this.supervisor.getAllStatus();
|
|
589
597
|
const cssReady = await this.isCssReady();
|
|
@@ -593,6 +601,10 @@ class GatewayProxy {
|
|
|
593
601
|
return;
|
|
594
602
|
}
|
|
595
603
|
if (pathname === '/service/logs') {
|
|
604
|
+
if (!operatorAuthorized) {
|
|
605
|
+
sendOperatorRequired(res);
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
596
608
|
const level = parsed.searchParams.get('level') ?? undefined;
|
|
597
609
|
const source = parsed.searchParams.get('source') ?? undefined;
|
|
598
610
|
const limitValue = parsed.searchParams.get('limit');
|
|
@@ -608,6 +620,10 @@ class GatewayProxy {
|
|
|
608
620
|
}
|
|
609
621
|
const restartMatch = /^\/service\/restart\/([^/]+)$/.exec(pathname);
|
|
610
622
|
if (restartMatch && req.method === 'POST') {
|
|
623
|
+
if (!operatorAuthorized) {
|
|
624
|
+
sendOperatorRequired(res);
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
611
627
|
const service = decodeURIComponent(restartMatch[1]);
|
|
612
628
|
if (service === 'gateway') {
|
|
613
629
|
res.writeHead(409, { 'Content-Type': 'application/json' });
|
|
@@ -630,6 +646,10 @@ class GatewayProxy {
|
|
|
630
646
|
return;
|
|
631
647
|
}
|
|
632
648
|
if (pathname === '/service/stop' && req.method === 'POST') {
|
|
649
|
+
if (!operatorAuthorized) {
|
|
650
|
+
sendOperatorRequired(res);
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
633
653
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
634
654
|
res.end(JSON.stringify({ ok: true }));
|
|
635
655
|
setImmediate(() => {
|
|
@@ -682,4 +702,51 @@ exports.GatewayProxy = GatewayProxy;
|
|
|
682
702
|
function isBunRuntime() {
|
|
683
703
|
return typeof globalThis.Bun !== 'undefined';
|
|
684
704
|
}
|
|
705
|
+
/**
|
|
706
|
+
* Whether the client-address headers say this request travelled through a forwarder outside
|
|
707
|
+
* this machine.
|
|
708
|
+
*
|
|
709
|
+
* This is the fallback behind the structural gate (the Gateway's tunnel entry listener): a
|
|
710
|
+
* tunnel pointed at the Gateway port by mistake still must not inherit local trust. Only
|
|
711
|
+
* headers that carry a client *address* count - `x-forwarded-for`, `x-real-ip` and the
|
|
712
|
+
* `for=` part of `Forwarded`. `x-forwarded-host`/`x-forwarded-proto` do not: a local dev proxy
|
|
713
|
+
* sets them for local browsers too, so treating them as evidence would lock the operator out
|
|
714
|
+
* of their own admin surface.
|
|
715
|
+
*
|
|
716
|
+
* The right-most value is the address the last forwarder saw - the one our own handlers append
|
|
717
|
+
* (`ClusterIngressRouter`, `EdgeNodeProxyHttpHandler`, `PodRoutingHttpHandler`) - so a client
|
|
718
|
+
* cannot look local by sending its own. An unreadable value is treated as remote.
|
|
719
|
+
*/
|
|
720
|
+
/** Service control and logs are for the operator, not for whoever reached the entry. */
|
|
721
|
+
function sendOperatorRequired(res) {
|
|
722
|
+
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
723
|
+
res.end(JSON.stringify({ error: 'Operator access required' }));
|
|
724
|
+
}
|
|
725
|
+
function forwardedFromOutside(req) {
|
|
726
|
+
const evidence = [
|
|
727
|
+
...splitHeaderList(req.headers['x-forwarded-for']),
|
|
728
|
+
...splitHeaderList(req.headers['x-real-ip']),
|
|
729
|
+
...forwardedForParameters(req.headers.forwarded),
|
|
730
|
+
];
|
|
731
|
+
const last = evidence.at(-1);
|
|
732
|
+
if (last === undefined) {
|
|
733
|
+
return false;
|
|
734
|
+
}
|
|
735
|
+
return (0, node_net_1.isIP)(last) === 0 || !(0, GatewayAdminProxyAuth_1.isLoopbackRemoteAddress)(last);
|
|
736
|
+
}
|
|
737
|
+
function splitHeaderList(raw) {
|
|
738
|
+
const value = Array.isArray(raw) ? raw.join(',') : raw;
|
|
739
|
+
return value?.split(',').map((entry) => entry.trim()).filter(Boolean) ?? [];
|
|
740
|
+
}
|
|
741
|
+
/** The `for=` values of an RFC 7239 `Forwarded` header, in order. */
|
|
742
|
+
function forwardedForParameters(raw) {
|
|
743
|
+
const value = Array.isArray(raw) ? raw.join(',') : raw;
|
|
744
|
+
if (!value) {
|
|
745
|
+
return [];
|
|
746
|
+
}
|
|
747
|
+
return value.split(/[;,]/u)
|
|
748
|
+
.map((part) => /^\s*for\s*=\s*(.+)$/iu.exec(part.trim())?.[1])
|
|
749
|
+
.filter((entry) => Boolean(entry))
|
|
750
|
+
.map((entry) => entry.replace(/^"|"$/gu, '').replace(/^\[|\]$/gu, ''));
|
|
751
|
+
}
|
|
685
752
|
//# sourceMappingURL=Proxy.js.map
|
|
@@ -25,6 +25,16 @@ export interface RuntimeBootstrapState {
|
|
|
25
25
|
ports: XpodRuntimePorts;
|
|
26
26
|
sockets: XpodRuntimeSockets;
|
|
27
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Address that a co-located runtime service must be reached on when it was bound
|
|
30
|
+
* to `bindHost`. Wildcard binds are not connectable on every platform, so they
|
|
31
|
+
* map to their loopback counterpart; any other value (including a hostname such
|
|
32
|
+
* as `localhost`, which may resolve to IPv6 only) is used as-is, because the
|
|
33
|
+
* gateway must connect to the very address the service actually bound.
|
|
34
|
+
*/
|
|
35
|
+
export declare function connectHostForBindHost(bindHost?: string): string;
|
|
36
|
+
/** Loopback URL for a runtime service bound to `bindHost`. */
|
|
37
|
+
export declare function localServiceUrl(bindHost: string | undefined, port: number): string;
|
|
28
38
|
export declare function cssAuthModeConfigImports(authMode: AuthMode): string[];
|
|
29
39
|
export declare function resolveRuntimeBootstrap(id: string, options: XpodRuntimeOptions, host: RuntimeHost, platform?: RuntimePlatform): Promise<RuntimeBootstrapState>;
|
|
30
40
|
export declare function buildRuntimeEnv(state: RuntimeBootstrapState, options: XpodRuntimeOptions, envFromFile?: Record<string, string | undefined>): Record<string, string | undefined>;
|
|
@@ -3,6 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.connectHostForBindHost = connectHostForBindHost;
|
|
7
|
+
exports.localServiceUrl = localServiceUrl;
|
|
6
8
|
exports.cssAuthModeConfigImports = cssAuthModeConfigImports;
|
|
7
9
|
exports.resolveRuntimeBootstrap = resolveRuntimeBootstrap;
|
|
8
10
|
exports.buildRuntimeEnv = buildRuntimeEnv;
|
|
@@ -35,6 +37,28 @@ function ensureTrailingSlash(url) {
|
|
|
35
37
|
function publicHostForBindHost(bindHost) {
|
|
36
38
|
return bindHost === '127.0.0.1' || bindHost === '::1' ? 'localhost' : bindHost;
|
|
37
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Address that a co-located runtime service must be reached on when it was bound
|
|
42
|
+
* to `bindHost`. Wildcard binds are not connectable on every platform, so they
|
|
43
|
+
* map to their loopback counterpart; any other value (including a hostname such
|
|
44
|
+
* as `localhost`, which may resolve to IPv6 only) is used as-is, because the
|
|
45
|
+
* gateway must connect to the very address the service actually bound.
|
|
46
|
+
*/
|
|
47
|
+
function connectHostForBindHost(bindHost) {
|
|
48
|
+
const host = bindHost ?? '127.0.0.1';
|
|
49
|
+
if (host === '0.0.0.0') {
|
|
50
|
+
return '127.0.0.1';
|
|
51
|
+
}
|
|
52
|
+
if (host === '::' || host === '[::]' || host === '::0') {
|
|
53
|
+
return '::1';
|
|
54
|
+
}
|
|
55
|
+
return host;
|
|
56
|
+
}
|
|
57
|
+
/** Loopback URL for a runtime service bound to `bindHost`. */
|
|
58
|
+
function localServiceUrl(bindHost, port) {
|
|
59
|
+
const host = connectHostForBindHost(bindHost);
|
|
60
|
+
return `http://${host.includes(':') ? `[${host}]` : host}:${port}`;
|
|
61
|
+
}
|
|
38
62
|
function normalizeWindowsAbsolutePath(filePath) {
|
|
39
63
|
return filePath.replace(/^[\\/]+(?=[A-Za-z]:[\\/])/, '');
|
|
40
64
|
}
|
|
@@ -23,9 +23,11 @@ class NodeRuntimeHost {
|
|
|
23
23
|
const gateway = options.gatewayPort ?? await (0, port_finder_1.getFreePort)(options.basePort ?? 5600);
|
|
24
24
|
const css = options.cssPort ?? await (0, port_finder_1.getFreePort)(gateway + 1);
|
|
25
25
|
const api = options.apiPort ?? await (0, port_finder_1.getFreePort)(css + 1);
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
|
|
26
|
+
// Tunnels (and the P2P data plane) terminate here, and this listener never treats a
|
|
27
|
+
// caller as local whatever headers it carries - that is the gate. Its port is the one
|
|
28
|
+
// number the user copies into a provider console, so it is predictable rather than
|
|
29
|
+
// random, and it is what the runtime reports as the tunnel origin.
|
|
30
|
+
const ingress = options.ingressPort ?? await (0, port_finder_1.findGatewayIngressPort)(gateway);
|
|
29
31
|
return { gateway, css, api, ingress };
|
|
30
32
|
}
|
|
31
33
|
createListenEndpoint(options) {
|
package/dist/runtime/index.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export { createOpenAuthContext, registerManagedRuntimeServices, startApiRuntime,
|
|
|
10
10
|
export type { RuntimeServices } from './lifecycle';
|
|
11
11
|
export { GatewayProxy } from './Proxy';
|
|
12
12
|
export { createGatewayAdminProxyAuthSecret } from './GatewayAdminProxyAuth';
|
|
13
|
-
export { getEphemeralLoopbackPort, getFreePort, getFreePortForWildcard,
|
|
13
|
+
export { findGatewayIngressPort, getEphemeralLoopbackPort, getFreePort, getFreePortForWildcard, requireFreePortForWildcard } from './port-finder';
|
|
14
14
|
export { ensureTrailingSlash, INVALID_CONFIGURATION_PREFIX, validateBaseUrl } from './base-url';
|
|
15
15
|
export type { ValidateBaseUrlOptions } from './base-url';
|
|
16
16
|
export { applyEnv, loadEnvFile } from './env-utils';
|
package/dist/runtime/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.startXpodRuntime = exports.nodeGatewayRuntimeRunner = exports.NodeGatewayRuntimeRunner = exports.nodeApiRuntimeRunner = exports.NodeApiRuntimeRunner = exports.communitySolidServerCssRunner = exports.CommunitySolidServerCssRunner = exports.registerSocketOriginShims = exports.registerSocketFetchOrigin = exports.nodeRuntimePlatform = exports.NodeRuntimePlatform = exports.nodeRuntimeHost = exports.NodeRuntimeHost = exports.resolveXpodEnvPath = exports.defaultXpodEnvPath = exports.loadEnvFile = exports.applyEnv = exports.validateBaseUrl = exports.INVALID_CONFIGURATION_PREFIX = exports.ensureTrailingSlash = exports.
|
|
3
|
+
exports.startXpodRuntime = exports.nodeGatewayRuntimeRunner = exports.NodeGatewayRuntimeRunner = exports.nodeApiRuntimeRunner = exports.NodeApiRuntimeRunner = exports.communitySolidServerCssRunner = exports.CommunitySolidServerCssRunner = exports.registerSocketOriginShims = exports.registerSocketFetchOrigin = exports.nodeRuntimePlatform = exports.NodeRuntimePlatform = exports.nodeRuntimeHost = exports.NodeRuntimeHost = exports.resolveXpodEnvPath = exports.defaultXpodEnvPath = exports.loadEnvFile = exports.applyEnv = exports.validateBaseUrl = exports.INVALID_CONFIGURATION_PREFIX = exports.ensureTrailingSlash = exports.requireFreePortForWildcard = exports.getFreePortForWildcard = exports.getFreePort = exports.getEphemeralLoopbackPort = exports.findGatewayIngressPort = exports.createGatewayAdminProxyAuthSecret = exports.GatewayProxy = exports.stopRuntimeServices = exports.startGatewayRuntime = exports.startCssRuntime = exports.startApiRuntime = exports.registerManagedRuntimeServices = exports.createOpenAuthContext = exports.createRuntimeEnvironmentSession = exports.nodeRuntimeDriver = exports.NodeRuntimeDriver = exports.resolveRuntimeBootstrap = exports.initRuntimeLogger = exports.createCssRuntimeConfig = exports.buildRuntimeShorthand = exports.buildRuntimeEnv = exports.ensureBunUndiciCompat = exports.findPackageRoot = exports.PACKAGE_ROOT = void 0;
|
|
4
4
|
require("./configure-drizzle-solid");
|
|
5
5
|
var package_root_1 = require("./package-root");
|
|
6
6
|
Object.defineProperty(exports, "PACKAGE_ROOT", { enumerable: true, get: function () { return package_root_1.PACKAGE_ROOT; } });
|
|
@@ -30,12 +30,11 @@ Object.defineProperty(exports, "GatewayProxy", { enumerable: true, get: function
|
|
|
30
30
|
var GatewayAdminProxyAuth_1 = require("./GatewayAdminProxyAuth");
|
|
31
31
|
Object.defineProperty(exports, "createGatewayAdminProxyAuthSecret", { enumerable: true, get: function () { return GatewayAdminProxyAuth_1.createGatewayAdminProxyAuthSecret; } });
|
|
32
32
|
var port_finder_1 = require("./port-finder");
|
|
33
|
+
Object.defineProperty(exports, "findGatewayIngressPort", { enumerable: true, get: function () { return port_finder_1.findGatewayIngressPort; } });
|
|
33
34
|
Object.defineProperty(exports, "getEphemeralLoopbackPort", { enumerable: true, get: function () { return port_finder_1.getEphemeralLoopbackPort; } });
|
|
34
35
|
Object.defineProperty(exports, "getFreePort", { enumerable: true, get: function () { return port_finder_1.getFreePort; } });
|
|
35
36
|
Object.defineProperty(exports, "getFreePortForWildcard", { enumerable: true, get: function () { return port_finder_1.getFreePortForWildcard; } });
|
|
36
|
-
Object.defineProperty(exports, "readPortFile", { enumerable: true, get: function () { return port_finder_1.readPortFile; } });
|
|
37
37
|
Object.defineProperty(exports, "requireFreePortForWildcard", { enumerable: true, get: function () { return port_finder_1.requireFreePortForWildcard; } });
|
|
38
|
-
Object.defineProperty(exports, "resolveStableLoopbackPort", { enumerable: true, get: function () { return port_finder_1.resolveStableLoopbackPort; } });
|
|
39
38
|
var base_url_1 = require("./base-url");
|
|
40
39
|
Object.defineProperty(exports, "ensureTrailingSlash", { enumerable: true, get: function () { return base_url_1.ensureTrailingSlash; } });
|
|
41
40
|
Object.defineProperty(exports, "INVALID_CONFIGURATION_PREFIX", { enumerable: true, get: function () { return base_url_1.INVALID_CONFIGURATION_PREFIX; } });
|
|
@@ -19,8 +19,15 @@ function findBunExecutable() {
|
|
|
19
19
|
return result.status === 0 ? result.stdout.trim() || undefined : undefined;
|
|
20
20
|
}
|
|
21
21
|
function jsEntrypointArgs(entrypoint, isBun) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
if (!isBun) {
|
|
23
|
+
return entrypoint.endsWith('.ts')
|
|
24
|
+
? ['-r', require.resolve('ts-node/register/transpile-only'), entrypoint]
|
|
25
|
+
: [entrypoint];
|
|
26
|
+
}
|
|
27
|
+
// A child service runs on the environment its parent decided for it. Without this, Bun loads
|
|
28
|
+
// whichever .env files happen to sit in the child's cwd, so a checkout's developer env files
|
|
29
|
+
// leak into a runtime that was started standalone (`bun run local` already loads them once, for
|
|
30
|
+
// the parent, on purpose).
|
|
31
|
+
return ['--no-env-file', entrypoint];
|
|
25
32
|
}
|
|
26
33
|
//# sourceMappingURL=js-runtime.js.map
|
|
@@ -7,6 +7,7 @@ exports.startApiRuntime = startApiRuntime;
|
|
|
7
7
|
exports.startGatewayRuntime = startGatewayRuntime;
|
|
8
8
|
exports.stopRuntimeServices = stopRuntimeServices;
|
|
9
9
|
const package_root_1 = require("./package-root");
|
|
10
|
+
const bootstrap_1 = require("./bootstrap");
|
|
10
11
|
const RedisClientLifecycle_1 = require("../storage/redis/RedisClientLifecycle");
|
|
11
12
|
const DEFAULT_SERVICE_STOP_TIMEOUT_MS = 5_000;
|
|
12
13
|
async function stopWithTimeout(name, stop, timeoutMs) {
|
|
@@ -80,7 +81,7 @@ async function startCssRuntime({ state, host, runtimeShorthand, supervisor, crea
|
|
|
80
81
|
}
|
|
81
82
|
else {
|
|
82
83
|
await host.waitForPortReady(state.ports.css, '127.0.0.1');
|
|
83
|
-
supervisor.addLog('css', 'info', `CSS started (
|
|
84
|
+
supervisor.addLog('css', 'info', `CSS started (${(0, bootstrap_1.localServiceUrl)(state.bindHost, state.ports.css)})`);
|
|
84
85
|
}
|
|
85
86
|
supervisor.setStatus('css', 'running', { startTime: Date.now() });
|
|
86
87
|
return cssApp;
|
|
@@ -92,7 +93,7 @@ async function startApiRuntime({ state, host, supervisor, authContext, apiRunner
|
|
|
92
93
|
authContext: createOpenAuthContext(state.canonicalBaseUrl ?? state.baseUrl, authContext),
|
|
93
94
|
runtimeHost: host,
|
|
94
95
|
});
|
|
95
|
-
supervisor.addLog('api', 'info', `API started (${state.transport === 'socket' ? `unix://${state.sockets.api}` :
|
|
96
|
+
supervisor.addLog('api', 'info', `API started (${state.transport === 'socket' ? `unix://${state.sockets.api}` : (0, bootstrap_1.localServiceUrl)(state.bindHost, state.ports.api)})`);
|
|
96
97
|
supervisor.setStatus('api', 'running', { startTime: Date.now() });
|
|
97
98
|
return apiService;
|
|
98
99
|
}
|
|
@@ -110,8 +111,8 @@ async function startGatewayRuntime({ state, host, supervisor, shutdownHandler, g
|
|
|
110
111
|
clientRemoteAddressResolver,
|
|
111
112
|
ingressPort: state.ports.ingress,
|
|
112
113
|
targets: {
|
|
113
|
-
css: state.transport === 'socket' ? { socketPath: state.sockets.css } : { url:
|
|
114
|
-
api: state.transport === 'socket' ? { socketPath: state.sockets.api } : { url:
|
|
114
|
+
css: state.transport === 'socket' ? { socketPath: state.sockets.css } : { url: (0, bootstrap_1.localServiceUrl)(state.bindHost, state.ports.css) },
|
|
115
|
+
api: state.transport === 'socket' ? { socketPath: state.sockets.api } : { url: (0, bootstrap_1.localServiceUrl)(state.bindHost, state.ports.api) },
|
|
115
116
|
},
|
|
116
117
|
});
|
|
117
118
|
supervisor.addLog('xpod', 'info', `Gateway started (${state.transport === 'socket' ? `unix://${state.sockets.gateway}` : state.baseUrl})`);
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The port a remote tunnel forwards to: the Gateway's tunnel entry.
|
|
3
|
+
*
|
|
4
|
+
* It sits a few ports above the Gateway so it is predictable - the user copies this one number
|
|
5
|
+
* into a provider console and it survives restarts - while staying clear of the block a
|
|
6
|
+
* neighbouring runtime plans for its own services. When the neighbourhood is busy the caller
|
|
7
|
+
* gets an OS-assigned port instead of a stolen one; the runtime reports whichever it got.
|
|
8
|
+
*/
|
|
9
|
+
export declare function findGatewayIngressPort(gatewayPort: number): Promise<number>;
|
|
1
10
|
export declare function getFreePort(basePort: number, host?: string, timeoutMs?: number): Promise<number>;
|
|
2
11
|
/**
|
|
3
12
|
* Loopback port handed out by the OS for a listener that must not collide with ports the
|
|
@@ -9,19 +18,6 @@ export declare function getFreePort(basePort: number, host?: string, timeoutMs?:
|
|
|
9
18
|
* runtime's service.
|
|
10
19
|
*/
|
|
11
20
|
export declare function getEphemeralLoopbackPort(): Promise<number>;
|
|
12
|
-
/**
|
|
13
|
-
* The ingress port this deployment should keep using.
|
|
14
|
-
*
|
|
15
|
-
* Remote tunnels forward to a port the operator typed into a provider console, so an
|
|
16
|
-
* OS-assigned port would invalidate that copy on every restart. The port is remembered in
|
|
17
|
-
* the runtime state directory; if something else took it meanwhile, a new one is chosen and
|
|
18
|
-
* persisted — the caller reports the change so the stale console value can be corrected.
|
|
19
|
-
*/
|
|
20
|
-
export declare function resolveStableLoopbackPort(stateFile: string): Promise<{
|
|
21
|
-
port: number;
|
|
22
|
-
changed: boolean;
|
|
23
|
-
}>;
|
|
24
|
-
export declare function readPortFile(stateFile: string): number | undefined;
|
|
25
21
|
/**
|
|
26
22
|
* Allocates a port for child services that bind wildcard addresses.
|
|
27
23
|
* CSS may bind `::` while the API binds `0.0.0.0`, so probing only localhost
|
|
@@ -3,16 +3,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.findGatewayIngressPort = findGatewayIngressPort;
|
|
6
7
|
exports.getFreePort = getFreePort;
|
|
7
8
|
exports.getEphemeralLoopbackPort = getEphemeralLoopbackPort;
|
|
8
|
-
exports.resolveStableLoopbackPort = resolveStableLoopbackPort;
|
|
9
|
-
exports.readPortFile = readPortFile;
|
|
10
9
|
exports.requireFreePortForWildcard = requireFreePortForWildcard;
|
|
11
10
|
exports.getFreePortForWildcard = getFreePortForWildcard;
|
|
12
11
|
const node_net_1 = __importDefault(require("node:net"));
|
|
13
12
|
const node_os_1 = __importDefault(require("node:os"));
|
|
14
|
-
const node_fs_1 = require("node:fs");
|
|
15
|
-
const node_path_1 = require("node:path");
|
|
16
13
|
const HIGHEST_PORT = 65_535;
|
|
17
14
|
const PORT_PROBE_TIMEOUT_MS = 1_000;
|
|
18
15
|
const RETRYABLE_PORT_ERRORS = new Set([
|
|
@@ -103,6 +100,23 @@ async function canListen(port, host, timeoutMs = PORT_PROBE_TIMEOUT_MS) {
|
|
|
103
100
|
server.listen(port, host);
|
|
104
101
|
});
|
|
105
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* The port a remote tunnel forwards to: the Gateway's tunnel entry.
|
|
105
|
+
*
|
|
106
|
+
* It sits a few ports above the Gateway so it is predictable - the user copies this one number
|
|
107
|
+
* into a provider console and it survives restarts - while staying clear of the block a
|
|
108
|
+
* neighbouring runtime plans for its own services. When the neighbourhood is busy the caller
|
|
109
|
+
* gets an OS-assigned port instead of a stolen one; the runtime reports whichever it got.
|
|
110
|
+
*/
|
|
111
|
+
async function findGatewayIngressPort(gatewayPort) {
|
|
112
|
+
for (let offset = 3; offset < 10; offset += 1) {
|
|
113
|
+
const candidate = gatewayPort + offset;
|
|
114
|
+
if (await getFreePortForWildcard(candidate) === candidate) {
|
|
115
|
+
return candidate;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return await getEphemeralLoopbackPort();
|
|
119
|
+
}
|
|
106
120
|
async function getFreePort(basePort, host = '127.0.0.1', timeoutMs = PORT_PROBE_TIMEOUT_MS) {
|
|
107
121
|
for (let port = basePort; port <= HIGHEST_PORT; port++) {
|
|
108
122
|
if (await canListen(port, host, timeoutMs)) {
|
|
@@ -137,39 +151,6 @@ async function getEphemeralLoopbackPort() {
|
|
|
137
151
|
});
|
|
138
152
|
});
|
|
139
153
|
}
|
|
140
|
-
/**
|
|
141
|
-
* The ingress port this deployment should keep using.
|
|
142
|
-
*
|
|
143
|
-
* Remote tunnels forward to a port the operator typed into a provider console, so an
|
|
144
|
-
* OS-assigned port would invalidate that copy on every restart. The port is remembered in
|
|
145
|
-
* the runtime state directory; if something else took it meanwhile, a new one is chosen and
|
|
146
|
-
* persisted — the caller reports the change so the stale console value can be corrected.
|
|
147
|
-
*/
|
|
148
|
-
async function resolveStableLoopbackPort(stateFile) {
|
|
149
|
-
const remembered = readPortFile(stateFile);
|
|
150
|
-
if (remembered !== undefined && await canListen(remembered, '127.0.0.1')) {
|
|
151
|
-
return { port: remembered, changed: false };
|
|
152
|
-
}
|
|
153
|
-
const port = await getEphemeralLoopbackPort();
|
|
154
|
-
try {
|
|
155
|
-
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(stateFile), { recursive: true });
|
|
156
|
-
(0, node_fs_1.writeFileSync)(stateFile, `${port}\n`);
|
|
157
|
-
}
|
|
158
|
-
catch {
|
|
159
|
-
// A deployment without a writable state directory still gets a working port; it just
|
|
160
|
-
// cannot promise the same one next time.
|
|
161
|
-
}
|
|
162
|
-
return { port, changed: remembered !== undefined };
|
|
163
|
-
}
|
|
164
|
-
function readPortFile(stateFile) {
|
|
165
|
-
try {
|
|
166
|
-
const port = Number.parseInt((0, node_fs_1.readFileSync)(stateFile, 'utf8').trim(), 10);
|
|
167
|
-
return Number.isInteger(port) && port > 0 && port <= 65535 ? port : undefined;
|
|
168
|
-
}
|
|
169
|
-
catch {
|
|
170
|
-
return undefined;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
154
|
function hasIpv6Address() {
|
|
174
155
|
return Object.values(node_os_1.default.networkInterfaces()).some((entries) => entries?.some((entry) => entry.family === 'IPv6'));
|
|
175
156
|
}
|