@tinycloud/node-sdk 3.0.0-beta.8 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{core-WJQ9KiSD.d.cts → core-DufYfxi-.d.cts} +31 -70
- package/dist/{core-WJQ9KiSD.d.ts → core-DufYfxi-.d.ts} +31 -70
- package/dist/core.cjs +171 -108
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +1 -1
- package/dist/core.d.ts +1 -1
- package/dist/core.js +171 -108
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +173 -108
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -2
- package/dist/index.d.ts +24 -2
- package/dist/index.js +172 -108
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -17209,6 +17209,7 @@ import {
|
|
|
17209
17209
|
canonicalizeAddress as canonicalizeAddress2,
|
|
17210
17210
|
pkhDid as pkhDid2,
|
|
17211
17211
|
resolveTinyCloudHosts as resolveTinyCloudHosts2,
|
|
17212
|
+
publishLocationRecord,
|
|
17212
17213
|
principalDidEquals as principalDidEquals2,
|
|
17213
17214
|
parseNetworkId as parseNetworkId2,
|
|
17214
17215
|
resolveSecretPath as resolveSecretPath2,
|
|
@@ -19298,6 +19299,22 @@ function cloneRecapCaveats(caveats) {
|
|
|
19298
19299
|
throw new Error("Verified persisted session ReCap contains invalid caveats.");
|
|
19299
19300
|
}
|
|
19300
19301
|
}
|
|
19302
|
+
function jsonFromVerifier(value) {
|
|
19303
|
+
if (value === void 0) return null;
|
|
19304
|
+
if (Array.isArray(value)) return value.map(jsonFromVerifier);
|
|
19305
|
+
if (value instanceof Map) {
|
|
19306
|
+
const entries = [...value.entries()];
|
|
19307
|
+
if (!entries.every(([key2]) => typeof key2 === "string")) return value;
|
|
19308
|
+
return Object.fromEntries(entries.map(([key2, nested]) => [key2, jsonFromVerifier(nested)]));
|
|
19309
|
+
}
|
|
19310
|
+
if (value !== null && typeof value === "object" && Object.getPrototypeOf(value) === Object.prototype) {
|
|
19311
|
+
return Object.fromEntries(Object.entries(value).map(([key2, nested]) => [key2, jsonFromVerifier(nested)]));
|
|
19312
|
+
}
|
|
19313
|
+
return value;
|
|
19314
|
+
}
|
|
19315
|
+
function verifierRecapEntries(entries) {
|
|
19316
|
+
return entries.map((entry) => Array.isArray(entry.caveats) ? { ...entry, caveats: entry.caveats.map((caveat) => jsonFromVerifier(caveat)) } : entry);
|
|
19317
|
+
}
|
|
19301
19318
|
var ServiceGraphLifetime = class {
|
|
19302
19319
|
constructor(invokeFn, invokeAnyFn, fetchFn) {
|
|
19303
19320
|
this.invokeFn = invokeFn;
|
|
@@ -19370,18 +19387,23 @@ var UnsupportedSessionRestoreError = class extends Error {
|
|
|
19370
19387
|
this.name = "UnsupportedSessionRestoreError";
|
|
19371
19388
|
}
|
|
19372
19389
|
};
|
|
19390
|
+
var InvalidRestoredSessionError = class extends Error {
|
|
19391
|
+
constructor(message = "Persisted session authority is invalid or expired.") {
|
|
19392
|
+
super(message);
|
|
19393
|
+
this.code = "AUTH_EXPIRED";
|
|
19394
|
+
this.name = "InvalidRestoredSessionError";
|
|
19395
|
+
}
|
|
19396
|
+
};
|
|
19373
19397
|
function canonicalRestoredVerificationMethod(canonicalVerificationMethod, persistedVerificationMethod) {
|
|
19374
19398
|
if (typeof persistedVerificationMethod !== "string") return void 0;
|
|
19375
19399
|
const principal = canonicalVerificationMethod.split("#", 1)[0];
|
|
19376
19400
|
return persistedVerificationMethod === principal || persistedVerificationMethod === canonicalVerificationMethod ? canonicalVerificationMethod : void 0;
|
|
19377
19401
|
}
|
|
19378
19402
|
function persistedExpiry(value) {
|
|
19379
|
-
if (typeof value !== "string")
|
|
19380
|
-
throw new Error("Persisted session without a SIWE expiration must include expiresAt.");
|
|
19381
|
-
}
|
|
19403
|
+
if (typeof value !== "string") throw new InvalidRestoredSessionError("Persisted session must include expiresAt.");
|
|
19382
19404
|
const expiry = new Date(value);
|
|
19383
19405
|
if (!Number.isFinite(expiry.getTime()) || expiry.getTime() <= Date.now()) {
|
|
19384
|
-
throw new
|
|
19406
|
+
throw new InvalidRestoredSessionError();
|
|
19385
19407
|
}
|
|
19386
19408
|
return expiry;
|
|
19387
19409
|
}
|
|
@@ -19860,6 +19882,29 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19860
19882
|
const authHosts = this.auth?.hosts ?? [];
|
|
19861
19883
|
return authHosts.length > 0 ? authHosts : [this.config.host];
|
|
19862
19884
|
}
|
|
19885
|
+
/** The registry-resolved Node endpoint and the identity returned by that exact Node. */
|
|
19886
|
+
async activeNodeIdentity() {
|
|
19887
|
+
const host = this.hosts[0];
|
|
19888
|
+
if (host === void 0) throw new Error("TinyCloud node host has not been resolved");
|
|
19889
|
+
const parsed = new URL(host);
|
|
19890
|
+
if (parsed.protocol !== "https:" && !(parsed.protocol === "http:" && (parsed.hostname === "127.0.0.1" || parsed.hostname === "localhost")) || parsed.origin !== host) {
|
|
19891
|
+
throw new Error("TinyCloud node host is not a canonical origin");
|
|
19892
|
+
}
|
|
19893
|
+
const nodeDid = await this.fetchNodeId();
|
|
19894
|
+
if (!nodeDid.startsWith("did:key:z")) throw new Error("TinyCloud node identity is not a did:key");
|
|
19895
|
+
return Object.freeze({ origin: parsed.origin, nodeDid });
|
|
19896
|
+
}
|
|
19897
|
+
/** Publish the active owner Node as a session-signed registry record. */
|
|
19898
|
+
async publishActiveNodeLocation(registryUrl, fetchFn = globalThis.fetch.bind(globalThis)) {
|
|
19899
|
+
const node = await this.activeNodeIdentity();
|
|
19900
|
+
return publishLocationRecord({
|
|
19901
|
+
registryUrl,
|
|
19902
|
+
subject: this.credentialHolderDid,
|
|
19903
|
+
nodeOrigin: node.origin,
|
|
19904
|
+
signer: { type: "did:key", signBytes: (bytes) => this.signSessionBytes(bytes) },
|
|
19905
|
+
fetch: fetchFn
|
|
19906
|
+
});
|
|
19907
|
+
}
|
|
19863
19908
|
/**
|
|
19864
19909
|
* Get the primary identity DID for this user.
|
|
19865
19910
|
* - If wallet connected and signed in: returns PKH DID (did:pkh:eip155:{chainId}:{address})
|
|
@@ -19901,16 +19946,26 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19901
19946
|
*/
|
|
19902
19947
|
getVerifiedSessionCapabilities() {
|
|
19903
19948
|
const session = this.currentTinyCloudSession();
|
|
19904
|
-
|
|
19905
|
-
|
|
19906
|
-
|
|
19907
|
-
|
|
19908
|
-
).map((entry) =>
|
|
19909
|
-
|
|
19910
|
-
|
|
19911
|
-
|
|
19912
|
-
|
|
19913
|
-
|
|
19949
|
+
return session?.siwe ? this.projectSignedRecapCapabilities(session.siwe) : [];
|
|
19950
|
+
}
|
|
19951
|
+
/** Normalize signed capabilities without collapsing nested encryption into raw authority. */
|
|
19952
|
+
projectSignedRecapCapabilities(siwe) {
|
|
19953
|
+
const projected = this.parseRecapWithCaveats(siwe).map((entry) => {
|
|
19954
|
+
const normalized = parseRecapCapabilities(() => [entry], siwe)[0];
|
|
19955
|
+
if (normalized.service !== "tinycloud.encryption") return normalized;
|
|
19956
|
+
if (!this.isEncryptionNetworkOperation("encryption", entry.path, entry.space)) {
|
|
19957
|
+
return { ...normalized, space: entry.space };
|
|
19958
|
+
}
|
|
19959
|
+
return {
|
|
19960
|
+
service: normalized.service,
|
|
19961
|
+
path: normalized.path,
|
|
19962
|
+
actions: normalized.actions,
|
|
19963
|
+
...normalized.caveats === void 0 ? {} : { caveats: normalized.caveats }
|
|
19964
|
+
};
|
|
19965
|
+
});
|
|
19966
|
+
return projected.sort(
|
|
19967
|
+
(left, right) => (left.space ?? "").localeCompare(right.space ?? "") || left.service.localeCompare(right.service) || left.path.localeCompare(right.path)
|
|
19968
|
+
);
|
|
19914
19969
|
}
|
|
19915
19970
|
/**
|
|
19916
19971
|
* Get the Ethereum address for this user.
|
|
@@ -20365,9 +20420,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20365
20420
|
* We must keep the full owner-scoped URI so a synthetic primary grant can
|
|
20366
20421
|
* never cover an operation on a different owner's space.
|
|
20367
20422
|
*
|
|
20368
|
-
*
|
|
20369
|
-
*
|
|
20370
|
-
*
|
|
20423
|
+
* Only top-level encryption network entries (`space: "encryption"` or
|
|
20424
|
+
* absent) become `resource` ops. A network-shaped path nested under a
|
|
20425
|
+
* data space remains a `spaceId` op, not raw network authority.
|
|
20371
20426
|
* One op per action.
|
|
20372
20427
|
*
|
|
20373
20428
|
* Returns `[]` for session-only / restored-without-siwe modes and for any
|
|
@@ -20388,7 +20443,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20388
20443
|
operations = entries.flatMap((entry) => {
|
|
20389
20444
|
const service = this.invocationServiceName(entry.service);
|
|
20390
20445
|
return entry.actions.map((action) => ({
|
|
20391
|
-
...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId: entry.space },
|
|
20446
|
+
...this.isEncryptionNetworkOperation(service, entry.path, entry.space) ? { resource: entry.path } : { spaceId: entry.space },
|
|
20392
20447
|
service,
|
|
20393
20448
|
path: entry.path,
|
|
20394
20449
|
action,
|
|
@@ -20550,7 +20605,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20550
20605
|
return;
|
|
20551
20606
|
} catch (error) {
|
|
20552
20607
|
const message = error instanceof Error ? error.message : String(error);
|
|
20553
|
-
if (/Unauthorized Action|\
|
|
20608
|
+
if (/Unauthorized Action|\b(?:401|403)\b/.test(message)) {
|
|
20554
20609
|
console.warn(
|
|
20555
20610
|
"TinyCloud account registry sync stopped: authorization verdict is not retryable",
|
|
20556
20611
|
error
|
|
@@ -20800,9 +20855,14 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20800
20855
|
*/
|
|
20801
20856
|
async restoreSession(sessionData) {
|
|
20802
20857
|
await this.wasmBindings.ensureInitialized?.();
|
|
20803
|
-
|
|
20858
|
+
let restoredJwk;
|
|
20859
|
+
try {
|
|
20860
|
+
restoredJwk = clonePersistedSessionJwk(sessionData.jwk);
|
|
20861
|
+
} catch {
|
|
20862
|
+
throw new InvalidRestoredSessionError("Persisted session has an invalid private Ed25519 session key.");
|
|
20863
|
+
}
|
|
20804
20864
|
if (sessionData.chainId !== void 0 && (!Number.isSafeInteger(sessionData.chainId) || sessionData.chainId <= 0)) {
|
|
20805
|
-
throw new
|
|
20865
|
+
throw new InvalidRestoredSessionError("Persisted session chain ID must be a positive safe integer.");
|
|
20806
20866
|
}
|
|
20807
20867
|
const stagedManager = this.wasmBindings.createSessionManager();
|
|
20808
20868
|
const stagedReplace = stagedManager.replaceSessionKey;
|
|
@@ -20814,32 +20874,31 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20814
20874
|
let stagedKeyId;
|
|
20815
20875
|
let stagedJwk;
|
|
20816
20876
|
let canonicalVerificationMethod;
|
|
20877
|
+
const keyIds = liveKeys.call(this.sessionManager);
|
|
20878
|
+
if (!Array.isArray(keyIds) || !keyIds.every((keyId) => typeof keyId === "string")) {
|
|
20879
|
+
throw new UnsupportedSessionRestoreError("it cannot reliably enumerate every live session signer");
|
|
20880
|
+
}
|
|
20881
|
+
for (const keyId of keyIds) {
|
|
20882
|
+
if (keyId === this.sessionKeyId) continue;
|
|
20883
|
+
const jwk = this.sessionManager.jwk(keyId);
|
|
20884
|
+
if (!jwk) throw new Error("missing live session key");
|
|
20885
|
+
stagedReplace.call(stagedManager, clonePersistedSessionJwk(JSON.parse(jwk)), keyId);
|
|
20886
|
+
}
|
|
20817
20887
|
try {
|
|
20818
|
-
const keyIds = liveKeys.call(this.sessionManager);
|
|
20819
|
-
if (!Array.isArray(keyIds) || !keyIds.every((keyId) => typeof keyId === "string")) {
|
|
20820
|
-
throw new UnsupportedSessionRestoreError("it cannot reliably enumerate every live session signer");
|
|
20821
|
-
}
|
|
20822
|
-
for (const keyId of keyIds) {
|
|
20823
|
-
if (keyId === this.sessionKeyId) continue;
|
|
20824
|
-
const jwk = this.sessionManager.jwk(keyId);
|
|
20825
|
-
if (!jwk) throw new Error("missing live session key");
|
|
20826
|
-
stagedReplace.call(stagedManager, clonePersistedSessionJwk(JSON.parse(jwk)), keyId);
|
|
20827
|
-
}
|
|
20828
20888
|
stagedKeyId = stagedReplace.call(stagedManager, restoredJwk, this.sessionKeyId);
|
|
20829
|
-
|
|
20830
|
-
|
|
20831
|
-
stagedJwk = clonePersistedSessionJwk(JSON.parse(stagedJwkJson));
|
|
20832
|
-
canonicalVerificationMethod = stagedManager.getDID(stagedKeyId);
|
|
20833
|
-
} catch (error) {
|
|
20834
|
-
if (error instanceof UnsupportedSessionRestoreError) throw error;
|
|
20835
|
-
throw new Error("Persisted session has an invalid private Ed25519 session key.");
|
|
20889
|
+
} catch {
|
|
20890
|
+
throw new InvalidRestoredSessionError("Persisted session has an invalid private Ed25519 session key.");
|
|
20836
20891
|
}
|
|
20892
|
+
const stagedJwkJson = stagedManager.jwk(stagedKeyId);
|
|
20893
|
+
if (!stagedJwkJson) throw new Error("missing restored session key");
|
|
20894
|
+
stagedJwk = clonePersistedSessionJwk(JSON.parse(stagedJwkJson));
|
|
20895
|
+
canonicalVerificationMethod = stagedManager.getDID(stagedKeyId);
|
|
20837
20896
|
const restoredVerificationMethod = canonicalRestoredVerificationMethod(
|
|
20838
20897
|
canonicalVerificationMethod,
|
|
20839
20898
|
sessionData.verificationMethod
|
|
20840
20899
|
);
|
|
20841
20900
|
if (!restoredVerificationMethod) {
|
|
20842
|
-
throw new
|
|
20901
|
+
throw new InvalidRestoredSessionError(
|
|
20843
20902
|
"Persisted session verification method does not match its private Ed25519 session key."
|
|
20844
20903
|
);
|
|
20845
20904
|
}
|
|
@@ -20851,26 +20910,37 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20851
20910
|
];
|
|
20852
20911
|
const hasPersistedProof = proofValues.every((value) => value !== void 0);
|
|
20853
20912
|
if (!hasPersistedProof && (proofValues.some((value) => value !== void 0) || sessionData.expiresAt !== void 0)) {
|
|
20854
|
-
throw new
|
|
20913
|
+
throw new InvalidRestoredSessionError("Persisted session authority metadata is incomplete.");
|
|
20914
|
+
}
|
|
20915
|
+
let restoredAddress;
|
|
20916
|
+
try {
|
|
20917
|
+
restoredAddress = hasPersistedProof ? canonicalizeAddress2(sessionData.address) : void 0;
|
|
20918
|
+
} catch {
|
|
20919
|
+
throw new InvalidRestoredSessionError("Persisted session has an invalid wallet address.");
|
|
20855
20920
|
}
|
|
20856
|
-
const restoredAddress = hasPersistedProof ? canonicalizeAddress2(sessionData.address) : void 0;
|
|
20857
20921
|
let stagedSessionExpiry = /* @__PURE__ */ new Date(0);
|
|
20858
20922
|
let stagedRecap = [];
|
|
20859
20923
|
if (hasPersistedProof) {
|
|
20860
20924
|
if (typeof this.wasmBindings.validatePersistedSession !== "function") {
|
|
20861
20925
|
throw new UnsupportedSessionRestoreError("it cannot verify persisted SIWE authority");
|
|
20862
20926
|
}
|
|
20863
|
-
const verified =
|
|
20864
|
-
|
|
20865
|
-
|
|
20866
|
-
|
|
20867
|
-
|
|
20868
|
-
|
|
20869
|
-
|
|
20870
|
-
|
|
20871
|
-
|
|
20872
|
-
|
|
20873
|
-
|
|
20927
|
+
const verified = (() => {
|
|
20928
|
+
try {
|
|
20929
|
+
return this.wasmBindings.validatePersistedSession({
|
|
20930
|
+
delegationHeader: sessionData.delegationHeader,
|
|
20931
|
+
delegationCid: sessionData.delegationCid,
|
|
20932
|
+
spaceId: sessionData.spaceId,
|
|
20933
|
+
jwk: stagedJwk,
|
|
20934
|
+
address: restoredAddress,
|
|
20935
|
+
chainId: sessionData.chainId,
|
|
20936
|
+
siwe: sessionData.siwe,
|
|
20937
|
+
signature: sessionData.signature
|
|
20938
|
+
});
|
|
20939
|
+
} catch (error) {
|
|
20940
|
+
throw new InvalidRestoredSessionError(error instanceof Error ? error.message : String(error));
|
|
20941
|
+
}
|
|
20942
|
+
})();
|
|
20943
|
+
const exactRecap = Array.isArray(verified.verifiedRecap) ? verifierRecapEntries(verified.verifiedRecap) : verified.verifiedRecap;
|
|
20874
20944
|
if (!Array.isArray(exactRecap) || !exactRecap.every(
|
|
20875
20945
|
(entry) => entry !== null && typeof entry === "object" && typeof entry.service === "string" && typeof entry.space === "string" && typeof entry.path === "string" && Array.isArray(entry.actions) && entry.actions.every((action) => typeof action === "string") && Array.isArray(entry.caveats) && entry.caveats.every(
|
|
20876
20946
|
(caveat) => caveat !== null && typeof caveat === "object" && !Array.isArray(caveat)
|
|
@@ -20890,7 +20960,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20890
20960
|
const signedExpiry = verified.expiresAt === void 0 ? void 0 : persistedExpiry(verified.expiresAt);
|
|
20891
20961
|
const persistedPolicyExpiry = sessionData.expiresAt === void 0 ? void 0 : persistedExpiry(sessionData.expiresAt);
|
|
20892
20962
|
if (signedExpiry && persistedPolicyExpiry && !sameInstant(signedExpiry, persistedPolicyExpiry)) {
|
|
20893
|
-
throw new
|
|
20963
|
+
throw new InvalidRestoredSessionError("Persisted session expiry does not match its signed SIWE authority.");
|
|
20894
20964
|
}
|
|
20895
20965
|
stagedSessionExpiry = signedExpiry ?? persistedPolicyExpiry ?? persistedExpiry(void 0);
|
|
20896
20966
|
}
|
|
@@ -20987,7 +21057,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20987
21057
|
operations = recap.flatMap((entry) => {
|
|
20988
21058
|
const service = this.invocationServiceName(entry.service);
|
|
20989
21059
|
return entry.actions.map((action) => ({
|
|
20990
|
-
...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId: entry.space },
|
|
21060
|
+
...this.isEncryptionNetworkOperation(service, entry.path, entry.space) ? { resource: entry.path } : { spaceId: entry.space },
|
|
20991
21061
|
service,
|
|
20992
21062
|
path: entry.path,
|
|
20993
21063
|
action,
|
|
@@ -21083,7 +21153,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21083
21153
|
const delegations = input.recap.map((entry) => ({
|
|
21084
21154
|
cid: session.delegationCid,
|
|
21085
21155
|
delegateDID: session.verificationMethod,
|
|
21086
|
-
spaceId: entry.space,
|
|
21156
|
+
spaceId: this.spaceIdsEqual(entry.space, session.spaceId) ? session.spaceId : entry.space,
|
|
21087
21157
|
path: entry.path,
|
|
21088
21158
|
actions: [...entry.actions],
|
|
21089
21159
|
caveats: cloneRecapCaveats(entry.caveats),
|
|
@@ -21773,16 +21843,11 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21773
21843
|
);
|
|
21774
21844
|
graph.assertActive();
|
|
21775
21845
|
if (!response.ok) {
|
|
21776
|
-
|
|
21777
|
-
|
|
21778
|
-
|
|
21779
|
-
|
|
21780
|
-
|
|
21781
|
-
const nested = typeof record?.error === "object" && record.error !== null ? record.error : void 0;
|
|
21782
|
-
permissionHint = parsePermissionHint(record?.permissionHint) ?? parsePermissionHint(nested?.permissionHint);
|
|
21783
|
-
} catch {
|
|
21784
|
-
}
|
|
21785
|
-
}
|
|
21846
|
+
const permissionHint = response.status === 401 || response.status === 403 ? parsePermissionHint({
|
|
21847
|
+
service: "tinycloud.encryption",
|
|
21848
|
+
path: networkId,
|
|
21849
|
+
actions: ["tinycloud.encryption/decrypt"]
|
|
21850
|
+
}) : void 0;
|
|
21786
21851
|
throw new DecryptTransportResponseError(response.status, permissionHint);
|
|
21787
21852
|
}
|
|
21788
21853
|
return await response.json();
|
|
@@ -22216,16 +22281,16 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22216
22281
|
fetch: globalThis.fetch.bind(globalThis)
|
|
22217
22282
|
});
|
|
22218
22283
|
}
|
|
22219
|
-
/** @deprecated Policy/v2 delivery transport is retired. Use authorizeShareDeliveryV3. */
|
|
22220
|
-
async authorizeShareDelivery(input) {
|
|
22221
|
-
void input;
|
|
22222
|
-
throw new Error("Policy/v2 share delivery is retired; use authorizeShareDeliveryV3");
|
|
22223
|
-
}
|
|
22224
22284
|
/** Authorize one short-lived v3 delivery against the signed v3 envelope and registered roots. */
|
|
22225
22285
|
async authorizeShareDeliveryV3(input) {
|
|
22226
22286
|
const session = this.currentTinyCloudSession();
|
|
22227
22287
|
const serviceSession = this._serviceContext?.session;
|
|
22228
22288
|
if (!session || !serviceSession) throw new Error("Share delivery requires an authenticated session");
|
|
22289
|
+
const idempotencyBytes = new TextEncoder().encode(input.idempotencyKey);
|
|
22290
|
+
if (idempotencyBytes.byteLength === 0 || idempotencyBytes.byteLength > 512) {
|
|
22291
|
+
throw new Error("Share delivery idempotency key is invalid");
|
|
22292
|
+
}
|
|
22293
|
+
const jtiDigest = new Uint8Array(await crypto.subtle.digest("SHA-256", idempotencyBytes));
|
|
22229
22294
|
const body = {
|
|
22230
22295
|
envelope: input.envelope,
|
|
22231
22296
|
sealedEnvelope: input.sealedEnvelope,
|
|
@@ -22234,7 +22299,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22234
22299
|
recipientEmail: input.recipientEmail,
|
|
22235
22300
|
shareUrl: input.shareUrl,
|
|
22236
22301
|
documentName: input.documentName,
|
|
22237
|
-
jti: base64UrlEncode(
|
|
22302
|
+
jti: base64UrlEncode(jtiDigest.slice(0, 16)),
|
|
22238
22303
|
expiresAt: new Date(input.expiresAt).toISOString().replace(/\.\d{3}Z$/, "Z")
|
|
22239
22304
|
};
|
|
22240
22305
|
const requestBodyDigest = base64UrlEncode(new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(canonicalizeEncryptionJson(body)))));
|
|
@@ -22249,9 +22314,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22249
22314
|
const verified = validateShareDeliveryAuthorizationV3Bytes(new Uint8Array(await response.arrayBuffer()), {
|
|
22250
22315
|
request,
|
|
22251
22316
|
senderKeyDid: this.credentialHolderDid,
|
|
22252
|
-
|
|
22317
|
+
deliveryAudience: input.deliveryAudience
|
|
22253
22318
|
});
|
|
22254
|
-
void input.nodeProof;
|
|
22255
22319
|
const requestDigest = new Uint8Array(await crypto.subtle.digest(
|
|
22256
22320
|
"SHA-256",
|
|
22257
22321
|
new TextEncoder().encode(`${CREDENTIAL_INVITATION_REQUEST_DOMAIN}${canonicalizeEncryptionJson(verified.request)}`)
|
|
@@ -22718,7 +22782,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22718
22782
|
continue;
|
|
22719
22783
|
}
|
|
22720
22784
|
let space;
|
|
22721
|
-
if (service !== "tinycloud.encryption") {
|
|
22785
|
+
if (service !== "tinycloud.encryption" || operation.resource === void 0) {
|
|
22722
22786
|
if (typeof operation.spaceId !== "string") continue;
|
|
22723
22787
|
try {
|
|
22724
22788
|
space = this.resolvePermissionSpace(operation.spaceId, session);
|
|
@@ -23263,11 +23327,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23263
23327
|
);
|
|
23264
23328
|
return { delegation: delegation2, prompted: true };
|
|
23265
23329
|
}
|
|
23266
|
-
const granted =
|
|
23267
|
-
|
|
23268
|
-
session.siwe
|
|
23269
|
-
);
|
|
23270
|
-
const { subset, missing } = isCapabilitySubset(expandedEntries, granted);
|
|
23330
|
+
const granted = this.projectSignedRecapCapabilities(session.siwe);
|
|
23331
|
+
const { subset, missing } = this.signedCapabilitySubset(expandedEntries, granted);
|
|
23271
23332
|
if (!subset) {
|
|
23272
23333
|
const runtimeOperations = this.permissionEntriesToOperations(expandedEntries, session);
|
|
23273
23334
|
const runtimeGrant = this.findGrantForOperations(
|
|
@@ -23587,7 +23648,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23587
23648
|
return abilities;
|
|
23588
23649
|
}
|
|
23589
23650
|
isEncryptionPermissionEntry(entry) {
|
|
23590
|
-
return entry.service === ENCRYPTION_PERMISSION_SERVICE2 &&
|
|
23651
|
+
return entry.service === ENCRYPTION_PERMISSION_SERVICE2 && this.isEncryptionNetworkOperation("encryption", entry.path, entry.space);
|
|
23591
23652
|
}
|
|
23592
23653
|
permissionsToRawAbilities(entries) {
|
|
23593
23654
|
const rawAbilities = {};
|
|
@@ -23611,7 +23672,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23611
23672
|
return entries.flatMap((entry) => {
|
|
23612
23673
|
const service = this.shortServiceName(entry.service);
|
|
23613
23674
|
return entry.actions.map((action) => ({
|
|
23614
|
-
...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
|
|
23675
|
+
...this.isEncryptionNetworkOperation(service, entry.path, entry.space) ? { resource: entry.path } : { spaceId },
|
|
23615
23676
|
service,
|
|
23616
23677
|
path: entry.path,
|
|
23617
23678
|
action,
|
|
@@ -23619,13 +23680,21 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23619
23680
|
}));
|
|
23620
23681
|
});
|
|
23621
23682
|
}
|
|
23683
|
+
/** Raw network authority cannot be covered by a nested encryption space. */
|
|
23684
|
+
signedCapabilitySubset(requested, granted) {
|
|
23685
|
+
const rawGranted = granted.filter((entry) => this.isEncryptionPermissionEntry(entry)).map((entry) => ({ ...entry, space: "encryption" }));
|
|
23686
|
+
const spaceGranted = granted.filter((entry) => !this.isEncryptionPermissionEntry(entry));
|
|
23687
|
+
const missing = requested.filter((entry) => {
|
|
23688
|
+
const raw = this.isEncryptionPermissionEntry(entry);
|
|
23689
|
+
const comparable = raw ? { ...entry, space: "encryption" } : entry;
|
|
23690
|
+
return !isCapabilitySubset([comparable], raw ? rawGranted : spaceGranted).subset;
|
|
23691
|
+
});
|
|
23692
|
+
return { subset: missing.length === 0, missing };
|
|
23693
|
+
}
|
|
23622
23694
|
sessionCoversPermissionEntries(session, entries) {
|
|
23623
23695
|
try {
|
|
23624
|
-
const granted =
|
|
23625
|
-
|
|
23626
|
-
session.siwe
|
|
23627
|
-
);
|
|
23628
|
-
return isCapabilitySubset(entries, granted).subset;
|
|
23696
|
+
const granted = this.projectSignedRecapCapabilities(session.siwe);
|
|
23697
|
+
return this.signedCapabilitySubset(entries, granted).subset;
|
|
23629
23698
|
} catch {
|
|
23630
23699
|
return false;
|
|
23631
23700
|
}
|
|
@@ -23635,7 +23704,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23635
23704
|
const spaceId = this.resolvePermissionSpace(entry.space, session);
|
|
23636
23705
|
const service = this.shortServiceName(entry.service);
|
|
23637
23706
|
return entry.actions.map((action) => ({
|
|
23638
|
-
...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
|
|
23707
|
+
...this.isEncryptionNetworkOperation(service, entry.path, entry.space) ? { resource: entry.path } : { spaceId },
|
|
23639
23708
|
service,
|
|
23640
23709
|
path: entry.path,
|
|
23641
23710
|
action,
|
|
@@ -23724,7 +23793,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23724
23793
|
return resources.flatMap((resource) => {
|
|
23725
23794
|
const service = this.invocationServiceName(resource.service);
|
|
23726
23795
|
return resource.actions.map((action) => ({
|
|
23727
|
-
...this.isEncryptionNetworkOperation(service, resource.path) ? { resource: resource.path } : { spaceId: resource.space },
|
|
23796
|
+
...this.isEncryptionNetworkOperation(service, resource.path, resource.space) ? { resource: resource.path } : { spaceId: resource.space },
|
|
23728
23797
|
service,
|
|
23729
23798
|
path: resource.path,
|
|
23730
23799
|
action,
|
|
@@ -23785,7 +23854,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23785
23854
|
};
|
|
23786
23855
|
for (const resource of resources) {
|
|
23787
23856
|
const service = this.invocationServiceName(resource.service);
|
|
23788
|
-
if (this.isEncryptionNetworkOperation(service, resource.path)) {
|
|
23857
|
+
if (this.isEncryptionNetworkOperation(service, resource.path, resource.space)) {
|
|
23789
23858
|
rawAbilities[_a = resource.path] ?? (rawAbilities[_a] = []);
|
|
23790
23859
|
addActions(rawAbilities[resource.path], resource.actions);
|
|
23791
23860
|
continue;
|
|
@@ -23854,21 +23923,15 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23854
23923
|
}
|
|
23855
23924
|
return granted.spaceId !== void 0 && requested.spaceId !== void 0 && this.spaceIdsEqual(granted.spaceId, requested.spaceId) && this.pathContains(granted.path, requested.path);
|
|
23856
23925
|
}
|
|
23857
|
-
// Space IDs are `tinycloud:pkh:eip155:<chain>:<0xADDR>:<name>`.
|
|
23858
|
-
// EIP-155 address
|
|
23859
|
-
//
|
|
23860
|
-
// the EIP-55 checksummed form — so a byte-for-byte compare spuriously rejects
|
|
23861
|
-
// an otherwise-valid grant. Lowercase ONLY the `eip155:<chain>:0x<addr>`
|
|
23862
|
-
// segment and leave everything else (crucially the case-sensitive space NAME)
|
|
23863
|
-
// byte-exact. Mirrors the CLI's `normalizeSpaceForCompare` (OPENKEY_SCOPE_MISMATCH fix).
|
|
23926
|
+
// Space IDs are `tinycloud:pkh:eip155:<chain>:<0xADDR>:<name>`. Compare
|
|
23927
|
+
// only the embedded EIP-155 address without case sensitivity; chain, name,
|
|
23928
|
+
// and all non-PKH space identifiers remain byte-exact.
|
|
23864
23929
|
spaceIdsEqual(a, b) {
|
|
23865
23930
|
return this.normalizeSpaceAddress(a) === this.normalizeSpaceAddress(b);
|
|
23866
23931
|
}
|
|
23867
23932
|
normalizeSpaceAddress(space) {
|
|
23868
|
-
|
|
23869
|
-
|
|
23870
|
-
(_match, prefix, addr) => prefix + addr.toLowerCase()
|
|
23871
|
-
);
|
|
23933
|
+
const match = /^(tinycloud:pkh:eip155:\d+:)(0x[0-9a-fA-F]{40})(:.+)$/.exec(space);
|
|
23934
|
+
return match === null ? space : `${match[1]}${match[2].toLowerCase()}${match[3]}`;
|
|
23872
23935
|
}
|
|
23873
23936
|
actionContains(grantedAction, requestedAction) {
|
|
23874
23937
|
if (grantedAction === requestedAction) {
|
|
@@ -23885,10 +23948,11 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23885
23948
|
}
|
|
23886
23949
|
/** Prefer the v2 caveat-preserving parser while retaining old custom WASM. */
|
|
23887
23950
|
parseRecapWithCaveats(siwe) {
|
|
23888
|
-
|
|
23951
|
+
const verified = this.wasmBindings.parseVerifiedRecapFromSiwe?.(siwe);
|
|
23952
|
+
return verified == null ? this.wasmBindings.parseRecapFromSiwe(siwe) : verifierRecapEntries(verified);
|
|
23889
23953
|
}
|
|
23890
|
-
isEncryptionNetworkOperation(service, path) {
|
|
23891
|
-
return service === "encryption" && path.startsWith("urn:tinycloud:encryption:");
|
|
23954
|
+
isEncryptionNetworkOperation(service, path, space) {
|
|
23955
|
+
return service === "encryption" && (space === void 0 || space === "encryption") && path.startsWith("urn:tinycloud:encryption:");
|
|
23892
23956
|
}
|
|
23893
23957
|
operationFromInvokeAnyEntry(entry) {
|
|
23894
23958
|
const service = this.invocationServiceName(entry.service);
|
|
@@ -23901,7 +23965,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23901
23965
|
...entry.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(entry.caveats) }
|
|
23902
23966
|
};
|
|
23903
23967
|
}
|
|
23904
|
-
if (this.isEncryptionNetworkOperation(service, entry.path)) {
|
|
23968
|
+
if (entry.spaceId === void 0 && this.isEncryptionNetworkOperation(service, entry.path)) {
|
|
23905
23969
|
return {
|
|
23906
23970
|
resource: entry.path,
|
|
23907
23971
|
service,
|
|
@@ -24186,8 +24250,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
24186
24250
|
const jwk = mySession.jwk;
|
|
24187
24251
|
const { abilities, rawAbilities } = this.buildActivationAbilities(delegation);
|
|
24188
24252
|
const now = /* @__PURE__ */ new Date();
|
|
24189
|
-
const
|
|
24190
|
-
const expirationTime = delegation.expiry < maxExpiry ? delegation.expiry : maxExpiry;
|
|
24253
|
+
const expirationTime = delegation.expiry;
|
|
24191
24254
|
const prepared = this.wasmBindings.prepareSession({
|
|
24192
24255
|
abilities,
|
|
24193
24256
|
address: this.wasmBindings.ensureEip55(mySession.address),
|
|
@@ -24291,8 +24354,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
24291
24354
|
);
|
|
24292
24355
|
}
|
|
24293
24356
|
const now = /* @__PURE__ */ new Date();
|
|
24294
|
-
const
|
|
24295
|
-
const requestedExpiry = new Date(now.getTime() + expiryMs);
|
|
24357
|
+
const requestedExpiry = params.expiryMs === void 0 ? parentDelegation.expiry : new Date(now.getTime() + params.expiryMs);
|
|
24296
24358
|
const actualExpiry = requestedExpiry > parentDelegation.expiry ? parentDelegation.expiry : requestedExpiry;
|
|
24297
24359
|
const abilities = {};
|
|
24298
24360
|
const rawAbilities = {};
|
|
@@ -25097,6 +25159,7 @@ export {
|
|
|
25097
25159
|
FileSessionStorage,
|
|
25098
25160
|
HooksService3 as HooksService,
|
|
25099
25161
|
IdentityParseError,
|
|
25162
|
+
InvalidRestoredSessionError,
|
|
25100
25163
|
KVService3 as KVService,
|
|
25101
25164
|
ManifestValidationError,
|
|
25102
25165
|
MemorySessionStorage,
|
|
@@ -25176,6 +25239,7 @@ export {
|
|
|
25176
25239
|
expandActionShortNames,
|
|
25177
25240
|
expandPermissionEntries2 as expandPermissionEntries,
|
|
25178
25241
|
expandPermissionEntry,
|
|
25242
|
+
extractSiweExpiration,
|
|
25179
25243
|
generateRandomReceiverKey,
|
|
25180
25244
|
getTinyCloudDebugLogs,
|
|
25181
25245
|
grantAuthRequest,
|