@le-space/ui 0.3.26 → 0.3.28
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/package.json +3 -3
- package/react/index.js +174 -30
- package/shared/index.d.ts +1 -1
- package/shared/index.js +174 -30
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@le-space/ui",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.28",
|
|
4
4
|
"description": "Floating Aleph sponsor relay UI components for Svelte and React.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
"access": "public"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@le-space/browser": "0.3.
|
|
40
|
-
"@le-space/core": "0.3.
|
|
39
|
+
"@le-space/browser": "0.3.28",
|
|
40
|
+
"@le-space/core": "0.3.28",
|
|
41
41
|
"viem": "^2.38.5"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
package/react/index.js
CHANGED
|
@@ -28,7 +28,7 @@ var RECENT_INSTANCE_RUNTIME_GRACE_MS = 10 * 60 * 1e3;
|
|
|
28
28
|
var DEPLOYMENT_PENDING_WARNING_MS = 3 * 60 * 1e3;
|
|
29
29
|
|
|
30
30
|
// src/shared/package-version.ts
|
|
31
|
-
var UI_PACKAGE_VERSION = "0.3.
|
|
31
|
+
var UI_PACKAGE_VERSION = "0.3.28";
|
|
32
32
|
|
|
33
33
|
// src/shared/format.ts
|
|
34
34
|
function shortHash(value, head = 8, tail = 6) {
|
|
@@ -1237,16 +1237,6 @@ function normalizeBroadcastStatus(httpStatus, responseStatus) {
|
|
|
1237
1237
|
if (httpStatus === 202) return "pending";
|
|
1238
1238
|
return normalizeMessageStatus2(responseStatus);
|
|
1239
1239
|
}
|
|
1240
|
-
function isInvalidMessageFormatResponse2(response, payload) {
|
|
1241
|
-
if (response.status !== 422) return false;
|
|
1242
|
-
const details = payload?.details;
|
|
1243
|
-
if (typeof details === "string" && details.includes("InvalidMessageFormat")) return true;
|
|
1244
|
-
if (details && typeof details === "object") {
|
|
1245
|
-
const detailMessage = details.message;
|
|
1246
|
-
if (typeof detailMessage === "string" && detailMessage.includes("InvalidMessageFormat")) return true;
|
|
1247
|
-
}
|
|
1248
|
-
return false;
|
|
1249
|
-
}
|
|
1250
1240
|
function isRetryableBroadcastFailure(response, payload) {
|
|
1251
1241
|
if (response.status >= 500) return true;
|
|
1252
1242
|
const publicationStatus = payload?.publication_status?.status;
|
|
@@ -1268,25 +1258,22 @@ async function postBroadcastPayload2(body, options) {
|
|
|
1268
1258
|
};
|
|
1269
1259
|
}
|
|
1270
1260
|
async function broadcastAlephMessage2(message, options) {
|
|
1271
|
-
const
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
];
|
|
1276
|
-
for (let index = 0; index < attempts.length; index += 1) {
|
|
1277
|
-
const result = await postBroadcastPayload2(attempts[index], {
|
|
1261
|
+
const requestBody = { sync: options.sync ?? false, message };
|
|
1262
|
+
const maxAttempts = 3;
|
|
1263
|
+
for (let index = 0; index < maxAttempts; index += 1) {
|
|
1264
|
+
const result = await postBroadcastPayload2(requestBody, {
|
|
1278
1265
|
apiHost: options.apiHost,
|
|
1279
1266
|
fetch: options.fetch
|
|
1280
1267
|
});
|
|
1281
1268
|
if (result.httpStatus === 202 || normalizeBroadcastStatus(result.httpStatus, result.response?.message_status) !== "unknown" || result.httpStatus >= 200 && result.httpStatus < 300) {
|
|
1282
1269
|
return result;
|
|
1283
1270
|
}
|
|
1284
|
-
const canRetry = index <
|
|
1271
|
+
const canRetry = index < maxAttempts - 1 && isRetryableBroadcastFailure({ status: result.httpStatus }, result.response);
|
|
1285
1272
|
if (!canRetry) {
|
|
1286
1273
|
throw new Error(`Broadcast failed: ${result.httpStatus} ${JSON.stringify(result.response ?? {})}`);
|
|
1287
1274
|
}
|
|
1288
1275
|
}
|
|
1289
|
-
throw new Error("Broadcast failed:
|
|
1276
|
+
throw new Error("Broadcast failed: retry budget exhausted");
|
|
1290
1277
|
}
|
|
1291
1278
|
|
|
1292
1279
|
// ../core/src/forget.ts
|
|
@@ -1891,6 +1878,13 @@ async function waitForVmRuntime(args) {
|
|
|
1891
1878
|
}
|
|
1892
1879
|
|
|
1893
1880
|
// ../core/src/guest.ts
|
|
1881
|
+
function proxyHostnameFromUrl(value) {
|
|
1882
|
+
try {
|
|
1883
|
+
return value ? new URL(value).hostname : null;
|
|
1884
|
+
} catch {
|
|
1885
|
+
return null;
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1894
1888
|
async function defaultHttpProbe(fetch2, url, timeoutMs = 1e4) {
|
|
1895
1889
|
try {
|
|
1896
1890
|
const controller = new AbortController();
|
|
@@ -2132,6 +2126,59 @@ async function fetchUcGoPeerMetadata(args) {
|
|
|
2132
2126
|
`Relay metadata did not become ready after ${attempts} attempts: ${lastError ? lastError : typeof lastPayload === "string" ? lastPayload : JSON.stringify(lastPayload ?? {})}`
|
|
2133
2127
|
);
|
|
2134
2128
|
}
|
|
2129
|
+
async function verifyUcGoPeerReachability(args) {
|
|
2130
|
+
const checks = {};
|
|
2131
|
+
const mappedPorts2 = args.mappedPorts ?? {};
|
|
2132
|
+
const hostIpv4 = args.hostIpv4;
|
|
2133
|
+
const skippedInternalPorts = new Set(
|
|
2134
|
+
(args.skipInternalPorts ?? ["80"]).map((value) => String(value))
|
|
2135
|
+
);
|
|
2136
|
+
const httpProbe = args.httpProbe ?? ((url, timeoutMs) => defaultHttpProbe(args.fetch, url, timeoutMs));
|
|
2137
|
+
if (hostIpv4) {
|
|
2138
|
+
for (const [internalPort, mapping] of Object.entries(mappedPorts2)) {
|
|
2139
|
+
if (skippedInternalPorts.has(String(internalPort))) continue;
|
|
2140
|
+
if (mapping?.tcp === true && mapping?.host) {
|
|
2141
|
+
checks[`tcp:${internalPort}`] = {
|
|
2142
|
+
host: hostIpv4,
|
|
2143
|
+
port: mapping.host,
|
|
2144
|
+
...await args.tcpProbe(
|
|
2145
|
+
hostIpv4,
|
|
2146
|
+
mapping.host,
|
|
2147
|
+
Number(args.tcpTimeoutMs ?? 5e3)
|
|
2148
|
+
)
|
|
2149
|
+
};
|
|
2150
|
+
} else if (mapping?.udp === true && mapping?.host) {
|
|
2151
|
+
checks[`udp:${internalPort}`] = {
|
|
2152
|
+
host: hostIpv4,
|
|
2153
|
+
port: mapping.host,
|
|
2154
|
+
ok: null,
|
|
2155
|
+
note: "UDP mapping published; CI does not perform an application-level UDP handshake probe."
|
|
2156
|
+
};
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
if (args.proxyUrl && args.verifyProxyHttp !== false) {
|
|
2161
|
+
checks["https:proxy"] = await httpProbe(
|
|
2162
|
+
args.proxyUrl,
|
|
2163
|
+
Number(args.httpTimeoutMs ?? 1e4)
|
|
2164
|
+
);
|
|
2165
|
+
}
|
|
2166
|
+
const proxyHostname = proxyHostnameFromUrl(args.proxyUrl);
|
|
2167
|
+
if (proxyHostname) {
|
|
2168
|
+
checks["tcp:proxy-443"] = await args.tcpProbe(
|
|
2169
|
+
proxyHostname,
|
|
2170
|
+
443,
|
|
2171
|
+
Number(args.tcpTimeoutMs ?? 5e3)
|
|
2172
|
+
);
|
|
2173
|
+
}
|
|
2174
|
+
const failedChecks = Object.entries(checks).filter(
|
|
2175
|
+
([, value]) => value?.ok === false
|
|
2176
|
+
);
|
|
2177
|
+
return {
|
|
2178
|
+
ok: failedChecks.length === 0,
|
|
2179
|
+
checks
|
|
2180
|
+
};
|
|
2181
|
+
}
|
|
2135
2182
|
|
|
2136
2183
|
// ../aleph-bootstrap/src/index.ts
|
|
2137
2184
|
import { recoverMessageAddress } from "viem";
|
|
@@ -3232,6 +3279,8 @@ var UI_DEPLOY_WAIT_ATTEMPTS = 60;
|
|
|
3232
3279
|
var UI_DEPLOY_WAIT_DELAY_MS = 5e3;
|
|
3233
3280
|
var UI_RUNTIME_WAIT_ATTEMPTS = 40;
|
|
3234
3281
|
var UI_RUNTIME_WAIT_DELAY_MS = 5e3;
|
|
3282
|
+
var UI_REACHABILITY_VERIFY_ATTEMPTS = 25;
|
|
3283
|
+
var UI_REACHABILITY_VERIFY_DELAY_MS = 3e3;
|
|
3235
3284
|
function hasUsableRuntime(details) {
|
|
3236
3285
|
return Boolean(details.hostIpv4) || Boolean(details.vmIpv4) || details.mappedPorts.length > 0 || Boolean(details.webUrl) || Boolean(details.execution);
|
|
3237
3286
|
}
|
|
@@ -3338,15 +3387,16 @@ var SponsorRelayController = class {
|
|
|
3338
3387
|
this.progressEmitter.emit(nextEvent);
|
|
3339
3388
|
}
|
|
3340
3389
|
async configureRelayBootstrapRegistration(args) {
|
|
3390
|
+
let runtime = args.runtime;
|
|
3341
3391
|
const profile = relayProfileForManifest(this.state.manifest);
|
|
3342
3392
|
if (!profile) return;
|
|
3343
3393
|
if (!this.state.wallet.address) {
|
|
3344
3394
|
throw new Error("A connected wallet is required to register relay bootstrap addresses.");
|
|
3345
3395
|
}
|
|
3346
|
-
if (!
|
|
3396
|
+
if (!runtime.hostIpv4) {
|
|
3347
3397
|
throw new Error("Relay runtime did not expose a host IPv4 address.");
|
|
3348
3398
|
}
|
|
3349
|
-
|
|
3399
|
+
let mappedPorts2 = runtime.mappedPorts ?? {};
|
|
3350
3400
|
const setupPort = mappedPorts2["80"]?.host ?? null;
|
|
3351
3401
|
if (!setupPort) {
|
|
3352
3402
|
throw new Error("Relay runtime did not expose the temporary setup endpoint.");
|
|
@@ -3361,7 +3411,7 @@ var SponsorRelayController = class {
|
|
|
3361
3411
|
detail: "Waiting for the guest setup endpoint before collecting relay metadata."
|
|
3362
3412
|
});
|
|
3363
3413
|
const setupHealth = await waitForSetupEndpoint({
|
|
3364
|
-
hostIpv4:
|
|
3414
|
+
hostIpv4: runtime.hostIpv4,
|
|
3365
3415
|
setupPort,
|
|
3366
3416
|
fetch: (url, init) => fetch(url, init),
|
|
3367
3417
|
attempts: 15,
|
|
@@ -3387,12 +3437,12 @@ var SponsorRelayController = class {
|
|
|
3387
3437
|
throw new Error("OrbitDB relay runtime is missing required mapped TCP/WS ports.");
|
|
3388
3438
|
}
|
|
3389
3439
|
const configureResult = await configureOrbitdbRelaySetup2({
|
|
3390
|
-
hostIpv4:
|
|
3391
|
-
publicIpv6:
|
|
3440
|
+
hostIpv4: runtime.hostIpv4,
|
|
3441
|
+
publicIpv6: runtime.ipv6,
|
|
3392
3442
|
setupPort,
|
|
3393
3443
|
tcpPort,
|
|
3394
3444
|
wsPort,
|
|
3395
|
-
proxyUrl:
|
|
3445
|
+
proxyUrl: runtime.proxyUrl ?? void 0,
|
|
3396
3446
|
metricsPort: mappedPorts2["9090"]?.host ?? null,
|
|
3397
3447
|
metricsHttpsPort: mappedPorts2["443"]?.host ?? null,
|
|
3398
3448
|
webrtcPort: mappedPorts2["9093"]?.host ?? null,
|
|
@@ -3409,6 +3459,51 @@ var SponsorRelayController = class {
|
|
|
3409
3459
|
if (!args.deploymentToken) {
|
|
3410
3460
|
throw new Error("Missing deployment token for Aleph bootstrap config handoff.");
|
|
3411
3461
|
}
|
|
3462
|
+
if (runtime.proxyUrl && runtime.webAccess?.active !== true) {
|
|
3463
|
+
this.emitProgress({
|
|
3464
|
+
stage: "deployment-confirmed",
|
|
3465
|
+
label: "Waiting for HTTPS route",
|
|
3466
|
+
progress: 93,
|
|
3467
|
+
status: "warning",
|
|
3468
|
+
itemHash: args.itemHash,
|
|
3469
|
+
detail: "The relay received a 2n6 hostname, but it is not active yet. Waiting before handing HTTPS config to the guest."
|
|
3470
|
+
});
|
|
3471
|
+
let latestRuntime = runtime;
|
|
3472
|
+
for (let attempt = 0; attempt < UI_RUNTIME_WAIT_ATTEMPTS; attempt += 1) {
|
|
3473
|
+
if (latestRuntime.webAccess?.active === true) {
|
|
3474
|
+
break;
|
|
3475
|
+
}
|
|
3476
|
+
await new Promise(
|
|
3477
|
+
(resolve) => globalThis.setTimeout(resolve, UI_RUNTIME_WAIT_DELAY_MS)
|
|
3478
|
+
);
|
|
3479
|
+
latestRuntime = await fetchVmRuntime({
|
|
3480
|
+
itemHash: args.itemHash,
|
|
3481
|
+
fetch: (url, init) => fetch(url, init),
|
|
3482
|
+
crnHash: runtime.selectedCrn?.hash ?? runtime.allocation?.crnHash ?? void 0,
|
|
3483
|
+
crns: this.state.crns,
|
|
3484
|
+
crnListUrl: this.props.crnListUrl
|
|
3485
|
+
}).catch(() => latestRuntime);
|
|
3486
|
+
this.trace("deploy:bootstrap-proxy-activation", {
|
|
3487
|
+
itemHash: args.itemHash,
|
|
3488
|
+
attempt: attempt + 1,
|
|
3489
|
+
attempts: UI_RUNTIME_WAIT_ATTEMPTS,
|
|
3490
|
+
active: latestRuntime.webAccess?.active === true,
|
|
3491
|
+
proxyUrl: latestRuntime.proxyUrl ?? null
|
|
3492
|
+
});
|
|
3493
|
+
}
|
|
3494
|
+
runtime = latestRuntime;
|
|
3495
|
+
mappedPorts2 = runtime.mappedPorts ?? mappedPorts2;
|
|
3496
|
+
if (runtime.proxyUrl && runtime.webAccess?.active !== true) {
|
|
3497
|
+
throw new Error(
|
|
3498
|
+
"Aleph reserved a 2n6 proxy URL, but it never became active in time. Aborting guest HTTPS configuration to avoid a broken Caddy setup."
|
|
3499
|
+
);
|
|
3500
|
+
}
|
|
3501
|
+
}
|
|
3502
|
+
if (!runtime.hostIpv4) {
|
|
3503
|
+
throw new Error("Relay runtime lost its host IPv4 address while waiting for HTTPS activation.");
|
|
3504
|
+
}
|
|
3505
|
+
const runtimeHostIpv4 = runtime.hostIpv4;
|
|
3506
|
+
const activeProxyUrl = runtime.webAccess?.active === true ? runtime.proxyUrl ?? null : null;
|
|
3412
3507
|
this.emitProgress({
|
|
3413
3508
|
stage: "deployment-confirmed",
|
|
3414
3509
|
label: "Publishing relay config",
|
|
@@ -3426,9 +3521,9 @@ var SponsorRelayController = class {
|
|
|
3426
3521
|
expiresAt: new Date(Date.now() + 30 * 6e4).toISOString(),
|
|
3427
3522
|
status: "pending",
|
|
3428
3523
|
runtime: {
|
|
3429
|
-
publicIpv4:
|
|
3430
|
-
publicIpv6:
|
|
3431
|
-
proxyUrl:
|
|
3524
|
+
publicIpv4: runtimeHostIpv4,
|
|
3525
|
+
publicIpv6: runtime.ipv6 ?? null,
|
|
3526
|
+
proxyUrl: activeProxyUrl,
|
|
3432
3527
|
mappedPorts: mappedPorts2
|
|
3433
3528
|
},
|
|
3434
3529
|
bootstrap: {
|
|
@@ -3494,7 +3589,7 @@ var SponsorRelayController = class {
|
|
|
3494
3589
|
detail: "Waiting for the relay to report its final public multiaddrs."
|
|
3495
3590
|
});
|
|
3496
3591
|
const relayMetadata = await fetchRelayMetadataForRuntime({
|
|
3497
|
-
runtime
|
|
3592
|
+
runtime,
|
|
3498
3593
|
fetch: (url, init) => fetch(url, init),
|
|
3499
3594
|
preferSecureMetadata: profile === "uc-go-peer",
|
|
3500
3595
|
attempts: 80,
|
|
@@ -3527,6 +3622,55 @@ var SponsorRelayController = class {
|
|
|
3527
3622
|
if (!relayMetadata) {
|
|
3528
3623
|
throw new Error("Relay metadata did not include a peer ID and public multiaddrs.");
|
|
3529
3624
|
}
|
|
3625
|
+
if (profile === "uc-go-peer" && runtime.proxyUrl) {
|
|
3626
|
+
this.emitProgress({
|
|
3627
|
+
stage: "publishing-bootstrap",
|
|
3628
|
+
label: "Verifying reachability",
|
|
3629
|
+
progress: 96,
|
|
3630
|
+
status: "info",
|
|
3631
|
+
itemHash: args.itemHash,
|
|
3632
|
+
detail: "Checking that the secure relay endpoint is reachable before trusting the deployment as ready."
|
|
3633
|
+
});
|
|
3634
|
+
const verificationUrl = (() => {
|
|
3635
|
+
try {
|
|
3636
|
+
return new URL("/bootstrap/metadata", runtime.proxyUrl).toString();
|
|
3637
|
+
} catch {
|
|
3638
|
+
return runtime.proxyUrl ?? "";
|
|
3639
|
+
}
|
|
3640
|
+
})();
|
|
3641
|
+
let verificationResult = null;
|
|
3642
|
+
for (let attempt = 0; attempt < UI_REACHABILITY_VERIFY_ATTEMPTS; attempt += 1) {
|
|
3643
|
+
verificationResult = await verifyUcGoPeerReachability({
|
|
3644
|
+
proxyUrl: verificationUrl,
|
|
3645
|
+
verifyProxyHttp: true,
|
|
3646
|
+
fetch: (url, init) => fetch(url, init),
|
|
3647
|
+
tcpProbe: async () => ({ ok: null })
|
|
3648
|
+
});
|
|
3649
|
+
this.trace("deploy:reachability-verification-attempt", {
|
|
3650
|
+
itemHash: args.itemHash,
|
|
3651
|
+
attempt: attempt + 1,
|
|
3652
|
+
attempts: UI_REACHABILITY_VERIFY_ATTEMPTS,
|
|
3653
|
+
verificationUrl,
|
|
3654
|
+
ok: verificationResult.ok,
|
|
3655
|
+
checks: verificationResult.checks
|
|
3656
|
+
});
|
|
3657
|
+
const proxyHttpCheck2 = verificationResult.checks["https:proxy"];
|
|
3658
|
+
if (verificationResult.ok && proxyHttpCheck2?.ok === true) {
|
|
3659
|
+
break;
|
|
3660
|
+
}
|
|
3661
|
+
if (attempt < UI_REACHABILITY_VERIFY_ATTEMPTS - 1) {
|
|
3662
|
+
await new Promise(
|
|
3663
|
+
(resolve) => globalThis.setTimeout(resolve, UI_REACHABILITY_VERIFY_DELAY_MS)
|
|
3664
|
+
);
|
|
3665
|
+
}
|
|
3666
|
+
}
|
|
3667
|
+
const proxyHttpCheck = verificationResult?.checks["https:proxy"];
|
|
3668
|
+
if (!verificationResult?.ok || proxyHttpCheck?.ok !== true) {
|
|
3669
|
+
throw new Error(
|
|
3670
|
+
proxyHttpCheck?.error ? `Secure relay endpoint did not become reachable in time: ${proxyHttpCheck.error}` : "Secure relay endpoint did not become reachable in time."
|
|
3671
|
+
);
|
|
3672
|
+
}
|
|
3673
|
+
}
|
|
3530
3674
|
const { peerId } = relayMetadata;
|
|
3531
3675
|
const registrationId = createRelayBootstrapRegistrationId(
|
|
3532
3676
|
profile,
|
package/shared/index.d.ts
CHANGED
|
@@ -290,7 +290,7 @@ declare const STALE_INSTANCE_ALLOCATION_COOLDOWN_MS: number;
|
|
|
290
290
|
declare const RECENT_INSTANCE_RUNTIME_GRACE_MS: number;
|
|
291
291
|
declare const DEPLOYMENT_PENDING_WARNING_MS: number;
|
|
292
292
|
|
|
293
|
-
declare const UI_PACKAGE_VERSION = "0.3.
|
|
293
|
+
declare const UI_PACKAGE_VERSION = "0.3.28";
|
|
294
294
|
|
|
295
295
|
interface RootfsRequiredPortForward {
|
|
296
296
|
port: number;
|
package/shared/index.js
CHANGED
|
@@ -27,7 +27,7 @@ var RECENT_INSTANCE_RUNTIME_GRACE_MS = 10 * 60 * 1e3;
|
|
|
27
27
|
var DEPLOYMENT_PENDING_WARNING_MS = 3 * 60 * 1e3;
|
|
28
28
|
|
|
29
29
|
// src/shared/package-version.ts
|
|
30
|
-
var UI_PACKAGE_VERSION = "0.3.
|
|
30
|
+
var UI_PACKAGE_VERSION = "0.3.28";
|
|
31
31
|
|
|
32
32
|
// src/shared/format.ts
|
|
33
33
|
function shortHash(value, head = 8, tail = 6) {
|
|
@@ -1236,16 +1236,6 @@ function normalizeBroadcastStatus(httpStatus, responseStatus) {
|
|
|
1236
1236
|
if (httpStatus === 202) return "pending";
|
|
1237
1237
|
return normalizeMessageStatus2(responseStatus);
|
|
1238
1238
|
}
|
|
1239
|
-
function isInvalidMessageFormatResponse2(response, payload) {
|
|
1240
|
-
if (response.status !== 422) return false;
|
|
1241
|
-
const details = payload?.details;
|
|
1242
|
-
if (typeof details === "string" && details.includes("InvalidMessageFormat")) return true;
|
|
1243
|
-
if (details && typeof details === "object") {
|
|
1244
|
-
const detailMessage = details.message;
|
|
1245
|
-
if (typeof detailMessage === "string" && detailMessage.includes("InvalidMessageFormat")) return true;
|
|
1246
|
-
}
|
|
1247
|
-
return false;
|
|
1248
|
-
}
|
|
1249
1239
|
function isRetryableBroadcastFailure(response, payload) {
|
|
1250
1240
|
if (response.status >= 500) return true;
|
|
1251
1241
|
const publicationStatus = payload?.publication_status?.status;
|
|
@@ -1267,25 +1257,22 @@ async function postBroadcastPayload2(body, options) {
|
|
|
1267
1257
|
};
|
|
1268
1258
|
}
|
|
1269
1259
|
async function broadcastAlephMessage2(message, options) {
|
|
1270
|
-
const
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
];
|
|
1275
|
-
for (let index = 0; index < attempts.length; index += 1) {
|
|
1276
|
-
const result = await postBroadcastPayload2(attempts[index], {
|
|
1260
|
+
const requestBody = { sync: options.sync ?? false, message };
|
|
1261
|
+
const maxAttempts = 3;
|
|
1262
|
+
for (let index = 0; index < maxAttempts; index += 1) {
|
|
1263
|
+
const result = await postBroadcastPayload2(requestBody, {
|
|
1277
1264
|
apiHost: options.apiHost,
|
|
1278
1265
|
fetch: options.fetch
|
|
1279
1266
|
});
|
|
1280
1267
|
if (result.httpStatus === 202 || normalizeBroadcastStatus(result.httpStatus, result.response?.message_status) !== "unknown" || result.httpStatus >= 200 && result.httpStatus < 300) {
|
|
1281
1268
|
return result;
|
|
1282
1269
|
}
|
|
1283
|
-
const canRetry = index <
|
|
1270
|
+
const canRetry = index < maxAttempts - 1 && isRetryableBroadcastFailure({ status: result.httpStatus }, result.response);
|
|
1284
1271
|
if (!canRetry) {
|
|
1285
1272
|
throw new Error(`Broadcast failed: ${result.httpStatus} ${JSON.stringify(result.response ?? {})}`);
|
|
1286
1273
|
}
|
|
1287
1274
|
}
|
|
1288
|
-
throw new Error("Broadcast failed:
|
|
1275
|
+
throw new Error("Broadcast failed: retry budget exhausted");
|
|
1289
1276
|
}
|
|
1290
1277
|
|
|
1291
1278
|
// ../core/src/forget.ts
|
|
@@ -1890,6 +1877,13 @@ async function waitForVmRuntime(args) {
|
|
|
1890
1877
|
}
|
|
1891
1878
|
|
|
1892
1879
|
// ../core/src/guest.ts
|
|
1880
|
+
function proxyHostnameFromUrl(value) {
|
|
1881
|
+
try {
|
|
1882
|
+
return value ? new URL(value).hostname : null;
|
|
1883
|
+
} catch {
|
|
1884
|
+
return null;
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1893
1887
|
async function defaultHttpProbe(fetch2, url, timeoutMs = 1e4) {
|
|
1894
1888
|
try {
|
|
1895
1889
|
const controller = new AbortController();
|
|
@@ -2131,6 +2125,59 @@ async function fetchUcGoPeerMetadata(args) {
|
|
|
2131
2125
|
`Relay metadata did not become ready after ${attempts} attempts: ${lastError ? lastError : typeof lastPayload === "string" ? lastPayload : JSON.stringify(lastPayload ?? {})}`
|
|
2132
2126
|
);
|
|
2133
2127
|
}
|
|
2128
|
+
async function verifyUcGoPeerReachability(args) {
|
|
2129
|
+
const checks = {};
|
|
2130
|
+
const mappedPorts2 = args.mappedPorts ?? {};
|
|
2131
|
+
const hostIpv4 = args.hostIpv4;
|
|
2132
|
+
const skippedInternalPorts = new Set(
|
|
2133
|
+
(args.skipInternalPorts ?? ["80"]).map((value) => String(value))
|
|
2134
|
+
);
|
|
2135
|
+
const httpProbe = args.httpProbe ?? ((url, timeoutMs) => defaultHttpProbe(args.fetch, url, timeoutMs));
|
|
2136
|
+
if (hostIpv4) {
|
|
2137
|
+
for (const [internalPort, mapping] of Object.entries(mappedPorts2)) {
|
|
2138
|
+
if (skippedInternalPorts.has(String(internalPort))) continue;
|
|
2139
|
+
if (mapping?.tcp === true && mapping?.host) {
|
|
2140
|
+
checks[`tcp:${internalPort}`] = {
|
|
2141
|
+
host: hostIpv4,
|
|
2142
|
+
port: mapping.host,
|
|
2143
|
+
...await args.tcpProbe(
|
|
2144
|
+
hostIpv4,
|
|
2145
|
+
mapping.host,
|
|
2146
|
+
Number(args.tcpTimeoutMs ?? 5e3)
|
|
2147
|
+
)
|
|
2148
|
+
};
|
|
2149
|
+
} else if (mapping?.udp === true && mapping?.host) {
|
|
2150
|
+
checks[`udp:${internalPort}`] = {
|
|
2151
|
+
host: hostIpv4,
|
|
2152
|
+
port: mapping.host,
|
|
2153
|
+
ok: null,
|
|
2154
|
+
note: "UDP mapping published; CI does not perform an application-level UDP handshake probe."
|
|
2155
|
+
};
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2159
|
+
if (args.proxyUrl && args.verifyProxyHttp !== false) {
|
|
2160
|
+
checks["https:proxy"] = await httpProbe(
|
|
2161
|
+
args.proxyUrl,
|
|
2162
|
+
Number(args.httpTimeoutMs ?? 1e4)
|
|
2163
|
+
);
|
|
2164
|
+
}
|
|
2165
|
+
const proxyHostname = proxyHostnameFromUrl(args.proxyUrl);
|
|
2166
|
+
if (proxyHostname) {
|
|
2167
|
+
checks["tcp:proxy-443"] = await args.tcpProbe(
|
|
2168
|
+
proxyHostname,
|
|
2169
|
+
443,
|
|
2170
|
+
Number(args.tcpTimeoutMs ?? 5e3)
|
|
2171
|
+
);
|
|
2172
|
+
}
|
|
2173
|
+
const failedChecks = Object.entries(checks).filter(
|
|
2174
|
+
([, value]) => value?.ok === false
|
|
2175
|
+
);
|
|
2176
|
+
return {
|
|
2177
|
+
ok: failedChecks.length === 0,
|
|
2178
|
+
checks
|
|
2179
|
+
};
|
|
2180
|
+
}
|
|
2134
2181
|
|
|
2135
2182
|
// ../aleph-bootstrap/src/index.ts
|
|
2136
2183
|
import { recoverMessageAddress } from "viem";
|
|
@@ -3231,6 +3278,8 @@ var UI_DEPLOY_WAIT_ATTEMPTS = 60;
|
|
|
3231
3278
|
var UI_DEPLOY_WAIT_DELAY_MS = 5e3;
|
|
3232
3279
|
var UI_RUNTIME_WAIT_ATTEMPTS = 40;
|
|
3233
3280
|
var UI_RUNTIME_WAIT_DELAY_MS = 5e3;
|
|
3281
|
+
var UI_REACHABILITY_VERIFY_ATTEMPTS = 25;
|
|
3282
|
+
var UI_REACHABILITY_VERIFY_DELAY_MS = 3e3;
|
|
3234
3283
|
function hasUsableRuntime(details) {
|
|
3235
3284
|
return Boolean(details.hostIpv4) || Boolean(details.vmIpv4) || details.mappedPorts.length > 0 || Boolean(details.webUrl) || Boolean(details.execution);
|
|
3236
3285
|
}
|
|
@@ -3337,15 +3386,16 @@ var SponsorRelayController = class {
|
|
|
3337
3386
|
this.progressEmitter.emit(nextEvent);
|
|
3338
3387
|
}
|
|
3339
3388
|
async configureRelayBootstrapRegistration(args) {
|
|
3389
|
+
let runtime = args.runtime;
|
|
3340
3390
|
const profile = relayProfileForManifest(this.state.manifest);
|
|
3341
3391
|
if (!profile) return;
|
|
3342
3392
|
if (!this.state.wallet.address) {
|
|
3343
3393
|
throw new Error("A connected wallet is required to register relay bootstrap addresses.");
|
|
3344
3394
|
}
|
|
3345
|
-
if (!
|
|
3395
|
+
if (!runtime.hostIpv4) {
|
|
3346
3396
|
throw new Error("Relay runtime did not expose a host IPv4 address.");
|
|
3347
3397
|
}
|
|
3348
|
-
|
|
3398
|
+
let mappedPorts2 = runtime.mappedPorts ?? {};
|
|
3349
3399
|
const setupPort = mappedPorts2["80"]?.host ?? null;
|
|
3350
3400
|
if (!setupPort) {
|
|
3351
3401
|
throw new Error("Relay runtime did not expose the temporary setup endpoint.");
|
|
@@ -3360,7 +3410,7 @@ var SponsorRelayController = class {
|
|
|
3360
3410
|
detail: "Waiting for the guest setup endpoint before collecting relay metadata."
|
|
3361
3411
|
});
|
|
3362
3412
|
const setupHealth = await waitForSetupEndpoint({
|
|
3363
|
-
hostIpv4:
|
|
3413
|
+
hostIpv4: runtime.hostIpv4,
|
|
3364
3414
|
setupPort,
|
|
3365
3415
|
fetch: (url, init) => fetch(url, init),
|
|
3366
3416
|
attempts: 15,
|
|
@@ -3386,12 +3436,12 @@ var SponsorRelayController = class {
|
|
|
3386
3436
|
throw new Error("OrbitDB relay runtime is missing required mapped TCP/WS ports.");
|
|
3387
3437
|
}
|
|
3388
3438
|
const configureResult = await configureOrbitdbRelaySetup2({
|
|
3389
|
-
hostIpv4:
|
|
3390
|
-
publicIpv6:
|
|
3439
|
+
hostIpv4: runtime.hostIpv4,
|
|
3440
|
+
publicIpv6: runtime.ipv6,
|
|
3391
3441
|
setupPort,
|
|
3392
3442
|
tcpPort,
|
|
3393
3443
|
wsPort,
|
|
3394
|
-
proxyUrl:
|
|
3444
|
+
proxyUrl: runtime.proxyUrl ?? void 0,
|
|
3395
3445
|
metricsPort: mappedPorts2["9090"]?.host ?? null,
|
|
3396
3446
|
metricsHttpsPort: mappedPorts2["443"]?.host ?? null,
|
|
3397
3447
|
webrtcPort: mappedPorts2["9093"]?.host ?? null,
|
|
@@ -3408,6 +3458,51 @@ var SponsorRelayController = class {
|
|
|
3408
3458
|
if (!args.deploymentToken) {
|
|
3409
3459
|
throw new Error("Missing deployment token for Aleph bootstrap config handoff.");
|
|
3410
3460
|
}
|
|
3461
|
+
if (runtime.proxyUrl && runtime.webAccess?.active !== true) {
|
|
3462
|
+
this.emitProgress({
|
|
3463
|
+
stage: "deployment-confirmed",
|
|
3464
|
+
label: "Waiting for HTTPS route",
|
|
3465
|
+
progress: 93,
|
|
3466
|
+
status: "warning",
|
|
3467
|
+
itemHash: args.itemHash,
|
|
3468
|
+
detail: "The relay received a 2n6 hostname, but it is not active yet. Waiting before handing HTTPS config to the guest."
|
|
3469
|
+
});
|
|
3470
|
+
let latestRuntime = runtime;
|
|
3471
|
+
for (let attempt = 0; attempt < UI_RUNTIME_WAIT_ATTEMPTS; attempt += 1) {
|
|
3472
|
+
if (latestRuntime.webAccess?.active === true) {
|
|
3473
|
+
break;
|
|
3474
|
+
}
|
|
3475
|
+
await new Promise(
|
|
3476
|
+
(resolve) => globalThis.setTimeout(resolve, UI_RUNTIME_WAIT_DELAY_MS)
|
|
3477
|
+
);
|
|
3478
|
+
latestRuntime = await fetchVmRuntime({
|
|
3479
|
+
itemHash: args.itemHash,
|
|
3480
|
+
fetch: (url, init) => fetch(url, init),
|
|
3481
|
+
crnHash: runtime.selectedCrn?.hash ?? runtime.allocation?.crnHash ?? void 0,
|
|
3482
|
+
crns: this.state.crns,
|
|
3483
|
+
crnListUrl: this.props.crnListUrl
|
|
3484
|
+
}).catch(() => latestRuntime);
|
|
3485
|
+
this.trace("deploy:bootstrap-proxy-activation", {
|
|
3486
|
+
itemHash: args.itemHash,
|
|
3487
|
+
attempt: attempt + 1,
|
|
3488
|
+
attempts: UI_RUNTIME_WAIT_ATTEMPTS,
|
|
3489
|
+
active: latestRuntime.webAccess?.active === true,
|
|
3490
|
+
proxyUrl: latestRuntime.proxyUrl ?? null
|
|
3491
|
+
});
|
|
3492
|
+
}
|
|
3493
|
+
runtime = latestRuntime;
|
|
3494
|
+
mappedPorts2 = runtime.mappedPorts ?? mappedPorts2;
|
|
3495
|
+
if (runtime.proxyUrl && runtime.webAccess?.active !== true) {
|
|
3496
|
+
throw new Error(
|
|
3497
|
+
"Aleph reserved a 2n6 proxy URL, but it never became active in time. Aborting guest HTTPS configuration to avoid a broken Caddy setup."
|
|
3498
|
+
);
|
|
3499
|
+
}
|
|
3500
|
+
}
|
|
3501
|
+
if (!runtime.hostIpv4) {
|
|
3502
|
+
throw new Error("Relay runtime lost its host IPv4 address while waiting for HTTPS activation.");
|
|
3503
|
+
}
|
|
3504
|
+
const runtimeHostIpv4 = runtime.hostIpv4;
|
|
3505
|
+
const activeProxyUrl = runtime.webAccess?.active === true ? runtime.proxyUrl ?? null : null;
|
|
3411
3506
|
this.emitProgress({
|
|
3412
3507
|
stage: "deployment-confirmed",
|
|
3413
3508
|
label: "Publishing relay config",
|
|
@@ -3425,9 +3520,9 @@ var SponsorRelayController = class {
|
|
|
3425
3520
|
expiresAt: new Date(Date.now() + 30 * 6e4).toISOString(),
|
|
3426
3521
|
status: "pending",
|
|
3427
3522
|
runtime: {
|
|
3428
|
-
publicIpv4:
|
|
3429
|
-
publicIpv6:
|
|
3430
|
-
proxyUrl:
|
|
3523
|
+
publicIpv4: runtimeHostIpv4,
|
|
3524
|
+
publicIpv6: runtime.ipv6 ?? null,
|
|
3525
|
+
proxyUrl: activeProxyUrl,
|
|
3431
3526
|
mappedPorts: mappedPorts2
|
|
3432
3527
|
},
|
|
3433
3528
|
bootstrap: {
|
|
@@ -3493,7 +3588,7 @@ var SponsorRelayController = class {
|
|
|
3493
3588
|
detail: "Waiting for the relay to report its final public multiaddrs."
|
|
3494
3589
|
});
|
|
3495
3590
|
const relayMetadata = await fetchRelayMetadataForRuntime({
|
|
3496
|
-
runtime
|
|
3591
|
+
runtime,
|
|
3497
3592
|
fetch: (url, init) => fetch(url, init),
|
|
3498
3593
|
preferSecureMetadata: profile === "uc-go-peer",
|
|
3499
3594
|
attempts: 80,
|
|
@@ -3526,6 +3621,55 @@ var SponsorRelayController = class {
|
|
|
3526
3621
|
if (!relayMetadata) {
|
|
3527
3622
|
throw new Error("Relay metadata did not include a peer ID and public multiaddrs.");
|
|
3528
3623
|
}
|
|
3624
|
+
if (profile === "uc-go-peer" && runtime.proxyUrl) {
|
|
3625
|
+
this.emitProgress({
|
|
3626
|
+
stage: "publishing-bootstrap",
|
|
3627
|
+
label: "Verifying reachability",
|
|
3628
|
+
progress: 96,
|
|
3629
|
+
status: "info",
|
|
3630
|
+
itemHash: args.itemHash,
|
|
3631
|
+
detail: "Checking that the secure relay endpoint is reachable before trusting the deployment as ready."
|
|
3632
|
+
});
|
|
3633
|
+
const verificationUrl = (() => {
|
|
3634
|
+
try {
|
|
3635
|
+
return new URL("/bootstrap/metadata", runtime.proxyUrl).toString();
|
|
3636
|
+
} catch {
|
|
3637
|
+
return runtime.proxyUrl ?? "";
|
|
3638
|
+
}
|
|
3639
|
+
})();
|
|
3640
|
+
let verificationResult = null;
|
|
3641
|
+
for (let attempt = 0; attempt < UI_REACHABILITY_VERIFY_ATTEMPTS; attempt += 1) {
|
|
3642
|
+
verificationResult = await verifyUcGoPeerReachability({
|
|
3643
|
+
proxyUrl: verificationUrl,
|
|
3644
|
+
verifyProxyHttp: true,
|
|
3645
|
+
fetch: (url, init) => fetch(url, init),
|
|
3646
|
+
tcpProbe: async () => ({ ok: null })
|
|
3647
|
+
});
|
|
3648
|
+
this.trace("deploy:reachability-verification-attempt", {
|
|
3649
|
+
itemHash: args.itemHash,
|
|
3650
|
+
attempt: attempt + 1,
|
|
3651
|
+
attempts: UI_REACHABILITY_VERIFY_ATTEMPTS,
|
|
3652
|
+
verificationUrl,
|
|
3653
|
+
ok: verificationResult.ok,
|
|
3654
|
+
checks: verificationResult.checks
|
|
3655
|
+
});
|
|
3656
|
+
const proxyHttpCheck2 = verificationResult.checks["https:proxy"];
|
|
3657
|
+
if (verificationResult.ok && proxyHttpCheck2?.ok === true) {
|
|
3658
|
+
break;
|
|
3659
|
+
}
|
|
3660
|
+
if (attempt < UI_REACHABILITY_VERIFY_ATTEMPTS - 1) {
|
|
3661
|
+
await new Promise(
|
|
3662
|
+
(resolve) => globalThis.setTimeout(resolve, UI_REACHABILITY_VERIFY_DELAY_MS)
|
|
3663
|
+
);
|
|
3664
|
+
}
|
|
3665
|
+
}
|
|
3666
|
+
const proxyHttpCheck = verificationResult?.checks["https:proxy"];
|
|
3667
|
+
if (!verificationResult?.ok || proxyHttpCheck?.ok !== true) {
|
|
3668
|
+
throw new Error(
|
|
3669
|
+
proxyHttpCheck?.error ? `Secure relay endpoint did not become reachable in time: ${proxyHttpCheck.error}` : "Secure relay endpoint did not become reachable in time."
|
|
3670
|
+
);
|
|
3671
|
+
}
|
|
3672
|
+
}
|
|
3529
3673
|
const { peerId } = relayMetadata;
|
|
3530
3674
|
const registrationId = createRelayBootstrapRegistrationId(
|
|
3531
3675
|
profile,
|