@tinycloud/node-sdk 3.8.0-beta.2 → 3.8.0-beta.20
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/README.md +6 -0
- package/dist/{TinyCloudNode-B6VOBrCb.d.ts → TinyCloudNode-BRyeZas2.d.ts} +47 -6
- package/dist/{TinyCloudNode-CVO41O12.d.cts → TinyCloudNode-NrmztjDd.d.cts} +47 -6
- package/dist/{WasmKeyProvider-5Ee7fcAw.d.cts → WasmKeyProvider-BKCPRC-c.d.cts} +21 -1
- package/dist/{WasmKeyProvider-5Ee7fcAw.d.ts → WasmKeyProvider-BKCPRC-c.d.ts} +21 -1
- package/dist/{authority-DGi1hIMK.d.ts → authority-D6JMJwor.d.ts} +1 -1
- package/dist/{authority-DgVV80bI.d.cts → authority-Z-reu6zL.d.cts} +1 -1
- package/dist/core.cjs +336 -29
- 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 +330 -24
- 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 -40
- 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 +293 -27
- package/dist/index.js.map +1 -1
- package/dist/replication/authority.cjs +3 -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 +3 -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.js +1 -0
- package/dist/replication/runtime.js.map +1 -1
- package/dist/share-receive.cjs +775 -0
- package/dist/share-receive.cjs.map +1 -0
- package/dist/share-receive.d.cts +32 -0
- package/dist/share-receive.d.ts +32 -0
- package/dist/share-receive.js +777 -0
- package/dist/share-receive.js.map +1 -0
- package/package.json +11 -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
|
}
|
|
@@ -17964,6 +17968,7 @@ __export(src_exports, {
|
|
|
17964
17968
|
hexDecode: () => import_sdk_core22.hexDecode,
|
|
17965
17969
|
hexEncode: () => import_sdk_core22.hexEncode,
|
|
17966
17970
|
installTinyCloudDebugGlobals: () => import_sdk_core16.installTinyCloudDebugGlobals,
|
|
17971
|
+
isAppKeyHostBound: () => isAppKeyHostBound,
|
|
17967
17972
|
isCapabilitySubset: () => import_sdk_core17.isCapabilitySubset,
|
|
17968
17973
|
isEvmAddress: () => import_sdk_core16.isEvmAddress,
|
|
17969
17974
|
isNetworkId: () => import_sdk_core22.isNetworkId,
|
|
@@ -18824,13 +18829,54 @@ function getNodeReplicationLoaders() {
|
|
|
18824
18829
|
|
|
18825
18830
|
// src/TinyCloudNode.ts
|
|
18826
18831
|
var import_sdk_core10 = require("@tinycloud/sdk-core");
|
|
18827
|
-
var
|
|
18832
|
+
var import_sdk_services4 = require("@tinycloud/sdk-services");
|
|
18828
18833
|
var import_requestLog2 = require("@tinycloud/sdk-services/telemetry/requestLog");
|
|
18834
|
+
var import_resource2 = require("@tinycloud/sdk-services/encryption/resource");
|
|
18829
18835
|
|
|
18830
18836
|
// src/authorization/NodeUserAuthorization.ts
|
|
18831
18837
|
var import_node_crypto = require("crypto");
|
|
18832
18838
|
var import_siwe = require("siwe");
|
|
18833
18839
|
var import_sdk_core5 = require("@tinycloud/sdk-core");
|
|
18840
|
+
var import_resource = require("@tinycloud/sdk-services/encryption/resource");
|
|
18841
|
+
var import_sdk_services3 = require("@tinycloud/sdk-services");
|
|
18842
|
+
|
|
18843
|
+
// src/authorization/appKeyHostBinding.ts
|
|
18844
|
+
var DOMAIN_LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
18845
|
+
var IPV4 = /^\d+\.\d+\.\d+\.\d+$/;
|
|
18846
|
+
var APP_ID = /^[a-z0-9][a-z0-9._-]{0,63}$/;
|
|
18847
|
+
function appKeyHostBinding(domain, appId) {
|
|
18848
|
+
if (!APP_ID.test(appId)) return false;
|
|
18849
|
+
if (domain.endsWith(":")) return false;
|
|
18850
|
+
let url;
|
|
18851
|
+
try {
|
|
18852
|
+
url = new URL(`https://${domain}`);
|
|
18853
|
+
} catch {
|
|
18854
|
+
return false;
|
|
18855
|
+
}
|
|
18856
|
+
if (url.protocol !== "https:" || url.port !== "" || url.username !== "" || url.password !== "") return false;
|
|
18857
|
+
if (url.pathname !== "/" || url.search !== "" || url.hash !== "") return false;
|
|
18858
|
+
const host = url.hostname;
|
|
18859
|
+
if (host.startsWith("[") || host.endsWith(".") || IPV4.test(host)) return false;
|
|
18860
|
+
const labels = host.split(".");
|
|
18861
|
+
if (labels.length < 2 || labels[labels.length - 1] === "localhost") return false;
|
|
18862
|
+
if (!labels.every((label) => DOMAIN_LABEL.test(label))) return false;
|
|
18863
|
+
const reversed = labels.reverse().join(".");
|
|
18864
|
+
return appId === reversed || appId.startsWith(`${reversed}.`);
|
|
18865
|
+
}
|
|
18866
|
+
function isAppKeyHostBound(keysAppId, origin) {
|
|
18867
|
+
if (typeof origin === "string" && !origin.includes("://")) {
|
|
18868
|
+
return appKeyHostBinding(origin, keysAppId);
|
|
18869
|
+
}
|
|
18870
|
+
let url;
|
|
18871
|
+
try {
|
|
18872
|
+
url = typeof origin === "string" ? new URL(origin) : origin;
|
|
18873
|
+
} catch {
|
|
18874
|
+
return false;
|
|
18875
|
+
}
|
|
18876
|
+
return appKeyHostBinding(url.host, keysAppId);
|
|
18877
|
+
}
|
|
18878
|
+
|
|
18879
|
+
// src/authorization/NodeUserAuthorization.ts
|
|
18834
18880
|
init_authority_sign_in();
|
|
18835
18881
|
|
|
18836
18882
|
// src/authorization/caveatRefusal.ts
|
|
@@ -18986,6 +19032,7 @@ function cloneAbilitiesMap(actions) {
|
|
|
18986
19032
|
var NodeUserAuthorization = class {
|
|
18987
19033
|
constructor(config) {
|
|
18988
19034
|
this.activeSessionKeyId = "default";
|
|
19035
|
+
this.preparedSessionCounter = 0;
|
|
18989
19036
|
/** Incremented by resetSession; a sign-in started earlier publishes nothing. */
|
|
18990
19037
|
this.sessionGeneration = 0;
|
|
18991
19038
|
/**
|
|
@@ -19342,6 +19389,23 @@ var NodeUserAuthorization = class {
|
|
|
19342
19389
|
);
|
|
19343
19390
|
return this._capabilityRequest;
|
|
19344
19391
|
}
|
|
19392
|
+
async refreshNamedKeyFeatureForSignIn(address, chainId) {
|
|
19393
|
+
const request = this.getCapabilityRequest();
|
|
19394
|
+
const siweDomain = this.siweConfig?.domain ?? this.domain;
|
|
19395
|
+
const hasNamedKeys = request?.manifests.some((manifest) => manifest.keys?.length && appKeyHostBinding(siweDomain, manifest.keys_app_id ?? manifest.app_id));
|
|
19396
|
+
if (hasNamedKeys) {
|
|
19397
|
+
await this.resolveTinyCloudHostsForSignIn(address, chainId);
|
|
19398
|
+
await this.refreshNodeInfo();
|
|
19399
|
+
}
|
|
19400
|
+
}
|
|
19401
|
+
/** Manifest key paths that this sign-in cannot grant. */
|
|
19402
|
+
get unavailableKeyPaths() {
|
|
19403
|
+
const request = this.getCapabilityRequest();
|
|
19404
|
+
const siweDomain = this.siweConfig?.domain ?? this.domain;
|
|
19405
|
+
return (request?.manifests ?? []).flatMap(
|
|
19406
|
+
(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)
|
|
19407
|
+
);
|
|
19408
|
+
}
|
|
19345
19409
|
resolveSpaceName(space, address, chainId) {
|
|
19346
19410
|
if (space.startsWith("tinycloud:")) {
|
|
19347
19411
|
return space;
|
|
@@ -19533,15 +19597,32 @@ var NodeUserAuthorization = class {
|
|
|
19533
19597
|
}
|
|
19534
19598
|
const rawAbilities = {};
|
|
19535
19599
|
const currentDid = (0, import_sdk_core5.pkhDid)(address, chainId);
|
|
19600
|
+
if (this._nodeFeatures.includes("encryption-named-keys-v1")) {
|
|
19601
|
+
const siweDomain = this.siweConfig?.domain ?? this.domain;
|
|
19602
|
+
for (const manifest of request.manifests) {
|
|
19603
|
+
if (!appKeyHostBinding(siweDomain, manifest.keys_app_id ?? manifest.app_id)) continue;
|
|
19604
|
+
for (const declaration of manifest.keys ?? []) {
|
|
19605
|
+
const path = "path" in declaration ? declaration.path : declaration.prefix;
|
|
19606
|
+
const resource = `urn:tinycloud:encryption:${currentDid}:default/${path}`;
|
|
19607
|
+
for (const ability of declaration.abilities) {
|
|
19608
|
+
addRawAbility(rawAbilities, resource, `tinycloud.encryption/${ability}`);
|
|
19609
|
+
}
|
|
19610
|
+
}
|
|
19611
|
+
}
|
|
19612
|
+
}
|
|
19536
19613
|
const spaceResources = request.resources.filter((entry) => {
|
|
19537
19614
|
if (entry.service !== import_sdk_core5.ENCRYPTION_PERMISSION_SERVICE) {
|
|
19538
19615
|
return true;
|
|
19539
19616
|
}
|
|
19617
|
+
const parsed = entry.path.includes("/") ? (0, import_resource.parseEncryptionResource)(entry.path) : { ...(0, import_sdk_services3.parseNetworkId)(entry.path), kind: "network" };
|
|
19618
|
+
const sharePrefix = parsed.kind === "prefix" && parsed.network === "default" && parsed.prefix === "share" && entry.actions.length === 1 && entry.actions[0] === DECRYPT_ACTION && didPrincipalMatches(parsed.ownerDid, currentDid);
|
|
19619
|
+
if (parsed.kind !== "network" && !sharePrefix) {
|
|
19620
|
+
throw new import_sdk_core5.ManifestValidationError("named keys go in manifest.keys");
|
|
19621
|
+
}
|
|
19540
19622
|
for (const action of entry.actions) {
|
|
19541
19623
|
addRawAbility(rawAbilities, entry.path, action);
|
|
19542
19624
|
}
|
|
19543
|
-
if (entry.actions.includes(DECRYPT_ACTION)) {
|
|
19544
|
-
const parsed = (0, import_sdk_core5.parseNetworkId)(entry.path);
|
|
19625
|
+
if (entry.actions.includes(DECRYPT_ACTION) && parsed.kind === "network") {
|
|
19545
19626
|
if (didPrincipalMatches(parsed.ownerDid, currentDid)) {
|
|
19546
19627
|
addRawAbility(rawAbilities, entry.path, NETWORK_CREATE_ACTION);
|
|
19547
19628
|
}
|
|
@@ -19578,8 +19659,7 @@ var NodeUserAuthorization = class {
|
|
|
19578
19659
|
/**
|
|
19579
19660
|
* Build SIWE overrides from the top-level nonce and siweConfig.
|
|
19580
19661
|
* - Top-level `nonce` is seeded first so `siweConfig.nonce` wins if both are set.
|
|
19581
|
-
* -
|
|
19582
|
-
* - resources are appended to the default resources
|
|
19662
|
+
* - resources are inserted after WASM preparation, before the final ReCap
|
|
19583
19663
|
* - uri triggers a warning (overwriting delegation target)
|
|
19584
19664
|
* - all other fields override directly
|
|
19585
19665
|
* - per-call nonce overrides siweConfig.nonce when provided
|
|
@@ -19597,9 +19677,6 @@ var NodeUserAuthorization = class {
|
|
|
19597
19677
|
if (statement) {
|
|
19598
19678
|
overrides.statement = this.statement ? `${statement} ${this.statement}` : statement;
|
|
19599
19679
|
}
|
|
19600
|
-
if (resources && resources.length > 0) {
|
|
19601
|
-
overrides.resources = resources;
|
|
19602
|
-
}
|
|
19603
19680
|
if (uri) {
|
|
19604
19681
|
console.warn(
|
|
19605
19682
|
"[tinycloud] siweConfig.uri is overwriting the delegation target URI. This may break delegation chain validation if the URI does not match the session key DID."
|
|
@@ -19611,6 +19688,29 @@ var NodeUserAuthorization = class {
|
|
|
19611
19688
|
}
|
|
19612
19689
|
return overrides;
|
|
19613
19690
|
}
|
|
19691
|
+
prepareSessionWithResources(params) {
|
|
19692
|
+
const resources = this.siweConfig?.resources;
|
|
19693
|
+
if (resources?.some((resource) => typeof resource !== "string" || !/^urn:tinycloud:recipients:[A-Za-z0-9_-]+$/.test(resource))) {
|
|
19694
|
+
throw new Error("SIWE metadata resources must be base64url recipients URIs");
|
|
19695
|
+
}
|
|
19696
|
+
const prepared = this.wasm.prepareSession(params);
|
|
19697
|
+
if (!resources?.length) return prepared;
|
|
19698
|
+
const recapLines = /^- urn:recap:[^\r\n]+$/gim;
|
|
19699
|
+
const recaps = prepared.siwe.match(recapLines);
|
|
19700
|
+
const recap = recaps?.[0];
|
|
19701
|
+
if (recaps?.length !== 1 || !prepared.siwe.endsWith(recap)) {
|
|
19702
|
+
throw new Error("Prepared SIWE must end with exactly one ReCap");
|
|
19703
|
+
}
|
|
19704
|
+
const insertedLines = resources.map((resource) => `- ${resource}`);
|
|
19705
|
+
const siwe = prepared.siwe.slice(0, -recap.length) + insertedLines.join("\n") + "\n" + recap;
|
|
19706
|
+
const expectedFields = (0, import_sdk_core5.extractImmutableSiweFields)(prepared.siwe);
|
|
19707
|
+
expectedFields.nonRecapResources = [expectedFields.nonRecapResources, ...insertedLines].filter(Boolean).join("\n");
|
|
19708
|
+
const actualRecaps = siwe.match(recapLines);
|
|
19709
|
+
if (actualRecaps?.length !== 1 || actualRecaps[0] !== recap || (0, import_sdk_core5.diffImmutableSiweFields)(expectedFields, (0, import_sdk_core5.extractImmutableSiweFields)(siwe)).length) {
|
|
19710
|
+
throw new Error("SIWE metadata insertion changed the prepared message");
|
|
19711
|
+
}
|
|
19712
|
+
return { ...prepared, siwe };
|
|
19713
|
+
}
|
|
19614
19714
|
/**
|
|
19615
19715
|
* Add an extension to the authorization flow.
|
|
19616
19716
|
*/
|
|
@@ -19858,7 +19958,7 @@ var NodeUserAuthorization = class {
|
|
|
19858
19958
|
spaceAbilities[spaceId] = (0, import_sdk_core5.resourceCapabilitiesToAbilitiesMap)(resources);
|
|
19859
19959
|
}
|
|
19860
19960
|
const abilities = spaceAbilities[options.spaceId] ?? (0, import_sdk_core5.resourceCapabilitiesToAbilitiesMap)([]);
|
|
19861
|
-
const prepared = this.
|
|
19961
|
+
const prepared = this.prepareSessionWithResources({
|
|
19862
19962
|
abilities,
|
|
19863
19963
|
spaceAbilities,
|
|
19864
19964
|
...options.rawAbilities !== void 0 ? { rawAbilities: options.rawAbilities } : {},
|
|
@@ -19937,6 +20037,8 @@ var NodeUserAuthorization = class {
|
|
|
19937
20037
|
this._chainId = chainId;
|
|
19938
20038
|
await this.resolveTinyCloudHostsForSignIn(address, chainId);
|
|
19939
20039
|
this.assertSignInCurrent(generation);
|
|
20040
|
+
await this.refreshNamedKeyFeatureForSignIn(address, chainId);
|
|
20041
|
+
this.assertSignInCurrent(generation);
|
|
19940
20042
|
const keyId = `session-${Date.now()}`;
|
|
19941
20043
|
this.sessionManager.renameSessionKeyId(this.activeSessionKeyId, keyId);
|
|
19942
20044
|
this.activeSessionKeyId = keyId;
|
|
@@ -19949,7 +20051,7 @@ var NodeUserAuthorization = class {
|
|
|
19949
20051
|
const spaceId = capabilityPlan.spaceId;
|
|
19950
20052
|
const now = /* @__PURE__ */ new Date();
|
|
19951
20053
|
const expirationTime = new Date(now.getTime() + this.sessionExpirationMs);
|
|
19952
|
-
const prepared = this.
|
|
20054
|
+
const prepared = this.prepareSessionWithResources({
|
|
19953
20055
|
abilities: capabilityPlan.abilities,
|
|
19954
20056
|
...capabilityPlan.spaceAbilities !== void 0 ? { spaceAbilities: capabilityPlan.spaceAbilities } : {},
|
|
19955
20057
|
...capabilityPlan.rawAbilities !== void 0 ? { rawAbilities: capabilityPlan.rawAbilities } : {},
|
|
@@ -20083,9 +20185,9 @@ var NodeUserAuthorization = class {
|
|
|
20083
20185
|
refuseCaveated(this.getCapabilityRequest()?.manifests);
|
|
20084
20186
|
const address = (0, import_sdk_core5.canonicalizeAddress)(await this.signer.getAddress());
|
|
20085
20187
|
const chainId = await this.signer.getChainId();
|
|
20086
|
-
|
|
20087
|
-
|
|
20088
|
-
this.
|
|
20188
|
+
await this.refreshNamedKeyFeatureForSignIn(address, chainId);
|
|
20189
|
+
const keyId = `openkey-session-${Date.now()}-${++this.preparedSessionCounter}`;
|
|
20190
|
+
this.sessionManager.createSessionKey(keyId);
|
|
20089
20191
|
const jwkString = this.sessionManager.jwk(keyId);
|
|
20090
20192
|
if (!jwkString) {
|
|
20091
20193
|
throw new Error("Failed to create session key");
|
|
@@ -20095,7 +20197,7 @@ var NodeUserAuthorization = class {
|
|
|
20095
20197
|
const spaceId = capabilityPlan.spaceId;
|
|
20096
20198
|
const now = /* @__PURE__ */ new Date();
|
|
20097
20199
|
const expirationTime = new Date(now.getTime() + this.sessionExpirationMs);
|
|
20098
|
-
const prepared = this.
|
|
20200
|
+
const prepared = this.prepareSessionWithResources({
|
|
20099
20201
|
abilities: capabilityPlan.abilities,
|
|
20100
20202
|
...capabilityPlan.spaceAbilities !== void 0 ? { spaceAbilities: capabilityPlan.spaceAbilities } : {},
|
|
20101
20203
|
...capabilityPlan.rawAbilities !== void 0 ? { rawAbilities: capabilityPlan.rawAbilities } : {},
|
|
@@ -20166,6 +20268,7 @@ var NodeUserAuthorization = class {
|
|
|
20166
20268
|
await this.sessionStorage.save(address, persistedData);
|
|
20167
20269
|
this._session = clientSession;
|
|
20168
20270
|
this._tinyCloudSession = tinyCloudSession;
|
|
20271
|
+
this.activeSessionKeyId = keyId;
|
|
20169
20272
|
this._address = address;
|
|
20170
20273
|
this._chainId = chainId;
|
|
20171
20274
|
await this.refreshNodeInfo();
|
|
@@ -20212,6 +20315,11 @@ var NodeUserAuthorization = class {
|
|
|
20212
20315
|
* @param options Optional overrides for the OpenKey request.
|
|
20213
20316
|
*/
|
|
20214
20317
|
async signInWithOpenKey(authorizeFn, options) {
|
|
20318
|
+
const pending = await this.prepareOpenKeySignIn(options);
|
|
20319
|
+
return this.completeOpenKeySignIn(pending, authorizeFn(pending.request), options);
|
|
20320
|
+
}
|
|
20321
|
+
/** Finish address, host and SIWE preparation before an approval click. */
|
|
20322
|
+
async prepareOpenKeySignIn(options) {
|
|
20215
20323
|
refuseCaveated(this.getCapabilityRequest()?.manifests);
|
|
20216
20324
|
if (options?.host !== void 0) {
|
|
20217
20325
|
const overrideHost = options.host.trim();
|
|
@@ -20223,7 +20331,7 @@ var NodeUserAuthorization = class {
|
|
|
20223
20331
|
const signingAddress = (0, import_sdk_core5.canonicalizeAddress)(await this.signer.getAddress());
|
|
20224
20332
|
const signingChainId = await this.signer.getChainId();
|
|
20225
20333
|
await this.resolveTinyCloudHostsForSignIn(signingAddress, signingChainId);
|
|
20226
|
-
const { prepared, keyId, jwk } = await this.prepareSessionForSigning();
|
|
20334
|
+
const { prepared, keyId, jwk, address, chainId } = await this.prepareSessionForSigning();
|
|
20227
20335
|
const presentation = this.buildPresentationEnvelope(options?.reason);
|
|
20228
20336
|
const hostHint = this.primaryTinyCloudHost;
|
|
20229
20337
|
const originalRequest = {
|
|
@@ -20235,12 +20343,32 @@ var NodeUserAuthorization = class {
|
|
|
20235
20343
|
keyId: options?.openkeyKeyId,
|
|
20236
20344
|
presentation
|
|
20237
20345
|
};
|
|
20346
|
+
return {
|
|
20347
|
+
request: originalRequest,
|
|
20348
|
+
prepared,
|
|
20349
|
+
keyId,
|
|
20350
|
+
jwk,
|
|
20351
|
+
address,
|
|
20352
|
+
chainId,
|
|
20353
|
+
generation: this.sessionGeneration,
|
|
20354
|
+
createdAt: Date.now()
|
|
20355
|
+
};
|
|
20356
|
+
}
|
|
20357
|
+
/** Complete a prepared approval. The caller starts authorization before awaiting this method. */
|
|
20358
|
+
async completeOpenKeySignIn(pending, authorization, options) {
|
|
20359
|
+
if (Date.now() - pending.createdAt > 5 * 6e4) {
|
|
20360
|
+
throw new Error("Prepared OpenKey sign-in expired; prepare again");
|
|
20361
|
+
}
|
|
20362
|
+
if (pending.generation !== this.sessionGeneration) {
|
|
20363
|
+
throw new Error("Prepared OpenKey sign-in expired after sign-out");
|
|
20364
|
+
}
|
|
20365
|
+
const { request: originalRequest, prepared, keyId, jwk } = pending;
|
|
20238
20366
|
let result;
|
|
20239
20367
|
let signed2;
|
|
20240
20368
|
let signature2;
|
|
20241
20369
|
for (let attempt = 0; ; ) {
|
|
20242
20370
|
try {
|
|
20243
|
-
result ?? (result = await
|
|
20371
|
+
result ?? (result = await authorization);
|
|
20244
20372
|
signed2 = await this.verifyOpenKeyResult(result, prepared);
|
|
20245
20373
|
signature2 = result.signature;
|
|
20246
20374
|
break;
|
|
@@ -21136,6 +21264,7 @@ var ROOT_DELEGATION_ACTIONS = [
|
|
|
21136
21264
|
import_sdk_core10.DUCKDB.ALL
|
|
21137
21265
|
];
|
|
21138
21266
|
var DEFAULT_SESSION_EXPIRATION_MS = import_sdk_core10.EXPIRY.SESSION_MS;
|
|
21267
|
+
var OWNER_REVOCATION_WINDOW_MS = 10 * 6e4;
|
|
21139
21268
|
function unifiedRootAttenuation(capabilities) {
|
|
21140
21269
|
const attenuation = {};
|
|
21141
21270
|
for (const capability of capabilities) {
|
|
@@ -21633,6 +21762,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21633
21762
|
constructor(config = {}) {
|
|
21634
21763
|
this.signer = null;
|
|
21635
21764
|
this.auth = null;
|
|
21765
|
+
this.consumedOpenKeyPendings = /* @__PURE__ */ new WeakSet();
|
|
21636
21766
|
this.tc = null;
|
|
21637
21767
|
this._chainId = 1;
|
|
21638
21768
|
this.replicaPurgeFailedHandlers = /* @__PURE__ */ new Set();
|
|
@@ -21667,6 +21797,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21667
21797
|
return void 0;
|
|
21668
21798
|
}
|
|
21669
21799
|
};
|
|
21800
|
+
this._keysUnavailable = [];
|
|
21670
21801
|
this._baseVaults = /* @__PURE__ */ new Map();
|
|
21671
21802
|
this._baseSecrets = /* @__PURE__ */ new Map();
|
|
21672
21803
|
this._secrets = /* @__PURE__ */ new Map();
|
|
@@ -22494,6 +22625,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22494
22625
|
this._hooks = void 0;
|
|
22495
22626
|
this._vault = void 0;
|
|
22496
22627
|
this._encryption = void 0;
|
|
22628
|
+
this._namedKeyClient = void 0;
|
|
22629
|
+
this._keysUnavailable = [];
|
|
22497
22630
|
this._baseVaults.clear();
|
|
22498
22631
|
this._baseSecrets.clear();
|
|
22499
22632
|
this._secrets.clear();
|
|
@@ -22570,6 +22703,37 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22570
22703
|
}
|
|
22571
22704
|
await this.signInWith(() => this.auth.signInWithOpenKey(authorizeFn, options));
|
|
22572
22705
|
}
|
|
22706
|
+
/** Prepare the OpenKey request before the approval click. */
|
|
22707
|
+
async prepareOpenKeySignIn() {
|
|
22708
|
+
if (!this.signer || !this.tc || !this.auth) {
|
|
22709
|
+
throw new Error("Cannot prepare OpenKey sign-in in session-only mode. Provide a signer in config.");
|
|
22710
|
+
}
|
|
22711
|
+
await this.signOutInFlight;
|
|
22712
|
+
await this.accountRegistryTail;
|
|
22713
|
+
await this.wasmBindings.ensureInitialized?.();
|
|
22714
|
+
return this.auth.prepareOpenKeySignIn();
|
|
22715
|
+
}
|
|
22716
|
+
/** Start OpenKey authorization synchronously, then complete normal session setup. */
|
|
22717
|
+
signInWithPreparedOpenKey(pending, authorizeFn) {
|
|
22718
|
+
if (!this.signer || !this.tc || !this.auth || this.signOutInFlight) {
|
|
22719
|
+
throw new Error("Prepared OpenKey sign-in is unavailable");
|
|
22720
|
+
}
|
|
22721
|
+
if (this.consumedOpenKeyPendings.has(pending)) {
|
|
22722
|
+
throw new Error("Prepared OpenKey sign-in was already used");
|
|
22723
|
+
}
|
|
22724
|
+
if (Date.now() - pending.createdAt > 5 * 6e4) {
|
|
22725
|
+
throw new Error("Prepared OpenKey sign-in expired; prepare again");
|
|
22726
|
+
}
|
|
22727
|
+
this.consumedOpenKeyPendings.add(pending);
|
|
22728
|
+
const authorization = authorizeFn(pending.request);
|
|
22729
|
+
void authorization.catch(() => void 0);
|
|
22730
|
+
return this.signInWith(() => {
|
|
22731
|
+
if (this._address !== pending.address || this._chainId !== pending.chainId) {
|
|
22732
|
+
throw new Error("OpenKey account changed after sign-in preparation");
|
|
22733
|
+
}
|
|
22734
|
+
return this.auth.completeOpenKeySignIn(pending, authorization);
|
|
22735
|
+
});
|
|
22736
|
+
}
|
|
22573
22737
|
async signInWith(performSignIn) {
|
|
22574
22738
|
refuseCaveated(this.auth?.capabilityRequest?.manifests);
|
|
22575
22739
|
const epoch = this.sessionEpoch;
|
|
@@ -22616,6 +22780,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22616
22780
|
this._hooks = void 0;
|
|
22617
22781
|
this._vault = void 0;
|
|
22618
22782
|
this._encryption = void 0;
|
|
22783
|
+
this._namedKeyClient = void 0;
|
|
22784
|
+
this._keysUnavailable = [];
|
|
22619
22785
|
this._baseVaults.clear();
|
|
22620
22786
|
this._baseSecrets.clear();
|
|
22621
22787
|
this._secrets.clear();
|
|
@@ -22668,6 +22834,26 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22668
22834
|
this.assertSessionEpoch(epoch, "Sign-in");
|
|
22669
22835
|
await this.ensureRequestedEncryptionNetworks();
|
|
22670
22836
|
this.assertSessionEpoch(epoch, "Sign-in");
|
|
22837
|
+
this._keysUnavailable = this.auth?.unavailableKeyPaths ?? [];
|
|
22838
|
+
if (this._keysUnavailable.length > 0) {
|
|
22839
|
+
this.notificationHandler.warning(`Named keys unavailable: ${this._keysUnavailable.join(", ")}`);
|
|
22840
|
+
}
|
|
22841
|
+
if (this.nodeFeatures.includes("encryption-named-keys-v1")) {
|
|
22842
|
+
for (const manifest of this.auth?.capabilityRequest?.manifests ?? []) {
|
|
22843
|
+
for (const declaration of manifest.keys ?? []) {
|
|
22844
|
+
if ("path" in declaration && declaration.abilities.includes("key.create") && !this._keysUnavailable.includes(declaration.path)) {
|
|
22845
|
+
try {
|
|
22846
|
+
await this.keys.ensureKey(declaration.path);
|
|
22847
|
+
} catch (error) {
|
|
22848
|
+
this._keysUnavailable.push(declaration.path);
|
|
22849
|
+
const code = error && typeof error === "object" && "code" in error && typeof error.code === "string" && /^[A-Z_]+$/.test(error.code) ? error.code : "UNKNOWN";
|
|
22850
|
+
this.notificationHandler.warning(`Named key unavailable: ${declaration.path} (${code})`);
|
|
22851
|
+
}
|
|
22852
|
+
this.assertSessionEpoch(epoch, "Sign-in");
|
|
22853
|
+
}
|
|
22854
|
+
}
|
|
22855
|
+
}
|
|
22856
|
+
}
|
|
22671
22857
|
if (!bootstrapped && this.config.manifest === void 0 && this.config.capabilityRequest === void 0) {
|
|
22672
22858
|
await this.ensureOwnedSpaceHostedById(this.ownedSpaceId(import_sdk_core10.SECRETS_SPACE));
|
|
22673
22859
|
}
|
|
@@ -23244,7 +23430,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23244
23430
|
if (!this.currentSessionCoversAccountRegistry()) {
|
|
23245
23431
|
if (!_TinyCloudNode.accountRegistrySkipLogged) {
|
|
23246
23432
|
_TinyCloudNode.accountRegistrySkipLogged = true;
|
|
23247
|
-
|
|
23433
|
+
import_sdk_services4.tinyCloudDebugLogger.log("registry.skip");
|
|
23248
23434
|
}
|
|
23249
23435
|
return;
|
|
23250
23436
|
}
|
|
@@ -23455,7 +23641,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23455
23641
|
const networkIds = /* @__PURE__ */ new Set();
|
|
23456
23642
|
for (const resource of request.resources) {
|
|
23457
23643
|
if (resource.service === import_sdk_core10.ENCRYPTION_PERMISSION_SERVICE && resource.path.startsWith("urn:tinycloud:encryption:") && resource.actions.includes(DECRYPT_ACTION2)) {
|
|
23458
|
-
|
|
23644
|
+
const parsed = resource.path.includes("/") ? (0, import_resource2.parseEncryptionResource)(resource.path) : { ...(0, import_sdk_core10.parseNetworkId)(resource.path), kind: "network" };
|
|
23645
|
+
if (parsed.kind === "network") networkIds.add(resource.path);
|
|
23459
23646
|
}
|
|
23460
23647
|
}
|
|
23461
23648
|
return [...networkIds];
|
|
@@ -24005,6 +24192,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
24005
24192
|
this._hooks = stagedGraph.hooks;
|
|
24006
24193
|
this._vault = stagedGraph.vault;
|
|
24007
24194
|
this._encryption = stagedGraph.encryption;
|
|
24195
|
+
this._namedKeyClient = void 0;
|
|
24196
|
+
this._keysUnavailable = [];
|
|
24008
24197
|
this._capabilityRegistry = stagedGraph.capabilityRegistry;
|
|
24009
24198
|
this._keyProvider = stagedGraph.keyProvider;
|
|
24010
24199
|
this._sharingService = stagedGraph.sharingService;
|
|
@@ -24186,6 +24375,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
24186
24375
|
invoke: graph.invoke,
|
|
24187
24376
|
invokeAny: graph.invokeAny,
|
|
24188
24377
|
signOwnerRevocation: this.signer ? (cid) => this.signOwnerRevocation(cid) : void 0,
|
|
24378
|
+
sessionInvocationSigner: this.sessionInvocationSignerFor(input.serviceSession),
|
|
24189
24379
|
fetch: graph.fetch
|
|
24190
24380
|
});
|
|
24191
24381
|
const keyProvider = new WasmKeyProvider({ sessionManager: input.manager });
|
|
@@ -24401,6 +24591,31 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
24401
24591
|
if (session === void 0) throw new Error("Not signed in. Call signIn() first.");
|
|
24402
24592
|
return signBytesWithJwk(bytes, session.jwk);
|
|
24403
24593
|
}
|
|
24594
|
+
/**
|
|
24595
|
+
* Signs proofless invocations with `session`'s own did:key, for the node's
|
|
24596
|
+
* issued-delegation list and revocation by the delegator. The audience is
|
|
24597
|
+
* this Node's DID; each invocation lives 60 seconds.
|
|
24598
|
+
*/
|
|
24599
|
+
sessionInvocationSignerFor(session) {
|
|
24600
|
+
return {
|
|
24601
|
+
did: () => session.verificationMethod.split("#", 1)[0],
|
|
24602
|
+
sign: async (attenuation) => {
|
|
24603
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
24604
|
+
const invocation = await (0, import_sdk_core10.signCompactUcanAuthorization)({
|
|
24605
|
+
issuerDid: session.verificationMethod.split("#", 1)[0],
|
|
24606
|
+
audienceDid: await this.fetchNodeId(),
|
|
24607
|
+
attenuation,
|
|
24608
|
+
facts: [{}],
|
|
24609
|
+
proofs: [],
|
|
24610
|
+
notBefore: now,
|
|
24611
|
+
expiresAt: now + 60,
|
|
24612
|
+
nonce: `urn:uuid:${crypto.randomUUID()}`,
|
|
24613
|
+
sign: (bytes) => signBytesWithJwk(bytes, session.jwk)
|
|
24614
|
+
});
|
|
24615
|
+
return invocation.authorization;
|
|
24616
|
+
}
|
|
24617
|
+
};
|
|
24618
|
+
}
|
|
24404
24619
|
/** Author a proofless Policy/v3 owner root with the authenticated holder key. */
|
|
24405
24620
|
async createUnifiedOwnerRoot(input) {
|
|
24406
24621
|
const ownerDid = this.credentialHolderDid;
|
|
@@ -24914,7 +25129,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
24914
25129
|
);
|
|
24915
25130
|
graph.assertActive();
|
|
24916
25131
|
if (!response.ok) {
|
|
24917
|
-
const permissionHint = response.status === 401 || response.status === 403 ? (0,
|
|
25132
|
+
const permissionHint = response.status === 401 || response.status === 403 ? (0, import_sdk_services4.parsePermissionHint)({
|
|
24918
25133
|
service: "tinycloud.encryption",
|
|
24919
25134
|
path: networkId,
|
|
24920
25135
|
actions: ["tinycloud.encryption/decrypt"]
|
|
@@ -24935,7 +25150,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
24935
25150
|
graph.assertActive();
|
|
24936
25151
|
const signed2 = await this.signRawNetworkAuthorization({
|
|
24937
25152
|
targetNode: input.targetNode,
|
|
24938
|
-
networkId: input.networkId
|
|
25153
|
+
networkId: input.body.keyPath === void 0 ? input.networkId : `${input.networkId}/${input.body.keyPath}`,
|
|
24939
25154
|
action: DECRYPT_ACTION2,
|
|
24940
25155
|
facts: input.facts
|
|
24941
25156
|
}, { graph, session });
|
|
@@ -25108,6 +25323,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
25108
25323
|
invoke: graph.invoke,
|
|
25109
25324
|
invokeAny: graph.invokeAny,
|
|
25110
25325
|
signOwnerRevocation: this.signer ? (cid) => this.signOwnerRevocation(cid) : void 0,
|
|
25326
|
+
sessionInvocationSigner: this.sessionInvocationSignerFor(serviceSession),
|
|
25111
25327
|
fetch: graph.fetch
|
|
25112
25328
|
});
|
|
25113
25329
|
this._spaceService = new import_sdk_core10.SpaceService({
|
|
@@ -25358,7 +25574,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
25358
25574
|
* Sign a wallet (CACAO) revocation of the delegation `cid` for the node's
|
|
25359
25575
|
* `POST /revoke`, and return its Authorization header value. The node
|
|
25360
25576
|
* accepts it when the owner delegated `cid` or owns a space its abilities
|
|
25361
|
-
* target, whichever key issued it. Valid for
|
|
25577
|
+
* target, whichever key issued it. Valid for 10 minutes. Requires the
|
|
25362
25578
|
* owner wallet signer.
|
|
25363
25579
|
*/
|
|
25364
25580
|
async signOwnerRevocation(cid) {
|
|
@@ -25374,7 +25590,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
25374
25590
|
chainId: session.chainId,
|
|
25375
25591
|
nonce: (0, import_siwe2.generateNonce)(),
|
|
25376
25592
|
issuedAt: issuedAt.toISOString(),
|
|
25377
|
-
expirationTime: new Date(issuedAt.getTime() +
|
|
25593
|
+
expirationTime: new Date(issuedAt.getTime() + OWNER_REVOCATION_WINDOW_MS).toISOString()
|
|
25378
25594
|
}).prepareMessage();
|
|
25379
25595
|
const signature2 = await this.signer.signMessage(siwe);
|
|
25380
25596
|
return this.wasmBindings.siweToDelegationHeaders({ siwe, signature: signature2 }).Authorization;
|
|
@@ -25601,6 +25817,40 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
25601
25817
|
get encryption() {
|
|
25602
25818
|
return this.getEncryptionService();
|
|
25603
25819
|
}
|
|
25820
|
+
/** Named keys bound to the current session. */
|
|
25821
|
+
get keys() {
|
|
25822
|
+
return new Proxy({}, {
|
|
25823
|
+
get: (_target, property) => {
|
|
25824
|
+
if (property === "refreshNodeInfo") {
|
|
25825
|
+
return () => {
|
|
25826
|
+
void this._namedKeyClient?.then((client) => client.refreshNodeInfo());
|
|
25827
|
+
};
|
|
25828
|
+
}
|
|
25829
|
+
if (typeof property !== "string" || ![
|
|
25830
|
+
"getPublicKey",
|
|
25831
|
+
"describeKey",
|
|
25832
|
+
"createKey",
|
|
25833
|
+
"blockKey",
|
|
25834
|
+
"unblockKey",
|
|
25835
|
+
"ensureKey",
|
|
25836
|
+
"encrypt",
|
|
25837
|
+
"decrypt",
|
|
25838
|
+
"decryptMany"
|
|
25839
|
+
].includes(property)) return void 0;
|
|
25840
|
+
return (...args) => {
|
|
25841
|
+
this._namedKeyClient ?? (this._namedKeyClient = import("./encryption-keys.cjs").then(({ createNamedKeyClient }) => createNamedKeyClient(this)));
|
|
25842
|
+
return this._namedKeyClient.then((client) => {
|
|
25843
|
+
const method = client[property];
|
|
25844
|
+
return method.apply(client, args);
|
|
25845
|
+
});
|
|
25846
|
+
};
|
|
25847
|
+
}
|
|
25848
|
+
});
|
|
25849
|
+
}
|
|
25850
|
+
/** Manifest key paths unavailable during the latest `signIn()` attempt. */
|
|
25851
|
+
get keysUnavailable() {
|
|
25852
|
+
return this._keysUnavailable;
|
|
25853
|
+
}
|
|
25604
25854
|
async getEncryptionNetwork(nameOrNetworkId = this.getDefaultEncryptionNetworkId()) {
|
|
25605
25855
|
const networkId = nameOrNetworkId.startsWith("urn:tinycloud:encryption:") ? nameOrNetworkId : this.getDefaultEncryptionNetworkId(nameOrNetworkId);
|
|
25606
25856
|
return this.fetchEncryptionNetworkAt(
|
|
@@ -25728,7 +25978,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
25728
25978
|
);
|
|
25729
25979
|
if (result.status !== "ok") {
|
|
25730
25980
|
if (result.status === "permission_required") {
|
|
25731
|
-
const hint = (0,
|
|
25981
|
+
const hint = (0, import_sdk_services4.parsePermissionHint)(result.hint);
|
|
25732
25982
|
if (hint === void 0) return { status: "read_failed" };
|
|
25733
25983
|
return {
|
|
25734
25984
|
status: "permission_required",
|
|
@@ -26490,7 +26740,19 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
26490
26740
|
"delegateTo requires a non-empty permissions array"
|
|
26491
26741
|
);
|
|
26492
26742
|
}
|
|
26493
|
-
const expandedEntries =
|
|
26743
|
+
const expandedEntries = (0, import_sdk_core10.expandDelegationPermissionEntries)(permissions);
|
|
26744
|
+
const namedKeyEntries = expandedEntries.filter((entry) => entry.service === import_sdk_core10.ENCRYPTION_PERMISSION_SERVICE && entry.path.includes("/"));
|
|
26745
|
+
if (namedKeyEntries.length > 0) {
|
|
26746
|
+
if (expandedEntries.some((entry) => entry.service === import_sdk_core10.ENCRYPTION_PERMISSION_SERVICE && !entry.path.includes("/"))) {
|
|
26747
|
+
throw new import_sdk_core10.ManifestValidationError("named-key delegation cannot include bare-network permissions");
|
|
26748
|
+
}
|
|
26749
|
+
if (options?.forceWalletSign) {
|
|
26750
|
+
throw new import_sdk_core10.ManifestValidationError("named-key delegation requires a signed session grant");
|
|
26751
|
+
}
|
|
26752
|
+
const granted = this.projectSignedRecapCapabilities(session.siwe);
|
|
26753
|
+
const { subset, missing } = this.signedCapabilitySubset(namedKeyEntries, granted);
|
|
26754
|
+
if (!subset) throw new import_sdk_core10.PermissionNotInManifestError(missing, granted);
|
|
26755
|
+
}
|
|
26494
26756
|
const now = /* @__PURE__ */ new Date();
|
|
26495
26757
|
const expiryMs = resolveExpiryMs(options?.expiry);
|
|
26496
26758
|
const expirationTime = new Date(now.getTime() + expiryMs);
|
|
@@ -28009,7 +28271,7 @@ var FileSessionStorage = class {
|
|
|
28009
28271
|
};
|
|
28010
28272
|
|
|
28011
28273
|
// src/authorization/openKeyBridge.ts
|
|
28012
|
-
function wireOpenKeyAuthorize(openkey) {
|
|
28274
|
+
function wireOpenKeyAuthorize(openkey, opts) {
|
|
28013
28275
|
return async (input) => {
|
|
28014
28276
|
const result = await openkey.authorizeTinyCloud({
|
|
28015
28277
|
protocolVersion: 1,
|
|
@@ -28020,7 +28282,7 @@ function wireOpenKeyAuthorize(openkey) {
|
|
|
28020
28282
|
// Forward the presentation envelope verbatim — the OpenKey SDK
|
|
28021
28283
|
// validates and size-bounds it before use.
|
|
28022
28284
|
presentation: input.presentation
|
|
28023
|
-
});
|
|
28285
|
+
}, opts);
|
|
28024
28286
|
if (result.protocolVersion !== 1) {
|
|
28025
28287
|
throw new Error(
|
|
28026
28288
|
`wireOpenKeyAuthorize: OpenKey returned unsupported protocolVersion ${result.protocolVersion}`
|
|
@@ -28073,7 +28335,7 @@ var import_node_fs = require("fs");
|
|
|
28073
28335
|
var import_promises = require("fs/promises");
|
|
28074
28336
|
var import_node_path = require("path");
|
|
28075
28337
|
var import_sdk_core14 = require("@tinycloud/sdk-core");
|
|
28076
|
-
var
|
|
28338
|
+
var import_sdk_services5 = require("@tinycloud/sdk-services");
|
|
28077
28339
|
var import_contractConstants = require("@tinycloud/sdk-services/kv/replication/contractConstants");
|
|
28078
28340
|
init_authority();
|
|
28079
28341
|
var REPLICA_LOGOUT_PENDING_FILE = "replica-logout-pending.json";
|
|
@@ -28635,7 +28897,7 @@ function sha256Base32(material) {
|
|
|
28635
28897
|
return out.slice(0, 26);
|
|
28636
28898
|
}
|
|
28637
28899
|
function partitionDirOf(root, identity) {
|
|
28638
|
-
return (0, import_node_path.join)(root, sha256Base32((0,
|
|
28900
|
+
return (0, import_node_path.join)(root, sha256Base32((0, import_sdk_services5.replicationIdentityKey)(identity)));
|
|
28639
28901
|
}
|
|
28640
28902
|
function replicaHashOf(prefix, deviceDid) {
|
|
28641
28903
|
return sha256Base32(`${prefix}
|
|
@@ -28709,8 +28971,8 @@ function deviceRecordOf(value) {
|
|
|
28709
28971
|
}
|
|
28710
28972
|
function sameIdentity(a, b) {
|
|
28711
28973
|
try {
|
|
28712
|
-
const left = (0,
|
|
28713
|
-
const right = (0,
|
|
28974
|
+
const left = (0, import_sdk_services5.canonicalReplicationIdentity)(a);
|
|
28975
|
+
const right = (0, import_sdk_services5.canonicalReplicationIdentity)(b);
|
|
28714
28976
|
return left.host === right.host && left.space === right.space && left.principal === right.principal;
|
|
28715
28977
|
} catch {
|
|
28716
28978
|
return false;
|
|
@@ -28726,7 +28988,7 @@ async function ensurePartitionIdentity(dir, identity, dirSync) {
|
|
|
28726
28988
|
const file = (0, import_node_path.join)(dir, "identity.json");
|
|
28727
28989
|
const existing = await readJsonFile(file);
|
|
28728
28990
|
if (existing === void 0) {
|
|
28729
|
-
await writeJsonAtomic(file, { v: 1, ...(0,
|
|
28991
|
+
await writeJsonAtomic(file, { v: 1, ...(0, import_sdk_services5.canonicalReplicationIdentity)(identity) }, dirSync);
|
|
28730
28992
|
return;
|
|
28731
28993
|
}
|
|
28732
28994
|
const record = identityRecordOf(existing);
|
|
@@ -28825,7 +29087,7 @@ withGate_fn = async function(section) {
|
|
|
28825
29087
|
if (snapshot === void 0) throw gated("gate_unavailable");
|
|
28826
29088
|
const [principalHash, identityKeyHash] = await Promise.all([
|
|
28827
29089
|
gate.principalHashOf(snapshot.salt, this.identity.principal),
|
|
28828
|
-
gate.identityHashOf(snapshot.salt, (0,
|
|
29090
|
+
gate.identityHashOf(snapshot.salt, (0, import_sdk_services5.replicationIdentityKey)(this.identity))
|
|
28829
29091
|
]);
|
|
28830
29092
|
const access = {
|
|
28831
29093
|
stamp: __privateGet(this, _stamp),
|
|
@@ -29096,7 +29358,8 @@ var SqliteReplicaHandle = class {
|
|
|
29096
29358
|
options.signal.addEventListener("abort", onAbort, { once: true });
|
|
29097
29359
|
const engine = new (__privateGet(this, _replica)).Replica({
|
|
29098
29360
|
store: __privateGet(this, _store),
|
|
29099
|
-
transportFor
|
|
29361
|
+
transportFor,
|
|
29362
|
+
...this.spec.contentFetch === void 0 ? {} : { contentFetch: this.spec.contentFetch }
|
|
29100
29363
|
});
|
|
29101
29364
|
const running = engine.sync({ signal: abort.signal });
|
|
29102
29365
|
const registration = { promise: running, abort: () => abort.abort() };
|
|
@@ -29325,7 +29588,7 @@ function createSqliteReplicaStorage(options) {
|
|
|
29325
29588
|
if (snapshot === void 0) throw gated("gate_unavailable");
|
|
29326
29589
|
const [principalHash, identityKeyHash] = await Promise.all([
|
|
29327
29590
|
gate.principalHashOf(snapshot.salt, principal),
|
|
29328
|
-
identity === void 0 ? Promise.resolve(void 0) : gate.identityHashOf(snapshot.salt, (0,
|
|
29591
|
+
identity === void 0 ? Promise.resolve(void 0) : gate.identityHashOf(snapshot.salt, (0, import_sdk_services5.replicationIdentityKey)(identity))
|
|
29329
29592
|
]);
|
|
29330
29593
|
return profileSection(async () => {
|
|
29331
29594
|
assertLogoutClear(options.logoutPendingFile);
|
|
@@ -29348,7 +29611,7 @@ function createSqliteReplicaStorage(options) {
|
|
|
29348
29611
|
if (snapshot === void 0) throw gated("gate_unavailable");
|
|
29349
29612
|
const [principalHash, identityKeyHash] = await Promise.all([
|
|
29350
29613
|
gate.principalHashOf(snapshot.salt, spec.identity.principal),
|
|
29351
|
-
gate.identityHashOf(snapshot.salt, (0,
|
|
29614
|
+
gate.identityHashOf(snapshot.salt, (0, import_sdk_services5.replicationIdentityKey)(spec.identity))
|
|
29352
29615
|
]);
|
|
29353
29616
|
const access = {
|
|
29354
29617
|
stamp: spec.stamp,
|
|
@@ -29432,7 +29695,7 @@ function createSqliteReplicaStorage(options) {
|
|
|
29432
29695
|
const identity = identityRecordOf(await readJsonFile((0, import_node_path.join)(idDir, "identity.json")));
|
|
29433
29696
|
if (identity === void 0) throw new ReplicaStorageError("STORAGE_ERROR", `Missing identity at ${idDir}.`);
|
|
29434
29697
|
if (await gate.principalHashOf(salt, identity.principal) !== principalHash) continue;
|
|
29435
|
-
const identityKeyHash = await gate.identityHashOf(salt, (0,
|
|
29698
|
+
const identityKeyHash = await gate.identityHashOf(salt, (0, import_sdk_services5.replicationIdentityKey)(identity));
|
|
29436
29699
|
const recordedAt = Date.now();
|
|
29437
29700
|
if (await pathExists((0, import_node_path.join)(idDir, "pending.json")) || await pathExists((0, import_node_path.join)(idDir, "device.jwk"))) {
|
|
29438
29701
|
intents.push({ id: `pending:${identityKeyHash}`, kind: "pending", identityKeyHash, principalHash, recordedAt });
|
|
@@ -30110,7 +30373,7 @@ function createSqliteReplicaStorage(options) {
|
|
|
30110
30373
|
for (const dir of await identityDirs()) {
|
|
30111
30374
|
const identity = identityRecordOf(await readJsonFile((0, import_node_path.join)(dir, "identity.json")));
|
|
30112
30375
|
if (identity === void 0) throw new ReplicaStorageError("STORAGE_ERROR", `Missing identity at ${dir}.`);
|
|
30113
|
-
const hash3 = await gate.identityHashOf(state.salt, (0,
|
|
30376
|
+
const hash3 = await gate.identityHashOf(state.salt, (0, import_sdk_services5.replicationIdentityKey)(identity));
|
|
30114
30377
|
if (intent.kind === "pending" ? hash3 === intent.identityKeyHash : intent.partitionId.startsWith(`${hash3}:`)) {
|
|
30115
30378
|
idDir = dir;
|
|
30116
30379
|
break;
|
|
@@ -30256,7 +30519,7 @@ function createSqliteReplicaStorage(options) {
|
|
|
30256
30519
|
const gate = await fileGate();
|
|
30257
30520
|
const snapshot = await gate.readGateSnapshot(profileDir);
|
|
30258
30521
|
if (snapshot === void 0) throw gated("gate_unavailable");
|
|
30259
|
-
const identityKeyHash = await gate.identityHashOf(snapshot.salt, (0,
|
|
30522
|
+
const identityKeyHash = await gate.identityHashOf(snapshot.salt, (0, import_sdk_services5.replicationIdentityKey)(spec.identity));
|
|
30260
30523
|
const resolvePolicy = async (at, recordFirst) => {
|
|
30261
30524
|
const policy = spec.grantPolicy;
|
|
30262
30525
|
if (!Number.isSafeInteger(policy.grantTtlMs) || policy.grantTtlMs <= 0 || policy.grantTtlMs > import_contractConstants.MAX_GRANT_TTL_MS) {
|
|
@@ -30278,7 +30541,7 @@ function createSqliteReplicaStorage(options) {
|
|
|
30278
30541
|
let partitionId;
|
|
30279
30542
|
const storeGuard = (section) => guarded(spec.identity.principal, spec.stamp, spec.identity, partitionId, section);
|
|
30280
30543
|
return guarded(spec.identity.principal, spec.stamp, spec.identity, void 0, async () => {
|
|
30281
|
-
if ((0,
|
|
30544
|
+
if ((0, import_sdk_services5.requiresSecretsOptIn)(spec.space, spec.prefix) && spec.allowSecrets !== true) {
|
|
30282
30545
|
throw new replica.ReplicaError(
|
|
30283
30546
|
replica.ReplicaErrorCode.SECRETS_OPT_IN_REQUIRED,
|
|
30284
30547
|
`Replicating ${spec.space}/kv/${spec.prefix} copies secret material to disk; pass allowSecrets to opt in.`
|
|
@@ -30524,7 +30787,7 @@ function createSqliteReplicaStorage(options) {
|
|
|
30524
30787
|
const gate = await fileGate();
|
|
30525
30788
|
const currentGate = await gate.readGateSnapshot(profileDir);
|
|
30526
30789
|
if (currentGate !== void 0 && currentGate.principalHash !== null && currentGate.principalHash === await gate.principalHashOf(currentGate.salt, target.identity.principal)) {
|
|
30527
|
-
const identityKeyHash = await gate.identityHashOf(currentGate.salt, (0,
|
|
30790
|
+
const identityKeyHash = await gate.identityHashOf(currentGate.salt, (0, import_sdk_services5.replicationIdentityKey)(target.identity));
|
|
30528
30791
|
await retainGrantInstalls(gate, currentGate, identityKeyHash, store);
|
|
30529
30792
|
}
|
|
30530
30793
|
if (state.revoked === null) {
|
|
@@ -30713,6 +30976,7 @@ function sqliteReplicaStorage(options) {
|
|
|
30713
30976
|
hexDecode,
|
|
30714
30977
|
hexEncode,
|
|
30715
30978
|
installTinyCloudDebugGlobals,
|
|
30979
|
+
isAppKeyHostBound,
|
|
30716
30980
|
isCapabilitySubset,
|
|
30717
30981
|
isEvmAddress,
|
|
30718
30982
|
isNetworkId,
|