@tinycloud/node-sdk 3.2.6-beta.6 → 3.3.0-beta.7
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/dist/{WasmKeyProvider-DEdSyQ0I.d.cts → TinyCloudNode-BchWB_zv.d.cts} +162 -283
- package/dist/{WasmKeyProvider-DEdSyQ0I.d.ts → TinyCloudNode-BchWB_zv.d.ts} +162 -283
- package/dist/WasmKeyProvider-5Ee7fcAw.d.cts +217 -0
- package/dist/WasmKeyProvider-5Ee7fcAw.d.ts +217 -0
- package/dist/authority-4HXYteKF.d.ts +118 -0
- package/dist/authority-lrA-698X.d.cts +118 -0
- package/dist/core.cjs +368 -147
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +37 -4
- package/dist/core.d.ts +37 -4
- package/dist/core.js +374 -155
- package/dist/core.js.map +1 -1
- package/dist/gated-network-BEF2XSTC.d.cts +16 -0
- package/dist/gated-network-BEF2XSTC.d.ts +16 -0
- package/dist/index.cjs +2702 -524
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +35 -89
- package/dist/index.d.ts +35 -89
- package/dist/index.js +2702 -530
- package/dist/index.js.map +1 -1
- package/dist/replication/authority.cjs +153 -15
- package/dist/replication/authority.cjs.map +1 -1
- package/dist/replication/authority.d.cts +4 -1
- package/dist/replication/authority.d.ts +4 -1
- package/dist/replication/authority.js +147 -14
- package/dist/replication/authority.js.map +1 -1
- package/dist/replication/gated-network.cjs +90 -0
- package/dist/replication/gated-network.cjs.map +1 -0
- package/dist/replication/gated-network.d.cts +2 -0
- package/dist/replication/gated-network.d.ts +2 -0
- package/dist/replication/gated-network.js +65 -0
- package/dist/replication/gated-network.js.map +1 -0
- package/dist/replication/runtime.cjs +84 -6
- package/dist/replication/runtime.cjs.map +1 -1
- package/dist/replication/runtime.d.cts +2 -1
- package/dist/replication/runtime.d.ts +2 -1
- package/dist/replication/runtime.js +84 -6
- package/dist/replication/runtime.js.map +1 -1
- package/dist/{runtime-B5P5SRpC.d.ts → runtime-D1o9Eybt.d.cts} +14 -3
- package/dist/{runtime-B5P5SRpC.d.cts → runtime-v62FzQHz.d.ts} +14 -3
- package/package.json +14 -4
- package/dist/authority-DDGtccBK.d.cts +0 -96
- package/dist/authority-DDGtccBK.d.ts +0 -96
package/dist/core.js
CHANGED
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
makePkhSpaceId as makePkhSpaceId2,
|
|
31
31
|
parsePkhDid,
|
|
32
32
|
pkhDid as pkhDid3,
|
|
33
|
-
principalDid
|
|
33
|
+
principalDid,
|
|
34
34
|
principalDidEquals as principalDidEquals4,
|
|
35
35
|
parseCanonicalNetworkId,
|
|
36
36
|
TinyCloudDebugLogger,
|
|
@@ -454,6 +454,7 @@ var NodeUserAuthorization = class {
|
|
|
454
454
|
this.siweConfig = config.siweConfig;
|
|
455
455
|
this.includeAccountRegistryPermissions = config.includeAccountRegistryPermissions ?? true;
|
|
456
456
|
this.replication = config.replication?.enabled === true ? config.replication : void 0;
|
|
457
|
+
this.establishReplicaStamp = config.establishReplicaStamp;
|
|
457
458
|
this._manifest = config.manifest;
|
|
458
459
|
this._capabilityRequest = config.capabilityRequest;
|
|
459
460
|
this.sessionManager = config.sessionManager ?? this.wasm.createSessionManager();
|
|
@@ -1223,6 +1224,27 @@ var NodeUserAuthorization = class {
|
|
|
1223
1224
|
signature
|
|
1224
1225
|
};
|
|
1225
1226
|
}
|
|
1227
|
+
persistedData(session, expiresAt, createdAt, replicaStamp) {
|
|
1228
|
+
return {
|
|
1229
|
+
address: session.address,
|
|
1230
|
+
chainId: session.chainId,
|
|
1231
|
+
sessionKey: JSON.stringify(session.jwk),
|
|
1232
|
+
siwe: session.siwe,
|
|
1233
|
+
signature: session.signature,
|
|
1234
|
+
tinycloudSession: {
|
|
1235
|
+
delegationHeader: session.delegationHeader,
|
|
1236
|
+
delegationCid: session.delegationCid,
|
|
1237
|
+
spaceId: session.spaceId,
|
|
1238
|
+
spaces: session.spaces,
|
|
1239
|
+
verificationMethod: session.verificationMethod
|
|
1240
|
+
},
|
|
1241
|
+
expiresAt,
|
|
1242
|
+
createdAt,
|
|
1243
|
+
version: "1.0",
|
|
1244
|
+
tinycloudHosts: this.tinycloudHosts,
|
|
1245
|
+
...replicaStamp ? { replicaStamp } : {}
|
|
1246
|
+
};
|
|
1247
|
+
}
|
|
1226
1248
|
/**
|
|
1227
1249
|
* Sign in and create a new session.
|
|
1228
1250
|
*
|
|
@@ -1302,24 +1324,13 @@ var NodeUserAuthorization = class {
|
|
|
1302
1324
|
siwe: prepared.siwe,
|
|
1303
1325
|
signature
|
|
1304
1326
|
};
|
|
1305
|
-
const
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
delegationHeader: session.delegationHeader,
|
|
1313
|
-
delegationCid: session.delegationCid,
|
|
1314
|
-
spaceId,
|
|
1315
|
-
spaces: spacesMetadata,
|
|
1316
|
-
verificationMethod: this.sessionManager.getDID(keyId)
|
|
1317
|
-
},
|
|
1318
|
-
expiresAt: expirationTime.toISOString(),
|
|
1319
|
-
createdAt: now.toISOString(),
|
|
1320
|
-
version: "1.0",
|
|
1321
|
-
tinycloudHosts: this.tinycloudHosts
|
|
1322
|
-
};
|
|
1327
|
+
const replicaStamp = await this.establishReplicaStamp?.(tinyCloudSession);
|
|
1328
|
+
const persistedData = this.persistedData(
|
|
1329
|
+
tinyCloudSession,
|
|
1330
|
+
expirationTime.toISOString(),
|
|
1331
|
+
now.toISOString(),
|
|
1332
|
+
replicaStamp
|
|
1333
|
+
);
|
|
1323
1334
|
await this.enqueuePersistence(async () => {
|
|
1324
1335
|
await this.sessionStorage.save(address, persistedData);
|
|
1325
1336
|
if (generation !== this.sessionGeneration) {
|
|
@@ -1473,28 +1484,12 @@ var NodeUserAuthorization = class {
|
|
|
1473
1484
|
siwe: prepared.siwe,
|
|
1474
1485
|
signature
|
|
1475
1486
|
};
|
|
1476
|
-
const expirationMatch = prepared.siwe
|
|
1477
|
-
const issuedAtMatch = prepared.siwe
|
|
1487
|
+
const expirationMatch = prepared.siwe?.match(/Expiration Time: (.+)/);
|
|
1488
|
+
const issuedAtMatch = prepared.siwe?.match(/Issued At: (.+)/);
|
|
1478
1489
|
const expiresAt = expirationMatch?.[1] ?? new Date(Date.now() + this.sessionExpirationMs).toISOString();
|
|
1479
1490
|
const createdAt = issuedAtMatch?.[1] ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1480
|
-
const
|
|
1481
|
-
|
|
1482
|
-
chainId,
|
|
1483
|
-
sessionKey: JSON.stringify(jwk),
|
|
1484
|
-
siwe: prepared.siwe,
|
|
1485
|
-
signature,
|
|
1486
|
-
tinycloudSession: {
|
|
1487
|
-
delegationHeader: session.delegationHeader,
|
|
1488
|
-
delegationCid: session.delegationCid,
|
|
1489
|
-
spaceId: prepared.spaceId,
|
|
1490
|
-
spaces: spacesMetadata,
|
|
1491
|
-
verificationMethod: this.sessionManager.getDID(keyId)
|
|
1492
|
-
},
|
|
1493
|
-
expiresAt,
|
|
1494
|
-
createdAt,
|
|
1495
|
-
version: "1.0",
|
|
1496
|
-
tinycloudHosts: this.tinycloudHosts
|
|
1497
|
-
};
|
|
1491
|
+
const replicaStamp = await this.establishReplicaStamp?.(tinyCloudSession);
|
|
1492
|
+
const persistedData = this.persistedData(tinyCloudSession, expiresAt, createdAt, replicaStamp);
|
|
1498
1493
|
await this.sessionStorage.save(address, persistedData);
|
|
1499
1494
|
this._session = clientSession;
|
|
1500
1495
|
this._tinyCloudSession = tinyCloudSession;
|
|
@@ -1557,15 +1552,36 @@ var NodeUserAuthorization = class {
|
|
|
1557
1552
|
const { prepared, keyId, jwk } = await this.prepareSessionForSigning();
|
|
1558
1553
|
const presentation = this.buildPresentationEnvelope(options?.reason);
|
|
1559
1554
|
const hostHint = this.primaryTinyCloudHost;
|
|
1560
|
-
const
|
|
1555
|
+
const originalRequest = {
|
|
1561
1556
|
protocolVersion: 1,
|
|
1562
1557
|
siwe: prepared.siwe,
|
|
1563
1558
|
jwk,
|
|
1559
|
+
verificationMethod: prepared.verificationMethod,
|
|
1564
1560
|
host: hostHint,
|
|
1565
1561
|
keyId: options?.openkeyKeyId,
|
|
1566
1562
|
presentation
|
|
1567
|
-
}
|
|
1568
|
-
|
|
1563
|
+
};
|
|
1564
|
+
let result;
|
|
1565
|
+
let signed;
|
|
1566
|
+
let signature;
|
|
1567
|
+
for (let attempt = 0; ; ) {
|
|
1568
|
+
try {
|
|
1569
|
+
result ?? (result = await authorizeFn(originalRequest));
|
|
1570
|
+
signed = await this.verifyOpenKeyResult(result, prepared);
|
|
1571
|
+
signature = result.signature;
|
|
1572
|
+
break;
|
|
1573
|
+
} catch (error) {
|
|
1574
|
+
if (!options?.retryResult) throw error;
|
|
1575
|
+
const next = await options.retryResult(
|
|
1576
|
+
error instanceof Error ? error : new Error(String(error)),
|
|
1577
|
+
originalRequest,
|
|
1578
|
+
++attempt
|
|
1579
|
+
);
|
|
1580
|
+
if (next === null) throw error;
|
|
1581
|
+
result = next;
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
return this.signInWithPreparedSession(signed, signature, keyId, jwk);
|
|
1569
1585
|
}
|
|
1570
1586
|
/**
|
|
1571
1587
|
* Build a `CapabilityPresentationEnvelopeV1` from `this._manifest`. The
|
|
@@ -1645,6 +1661,11 @@ var NodeUserAuthorization = class {
|
|
|
1645
1661
|
* (resource, action) pair in the returned SIWE.
|
|
1646
1662
|
*/
|
|
1647
1663
|
async signInWithOpenKeyResult(result, prepared, keyId, jwk) {
|
|
1664
|
+
const signed = await this.verifyOpenKeyResult(result, prepared);
|
|
1665
|
+
return this.signInWithPreparedSession(signed, result.signature, keyId, jwk);
|
|
1666
|
+
}
|
|
1667
|
+
/** Validate a candidate without activating a network session, so paste retries reuse one preparation. */
|
|
1668
|
+
async verifyOpenKeyResult(result, prepared) {
|
|
1648
1669
|
if (result.protocolVersion !== 1) {
|
|
1649
1670
|
throw new Error(`Unsupported OpenKey protocol version ${result.protocolVersion}`);
|
|
1650
1671
|
}
|
|
@@ -1889,17 +1910,12 @@ var NodeUserAuthorization = class {
|
|
|
1889
1910
|
}
|
|
1890
1911
|
}
|
|
1891
1912
|
void nonRequiredGrantedPairs;
|
|
1892
|
-
return
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
},
|
|
1899
|
-
result.signature,
|
|
1900
|
-
keyId,
|
|
1901
|
-
jwk
|
|
1902
|
-
);
|
|
1913
|
+
return {
|
|
1914
|
+
siwe: result.signedMessage,
|
|
1915
|
+
jwk: prepared.jwk,
|
|
1916
|
+
spaceId: prepared.spaceId,
|
|
1917
|
+
verificationMethod: prepared.verificationMethod
|
|
1918
|
+
};
|
|
1903
1919
|
}
|
|
1904
1920
|
/**
|
|
1905
1921
|
* Clear persisted session data.
|
|
@@ -2462,45 +2478,10 @@ function deserializeDelegation(data) {
|
|
|
2462
2478
|
|
|
2463
2479
|
// src/TinyCloudNode.ts
|
|
2464
2480
|
import { generateNonce, SiweMessage as SiweMessage3 } from "siwe";
|
|
2465
|
-
|
|
2466
|
-
// src/replication/config.ts
|
|
2467
|
-
import { assertFolderPrefix as assertPureFolderPrefix, kvPrefixCovers } from "@tinycloud/sdk-services/kv/replication/prefix";
|
|
2468
|
-
function assertValidReplicationConfig(replication) {
|
|
2469
|
-
if (replication === void 0 || replication.enabled !== true) return;
|
|
2470
|
-
if (replication.storage === void 0 || replication.storage === null) {
|
|
2471
|
-
throw new TypeError("replication.enabled requires replication.storage");
|
|
2472
|
-
}
|
|
2473
|
-
if (!Array.isArray(replication.prefixes) || replication.prefixes.length === 0) {
|
|
2474
|
-
throw new TypeError("replication.prefixes must be a non-empty array");
|
|
2475
|
-
}
|
|
2476
|
-
for (const prefix of replication.prefixes) {
|
|
2477
|
-
try {
|
|
2478
|
-
assertPureFolderPrefix(prefix);
|
|
2479
|
-
} catch (error) {
|
|
2480
|
-
throw new TypeError(error instanceof Error ? error.message : String(error));
|
|
2481
|
-
}
|
|
2482
|
-
}
|
|
2483
|
-
for (const [index, prefix] of replication.prefixes.entries()) {
|
|
2484
|
-
for (const other of replication.prefixes.slice(index + 1)) {
|
|
2485
|
-
if (kvPrefixCovers(prefix, other) || kvPrefixCovers(other, prefix)) {
|
|
2486
|
-
throw new TypeError(
|
|
2487
|
-
`replication.prefixes must not overlap: ${JSON.stringify(prefix)} and ${JSON.stringify(other)}`
|
|
2488
|
-
);
|
|
2489
|
-
}
|
|
2490
|
-
}
|
|
2491
|
-
if ((prefix === "/" || prefix.split("/", 1)[0] === "vault") && replication.allowSecrets !== true) {
|
|
2492
|
-
throw new TypeError(
|
|
2493
|
-
`replication prefix ${JSON.stringify(prefix)} overlaps the vault namespace; pass allowSecrets to opt in`
|
|
2494
|
-
);
|
|
2495
|
-
}
|
|
2496
|
-
}
|
|
2497
|
-
}
|
|
2498
|
-
|
|
2499
|
-
// src/TinyCloudNode.ts
|
|
2500
2481
|
import {
|
|
2501
2482
|
TinyCloud,
|
|
2502
2483
|
checkNodeInfo as checkNodeInfo2,
|
|
2503
|
-
|
|
2484
|
+
ProtocolMismatchError,
|
|
2504
2485
|
activateSessionWithHost as activateSessionWithHost2,
|
|
2505
2486
|
authorizationVerdictOf,
|
|
2506
2487
|
httpResponseError,
|
|
@@ -2551,7 +2532,6 @@ import {
|
|
|
2551
2532
|
verifyDidKeyEd25519Signature,
|
|
2552
2533
|
canonicalizeAddress as canonicalizeAddress2,
|
|
2553
2534
|
pkhDid as pkhDid2,
|
|
2554
|
-
principalDid,
|
|
2555
2535
|
resolveTinyCloudHosts as resolveTinyCloudHosts2,
|
|
2556
2536
|
publishLocationRecord,
|
|
2557
2537
|
principalDidEquals as principalDidEquals3,
|
|
@@ -3499,6 +3479,20 @@ function accountIndexWarningDiagnostic(error) {
|
|
|
3499
3479
|
}
|
|
3500
3480
|
return diagnostic;
|
|
3501
3481
|
}
|
|
3482
|
+
function replicaRestoreRefusal(error) {
|
|
3483
|
+
if (error !== null && typeof error === "object" && "code" in error && error.code === "REPLICA_GATED" && "gate" in error) {
|
|
3484
|
+
switch (error.gate) {
|
|
3485
|
+
case "not_established":
|
|
3486
|
+
case "stamp_stale":
|
|
3487
|
+
case "principal_mismatch":
|
|
3488
|
+
case "purge_pending":
|
|
3489
|
+
case "logout_pending":
|
|
3490
|
+
case "gate_unavailable":
|
|
3491
|
+
return error.gate;
|
|
3492
|
+
}
|
|
3493
|
+
}
|
|
3494
|
+
return "gate_unavailable";
|
|
3495
|
+
}
|
|
3502
3496
|
var _TinyCloudNode = class _TinyCloudNode {
|
|
3503
3497
|
/**
|
|
3504
3498
|
* Create a new TinyCloudNode instance.
|
|
@@ -3528,6 +3522,37 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3528
3522
|
this.auth = null;
|
|
3529
3523
|
this.tc = null;
|
|
3530
3524
|
this._chainId = 1;
|
|
3525
|
+
/** Mints the replica session stamp on each fresh sign-in (TC-858 §4.1). Undefined when replication is off. */
|
|
3526
|
+
this.establishReplicaStamp = async (session) => {
|
|
3527
|
+
const replication = this.config.replication;
|
|
3528
|
+
if (!replication?.enabled) return void 0;
|
|
3529
|
+
const epoch = this.sessionEpoch;
|
|
3530
|
+
const attempt = this.latestSessionAttempt;
|
|
3531
|
+
try {
|
|
3532
|
+
const { verifiedOwnerConsentAnchor, establishReplicaSessionStamp } = await getNodeReplicationLoaders().authority();
|
|
3533
|
+
if (epoch !== this.sessionEpoch || attempt !== this.latestSessionAttempt) return void 0;
|
|
3534
|
+
const anchor = verifiedOwnerConsentAnchor(session, this.wasmBindings, exactVerifiedRecap);
|
|
3535
|
+
if (anchor === null) {
|
|
3536
|
+
throw Object.assign(new Error("Replica sign-in requires a verified finite owner SIWE and exact-space KV get+sync."), {
|
|
3537
|
+
code: "REPLICA_GATED",
|
|
3538
|
+
gate: "not_established"
|
|
3539
|
+
});
|
|
3540
|
+
}
|
|
3541
|
+
const stamp = await establishReplicaSessionStamp(
|
|
3542
|
+
replication.storage,
|
|
3543
|
+
pkhDid2(session.address, session.chainId),
|
|
3544
|
+
anchor
|
|
3545
|
+
);
|
|
3546
|
+
if (epoch !== this.sessionEpoch || attempt !== this.latestSessionAttempt) return void 0;
|
|
3547
|
+
this.replicaSessionStamp = stamp;
|
|
3548
|
+
return stamp;
|
|
3549
|
+
} catch (error) {
|
|
3550
|
+
if (epoch === this.sessionEpoch && attempt === this.latestSessionAttempt) {
|
|
3551
|
+
this.replicaRestoreGate = replicaRestoreRefusal(error);
|
|
3552
|
+
}
|
|
3553
|
+
return void 0;
|
|
3554
|
+
}
|
|
3555
|
+
};
|
|
3531
3556
|
this._baseVaults = /* @__PURE__ */ new Map();
|
|
3532
3557
|
this._baseSecrets = /* @__PURE__ */ new Map();
|
|
3533
3558
|
this._secrets = /* @__PURE__ */ new Map();
|
|
@@ -3652,7 +3677,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3652
3677
|
});
|
|
3653
3678
|
return this.wasmBindings.invokeAny(invocationSession, caveatPreservingEntries, facts);
|
|
3654
3679
|
};
|
|
3655
|
-
|
|
3680
|
+
if (config.replication?.enabled) getNodeReplicationLoaders().validateConfig(config.replication);
|
|
3656
3681
|
this.explicitHost = config.host;
|
|
3657
3682
|
this.config = {
|
|
3658
3683
|
...config,
|
|
@@ -3667,6 +3692,16 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3667
3692
|
this.replicationRuntime = runtime;
|
|
3668
3693
|
this.replicationControl = {
|
|
3669
3694
|
status: async () => {
|
|
3695
|
+
const gate = this.replicaRestoreGate;
|
|
3696
|
+
if (gate) return this.config.replication.prefixes.map((prefix) => ({
|
|
3697
|
+
prefix,
|
|
3698
|
+
state: "gated",
|
|
3699
|
+
gate,
|
|
3700
|
+
errorCode: "REPLICA_GATED",
|
|
3701
|
+
pending: { inFlight: 0, committed: 0, ambiguous: 0 },
|
|
3702
|
+
pinned: [],
|
|
3703
|
+
lagMs: null
|
|
3704
|
+
}));
|
|
3670
3705
|
const status = await runtime.then((ready) => ready.control.status());
|
|
3671
3706
|
const scopeError = this.replicationScopeError;
|
|
3672
3707
|
if (!scopeError) return status;
|
|
@@ -3683,7 +3718,15 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3683
3718
|
lagMs: null
|
|
3684
3719
|
}));
|
|
3685
3720
|
},
|
|
3686
|
-
sync: async (input) =>
|
|
3721
|
+
sync: async (input) => {
|
|
3722
|
+
if (this.replicaRestoreGate) {
|
|
3723
|
+
throw Object.assign(new Error("Replica restore is gated."), {
|
|
3724
|
+
code: "REPLICA_GATED",
|
|
3725
|
+
gate: this.replicaRestoreGate
|
|
3726
|
+
});
|
|
3727
|
+
}
|
|
3728
|
+
return (await runtime).control.sync(input);
|
|
3729
|
+
},
|
|
3687
3730
|
purge: (input) => {
|
|
3688
3731
|
const instance = this.replicationRuntimeInstance;
|
|
3689
3732
|
return instance ? instance.control.purge(input) : runtime.then((ready) => ready.control.purge(input));
|
|
@@ -3899,6 +3942,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3899
3942
|
signer: this.signer,
|
|
3900
3943
|
signStrategy: config.signStrategy ?? { type: "auto-sign" },
|
|
3901
3944
|
wasmBindings: this.wasmBindings,
|
|
3945
|
+
...config.replication?.enabled === true ? { establishReplicaStamp: this.establishReplicaStamp } : {},
|
|
3902
3946
|
sessionManager: this.sessionManager,
|
|
3903
3947
|
sessionStorage: config.sessionStorage ?? new MemorySessionStorage(),
|
|
3904
3948
|
domain: this.siweDomain,
|
|
@@ -4096,11 +4140,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4096
4140
|
return this.signer === null;
|
|
4097
4141
|
}
|
|
4098
4142
|
/**
|
|
4099
|
-
*
|
|
4100
|
-
* Available after signIn().
|
|
4143
|
+
* Primary space of the signed-in or restored network session, including compact sessions.
|
|
4101
4144
|
*/
|
|
4102
4145
|
get spaceId() {
|
|
4103
|
-
return this.auth?.tinyCloudSession?.spaceId;
|
|
4146
|
+
return this.auth?.tinyCloudSession?.spaceId ?? this._activeServiceSession?.spaceId;
|
|
4104
4147
|
}
|
|
4105
4148
|
/**
|
|
4106
4149
|
* Get the account space ID for this wallet identity.
|
|
@@ -4148,6 +4191,12 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4148
4191
|
get restorableSession() {
|
|
4149
4192
|
return this.currentTinyCloudSession();
|
|
4150
4193
|
}
|
|
4194
|
+
/** The active, verified replica stamp. Undefined when the replica is refused or the session has ended. */
|
|
4195
|
+
get replicaStamp() {
|
|
4196
|
+
const stamp = this.replicaSessionStamp;
|
|
4197
|
+
if (!stamp || this.replicaRestoreGate !== void 0 || !this.currentTinyCloudSession()) return void 0;
|
|
4198
|
+
return { incarnation: stamp.incarnation, lifecycleEpoch: stamp.lifecycleEpoch };
|
|
4199
|
+
}
|
|
4151
4200
|
/**
|
|
4152
4201
|
* Sign out and end the session on this instance.
|
|
4153
4202
|
*
|
|
@@ -4168,18 +4217,60 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4168
4217
|
}
|
|
4169
4218
|
async endSession() {
|
|
4170
4219
|
const leavingAddress = this.address ?? this._restoredTcSession?.address;
|
|
4220
|
+
const leavingChainId = this._chainId;
|
|
4221
|
+
const leavingPrincipal = this.replicaSessionPrincipal ?? (leavingAddress ? pkhDid2(leavingAddress, leavingChainId) : void 0);
|
|
4171
4222
|
const leavingAuth = this.auth;
|
|
4223
|
+
const leavingContext = this._serviceContext;
|
|
4224
|
+
let denied;
|
|
4172
4225
|
try {
|
|
4173
4226
|
this.teardownSession();
|
|
4174
4227
|
} finally {
|
|
4228
|
+
const denial = leavingPrincipal ? this.denyReplicaGate({ reason: "sign_out", principal: leavingPrincipal }, leavingContext) : Promise.resolve(void 0);
|
|
4175
4229
|
try {
|
|
4176
|
-
await
|
|
4177
|
-
if (this.replicationRuntime) await (await this.replicationRuntime).unbind();
|
|
4230
|
+
denied = await denial;
|
|
4178
4231
|
} finally {
|
|
4179
|
-
|
|
4180
|
-
await
|
|
4232
|
+
try {
|
|
4233
|
+
await this.accountRegistryTail;
|
|
4234
|
+
await this.quiesceReplicaRuntime();
|
|
4235
|
+
} finally {
|
|
4236
|
+
if (leavingAddress) {
|
|
4237
|
+
await (leavingAuth ? leavingAuth.clearPersistedSession(leavingAddress) : this.config.sessionStorage?.clear(leavingAddress));
|
|
4238
|
+
}
|
|
4239
|
+
}
|
|
4240
|
+
}
|
|
4241
|
+
}
|
|
4242
|
+
if (denied?.status === "unguarded") {
|
|
4243
|
+
throw new Error(`Replica gate was not denied (${denied.code ?? "unknown"}); the replica may still be restorable.`);
|
|
4244
|
+
}
|
|
4245
|
+
}
|
|
4246
|
+
/**
|
|
4247
|
+
* Step 1 of replica sign-out. Never rejects: failures come back as `unguarded` so the session
|
|
4248
|
+
* is still cleared. Returns `undefined` when replication is off.
|
|
4249
|
+
*/
|
|
4250
|
+
async denyReplicaGate(input, context = this._serviceContext) {
|
|
4251
|
+
const replication = this.config.replication;
|
|
4252
|
+
if (!replication?.enabled) return void 0;
|
|
4253
|
+
try {
|
|
4254
|
+
const lifecycle = replication.storage.lifecycle;
|
|
4255
|
+
if (lifecycle?.kind !== (replication.storage.kind === "indexeddb" ? "indexeddb" : "file")) {
|
|
4256
|
+
return { status: "unguarded", code: "gate_unavailable" };
|
|
4257
|
+
}
|
|
4258
|
+
const result = await lifecycle.deny(input);
|
|
4259
|
+
if (result.status === "denied" || result.status === "deleted" || result.status === "marker") {
|
|
4260
|
+
try {
|
|
4261
|
+
(await getNodeReplicationLoaders().authority()).reportReplicaDenial(
|
|
4262
|
+
result,
|
|
4263
|
+
input.reason,
|
|
4264
|
+
replication.onEvent,
|
|
4265
|
+
context
|
|
4266
|
+
);
|
|
4267
|
+
} catch {
|
|
4181
4268
|
}
|
|
4182
4269
|
}
|
|
4270
|
+
return result;
|
|
4271
|
+
} catch (error) {
|
|
4272
|
+
const code = typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : "gate_unavailable";
|
|
4273
|
+
return { status: "unguarded", code };
|
|
4183
4274
|
}
|
|
4184
4275
|
}
|
|
4185
4276
|
/**
|
|
@@ -4199,15 +4290,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4199
4290
|
this._activeServiceSession = void 0;
|
|
4200
4291
|
oldGraph.retire();
|
|
4201
4292
|
oldCore?.retireServices();
|
|
4202
|
-
oldAuth?.resetSession(
|
|
4203
|
-
this.sessionManager,
|
|
4204
|
-
this.sessionKeyId,
|
|
4205
|
-
this.explicitHost ? [this.explicitHost] : void 0
|
|
4206
|
-
);
|
|
4207
4293
|
this.config.host = this.explicitHost ?? DEFAULT_HOST;
|
|
4208
4294
|
this._chainId = 1;
|
|
4209
4295
|
this._restoredNodeFeatures = void 0;
|
|
4210
|
-
this._activeServiceSession = void 0;
|
|
4211
4296
|
this._serviceContext = void 0;
|
|
4212
4297
|
this._kv = void 0;
|
|
4213
4298
|
this._sql = void 0;
|
|
@@ -4232,6 +4317,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4232
4317
|
this._bootstrapStatus = { skipped: false };
|
|
4233
4318
|
this.replicationDelegateSession = void 0;
|
|
4234
4319
|
this.replicationScopeError = void 0;
|
|
4320
|
+
this.replicaSessionPrincipal = void 0;
|
|
4321
|
+
this.replicaSessionStamp = void 0;
|
|
4322
|
+
this.replicaRestoreGate = void 0;
|
|
4235
4323
|
const sessionKey = createDefaultSessionKey(this.wasmBindings);
|
|
4236
4324
|
this.sessionManager = sessionKey.manager;
|
|
4237
4325
|
this.sessionKeyId = sessionKey.keyId;
|
|
@@ -4241,6 +4329,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4241
4329
|
sessionKey.keyId,
|
|
4242
4330
|
this.explicitHost ? [this.explicitHost] : void 0
|
|
4243
4331
|
);
|
|
4332
|
+
this.replicaSessionPrincipal = void 0;
|
|
4333
|
+
this.replicaSessionStamp = void 0;
|
|
4334
|
+
this.replicaRestoreGate = void 0;
|
|
4244
4335
|
this._serviceGraph = this.createServiceGraphLifetime();
|
|
4245
4336
|
this.auth = oldAuth;
|
|
4246
4337
|
this.tc = this.auth ? new TinyCloud(this.auth, {
|
|
@@ -4270,11 +4361,25 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4270
4361
|
"Cannot signIn() in session-only mode. Provide a privateKey in config to create your own space."
|
|
4271
4362
|
);
|
|
4272
4363
|
}
|
|
4364
|
+
await this.signInWith(() => this.tc.signIn(options));
|
|
4365
|
+
}
|
|
4366
|
+
/**
|
|
4367
|
+
* Fresh OpenKey sign-in. The callback receives the SDK's original SIWE and
|
|
4368
|
+
* session JWK before approval; the signed result is verified against those
|
|
4369
|
+
* bytes before the network session or replica stamp is committed.
|
|
4370
|
+
*/
|
|
4371
|
+
async signInWithOpenKey(authorizeFn, options) {
|
|
4372
|
+
if (!this.signer || !this.tc || !this.auth) {
|
|
4373
|
+
throw new Error("Cannot signInWithOpenKey() in session-only mode. Provide a signer in config.");
|
|
4374
|
+
}
|
|
4375
|
+
await this.signInWith(() => this.auth.signInWithOpenKey(authorizeFn, options));
|
|
4376
|
+
}
|
|
4377
|
+
async signInWith(performSignIn) {
|
|
4273
4378
|
const epoch = this.sessionEpoch;
|
|
4274
4379
|
const attempt = ++this.latestSessionAttempt;
|
|
4275
4380
|
await this.signOutInFlight;
|
|
4276
4381
|
try {
|
|
4277
|
-
await this.establishSignIn(epoch,
|
|
4382
|
+
await this.establishSignIn(epoch, performSignIn);
|
|
4278
4383
|
} catch (error) {
|
|
4279
4384
|
if (epoch !== this.sessionEpoch && attempt === this.latestSessionAttempt) {
|
|
4280
4385
|
await this.endSession();
|
|
@@ -4282,7 +4387,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4282
4387
|
throw error;
|
|
4283
4388
|
}
|
|
4284
4389
|
}
|
|
4285
|
-
async establishSignIn(epoch,
|
|
4390
|
+
async establishSignIn(epoch, performSignIn) {
|
|
4286
4391
|
if (!this.signer || !this.tc) {
|
|
4287
4392
|
throw new Error("Not signed in. Call signIn() first.");
|
|
4288
4393
|
}
|
|
@@ -4292,6 +4397,12 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4292
4397
|
const address = canonicalizeAddress2(await this.signer.getAddress());
|
|
4293
4398
|
const chainId = await this.signer.getChainId();
|
|
4294
4399
|
this.assertSessionEpoch(epoch, "Sign-in");
|
|
4400
|
+
const nextPrincipal = pkhDid2(address, chainId);
|
|
4401
|
+
const priorPrincipal = this.replicaSessionPrincipal ?? (this.address ? pkhDid2(this.address, this._chainId) : void 0);
|
|
4402
|
+
if (this.config.replication?.enabled && priorPrincipal && priorPrincipal !== nextPrincipal) {
|
|
4403
|
+
const denial = await this.denyReplicaGate({ reason: "account_switch", principal: priorPrincipal, next: nextPrincipal });
|
|
4404
|
+
this.replicaRestoreGate = denial?.status === "unguarded" ? "gate_unavailable" : "stamp_stale";
|
|
4405
|
+
}
|
|
4295
4406
|
if (address !== this._address || chainId !== this._chainId) this.storageFull.clear();
|
|
4296
4407
|
this._address = address;
|
|
4297
4408
|
this._chainId = chainId;
|
|
@@ -4309,7 +4420,20 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4309
4420
|
this.runtimePermissionGrants = [];
|
|
4310
4421
|
this.confirmedHostedSpaceIds.clear();
|
|
4311
4422
|
this.hostedOwnedSpaceIds.clear();
|
|
4312
|
-
|
|
4423
|
+
const signInAttempt = this.latestSessionAttempt;
|
|
4424
|
+
const previousReplicaGate = this.replicaRestoreGate;
|
|
4425
|
+
const previousReplicaStamp = this.replicaSessionStamp;
|
|
4426
|
+
this.replicaRestoreGate = void 0;
|
|
4427
|
+
this.replicaSessionStamp = void 0;
|
|
4428
|
+
try {
|
|
4429
|
+
await performSignIn();
|
|
4430
|
+
} catch (error) {
|
|
4431
|
+
if (epoch === this.sessionEpoch && signInAttempt === this.latestSessionAttempt) {
|
|
4432
|
+
this.replicaRestoreGate = previousReplicaGate;
|
|
4433
|
+
this.replicaSessionStamp = previousReplicaStamp;
|
|
4434
|
+
}
|
|
4435
|
+
throw error;
|
|
4436
|
+
}
|
|
4313
4437
|
this.assertSessionEpoch(epoch, "Sign-in");
|
|
4314
4438
|
this.cancelOwnedSpaceHosts();
|
|
4315
4439
|
this.syncResolvedHostFromAuth();
|
|
@@ -4317,8 +4441,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4317
4441
|
this.hostedOwnedSpaceIds.add(spaceId);
|
|
4318
4442
|
}
|
|
4319
4443
|
this.replicationDelegateSession = void 0;
|
|
4444
|
+
this.replicaSessionPrincipal = pkhDid2(address, chainId);
|
|
4320
4445
|
this.replicationScopeError = void 0;
|
|
4321
|
-
|
|
4446
|
+
await this.quiesceReplicaRuntime();
|
|
4322
4447
|
this.assertSessionEpoch(epoch, "Sign-in");
|
|
4323
4448
|
const oldGraph = this._serviceGraph;
|
|
4324
4449
|
this._serviceGraph = this.createServiceGraphLifetime();
|
|
@@ -4905,6 +5030,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4905
5030
|
if (this.ownStorageFull) {
|
|
4906
5031
|
return;
|
|
4907
5032
|
}
|
|
5033
|
+
if (!this.currentSessionCoversAccountRegistry()) {
|
|
5034
|
+
return;
|
|
5035
|
+
}
|
|
4908
5036
|
const controller = new AbortController();
|
|
4909
5037
|
const deadline = setTimeout(
|
|
4910
5038
|
() => controller.abort(new DOMException("Account registry deadline exceeded", "TimeoutError")),
|
|
@@ -5019,6 +5147,27 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5019
5147
|
(operation) => operation.service === "space" && this.actionContains(operation.action, "tinycloud.space/list")
|
|
5020
5148
|
);
|
|
5021
5149
|
}
|
|
5150
|
+
/**
|
|
5151
|
+
* Whether the current primary session's recap covers the account index read
|
|
5152
|
+
* (`tinycloud.sql/read` on the owned account space's `account` database),
|
|
5153
|
+
* the first call of every registry sync. As in
|
|
5154
|
+
* {@link currentSessionCanListSpaces}, a session with no parseable recap
|
|
5155
|
+
* keeps today's behavior.
|
|
5156
|
+
*/
|
|
5157
|
+
currentSessionCoversAccountRegistry() {
|
|
5158
|
+
const session = this.currentTinyCloudSession();
|
|
5159
|
+
const operations = session ? this.recapOperationsFromSession(session) : [];
|
|
5160
|
+
if (operations.length === 0) {
|
|
5161
|
+
return true;
|
|
5162
|
+
}
|
|
5163
|
+
const indexRead = {
|
|
5164
|
+
spaceId: this.ownedSpaceId(ACCOUNT_REGISTRY_SPACE2),
|
|
5165
|
+
service: "sql",
|
|
5166
|
+
path: "account",
|
|
5167
|
+
action: "tinycloud.sql/read"
|
|
5168
|
+
};
|
|
5169
|
+
return operations.some((granted) => this.operationCovers(granted, indexRead));
|
|
5170
|
+
}
|
|
5022
5171
|
async withAccountRegistryRetry(task, signal) {
|
|
5023
5172
|
const delays = [250, 1e3, 3e3];
|
|
5024
5173
|
const stopped = (detail) => {
|
|
@@ -5485,11 +5634,12 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5485
5634
|
);
|
|
5486
5635
|
const stagedHost = resolvedHost ?? this.config.host;
|
|
5487
5636
|
let restoredNodeFeatures;
|
|
5637
|
+
let replicaProtocolMismatch = false;
|
|
5488
5638
|
if (this.config.replication?.enabled) {
|
|
5489
5639
|
try {
|
|
5490
5640
|
restoredNodeFeatures = (await checkNodeInfo2(stagedHost, this.wasmBindings.protocolVersion())).features;
|
|
5491
5641
|
} catch (error) {
|
|
5492
|
-
|
|
5642
|
+
replicaProtocolMismatch = error instanceof ProtocolMismatchError;
|
|
5493
5643
|
}
|
|
5494
5644
|
}
|
|
5495
5645
|
const stagedAddress = restoredAddress;
|
|
@@ -5497,28 +5647,55 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5497
5647
|
let replicationPrincipal;
|
|
5498
5648
|
let compactReplicationSession;
|
|
5499
5649
|
let replicationScopeError;
|
|
5650
|
+
let replicationScopeUnavailable = false;
|
|
5500
5651
|
if (this.config.replication?.enabled) {
|
|
5501
5652
|
if (stagedAddress !== void 0) {
|
|
5502
5653
|
replicationPrincipal = pkhDid2(stagedAddress, stagedChainId);
|
|
5503
5654
|
} else {
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
delegationCid: sessionData.delegationCid,
|
|
5508
|
-
verificationMethod: restoredVerificationMethod,
|
|
5509
|
-
persistedSpace: sessionData.spaceId
|
|
5510
|
-
});
|
|
5511
|
-
if (!signedScope) {
|
|
5512
|
-
replicationScopeError = "Persisted space does not match the locally verified delegation scope.";
|
|
5513
|
-
} else {
|
|
5514
|
-
replicationPrincipal = signedScope.principal;
|
|
5515
|
-
compactReplicationSession = {
|
|
5655
|
+
try {
|
|
5656
|
+
const { replicationScopeFromSignedSession } = await getNodeReplicationLoaders().authority();
|
|
5657
|
+
const signedScope = replicationScopeFromSignedSession({
|
|
5516
5658
|
delegationHeader: sessionData.delegationHeader,
|
|
5517
5659
|
delegationCid: sessionData.delegationCid,
|
|
5518
|
-
spaceId: signedScope.space,
|
|
5519
5660
|
verificationMethod: restoredVerificationMethod,
|
|
5520
|
-
|
|
5521
|
-
};
|
|
5661
|
+
persistedSpace: sessionData.spaceId
|
|
5662
|
+
});
|
|
5663
|
+
if (!signedScope) {
|
|
5664
|
+
replicationScopeError = "Persisted space does not match the locally verified delegation scope.";
|
|
5665
|
+
} else {
|
|
5666
|
+
replicationPrincipal = signedScope.principal;
|
|
5667
|
+
compactReplicationSession = {
|
|
5668
|
+
delegationHeader: sessionData.delegationHeader,
|
|
5669
|
+
delegationCid: sessionData.delegationCid,
|
|
5670
|
+
spaceId: signedScope.space,
|
|
5671
|
+
verificationMethod: restoredVerificationMethod,
|
|
5672
|
+
jwk: stagedJwk
|
|
5673
|
+
};
|
|
5674
|
+
}
|
|
5675
|
+
} catch {
|
|
5676
|
+
replicationScopeUnavailable = true;
|
|
5677
|
+
replicationScopeError = "Replica scope could not be verified for this network session.";
|
|
5678
|
+
}
|
|
5679
|
+
}
|
|
5680
|
+
}
|
|
5681
|
+
let replicaRestoreGate;
|
|
5682
|
+
let restoredReplicaStamp;
|
|
5683
|
+
const replication = this.config.replication;
|
|
5684
|
+
if (replication?.enabled) {
|
|
5685
|
+
if (replicaProtocolMismatch) {
|
|
5686
|
+
replicaRestoreGate = "gate_unavailable";
|
|
5687
|
+
} else if (replicationPrincipal === void 0) {
|
|
5688
|
+
replicaRestoreGate = replicationScopeUnavailable ? "gate_unavailable" : "principal_mismatch";
|
|
5689
|
+
} else {
|
|
5690
|
+
try {
|
|
5691
|
+
const { restoreReplicaSessionStamp } = await getNodeReplicationLoaders().authority();
|
|
5692
|
+
restoredReplicaStamp = await restoreReplicaSessionStamp(
|
|
5693
|
+
replication.storage,
|
|
5694
|
+
replicationPrincipal,
|
|
5695
|
+
sessionData.replicaStamp ?? void 0
|
|
5696
|
+
);
|
|
5697
|
+
} catch (error) {
|
|
5698
|
+
replicaRestoreGate = replicaRestoreRefusal(error);
|
|
5522
5699
|
}
|
|
5523
5700
|
}
|
|
5524
5701
|
}
|
|
@@ -5611,7 +5788,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5611
5788
|
}
|
|
5612
5789
|
this.replicationDelegateSession = compactReplicationSession;
|
|
5613
5790
|
this.replicationScopeError = replicationScopeError;
|
|
5614
|
-
|
|
5791
|
+
this.replicaRestoreGate = replicaRestoreGate;
|
|
5792
|
+
this.replicaSessionPrincipal = replicationPrincipal;
|
|
5793
|
+
this.replicaSessionStamp = restoredReplicaStamp;
|
|
5794
|
+
await this.quiesceReplicaRuntime();
|
|
5615
5795
|
if (epoch !== this.sessionEpoch) {
|
|
5616
5796
|
oldGraph.retire();
|
|
5617
5797
|
oldCore?.retireServices?.();
|
|
@@ -5626,7 +5806,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5626
5806
|
detail: replicationScopeError
|
|
5627
5807
|
});
|
|
5628
5808
|
}
|
|
5629
|
-
await this.
|
|
5809
|
+
await this.bindReplicaOrGate(stagedGraph.serviceContext, serviceSession, stagedGraph.kv, replicationPrincipal);
|
|
5630
5810
|
oldCore?.retireServices?.();
|
|
5631
5811
|
if (epoch !== this.sessionEpoch) {
|
|
5632
5812
|
if (attempt === this.latestSessionAttempt) await this.endSession();
|
|
@@ -5759,6 +5939,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5759
5939
|
session: input.serviceSession,
|
|
5760
5940
|
invoke: graph.invoke,
|
|
5761
5941
|
invokeAny: graph.invokeAny,
|
|
5942
|
+
signOwnerRevocation: this.signer ? (cid) => this.signOwnerRevocation(cid) : void 0,
|
|
5762
5943
|
fetch: graph.fetch
|
|
5763
5944
|
});
|
|
5764
5945
|
const keyProvider = new WasmKeyProvider({ sessionManager: input.manager });
|
|
@@ -6102,6 +6283,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6102
6283
|
signer: this.signer,
|
|
6103
6284
|
signStrategy: this.config.signStrategy ?? { type: "auto-sign" },
|
|
6104
6285
|
wasmBindings: this.wasmBindings,
|
|
6286
|
+
...this.config.replication?.enabled === true ? { establishReplicaStamp: this.establishReplicaStamp } : {},
|
|
6105
6287
|
sessionManager: this.sessionManager,
|
|
6106
6288
|
sessionStorage: options?.sessionStorage ?? this.config.sessionStorage ?? new MemorySessionStorage(),
|
|
6107
6289
|
domain: this.siweDomain,
|
|
@@ -6160,6 +6342,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6160
6342
|
signer: this.signer,
|
|
6161
6343
|
signStrategy: this.config.signStrategy ?? { type: "auto-sign" },
|
|
6162
6344
|
wasmBindings: this.wasmBindings,
|
|
6345
|
+
...this.config.replication?.enabled === true ? { establishReplicaStamp: this.establishReplicaStamp } : {},
|
|
6163
6346
|
sessionManager: this.sessionManager,
|
|
6164
6347
|
sessionStorage: options?.sessionStorage ?? this.config.sessionStorage ?? new MemorySessionStorage(),
|
|
6165
6348
|
domain: this.siweDomain,
|
|
@@ -6240,7 +6423,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6240
6423
|
jwk: session.jwk
|
|
6241
6424
|
};
|
|
6242
6425
|
this._serviceContext.setSession(serviceSession);
|
|
6243
|
-
await this.
|
|
6426
|
+
await this.bindReplicaOrGate(this._serviceContext, serviceSession, this._kv);
|
|
6244
6427
|
this.tc.serviceContext.setSession(serviceSession);
|
|
6245
6428
|
this._vault = this.createVaultService(session.spaceId, this._kv);
|
|
6246
6429
|
this._vault.initialize(this._serviceContext);
|
|
@@ -6634,6 +6817,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6634
6817
|
session: serviceSession,
|
|
6635
6818
|
invoke: graph.invoke,
|
|
6636
6819
|
invokeAny: graph.invokeAny,
|
|
6820
|
+
signOwnerRevocation: this.signer ? (cid) => this.signOwnerRevocation(cid) : void 0,
|
|
6637
6821
|
fetch: graph.fetch
|
|
6638
6822
|
});
|
|
6639
6823
|
this._spaceService = new SpaceService({
|
|
@@ -7926,7 +8110,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7926
8110
|
return this.delegationManager.create(params);
|
|
7927
8111
|
}
|
|
7928
8112
|
/**
|
|
7929
|
-
* Revoke a delegation
|
|
8113
|
+
* Revoke a delegation with the owner wallet when available. Session-only
|
|
8114
|
+
* clients continue to use their delegated revocation authority.
|
|
7930
8115
|
*
|
|
7931
8116
|
* @param cid - The CID of the delegation to revoke
|
|
7932
8117
|
* @returns Result indicating success or failure
|
|
@@ -8201,34 +8386,67 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
8201
8386
|
return auth.replicationSignInEntries();
|
|
8202
8387
|
}
|
|
8203
8388
|
async replicationAuthority() {
|
|
8204
|
-
const { createReplicationAuthority } = await getNodeReplicationLoaders().authority();
|
|
8389
|
+
const { createReplicationAuthority, mintNodeReplicationGrant, verifiedOwnerConsentAnchor } = await getNodeReplicationLoaders().authority();
|
|
8205
8390
|
return createReplicationAuthority({
|
|
8206
8391
|
replicationSession: () => this.currentTinyCloudSession() ?? this.replicationDelegateSession,
|
|
8392
|
+
verifiedConsentAnchor: (session) => {
|
|
8393
|
+
const current = this.currentTinyCloudSession();
|
|
8394
|
+
return session === current && current !== void 0 ? verifiedOwnerConsentAnchor(current, this.wasmBindings, exactVerifiedRecap) : null;
|
|
8395
|
+
},
|
|
8207
8396
|
siweExpiration: (siwe) => extractSiweExpiration(siwe),
|
|
8208
8397
|
planDelegation: (entries, options) => this.planDelegation(entries, options),
|
|
8209
|
-
mintDelegation:
|
|
8210
|
-
|
|
8211
|
-
|
|
8212
|
-
|
|
8213
|
-
|
|
8214
|
-
|
|
8215
|
-
|
|
8398
|
+
mintDelegation: (deviceDid, entries, options) => mintNodeReplicationGrant(
|
|
8399
|
+
deviceDid,
|
|
8400
|
+
entries,
|
|
8401
|
+
options,
|
|
8402
|
+
options?.renewalOnly ? this.currentTinyCloudSession() : void 0,
|
|
8403
|
+
this
|
|
8404
|
+
)
|
|
8216
8405
|
});
|
|
8217
8406
|
}
|
|
8218
|
-
async
|
|
8407
|
+
async quiesceReplicaRuntime() {
|
|
8219
8408
|
if (!this.replicationRuntime) return;
|
|
8220
|
-
|
|
8221
|
-
|
|
8222
|
-
|
|
8223
|
-
|
|
8224
|
-
|
|
8225
|
-
|
|
8226
|
-
|
|
8227
|
-
|
|
8228
|
-
|
|
8229
|
-
|
|
8230
|
-
|
|
8231
|
-
|
|
8409
|
+
try {
|
|
8410
|
+
await (await this.replicationRuntime).unbind();
|
|
8411
|
+
} catch {
|
|
8412
|
+
this.replicaRestoreGate = "gate_unavailable";
|
|
8413
|
+
}
|
|
8414
|
+
}
|
|
8415
|
+
async bindReplicaOrGate(context, session, kv, principal) {
|
|
8416
|
+
if (!this.replicationRuntime) return;
|
|
8417
|
+
const gate = this.replicaRestoreGate;
|
|
8418
|
+
const epoch = this.sessionEpoch;
|
|
8419
|
+
const attempt = this.latestSessionAttempt;
|
|
8420
|
+
try {
|
|
8421
|
+
const runtime = await this.replicationRuntime;
|
|
8422
|
+
if (epoch !== this.sessionEpoch || attempt !== this.latestSessionAttempt || this._kv !== kv || this._serviceContext !== context || gate !== this.replicaRestoreGate) return;
|
|
8423
|
+
if (gate) {
|
|
8424
|
+
await runtime.bindGatedNetworkOnly({
|
|
8425
|
+
context,
|
|
8426
|
+
session,
|
|
8427
|
+
gate,
|
|
8428
|
+
primaryKV: [{ space: session.spaceId, kv }]
|
|
8429
|
+
}, getNodeReplicationLoaders().gatedNetwork);
|
|
8430
|
+
return;
|
|
8431
|
+
}
|
|
8432
|
+
const identityPrincipal = principal ?? (this._address === void 0 ? void 0 : pkhDid2(this._address, this._chainId));
|
|
8433
|
+
if (identityPrincipal === void 0) return;
|
|
8434
|
+
const authority = await this.replicationAuthority();
|
|
8435
|
+
if (epoch !== this.sessionEpoch || attempt !== this.latestSessionAttempt || this._kv !== kv || this._serviceContext !== context) return;
|
|
8436
|
+
await runtime.bind({
|
|
8437
|
+
context,
|
|
8438
|
+
session,
|
|
8439
|
+
principal: identityPrincipal,
|
|
8440
|
+
stamp: this.replicaSessionStamp,
|
|
8441
|
+
authority,
|
|
8442
|
+
nodeFeatures: this.replicationNodeFeatures,
|
|
8443
|
+
primaryKV: [{ space: session.spaceId, kv }]
|
|
8444
|
+
});
|
|
8445
|
+
} catch {
|
|
8446
|
+
if (epoch !== this.sessionEpoch || attempt !== this.latestSessionAttempt || this._kv !== kv || this._serviceContext !== context) return;
|
|
8447
|
+
this.replicaRestoreGate = "gate_unavailable";
|
|
8448
|
+
await this.quiesceReplicaRuntime();
|
|
8449
|
+
}
|
|
8232
8450
|
}
|
|
8233
8451
|
/** Stable control facade for this node's optional replication runtime. */
|
|
8234
8452
|
get replication() {
|
|
@@ -9436,8 +9654,8 @@ import {
|
|
|
9436
9654
|
Space
|
|
9437
9655
|
} from "@tinycloud/sdk-core";
|
|
9438
9656
|
import {
|
|
9439
|
-
ProtocolMismatchError,
|
|
9440
|
-
VersionCheckError
|
|
9657
|
+
ProtocolMismatchError as ProtocolMismatchError2,
|
|
9658
|
+
VersionCheckError,
|
|
9441
9659
|
UnsupportedFeatureError as UnsupportedFeatureError2,
|
|
9442
9660
|
checkNodeInfo as checkNodeInfo3
|
|
9443
9661
|
} from "@tinycloud/sdk-core";
|
|
@@ -9470,7 +9688,7 @@ export {
|
|
|
9470
9688
|
NodeUserAuthorization,
|
|
9471
9689
|
PermissionNotInManifestError2 as PermissionNotInManifestError,
|
|
9472
9690
|
PrefixedKVService,
|
|
9473
|
-
ProtocolMismatchError,
|
|
9691
|
+
ProtocolMismatchError2 as ProtocolMismatchError,
|
|
9474
9692
|
SECRET_NAME_RE,
|
|
9475
9693
|
SQLAction,
|
|
9476
9694
|
SQLService3 as SQLService,
|
|
@@ -9490,7 +9708,7 @@ export {
|
|
|
9490
9708
|
VAULT_PERMISSION_SERVICE,
|
|
9491
9709
|
VaultHeaders,
|
|
9492
9710
|
VaultPublicSpaceKVActions,
|
|
9493
|
-
|
|
9711
|
+
VersionCheckError,
|
|
9494
9712
|
WasmKeyProvider,
|
|
9495
9713
|
activateCompactRuntimeDelegation,
|
|
9496
9714
|
addressStorageKey,
|
|
@@ -9519,6 +9737,7 @@ export {
|
|
|
9519
9737
|
expandActionShortNames,
|
|
9520
9738
|
expandPermissionEntries2 as expandPermissionEntries,
|
|
9521
9739
|
expandPermissionEntry,
|
|
9740
|
+
getNodeReplicationLoaders,
|
|
9522
9741
|
getTinyCloudDebugLogs,
|
|
9523
9742
|
installTinyCloudDebugGlobals,
|
|
9524
9743
|
isCapabilitySubset3 as isCapabilitySubset,
|
|
@@ -9532,7 +9751,7 @@ export {
|
|
|
9532
9751
|
parseSecretCatalogKey,
|
|
9533
9752
|
parseSpaceUri2 as parseSpaceUri,
|
|
9534
9753
|
pkhDid3 as pkhDid,
|
|
9535
|
-
|
|
9754
|
+
principalDid,
|
|
9536
9755
|
principalDidEquals4 as principalDidEquals,
|
|
9537
9756
|
registerReplicationLoaders,
|
|
9538
9757
|
resolveManifest2 as resolveManifest,
|