@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
|
@@ -2,7 +2,7 @@ import { getLoggerFor } from 'global-logger-factory';
|
|
|
2
2
|
import type { ApiServer } from '../ApiServer';
|
|
3
3
|
import type { PodLookupRepository } from '../../identity/drizzle/PodLookupRepository';
|
|
4
4
|
import type { UsageRepository } from '../../storage/quota/UsageRepository';
|
|
5
|
-
import type {
|
|
5
|
+
import type { PodAccessFetchProvider } from '../ai-gateway/pod/OwnerPodAccess';
|
|
6
6
|
export interface PodSettingsStatus {
|
|
7
7
|
identity: {
|
|
8
8
|
webId: string;
|
|
@@ -57,10 +57,10 @@ export interface PodSettingsHandlerOptions {
|
|
|
57
57
|
}
|
|
58
58
|
export declare function registerPodSettingsRoutes(server: ApiServer, options: PodSettingsHandlerOptions): void;
|
|
59
59
|
export declare class DrizzlePodAiConnectionsStatusReader implements PodAiConnectionsStatusReader {
|
|
60
|
-
private readonly
|
|
60
|
+
private readonly podAccess?;
|
|
61
61
|
private readonly deployment;
|
|
62
62
|
private readonly dbFactory;
|
|
63
|
-
constructor(
|
|
63
|
+
constructor(podAccess?: PodAccessFetchProvider | undefined, deployment?: string, dbFactory?: (input: {
|
|
64
64
|
webId: string;
|
|
65
65
|
podUrl: string;
|
|
66
66
|
fetch: typeof fetch;
|
|
@@ -77,13 +77,13 @@ async function readStorageStatus(usageRepo, pod) {
|
|
|
77
77
|
};
|
|
78
78
|
}
|
|
79
79
|
class DrizzlePodAiConnectionsStatusReader {
|
|
80
|
-
constructor(
|
|
81
|
-
this.
|
|
80
|
+
constructor(podAccess, deployment = 'local', dbFactory = createAiConnectionsStatusDb) {
|
|
81
|
+
this.podAccess = podAccess;
|
|
82
82
|
this.deployment = deployment;
|
|
83
83
|
this.dbFactory = dbFactory;
|
|
84
84
|
}
|
|
85
85
|
async read({ webId, podUrl }) {
|
|
86
|
-
const trustedFetch = await this.
|
|
86
|
+
const trustedFetch = await this.podAccess?.getPodFetch(webId);
|
|
87
87
|
if (!trustedFetch) {
|
|
88
88
|
return { status: 'unsupported', reason: 'not_configured' };
|
|
89
89
|
}
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* 返回 SP 配置状态,供 Linx 查询
|
|
15
15
|
*/
|
|
16
16
|
import type { ApiServer } from '../ApiServer';
|
|
17
|
+
import type { AccessRoute } from '../../edge/reachability/types';
|
|
17
18
|
import type { EdgeNodeRepository } from '../../identity/drizzle/EdgeNodeRepository';
|
|
18
19
|
import type { ServiceTokenRepositoryPort } from '../../identity/drizzle/ServiceTokenRepository';
|
|
19
20
|
import type { DdnsRepository } from '../../identity/drizzle/DdnsRepository';
|
|
@@ -67,6 +68,25 @@ export interface ProvisionStatusOptions {
|
|
|
67
68
|
cloudBaseUrl?: string;
|
|
68
69
|
/** provisionCode(可选,由环境变量传入) */
|
|
69
70
|
provisionCode?: string;
|
|
71
|
+
/**
|
|
72
|
+
* Live state of the public route, read per request.
|
|
73
|
+
*
|
|
74
|
+
* A configured tunnel is not the same thing as a reachable public route, and a
|
|
75
|
+
* node whose tunnel is down still owns a canonical URL that same-machine
|
|
76
|
+
* clients reach over the loopback access route (`docs/multi-channel-access.md`).
|
|
77
|
+
* Clients need to tell those apart before they decide whether a canonical
|
|
78
|
+
* request can leave the machine, so this is reported rather than implied.
|
|
79
|
+
*/
|
|
80
|
+
readPublicRoute?: () => PublicRouteStatus | Promise<PublicRouteStatus>;
|
|
81
|
+
/**
|
|
82
|
+
* Every access point this node can currently prove it offers, read per request.
|
|
83
|
+
*
|
|
84
|
+
* A canonical URL is one identity reachable over several physical paths, and
|
|
85
|
+
* only the client knows which path it is standing on, so the runtime reports
|
|
86
|
+
* the paths themselves (canonical public route, tunnel, ...) instead of picking
|
|
87
|
+
* one for the client (`docs/multi-channel-access.md`).
|
|
88
|
+
*/
|
|
89
|
+
readAccessRoutes?: () => AccessRoute[] | Promise<AccessRoute[]>;
|
|
70
90
|
/** Persist refreshed local-only setup state to the single local setup file. */
|
|
71
91
|
persistState?: (state: ProvisionStatusStateUpdate) => Promise<void> | void;
|
|
72
92
|
/** 测试/调试注入 */
|
|
@@ -74,6 +94,15 @@ export interface ProvisionStatusOptions {
|
|
|
74
94
|
now?: () => number;
|
|
75
95
|
refreshGraceSeconds?: number;
|
|
76
96
|
}
|
|
97
|
+
/** What a client can learn about reaching this node's canonical URL from outside. */
|
|
98
|
+
export interface PublicRouteStatus {
|
|
99
|
+
/** A tunnel provider is configured for this node. */
|
|
100
|
+
configured: boolean;
|
|
101
|
+
/** The provider confirmed a proxy-ready public route. */
|
|
102
|
+
available: boolean;
|
|
103
|
+
provider?: string;
|
|
104
|
+
endpoint?: string;
|
|
105
|
+
}
|
|
77
106
|
export interface ProvisionStatusStateUpdate {
|
|
78
107
|
nodeId: string;
|
|
79
108
|
nodeToken: string;
|
|
@@ -442,6 +442,21 @@ function readManagedTunnelConfig(metadata) {
|
|
|
442
442
|
},
|
|
443
443
|
};
|
|
444
444
|
}
|
|
445
|
+
/**
|
|
446
|
+
* Only the facts a client needs to rank and use an access point are reported:
|
|
447
|
+
* where it points, how it is reached and whether it is currently known to work.
|
|
448
|
+
*/
|
|
449
|
+
function reportableAccessRoutes(routes) {
|
|
450
|
+
return (routes ?? []).map((route) => ({
|
|
451
|
+
id: route.id,
|
|
452
|
+
kind: route.kind,
|
|
453
|
+
targetUrl: route.targetUrl,
|
|
454
|
+
priority: route.priority,
|
|
455
|
+
requiresManagedClient: route.requiresManagedClient,
|
|
456
|
+
visibility: route.visibility,
|
|
457
|
+
health: route.health,
|
|
458
|
+
}));
|
|
459
|
+
}
|
|
445
460
|
function registerProvisionStatusRoute(server, options) {
|
|
446
461
|
const logger = (0, global_logger_factory_1.getLoggerFor)('ProvisionStatusHandler');
|
|
447
462
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
@@ -479,6 +494,37 @@ function registerProvisionStatusRoute(server, options) {
|
|
|
479
494
|
body.cloudUrl = options.cloudUrl;
|
|
480
495
|
body.oidcIssuer = normalizeUrl(options.cloudBaseUrl);
|
|
481
496
|
}
|
|
497
|
+
// Whether the canonical URL is reachable from outside is a separate fact
|
|
498
|
+
// from whether it exists. A failed read is reported as unavailable, never as
|
|
499
|
+
// available, because guessing here would send a same-machine client to a
|
|
500
|
+
// public route that does not answer.
|
|
501
|
+
if (options.readPublicRoute) {
|
|
502
|
+
let publicRoute;
|
|
503
|
+
try {
|
|
504
|
+
publicRoute = await options.readPublicRoute();
|
|
505
|
+
}
|
|
506
|
+
catch (error) {
|
|
507
|
+
logger.warn(`Failed to read public route state: ${error}`);
|
|
508
|
+
}
|
|
509
|
+
body.publicRoute = {
|
|
510
|
+
configured: Boolean(publicRoute?.configured),
|
|
511
|
+
available: Boolean(publicRoute?.available),
|
|
512
|
+
...(publicRoute?.provider ? { provider: publicRoute.provider } : {}),
|
|
513
|
+
...(publicRoute?.endpoint ? { endpoint: publicRoute.endpoint } : {}),
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
// Access points are reported as data: which one is best depends on where the
|
|
517
|
+
// client stands, which only the client knows.
|
|
518
|
+
if (options.readAccessRoutes) {
|
|
519
|
+
let routes;
|
|
520
|
+
try {
|
|
521
|
+
routes = await options.readAccessRoutes();
|
|
522
|
+
}
|
|
523
|
+
catch (error) {
|
|
524
|
+
logger.warn(`Failed to read access routes: ${error}`);
|
|
525
|
+
}
|
|
526
|
+
body.routes = reportableAccessRoutes(routes);
|
|
527
|
+
}
|
|
482
528
|
if (registered) {
|
|
483
529
|
const canRefresh = canRefreshProvisionStatus(options, state);
|
|
484
530
|
const currentNow = now();
|
|
@@ -214,7 +214,9 @@ function resolveAccess(request, nodeId) {
|
|
|
214
214
|
if (auth.nodeId !== nodeId) {
|
|
215
215
|
return { allowed: false, status: 403, error: 'Node token cannot access another node' };
|
|
216
216
|
}
|
|
217
|
-
|
|
217
|
+
// The node asking about itself stands on its own host, which is the one place its
|
|
218
|
+
// `local-only` loopback access point is usable (`docs/multi-channel-access.md`).
|
|
219
|
+
return { allowed: true, audience: 'local' };
|
|
218
220
|
}
|
|
219
221
|
if ((0, AuthContext_1.isServiceAuth)(auth)) {
|
|
220
222
|
return { allowed: true, audience: 'managed' };
|
package/dist/api/runtime.d.ts
CHANGED
|
@@ -26,5 +26,14 @@ export declare function autoProvisionFirstRunLocal(config: ApiContainerConfig, l
|
|
|
26
26
|
* would let forwarded remote requests inherit local trust. Falls back to the
|
|
27
27
|
* historical gateway port only when no ingress listener was provisioned.
|
|
28
28
|
*/
|
|
29
|
+
/**
|
|
30
|
+
* The port a remote tunnel forwards to: the Gateway's tunnel entry.
|
|
31
|
+
*
|
|
32
|
+
* The runtime records the listener it actually bound in `XPOD_GATEWAY_INGRESS_PORT`, and the
|
|
33
|
+
* Gateway's own port is only the fallback for an embedding that started no tunnel entry. The
|
|
34
|
+
* gate is structural - anything arriving on that listener is remote whatever it claims - so a
|
|
35
|
+
* tunnel pointed at the Gateway port by mistake still has the forwarding-header check behind
|
|
36
|
+
* it rather than inheriting local trust.
|
|
37
|
+
*/
|
|
29
38
|
export declare function resolveTunnelIngressPort(env?: NodeJS.ProcessEnv): number;
|
|
30
39
|
export declare function startApiService(options?: StartApiServiceOptions): Promise<ApiServiceHandle>;
|
package/dist/api/runtime.js
CHANGED
|
@@ -323,10 +323,23 @@ async function reconcileLocalOwnerRoles(container, config, logger) {
|
|
|
323
323
|
* would let forwarded remote requests inherit local trust. Falls back to the
|
|
324
324
|
* historical gateway port only when no ingress listener was provisioned.
|
|
325
325
|
*/
|
|
326
|
+
/**
|
|
327
|
+
* The port a remote tunnel forwards to: the Gateway's tunnel entry.
|
|
328
|
+
*
|
|
329
|
+
* The runtime records the listener it actually bound in `XPOD_GATEWAY_INGRESS_PORT`, and the
|
|
330
|
+
* Gateway's own port is only the fallback for an embedding that started no tunnel entry. The
|
|
331
|
+
* gate is structural - anything arriving on that listener is remote whatever it claims - so a
|
|
332
|
+
* tunnel pointed at the Gateway port by mistake still has the forwarding-header check behind
|
|
333
|
+
* it rather than inheriting local trust.
|
|
334
|
+
*/
|
|
326
335
|
function resolveTunnelIngressPort(env = process.env) {
|
|
327
|
-
const
|
|
328
|
-
|
|
329
|
-
|
|
336
|
+
for (const value of [env.XPOD_GATEWAY_INGRESS_PORT, env.XPOD_MAIN_PORT, env.XPOD_PORT, env.PORT]) {
|
|
337
|
+
const parsed = Number.parseInt(value ?? '', 10);
|
|
338
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
339
|
+
return parsed;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return 3000;
|
|
330
343
|
}
|
|
331
344
|
async function startBackgroundServices(container, logger) {
|
|
332
345
|
try {
|
|
@@ -17,12 +17,19 @@ export declare const startCommand: CommandModule<object, StartArgs>;
|
|
|
17
17
|
* port → an OS-assigned loopback port. The first two are strict, because a tunnel that
|
|
18
18
|
* forwards to a specific port cannot follow us somewhere else.
|
|
19
19
|
*/
|
|
20
|
-
|
|
20
|
+
/**
|
|
21
|
+
* The port a remote tunnel forwards to: the Gateway's tunnel entry.
|
|
22
|
+
*
|
|
23
|
+
* It is derived from the Gateway port by the same rule the runtime uses, so the CLI can hand
|
|
24
|
+
* it to the managed edge agent before the runtime binds it. A console that forwards somewhere
|
|
25
|
+
* else is a mismatch the tunnel provider reports from its own read-back - the runtime never
|
|
26
|
+
* moves its gate to match a console.
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveIngressPort(provisionedConfig: {
|
|
21
29
|
tunnelProfiles?: Array<{
|
|
22
30
|
id: string;
|
|
23
31
|
provider: string;
|
|
24
32
|
credentialEnvKey?: string;
|
|
25
|
-
credentialConfigured?: boolean;
|
|
26
33
|
}>;
|
|
27
34
|
tunnelActiveProfileId?: string;
|
|
28
35
|
}, mainPort: number): Promise<number>;
|
|
@@ -239,30 +239,28 @@ exports.startCommand = {
|
|
|
239
239
|
* port → an OS-assigned loopback port. The first two are strict, because a tunnel that
|
|
240
240
|
* forwards to a specific port cannot follow us somewhere else.
|
|
241
241
|
*/
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
242
|
+
/**
|
|
243
|
+
* The port a remote tunnel forwards to: the Gateway's tunnel entry.
|
|
244
|
+
*
|
|
245
|
+
* It is derived from the Gateway port by the same rule the runtime uses, so the CLI can hand
|
|
246
|
+
* it to the managed edge agent before the runtime binds it. A console that forwards somewhere
|
|
247
|
+
* else is a mismatch the tunnel provider reports from its own read-back - the runtime never
|
|
248
|
+
* moves its gate to match a console.
|
|
249
|
+
*/
|
|
250
|
+
async function resolveIngressPort(provisionedConfig, mainPort) {
|
|
251
|
+
const port = await (0, runtime_1.findGatewayIngressPort)(mainPort);
|
|
252
|
+
const active = provisionedConfig.tunnelProfiles?.find((profile) => profile.id === provisionedConfig.tunnelActiveProfileId) ?? provisionedConfig.tunnelProfiles?.find((profile) => profile.provider === 'sakura_frp');
|
|
249
253
|
if (active?.provider === 'sakura_frp') {
|
|
250
254
|
const credential = active.credentialEnvKey
|
|
251
255
|
? process.env[active.credentialEnvKey] ?? process.env.SAKURA_TUNNEL_TOKEN
|
|
252
256
|
: process.env.SAKURA_TUNNEL_TOKEN;
|
|
253
257
|
const assigned = await (0, SakuraFrpTunnelProvider_1.resolveSakuraAssignedLocalPort)(credential);
|
|
254
|
-
if (assigned && assigned !==
|
|
255
|
-
|
|
258
|
+
if (assigned !== undefined && assigned !== port) {
|
|
259
|
+
(0, global_logger_factory_1.getLoggerFor)('XpodStart').warn(`The Sakura console forwards to local port ${assigned}, but this runtime's tunnel entry is ${port}; `
|
|
260
|
+
+ `update the tunnel's local port in the console to ${port}`);
|
|
256
261
|
}
|
|
257
262
|
}
|
|
258
|
-
|
|
259
|
-
// restarts instead of being a fresh OS-assigned port every time.
|
|
260
|
-
const stateFile = path_1.default.join(process.cwd(), '.xpod', 'runtime', 'ingress-port');
|
|
261
|
-
const stable = await (0, runtime_1.resolveStableLoopbackPort)(stateFile);
|
|
262
|
-
if (stable.changed) {
|
|
263
|
-
(0, global_logger_factory_1.getLoggerFor)('XpodStart').warn(`The ingress port changed to ${stable.port}; update the tunnel console if it still forwards to the previous port`);
|
|
264
|
-
}
|
|
265
|
-
return stable.port;
|
|
263
|
+
return port;
|
|
266
264
|
}
|
|
267
265
|
function resolveCliOidcIssuer(env, provisionedIssuer, edition) {
|
|
268
266
|
return (0, oidc_issuer_1.resolveExternalOidcIssuer)(env)
|
|
@@ -31,7 +31,6 @@
|
|
|
31
31
|
"undefineds:dist/logging/ConfigurableLoggerFactory.jsonld",
|
|
32
32
|
"undefineds:dist/storage/SparqlUpdateResourceStore.jsonld",
|
|
33
33
|
"undefineds:dist/http/SubgraphSparqlHttpHandler.jsonld",
|
|
34
|
-
"undefineds:dist/http/InternalPodDataHttpHandler.jsonld",
|
|
35
34
|
"undefineds:dist/http/quota/QuotaAdminHttpHandler.jsonld",
|
|
36
35
|
"undefineds:dist/http/ClusterIngressRouter.jsonld",
|
|
37
36
|
"undefineds:dist/http/ClusterWebSocketConfigurator.jsonld",
|
|
@@ -134,6 +133,7 @@
|
|
|
134
133
|
"undefineds:dist/api/reconciler/WakeAgentQueue.jsonld",
|
|
135
134
|
"undefineds:dist/api/reconciler/ServerGroupReconcilerService.jsonld",
|
|
136
135
|
"undefineds:dist/edge/reachability/types.jsonld",
|
|
136
|
+
"undefineds:dist/edge/reachability/RouteSetBuilder.jsonld",
|
|
137
137
|
"undefineds:dist/edge/reachability/ReachabilitySessionService.jsonld",
|
|
138
138
|
"undefineds:dist/edge/reachability/ManagedRouteSelector.jsonld",
|
|
139
139
|
"undefineds:dist/edge/reachability/CanonicalFetch.jsonld",
|
|
@@ -1111,48 +1111,6 @@
|
|
|
1111
1111
|
}
|
|
1112
1112
|
}
|
|
1113
1113
|
},
|
|
1114
|
-
"InternalPodDataHttpHandler": {
|
|
1115
|
-
"@id": "undefineds:dist/http/InternalPodDataHttpHandler.jsonld#InternalPodDataHttpHandler",
|
|
1116
|
-
"@prefix": true,
|
|
1117
|
-
"@context": {
|
|
1118
|
-
"options_resourceStore": {
|
|
1119
|
-
"@id": "undefineds:dist/http/InternalPodDataHttpHandler.jsonld#InternalPodDataHttpHandler_options_resourceStore"
|
|
1120
|
-
},
|
|
1121
|
-
"options_gatewayAdminProxyAuthSecret": {
|
|
1122
|
-
"@id": "undefineds:dist/http/InternalPodDataHttpHandler.jsonld#InternalPodDataHttpHandler_options_gatewayAdminProxyAuthSecret"
|
|
1123
|
-
},
|
|
1124
|
-
"options_patchBodyParser": {
|
|
1125
|
-
"@id": "undefineds:dist/http/InternalPodDataHttpHandler.jsonld#InternalPodDataHttpHandler_options_patchBodyParser"
|
|
1126
|
-
},
|
|
1127
|
-
"options_sparqlHandler": {
|
|
1128
|
-
"@id": "undefineds:dist/http/InternalPodDataHttpHandler.jsonld#InternalPodDataHttpHandler_options_sparqlHandler"
|
|
1129
|
-
},
|
|
1130
|
-
"options_baseUrl": {
|
|
1131
|
-
"@id": "undefineds:dist/http/InternalPodDataHttpHandler.jsonld#InternalPodDataHttpHandler_options_baseUrl"
|
|
1132
|
-
},
|
|
1133
|
-
"options_basePath": {
|
|
1134
|
-
"@id": "undefineds:dist/http/InternalPodDataHttpHandler.jsonld#InternalPodDataHttpHandler_options_basePath"
|
|
1135
|
-
},
|
|
1136
|
-
"resourceStore": {
|
|
1137
|
-
"@id": "undefineds:dist/http/InternalPodDataHttpHandler.jsonld#InternalPodDataHttpHandler_options_resourceStore"
|
|
1138
|
-
},
|
|
1139
|
-
"gatewayAdminProxyAuthSecret": {
|
|
1140
|
-
"@id": "undefineds:dist/http/InternalPodDataHttpHandler.jsonld#InternalPodDataHttpHandler_options_gatewayAdminProxyAuthSecret"
|
|
1141
|
-
},
|
|
1142
|
-
"patchBodyParser": {
|
|
1143
|
-
"@id": "undefineds:dist/http/InternalPodDataHttpHandler.jsonld#InternalPodDataHttpHandler_options_patchBodyParser"
|
|
1144
|
-
},
|
|
1145
|
-
"sparqlHandler": {
|
|
1146
|
-
"@id": "undefineds:dist/http/InternalPodDataHttpHandler.jsonld#InternalPodDataHttpHandler_options_sparqlHandler"
|
|
1147
|
-
},
|
|
1148
|
-
"baseUrl": {
|
|
1149
|
-
"@id": "undefineds:dist/http/InternalPodDataHttpHandler.jsonld#InternalPodDataHttpHandler_options_baseUrl"
|
|
1150
|
-
},
|
|
1151
|
-
"basePath": {
|
|
1152
|
-
"@id": "undefineds:dist/http/InternalPodDataHttpHandler.jsonld#InternalPodDataHttpHandler_options_basePath"
|
|
1153
|
-
}
|
|
1154
|
-
}
|
|
1155
|
-
},
|
|
1156
1114
|
"QuotaAdminHttpHandler": {
|
|
1157
1115
|
"@id": "undefineds:dist/http/quota/QuotaAdminHttpHandler.jsonld#QuotaAdminHttpHandler",
|
|
1158
1116
|
"@prefix": true,
|
|
@@ -4024,6 +3982,11 @@
|
|
|
4024
3982
|
"@prefix": true,
|
|
4025
3983
|
"@context": {}
|
|
4026
3984
|
},
|
|
3985
|
+
"BuildPublicAccessRoutesOptions": {
|
|
3986
|
+
"@id": "undefineds:dist/edge/reachability/RouteSetBuilder.jsonld#BuildPublicAccessRoutesOptions",
|
|
3987
|
+
"@prefix": true,
|
|
3988
|
+
"@context": {}
|
|
3989
|
+
},
|
|
4027
3990
|
"ReachabilitySessionService": {
|
|
4028
3991
|
"@id": "undefineds:dist/edge/reachability/ReachabilitySessionService.jsonld#ReachabilitySessionService",
|
|
4029
3992
|
"@prefix": true,
|
|
@@ -91,6 +91,16 @@ export declare class EdgeNodeAgent {
|
|
|
91
91
|
private recordWithContent;
|
|
92
92
|
private collectSystemMetrics;
|
|
93
93
|
private buildHeartbeatMetadata;
|
|
94
|
+
/**
|
|
95
|
+
* Same-machine access point: the local runtime the agent heartbeats for.
|
|
96
|
+
*
|
|
97
|
+
* It is published as `local-only`, so it never reaches public route discovery -
|
|
98
|
+
* a loopback address is meaningless on any other machine - while managed
|
|
99
|
+
* clients still see it first (priority 10). The canonical URL stays canonical:
|
|
100
|
+
* loopback is only its access route.
|
|
101
|
+
*/
|
|
102
|
+
private buildLoopbackHeartbeatRoute;
|
|
103
|
+
private resolveLoopbackTargetUrl;
|
|
94
104
|
private buildLanHeartbeatRoutes;
|
|
95
105
|
private buildLanHeartbeatRoute;
|
|
96
106
|
private buildP2PHeartbeatRoute;
|
|
@@ -229,6 +229,13 @@ class EdgeNodeAgent {
|
|
|
229
229
|
}
|
|
230
230
|
buildHeartbeatMetadata(options) {
|
|
231
231
|
const metadata = { ...(options.metadata ?? {}) };
|
|
232
|
+
// Every access point this node has is reported, not just the public one:
|
|
233
|
+
// clients pick the best path for where they are (`docs/multi-channel-access.md`),
|
|
234
|
+
// and a same-machine client needs the loopback entry to do that.
|
|
235
|
+
const loopbackRoute = this.buildLoopbackHeartbeatRoute(options);
|
|
236
|
+
if (loopbackRoute) {
|
|
237
|
+
metadata.routes = this.mergeHeartbeatRoutes(metadata.routes, [loopbackRoute]);
|
|
238
|
+
}
|
|
232
239
|
const lanRoutes = this.buildLanHeartbeatRoutes(options);
|
|
233
240
|
if (lanRoutes.length > 0) {
|
|
234
241
|
metadata.routes = this.mergeHeartbeatRoutes(metadata.routes, lanRoutes);
|
|
@@ -239,6 +246,54 @@ class EdgeNodeAgent {
|
|
|
239
246
|
}
|
|
240
247
|
return metadata;
|
|
241
248
|
}
|
|
249
|
+
/**
|
|
250
|
+
* Same-machine access point: the local runtime the agent heartbeats for.
|
|
251
|
+
*
|
|
252
|
+
* It is published as `local-only`, so it never reaches public route discovery -
|
|
253
|
+
* a loopback address is meaningless on any other machine - while managed
|
|
254
|
+
* clients still see it first (priority 10). The canonical URL stays canonical:
|
|
255
|
+
* loopback is only its access route.
|
|
256
|
+
*/
|
|
257
|
+
buildLoopbackHeartbeatRoute(options) {
|
|
258
|
+
const target = options.p2p?.targetBaseUrl;
|
|
259
|
+
const targetUrl = this.resolveLoopbackTargetUrl(target);
|
|
260
|
+
if (!targetUrl) {
|
|
261
|
+
return undefined;
|
|
262
|
+
}
|
|
263
|
+
return {
|
|
264
|
+
id: 'loopback',
|
|
265
|
+
nodeId: options.nodeId,
|
|
266
|
+
...(options.baseUrl ? { canonicalUrl: options.baseUrl } : {}),
|
|
267
|
+
kind: 'loopback',
|
|
268
|
+
targetUrl,
|
|
269
|
+
priority: 10,
|
|
270
|
+
requiresManagedClient: true,
|
|
271
|
+
visibility: 'local-only',
|
|
272
|
+
health: 'healthy',
|
|
273
|
+
metadata: {
|
|
274
|
+
source: 'local-runtime',
|
|
275
|
+
},
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
resolveLoopbackTargetUrl(target) {
|
|
279
|
+
if (!target) {
|
|
280
|
+
return undefined;
|
|
281
|
+
}
|
|
282
|
+
try {
|
|
283
|
+
const url = target instanceof URL ? target : new URL(String(target));
|
|
284
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
285
|
+
return undefined;
|
|
286
|
+
}
|
|
287
|
+
const hostname = url.hostname.toLowerCase().replace(/^\[|\]$/gu, '');
|
|
288
|
+
if (hostname !== 'localhost' && hostname !== '127.0.0.1' && hostname !== '::1') {
|
|
289
|
+
return undefined;
|
|
290
|
+
}
|
|
291
|
+
return url.href.endsWith('/') ? url.href : `${url.href}/`;
|
|
292
|
+
}
|
|
293
|
+
catch {
|
|
294
|
+
return undefined;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
242
297
|
buildLanHeartbeatRoutes(options) {
|
|
243
298
|
const p2p = options.p2p;
|
|
244
299
|
if (!p2p || this.normalizeBoolean(p2p.enabled) === false) {
|
|
@@ -212,6 +212,14 @@
|
|
|
212
212
|
"@id": "undefineds:dist/edge/EdgeNodeAgent.jsonld#EdgeNodeAgent__member_buildHeartbeatMetadata",
|
|
213
213
|
"memberFieldName": "buildHeartbeatMetadata"
|
|
214
214
|
},
|
|
215
|
+
{
|
|
216
|
+
"@id": "undefineds:dist/edge/EdgeNodeAgent.jsonld#EdgeNodeAgent__member_buildLoopbackHeartbeatRoute",
|
|
217
|
+
"memberFieldName": "buildLoopbackHeartbeatRoute"
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
"@id": "undefineds:dist/edge/EdgeNodeAgent.jsonld#EdgeNodeAgent__member_resolveLoopbackTargetUrl",
|
|
221
|
+
"memberFieldName": "resolveLoopbackTargetUrl"
|
|
222
|
+
},
|
|
215
223
|
{
|
|
216
224
|
"@id": "undefineds:dist/edge/EdgeNodeAgent.jsonld#EdgeNodeAgent__member_buildLanHeartbeatRoutes",
|
|
217
225
|
"memberFieldName": "buildLanHeartbeatRoutes"
|
|
@@ -38,7 +38,10 @@ class ReachabilitySessionService {
|
|
|
38
38
|
throw new P2PCandidateSessionLimitExceededError('P2P candidate session limit exceeded');
|
|
39
39
|
}
|
|
40
40
|
const routeSet = (0, RouteSetBuilder_1.buildRouteSet)(source, {
|
|
41
|
-
|
|
41
|
+
// The node publishes its own access points, loopback included: a client on this host
|
|
42
|
+
// reaches the node there, and a client elsewhere ranks the path out by probing it
|
|
43
|
+
// (`docs/multi-channel-access.md`).
|
|
44
|
+
audience: 'local',
|
|
42
45
|
baseStorageDomain: this.options.baseStorageDomain,
|
|
43
46
|
now: createdAt,
|
|
44
47
|
});
|
|
@@ -1,2 +1,19 @@
|
|
|
1
|
-
import type { BuildRouteSetOptions, BuildRouteSetSource, RouteSet } from './types';
|
|
1
|
+
import type { AccessRoute, BuildRouteSetOptions, BuildRouteSetSource, RouteSet } from './types';
|
|
2
2
|
export declare function buildRouteSet(source: BuildRouteSetSource, options?: BuildRouteSetOptions): RouteSet;
|
|
3
|
+
export interface BuildPublicAccessRoutesOptions {
|
|
4
|
+
/** The node's canonical public URL, when it has one. */
|
|
5
|
+
canonicalUrl?: string;
|
|
6
|
+
/** A provider confirmed the canonical URL is proxy-ready right now. */
|
|
7
|
+
publicRouteAvailable?: boolean;
|
|
8
|
+
/** Endpoint of the user's own tunnel, when one is connected. */
|
|
9
|
+
tunnelEndpoint?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Access points a node can prove from its own configuration: the canonical public
|
|
13
|
+
* URL and the user's tunnel endpoint.
|
|
14
|
+
*
|
|
15
|
+
* Loopback and LAN are deliberately absent: whether either works depends on where
|
|
16
|
+
* the client stands, so the client derives them from its own position instead of
|
|
17
|
+
* being told about paths it may not have (`docs/multi-channel-access.md`).
|
|
18
|
+
*/
|
|
19
|
+
export declare function buildPublicAccessRoutes(options: BuildPublicAccessRoutesOptions): AccessRoute[];
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.buildRouteSet = buildRouteSet;
|
|
4
|
+
exports.buildPublicAccessRoutes = buildPublicAccessRoutes;
|
|
4
5
|
const DEFAULT_PRIORITY = {
|
|
5
6
|
loopback: 10,
|
|
6
7
|
lan: 20,
|
|
@@ -34,6 +35,44 @@ function buildRouteSet(source, options = {}) {
|
|
|
34
35
|
routes,
|
|
35
36
|
};
|
|
36
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Access points a node can prove from its own configuration: the canonical public
|
|
40
|
+
* URL and the user's tunnel endpoint.
|
|
41
|
+
*
|
|
42
|
+
* Loopback and LAN are deliberately absent: whether either works depends on where
|
|
43
|
+
* the client stands, so the client derives them from its own position instead of
|
|
44
|
+
* being told about paths it may not have (`docs/multi-channel-access.md`).
|
|
45
|
+
*/
|
|
46
|
+
function buildPublicAccessRoutes(options) {
|
|
47
|
+
const canonicalUrl = normalizeUrlString(options.canonicalUrl);
|
|
48
|
+
const tunnelEndpoint = normalizeUrlString(options.tunnelEndpoint);
|
|
49
|
+
const routes = [];
|
|
50
|
+
if (canonicalUrl) {
|
|
51
|
+
routes.push({
|
|
52
|
+
id: 'public-direct',
|
|
53
|
+
kind: 'public-direct',
|
|
54
|
+
canonicalUrl,
|
|
55
|
+
targetUrl: canonicalUrl,
|
|
56
|
+
priority: DEFAULT_PRIORITY['public-direct'],
|
|
57
|
+
requiresManagedClient: false,
|
|
58
|
+
visibility: 'public',
|
|
59
|
+
health: options.publicRouteAvailable ? 'healthy' : 'unknown',
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
if (tunnelEndpoint && tunnelEndpoint !== canonicalUrl) {
|
|
63
|
+
routes.push({
|
|
64
|
+
id: 'user-tunnel',
|
|
65
|
+
kind: 'user-tunnel',
|
|
66
|
+
canonicalUrl: canonicalUrl ?? tunnelEndpoint,
|
|
67
|
+
targetUrl: tunnelEndpoint,
|
|
68
|
+
priority: DEFAULT_PRIORITY['user-tunnel'],
|
|
69
|
+
requiresManagedClient: false,
|
|
70
|
+
visibility: 'public',
|
|
71
|
+
health: 'healthy',
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
return routes;
|
|
75
|
+
}
|
|
37
76
|
function routesFromMetadata(source, canonicalUrl, metadata) {
|
|
38
77
|
const rawRoutes = Array.isArray(metadata.routes) ? metadata.routes : [];
|
|
39
78
|
return rawRoutes
|
|
@@ -165,6 +204,14 @@ function defaultVisibility(kind) {
|
|
|
165
204
|
return PUBLIC_KINDS.has(kind) ? 'public' : 'authorized-client';
|
|
166
205
|
}
|
|
167
206
|
function isVisibleToAudience(route, audience) {
|
|
207
|
+
if (audience === 'local') {
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
// `local-only` means loopback: usable on the node's own host and nowhere else,
|
|
211
|
+
// so it is never offered to a client on another machine.
|
|
212
|
+
if (route.visibility === 'local-only') {
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
168
215
|
if (audience === 'managed') {
|
|
169
216
|
return true;
|
|
170
217
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"@context": [
|
|
3
|
+
"https://linkedsoftwaredependencies.org/bundles/npm/@undefineds.co/xpod/^0.0.0/components/context.jsonld"
|
|
4
|
+
],
|
|
5
|
+
"@id": "npmd:@undefineds.co/xpod",
|
|
6
|
+
"components": [
|
|
7
|
+
{
|
|
8
|
+
"@id": "undefineds:dist/edge/reachability/RouteSetBuilder.jsonld#BuildPublicAccessRoutesOptions",
|
|
9
|
+
"@type": "AbstractClass",
|
|
10
|
+
"requireElement": "BuildPublicAccessRoutesOptions",
|
|
11
|
+
"parameters": [],
|
|
12
|
+
"memberFields": [
|
|
13
|
+
{
|
|
14
|
+
"@id": "undefineds:dist/edge/reachability/RouteSetBuilder.jsonld#BuildPublicAccessRoutesOptions__member_canonicalUrl",
|
|
15
|
+
"memberFieldName": "canonicalUrl"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"@id": "undefineds:dist/edge/reachability/RouteSetBuilder.jsonld#BuildPublicAccessRoutesOptions__member_publicRouteAvailable",
|
|
19
|
+
"memberFieldName": "publicRouteAvailable"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"@id": "undefineds:dist/edge/reachability/RouteSetBuilder.jsonld#BuildPublicAccessRoutesOptions__member_tunnelEndpoint",
|
|
23
|
+
"memberFieldName": "tunnelEndpoint"
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
"constructorArguments": []
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
}
|
|
@@ -3,7 +3,8 @@ export type AccessRouteVisibility = 'local-only' | 'same-account' | 'authorized-
|
|
|
3
3
|
export type AccessRouteHealth = 'unknown' | 'healthy' | 'degraded' | 'unreachable';
|
|
4
4
|
export interface AccessRoute {
|
|
5
5
|
id: string;
|
|
6
|
-
|
|
6
|
+
/** Absent while the node has no identity yet, as on a standalone Local runtime. */
|
|
7
|
+
nodeId?: string;
|
|
7
8
|
canonicalUrl: string;
|
|
8
9
|
kind: AccessRouteKind;
|
|
9
10
|
targetUrl: string;
|
|
@@ -21,7 +22,12 @@ export interface RouteSet {
|
|
|
21
22
|
generatedAt: string;
|
|
22
23
|
routes: AccessRoute[];
|
|
23
24
|
}
|
|
24
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Who is asking for routes. `local` is the node's own host, which is the only
|
|
27
|
+
* place a loopback access point means anything; `managed` is an authorized
|
|
28
|
+
* client elsewhere, and `public` is unauthenticated discovery.
|
|
29
|
+
*/
|
|
30
|
+
export type RouteAudience = 'public' | 'managed' | 'local';
|
|
25
31
|
export interface BuildRouteSetSource {
|
|
26
32
|
nodeId: string;
|
|
27
33
|
canonicalUrl?: string;
|