@tinycloud/node-sdk 3.8.0-beta.1 → 3.8.0-beta.11
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/{TinyCloudNode-Ckqu_fqH.d.ts → TinyCloudNode-BVDUdEcf.d.cts} +53 -5
- package/dist/{TinyCloudNode-DDfx8Fx5.d.cts → TinyCloudNode-jMK_2Uj8.d.ts} +53 -5
- package/dist/{authority-BwM_s3Ya.d.cts → authority-DJ-PE4Xo.d.cts} +20 -4
- package/dist/{authority-DcyJpajL.d.ts → authority-i22vHbbp.d.ts} +20 -4
- package/dist/core.cjs +300 -32
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +5 -4
- package/dist/core.d.ts +5 -4
- package/dist/core.js +292 -27
- package/dist/core.js.map +1 -1
- package/dist/credentials-core.d.cts +1 -1
- package/dist/credentials-core.d.ts +1 -1
- package/dist/credentials.d.cts +1 -1
- package/dist/credentials.d.ts +1 -1
- package/dist/{delegation-0LKZkmP2.d.cts → delegation-Ckc3UETl.d.cts} +1 -1
- package/dist/{delegation-0LKZkmP2.d.ts → delegation-Ckc3UETl.d.ts} +1 -1
- package/dist/encryption-keys.d.cts +2 -2
- package/dist/encryption-keys.d.ts +2 -2
- package/dist/index.cjs +304 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +291 -31
- package/dist/index.js.map +1 -1
- package/dist/replication/authority.cjs +39 -0
- package/dist/replication/authority.cjs.map +1 -1
- package/dist/replication/authority.d.cts +4 -3
- package/dist/replication/authority.d.ts +4 -3
- package/dist/replication/authority.js +38 -0
- package/dist/replication/authority.js.map +1 -1
- package/dist/replication/runtime.cjs +1 -0
- package/dist/replication/runtime.cjs.map +1 -1
- package/dist/replication/runtime.d.cts +1 -1
- package/dist/replication/runtime.d.ts +1 -1
- package/dist/replication/runtime.js +1 -0
- package/dist/replication/runtime.js.map +1 -1
- package/dist/{runtime-v62FzQHz.d.ts → runtime-CO4VXnPq.d.ts} +2 -1
- package/dist/{runtime-D1o9Eybt.d.cts → runtime-mRhSu-vJ.d.cts} +2 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -17089,6 +17089,9 @@ function assertValidReplicationConfig(replication) {
|
|
|
17089
17089
|
if (replication?.syncScope !== void 0 && replication.syncScope !== "prefixes") {
|
|
17090
17090
|
throw new TypeError('replication.syncScope must be "prefixes"');
|
|
17091
17091
|
}
|
|
17092
|
+
if (replication?.contentFetch !== void 0 && replication.contentFetch !== "eager" && replication.contentFetch !== "lazy") {
|
|
17093
|
+
throw new TypeError('replication.contentFetch must be "eager" or "lazy"');
|
|
17094
|
+
}
|
|
17092
17095
|
if (replication === void 0 || !replication.enabled) return;
|
|
17093
17096
|
if (replication.storage === void 0 || replication.storage === null) {
|
|
17094
17097
|
throw new TypeError("replication.enabled requires replication.storage");
|
|
@@ -17143,6 +17146,7 @@ function resolvedOptions(options) {
|
|
|
17143
17146
|
maxStalenessMs: options.maxStalenessMs ?? 12e4,
|
|
17144
17147
|
staleSyncTimeoutMs: options.staleSyncTimeoutMs ?? 5e3,
|
|
17145
17148
|
allowSecrets: options.allowSecrets === true,
|
|
17149
|
+
contentFetch: options.contentFetch ?? "eager",
|
|
17146
17150
|
verify: options.verify === true
|
|
17147
17151
|
};
|
|
17148
17152
|
}
|
|
@@ -17414,6 +17418,7 @@ __export(authority_exports, {
|
|
|
17414
17418
|
assertValidReplicationConfig: () => assertValidReplicationConfig,
|
|
17415
17419
|
augmentSignInEntriesWithReplication: () => augmentSignInEntriesWithReplication,
|
|
17416
17420
|
createReplicationAuthority: () => createReplicationAuthority,
|
|
17421
|
+
eraseReplicaLocalCopy: () => eraseReplicaLocalCopy,
|
|
17417
17422
|
establishReplicaSessionStamp: () => establishReplicaSessionStamp,
|
|
17418
17423
|
hasUnrestrictedGetCoverage: () => hasUnrestrictedGetCoverage,
|
|
17419
17424
|
mintNodeReplicationGrant: () => mintNodeReplicationGrant,
|
|
@@ -17444,6 +17449,38 @@ function reportReplicaDenial(result, reason, onEvent, context) {
|
|
|
17444
17449
|
} catch {
|
|
17445
17450
|
}
|
|
17446
17451
|
}
|
|
17452
|
+
async function eraseReplicaLocalCopy(lifecycle, trigger, denyStatus, handlers) {
|
|
17453
|
+
if (lifecycle?.kind !== "indexeddb" || lifecycle.eraseLocalCopy === void 0) return void 0;
|
|
17454
|
+
let outcome = { status: "failed", code: "STORAGE_ERROR", marker: false };
|
|
17455
|
+
for (let attempt = 0; attempt <= ERASE_RETRY_DELAYS_MS.length; attempt++) {
|
|
17456
|
+
if (attempt > 0) await new Promise((resolve2) => setTimeout(resolve2, ERASE_RETRY_DELAYS_MS[attempt - 1]));
|
|
17457
|
+
try {
|
|
17458
|
+
outcome = await lifecycle.eraseLocalCopy({ signal: AbortSignal.timeout(ERASE_ATTEMPT_MS) });
|
|
17459
|
+
} catch {
|
|
17460
|
+
outcome = { status: "failed", code: "STORAGE_ERROR", marker: false };
|
|
17461
|
+
}
|
|
17462
|
+
if (outcome.status !== "failed") break;
|
|
17463
|
+
}
|
|
17464
|
+
const keysKept = denyStatus === "marker" || denyStatus === "unguarded";
|
|
17465
|
+
const purge = outcome.status === "erased" ? { ok: true } : { ok: false, retryNow: outcome.status === "failed" };
|
|
17466
|
+
if (outcome.status === "erased" || outcome.status === "refused" && !keysKept) return { outcome, purge };
|
|
17467
|
+
const logoutPending = outcome.status === "failed" && outcome.marker;
|
|
17468
|
+
const event = {
|
|
17469
|
+
type: "replica.purge.failed",
|
|
17470
|
+
trigger,
|
|
17471
|
+
reason: logoutPending ? "logout_pending" : "gate_unavailable",
|
|
17472
|
+
code: outcome.code,
|
|
17473
|
+
retryNow: outcome.status === "failed"
|
|
17474
|
+
};
|
|
17475
|
+
for (const handler of handlers) {
|
|
17476
|
+
try {
|
|
17477
|
+
handler(event);
|
|
17478
|
+
} catch (error) {
|
|
17479
|
+
console.error('Error in "replica.purge.failed" handler:', error);
|
|
17480
|
+
}
|
|
17481
|
+
}
|
|
17482
|
+
return logoutPending ? { outcome, purge, logoutPending } : { outcome, purge };
|
|
17483
|
+
}
|
|
17447
17484
|
function verifiedOwnerConsentAnchor(session, wasmBindings, exactVerifiedRecap2) {
|
|
17448
17485
|
if (typeof session.siwe !== "string" || typeof session.signature !== "string" || typeof session.address !== "string" || typeof session.chainId !== "number") return null;
|
|
17449
17486
|
try {
|
|
@@ -17702,7 +17739,7 @@ function createReplicationAuthority(host) {
|
|
|
17702
17739
|
};
|
|
17703
17740
|
return authority;
|
|
17704
17741
|
}
|
|
17705
|
-
var import_siwe3, import_sdk_core12, import_replica, parseUcanGrant, KV_GET2, KV_SYNC2, KV_SERVICE2, AUTHORITY_EXPIRY_MARGIN_MS;
|
|
17742
|
+
var import_siwe3, import_sdk_core12, import_replica, parseUcanGrant, ERASE_RETRY_DELAYS_MS, ERASE_ATTEMPT_MS, KV_GET2, KV_SYNC2, KV_SERVICE2, AUTHORITY_EXPIRY_MARGIN_MS;
|
|
17706
17743
|
var init_authority = __esm({
|
|
17707
17744
|
"src/replication/authority.ts"() {
|
|
17708
17745
|
"use strict";
|
|
@@ -17712,6 +17749,8 @@ var init_authority = __esm({
|
|
|
17712
17749
|
init_config();
|
|
17713
17750
|
init_authority_sign_in();
|
|
17714
17751
|
parseUcanGrant = import_replica.parseUcanGrant;
|
|
17752
|
+
ERASE_RETRY_DELAYS_MS = [150, 450];
|
|
17753
|
+
ERASE_ATTEMPT_MS = 3e3;
|
|
17715
17754
|
KV_GET2 = import_sdk_core12.KV.GET;
|
|
17716
17755
|
KV_SYNC2 = import_sdk_core12.KV.SYNC;
|
|
17717
17756
|
KV_SERVICE2 = "tinycloud.kv";
|
|
@@ -18789,13 +18828,42 @@ function getNodeReplicationLoaders() {
|
|
|
18789
18828
|
|
|
18790
18829
|
// src/TinyCloudNode.ts
|
|
18791
18830
|
var import_sdk_core10 = require("@tinycloud/sdk-core");
|
|
18792
|
-
var
|
|
18831
|
+
var import_sdk_services4 = require("@tinycloud/sdk-services");
|
|
18793
18832
|
var import_requestLog2 = require("@tinycloud/sdk-services/telemetry/requestLog");
|
|
18833
|
+
var import_resource2 = require("@tinycloud/sdk-services/encryption/resource");
|
|
18794
18834
|
|
|
18795
18835
|
// src/authorization/NodeUserAuthorization.ts
|
|
18796
18836
|
var import_node_crypto = require("crypto");
|
|
18797
18837
|
var import_siwe = require("siwe");
|
|
18798
18838
|
var import_sdk_core5 = require("@tinycloud/sdk-core");
|
|
18839
|
+
var import_resource = require("@tinycloud/sdk-services/encryption/resource");
|
|
18840
|
+
var import_sdk_services3 = require("@tinycloud/sdk-services");
|
|
18841
|
+
|
|
18842
|
+
// src/authorization/appKeyHostBinding.ts
|
|
18843
|
+
var DOMAIN_LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
18844
|
+
var IPV4 = /^\d+\.\d+\.\d+\.\d+$/;
|
|
18845
|
+
var APP_ID = /^[a-z0-9][a-z0-9._-]{0,63}$/;
|
|
18846
|
+
function appKeyHostBinding(domain, appId) {
|
|
18847
|
+
if (!APP_ID.test(appId)) return false;
|
|
18848
|
+
if (domain.endsWith(":")) return false;
|
|
18849
|
+
let url;
|
|
18850
|
+
try {
|
|
18851
|
+
url = new URL(`https://${domain}`);
|
|
18852
|
+
} catch {
|
|
18853
|
+
return false;
|
|
18854
|
+
}
|
|
18855
|
+
if (url.protocol !== "https:" || url.port !== "" || url.username !== "" || url.password !== "") return false;
|
|
18856
|
+
if (url.pathname !== "/" || url.search !== "" || url.hash !== "") return false;
|
|
18857
|
+
const host = url.hostname;
|
|
18858
|
+
if (host.startsWith("[") || host.endsWith(".") || IPV4.test(host)) return false;
|
|
18859
|
+
const labels = host.split(".");
|
|
18860
|
+
if (labels.length < 2 || labels[labels.length - 1] === "localhost") return false;
|
|
18861
|
+
if (!labels.every((label) => DOMAIN_LABEL.test(label))) return false;
|
|
18862
|
+
const reversed = labels.reverse().join(".");
|
|
18863
|
+
return appId === reversed || appId.startsWith(`${reversed}.`);
|
|
18864
|
+
}
|
|
18865
|
+
|
|
18866
|
+
// src/authorization/NodeUserAuthorization.ts
|
|
18799
18867
|
init_authority_sign_in();
|
|
18800
18868
|
|
|
18801
18869
|
// src/authorization/caveatRefusal.ts
|
|
@@ -18951,6 +19019,7 @@ function cloneAbilitiesMap(actions) {
|
|
|
18951
19019
|
var NodeUserAuthorization = class {
|
|
18952
19020
|
constructor(config) {
|
|
18953
19021
|
this.activeSessionKeyId = "default";
|
|
19022
|
+
this.preparedSessionCounter = 0;
|
|
18954
19023
|
/** Incremented by resetSession; a sign-in started earlier publishes nothing. */
|
|
18955
19024
|
this.sessionGeneration = 0;
|
|
18956
19025
|
/**
|
|
@@ -19307,6 +19376,23 @@ var NodeUserAuthorization = class {
|
|
|
19307
19376
|
);
|
|
19308
19377
|
return this._capabilityRequest;
|
|
19309
19378
|
}
|
|
19379
|
+
async refreshNamedKeyFeatureForSignIn(address, chainId) {
|
|
19380
|
+
const request = this.getCapabilityRequest();
|
|
19381
|
+
const siweDomain = this.siweConfig?.domain ?? this.domain;
|
|
19382
|
+
const hasNamedKeys = request?.manifests.some((manifest) => manifest.keys?.length && appKeyHostBinding(siweDomain, manifest.keys_app_id ?? manifest.app_id));
|
|
19383
|
+
if (hasNamedKeys) {
|
|
19384
|
+
await this.resolveTinyCloudHostsForSignIn(address, chainId);
|
|
19385
|
+
await this.refreshNodeInfo();
|
|
19386
|
+
}
|
|
19387
|
+
}
|
|
19388
|
+
/** Manifest key paths that this sign-in cannot grant. */
|
|
19389
|
+
get unavailableKeyPaths() {
|
|
19390
|
+
const request = this.getCapabilityRequest();
|
|
19391
|
+
const siweDomain = this.siweConfig?.domain ?? this.domain;
|
|
19392
|
+
return (request?.manifests ?? []).flatMap(
|
|
19393
|
+
(manifest) => this._nodeFeatures.includes("encryption-named-keys-v1") && appKeyHostBinding(siweDomain, manifest.keys_app_id ?? manifest.app_id) ? [] : (manifest.keys ?? []).map((entry) => "path" in entry ? entry.path : entry.prefix)
|
|
19394
|
+
);
|
|
19395
|
+
}
|
|
19310
19396
|
resolveSpaceName(space, address, chainId) {
|
|
19311
19397
|
if (space.startsWith("tinycloud:")) {
|
|
19312
19398
|
return space;
|
|
@@ -19498,15 +19584,31 @@ var NodeUserAuthorization = class {
|
|
|
19498
19584
|
}
|
|
19499
19585
|
const rawAbilities = {};
|
|
19500
19586
|
const currentDid = (0, import_sdk_core5.pkhDid)(address, chainId);
|
|
19587
|
+
if (this._nodeFeatures.includes("encryption-named-keys-v1")) {
|
|
19588
|
+
const siweDomain = this.siweConfig?.domain ?? this.domain;
|
|
19589
|
+
for (const manifest of request.manifests) {
|
|
19590
|
+
if (!appKeyHostBinding(siweDomain, manifest.keys_app_id ?? manifest.app_id)) continue;
|
|
19591
|
+
for (const declaration of manifest.keys ?? []) {
|
|
19592
|
+
const path = "path" in declaration ? declaration.path : declaration.prefix;
|
|
19593
|
+
const resource = `urn:tinycloud:encryption:${currentDid}:default/${path}`;
|
|
19594
|
+
for (const ability of declaration.abilities) {
|
|
19595
|
+
addRawAbility(rawAbilities, resource, `tinycloud.encryption/${ability}`);
|
|
19596
|
+
}
|
|
19597
|
+
}
|
|
19598
|
+
}
|
|
19599
|
+
}
|
|
19501
19600
|
const spaceResources = request.resources.filter((entry) => {
|
|
19502
19601
|
if (entry.service !== import_sdk_core5.ENCRYPTION_PERMISSION_SERVICE) {
|
|
19503
19602
|
return true;
|
|
19504
19603
|
}
|
|
19604
|
+
const parsed = entry.path.includes("/") ? (0, import_resource.parseEncryptionResource)(entry.path) : { ...(0, import_sdk_services3.parseNetworkId)(entry.path), kind: "network" };
|
|
19605
|
+
if (parsed.kind !== "network") {
|
|
19606
|
+
throw new import_sdk_core5.ManifestValidationError("named keys go in manifest.keys");
|
|
19607
|
+
}
|
|
19505
19608
|
for (const action of entry.actions) {
|
|
19506
19609
|
addRawAbility(rawAbilities, entry.path, action);
|
|
19507
19610
|
}
|
|
19508
|
-
if (entry.actions.includes(DECRYPT_ACTION)) {
|
|
19509
|
-
const parsed = (0, import_sdk_core5.parseNetworkId)(entry.path);
|
|
19611
|
+
if (entry.actions.includes(DECRYPT_ACTION) && parsed.kind === "network") {
|
|
19510
19612
|
if (didPrincipalMatches(parsed.ownerDid, currentDid)) {
|
|
19511
19613
|
addRawAbility(rawAbilities, entry.path, NETWORK_CREATE_ACTION);
|
|
19512
19614
|
}
|
|
@@ -19902,6 +20004,8 @@ var NodeUserAuthorization = class {
|
|
|
19902
20004
|
this._chainId = chainId;
|
|
19903
20005
|
await this.resolveTinyCloudHostsForSignIn(address, chainId);
|
|
19904
20006
|
this.assertSignInCurrent(generation);
|
|
20007
|
+
await this.refreshNamedKeyFeatureForSignIn(address, chainId);
|
|
20008
|
+
this.assertSignInCurrent(generation);
|
|
19905
20009
|
const keyId = `session-${Date.now()}`;
|
|
19906
20010
|
this.sessionManager.renameSessionKeyId(this.activeSessionKeyId, keyId);
|
|
19907
20011
|
this.activeSessionKeyId = keyId;
|
|
@@ -20048,9 +20152,9 @@ var NodeUserAuthorization = class {
|
|
|
20048
20152
|
refuseCaveated(this.getCapabilityRequest()?.manifests);
|
|
20049
20153
|
const address = (0, import_sdk_core5.canonicalizeAddress)(await this.signer.getAddress());
|
|
20050
20154
|
const chainId = await this.signer.getChainId();
|
|
20051
|
-
|
|
20052
|
-
|
|
20053
|
-
this.
|
|
20155
|
+
await this.refreshNamedKeyFeatureForSignIn(address, chainId);
|
|
20156
|
+
const keyId = `openkey-session-${Date.now()}-${++this.preparedSessionCounter}`;
|
|
20157
|
+
this.sessionManager.createSessionKey(keyId);
|
|
20054
20158
|
const jwkString = this.sessionManager.jwk(keyId);
|
|
20055
20159
|
if (!jwkString) {
|
|
20056
20160
|
throw new Error("Failed to create session key");
|
|
@@ -20131,6 +20235,7 @@ var NodeUserAuthorization = class {
|
|
|
20131
20235
|
await this.sessionStorage.save(address, persistedData);
|
|
20132
20236
|
this._session = clientSession;
|
|
20133
20237
|
this._tinyCloudSession = tinyCloudSession;
|
|
20238
|
+
this.activeSessionKeyId = keyId;
|
|
20134
20239
|
this._address = address;
|
|
20135
20240
|
this._chainId = chainId;
|
|
20136
20241
|
await this.refreshNodeInfo();
|
|
@@ -20177,6 +20282,11 @@ var NodeUserAuthorization = class {
|
|
|
20177
20282
|
* @param options Optional overrides for the OpenKey request.
|
|
20178
20283
|
*/
|
|
20179
20284
|
async signInWithOpenKey(authorizeFn, options) {
|
|
20285
|
+
const pending = await this.prepareOpenKeySignIn(options);
|
|
20286
|
+
return this.completeOpenKeySignIn(pending, authorizeFn(pending.request), options);
|
|
20287
|
+
}
|
|
20288
|
+
/** Finish address, host and SIWE preparation before an approval click. */
|
|
20289
|
+
async prepareOpenKeySignIn(options) {
|
|
20180
20290
|
refuseCaveated(this.getCapabilityRequest()?.manifests);
|
|
20181
20291
|
if (options?.host !== void 0) {
|
|
20182
20292
|
const overrideHost = options.host.trim();
|
|
@@ -20188,7 +20298,7 @@ var NodeUserAuthorization = class {
|
|
|
20188
20298
|
const signingAddress = (0, import_sdk_core5.canonicalizeAddress)(await this.signer.getAddress());
|
|
20189
20299
|
const signingChainId = await this.signer.getChainId();
|
|
20190
20300
|
await this.resolveTinyCloudHostsForSignIn(signingAddress, signingChainId);
|
|
20191
|
-
const { prepared, keyId, jwk } = await this.prepareSessionForSigning();
|
|
20301
|
+
const { prepared, keyId, jwk, address, chainId } = await this.prepareSessionForSigning();
|
|
20192
20302
|
const presentation = this.buildPresentationEnvelope(options?.reason);
|
|
20193
20303
|
const hostHint = this.primaryTinyCloudHost;
|
|
20194
20304
|
const originalRequest = {
|
|
@@ -20200,12 +20310,32 @@ var NodeUserAuthorization = class {
|
|
|
20200
20310
|
keyId: options?.openkeyKeyId,
|
|
20201
20311
|
presentation
|
|
20202
20312
|
};
|
|
20313
|
+
return {
|
|
20314
|
+
request: originalRequest,
|
|
20315
|
+
prepared,
|
|
20316
|
+
keyId,
|
|
20317
|
+
jwk,
|
|
20318
|
+
address,
|
|
20319
|
+
chainId,
|
|
20320
|
+
generation: this.sessionGeneration,
|
|
20321
|
+
createdAt: Date.now()
|
|
20322
|
+
};
|
|
20323
|
+
}
|
|
20324
|
+
/** Complete a prepared approval. The caller starts authorization before awaiting this method. */
|
|
20325
|
+
async completeOpenKeySignIn(pending, authorization, options) {
|
|
20326
|
+
if (Date.now() - pending.createdAt > 5 * 6e4) {
|
|
20327
|
+
throw new Error("Prepared OpenKey sign-in expired; prepare again");
|
|
20328
|
+
}
|
|
20329
|
+
if (pending.generation !== this.sessionGeneration) {
|
|
20330
|
+
throw new Error("Prepared OpenKey sign-in expired after sign-out");
|
|
20331
|
+
}
|
|
20332
|
+
const { request: originalRequest, prepared, keyId, jwk } = pending;
|
|
20203
20333
|
let result;
|
|
20204
20334
|
let signed2;
|
|
20205
20335
|
let signature2;
|
|
20206
20336
|
for (let attempt = 0; ; ) {
|
|
20207
20337
|
try {
|
|
20208
|
-
result ?? (result = await
|
|
20338
|
+
result ?? (result = await authorization);
|
|
20209
20339
|
signed2 = await this.verifyOpenKeyResult(result, prepared);
|
|
20210
20340
|
signature2 = result.signature;
|
|
20211
20341
|
break;
|
|
@@ -21101,6 +21231,7 @@ var ROOT_DELEGATION_ACTIONS = [
|
|
|
21101
21231
|
import_sdk_core10.DUCKDB.ALL
|
|
21102
21232
|
];
|
|
21103
21233
|
var DEFAULT_SESSION_EXPIRATION_MS = import_sdk_core10.EXPIRY.SESSION_MS;
|
|
21234
|
+
var OWNER_REVOCATION_WINDOW_MS = 10 * 6e4;
|
|
21104
21235
|
function unifiedRootAttenuation(capabilities) {
|
|
21105
21236
|
const attenuation = {};
|
|
21106
21237
|
for (const capability of capabilities) {
|
|
@@ -21565,6 +21696,7 @@ function replicaRestoreRefusal(error) {
|
|
|
21565
21696
|
case "purge_pending":
|
|
21566
21697
|
case "logout_pending":
|
|
21567
21698
|
case "gate_unavailable":
|
|
21699
|
+
case "wallet_provider_required":
|
|
21568
21700
|
return error.gate;
|
|
21569
21701
|
}
|
|
21570
21702
|
}
|
|
@@ -21597,8 +21729,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21597
21729
|
constructor(config = {}) {
|
|
21598
21730
|
this.signer = null;
|
|
21599
21731
|
this.auth = null;
|
|
21732
|
+
this.consumedOpenKeyPendings = /* @__PURE__ */ new WeakSet();
|
|
21600
21733
|
this.tc = null;
|
|
21601
21734
|
this._chainId = 1;
|
|
21735
|
+
this.replicaPurgeFailedHandlers = /* @__PURE__ */ new Set();
|
|
21602
21736
|
/** Mints the replica session stamp on each fresh sign-in (TC-858 §4.1). Undefined when replication is off. */
|
|
21603
21737
|
this.establishReplicaStamp = async (session) => {
|
|
21604
21738
|
const replication = this.config.replication;
|
|
@@ -21630,6 +21764,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21630
21764
|
return void 0;
|
|
21631
21765
|
}
|
|
21632
21766
|
};
|
|
21767
|
+
this._keysUnavailable = [];
|
|
21633
21768
|
this._baseVaults = /* @__PURE__ */ new Map();
|
|
21634
21769
|
this._baseSecrets = /* @__PURE__ */ new Map();
|
|
21635
21770
|
this._secrets = /* @__PURE__ */ new Map();
|
|
@@ -21805,6 +21940,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21805
21940
|
return instance ? instance.control.purge(input) : runtime.then((ready) => ready.control.purge(input));
|
|
21806
21941
|
},
|
|
21807
21942
|
clearPending: async () => (await runtime).control.clearPending(),
|
|
21943
|
+
purgeLocalCopy: async () => (await this.eraseReplicaLocalCopy("purge_local_copy"))?.purge ?? { ok: false, retryNow: false },
|
|
21808
21944
|
close: async () => (await runtime).control.close()
|
|
21809
21945
|
};
|
|
21810
21946
|
}
|
|
@@ -21859,18 +21995,14 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21859
21995
|
get freshEncryptionNetworkCreated() {
|
|
21860
21996
|
return this._freshEncryptionNetworkCreated;
|
|
21861
21997
|
}
|
|
21862
|
-
/**
|
|
21863
|
-
* Subscribe to `storage.full`. It fires on the first write any service had
|
|
21864
|
-
* rejected because the owner's TinyCloud storage is full
|
|
21865
|
-
* (`STORAGE_QUOTA_EXCEEDED` or `STORAGE_LIMIT_REACHED`), so an app can show
|
|
21866
|
-
* one read-only banner. Reads keep working. For the signed-in account's own
|
|
21867
|
-
* storage it fires once per episode: the episode ends only when a write to
|
|
21868
|
-
* that storage succeeds, or on sign-out. A `storage.status()` read does not
|
|
21869
|
-
* end it. A rejection on another owner's delegated space fires once per
|
|
21870
|
-
* owner, until that owner's `storage.status()` reports space again. Returns
|
|
21871
|
-
* the unsubscribe function.
|
|
21872
|
-
*/
|
|
21873
21998
|
on(event, handler) {
|
|
21999
|
+
if (event === "replica.purge.failed") {
|
|
22000
|
+
const listener = handler;
|
|
22001
|
+
this.replicaPurgeFailedHandlers.add(listener);
|
|
22002
|
+
return () => {
|
|
22003
|
+
this.replicaPurgeFailedHandlers.delete(listener);
|
|
22004
|
+
};
|
|
22005
|
+
}
|
|
21874
22006
|
if (event !== "storage.full") {
|
|
21875
22007
|
throw new Error(`Unknown TinyCloud event: ${String(event)}`);
|
|
21876
22008
|
}
|
|
@@ -22351,6 +22483,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22351
22483
|
const leavingAuth = this.auth;
|
|
22352
22484
|
const leavingContext = this._serviceContext;
|
|
22353
22485
|
let denied;
|
|
22486
|
+
let erased;
|
|
22354
22487
|
try {
|
|
22355
22488
|
this.teardownSession();
|
|
22356
22489
|
} finally {
|
|
@@ -22361,6 +22494,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22361
22494
|
try {
|
|
22362
22495
|
await this.accountRegistryTail;
|
|
22363
22496
|
await this.quiesceReplicaRuntime();
|
|
22497
|
+
if (denied) erased = await this.eraseReplicaLocalCopy("sign_out", denied.status);
|
|
22364
22498
|
} finally {
|
|
22365
22499
|
if (leavingAddress) {
|
|
22366
22500
|
await (leavingAuth ? leavingAuth.clearPersistedSession(leavingAddress) : this.config.sessionStorage?.clear(leavingAddress));
|
|
@@ -22368,8 +22502,37 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22368
22502
|
}
|
|
22369
22503
|
}
|
|
22370
22504
|
}
|
|
22371
|
-
if (denied?.status === "unguarded") {
|
|
22372
|
-
throw
|
|
22505
|
+
if (denied?.status === "unguarded" && erased?.outcome.status !== "erased") {
|
|
22506
|
+
throw Object.assign(
|
|
22507
|
+
new Error(`Replica gate was not denied (${denied.code ?? "unknown"}); the replica may still be restorable.`),
|
|
22508
|
+
{
|
|
22509
|
+
code: "REPLICA_PURGE_FAILED",
|
|
22510
|
+
reason: "gate_unavailable",
|
|
22511
|
+
retryNow: erased?.outcome.status === "failed"
|
|
22512
|
+
}
|
|
22513
|
+
);
|
|
22514
|
+
}
|
|
22515
|
+
}
|
|
22516
|
+
/**
|
|
22517
|
+
* Erases the browser local copy after a deny (retry, event and next-load marker live in the lazy
|
|
22518
|
+
* replication authority). Never rejects; `undefined` when replication is off or the storage keeps
|
|
22519
|
+
* no browser database.
|
|
22520
|
+
*/
|
|
22521
|
+
async eraseReplicaLocalCopy(trigger, denyStatus) {
|
|
22522
|
+
const replication = this.config.replication;
|
|
22523
|
+
if (!replication?.enabled) return void 0;
|
|
22524
|
+
try {
|
|
22525
|
+
const { eraseReplicaLocalCopy: eraseReplicaLocalCopy2 } = await getNodeReplicationLoaders().authority();
|
|
22526
|
+
const erased = await eraseReplicaLocalCopy2(
|
|
22527
|
+
replication.storage.lifecycle,
|
|
22528
|
+
trigger,
|
|
22529
|
+
denyStatus,
|
|
22530
|
+
this.replicaPurgeFailedHandlers
|
|
22531
|
+
);
|
|
22532
|
+
if (erased?.logoutPending) this.replicaRestoreGate = "logout_pending";
|
|
22533
|
+
return erased;
|
|
22534
|
+
} catch {
|
|
22535
|
+
return void 0;
|
|
22373
22536
|
}
|
|
22374
22537
|
}
|
|
22375
22538
|
/**
|
|
@@ -22429,6 +22592,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22429
22592
|
this._hooks = void 0;
|
|
22430
22593
|
this._vault = void 0;
|
|
22431
22594
|
this._encryption = void 0;
|
|
22595
|
+
this._namedKeyClient = void 0;
|
|
22596
|
+
this._keysUnavailable = [];
|
|
22432
22597
|
this._baseVaults.clear();
|
|
22433
22598
|
this._baseSecrets.clear();
|
|
22434
22599
|
this._secrets.clear();
|
|
@@ -22505,6 +22670,37 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22505
22670
|
}
|
|
22506
22671
|
await this.signInWith(() => this.auth.signInWithOpenKey(authorizeFn, options));
|
|
22507
22672
|
}
|
|
22673
|
+
/** Prepare the OpenKey request before the approval click. */
|
|
22674
|
+
async prepareOpenKeySignIn() {
|
|
22675
|
+
if (!this.signer || !this.tc || !this.auth) {
|
|
22676
|
+
throw new Error("Cannot prepare OpenKey sign-in in session-only mode. Provide a signer in config.");
|
|
22677
|
+
}
|
|
22678
|
+
await this.signOutInFlight;
|
|
22679
|
+
await this.accountRegistryTail;
|
|
22680
|
+
await this.wasmBindings.ensureInitialized?.();
|
|
22681
|
+
return this.auth.prepareOpenKeySignIn();
|
|
22682
|
+
}
|
|
22683
|
+
/** Start OpenKey authorization synchronously, then complete normal session setup. */
|
|
22684
|
+
signInWithPreparedOpenKey(pending, authorizeFn) {
|
|
22685
|
+
if (!this.signer || !this.tc || !this.auth || this.signOutInFlight) {
|
|
22686
|
+
throw new Error("Prepared OpenKey sign-in is unavailable");
|
|
22687
|
+
}
|
|
22688
|
+
if (this.consumedOpenKeyPendings.has(pending)) {
|
|
22689
|
+
throw new Error("Prepared OpenKey sign-in was already used");
|
|
22690
|
+
}
|
|
22691
|
+
if (Date.now() - pending.createdAt > 5 * 6e4) {
|
|
22692
|
+
throw new Error("Prepared OpenKey sign-in expired; prepare again");
|
|
22693
|
+
}
|
|
22694
|
+
this.consumedOpenKeyPendings.add(pending);
|
|
22695
|
+
const authorization = authorizeFn(pending.request);
|
|
22696
|
+
void authorization.catch(() => void 0);
|
|
22697
|
+
return this.signInWith(() => {
|
|
22698
|
+
if (this._address !== pending.address || this._chainId !== pending.chainId) {
|
|
22699
|
+
throw new Error("OpenKey account changed after sign-in preparation");
|
|
22700
|
+
}
|
|
22701
|
+
return this.auth.completeOpenKeySignIn(pending, authorization);
|
|
22702
|
+
});
|
|
22703
|
+
}
|
|
22508
22704
|
async signInWith(performSignIn) {
|
|
22509
22705
|
refuseCaveated(this.auth?.capabilityRequest?.manifests);
|
|
22510
22706
|
const epoch = this.sessionEpoch;
|
|
@@ -22533,6 +22729,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22533
22729
|
const priorPrincipal = this.replicaSessionPrincipal ?? (this.address ? (0, import_sdk_core10.pkhDid)(this.address, this._chainId) : void 0);
|
|
22534
22730
|
if (this.config.replication?.enabled && priorPrincipal && priorPrincipal !== nextPrincipal) {
|
|
22535
22731
|
const denial = await this.denyReplicaGate({ reason: "account_switch", principal: priorPrincipal, next: nextPrincipal });
|
|
22732
|
+
if (denial) {
|
|
22733
|
+
await this.quiesceReplicaRuntime();
|
|
22734
|
+
await this.eraseReplicaLocalCopy("account_switch", denial.status);
|
|
22735
|
+
}
|
|
22536
22736
|
this.replicaRestoreGate = denial?.status === "unguarded" ? "gate_unavailable" : "stamp_stale";
|
|
22537
22737
|
}
|
|
22538
22738
|
if (address !== this._address || chainId !== this._chainId) {
|
|
@@ -22547,6 +22747,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22547
22747
|
this._hooks = void 0;
|
|
22548
22748
|
this._vault = void 0;
|
|
22549
22749
|
this._encryption = void 0;
|
|
22750
|
+
this._namedKeyClient = void 0;
|
|
22751
|
+
this._keysUnavailable = [];
|
|
22550
22752
|
this._baseVaults.clear();
|
|
22551
22753
|
this._baseSecrets.clear();
|
|
22552
22754
|
this._secrets.clear();
|
|
@@ -22599,6 +22801,26 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22599
22801
|
this.assertSessionEpoch(epoch, "Sign-in");
|
|
22600
22802
|
await this.ensureRequestedEncryptionNetworks();
|
|
22601
22803
|
this.assertSessionEpoch(epoch, "Sign-in");
|
|
22804
|
+
this._keysUnavailable = this.auth?.unavailableKeyPaths ?? [];
|
|
22805
|
+
if (this._keysUnavailable.length > 0) {
|
|
22806
|
+
this.notificationHandler.warning(`Named keys unavailable: ${this._keysUnavailable.join(", ")}`);
|
|
22807
|
+
}
|
|
22808
|
+
if (this.nodeFeatures.includes("encryption-named-keys-v1")) {
|
|
22809
|
+
for (const manifest of this.auth?.capabilityRequest?.manifests ?? []) {
|
|
22810
|
+
for (const declaration of manifest.keys ?? []) {
|
|
22811
|
+
if ("path" in declaration && declaration.abilities.includes("key.create") && !this._keysUnavailable.includes(declaration.path)) {
|
|
22812
|
+
try {
|
|
22813
|
+
await this.keys.ensureKey(declaration.path);
|
|
22814
|
+
} catch (error) {
|
|
22815
|
+
this._keysUnavailable.push(declaration.path);
|
|
22816
|
+
const code = error && typeof error === "object" && "code" in error && typeof error.code === "string" && /^[A-Z_]+$/.test(error.code) ? error.code : "UNKNOWN";
|
|
22817
|
+
this.notificationHandler.warning(`Named key unavailable: ${declaration.path} (${code})`);
|
|
22818
|
+
}
|
|
22819
|
+
this.assertSessionEpoch(epoch, "Sign-in");
|
|
22820
|
+
}
|
|
22821
|
+
}
|
|
22822
|
+
}
|
|
22823
|
+
}
|
|
22602
22824
|
if (!bootstrapped && this.config.manifest === void 0 && this.config.capabilityRequest === void 0) {
|
|
22603
22825
|
await this.ensureOwnedSpaceHostedById(this.ownedSpaceId(import_sdk_core10.SECRETS_SPACE));
|
|
22604
22826
|
}
|
|
@@ -23175,7 +23397,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23175
23397
|
if (!this.currentSessionCoversAccountRegistry()) {
|
|
23176
23398
|
if (!_TinyCloudNode.accountRegistrySkipLogged) {
|
|
23177
23399
|
_TinyCloudNode.accountRegistrySkipLogged = true;
|
|
23178
|
-
|
|
23400
|
+
import_sdk_services4.tinyCloudDebugLogger.log("registry.skip");
|
|
23179
23401
|
}
|
|
23180
23402
|
return;
|
|
23181
23403
|
}
|
|
@@ -23386,7 +23608,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23386
23608
|
const networkIds = /* @__PURE__ */ new Set();
|
|
23387
23609
|
for (const resource of request.resources) {
|
|
23388
23610
|
if (resource.service === import_sdk_core10.ENCRYPTION_PERMISSION_SERVICE && resource.path.startsWith("urn:tinycloud:encryption:") && resource.actions.includes(DECRYPT_ACTION2)) {
|
|
23389
|
-
|
|
23611
|
+
const parsed = resource.path.includes("/") ? (0, import_resource2.parseEncryptionResource)(resource.path) : { ...(0, import_sdk_core10.parseNetworkId)(resource.path), kind: "network" };
|
|
23612
|
+
if (parsed.kind === "network") networkIds.add(resource.path);
|
|
23390
23613
|
}
|
|
23391
23614
|
}
|
|
23392
23615
|
return [...networkIds];
|
|
@@ -23936,6 +24159,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23936
24159
|
this._hooks = stagedGraph.hooks;
|
|
23937
24160
|
this._vault = stagedGraph.vault;
|
|
23938
24161
|
this._encryption = stagedGraph.encryption;
|
|
24162
|
+
this._namedKeyClient = void 0;
|
|
24163
|
+
this._keysUnavailable = [];
|
|
23939
24164
|
this._capabilityRegistry = stagedGraph.capabilityRegistry;
|
|
23940
24165
|
this._keyProvider = stagedGraph.keyProvider;
|
|
23941
24166
|
this._sharingService = stagedGraph.sharingService;
|
|
@@ -24845,7 +25070,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
24845
25070
|
);
|
|
24846
25071
|
graph.assertActive();
|
|
24847
25072
|
if (!response.ok) {
|
|
24848
|
-
const permissionHint = response.status === 401 || response.status === 403 ? (0,
|
|
25073
|
+
const permissionHint = response.status === 401 || response.status === 403 ? (0, import_sdk_services4.parsePermissionHint)({
|
|
24849
25074
|
service: "tinycloud.encryption",
|
|
24850
25075
|
path: networkId,
|
|
24851
25076
|
actions: ["tinycloud.encryption/decrypt"]
|
|
@@ -25289,7 +25514,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
25289
25514
|
* Sign a wallet (CACAO) revocation of the delegation `cid` for the node's
|
|
25290
25515
|
* `POST /revoke`, and return its Authorization header value. The node
|
|
25291
25516
|
* accepts it when the owner delegated `cid` or owns a space its abilities
|
|
25292
|
-
* target, whichever key issued it. Valid for
|
|
25517
|
+
* target, whichever key issued it. Valid for 10 minutes. Requires the
|
|
25293
25518
|
* owner wallet signer.
|
|
25294
25519
|
*/
|
|
25295
25520
|
async signOwnerRevocation(cid) {
|
|
@@ -25305,7 +25530,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
25305
25530
|
chainId: session.chainId,
|
|
25306
25531
|
nonce: (0, import_siwe2.generateNonce)(),
|
|
25307
25532
|
issuedAt: issuedAt.toISOString(),
|
|
25308
|
-
expirationTime: new Date(issuedAt.getTime() +
|
|
25533
|
+
expirationTime: new Date(issuedAt.getTime() + OWNER_REVOCATION_WINDOW_MS).toISOString()
|
|
25309
25534
|
}).prepareMessage();
|
|
25310
25535
|
const signature2 = await this.signer.signMessage(siwe);
|
|
25311
25536
|
return this.wasmBindings.siweToDelegationHeaders({ siwe, signature: signature2 }).Authorization;
|
|
@@ -25532,6 +25757,40 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
25532
25757
|
get encryption() {
|
|
25533
25758
|
return this.getEncryptionService();
|
|
25534
25759
|
}
|
|
25760
|
+
/** Named keys bound to the current session. */
|
|
25761
|
+
get keys() {
|
|
25762
|
+
return new Proxy({}, {
|
|
25763
|
+
get: (_target, property) => {
|
|
25764
|
+
if (property === "refreshNodeInfo") {
|
|
25765
|
+
return () => {
|
|
25766
|
+
void this._namedKeyClient?.then((client) => client.refreshNodeInfo());
|
|
25767
|
+
};
|
|
25768
|
+
}
|
|
25769
|
+
if (typeof property !== "string" || ![
|
|
25770
|
+
"getPublicKey",
|
|
25771
|
+
"describeKey",
|
|
25772
|
+
"createKey",
|
|
25773
|
+
"blockKey",
|
|
25774
|
+
"unblockKey",
|
|
25775
|
+
"ensureKey",
|
|
25776
|
+
"encrypt",
|
|
25777
|
+
"decrypt",
|
|
25778
|
+
"decryptMany"
|
|
25779
|
+
].includes(property)) return void 0;
|
|
25780
|
+
return (...args) => {
|
|
25781
|
+
this._namedKeyClient ?? (this._namedKeyClient = import("./encryption-keys.cjs").then(({ createNamedKeyClient }) => createNamedKeyClient(this)));
|
|
25782
|
+
return this._namedKeyClient.then((client) => {
|
|
25783
|
+
const method = client[property];
|
|
25784
|
+
return method.apply(client, args);
|
|
25785
|
+
});
|
|
25786
|
+
};
|
|
25787
|
+
}
|
|
25788
|
+
});
|
|
25789
|
+
}
|
|
25790
|
+
/** Manifest key paths unavailable during the latest `signIn()` attempt. */
|
|
25791
|
+
get keysUnavailable() {
|
|
25792
|
+
return this._keysUnavailable;
|
|
25793
|
+
}
|
|
25535
25794
|
async getEncryptionNetwork(nameOrNetworkId = this.getDefaultEncryptionNetworkId()) {
|
|
25536
25795
|
const networkId = nameOrNetworkId.startsWith("urn:tinycloud:encryption:") ? nameOrNetworkId : this.getDefaultEncryptionNetworkId(nameOrNetworkId);
|
|
25537
25796
|
return this.fetchEncryptionNetworkAt(
|
|
@@ -25659,7 +25918,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
25659
25918
|
);
|
|
25660
25919
|
if (result.status !== "ok") {
|
|
25661
25920
|
if (result.status === "permission_required") {
|
|
25662
|
-
const hint = (0,
|
|
25921
|
+
const hint = (0, import_sdk_services4.parsePermissionHint)(result.hint);
|
|
25663
25922
|
if (hint === void 0) return { status: "read_failed" };
|
|
25664
25923
|
return {
|
|
25665
25924
|
status: "permission_required",
|
|
@@ -27940,7 +28199,7 @@ var FileSessionStorage = class {
|
|
|
27940
28199
|
};
|
|
27941
28200
|
|
|
27942
28201
|
// src/authorization/openKeyBridge.ts
|
|
27943
|
-
function wireOpenKeyAuthorize(openkey) {
|
|
28202
|
+
function wireOpenKeyAuthorize(openkey, opts) {
|
|
27944
28203
|
return async (input) => {
|
|
27945
28204
|
const result = await openkey.authorizeTinyCloud({
|
|
27946
28205
|
protocolVersion: 1,
|
|
@@ -27951,7 +28210,7 @@ function wireOpenKeyAuthorize(openkey) {
|
|
|
27951
28210
|
// Forward the presentation envelope verbatim — the OpenKey SDK
|
|
27952
28211
|
// validates and size-bounds it before use.
|
|
27953
28212
|
presentation: input.presentation
|
|
27954
|
-
});
|
|
28213
|
+
}, opts);
|
|
27955
28214
|
if (result.protocolVersion !== 1) {
|
|
27956
28215
|
throw new Error(
|
|
27957
28216
|
`wireOpenKeyAuthorize: OpenKey returned unsupported protocolVersion ${result.protocolVersion}`
|
|
@@ -28004,7 +28263,7 @@ var import_node_fs = require("fs");
|
|
|
28004
28263
|
var import_promises = require("fs/promises");
|
|
28005
28264
|
var import_node_path = require("path");
|
|
28006
28265
|
var import_sdk_core14 = require("@tinycloud/sdk-core");
|
|
28007
|
-
var
|
|
28266
|
+
var import_sdk_services5 = require("@tinycloud/sdk-services");
|
|
28008
28267
|
var import_contractConstants = require("@tinycloud/sdk-services/kv/replication/contractConstants");
|
|
28009
28268
|
init_authority();
|
|
28010
28269
|
var REPLICA_LOGOUT_PENDING_FILE = "replica-logout-pending.json";
|
|
@@ -28566,7 +28825,7 @@ function sha256Base32(material) {
|
|
|
28566
28825
|
return out.slice(0, 26);
|
|
28567
28826
|
}
|
|
28568
28827
|
function partitionDirOf(root, identity) {
|
|
28569
|
-
return (0, import_node_path.join)(root, sha256Base32((0,
|
|
28828
|
+
return (0, import_node_path.join)(root, sha256Base32((0, import_sdk_services5.replicationIdentityKey)(identity)));
|
|
28570
28829
|
}
|
|
28571
28830
|
function replicaHashOf(prefix, deviceDid) {
|
|
28572
28831
|
return sha256Base32(`${prefix}
|
|
@@ -28640,8 +28899,8 @@ function deviceRecordOf(value) {
|
|
|
28640
28899
|
}
|
|
28641
28900
|
function sameIdentity(a, b) {
|
|
28642
28901
|
try {
|
|
28643
|
-
const left = (0,
|
|
28644
|
-
const right = (0,
|
|
28902
|
+
const left = (0, import_sdk_services5.canonicalReplicationIdentity)(a);
|
|
28903
|
+
const right = (0, import_sdk_services5.canonicalReplicationIdentity)(b);
|
|
28645
28904
|
return left.host === right.host && left.space === right.space && left.principal === right.principal;
|
|
28646
28905
|
} catch {
|
|
28647
28906
|
return false;
|
|
@@ -28657,7 +28916,7 @@ async function ensurePartitionIdentity(dir, identity, dirSync) {
|
|
|
28657
28916
|
const file = (0, import_node_path.join)(dir, "identity.json");
|
|
28658
28917
|
const existing = await readJsonFile(file);
|
|
28659
28918
|
if (existing === void 0) {
|
|
28660
|
-
await writeJsonAtomic(file, { v: 1, ...(0,
|
|
28919
|
+
await writeJsonAtomic(file, { v: 1, ...(0, import_sdk_services5.canonicalReplicationIdentity)(identity) }, dirSync);
|
|
28661
28920
|
return;
|
|
28662
28921
|
}
|
|
28663
28922
|
const record = identityRecordOf(existing);
|
|
@@ -28756,7 +29015,7 @@ withGate_fn = async function(section) {
|
|
|
28756
29015
|
if (snapshot === void 0) throw gated("gate_unavailable");
|
|
28757
29016
|
const [principalHash, identityKeyHash] = await Promise.all([
|
|
28758
29017
|
gate.principalHashOf(snapshot.salt, this.identity.principal),
|
|
28759
|
-
gate.identityHashOf(snapshot.salt, (0,
|
|
29018
|
+
gate.identityHashOf(snapshot.salt, (0, import_sdk_services5.replicationIdentityKey)(this.identity))
|
|
28760
29019
|
]);
|
|
28761
29020
|
const access = {
|
|
28762
29021
|
stamp: __privateGet(this, _stamp),
|
|
@@ -29027,7 +29286,8 @@ var SqliteReplicaHandle = class {
|
|
|
29027
29286
|
options.signal.addEventListener("abort", onAbort, { once: true });
|
|
29028
29287
|
const engine = new (__privateGet(this, _replica)).Replica({
|
|
29029
29288
|
store: __privateGet(this, _store),
|
|
29030
|
-
transportFor
|
|
29289
|
+
transportFor,
|
|
29290
|
+
...this.spec.contentFetch === void 0 ? {} : { contentFetch: this.spec.contentFetch }
|
|
29031
29291
|
});
|
|
29032
29292
|
const running = engine.sync({ signal: abort.signal });
|
|
29033
29293
|
const registration = { promise: running, abort: () => abort.abort() };
|
|
@@ -29256,7 +29516,7 @@ function createSqliteReplicaStorage(options) {
|
|
|
29256
29516
|
if (snapshot === void 0) throw gated("gate_unavailable");
|
|
29257
29517
|
const [principalHash, identityKeyHash] = await Promise.all([
|
|
29258
29518
|
gate.principalHashOf(snapshot.salt, principal),
|
|
29259
|
-
identity === void 0 ? Promise.resolve(void 0) : gate.identityHashOf(snapshot.salt, (0,
|
|
29519
|
+
identity === void 0 ? Promise.resolve(void 0) : gate.identityHashOf(snapshot.salt, (0, import_sdk_services5.replicationIdentityKey)(identity))
|
|
29260
29520
|
]);
|
|
29261
29521
|
return profileSection(async () => {
|
|
29262
29522
|
assertLogoutClear(options.logoutPendingFile);
|
|
@@ -29279,7 +29539,7 @@ function createSqliteReplicaStorage(options) {
|
|
|
29279
29539
|
if (snapshot === void 0) throw gated("gate_unavailable");
|
|
29280
29540
|
const [principalHash, identityKeyHash] = await Promise.all([
|
|
29281
29541
|
gate.principalHashOf(snapshot.salt, spec.identity.principal),
|
|
29282
|
-
gate.identityHashOf(snapshot.salt, (0,
|
|
29542
|
+
gate.identityHashOf(snapshot.salt, (0, import_sdk_services5.replicationIdentityKey)(spec.identity))
|
|
29283
29543
|
]);
|
|
29284
29544
|
const access = {
|
|
29285
29545
|
stamp: spec.stamp,
|
|
@@ -29363,7 +29623,7 @@ function createSqliteReplicaStorage(options) {
|
|
|
29363
29623
|
const identity = identityRecordOf(await readJsonFile((0, import_node_path.join)(idDir, "identity.json")));
|
|
29364
29624
|
if (identity === void 0) throw new ReplicaStorageError("STORAGE_ERROR", `Missing identity at ${idDir}.`);
|
|
29365
29625
|
if (await gate.principalHashOf(salt, identity.principal) !== principalHash) continue;
|
|
29366
|
-
const identityKeyHash = await gate.identityHashOf(salt, (0,
|
|
29626
|
+
const identityKeyHash = await gate.identityHashOf(salt, (0, import_sdk_services5.replicationIdentityKey)(identity));
|
|
29367
29627
|
const recordedAt = Date.now();
|
|
29368
29628
|
if (await pathExists((0, import_node_path.join)(idDir, "pending.json")) || await pathExists((0, import_node_path.join)(idDir, "device.jwk"))) {
|
|
29369
29629
|
intents.push({ id: `pending:${identityKeyHash}`, kind: "pending", identityKeyHash, principalHash, recordedAt });
|
|
@@ -30041,7 +30301,7 @@ function createSqliteReplicaStorage(options) {
|
|
|
30041
30301
|
for (const dir of await identityDirs()) {
|
|
30042
30302
|
const identity = identityRecordOf(await readJsonFile((0, import_node_path.join)(dir, "identity.json")));
|
|
30043
30303
|
if (identity === void 0) throw new ReplicaStorageError("STORAGE_ERROR", `Missing identity at ${dir}.`);
|
|
30044
|
-
const hash3 = await gate.identityHashOf(state.salt, (0,
|
|
30304
|
+
const hash3 = await gate.identityHashOf(state.salt, (0, import_sdk_services5.replicationIdentityKey)(identity));
|
|
30045
30305
|
if (intent.kind === "pending" ? hash3 === intent.identityKeyHash : intent.partitionId.startsWith(`${hash3}:`)) {
|
|
30046
30306
|
idDir = dir;
|
|
30047
30307
|
break;
|
|
@@ -30187,7 +30447,7 @@ function createSqliteReplicaStorage(options) {
|
|
|
30187
30447
|
const gate = await fileGate();
|
|
30188
30448
|
const snapshot = await gate.readGateSnapshot(profileDir);
|
|
30189
30449
|
if (snapshot === void 0) throw gated("gate_unavailable");
|
|
30190
|
-
const identityKeyHash = await gate.identityHashOf(snapshot.salt, (0,
|
|
30450
|
+
const identityKeyHash = await gate.identityHashOf(snapshot.salt, (0, import_sdk_services5.replicationIdentityKey)(spec.identity));
|
|
30191
30451
|
const resolvePolicy = async (at, recordFirst) => {
|
|
30192
30452
|
const policy = spec.grantPolicy;
|
|
30193
30453
|
if (!Number.isSafeInteger(policy.grantTtlMs) || policy.grantTtlMs <= 0 || policy.grantTtlMs > import_contractConstants.MAX_GRANT_TTL_MS) {
|
|
@@ -30209,7 +30469,7 @@ function createSqliteReplicaStorage(options) {
|
|
|
30209
30469
|
let partitionId;
|
|
30210
30470
|
const storeGuard = (section) => guarded(spec.identity.principal, spec.stamp, spec.identity, partitionId, section);
|
|
30211
30471
|
return guarded(spec.identity.principal, spec.stamp, spec.identity, void 0, async () => {
|
|
30212
|
-
if ((0,
|
|
30472
|
+
if ((0, import_sdk_services5.requiresSecretsOptIn)(spec.space, spec.prefix) && spec.allowSecrets !== true) {
|
|
30213
30473
|
throw new replica.ReplicaError(
|
|
30214
30474
|
replica.ReplicaErrorCode.SECRETS_OPT_IN_REQUIRED,
|
|
30215
30475
|
`Replicating ${spec.space}/kv/${spec.prefix} copies secret material to disk; pass allowSecrets to opt in.`
|
|
@@ -30455,7 +30715,7 @@ function createSqliteReplicaStorage(options) {
|
|
|
30455
30715
|
const gate = await fileGate();
|
|
30456
30716
|
const currentGate = await gate.readGateSnapshot(profileDir);
|
|
30457
30717
|
if (currentGate !== void 0 && currentGate.principalHash !== null && currentGate.principalHash === await gate.principalHashOf(currentGate.salt, target.identity.principal)) {
|
|
30458
|
-
const identityKeyHash = await gate.identityHashOf(currentGate.salt, (0,
|
|
30718
|
+
const identityKeyHash = await gate.identityHashOf(currentGate.salt, (0, import_sdk_services5.replicationIdentityKey)(target.identity));
|
|
30459
30719
|
await retainGrantInstalls(gate, currentGate, identityKeyHash, store);
|
|
30460
30720
|
}
|
|
30461
30721
|
if (state.revoked === null) {
|