@tinycloud/node-sdk 3.8.0-beta.2 → 3.8.0-beta.21
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-DaD5vJhL.d.cts} +49 -6
- package/dist/{TinyCloudNode-CVO41O12.d.cts → TinyCloudNode-k3TbBwwj.d.ts} +49 -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-C_fWaxAu.d.ts} +1 -1
- package/dist/{authority-DgVV80bI.d.cts → authority-DJdbZM6k.d.cts} +1 -1
- package/dist/core.cjs +375 -40
- 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 +370 -35
- 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 +343 -51
- 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 +333 -38
- 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,
|
|
@@ -18809,6 +18814,7 @@ function deserializeDelegation(data) {
|
|
|
18809
18814
|
|
|
18810
18815
|
// src/TinyCloudNode.ts
|
|
18811
18816
|
var import_siwe2 = require("siwe");
|
|
18817
|
+
var import_share_envelope = require("@tinycloud/share-envelope");
|
|
18812
18818
|
|
|
18813
18819
|
// src/replication/module-registry.ts
|
|
18814
18820
|
var nodeLoaders;
|
|
@@ -18824,13 +18830,54 @@ function getNodeReplicationLoaders() {
|
|
|
18824
18830
|
|
|
18825
18831
|
// src/TinyCloudNode.ts
|
|
18826
18832
|
var import_sdk_core10 = require("@tinycloud/sdk-core");
|
|
18827
|
-
var
|
|
18833
|
+
var import_sdk_services4 = require("@tinycloud/sdk-services");
|
|
18828
18834
|
var import_requestLog2 = require("@tinycloud/sdk-services/telemetry/requestLog");
|
|
18835
|
+
var import_resource2 = require("@tinycloud/sdk-services/encryption/resource");
|
|
18829
18836
|
|
|
18830
18837
|
// src/authorization/NodeUserAuthorization.ts
|
|
18831
18838
|
var import_node_crypto = require("crypto");
|
|
18832
18839
|
var import_siwe = require("siwe");
|
|
18833
18840
|
var import_sdk_core5 = require("@tinycloud/sdk-core");
|
|
18841
|
+
var import_resource = require("@tinycloud/sdk-services/encryption/resource");
|
|
18842
|
+
var import_sdk_services3 = require("@tinycloud/sdk-services");
|
|
18843
|
+
|
|
18844
|
+
// src/authorization/appKeyHostBinding.ts
|
|
18845
|
+
var DOMAIN_LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
18846
|
+
var IPV4 = /^\d+\.\d+\.\d+\.\d+$/;
|
|
18847
|
+
var APP_ID = /^[a-z0-9][a-z0-9._-]{0,63}$/;
|
|
18848
|
+
function appKeyHostBinding(domain, appId) {
|
|
18849
|
+
if (!APP_ID.test(appId)) return false;
|
|
18850
|
+
if (domain.endsWith(":")) return false;
|
|
18851
|
+
let url;
|
|
18852
|
+
try {
|
|
18853
|
+
url = new URL(`https://${domain}`);
|
|
18854
|
+
} catch {
|
|
18855
|
+
return false;
|
|
18856
|
+
}
|
|
18857
|
+
if (url.protocol !== "https:" || url.port !== "" || url.username !== "" || url.password !== "") return false;
|
|
18858
|
+
if (url.pathname !== "/" || url.search !== "" || url.hash !== "") return false;
|
|
18859
|
+
const host = url.hostname;
|
|
18860
|
+
if (host.startsWith("[") || host.endsWith(".") || IPV4.test(host)) return false;
|
|
18861
|
+
const labels = host.split(".");
|
|
18862
|
+
if (labels.length < 2 || labels[labels.length - 1] === "localhost") return false;
|
|
18863
|
+
if (!labels.every((label) => DOMAIN_LABEL.test(label))) return false;
|
|
18864
|
+
const reversed = labels.reverse().join(".");
|
|
18865
|
+
return appId === reversed || appId.startsWith(`${reversed}.`);
|
|
18866
|
+
}
|
|
18867
|
+
function isAppKeyHostBound(keysAppId, origin) {
|
|
18868
|
+
if (typeof origin === "string" && !origin.includes("://")) {
|
|
18869
|
+
return appKeyHostBinding(origin, keysAppId);
|
|
18870
|
+
}
|
|
18871
|
+
let url;
|
|
18872
|
+
try {
|
|
18873
|
+
url = typeof origin === "string" ? new URL(origin) : origin;
|
|
18874
|
+
} catch {
|
|
18875
|
+
return false;
|
|
18876
|
+
}
|
|
18877
|
+
return appKeyHostBinding(url.host, keysAppId);
|
|
18878
|
+
}
|
|
18879
|
+
|
|
18880
|
+
// src/authorization/NodeUserAuthorization.ts
|
|
18834
18881
|
init_authority_sign_in();
|
|
18835
18882
|
|
|
18836
18883
|
// src/authorization/caveatRefusal.ts
|
|
@@ -18986,6 +19033,7 @@ function cloneAbilitiesMap(actions) {
|
|
|
18986
19033
|
var NodeUserAuthorization = class {
|
|
18987
19034
|
constructor(config) {
|
|
18988
19035
|
this.activeSessionKeyId = "default";
|
|
19036
|
+
this.preparedSessionCounter = 0;
|
|
18989
19037
|
/** Incremented by resetSession; a sign-in started earlier publishes nothing. */
|
|
18990
19038
|
this.sessionGeneration = 0;
|
|
18991
19039
|
/**
|
|
@@ -19342,6 +19390,23 @@ var NodeUserAuthorization = class {
|
|
|
19342
19390
|
);
|
|
19343
19391
|
return this._capabilityRequest;
|
|
19344
19392
|
}
|
|
19393
|
+
async refreshNamedKeyFeatureForSignIn(address, chainId) {
|
|
19394
|
+
const request = this.getCapabilityRequest();
|
|
19395
|
+
const siweDomain = this.siweConfig?.domain ?? this.domain;
|
|
19396
|
+
const hasNamedKeys = request?.manifests.some((manifest) => manifest.keys?.length && appKeyHostBinding(siweDomain, manifest.keys_app_id ?? manifest.app_id));
|
|
19397
|
+
if (hasNamedKeys) {
|
|
19398
|
+
await this.resolveTinyCloudHostsForSignIn(address, chainId);
|
|
19399
|
+
await this.refreshNodeInfo();
|
|
19400
|
+
}
|
|
19401
|
+
}
|
|
19402
|
+
/** Manifest key paths that this sign-in cannot grant. */
|
|
19403
|
+
get unavailableKeyPaths() {
|
|
19404
|
+
const request = this.getCapabilityRequest();
|
|
19405
|
+
const siweDomain = this.siweConfig?.domain ?? this.domain;
|
|
19406
|
+
return (request?.manifests ?? []).flatMap(
|
|
19407
|
+
(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)
|
|
19408
|
+
);
|
|
19409
|
+
}
|
|
19345
19410
|
resolveSpaceName(space, address, chainId) {
|
|
19346
19411
|
if (space.startsWith("tinycloud:")) {
|
|
19347
19412
|
return space;
|
|
@@ -19533,15 +19598,32 @@ var NodeUserAuthorization = class {
|
|
|
19533
19598
|
}
|
|
19534
19599
|
const rawAbilities = {};
|
|
19535
19600
|
const currentDid = (0, import_sdk_core5.pkhDid)(address, chainId);
|
|
19601
|
+
if (this._nodeFeatures.includes("encryption-named-keys-v1")) {
|
|
19602
|
+
const siweDomain = this.siweConfig?.domain ?? this.domain;
|
|
19603
|
+
for (const manifest of request.manifests) {
|
|
19604
|
+
if (!appKeyHostBinding(siweDomain, manifest.keys_app_id ?? manifest.app_id)) continue;
|
|
19605
|
+
for (const declaration of manifest.keys ?? []) {
|
|
19606
|
+
const path = "path" in declaration ? declaration.path : declaration.prefix;
|
|
19607
|
+
const resource = `urn:tinycloud:encryption:${currentDid}:default/${path}`;
|
|
19608
|
+
for (const ability of declaration.abilities) {
|
|
19609
|
+
addRawAbility(rawAbilities, resource, `tinycloud.encryption/${ability}`);
|
|
19610
|
+
}
|
|
19611
|
+
}
|
|
19612
|
+
}
|
|
19613
|
+
}
|
|
19536
19614
|
const spaceResources = request.resources.filter((entry) => {
|
|
19537
19615
|
if (entry.service !== import_sdk_core5.ENCRYPTION_PERMISSION_SERVICE) {
|
|
19538
19616
|
return true;
|
|
19539
19617
|
}
|
|
19618
|
+
const parsed = entry.path.includes("/") ? (0, import_resource.parseEncryptionResource)(entry.path) : { ...(0, import_sdk_services3.parseNetworkId)(entry.path), kind: "network" };
|
|
19619
|
+
const sharePrefix = parsed.kind === "prefix" && parsed.network === "default" && parsed.prefix === "share" && entry.actions.length === 1 && entry.actions[0] === DECRYPT_ACTION && didPrincipalMatches(parsed.ownerDid, currentDid);
|
|
19620
|
+
if (parsed.kind !== "network" && !sharePrefix) {
|
|
19621
|
+
throw new import_sdk_core5.ManifestValidationError("named keys go in manifest.keys");
|
|
19622
|
+
}
|
|
19540
19623
|
for (const action of entry.actions) {
|
|
19541
19624
|
addRawAbility(rawAbilities, entry.path, action);
|
|
19542
19625
|
}
|
|
19543
|
-
if (entry.actions.includes(DECRYPT_ACTION)) {
|
|
19544
|
-
const parsed = (0, import_sdk_core5.parseNetworkId)(entry.path);
|
|
19626
|
+
if (entry.actions.includes(DECRYPT_ACTION) && parsed.kind === "network") {
|
|
19545
19627
|
if (didPrincipalMatches(parsed.ownerDid, currentDid)) {
|
|
19546
19628
|
addRawAbility(rawAbilities, entry.path, NETWORK_CREATE_ACTION);
|
|
19547
19629
|
}
|
|
@@ -19578,8 +19660,7 @@ var NodeUserAuthorization = class {
|
|
|
19578
19660
|
/**
|
|
19579
19661
|
* Build SIWE overrides from the top-level nonce and siweConfig.
|
|
19580
19662
|
* - Top-level `nonce` is seeded first so `siweConfig.nonce` wins if both are set.
|
|
19581
|
-
* -
|
|
19582
|
-
* - resources are appended to the default resources
|
|
19663
|
+
* - resources are inserted after WASM preparation, before the final ReCap
|
|
19583
19664
|
* - uri triggers a warning (overwriting delegation target)
|
|
19584
19665
|
* - all other fields override directly
|
|
19585
19666
|
* - per-call nonce overrides siweConfig.nonce when provided
|
|
@@ -19597,9 +19678,6 @@ var NodeUserAuthorization = class {
|
|
|
19597
19678
|
if (statement) {
|
|
19598
19679
|
overrides.statement = this.statement ? `${statement} ${this.statement}` : statement;
|
|
19599
19680
|
}
|
|
19600
|
-
if (resources && resources.length > 0) {
|
|
19601
|
-
overrides.resources = resources;
|
|
19602
|
-
}
|
|
19603
19681
|
if (uri) {
|
|
19604
19682
|
console.warn(
|
|
19605
19683
|
"[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 +19689,29 @@ var NodeUserAuthorization = class {
|
|
|
19611
19689
|
}
|
|
19612
19690
|
return overrides;
|
|
19613
19691
|
}
|
|
19692
|
+
prepareSessionWithResources(params) {
|
|
19693
|
+
const resources = this.siweConfig?.resources;
|
|
19694
|
+
if (resources?.some((resource) => typeof resource !== "string" || !/^urn:tinycloud:recipients:[A-Za-z0-9_-]+$/.test(resource))) {
|
|
19695
|
+
throw new Error("SIWE metadata resources must be base64url recipients URIs");
|
|
19696
|
+
}
|
|
19697
|
+
const prepared = this.wasm.prepareSession(params);
|
|
19698
|
+
if (!resources?.length) return prepared;
|
|
19699
|
+
const recapLines = /^- urn:recap:[^\r\n]+$/gim;
|
|
19700
|
+
const recaps = prepared.siwe.match(recapLines);
|
|
19701
|
+
const recap = recaps?.[0];
|
|
19702
|
+
if (recaps?.length !== 1 || !prepared.siwe.endsWith(recap)) {
|
|
19703
|
+
throw new Error("Prepared SIWE must end with exactly one ReCap");
|
|
19704
|
+
}
|
|
19705
|
+
const insertedLines = resources.map((resource) => `- ${resource}`);
|
|
19706
|
+
const siwe = prepared.siwe.slice(0, -recap.length) + insertedLines.join("\n") + "\n" + recap;
|
|
19707
|
+
const expectedFields = (0, import_sdk_core5.extractImmutableSiweFields)(prepared.siwe);
|
|
19708
|
+
expectedFields.nonRecapResources = [expectedFields.nonRecapResources, ...insertedLines].filter(Boolean).join("\n");
|
|
19709
|
+
const actualRecaps = siwe.match(recapLines);
|
|
19710
|
+
if (actualRecaps?.length !== 1 || actualRecaps[0] !== recap || (0, import_sdk_core5.diffImmutableSiweFields)(expectedFields, (0, import_sdk_core5.extractImmutableSiweFields)(siwe)).length) {
|
|
19711
|
+
throw new Error("SIWE metadata insertion changed the prepared message");
|
|
19712
|
+
}
|
|
19713
|
+
return { ...prepared, siwe };
|
|
19714
|
+
}
|
|
19614
19715
|
/**
|
|
19615
19716
|
* Add an extension to the authorization flow.
|
|
19616
19717
|
*/
|
|
@@ -19858,7 +19959,7 @@ var NodeUserAuthorization = class {
|
|
|
19858
19959
|
spaceAbilities[spaceId] = (0, import_sdk_core5.resourceCapabilitiesToAbilitiesMap)(resources);
|
|
19859
19960
|
}
|
|
19860
19961
|
const abilities = spaceAbilities[options.spaceId] ?? (0, import_sdk_core5.resourceCapabilitiesToAbilitiesMap)([]);
|
|
19861
|
-
const prepared = this.
|
|
19962
|
+
const prepared = this.prepareSessionWithResources({
|
|
19862
19963
|
abilities,
|
|
19863
19964
|
spaceAbilities,
|
|
19864
19965
|
...options.rawAbilities !== void 0 ? { rawAbilities: options.rawAbilities } : {},
|
|
@@ -19937,8 +20038,10 @@ var NodeUserAuthorization = class {
|
|
|
19937
20038
|
this._chainId = chainId;
|
|
19938
20039
|
await this.resolveTinyCloudHostsForSignIn(address, chainId);
|
|
19939
20040
|
this.assertSignInCurrent(generation);
|
|
19940
|
-
|
|
19941
|
-
this.
|
|
20041
|
+
await this.refreshNamedKeyFeatureForSignIn(address, chainId);
|
|
20042
|
+
this.assertSignInCurrent(generation);
|
|
20043
|
+
const keyId = `session-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
20044
|
+
this.sessionManager.createSessionKey(keyId);
|
|
19942
20045
|
this.activeSessionKeyId = keyId;
|
|
19943
20046
|
const jwkString = this.sessionManager.jwk(keyId);
|
|
19944
20047
|
if (!jwkString) {
|
|
@@ -19949,7 +20052,7 @@ var NodeUserAuthorization = class {
|
|
|
19949
20052
|
const spaceId = capabilityPlan.spaceId;
|
|
19950
20053
|
const now = /* @__PURE__ */ new Date();
|
|
19951
20054
|
const expirationTime = new Date(now.getTime() + this.sessionExpirationMs);
|
|
19952
|
-
const prepared = this.
|
|
20055
|
+
const prepared = this.prepareSessionWithResources({
|
|
19953
20056
|
abilities: capabilityPlan.abilities,
|
|
19954
20057
|
...capabilityPlan.spaceAbilities !== void 0 ? { spaceAbilities: capabilityPlan.spaceAbilities } : {},
|
|
19955
20058
|
...capabilityPlan.rawAbilities !== void 0 ? { rawAbilities: capabilityPlan.rawAbilities } : {},
|
|
@@ -20083,9 +20186,9 @@ var NodeUserAuthorization = class {
|
|
|
20083
20186
|
refuseCaveated(this.getCapabilityRequest()?.manifests);
|
|
20084
20187
|
const address = (0, import_sdk_core5.canonicalizeAddress)(await this.signer.getAddress());
|
|
20085
20188
|
const chainId = await this.signer.getChainId();
|
|
20086
|
-
|
|
20087
|
-
|
|
20088
|
-
this.
|
|
20189
|
+
await this.refreshNamedKeyFeatureForSignIn(address, chainId);
|
|
20190
|
+
const keyId = `openkey-session-${Date.now()}-${++this.preparedSessionCounter}`;
|
|
20191
|
+
this.sessionManager.createSessionKey(keyId);
|
|
20089
20192
|
const jwkString = this.sessionManager.jwk(keyId);
|
|
20090
20193
|
if (!jwkString) {
|
|
20091
20194
|
throw new Error("Failed to create session key");
|
|
@@ -20095,7 +20198,7 @@ var NodeUserAuthorization = class {
|
|
|
20095
20198
|
const spaceId = capabilityPlan.spaceId;
|
|
20096
20199
|
const now = /* @__PURE__ */ new Date();
|
|
20097
20200
|
const expirationTime = new Date(now.getTime() + this.sessionExpirationMs);
|
|
20098
|
-
const prepared = this.
|
|
20201
|
+
const prepared = this.prepareSessionWithResources({
|
|
20099
20202
|
abilities: capabilityPlan.abilities,
|
|
20100
20203
|
...capabilityPlan.spaceAbilities !== void 0 ? { spaceAbilities: capabilityPlan.spaceAbilities } : {},
|
|
20101
20204
|
...capabilityPlan.rawAbilities !== void 0 ? { rawAbilities: capabilityPlan.rawAbilities } : {},
|
|
@@ -20166,6 +20269,7 @@ var NodeUserAuthorization = class {
|
|
|
20166
20269
|
await this.sessionStorage.save(address, persistedData);
|
|
20167
20270
|
this._session = clientSession;
|
|
20168
20271
|
this._tinyCloudSession = tinyCloudSession;
|
|
20272
|
+
this.activeSessionKeyId = keyId;
|
|
20169
20273
|
this._address = address;
|
|
20170
20274
|
this._chainId = chainId;
|
|
20171
20275
|
await this.refreshNodeInfo();
|
|
@@ -20212,6 +20316,11 @@ var NodeUserAuthorization = class {
|
|
|
20212
20316
|
* @param options Optional overrides for the OpenKey request.
|
|
20213
20317
|
*/
|
|
20214
20318
|
async signInWithOpenKey(authorizeFn, options) {
|
|
20319
|
+
const pending = await this.prepareOpenKeySignIn(options);
|
|
20320
|
+
return this.completeOpenKeySignIn(pending, authorizeFn(pending.request), options);
|
|
20321
|
+
}
|
|
20322
|
+
/** Finish address, host and SIWE preparation before an approval click. */
|
|
20323
|
+
async prepareOpenKeySignIn(options) {
|
|
20215
20324
|
refuseCaveated(this.getCapabilityRequest()?.manifests);
|
|
20216
20325
|
if (options?.host !== void 0) {
|
|
20217
20326
|
const overrideHost = options.host.trim();
|
|
@@ -20223,7 +20332,7 @@ var NodeUserAuthorization = class {
|
|
|
20223
20332
|
const signingAddress = (0, import_sdk_core5.canonicalizeAddress)(await this.signer.getAddress());
|
|
20224
20333
|
const signingChainId = await this.signer.getChainId();
|
|
20225
20334
|
await this.resolveTinyCloudHostsForSignIn(signingAddress, signingChainId);
|
|
20226
|
-
const { prepared, keyId, jwk } = await this.prepareSessionForSigning();
|
|
20335
|
+
const { prepared, keyId, jwk, address, chainId } = await this.prepareSessionForSigning();
|
|
20227
20336
|
const presentation = this.buildPresentationEnvelope(options?.reason);
|
|
20228
20337
|
const hostHint = this.primaryTinyCloudHost;
|
|
20229
20338
|
const originalRequest = {
|
|
@@ -20235,12 +20344,32 @@ var NodeUserAuthorization = class {
|
|
|
20235
20344
|
keyId: options?.openkeyKeyId,
|
|
20236
20345
|
presentation
|
|
20237
20346
|
};
|
|
20347
|
+
return {
|
|
20348
|
+
request: originalRequest,
|
|
20349
|
+
prepared,
|
|
20350
|
+
keyId,
|
|
20351
|
+
jwk,
|
|
20352
|
+
address,
|
|
20353
|
+
chainId,
|
|
20354
|
+
generation: this.sessionGeneration,
|
|
20355
|
+
createdAt: Date.now()
|
|
20356
|
+
};
|
|
20357
|
+
}
|
|
20358
|
+
/** Complete a prepared approval. The caller starts authorization before awaiting this method. */
|
|
20359
|
+
async completeOpenKeySignIn(pending, authorization, options) {
|
|
20360
|
+
if (Date.now() - pending.createdAt > 5 * 6e4) {
|
|
20361
|
+
throw new Error("Prepared OpenKey sign-in expired; prepare again");
|
|
20362
|
+
}
|
|
20363
|
+
if (pending.generation !== this.sessionGeneration) {
|
|
20364
|
+
throw new Error("Prepared OpenKey sign-in expired after sign-out");
|
|
20365
|
+
}
|
|
20366
|
+
const { request: originalRequest, prepared, keyId, jwk } = pending;
|
|
20238
20367
|
let result;
|
|
20239
20368
|
let signed2;
|
|
20240
20369
|
let signature2;
|
|
20241
20370
|
for (let attempt = 0; ; ) {
|
|
20242
20371
|
try {
|
|
20243
|
-
result ?? (result = await
|
|
20372
|
+
result ?? (result = await authorization);
|
|
20244
20373
|
signed2 = await this.verifyOpenKeyResult(result, prepared);
|
|
20245
20374
|
signature2 = result.signature;
|
|
20246
20375
|
break;
|
|
@@ -21136,6 +21265,7 @@ var ROOT_DELEGATION_ACTIONS = [
|
|
|
21136
21265
|
import_sdk_core10.DUCKDB.ALL
|
|
21137
21266
|
];
|
|
21138
21267
|
var DEFAULT_SESSION_EXPIRATION_MS = import_sdk_core10.EXPIRY.SESSION_MS;
|
|
21268
|
+
var OWNER_REVOCATION_WINDOW_MS = 10 * 6e4;
|
|
21139
21269
|
function unifiedRootAttenuation(capabilities) {
|
|
21140
21270
|
const attenuation = {};
|
|
21141
21271
|
for (const capability of capabilities) {
|
|
@@ -21633,6 +21763,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21633
21763
|
constructor(config = {}) {
|
|
21634
21764
|
this.signer = null;
|
|
21635
21765
|
this.auth = null;
|
|
21766
|
+
this.consumedOpenKeyPendings = /* @__PURE__ */ new WeakSet();
|
|
21636
21767
|
this.tc = null;
|
|
21637
21768
|
this._chainId = 1;
|
|
21638
21769
|
this.replicaPurgeFailedHandlers = /* @__PURE__ */ new Set();
|
|
@@ -21667,6 +21798,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21667
21798
|
return void 0;
|
|
21668
21799
|
}
|
|
21669
21800
|
};
|
|
21801
|
+
this._keysUnavailable = [];
|
|
21670
21802
|
this._baseVaults = /* @__PURE__ */ new Map();
|
|
21671
21803
|
this._baseSecrets = /* @__PURE__ */ new Map();
|
|
21672
21804
|
this._secrets = /* @__PURE__ */ new Map();
|
|
@@ -22494,6 +22626,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22494
22626
|
this._hooks = void 0;
|
|
22495
22627
|
this._vault = void 0;
|
|
22496
22628
|
this._encryption = void 0;
|
|
22629
|
+
this._namedKeyClient = void 0;
|
|
22630
|
+
this._keysUnavailable = [];
|
|
22497
22631
|
this._baseVaults.clear();
|
|
22498
22632
|
this._baseSecrets.clear();
|
|
22499
22633
|
this._secrets.clear();
|
|
@@ -22570,6 +22704,37 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22570
22704
|
}
|
|
22571
22705
|
await this.signInWith(() => this.auth.signInWithOpenKey(authorizeFn, options));
|
|
22572
22706
|
}
|
|
22707
|
+
/** Prepare the OpenKey request before the approval click. */
|
|
22708
|
+
async prepareOpenKeySignIn() {
|
|
22709
|
+
if (!this.signer || !this.tc || !this.auth) {
|
|
22710
|
+
throw new Error("Cannot prepare OpenKey sign-in in session-only mode. Provide a signer in config.");
|
|
22711
|
+
}
|
|
22712
|
+
await this.signOutInFlight;
|
|
22713
|
+
await this.accountRegistryTail;
|
|
22714
|
+
await this.wasmBindings.ensureInitialized?.();
|
|
22715
|
+
return this.auth.prepareOpenKeySignIn();
|
|
22716
|
+
}
|
|
22717
|
+
/** Start OpenKey authorization synchronously, then complete normal session setup. */
|
|
22718
|
+
signInWithPreparedOpenKey(pending, authorizeFn) {
|
|
22719
|
+
if (!this.signer || !this.tc || !this.auth || this.signOutInFlight) {
|
|
22720
|
+
throw new Error("Prepared OpenKey sign-in is unavailable");
|
|
22721
|
+
}
|
|
22722
|
+
if (this.consumedOpenKeyPendings.has(pending)) {
|
|
22723
|
+
throw new Error("Prepared OpenKey sign-in was already used");
|
|
22724
|
+
}
|
|
22725
|
+
if (Date.now() - pending.createdAt > 5 * 6e4) {
|
|
22726
|
+
throw new Error("Prepared OpenKey sign-in expired; prepare again");
|
|
22727
|
+
}
|
|
22728
|
+
this.consumedOpenKeyPendings.add(pending);
|
|
22729
|
+
const authorization = authorizeFn(pending.request);
|
|
22730
|
+
void authorization.catch(() => void 0);
|
|
22731
|
+
return this.signInWith(() => {
|
|
22732
|
+
if (this._address !== pending.address || this._chainId !== pending.chainId) {
|
|
22733
|
+
throw new Error("OpenKey account changed after sign-in preparation");
|
|
22734
|
+
}
|
|
22735
|
+
return this.auth.completeOpenKeySignIn(pending, authorization);
|
|
22736
|
+
});
|
|
22737
|
+
}
|
|
22573
22738
|
async signInWith(performSignIn) {
|
|
22574
22739
|
refuseCaveated(this.auth?.capabilityRequest?.manifests);
|
|
22575
22740
|
const epoch = this.sessionEpoch;
|
|
@@ -22616,6 +22781,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22616
22781
|
this._hooks = void 0;
|
|
22617
22782
|
this._vault = void 0;
|
|
22618
22783
|
this._encryption = void 0;
|
|
22784
|
+
this._namedKeyClient = void 0;
|
|
22785
|
+
this._keysUnavailable = [];
|
|
22619
22786
|
this._baseVaults.clear();
|
|
22620
22787
|
this._baseSecrets.clear();
|
|
22621
22788
|
this._secrets.clear();
|
|
@@ -22668,6 +22835,26 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22668
22835
|
this.assertSessionEpoch(epoch, "Sign-in");
|
|
22669
22836
|
await this.ensureRequestedEncryptionNetworks();
|
|
22670
22837
|
this.assertSessionEpoch(epoch, "Sign-in");
|
|
22838
|
+
this._keysUnavailable = this.auth?.unavailableKeyPaths ?? [];
|
|
22839
|
+
if (this._keysUnavailable.length > 0) {
|
|
22840
|
+
this.notificationHandler.warning(`Named keys unavailable: ${this._keysUnavailable.join(", ")}`);
|
|
22841
|
+
}
|
|
22842
|
+
if (this.nodeFeatures.includes("encryption-named-keys-v1")) {
|
|
22843
|
+
for (const manifest of this.auth?.capabilityRequest?.manifests ?? []) {
|
|
22844
|
+
for (const declaration of manifest.keys ?? []) {
|
|
22845
|
+
if ("path" in declaration && declaration.abilities.includes("key.create") && !this._keysUnavailable.includes(declaration.path)) {
|
|
22846
|
+
try {
|
|
22847
|
+
await this.keys.ensureKey(declaration.path);
|
|
22848
|
+
} catch (error) {
|
|
22849
|
+
this._keysUnavailable.push(declaration.path);
|
|
22850
|
+
const code = error && typeof error === "object" && "code" in error && typeof error.code === "string" && /^[A-Z_]+$/.test(error.code) ? error.code : "UNKNOWN";
|
|
22851
|
+
this.notificationHandler.warning(`Named key unavailable: ${declaration.path} (${code})`);
|
|
22852
|
+
}
|
|
22853
|
+
this.assertSessionEpoch(epoch, "Sign-in");
|
|
22854
|
+
}
|
|
22855
|
+
}
|
|
22856
|
+
}
|
|
22857
|
+
}
|
|
22671
22858
|
if (!bootstrapped && this.config.manifest === void 0 && this.config.capabilityRequest === void 0) {
|
|
22672
22859
|
await this.ensureOwnedSpaceHostedById(this.ownedSpaceId(import_sdk_core10.SECRETS_SPACE));
|
|
22673
22860
|
}
|
|
@@ -23244,7 +23431,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23244
23431
|
if (!this.currentSessionCoversAccountRegistry()) {
|
|
23245
23432
|
if (!_TinyCloudNode.accountRegistrySkipLogged) {
|
|
23246
23433
|
_TinyCloudNode.accountRegistrySkipLogged = true;
|
|
23247
|
-
|
|
23434
|
+
import_sdk_services4.tinyCloudDebugLogger.log("registry.skip");
|
|
23248
23435
|
}
|
|
23249
23436
|
return;
|
|
23250
23437
|
}
|
|
@@ -23455,7 +23642,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23455
23642
|
const networkIds = /* @__PURE__ */ new Set();
|
|
23456
23643
|
for (const resource of request.resources) {
|
|
23457
23644
|
if (resource.service === import_sdk_core10.ENCRYPTION_PERMISSION_SERVICE && resource.path.startsWith("urn:tinycloud:encryption:") && resource.actions.includes(DECRYPT_ACTION2)) {
|
|
23458
|
-
|
|
23645
|
+
const parsed = resource.path.includes("/") ? (0, import_resource2.parseEncryptionResource)(resource.path) : { ...(0, import_sdk_core10.parseNetworkId)(resource.path), kind: "network" };
|
|
23646
|
+
if (parsed.kind === "network") networkIds.add(resource.path);
|
|
23459
23647
|
}
|
|
23460
23648
|
}
|
|
23461
23649
|
return [...networkIds];
|
|
@@ -24005,6 +24193,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
24005
24193
|
this._hooks = stagedGraph.hooks;
|
|
24006
24194
|
this._vault = stagedGraph.vault;
|
|
24007
24195
|
this._encryption = stagedGraph.encryption;
|
|
24196
|
+
this._namedKeyClient = void 0;
|
|
24197
|
+
this._keysUnavailable = [];
|
|
24008
24198
|
this._capabilityRegistry = stagedGraph.capabilityRegistry;
|
|
24009
24199
|
this._keyProvider = stagedGraph.keyProvider;
|
|
24010
24200
|
this._sharingService = stagedGraph.sharingService;
|
|
@@ -24186,6 +24376,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
24186
24376
|
invoke: graph.invoke,
|
|
24187
24377
|
invokeAny: graph.invokeAny,
|
|
24188
24378
|
signOwnerRevocation: this.signer ? (cid) => this.signOwnerRevocation(cid) : void 0,
|
|
24379
|
+
sessionInvocationSigner: this.sessionInvocationSignerFor(input.serviceSession),
|
|
24189
24380
|
fetch: graph.fetch
|
|
24190
24381
|
});
|
|
24191
24382
|
const keyProvider = new WasmKeyProvider({ sessionManager: input.manager });
|
|
@@ -24401,6 +24592,31 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
24401
24592
|
if (session === void 0) throw new Error("Not signed in. Call signIn() first.");
|
|
24402
24593
|
return signBytesWithJwk(bytes, session.jwk);
|
|
24403
24594
|
}
|
|
24595
|
+
/**
|
|
24596
|
+
* Signs proofless invocations with `session`'s own did:key, for the node's
|
|
24597
|
+
* issued-delegation list and revocation by the delegator. The audience is
|
|
24598
|
+
* this Node's DID; each invocation lives 60 seconds.
|
|
24599
|
+
*/
|
|
24600
|
+
sessionInvocationSignerFor(session) {
|
|
24601
|
+
return {
|
|
24602
|
+
did: () => session.verificationMethod.split("#", 1)[0],
|
|
24603
|
+
sign: async (attenuation) => {
|
|
24604
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
24605
|
+
const invocation = await (0, import_sdk_core10.signCompactUcanAuthorization)({
|
|
24606
|
+
issuerDid: session.verificationMethod.split("#", 1)[0],
|
|
24607
|
+
audienceDid: await this.fetchNodeId(),
|
|
24608
|
+
attenuation,
|
|
24609
|
+
facts: [{}],
|
|
24610
|
+
proofs: [],
|
|
24611
|
+
notBefore: now,
|
|
24612
|
+
expiresAt: now + 60,
|
|
24613
|
+
nonce: `urn:uuid:${crypto.randomUUID()}`,
|
|
24614
|
+
sign: (bytes) => signBytesWithJwk(bytes, session.jwk)
|
|
24615
|
+
});
|
|
24616
|
+
return invocation.authorization;
|
|
24617
|
+
}
|
|
24618
|
+
};
|
|
24619
|
+
}
|
|
24404
24620
|
/** Author a proofless Policy/v3 owner root with the authenticated holder key. */
|
|
24405
24621
|
async createUnifiedOwnerRoot(input) {
|
|
24406
24622
|
const ownerDid = this.credentialHolderDid;
|
|
@@ -24914,7 +25130,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
24914
25130
|
);
|
|
24915
25131
|
graph.assertActive();
|
|
24916
25132
|
if (!response.ok) {
|
|
24917
|
-
const permissionHint = response.status === 401 || response.status === 403 ? (0,
|
|
25133
|
+
const permissionHint = response.status === 401 || response.status === 403 ? (0, import_sdk_services4.parsePermissionHint)({
|
|
24918
25134
|
service: "tinycloud.encryption",
|
|
24919
25135
|
path: networkId,
|
|
24920
25136
|
actions: ["tinycloud.encryption/decrypt"]
|
|
@@ -24935,7 +25151,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
24935
25151
|
graph.assertActive();
|
|
24936
25152
|
const signed2 = await this.signRawNetworkAuthorization({
|
|
24937
25153
|
targetNode: input.targetNode,
|
|
24938
|
-
networkId: input.networkId
|
|
25154
|
+
networkId: input.body.keyPath === void 0 ? input.networkId : `${input.networkId}/${input.body.keyPath}`,
|
|
24939
25155
|
action: DECRYPT_ACTION2,
|
|
24940
25156
|
facts: input.facts
|
|
24941
25157
|
}, { graph, session });
|
|
@@ -25108,6 +25324,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
25108
25324
|
invoke: graph.invoke,
|
|
25109
25325
|
invokeAny: graph.invokeAny,
|
|
25110
25326
|
signOwnerRevocation: this.signer ? (cid) => this.signOwnerRevocation(cid) : void 0,
|
|
25327
|
+
sessionInvocationSigner: this.sessionInvocationSignerFor(serviceSession),
|
|
25111
25328
|
fetch: graph.fetch
|
|
25112
25329
|
});
|
|
25113
25330
|
this._spaceService = new import_sdk_core10.SpaceService({
|
|
@@ -25358,7 +25575,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
25358
25575
|
* Sign a wallet (CACAO) revocation of the delegation `cid` for the node's
|
|
25359
25576
|
* `POST /revoke`, and return its Authorization header value. The node
|
|
25360
25577
|
* accepts it when the owner delegated `cid` or owns a space its abilities
|
|
25361
|
-
* target, whichever key issued it. Valid for
|
|
25578
|
+
* target, whichever key issued it. Valid for 10 minutes. Requires the
|
|
25362
25579
|
* owner wallet signer.
|
|
25363
25580
|
*/
|
|
25364
25581
|
async signOwnerRevocation(cid) {
|
|
@@ -25374,7 +25591,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
25374
25591
|
chainId: session.chainId,
|
|
25375
25592
|
nonce: (0, import_siwe2.generateNonce)(),
|
|
25376
25593
|
issuedAt: issuedAt.toISOString(),
|
|
25377
|
-
expirationTime: new Date(issuedAt.getTime() +
|
|
25594
|
+
expirationTime: new Date(issuedAt.getTime() + OWNER_REVOCATION_WINDOW_MS).toISOString()
|
|
25378
25595
|
}).prepareMessage();
|
|
25379
25596
|
const signature2 = await this.signer.signMessage(siwe);
|
|
25380
25597
|
return this.wasmBindings.siweToDelegationHeaders({ siwe, signature: signature2 }).Authorization;
|
|
@@ -25601,6 +25818,40 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
25601
25818
|
get encryption() {
|
|
25602
25819
|
return this.getEncryptionService();
|
|
25603
25820
|
}
|
|
25821
|
+
/** Named keys bound to the current session. */
|
|
25822
|
+
get keys() {
|
|
25823
|
+
return new Proxy({}, {
|
|
25824
|
+
get: (_target, property) => {
|
|
25825
|
+
if (property === "refreshNodeInfo") {
|
|
25826
|
+
return () => {
|
|
25827
|
+
void this._namedKeyClient?.then((client) => client.refreshNodeInfo());
|
|
25828
|
+
};
|
|
25829
|
+
}
|
|
25830
|
+
if (typeof property !== "string" || ![
|
|
25831
|
+
"getPublicKey",
|
|
25832
|
+
"describeKey",
|
|
25833
|
+
"createKey",
|
|
25834
|
+
"blockKey",
|
|
25835
|
+
"unblockKey",
|
|
25836
|
+
"ensureKey",
|
|
25837
|
+
"encrypt",
|
|
25838
|
+
"decrypt",
|
|
25839
|
+
"decryptMany"
|
|
25840
|
+
].includes(property)) return void 0;
|
|
25841
|
+
return (...args) => {
|
|
25842
|
+
this._namedKeyClient ?? (this._namedKeyClient = import("./encryption-keys.cjs").then(({ createNamedKeyClient }) => createNamedKeyClient(this)));
|
|
25843
|
+
return this._namedKeyClient.then((client) => {
|
|
25844
|
+
const method = client[property];
|
|
25845
|
+
return method.apply(client, args);
|
|
25846
|
+
});
|
|
25847
|
+
};
|
|
25848
|
+
}
|
|
25849
|
+
});
|
|
25850
|
+
}
|
|
25851
|
+
/** Manifest key paths unavailable during the latest `signIn()` attempt. */
|
|
25852
|
+
get keysUnavailable() {
|
|
25853
|
+
return this._keysUnavailable;
|
|
25854
|
+
}
|
|
25604
25855
|
async getEncryptionNetwork(nameOrNetworkId = this.getDefaultEncryptionNetworkId()) {
|
|
25605
25856
|
const networkId = nameOrNetworkId.startsWith("urn:tinycloud:encryption:") ? nameOrNetworkId : this.getDefaultEncryptionNetworkId(nameOrNetworkId);
|
|
25606
25857
|
return this.fetchEncryptionNetworkAt(
|
|
@@ -25728,7 +25979,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
25728
25979
|
);
|
|
25729
25980
|
if (result.status !== "ok") {
|
|
25730
25981
|
if (result.status === "permission_required") {
|
|
25731
|
-
const hint = (0,
|
|
25982
|
+
const hint = (0, import_sdk_services4.parsePermissionHint)(result.hint);
|
|
25732
25983
|
if (hint === void 0) return { status: "read_failed" };
|
|
25733
25984
|
return {
|
|
25734
25985
|
status: "permission_required",
|
|
@@ -25857,7 +26108,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
25857
26108
|
if (!session || !Array.isArray(permissions) || permissions.length === 0) {
|
|
25858
26109
|
return false;
|
|
25859
26110
|
}
|
|
25860
|
-
const expanded =
|
|
26111
|
+
const expanded = (0, import_sdk_core10.expandRuntimePermissionEntries)(permissions, (0, import_sdk_core10.pkhDid)(session.address, session.chainId));
|
|
25861
26112
|
if (this.sessionCoversPermissionEntries(session, expanded)) {
|
|
25862
26113
|
return true;
|
|
25863
26114
|
}
|
|
@@ -25877,7 +26128,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
25877
26128
|
if (!session || !Array.isArray(permissions) || permissions.length === 0) {
|
|
25878
26129
|
return [];
|
|
25879
26130
|
}
|
|
25880
|
-
const expanded =
|
|
26131
|
+
const expanded = (0, import_sdk_core10.expandRuntimePermissionEntries)(permissions, (0, import_sdk_core10.pkhDid)(session.address, session.chainId));
|
|
25881
26132
|
return this.findRuntimeGrantsForPermissionEntries(expanded, session).map(
|
|
25882
26133
|
(grant) => grant.delegation
|
|
25883
26134
|
);
|
|
@@ -26052,7 +26303,28 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
26052
26303
|
throw new import_sdk_core10.SessionExpiredError(sessionExpiry);
|
|
26053
26304
|
}
|
|
26054
26305
|
}
|
|
26055
|
-
const expanded =
|
|
26306
|
+
const expanded = (0, import_sdk_core10.expandRuntimePermissionEntries)(permissions, (0, import_sdk_core10.pkhDid)(session.address, session.chainId));
|
|
26307
|
+
const bindsShareKey = expanded.some((entry) => entry.service === import_sdk_core10.ENCRYPTION_PERMISSION_SERVICE && entry.actions.includes("tinycloud.encryption/key.create"));
|
|
26308
|
+
const captured = bindsShareKey ? { ...session, jwk: { ...options?.expectedSessionKey ?? session.jwk } } : session;
|
|
26309
|
+
const assertCapturedKey = bindsShareKey ? () => {
|
|
26310
|
+
const live = session.jwk;
|
|
26311
|
+
const key2 = captured.jwk;
|
|
26312
|
+
if (this.currentTinyCloudSession() !== session || session.verificationMethod !== captured.verificationMethod || session.delegationCid !== captured.delegationCid || session.spaceId !== captured.spaceId || session.address !== captured.address || session.chainId !== captured.chainId || live?.kty !== key2.kty || live?.crv !== key2.crv || live?.x !== key2.x || live?.y !== key2.y || live?.d !== key2.d) {
|
|
26313
|
+
throw new Error("Runtime permission request cancelled because the session key changed.");
|
|
26314
|
+
}
|
|
26315
|
+
} : void 0;
|
|
26316
|
+
const sessionKeyDid = bindsShareKey ? this.sessionDid : void 0;
|
|
26317
|
+
if (bindsShareKey) {
|
|
26318
|
+
const key2 = captured.jwk;
|
|
26319
|
+
if (key2.kty !== "OKP" || key2.crv !== "Ed25519" || typeof key2.x !== "string") {
|
|
26320
|
+
throw new Error("Runtime permission session key does not match its signed identity.");
|
|
26321
|
+
}
|
|
26322
|
+
const keyDid = (0, import_share_envelope.didKeyFromEd25519PublicKey)(base64UrlDecode(key2.x));
|
|
26323
|
+
if (!didPrincipalMatches2(keyDid, sessionKeyDid) || !didPrincipalMatches2(keyDid, captured.verificationMethod) || !didPrincipalMatches2(keyDid, new import_siwe2.SiweMessage(session.siwe).uri)) {
|
|
26324
|
+
throw new Error("Runtime permission session key does not match its signed identity.");
|
|
26325
|
+
}
|
|
26326
|
+
assertCapturedKey();
|
|
26327
|
+
}
|
|
26056
26328
|
if (this.sessionCoversPermissionEntries(session, expanded)) {
|
|
26057
26329
|
return [];
|
|
26058
26330
|
}
|
|
@@ -26102,13 +26374,13 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
26102
26374
|
return this.wasmBindings.prepareSession({
|
|
26103
26375
|
abilities,
|
|
26104
26376
|
...rawForDelegation.length > 0 ? { rawAbilities: this.permissionsToRawAbilities(rawForDelegation) } : {},
|
|
26105
|
-
address: this.wasmBindings.ensureEip55(
|
|
26106
|
-
chainId:
|
|
26377
|
+
address: this.wasmBindings.ensureEip55(captured.address),
|
|
26378
|
+
chainId: captured.chainId,
|
|
26107
26379
|
domain: this.siweDomain,
|
|
26108
26380
|
issuedAt: now.toISOString(),
|
|
26109
26381
|
expirationTime: expiresAt.toISOString(),
|
|
26110
26382
|
spaceId,
|
|
26111
|
-
jwk:
|
|
26383
|
+
jwk: captured.jwk
|
|
26112
26384
|
});
|
|
26113
26385
|
} catch (error) {
|
|
26114
26386
|
if (rawForDelegation.some((entry) => this.isRawRevocationPermissionEntry(entry)) && /invalid ReCap resource URI/i.test(error instanceof Error ? error.message : String(error))) {
|
|
@@ -26120,8 +26392,13 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
26120
26392
|
throw error;
|
|
26121
26393
|
}
|
|
26122
26394
|
})();
|
|
26395
|
+
if (bindsShareKey && !didPrincipalMatches2(new import_siwe2.SiweMessage(prepared.siwe).uri, sessionKeyDid)) {
|
|
26396
|
+
throw new Error("Prepared runtime permission targets a different session key.");
|
|
26397
|
+
}
|
|
26398
|
+
assertCapturedKey?.();
|
|
26123
26399
|
const signature2 = await this.signer.signMessage(prepared.siwe);
|
|
26124
26400
|
graph.assertActive();
|
|
26401
|
+
assertCapturedKey?.();
|
|
26125
26402
|
const delegatedSession = this.wasmBindings.completeSessionSetup({
|
|
26126
26403
|
...prepared,
|
|
26127
26404
|
signature: signature2
|
|
@@ -26150,18 +26427,19 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
26150
26427
|
delegatedSession,
|
|
26151
26428
|
delegatedEntries,
|
|
26152
26429
|
spaceId,
|
|
26153
|
-
|
|
26430
|
+
captured,
|
|
26154
26431
|
expiresAt,
|
|
26155
26432
|
{ siwe: prepared.siwe, signature: signature2 }
|
|
26156
26433
|
);
|
|
26157
26434
|
graph.assertActive();
|
|
26435
|
+
assertCapturedKey?.();
|
|
26158
26436
|
this.runtimePermissionGrants.push({
|
|
26159
26437
|
session: {
|
|
26160
26438
|
delegationHeader: delegatedSession.delegationHeader,
|
|
26161
26439
|
delegationCid: delegatedSession.delegationCid,
|
|
26162
26440
|
spaceId,
|
|
26163
|
-
verificationMethod:
|
|
26164
|
-
jwk:
|
|
26441
|
+
verificationMethod: captured.verificationMethod,
|
|
26442
|
+
jwk: captured.jwk
|
|
26165
26443
|
},
|
|
26166
26444
|
delegation,
|
|
26167
26445
|
operations: this.permissionOperations(delegatedEntries, spaceId),
|
|
@@ -26490,7 +26768,19 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
26490
26768
|
"delegateTo requires a non-empty permissions array"
|
|
26491
26769
|
);
|
|
26492
26770
|
}
|
|
26493
|
-
const expandedEntries =
|
|
26771
|
+
const expandedEntries = (0, import_sdk_core10.expandDelegationPermissionEntries)(permissions);
|
|
26772
|
+
const namedKeyEntries = expandedEntries.filter((entry) => entry.service === import_sdk_core10.ENCRYPTION_PERMISSION_SERVICE && entry.path.includes("/"));
|
|
26773
|
+
if (namedKeyEntries.length > 0) {
|
|
26774
|
+
if (expandedEntries.some((entry) => entry.service === import_sdk_core10.ENCRYPTION_PERMISSION_SERVICE && !entry.path.includes("/"))) {
|
|
26775
|
+
throw new import_sdk_core10.ManifestValidationError("named-key delegation cannot include bare-network permissions");
|
|
26776
|
+
}
|
|
26777
|
+
if (options?.forceWalletSign) {
|
|
26778
|
+
throw new import_sdk_core10.ManifestValidationError("named-key delegation requires a signed session grant");
|
|
26779
|
+
}
|
|
26780
|
+
const granted = this.projectSignedRecapCapabilities(session.siwe);
|
|
26781
|
+
const { subset, missing } = this.signedCapabilitySubset(namedKeyEntries, granted);
|
|
26782
|
+
if (!subset) throw new import_sdk_core10.PermissionNotInManifestError(missing, granted);
|
|
26783
|
+
}
|
|
26494
26784
|
const now = /* @__PURE__ */ new Date();
|
|
26495
26785
|
const expiryMs = resolveExpiryMs(options?.expiry);
|
|
26496
26786
|
const expirationTime = new Date(now.getTime() + expiryMs);
|
|
@@ -28009,7 +28299,7 @@ var FileSessionStorage = class {
|
|
|
28009
28299
|
};
|
|
28010
28300
|
|
|
28011
28301
|
// src/authorization/openKeyBridge.ts
|
|
28012
|
-
function wireOpenKeyAuthorize(openkey) {
|
|
28302
|
+
function wireOpenKeyAuthorize(openkey, opts) {
|
|
28013
28303
|
return async (input) => {
|
|
28014
28304
|
const result = await openkey.authorizeTinyCloud({
|
|
28015
28305
|
protocolVersion: 1,
|
|
@@ -28020,7 +28310,7 @@ function wireOpenKeyAuthorize(openkey) {
|
|
|
28020
28310
|
// Forward the presentation envelope verbatim — the OpenKey SDK
|
|
28021
28311
|
// validates and size-bounds it before use.
|
|
28022
28312
|
presentation: input.presentation
|
|
28023
|
-
});
|
|
28313
|
+
}, opts);
|
|
28024
28314
|
if (result.protocolVersion !== 1) {
|
|
28025
28315
|
throw new Error(
|
|
28026
28316
|
`wireOpenKeyAuthorize: OpenKey returned unsupported protocolVersion ${result.protocolVersion}`
|
|
@@ -28073,7 +28363,7 @@ var import_node_fs = require("fs");
|
|
|
28073
28363
|
var import_promises = require("fs/promises");
|
|
28074
28364
|
var import_node_path = require("path");
|
|
28075
28365
|
var import_sdk_core14 = require("@tinycloud/sdk-core");
|
|
28076
|
-
var
|
|
28366
|
+
var import_sdk_services5 = require("@tinycloud/sdk-services");
|
|
28077
28367
|
var import_contractConstants = require("@tinycloud/sdk-services/kv/replication/contractConstants");
|
|
28078
28368
|
init_authority();
|
|
28079
28369
|
var REPLICA_LOGOUT_PENDING_FILE = "replica-logout-pending.json";
|
|
@@ -28635,7 +28925,7 @@ function sha256Base32(material) {
|
|
|
28635
28925
|
return out.slice(0, 26);
|
|
28636
28926
|
}
|
|
28637
28927
|
function partitionDirOf(root, identity) {
|
|
28638
|
-
return (0, import_node_path.join)(root, sha256Base32((0,
|
|
28928
|
+
return (0, import_node_path.join)(root, sha256Base32((0, import_sdk_services5.replicationIdentityKey)(identity)));
|
|
28639
28929
|
}
|
|
28640
28930
|
function replicaHashOf(prefix, deviceDid) {
|
|
28641
28931
|
return sha256Base32(`${prefix}
|
|
@@ -28709,8 +28999,8 @@ function deviceRecordOf(value) {
|
|
|
28709
28999
|
}
|
|
28710
29000
|
function sameIdentity(a, b) {
|
|
28711
29001
|
try {
|
|
28712
|
-
const left = (0,
|
|
28713
|
-
const right = (0,
|
|
29002
|
+
const left = (0, import_sdk_services5.canonicalReplicationIdentity)(a);
|
|
29003
|
+
const right = (0, import_sdk_services5.canonicalReplicationIdentity)(b);
|
|
28714
29004
|
return left.host === right.host && left.space === right.space && left.principal === right.principal;
|
|
28715
29005
|
} catch {
|
|
28716
29006
|
return false;
|
|
@@ -28726,7 +29016,7 @@ async function ensurePartitionIdentity(dir, identity, dirSync) {
|
|
|
28726
29016
|
const file = (0, import_node_path.join)(dir, "identity.json");
|
|
28727
29017
|
const existing = await readJsonFile(file);
|
|
28728
29018
|
if (existing === void 0) {
|
|
28729
|
-
await writeJsonAtomic(file, { v: 1, ...(0,
|
|
29019
|
+
await writeJsonAtomic(file, { v: 1, ...(0, import_sdk_services5.canonicalReplicationIdentity)(identity) }, dirSync);
|
|
28730
29020
|
return;
|
|
28731
29021
|
}
|
|
28732
29022
|
const record = identityRecordOf(existing);
|
|
@@ -28825,7 +29115,7 @@ withGate_fn = async function(section) {
|
|
|
28825
29115
|
if (snapshot === void 0) throw gated("gate_unavailable");
|
|
28826
29116
|
const [principalHash, identityKeyHash] = await Promise.all([
|
|
28827
29117
|
gate.principalHashOf(snapshot.salt, this.identity.principal),
|
|
28828
|
-
gate.identityHashOf(snapshot.salt, (0,
|
|
29118
|
+
gate.identityHashOf(snapshot.salt, (0, import_sdk_services5.replicationIdentityKey)(this.identity))
|
|
28829
29119
|
]);
|
|
28830
29120
|
const access = {
|
|
28831
29121
|
stamp: __privateGet(this, _stamp),
|
|
@@ -29096,7 +29386,8 @@ var SqliteReplicaHandle = class {
|
|
|
29096
29386
|
options.signal.addEventListener("abort", onAbort, { once: true });
|
|
29097
29387
|
const engine = new (__privateGet(this, _replica)).Replica({
|
|
29098
29388
|
store: __privateGet(this, _store),
|
|
29099
|
-
transportFor
|
|
29389
|
+
transportFor,
|
|
29390
|
+
...this.spec.contentFetch === void 0 ? {} : { contentFetch: this.spec.contentFetch }
|
|
29100
29391
|
});
|
|
29101
29392
|
const running = engine.sync({ signal: abort.signal });
|
|
29102
29393
|
const registration = { promise: running, abort: () => abort.abort() };
|
|
@@ -29325,7 +29616,7 @@ function createSqliteReplicaStorage(options) {
|
|
|
29325
29616
|
if (snapshot === void 0) throw gated("gate_unavailable");
|
|
29326
29617
|
const [principalHash, identityKeyHash] = await Promise.all([
|
|
29327
29618
|
gate.principalHashOf(snapshot.salt, principal),
|
|
29328
|
-
identity === void 0 ? Promise.resolve(void 0) : gate.identityHashOf(snapshot.salt, (0,
|
|
29619
|
+
identity === void 0 ? Promise.resolve(void 0) : gate.identityHashOf(snapshot.salt, (0, import_sdk_services5.replicationIdentityKey)(identity))
|
|
29329
29620
|
]);
|
|
29330
29621
|
return profileSection(async () => {
|
|
29331
29622
|
assertLogoutClear(options.logoutPendingFile);
|
|
@@ -29348,7 +29639,7 @@ function createSqliteReplicaStorage(options) {
|
|
|
29348
29639
|
if (snapshot === void 0) throw gated("gate_unavailable");
|
|
29349
29640
|
const [principalHash, identityKeyHash] = await Promise.all([
|
|
29350
29641
|
gate.principalHashOf(snapshot.salt, spec.identity.principal),
|
|
29351
|
-
gate.identityHashOf(snapshot.salt, (0,
|
|
29642
|
+
gate.identityHashOf(snapshot.salt, (0, import_sdk_services5.replicationIdentityKey)(spec.identity))
|
|
29352
29643
|
]);
|
|
29353
29644
|
const access = {
|
|
29354
29645
|
stamp: spec.stamp,
|
|
@@ -29432,7 +29723,7 @@ function createSqliteReplicaStorage(options) {
|
|
|
29432
29723
|
const identity = identityRecordOf(await readJsonFile((0, import_node_path.join)(idDir, "identity.json")));
|
|
29433
29724
|
if (identity === void 0) throw new ReplicaStorageError("STORAGE_ERROR", `Missing identity at ${idDir}.`);
|
|
29434
29725
|
if (await gate.principalHashOf(salt, identity.principal) !== principalHash) continue;
|
|
29435
|
-
const identityKeyHash = await gate.identityHashOf(salt, (0,
|
|
29726
|
+
const identityKeyHash = await gate.identityHashOf(salt, (0, import_sdk_services5.replicationIdentityKey)(identity));
|
|
29436
29727
|
const recordedAt = Date.now();
|
|
29437
29728
|
if (await pathExists((0, import_node_path.join)(idDir, "pending.json")) || await pathExists((0, import_node_path.join)(idDir, "device.jwk"))) {
|
|
29438
29729
|
intents.push({ id: `pending:${identityKeyHash}`, kind: "pending", identityKeyHash, principalHash, recordedAt });
|
|
@@ -30110,7 +30401,7 @@ function createSqliteReplicaStorage(options) {
|
|
|
30110
30401
|
for (const dir of await identityDirs()) {
|
|
30111
30402
|
const identity = identityRecordOf(await readJsonFile((0, import_node_path.join)(dir, "identity.json")));
|
|
30112
30403
|
if (identity === void 0) throw new ReplicaStorageError("STORAGE_ERROR", `Missing identity at ${dir}.`);
|
|
30113
|
-
const hash3 = await gate.identityHashOf(state.salt, (0,
|
|
30404
|
+
const hash3 = await gate.identityHashOf(state.salt, (0, import_sdk_services5.replicationIdentityKey)(identity));
|
|
30114
30405
|
if (intent.kind === "pending" ? hash3 === intent.identityKeyHash : intent.partitionId.startsWith(`${hash3}:`)) {
|
|
30115
30406
|
idDir = dir;
|
|
30116
30407
|
break;
|
|
@@ -30256,7 +30547,7 @@ function createSqliteReplicaStorage(options) {
|
|
|
30256
30547
|
const gate = await fileGate();
|
|
30257
30548
|
const snapshot = await gate.readGateSnapshot(profileDir);
|
|
30258
30549
|
if (snapshot === void 0) throw gated("gate_unavailable");
|
|
30259
|
-
const identityKeyHash = await gate.identityHashOf(snapshot.salt, (0,
|
|
30550
|
+
const identityKeyHash = await gate.identityHashOf(snapshot.salt, (0, import_sdk_services5.replicationIdentityKey)(spec.identity));
|
|
30260
30551
|
const resolvePolicy = async (at, recordFirst) => {
|
|
30261
30552
|
const policy = spec.grantPolicy;
|
|
30262
30553
|
if (!Number.isSafeInteger(policy.grantTtlMs) || policy.grantTtlMs <= 0 || policy.grantTtlMs > import_contractConstants.MAX_GRANT_TTL_MS) {
|
|
@@ -30278,7 +30569,7 @@ function createSqliteReplicaStorage(options) {
|
|
|
30278
30569
|
let partitionId;
|
|
30279
30570
|
const storeGuard = (section) => guarded(spec.identity.principal, spec.stamp, spec.identity, partitionId, section);
|
|
30280
30571
|
return guarded(spec.identity.principal, spec.stamp, spec.identity, void 0, async () => {
|
|
30281
|
-
if ((0,
|
|
30572
|
+
if ((0, import_sdk_services5.requiresSecretsOptIn)(spec.space, spec.prefix) && spec.allowSecrets !== true) {
|
|
30282
30573
|
throw new replica.ReplicaError(
|
|
30283
30574
|
replica.ReplicaErrorCode.SECRETS_OPT_IN_REQUIRED,
|
|
30284
30575
|
`Replicating ${spec.space}/kv/${spec.prefix} copies secret material to disk; pass allowSecrets to opt in.`
|
|
@@ -30524,7 +30815,7 @@ function createSqliteReplicaStorage(options) {
|
|
|
30524
30815
|
const gate = await fileGate();
|
|
30525
30816
|
const currentGate = await gate.readGateSnapshot(profileDir);
|
|
30526
30817
|
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,
|
|
30818
|
+
const identityKeyHash = await gate.identityHashOf(currentGate.salt, (0, import_sdk_services5.replicationIdentityKey)(target.identity));
|
|
30528
30819
|
await retainGrantInstalls(gate, currentGate, identityKeyHash, store);
|
|
30529
30820
|
}
|
|
30530
30821
|
if (state.revoked === null) {
|
|
@@ -30713,6 +31004,7 @@ function sqliteReplicaStorage(options) {
|
|
|
30713
31004
|
hexDecode,
|
|
30714
31005
|
hexEncode,
|
|
30715
31006
|
installTinyCloudDebugGlobals,
|
|
31007
|
+
isAppKeyHostBound,
|
|
30716
31008
|
isCapabilitySubset,
|
|
30717
31009
|
isEvmAddress,
|
|
30718
31010
|
isNetworkId,
|