@tinycloud/node-sdk 3.0.0-beta.2 → 3.0.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/dist/{core-C6Czyr52.d.cts → core-DufYfxi-.d.cts} +39 -61
- package/dist/{core-C6Czyr52.d.ts → core-DufYfxi-.d.ts} +39 -61
- package/dist/core.cjs +213 -139
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +2 -2
- package/dist/core.d.ts +2 -2
- package/dist/core.js +216 -141
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +215 -139
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +25 -3
- package/dist/index.d.ts +25 -3
- package/dist/index.js +217 -141
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -17060,6 +17060,7 @@ __export(index_exports, {
|
|
|
17060
17060
|
FileSessionStorage: () => FileSessionStorage,
|
|
17061
17061
|
HooksService: () => import_sdk_core18.HooksService,
|
|
17062
17062
|
IdentityParseError: () => import_sdk_core11.IdentityParseError,
|
|
17063
|
+
InvalidRestoredSessionError: () => InvalidRestoredSessionError,
|
|
17063
17064
|
KVService: () => import_sdk_core13.KVService,
|
|
17064
17065
|
ManifestValidationError: () => import_sdk_core12.ManifestValidationError,
|
|
17065
17066
|
MemorySessionStorage: () => MemorySessionStorage,
|
|
@@ -17139,6 +17140,7 @@ __export(index_exports, {
|
|
|
17139
17140
|
expandActionShortNames: () => import_sdk_core12.expandActionShortNames,
|
|
17140
17141
|
expandPermissionEntries: () => import_sdk_core12.expandPermissionEntries,
|
|
17141
17142
|
expandPermissionEntry: () => import_sdk_core12.expandPermissionEntry,
|
|
17143
|
+
extractSiweExpiration: () => extractSiweExpiration,
|
|
17142
17144
|
generateRandomReceiverKey: () => import_sdk_core17.generateRandomReceiverKey,
|
|
17143
17145
|
getTinyCloudDebugLogs: () => import_sdk_core11.getTinyCloudDebugLogs,
|
|
17144
17146
|
grantAuthRequest: () => grantAuthRequest,
|
|
@@ -17157,6 +17159,7 @@ __export(index_exports, {
|
|
|
17157
17159
|
parseExpiry: () => import_sdk_core12.parseExpiry,
|
|
17158
17160
|
parseNetworkId: () => import_sdk_core17.parseNetworkId,
|
|
17159
17161
|
parsePkhDid: () => import_sdk_core11.parsePkhDid,
|
|
17162
|
+
parseSecretCatalogKey: () => import_sdk_core16.parseSecretCatalogKey,
|
|
17160
17163
|
parseSpaceUri: () => import_sdk_core21.parseSpaceUri,
|
|
17161
17164
|
pkhDid: () => import_sdk_core11.pkhDid,
|
|
17162
17165
|
principalDid: () => import_sdk_core11.principalDid,
|
|
@@ -19108,6 +19111,11 @@ var NodeSecretsService = class {
|
|
|
19108
19111
|
if (!permission.ok) return permission;
|
|
19109
19112
|
return options === void 0 ? this.service.list() : this.service.list(options);
|
|
19110
19113
|
}
|
|
19114
|
+
async listAll() {
|
|
19115
|
+
const permission = await this.ensurePermission("", void 0, "list");
|
|
19116
|
+
if (!permission.ok) return permission;
|
|
19117
|
+
return this.service.listAll();
|
|
19118
|
+
}
|
|
19111
19119
|
get service() {
|
|
19112
19120
|
return this.config.getService();
|
|
19113
19121
|
}
|
|
@@ -19169,7 +19177,11 @@ var NodeSecretsService = class {
|
|
|
19169
19177
|
(entry) => manifests.some((candidate) => {
|
|
19170
19178
|
const resolved = (0, import_sdk_core6.resolveManifest)(candidate);
|
|
19171
19179
|
return resolved.resources.some(
|
|
19172
|
-
(resource) => resource.service === entry.service && spaceMatches(
|
|
19180
|
+
(resource) => resource.service === entry.service && spaceMatches(
|
|
19181
|
+
resource.space,
|
|
19182
|
+
entry.space,
|
|
19183
|
+
this.config.resolveSpace
|
|
19184
|
+
) && resource.path === entry.path && entry.actions.every((action) => resource.actions.includes(action))
|
|
19173
19185
|
);
|
|
19174
19186
|
})
|
|
19175
19187
|
);
|
|
@@ -19302,6 +19314,22 @@ function cloneRecapCaveats(caveats) {
|
|
|
19302
19314
|
throw new Error("Verified persisted session ReCap contains invalid caveats.");
|
|
19303
19315
|
}
|
|
19304
19316
|
}
|
|
19317
|
+
function jsonFromVerifier(value) {
|
|
19318
|
+
if (value === void 0) return null;
|
|
19319
|
+
if (Array.isArray(value)) return value.map(jsonFromVerifier);
|
|
19320
|
+
if (value instanceof Map) {
|
|
19321
|
+
const entries = [...value.entries()];
|
|
19322
|
+
if (!entries.every(([key2]) => typeof key2 === "string")) return value;
|
|
19323
|
+
return Object.fromEntries(entries.map(([key2, nested]) => [key2, jsonFromVerifier(nested)]));
|
|
19324
|
+
}
|
|
19325
|
+
if (value !== null && typeof value === "object" && Object.getPrototypeOf(value) === Object.prototype) {
|
|
19326
|
+
return Object.fromEntries(Object.entries(value).map(([key2, nested]) => [key2, jsonFromVerifier(nested)]));
|
|
19327
|
+
}
|
|
19328
|
+
return value;
|
|
19329
|
+
}
|
|
19330
|
+
function verifierRecapEntries(entries) {
|
|
19331
|
+
return entries.map((entry) => Array.isArray(entry.caveats) ? { ...entry, caveats: entry.caveats.map((caveat) => jsonFromVerifier(caveat)) } : entry);
|
|
19332
|
+
}
|
|
19305
19333
|
var ServiceGraphLifetime = class {
|
|
19306
19334
|
constructor(invokeFn, invokeAnyFn, fetchFn) {
|
|
19307
19335
|
this.invokeFn = invokeFn;
|
|
@@ -19374,18 +19402,23 @@ var UnsupportedSessionRestoreError = class extends Error {
|
|
|
19374
19402
|
this.name = "UnsupportedSessionRestoreError";
|
|
19375
19403
|
}
|
|
19376
19404
|
};
|
|
19405
|
+
var InvalidRestoredSessionError = class extends Error {
|
|
19406
|
+
constructor(message = "Persisted session authority is invalid or expired.") {
|
|
19407
|
+
super(message);
|
|
19408
|
+
this.code = "AUTH_EXPIRED";
|
|
19409
|
+
this.name = "InvalidRestoredSessionError";
|
|
19410
|
+
}
|
|
19411
|
+
};
|
|
19377
19412
|
function canonicalRestoredVerificationMethod(canonicalVerificationMethod, persistedVerificationMethod) {
|
|
19378
19413
|
if (typeof persistedVerificationMethod !== "string") return void 0;
|
|
19379
19414
|
const principal = canonicalVerificationMethod.split("#", 1)[0];
|
|
19380
19415
|
return persistedVerificationMethod === principal || persistedVerificationMethod === canonicalVerificationMethod ? canonicalVerificationMethod : void 0;
|
|
19381
19416
|
}
|
|
19382
19417
|
function persistedExpiry(value) {
|
|
19383
|
-
if (typeof value !== "string")
|
|
19384
|
-
throw new Error("Persisted session without a SIWE expiration must include expiresAt.");
|
|
19385
|
-
}
|
|
19418
|
+
if (typeof value !== "string") throw new InvalidRestoredSessionError("Persisted session must include expiresAt.");
|
|
19386
19419
|
const expiry = new Date(value);
|
|
19387
19420
|
if (!Number.isFinite(expiry.getTime()) || expiry.getTime() <= Date.now()) {
|
|
19388
|
-
throw new
|
|
19421
|
+
throw new InvalidRestoredSessionError();
|
|
19389
19422
|
}
|
|
19390
19423
|
return expiry;
|
|
19391
19424
|
}
|
|
@@ -19864,6 +19897,29 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19864
19897
|
const authHosts = this.auth?.hosts ?? [];
|
|
19865
19898
|
return authHosts.length > 0 ? authHosts : [this.config.host];
|
|
19866
19899
|
}
|
|
19900
|
+
/** The registry-resolved Node endpoint and the identity returned by that exact Node. */
|
|
19901
|
+
async activeNodeIdentity() {
|
|
19902
|
+
const host = this.hosts[0];
|
|
19903
|
+
if (host === void 0) throw new Error("TinyCloud node host has not been resolved");
|
|
19904
|
+
const parsed = new URL(host);
|
|
19905
|
+
if (parsed.protocol !== "https:" && !(parsed.protocol === "http:" && (parsed.hostname === "127.0.0.1" || parsed.hostname === "localhost")) || parsed.origin !== host) {
|
|
19906
|
+
throw new Error("TinyCloud node host is not a canonical origin");
|
|
19907
|
+
}
|
|
19908
|
+
const nodeDid = await this.fetchNodeId();
|
|
19909
|
+
if (!nodeDid.startsWith("did:key:z")) throw new Error("TinyCloud node identity is not a did:key");
|
|
19910
|
+
return Object.freeze({ origin: parsed.origin, nodeDid });
|
|
19911
|
+
}
|
|
19912
|
+
/** Publish the active owner Node as a session-signed registry record. */
|
|
19913
|
+
async publishActiveNodeLocation(registryUrl, fetchFn = globalThis.fetch.bind(globalThis)) {
|
|
19914
|
+
const node = await this.activeNodeIdentity();
|
|
19915
|
+
return (0, import_sdk_core7.publishLocationRecord)({
|
|
19916
|
+
registryUrl,
|
|
19917
|
+
subject: this.credentialHolderDid,
|
|
19918
|
+
nodeOrigin: node.origin,
|
|
19919
|
+
signer: { type: "did:key", signBytes: (bytes) => this.signSessionBytes(bytes) },
|
|
19920
|
+
fetch: fetchFn
|
|
19921
|
+
});
|
|
19922
|
+
}
|
|
19867
19923
|
/**
|
|
19868
19924
|
* Get the primary identity DID for this user.
|
|
19869
19925
|
* - If wallet connected and signed in: returns PKH DID (did:pkh:eip155:{chainId}:{address})
|
|
@@ -19905,16 +19961,26 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
19905
19961
|
*/
|
|
19906
19962
|
getVerifiedSessionCapabilities() {
|
|
19907
19963
|
const session = this.currentTinyCloudSession();
|
|
19908
|
-
|
|
19909
|
-
|
|
19910
|
-
|
|
19911
|
-
|
|
19912
|
-
).map((entry) =>
|
|
19913
|
-
|
|
19914
|
-
|
|
19915
|
-
|
|
19916
|
-
|
|
19917
|
-
|
|
19964
|
+
return session?.siwe ? this.projectSignedRecapCapabilities(session.siwe) : [];
|
|
19965
|
+
}
|
|
19966
|
+
/** Normalize signed capabilities without collapsing nested encryption into raw authority. */
|
|
19967
|
+
projectSignedRecapCapabilities(siwe) {
|
|
19968
|
+
const projected = this.parseRecapWithCaveats(siwe).map((entry) => {
|
|
19969
|
+
const normalized = (0, import_sdk_core7.parseRecapCapabilities)(() => [entry], siwe)[0];
|
|
19970
|
+
if (normalized.service !== "tinycloud.encryption") return normalized;
|
|
19971
|
+
if (!this.isEncryptionNetworkOperation("encryption", entry.path, entry.space)) {
|
|
19972
|
+
return { ...normalized, space: entry.space };
|
|
19973
|
+
}
|
|
19974
|
+
return {
|
|
19975
|
+
service: normalized.service,
|
|
19976
|
+
path: normalized.path,
|
|
19977
|
+
actions: normalized.actions,
|
|
19978
|
+
...normalized.caveats === void 0 ? {} : { caveats: normalized.caveats }
|
|
19979
|
+
};
|
|
19980
|
+
});
|
|
19981
|
+
return projected.sort(
|
|
19982
|
+
(left, right) => (left.space ?? "").localeCompare(right.space ?? "") || left.service.localeCompare(right.service) || left.path.localeCompare(right.path)
|
|
19983
|
+
);
|
|
19918
19984
|
}
|
|
19919
19985
|
/**
|
|
19920
19986
|
* Get the Ethereum address for this user.
|
|
@@ -20369,9 +20435,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20369
20435
|
* We must keep the full owner-scoped URI so a synthetic primary grant can
|
|
20370
20436
|
* never cover an operation on a different owner's space.
|
|
20371
20437
|
*
|
|
20372
|
-
*
|
|
20373
|
-
*
|
|
20374
|
-
*
|
|
20438
|
+
* Only top-level encryption network entries (`space: "encryption"` or
|
|
20439
|
+
* absent) become `resource` ops. A network-shaped path nested under a
|
|
20440
|
+
* data space remains a `spaceId` op, not raw network authority.
|
|
20375
20441
|
* One op per action.
|
|
20376
20442
|
*
|
|
20377
20443
|
* Returns `[]` for session-only / restored-without-siwe modes and for any
|
|
@@ -20392,7 +20458,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20392
20458
|
operations = entries.flatMap((entry) => {
|
|
20393
20459
|
const service = this.invocationServiceName(entry.service);
|
|
20394
20460
|
return entry.actions.map((action) => ({
|
|
20395
|
-
...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId: entry.space },
|
|
20461
|
+
...this.isEncryptionNetworkOperation(service, entry.path, entry.space) ? { resource: entry.path } : { spaceId: entry.space },
|
|
20396
20462
|
service,
|
|
20397
20463
|
path: entry.path,
|
|
20398
20464
|
action,
|
|
@@ -20554,7 +20620,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20554
20620
|
return;
|
|
20555
20621
|
} catch (error) {
|
|
20556
20622
|
const message = error instanceof Error ? error.message : String(error);
|
|
20557
|
-
if (/Unauthorized Action|\
|
|
20623
|
+
if (/Unauthorized Action|\b(?:401|403)\b/.test(message)) {
|
|
20558
20624
|
console.warn(
|
|
20559
20625
|
"TinyCloud account registry sync stopped: authorization verdict is not retryable",
|
|
20560
20626
|
error
|
|
@@ -20804,9 +20870,14 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20804
20870
|
*/
|
|
20805
20871
|
async restoreSession(sessionData) {
|
|
20806
20872
|
await this.wasmBindings.ensureInitialized?.();
|
|
20807
|
-
|
|
20873
|
+
let restoredJwk;
|
|
20874
|
+
try {
|
|
20875
|
+
restoredJwk = clonePersistedSessionJwk(sessionData.jwk);
|
|
20876
|
+
} catch {
|
|
20877
|
+
throw new InvalidRestoredSessionError("Persisted session has an invalid private Ed25519 session key.");
|
|
20878
|
+
}
|
|
20808
20879
|
if (sessionData.chainId !== void 0 && (!Number.isSafeInteger(sessionData.chainId) || sessionData.chainId <= 0)) {
|
|
20809
|
-
throw new
|
|
20880
|
+
throw new InvalidRestoredSessionError("Persisted session chain ID must be a positive safe integer.");
|
|
20810
20881
|
}
|
|
20811
20882
|
const stagedManager = this.wasmBindings.createSessionManager();
|
|
20812
20883
|
const stagedReplace = stagedManager.replaceSessionKey;
|
|
@@ -20818,32 +20889,31 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20818
20889
|
let stagedKeyId;
|
|
20819
20890
|
let stagedJwk;
|
|
20820
20891
|
let canonicalVerificationMethod;
|
|
20892
|
+
const keyIds = liveKeys.call(this.sessionManager);
|
|
20893
|
+
if (!Array.isArray(keyIds) || !keyIds.every((keyId) => typeof keyId === "string")) {
|
|
20894
|
+
throw new UnsupportedSessionRestoreError("it cannot reliably enumerate every live session signer");
|
|
20895
|
+
}
|
|
20896
|
+
for (const keyId of keyIds) {
|
|
20897
|
+
if (keyId === this.sessionKeyId) continue;
|
|
20898
|
+
const jwk = this.sessionManager.jwk(keyId);
|
|
20899
|
+
if (!jwk) throw new Error("missing live session key");
|
|
20900
|
+
stagedReplace.call(stagedManager, clonePersistedSessionJwk(JSON.parse(jwk)), keyId);
|
|
20901
|
+
}
|
|
20821
20902
|
try {
|
|
20822
|
-
const keyIds = liveKeys.call(this.sessionManager);
|
|
20823
|
-
if (!Array.isArray(keyIds) || !keyIds.every((keyId) => typeof keyId === "string")) {
|
|
20824
|
-
throw new UnsupportedSessionRestoreError("it cannot reliably enumerate every live session signer");
|
|
20825
|
-
}
|
|
20826
|
-
for (const keyId of keyIds) {
|
|
20827
|
-
if (keyId === this.sessionKeyId) continue;
|
|
20828
|
-
const jwk = this.sessionManager.jwk(keyId);
|
|
20829
|
-
if (!jwk) throw new Error("missing live session key");
|
|
20830
|
-
stagedReplace.call(stagedManager, clonePersistedSessionJwk(JSON.parse(jwk)), keyId);
|
|
20831
|
-
}
|
|
20832
20903
|
stagedKeyId = stagedReplace.call(stagedManager, restoredJwk, this.sessionKeyId);
|
|
20833
|
-
|
|
20834
|
-
|
|
20835
|
-
stagedJwk = clonePersistedSessionJwk(JSON.parse(stagedJwkJson));
|
|
20836
|
-
canonicalVerificationMethod = stagedManager.getDID(stagedKeyId);
|
|
20837
|
-
} catch (error) {
|
|
20838
|
-
if (error instanceof UnsupportedSessionRestoreError) throw error;
|
|
20839
|
-
throw new Error("Persisted session has an invalid private Ed25519 session key.");
|
|
20904
|
+
} catch {
|
|
20905
|
+
throw new InvalidRestoredSessionError("Persisted session has an invalid private Ed25519 session key.");
|
|
20840
20906
|
}
|
|
20907
|
+
const stagedJwkJson = stagedManager.jwk(stagedKeyId);
|
|
20908
|
+
if (!stagedJwkJson) throw new Error("missing restored session key");
|
|
20909
|
+
stagedJwk = clonePersistedSessionJwk(JSON.parse(stagedJwkJson));
|
|
20910
|
+
canonicalVerificationMethod = stagedManager.getDID(stagedKeyId);
|
|
20841
20911
|
const restoredVerificationMethod = canonicalRestoredVerificationMethod(
|
|
20842
20912
|
canonicalVerificationMethod,
|
|
20843
20913
|
sessionData.verificationMethod
|
|
20844
20914
|
);
|
|
20845
20915
|
if (!restoredVerificationMethod) {
|
|
20846
|
-
throw new
|
|
20916
|
+
throw new InvalidRestoredSessionError(
|
|
20847
20917
|
"Persisted session verification method does not match its private Ed25519 session key."
|
|
20848
20918
|
);
|
|
20849
20919
|
}
|
|
@@ -20855,26 +20925,37 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20855
20925
|
];
|
|
20856
20926
|
const hasPersistedProof = proofValues.every((value) => value !== void 0);
|
|
20857
20927
|
if (!hasPersistedProof && (proofValues.some((value) => value !== void 0) || sessionData.expiresAt !== void 0)) {
|
|
20858
|
-
throw new
|
|
20928
|
+
throw new InvalidRestoredSessionError("Persisted session authority metadata is incomplete.");
|
|
20929
|
+
}
|
|
20930
|
+
let restoredAddress;
|
|
20931
|
+
try {
|
|
20932
|
+
restoredAddress = hasPersistedProof ? (0, import_sdk_core7.canonicalizeAddress)(sessionData.address) : void 0;
|
|
20933
|
+
} catch {
|
|
20934
|
+
throw new InvalidRestoredSessionError("Persisted session has an invalid wallet address.");
|
|
20859
20935
|
}
|
|
20860
|
-
const restoredAddress = hasPersistedProof ? (0, import_sdk_core7.canonicalizeAddress)(sessionData.address) : void 0;
|
|
20861
20936
|
let stagedSessionExpiry = /* @__PURE__ */ new Date(0);
|
|
20862
20937
|
let stagedRecap = [];
|
|
20863
20938
|
if (hasPersistedProof) {
|
|
20864
20939
|
if (typeof this.wasmBindings.validatePersistedSession !== "function") {
|
|
20865
20940
|
throw new UnsupportedSessionRestoreError("it cannot verify persisted SIWE authority");
|
|
20866
20941
|
}
|
|
20867
|
-
const verified =
|
|
20868
|
-
|
|
20869
|
-
|
|
20870
|
-
|
|
20871
|
-
|
|
20872
|
-
|
|
20873
|
-
|
|
20874
|
-
|
|
20875
|
-
|
|
20876
|
-
|
|
20877
|
-
|
|
20942
|
+
const verified = (() => {
|
|
20943
|
+
try {
|
|
20944
|
+
return this.wasmBindings.validatePersistedSession({
|
|
20945
|
+
delegationHeader: sessionData.delegationHeader,
|
|
20946
|
+
delegationCid: sessionData.delegationCid,
|
|
20947
|
+
spaceId: sessionData.spaceId,
|
|
20948
|
+
jwk: stagedJwk,
|
|
20949
|
+
address: restoredAddress,
|
|
20950
|
+
chainId: sessionData.chainId,
|
|
20951
|
+
siwe: sessionData.siwe,
|
|
20952
|
+
signature: sessionData.signature
|
|
20953
|
+
});
|
|
20954
|
+
} catch (error) {
|
|
20955
|
+
throw new InvalidRestoredSessionError(error instanceof Error ? error.message : String(error));
|
|
20956
|
+
}
|
|
20957
|
+
})();
|
|
20958
|
+
const exactRecap = Array.isArray(verified.verifiedRecap) ? verifierRecapEntries(verified.verifiedRecap) : verified.verifiedRecap;
|
|
20878
20959
|
if (!Array.isArray(exactRecap) || !exactRecap.every(
|
|
20879
20960
|
(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(
|
|
20880
20961
|
(caveat) => caveat !== null && typeof caveat === "object" && !Array.isArray(caveat)
|
|
@@ -20894,7 +20975,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20894
20975
|
const signedExpiry = verified.expiresAt === void 0 ? void 0 : persistedExpiry(verified.expiresAt);
|
|
20895
20976
|
const persistedPolicyExpiry = sessionData.expiresAt === void 0 ? void 0 : persistedExpiry(sessionData.expiresAt);
|
|
20896
20977
|
if (signedExpiry && persistedPolicyExpiry && !sameInstant(signedExpiry, persistedPolicyExpiry)) {
|
|
20897
|
-
throw new
|
|
20978
|
+
throw new InvalidRestoredSessionError("Persisted session expiry does not match its signed SIWE authority.");
|
|
20898
20979
|
}
|
|
20899
20980
|
stagedSessionExpiry = signedExpiry ?? persistedPolicyExpiry ?? persistedExpiry(void 0);
|
|
20900
20981
|
}
|
|
@@ -20991,7 +21072,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
20991
21072
|
operations = recap.flatMap((entry) => {
|
|
20992
21073
|
const service = this.invocationServiceName(entry.service);
|
|
20993
21074
|
return entry.actions.map((action) => ({
|
|
20994
|
-
...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId: entry.space },
|
|
21075
|
+
...this.isEncryptionNetworkOperation(service, entry.path, entry.space) ? { resource: entry.path } : { spaceId: entry.space },
|
|
20995
21076
|
service,
|
|
20996
21077
|
path: entry.path,
|
|
20997
21078
|
action,
|
|
@@ -21087,7 +21168,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21087
21168
|
const delegations = input.recap.map((entry) => ({
|
|
21088
21169
|
cid: session.delegationCid,
|
|
21089
21170
|
delegateDID: session.verificationMethod,
|
|
21090
|
-
spaceId: entry.space,
|
|
21171
|
+
spaceId: this.spaceIdsEqual(entry.space, session.spaceId) ? session.spaceId : entry.space,
|
|
21091
21172
|
path: entry.path,
|
|
21092
21173
|
actions: [...entry.actions],
|
|
21093
21174
|
caveats: cloneRecapCaveats(entry.caveats),
|
|
@@ -21777,16 +21858,11 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
21777
21858
|
);
|
|
21778
21859
|
graph.assertActive();
|
|
21779
21860
|
if (!response.ok) {
|
|
21780
|
-
|
|
21781
|
-
|
|
21782
|
-
|
|
21783
|
-
|
|
21784
|
-
|
|
21785
|
-
const nested = typeof record?.error === "object" && record.error !== null ? record.error : void 0;
|
|
21786
|
-
permissionHint = (0, import_sdk_services.parsePermissionHint)(record?.permissionHint) ?? (0, import_sdk_services.parsePermissionHint)(nested?.permissionHint);
|
|
21787
|
-
} catch {
|
|
21788
|
-
}
|
|
21789
|
-
}
|
|
21861
|
+
const permissionHint = response.status === 401 || response.status === 403 ? (0, import_sdk_services.parsePermissionHint)({
|
|
21862
|
+
service: "tinycloud.encryption",
|
|
21863
|
+
path: networkId,
|
|
21864
|
+
actions: ["tinycloud.encryption/decrypt"]
|
|
21865
|
+
}) : void 0;
|
|
21790
21866
|
throw new import_sdk_core7.DecryptTransportResponseError(response.status, permissionHint);
|
|
21791
21867
|
}
|
|
21792
21868
|
return await response.json();
|
|
@@ -22211,63 +22287,62 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22211
22287
|
}
|
|
22212
22288
|
};
|
|
22213
22289
|
}
|
|
22214
|
-
/** Register a policy
|
|
22215
|
-
async
|
|
22290
|
+
/** Register a signed policy and sibling roots with this Node's Policy/v3 runtime. */
|
|
22291
|
+
async registerPolicy(params) {
|
|
22216
22292
|
this._serviceGraph.assertActive();
|
|
22217
|
-
|
|
22218
|
-
|
|
22219
|
-
|
|
22220
|
-
|
|
22221
|
-
body: JSON.stringify({
|
|
22222
|
-
policy: { bytes: base64UrlEncode(params.policy.bytes), cid: params.policy.cid, proof: params.policy.proof },
|
|
22223
|
-
ownerDelegation: { cid: params.ownerDelegation.delegationCid, dagCbor: base64UrlEncode(params.ownerDelegation.signedDagCbor) },
|
|
22224
|
-
enforcementDelegation: params.enforcementDelegation,
|
|
22225
|
-
contentSourceDigest: params.contentSourceDigest
|
|
22226
|
-
})
|
|
22293
|
+
return (0, import_sdk_core7.registerPolicyV3)({
|
|
22294
|
+
...params,
|
|
22295
|
+
nodeOrigin: this.config.host,
|
|
22296
|
+
fetch: globalThis.fetch.bind(globalThis)
|
|
22227
22297
|
});
|
|
22228
|
-
if (!response.ok) {
|
|
22229
|
-
let code = "";
|
|
22230
|
-
try {
|
|
22231
|
-
const body = await response.clone().json();
|
|
22232
|
-
if (typeof body.error?.code === "string") code = ` ${body.error.code}`;
|
|
22233
|
-
} catch {
|
|
22234
|
-
}
|
|
22235
|
-
throw new Error(`Owner share policy registration failed: ${response.status}${code}`);
|
|
22236
|
-
}
|
|
22237
|
-
const responseBytes = new Uint8Array(await response.arrayBuffer());
|
|
22238
|
-
return (0, import_sdk_core7.validateOwnerSharePolicyRegistrationBytes)(responseBytes, params);
|
|
22239
22298
|
}
|
|
22240
|
-
/** Authorize
|
|
22241
|
-
async
|
|
22299
|
+
/** Authorize one short-lived v3 delivery against the signed v3 envelope and registered roots. */
|
|
22300
|
+
async authorizeShareDeliveryV3(input) {
|
|
22242
22301
|
const session = this.currentTinyCloudSession();
|
|
22243
22302
|
const serviceSession = this._serviceContext?.session;
|
|
22244
22303
|
if (!session || !serviceSession) throw new Error("Share delivery requires an authenticated session");
|
|
22304
|
+
const idempotencyBytes = new TextEncoder().encode(input.idempotencyKey);
|
|
22305
|
+
if (idempotencyBytes.byteLength === 0 || idempotencyBytes.byteLength > 512) {
|
|
22306
|
+
throw new Error("Share delivery idempotency key is invalid");
|
|
22307
|
+
}
|
|
22308
|
+
const jtiDigest = new Uint8Array(await crypto.subtle.digest("SHA-256", idempotencyBytes));
|
|
22245
22309
|
const body = {
|
|
22246
|
-
|
|
22310
|
+
envelope: input.envelope,
|
|
22311
|
+
sealedEnvelope: input.sealedEnvelope,
|
|
22312
|
+
envelopeKey: input.envelopeKey,
|
|
22247
22313
|
shareCid: input.shareCid,
|
|
22248
|
-
shareId: input.shareId,
|
|
22249
|
-
registrationCid: input.registrationCid,
|
|
22250
|
-
policyCid: input.policyCid,
|
|
22251
|
-
delegationCid: input.delegationCid,
|
|
22252
|
-
enforcementDelegationCid: input.enforcementDelegationCid,
|
|
22253
22314
|
recipientEmail: input.recipientEmail,
|
|
22254
22315
|
shareUrl: input.shareUrl,
|
|
22255
22316
|
documentName: input.documentName,
|
|
22256
|
-
jti: base64UrlEncode(
|
|
22257
|
-
|
|
22258
|
-
expiresAt: input.expiresAt
|
|
22317
|
+
jti: base64UrlEncode(jtiDigest.slice(0, 16)),
|
|
22318
|
+
expiresAt: new Date(input.expiresAt).toISOString().replace(/\.\d{3}Z$/, "Z")
|
|
22259
22319
|
};
|
|
22260
22320
|
const requestBodyDigest = base64UrlEncode(new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode((0, import_sdk_core7.canonicalizeEncryptionJson)(body)))));
|
|
22261
22321
|
const request = { ...body, requestBodyDigest };
|
|
22262
22322
|
const authorization = authorizationHeader(this.invokeAnyWithRuntimePermissions(serviceSession, [{ spaceId: session.spaceId, service: "kv", path: input.resourcePath, action: "tinycloud.kv/get" }]));
|
|
22263
|
-
const response = await fetch(`${this.config.host}/
|
|
22323
|
+
const response = await fetch(`${this.config.host}/policy/v3/deliveries/authorize`, {
|
|
22264
22324
|
method: "POST",
|
|
22265
22325
|
headers: { accept: "application/json", "content-type": "application/json", authorization },
|
|
22266
22326
|
body: (0, import_sdk_core7.canonicalizeEncryptionJson)(request)
|
|
22267
22327
|
});
|
|
22268
|
-
if (!response.ok) throw new Error(`
|
|
22269
|
-
const
|
|
22270
|
-
|
|
22328
|
+
if (!response.ok) throw new Error(`V3 share delivery authorization failed: ${response.status}`);
|
|
22329
|
+
const verified = (0, import_sdk_core7.validateShareDeliveryAuthorizationV3Bytes)(new Uint8Array(await response.arrayBuffer()), {
|
|
22330
|
+
request,
|
|
22331
|
+
senderKeyDid: this.credentialHolderDid,
|
|
22332
|
+
deliveryAudience: input.deliveryAudience
|
|
22333
|
+
});
|
|
22334
|
+
const requestDigest = new Uint8Array(await crypto.subtle.digest(
|
|
22335
|
+
"SHA-256",
|
|
22336
|
+
new TextEncoder().encode(`${import_sdk_core7.CREDENTIAL_INVITATION_REQUEST_DOMAIN}${(0, import_sdk_core7.canonicalizeEncryptionJson)(verified.request)}`)
|
|
22337
|
+
));
|
|
22338
|
+
return {
|
|
22339
|
+
...verified,
|
|
22340
|
+
proof: {
|
|
22341
|
+
alg: "EdDSA",
|
|
22342
|
+
kid: this.credentialHolderDid,
|
|
22343
|
+
signature: base64UrlEncode(await this.signSessionBytes(requestDigest))
|
|
22344
|
+
}
|
|
22345
|
+
};
|
|
22271
22346
|
}
|
|
22272
22347
|
async createRootDelegationForSharing(params, assertActive) {
|
|
22273
22348
|
try {
|
|
@@ -22722,7 +22797,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
22722
22797
|
continue;
|
|
22723
22798
|
}
|
|
22724
22799
|
let space;
|
|
22725
|
-
if (service !== "tinycloud.encryption") {
|
|
22800
|
+
if (service !== "tinycloud.encryption" || operation.resource === void 0) {
|
|
22726
22801
|
if (typeof operation.spaceId !== "string") continue;
|
|
22727
22802
|
try {
|
|
22728
22803
|
space = this.resolvePermissionSpace(operation.spaceId, session);
|
|
@@ -23267,11 +23342,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23267
23342
|
);
|
|
23268
23343
|
return { delegation: delegation2, prompted: true };
|
|
23269
23344
|
}
|
|
23270
|
-
const granted = (
|
|
23271
|
-
|
|
23272
|
-
session.siwe
|
|
23273
|
-
);
|
|
23274
|
-
const { subset, missing } = (0, import_sdk_core7.isCapabilitySubset)(expandedEntries, granted);
|
|
23345
|
+
const granted = this.projectSignedRecapCapabilities(session.siwe);
|
|
23346
|
+
const { subset, missing } = this.signedCapabilitySubset(expandedEntries, granted);
|
|
23275
23347
|
if (!subset) {
|
|
23276
23348
|
const runtimeOperations = this.permissionEntriesToOperations(expandedEntries, session);
|
|
23277
23349
|
const runtimeGrant = this.findGrantForOperations(
|
|
@@ -23591,7 +23663,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23591
23663
|
return abilities;
|
|
23592
23664
|
}
|
|
23593
23665
|
isEncryptionPermissionEntry(entry) {
|
|
23594
|
-
return entry.service === import_sdk_core7.ENCRYPTION_PERMISSION_SERVICE &&
|
|
23666
|
+
return entry.service === import_sdk_core7.ENCRYPTION_PERMISSION_SERVICE && this.isEncryptionNetworkOperation("encryption", entry.path, entry.space);
|
|
23595
23667
|
}
|
|
23596
23668
|
permissionsToRawAbilities(entries) {
|
|
23597
23669
|
const rawAbilities = {};
|
|
@@ -23615,7 +23687,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23615
23687
|
return entries.flatMap((entry) => {
|
|
23616
23688
|
const service = this.shortServiceName(entry.service);
|
|
23617
23689
|
return entry.actions.map((action) => ({
|
|
23618
|
-
...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
|
|
23690
|
+
...this.isEncryptionNetworkOperation(service, entry.path, entry.space) ? { resource: entry.path } : { spaceId },
|
|
23619
23691
|
service,
|
|
23620
23692
|
path: entry.path,
|
|
23621
23693
|
action,
|
|
@@ -23623,13 +23695,21 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23623
23695
|
}));
|
|
23624
23696
|
});
|
|
23625
23697
|
}
|
|
23698
|
+
/** Raw network authority cannot be covered by a nested encryption space. */
|
|
23699
|
+
signedCapabilitySubset(requested, granted) {
|
|
23700
|
+
const rawGranted = granted.filter((entry) => this.isEncryptionPermissionEntry(entry)).map((entry) => ({ ...entry, space: "encryption" }));
|
|
23701
|
+
const spaceGranted = granted.filter((entry) => !this.isEncryptionPermissionEntry(entry));
|
|
23702
|
+
const missing = requested.filter((entry) => {
|
|
23703
|
+
const raw = this.isEncryptionPermissionEntry(entry);
|
|
23704
|
+
const comparable = raw ? { ...entry, space: "encryption" } : entry;
|
|
23705
|
+
return !(0, import_sdk_core7.isCapabilitySubset)([comparable], raw ? rawGranted : spaceGranted).subset;
|
|
23706
|
+
});
|
|
23707
|
+
return { subset: missing.length === 0, missing };
|
|
23708
|
+
}
|
|
23626
23709
|
sessionCoversPermissionEntries(session, entries) {
|
|
23627
23710
|
try {
|
|
23628
|
-
const granted = (
|
|
23629
|
-
|
|
23630
|
-
session.siwe
|
|
23631
|
-
);
|
|
23632
|
-
return (0, import_sdk_core7.isCapabilitySubset)(entries, granted).subset;
|
|
23711
|
+
const granted = this.projectSignedRecapCapabilities(session.siwe);
|
|
23712
|
+
return this.signedCapabilitySubset(entries, granted).subset;
|
|
23633
23713
|
} catch {
|
|
23634
23714
|
return false;
|
|
23635
23715
|
}
|
|
@@ -23639,7 +23719,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23639
23719
|
const spaceId = this.resolvePermissionSpace(entry.space, session);
|
|
23640
23720
|
const service = this.shortServiceName(entry.service);
|
|
23641
23721
|
return entry.actions.map((action) => ({
|
|
23642
|
-
...this.isEncryptionNetworkOperation(service, entry.path) ? { resource: entry.path } : { spaceId },
|
|
23722
|
+
...this.isEncryptionNetworkOperation(service, entry.path, entry.space) ? { resource: entry.path } : { spaceId },
|
|
23643
23723
|
service,
|
|
23644
23724
|
path: entry.path,
|
|
23645
23725
|
action,
|
|
@@ -23728,7 +23808,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23728
23808
|
return resources.flatMap((resource) => {
|
|
23729
23809
|
const service = this.invocationServiceName(resource.service);
|
|
23730
23810
|
return resource.actions.map((action) => ({
|
|
23731
|
-
...this.isEncryptionNetworkOperation(service, resource.path) ? { resource: resource.path } : { spaceId: resource.space },
|
|
23811
|
+
...this.isEncryptionNetworkOperation(service, resource.path, resource.space) ? { resource: resource.path } : { spaceId: resource.space },
|
|
23732
23812
|
service,
|
|
23733
23813
|
path: resource.path,
|
|
23734
23814
|
action,
|
|
@@ -23789,7 +23869,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23789
23869
|
};
|
|
23790
23870
|
for (const resource of resources) {
|
|
23791
23871
|
const service = this.invocationServiceName(resource.service);
|
|
23792
|
-
if (this.isEncryptionNetworkOperation(service, resource.path)) {
|
|
23872
|
+
if (this.isEncryptionNetworkOperation(service, resource.path, resource.space)) {
|
|
23793
23873
|
rawAbilities[_a = resource.path] ?? (rawAbilities[_a] = []);
|
|
23794
23874
|
addActions(rawAbilities[resource.path], resource.actions);
|
|
23795
23875
|
continue;
|
|
@@ -23858,21 +23938,15 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23858
23938
|
}
|
|
23859
23939
|
return granted.spaceId !== void 0 && requested.spaceId !== void 0 && this.spaceIdsEqual(granted.spaceId, requested.spaceId) && this.pathContains(granted.path, requested.path);
|
|
23860
23940
|
}
|
|
23861
|
-
// Space IDs are `tinycloud:pkh:eip155:<chain>:<0xADDR>:<name>`.
|
|
23862
|
-
// EIP-155 address
|
|
23863
|
-
//
|
|
23864
|
-
// the EIP-55 checksummed form — so a byte-for-byte compare spuriously rejects
|
|
23865
|
-
// an otherwise-valid grant. Lowercase ONLY the `eip155:<chain>:0x<addr>`
|
|
23866
|
-
// segment and leave everything else (crucially the case-sensitive space NAME)
|
|
23867
|
-
// byte-exact. Mirrors the CLI's `normalizeSpaceForCompare` (OPENKEY_SCOPE_MISMATCH fix).
|
|
23941
|
+
// Space IDs are `tinycloud:pkh:eip155:<chain>:<0xADDR>:<name>`. Compare
|
|
23942
|
+
// only the embedded EIP-155 address without case sensitivity; chain, name,
|
|
23943
|
+
// and all non-PKH space identifiers remain byte-exact.
|
|
23868
23944
|
spaceIdsEqual(a, b) {
|
|
23869
23945
|
return this.normalizeSpaceAddress(a) === this.normalizeSpaceAddress(b);
|
|
23870
23946
|
}
|
|
23871
23947
|
normalizeSpaceAddress(space) {
|
|
23872
|
-
|
|
23873
|
-
|
|
23874
|
-
(_match, prefix, addr) => prefix + addr.toLowerCase()
|
|
23875
|
-
);
|
|
23948
|
+
const match = /^(tinycloud:pkh:eip155:\d+:)(0x[0-9a-fA-F]{40})(:.+)$/.exec(space);
|
|
23949
|
+
return match === null ? space : `${match[1]}${match[2].toLowerCase()}${match[3]}`;
|
|
23876
23950
|
}
|
|
23877
23951
|
actionContains(grantedAction, requestedAction) {
|
|
23878
23952
|
if (grantedAction === requestedAction) {
|
|
@@ -23889,10 +23963,11 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23889
23963
|
}
|
|
23890
23964
|
/** Prefer the v2 caveat-preserving parser while retaining old custom WASM. */
|
|
23891
23965
|
parseRecapWithCaveats(siwe) {
|
|
23892
|
-
|
|
23966
|
+
const verified = this.wasmBindings.parseVerifiedRecapFromSiwe?.(siwe);
|
|
23967
|
+
return verified == null ? this.wasmBindings.parseRecapFromSiwe(siwe) : verifierRecapEntries(verified);
|
|
23893
23968
|
}
|
|
23894
|
-
isEncryptionNetworkOperation(service, path) {
|
|
23895
|
-
return service === "encryption" && path.startsWith("urn:tinycloud:encryption:");
|
|
23969
|
+
isEncryptionNetworkOperation(service, path, space) {
|
|
23970
|
+
return service === "encryption" && (space === void 0 || space === "encryption") && path.startsWith("urn:tinycloud:encryption:");
|
|
23896
23971
|
}
|
|
23897
23972
|
operationFromInvokeAnyEntry(entry) {
|
|
23898
23973
|
const service = this.invocationServiceName(entry.service);
|
|
@@ -23905,7 +23980,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
23905
23980
|
...entry.caveats === void 0 ? {} : { caveats: cloneRecapCaveats(entry.caveats) }
|
|
23906
23981
|
};
|
|
23907
23982
|
}
|
|
23908
|
-
if (this.isEncryptionNetworkOperation(service, entry.path)) {
|
|
23983
|
+
if (entry.spaceId === void 0 && this.isEncryptionNetworkOperation(service, entry.path)) {
|
|
23909
23984
|
return {
|
|
23910
23985
|
resource: entry.path,
|
|
23911
23986
|
service,
|
|
@@ -24190,8 +24265,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
24190
24265
|
const jwk = mySession.jwk;
|
|
24191
24266
|
const { abilities, rawAbilities } = this.buildActivationAbilities(delegation);
|
|
24192
24267
|
const now = /* @__PURE__ */ new Date();
|
|
24193
|
-
const
|
|
24194
|
-
const expirationTime = delegation.expiry < maxExpiry ? delegation.expiry : maxExpiry;
|
|
24268
|
+
const expirationTime = delegation.expiry;
|
|
24195
24269
|
const prepared = this.wasmBindings.prepareSession({
|
|
24196
24270
|
abilities,
|
|
24197
24271
|
address: this.wasmBindings.ensureEip55(mySession.address),
|
|
@@ -24295,8 +24369,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
24295
24369
|
);
|
|
24296
24370
|
}
|
|
24297
24371
|
const now = /* @__PURE__ */ new Date();
|
|
24298
|
-
const
|
|
24299
|
-
const requestedExpiry = new Date(now.getTime() + expiryMs);
|
|
24372
|
+
const requestedExpiry = params.expiryMs === void 0 ? parentDelegation.expiry : new Date(now.getTime() + params.expiryMs);
|
|
24300
24373
|
const actualExpiry = requestedExpiry > parentDelegation.expiry ? parentDelegation.expiry : requestedExpiry;
|
|
24301
24374
|
const abilities = {};
|
|
24302
24375
|
const rawAbilities = {};
|
|
@@ -24969,6 +25042,7 @@ var import_sdk_core23 = require("@tinycloud/sdk-core");
|
|
|
24969
25042
|
FileSessionStorage,
|
|
24970
25043
|
HooksService,
|
|
24971
25044
|
IdentityParseError,
|
|
25045
|
+
InvalidRestoredSessionError,
|
|
24972
25046
|
KVService,
|
|
24973
25047
|
ManifestValidationError,
|
|
24974
25048
|
MemorySessionStorage,
|
|
@@ -25048,6 +25122,7 @@ var import_sdk_core23 = require("@tinycloud/sdk-core");
|
|
|
25048
25122
|
expandActionShortNames,
|
|
25049
25123
|
expandPermissionEntries,
|
|
25050
25124
|
expandPermissionEntry,
|
|
25125
|
+
extractSiweExpiration,
|
|
25051
25126
|
generateRandomReceiverKey,
|
|
25052
25127
|
getTinyCloudDebugLogs,
|
|
25053
25128
|
grantAuthRequest,
|
|
@@ -25066,6 +25141,7 @@ var import_sdk_core23 = require("@tinycloud/sdk-core");
|
|
|
25066
25141
|
parseExpiry,
|
|
25067
25142
|
parseNetworkId,
|
|
25068
25143
|
parsePkhDid,
|
|
25144
|
+
parseSecretCatalogKey,
|
|
25069
25145
|
parseSpaceUri,
|
|
25070
25146
|
pkhDid,
|
|
25071
25147
|
principalDid,
|