@tinycloud/node-sdk 3.1.0-beta.7 → 3.1.0-beta.9
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-BWvBTn9l.d.cts → core-CC7uDKZP.d.cts} +72 -6
- package/dist/{core-BWvBTn9l.d.ts → core-CC7uDKZP.d.ts} +72 -6
- package/dist/core.cjs +200 -53
- 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 +201 -53
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +252 -85
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +252 -85
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
|
@@ -758,6 +758,17 @@ interface PortableDelegation extends Omit<Delegation, "isRevoked"> {
|
|
|
758
758
|
* fields are authoritative (legacy single-resource shape).
|
|
759
759
|
*/
|
|
760
760
|
resources?: DelegatedResource[];
|
|
761
|
+
/**
|
|
762
|
+
* The wallet-signed SIWE message and EIP-191 signature behind a
|
|
763
|
+
* `delegationHeader` that is a CACAO session grant (OpenKey `/delegate`,
|
|
764
|
+
* {@link TinyCloudNode.grantRuntimePermissions}) rather than a compact UCAN.
|
|
765
|
+
* Not authority: activation rebuilds the CACAO from them and accepts only
|
|
766
|
+
* byte-identical authorization bytes.
|
|
767
|
+
*/
|
|
768
|
+
siweProof?: {
|
|
769
|
+
siwe: string;
|
|
770
|
+
signature: string;
|
|
771
|
+
};
|
|
761
772
|
}
|
|
762
773
|
/**
|
|
763
774
|
* The transport shape `tc auth request --emit` produces and that an owner
|
|
@@ -819,6 +830,11 @@ interface RuntimeDelegationActivator {
|
|
|
819
830
|
useRuntimeDelegation(delegation: PortableDelegation): Promise<void>;
|
|
820
831
|
useDelegation?(delegation: PortableDelegation): Promise<unknown>;
|
|
821
832
|
getRuntimePermissionDelegations(): PortableDelegation[];
|
|
833
|
+
/**
|
|
834
|
+
* Verify a wallet-signed session grant issued to this runtime's live session
|
|
835
|
+
* key. Required to activate a CACAO grant; compact UCANs do not use it.
|
|
836
|
+
*/
|
|
837
|
+
verifySessionGrant?(grant: SessionGrantProof): VerifiedSessionGrant;
|
|
822
838
|
}
|
|
823
839
|
/** The installed authority returned by {@link activateValidatedRuntimeDelegation}. */
|
|
824
840
|
interface ValidatedRuntimeDelegation {
|
|
@@ -842,13 +858,51 @@ interface CompactRuntimeDelegationInput {
|
|
|
842
858
|
readonly ownerAddress: string;
|
|
843
859
|
readonly chainId: number;
|
|
844
860
|
}
|
|
861
|
+
/** A CACAO session grant and the signed SIWE proof it was built from. */
|
|
862
|
+
interface SessionGrantProof {
|
|
863
|
+
readonly delegationHeader: {
|
|
864
|
+
Authorization: string;
|
|
865
|
+
};
|
|
866
|
+
readonly delegationCid: string;
|
|
867
|
+
readonly spaceId: string;
|
|
868
|
+
readonly address: string;
|
|
869
|
+
readonly chainId: number;
|
|
870
|
+
readonly siwe: string;
|
|
871
|
+
readonly signature: string;
|
|
872
|
+
}
|
|
873
|
+
/** Authority read from a verified session grant's signed ReCap. */
|
|
874
|
+
interface VerifiedSessionGrant {
|
|
875
|
+
readonly recap: readonly {
|
|
876
|
+
service: string;
|
|
877
|
+
space: string;
|
|
878
|
+
path: string;
|
|
879
|
+
actions: string[];
|
|
880
|
+
caveats?: Record<string, unknown>[];
|
|
881
|
+
}[];
|
|
882
|
+
readonly expiresAt: Date;
|
|
883
|
+
}
|
|
845
884
|
/**
|
|
846
|
-
*
|
|
885
|
+
* Stable reasons {@link activateValidatedRuntimeDelegation} refuses a
|
|
886
|
+
* delegation for. The error message is for the caller and may quote
|
|
887
|
+
* transport fields or a node's response; the reason is safe to publish.
|
|
888
|
+
*/
|
|
889
|
+
declare const RUNTIME_DELEGATION_REJECTIONS: readonly ["host_mismatch", "expired", "cid_mismatch", "authority_invalid", "proof_missing", "proof_invalid", "owner_mismatch", "audience_mismatch", "resources_mismatch", "unsupported", "activation_rejected"];
|
|
890
|
+
type RuntimeDelegationRejection = typeof RUNTIME_DELEGATION_REJECTIONS[number];
|
|
891
|
+
declare class RuntimeDelegationRejectedError extends Error {
|
|
892
|
+
readonly reason: RuntimeDelegationRejection;
|
|
893
|
+
constructor(reason: RuntimeDelegationRejection, message: string);
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* CID-bind and activate one runtime delegation.
|
|
847
897
|
*
|
|
848
|
-
*
|
|
849
|
-
*
|
|
850
|
-
*
|
|
851
|
-
*
|
|
898
|
+
* A compact UCAN's audience, expiry and permissions come from its signed
|
|
899
|
+
* payload, and its transport fields must agree with them. A CACAO session
|
|
900
|
+
* grant (OpenKey `/delegate`, `grantRuntimePermissions`) is verified from its
|
|
901
|
+
* `siweProof` against this runtime's live session key; its installed
|
|
902
|
+
* authority is exactly the verified ReCap, and its transport `resources` are
|
|
903
|
+
* display data that is never consulted. Either way the helper then invokes
|
|
904
|
+
* {@link TinyCloudNode.useRuntimeDelegation}; the node activation call remains
|
|
905
|
+
* the authority and delegation-chain validation boundary.
|
|
852
906
|
*
|
|
853
907
|
* `options.authorize`, when given, sees the signed capabilities before
|
|
854
908
|
* anything is activated or installed; returning `false` refuses the
|
|
@@ -1971,6 +2025,18 @@ declare class TinyCloudNode {
|
|
|
1971
2025
|
* live authority must combine this with {@link getVerifiedSessionCapabilities}.
|
|
1972
2026
|
*/
|
|
1973
2027
|
getEffectiveRuntimePermissionEntries(): PermissionEntry[];
|
|
2028
|
+
/**
|
|
2029
|
+
* Verify a wallet-signed session grant (a CACAO built from a SIWE and its
|
|
2030
|
+
* EIP-191 signature) issued to this instance's live session key. The
|
|
2031
|
+
* verifier rebuilds the CACAO from `siwe` and `signature`, so the grant is
|
|
2032
|
+
* accepted only when its authorization bytes and CID are exactly that CACAO,
|
|
2033
|
+
* its signer is `address`, its audience is this session key, it is valid
|
|
2034
|
+
* now, and its ReCap covers `spaceId` or names only raw encryption networks
|
|
2035
|
+
* (a decrypt-only grant has no space). Returns the signed ReCap and expiry.
|
|
2036
|
+
* Bindings without `validateSessionGrant` fall back to the session verifier,
|
|
2037
|
+
* which also refuses decrypt-only grants.
|
|
2038
|
+
*/
|
|
2039
|
+
verifySessionGrant(grant: SessionGrantProof): VerifiedSessionGrant;
|
|
1974
2040
|
/**
|
|
1975
2041
|
* Install a portable runtime permission delegation into this SDK instance so
|
|
1976
2042
|
* matching service calls and downstream `delegateTo()` calls can use it.
|
|
@@ -2479,4 +2545,4 @@ declare class WasmKeyProvider implements KeyProvider {
|
|
|
2479
2545
|
*/
|
|
2480
2546
|
declare function createWasmKeyProvider(sessionManager: SessionManagerWithListing): WasmKeyProvider;
|
|
2481
2547
|
|
|
2482
|
-
export { ACCEPTED_MARKER_VERSIONS as A, BOOTSTRAP_COMPLETION_MARKER_KEY as B, type CompactRuntimeDelegationInput as C, type DelegateToOptions as D, type EnsureEncryptionNetworkOptions as E, FileSessionStorage as F,
|
|
2548
|
+
export { ACCEPTED_MARKER_VERSIONS as A, BOOTSTRAP_COMPLETION_MARKER_KEY as B, type CompactRuntimeDelegationInput as C, type DelegateToOptions as D, type EnsureEncryptionNetworkOptions as E, FileSessionStorage as F, activateValidatedRuntimeDelegation as G, createWasmKeyProvider as H, InvalidRestoredSessionError as I, defaultSignStrategy as J, deserializeDelegation as K, grantAuthRequest as L, MemorySessionStorage as M, type NodeEventEmitterStrategy as N, type OwnerDelegationReceipt as O, type PortableDelegation as P, serializeDelegation as Q, RUNTIME_DELEGATION_REJECTIONS as R, type SecretPermissionHint as S, TinyCloudNode as T, type UnifiedOwnerRootInput as U, type ValidatedRuntimeDelegation as V, WasmKeyProvider as W, type AuthDelegationArtifact as a, type AuthRequestArtifact as b, BOOTSTRAP_COMPLETION_MARKER_VERSION as c, type BootstrapCompletionMarker as d, type BootstrapWarning as e, type CreateOwnerDelegationParams as f, type DelegateToResult as g, DelegatedAccess as h, type DelegationAuthority as i, NodeUserAuthorization as j, type NodeUserAuthorizationConfig as k, type RestorableSession as l, type RuntimeDelegationActivator as m, RuntimeDelegationRejectedError as n, type RuntimeDelegationRejection as o, type RuntimePermissionGrantOptions as p, type SecretReadInput as q, type SecretReadResult as r, type SessionGrantProof as s, type SignStrategy as t, type TinyCloudNodeConfig as u, type UnifiedOwnerRootReceipt as v, UnsupportedSessionRestoreError as w, type VerifiedSessionGrant as x, type WasmKeyProviderConfig as y, activateCompactRuntimeDelegation as z };
|
|
@@ -758,6 +758,17 @@ interface PortableDelegation extends Omit<Delegation, "isRevoked"> {
|
|
|
758
758
|
* fields are authoritative (legacy single-resource shape).
|
|
759
759
|
*/
|
|
760
760
|
resources?: DelegatedResource[];
|
|
761
|
+
/**
|
|
762
|
+
* The wallet-signed SIWE message and EIP-191 signature behind a
|
|
763
|
+
* `delegationHeader` that is a CACAO session grant (OpenKey `/delegate`,
|
|
764
|
+
* {@link TinyCloudNode.grantRuntimePermissions}) rather than a compact UCAN.
|
|
765
|
+
* Not authority: activation rebuilds the CACAO from them and accepts only
|
|
766
|
+
* byte-identical authorization bytes.
|
|
767
|
+
*/
|
|
768
|
+
siweProof?: {
|
|
769
|
+
siwe: string;
|
|
770
|
+
signature: string;
|
|
771
|
+
};
|
|
761
772
|
}
|
|
762
773
|
/**
|
|
763
774
|
* The transport shape `tc auth request --emit` produces and that an owner
|
|
@@ -819,6 +830,11 @@ interface RuntimeDelegationActivator {
|
|
|
819
830
|
useRuntimeDelegation(delegation: PortableDelegation): Promise<void>;
|
|
820
831
|
useDelegation?(delegation: PortableDelegation): Promise<unknown>;
|
|
821
832
|
getRuntimePermissionDelegations(): PortableDelegation[];
|
|
833
|
+
/**
|
|
834
|
+
* Verify a wallet-signed session grant issued to this runtime's live session
|
|
835
|
+
* key. Required to activate a CACAO grant; compact UCANs do not use it.
|
|
836
|
+
*/
|
|
837
|
+
verifySessionGrant?(grant: SessionGrantProof): VerifiedSessionGrant;
|
|
822
838
|
}
|
|
823
839
|
/** The installed authority returned by {@link activateValidatedRuntimeDelegation}. */
|
|
824
840
|
interface ValidatedRuntimeDelegation {
|
|
@@ -842,13 +858,51 @@ interface CompactRuntimeDelegationInput {
|
|
|
842
858
|
readonly ownerAddress: string;
|
|
843
859
|
readonly chainId: number;
|
|
844
860
|
}
|
|
861
|
+
/** A CACAO session grant and the signed SIWE proof it was built from. */
|
|
862
|
+
interface SessionGrantProof {
|
|
863
|
+
readonly delegationHeader: {
|
|
864
|
+
Authorization: string;
|
|
865
|
+
};
|
|
866
|
+
readonly delegationCid: string;
|
|
867
|
+
readonly spaceId: string;
|
|
868
|
+
readonly address: string;
|
|
869
|
+
readonly chainId: number;
|
|
870
|
+
readonly siwe: string;
|
|
871
|
+
readonly signature: string;
|
|
872
|
+
}
|
|
873
|
+
/** Authority read from a verified session grant's signed ReCap. */
|
|
874
|
+
interface VerifiedSessionGrant {
|
|
875
|
+
readonly recap: readonly {
|
|
876
|
+
service: string;
|
|
877
|
+
space: string;
|
|
878
|
+
path: string;
|
|
879
|
+
actions: string[];
|
|
880
|
+
caveats?: Record<string, unknown>[];
|
|
881
|
+
}[];
|
|
882
|
+
readonly expiresAt: Date;
|
|
883
|
+
}
|
|
845
884
|
/**
|
|
846
|
-
*
|
|
885
|
+
* Stable reasons {@link activateValidatedRuntimeDelegation} refuses a
|
|
886
|
+
* delegation for. The error message is for the caller and may quote
|
|
887
|
+
* transport fields or a node's response; the reason is safe to publish.
|
|
888
|
+
*/
|
|
889
|
+
declare const RUNTIME_DELEGATION_REJECTIONS: readonly ["host_mismatch", "expired", "cid_mismatch", "authority_invalid", "proof_missing", "proof_invalid", "owner_mismatch", "audience_mismatch", "resources_mismatch", "unsupported", "activation_rejected"];
|
|
890
|
+
type RuntimeDelegationRejection = typeof RUNTIME_DELEGATION_REJECTIONS[number];
|
|
891
|
+
declare class RuntimeDelegationRejectedError extends Error {
|
|
892
|
+
readonly reason: RuntimeDelegationRejection;
|
|
893
|
+
constructor(reason: RuntimeDelegationRejection, message: string);
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* CID-bind and activate one runtime delegation.
|
|
847
897
|
*
|
|
848
|
-
*
|
|
849
|
-
*
|
|
850
|
-
*
|
|
851
|
-
*
|
|
898
|
+
* A compact UCAN's audience, expiry and permissions come from its signed
|
|
899
|
+
* payload, and its transport fields must agree with them. A CACAO session
|
|
900
|
+
* grant (OpenKey `/delegate`, `grantRuntimePermissions`) is verified from its
|
|
901
|
+
* `siweProof` against this runtime's live session key; its installed
|
|
902
|
+
* authority is exactly the verified ReCap, and its transport `resources` are
|
|
903
|
+
* display data that is never consulted. Either way the helper then invokes
|
|
904
|
+
* {@link TinyCloudNode.useRuntimeDelegation}; the node activation call remains
|
|
905
|
+
* the authority and delegation-chain validation boundary.
|
|
852
906
|
*
|
|
853
907
|
* `options.authorize`, when given, sees the signed capabilities before
|
|
854
908
|
* anything is activated or installed; returning `false` refuses the
|
|
@@ -1971,6 +2025,18 @@ declare class TinyCloudNode {
|
|
|
1971
2025
|
* live authority must combine this with {@link getVerifiedSessionCapabilities}.
|
|
1972
2026
|
*/
|
|
1973
2027
|
getEffectiveRuntimePermissionEntries(): PermissionEntry[];
|
|
2028
|
+
/**
|
|
2029
|
+
* Verify a wallet-signed session grant (a CACAO built from a SIWE and its
|
|
2030
|
+
* EIP-191 signature) issued to this instance's live session key. The
|
|
2031
|
+
* verifier rebuilds the CACAO from `siwe` and `signature`, so the grant is
|
|
2032
|
+
* accepted only when its authorization bytes and CID are exactly that CACAO,
|
|
2033
|
+
* its signer is `address`, its audience is this session key, it is valid
|
|
2034
|
+
* now, and its ReCap covers `spaceId` or names only raw encryption networks
|
|
2035
|
+
* (a decrypt-only grant has no space). Returns the signed ReCap and expiry.
|
|
2036
|
+
* Bindings without `validateSessionGrant` fall back to the session verifier,
|
|
2037
|
+
* which also refuses decrypt-only grants.
|
|
2038
|
+
*/
|
|
2039
|
+
verifySessionGrant(grant: SessionGrantProof): VerifiedSessionGrant;
|
|
1974
2040
|
/**
|
|
1975
2041
|
* Install a portable runtime permission delegation into this SDK instance so
|
|
1976
2042
|
* matching service calls and downstream `delegateTo()` calls can use it.
|
|
@@ -2479,4 +2545,4 @@ declare class WasmKeyProvider implements KeyProvider {
|
|
|
2479
2545
|
*/
|
|
2480
2546
|
declare function createWasmKeyProvider(sessionManager: SessionManagerWithListing): WasmKeyProvider;
|
|
2481
2547
|
|
|
2482
|
-
export { ACCEPTED_MARKER_VERSIONS as A, BOOTSTRAP_COMPLETION_MARKER_KEY as B, type CompactRuntimeDelegationInput as C, type DelegateToOptions as D, type EnsureEncryptionNetworkOptions as E, FileSessionStorage as F,
|
|
2548
|
+
export { ACCEPTED_MARKER_VERSIONS as A, BOOTSTRAP_COMPLETION_MARKER_KEY as B, type CompactRuntimeDelegationInput as C, type DelegateToOptions as D, type EnsureEncryptionNetworkOptions as E, FileSessionStorage as F, activateValidatedRuntimeDelegation as G, createWasmKeyProvider as H, InvalidRestoredSessionError as I, defaultSignStrategy as J, deserializeDelegation as K, grantAuthRequest as L, MemorySessionStorage as M, type NodeEventEmitterStrategy as N, type OwnerDelegationReceipt as O, type PortableDelegation as P, serializeDelegation as Q, RUNTIME_DELEGATION_REJECTIONS as R, type SecretPermissionHint as S, TinyCloudNode as T, type UnifiedOwnerRootInput as U, type ValidatedRuntimeDelegation as V, WasmKeyProvider as W, type AuthDelegationArtifact as a, type AuthRequestArtifact as b, BOOTSTRAP_COMPLETION_MARKER_VERSION as c, type BootstrapCompletionMarker as d, type BootstrapWarning as e, type CreateOwnerDelegationParams as f, type DelegateToResult as g, DelegatedAccess as h, type DelegationAuthority as i, NodeUserAuthorization as j, type NodeUserAuthorizationConfig as k, type RestorableSession as l, type RuntimeDelegationActivator as m, RuntimeDelegationRejectedError as n, type RuntimeDelegationRejection as o, type RuntimePermissionGrantOptions as p, type SecretReadInput as q, type SecretReadResult as r, type SessionGrantProof as s, type SignStrategy as t, type TinyCloudNodeConfig as u, type UnifiedOwnerRootReceipt as v, UnsupportedSessionRestoreError as w, type VerifiedSessionGrant as x, type WasmKeyProviderConfig as y, activateCompactRuntimeDelegation as z };
|
package/dist/core.cjs
CHANGED
|
@@ -1732,14 +1732,14 @@ var NodeUserAuthorization = class {
|
|
|
1732
1732
|
* Request signature via event emitter with timeout.
|
|
1733
1733
|
*/
|
|
1734
1734
|
requestSignatureViaEmitter(request, emitter, timeout) {
|
|
1735
|
-
return new Promise((resolve,
|
|
1735
|
+
return new Promise((resolve, reject2) => {
|
|
1736
1736
|
const timeoutId = setTimeout(() => {
|
|
1737
|
-
|
|
1737
|
+
reject2(new Error("Sign request timed out"));
|
|
1738
1738
|
}, timeout);
|
|
1739
1739
|
const respond = async (response) => {
|
|
1740
1740
|
clearTimeout(timeoutId);
|
|
1741
1741
|
if (!response.approved) {
|
|
1742
|
-
|
|
1742
|
+
reject2(
|
|
1743
1743
|
new Error(response.reason ?? "Sign request rejected via emitter")
|
|
1744
1744
|
);
|
|
1745
1745
|
} else {
|
|
@@ -2322,6 +2322,25 @@ function jsonFromVerifier(value) {
|
|
|
2322
2322
|
function verifierRecapEntries(entries) {
|
|
2323
2323
|
return entries.map((entry) => Array.isArray(entry.caveats) ? { ...entry, caveats: entry.caveats.map((caveat) => jsonFromVerifier(caveat)) } : entry);
|
|
2324
2324
|
}
|
|
2325
|
+
function exactVerifiedRecap(verified) {
|
|
2326
|
+
const exactRecap = Array.isArray(verified.verifiedRecap) ? verifierRecapEntries(verified.verifiedRecap) : verified.verifiedRecap;
|
|
2327
|
+
if (!Array.isArray(exactRecap) || !exactRecap.every(
|
|
2328
|
+
(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(
|
|
2329
|
+
(caveat) => caveat !== null && typeof caveat === "object" && !Array.isArray(caveat)
|
|
2330
|
+
)
|
|
2331
|
+
)) {
|
|
2332
|
+
throw new UnsupportedSessionRestoreError(
|
|
2333
|
+
"it cannot reconstruct caveat-preserving persisted ReCap authority"
|
|
2334
|
+
);
|
|
2335
|
+
}
|
|
2336
|
+
return exactRecap.map((entry) => ({
|
|
2337
|
+
service: entry.service,
|
|
2338
|
+
space: entry.space,
|
|
2339
|
+
path: entry.path,
|
|
2340
|
+
actions: [...entry.actions],
|
|
2341
|
+
caveats: cloneRecapCaveats(entry.caveats)
|
|
2342
|
+
}));
|
|
2343
|
+
}
|
|
2325
2344
|
var ServiceGraphLifetime = class {
|
|
2326
2345
|
constructor(invokeFn, invokeAnyFn, fetchFn) {
|
|
2327
2346
|
this.invokeFn = invokeFn;
|
|
@@ -3986,23 +4005,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3986
4005
|
throw new InvalidRestoredSessionError(error instanceof Error ? error.message : String(error));
|
|
3987
4006
|
}
|
|
3988
4007
|
})();
|
|
3989
|
-
|
|
3990
|
-
if (!Array.isArray(exactRecap) || !exactRecap.every(
|
|
3991
|
-
(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(
|
|
3992
|
-
(caveat) => caveat !== null && typeof caveat === "object" && !Array.isArray(caveat)
|
|
3993
|
-
)
|
|
3994
|
-
)) {
|
|
3995
|
-
throw new UnsupportedSessionRestoreError(
|
|
3996
|
-
"it cannot reconstruct caveat-preserving persisted ReCap authority"
|
|
3997
|
-
);
|
|
3998
|
-
}
|
|
3999
|
-
stagedRecap = exactRecap.map((entry) => ({
|
|
4000
|
-
service: entry.service,
|
|
4001
|
-
space: entry.space,
|
|
4002
|
-
path: entry.path,
|
|
4003
|
-
actions: [...entry.actions],
|
|
4004
|
-
caveats: cloneRecapCaveats(entry.caveats)
|
|
4005
|
-
}));
|
|
4008
|
+
stagedRecap = exactVerifiedRecap(verified);
|
|
4006
4009
|
const signedExpiry = verified.expiresAt === void 0 ? void 0 : persistedExpiry(verified.expiresAt);
|
|
4007
4010
|
const persistedPolicyExpiry = sessionData.expiresAt === void 0 ? void 0 : persistedExpiry(sessionData.expiresAt);
|
|
4008
4011
|
if (signedExpiry && persistedPolicyExpiry && !sameInstant(signedExpiry, persistedPolicyExpiry)) {
|
|
@@ -5863,6 +5866,42 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5863
5866
|
(left, right) => left.service.localeCompare(right.service) || (left.space ?? "").localeCompare(right.space ?? "") || left.path.localeCompare(right.path) || left.actions.join("\0").localeCompare(right.actions.join("\0"))
|
|
5864
5867
|
);
|
|
5865
5868
|
}
|
|
5869
|
+
/**
|
|
5870
|
+
* Verify a wallet-signed session grant (a CACAO built from a SIWE and its
|
|
5871
|
+
* EIP-191 signature) issued to this instance's live session key. The
|
|
5872
|
+
* verifier rebuilds the CACAO from `siwe` and `signature`, so the grant is
|
|
5873
|
+
* accepted only when its authorization bytes and CID are exactly that CACAO,
|
|
5874
|
+
* its signer is `address`, its audience is this session key, it is valid
|
|
5875
|
+
* now, and its ReCap covers `spaceId` or names only raw encryption networks
|
|
5876
|
+
* (a decrypt-only grant has no space). Returns the signed ReCap and expiry.
|
|
5877
|
+
* Bindings without `validateSessionGrant` fall back to the session verifier,
|
|
5878
|
+
* which also refuses decrypt-only grants.
|
|
5879
|
+
*/
|
|
5880
|
+
verifySessionGrant(grant) {
|
|
5881
|
+
const bindings = this.wasmBindings;
|
|
5882
|
+
const validate = typeof bindings.validateSessionGrant === "function" ? bindings.validateSessionGrant.bind(bindings) : bindings.validatePersistedSession?.bind(bindings);
|
|
5883
|
+
if (validate === void 0) {
|
|
5884
|
+
throw new UnsupportedSessionRestoreError("it cannot verify persisted SIWE authority");
|
|
5885
|
+
}
|
|
5886
|
+
const verified = validate({
|
|
5887
|
+
delegationHeader: { Authorization: grant.delegationHeader.Authorization },
|
|
5888
|
+
delegationCid: grant.delegationCid,
|
|
5889
|
+
spaceId: grant.spaceId,
|
|
5890
|
+
jwk: this.sessionKeyJwk,
|
|
5891
|
+
address: (0, import_sdk_core8.canonicalizeAddress)(grant.address),
|
|
5892
|
+
chainId: grant.chainId,
|
|
5893
|
+
siwe: grant.siwe,
|
|
5894
|
+
signature: grant.signature
|
|
5895
|
+
});
|
|
5896
|
+
if (typeof verified.expiresAt !== "string") {
|
|
5897
|
+
throw new Error("Session grant has no signed expiry.");
|
|
5898
|
+
}
|
|
5899
|
+
const expiresAt = new Date(verified.expiresAt);
|
|
5900
|
+
if (!Number.isFinite(expiresAt.getTime())) {
|
|
5901
|
+
throw new Error("Session grant has an invalid signed expiry.");
|
|
5902
|
+
}
|
|
5903
|
+
return { recap: exactVerifiedRecap(verified), expiresAt };
|
|
5904
|
+
}
|
|
5866
5905
|
/**
|
|
5867
5906
|
* Install a portable runtime permission delegation into this SDK instance so
|
|
5868
5907
|
* matching service calls and downstream `delegateTo()` calls can use it.
|
|
@@ -5995,7 +6034,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5995
6034
|
delegatedEntries,
|
|
5996
6035
|
spaceId,
|
|
5997
6036
|
session,
|
|
5998
|
-
expiresAt
|
|
6037
|
+
expiresAt,
|
|
6038
|
+
{ siwe: prepared.siwe, signature }
|
|
5999
6039
|
);
|
|
6000
6040
|
this.runtimePermissionGrants.push({
|
|
6001
6041
|
session: {
|
|
@@ -6781,7 +6821,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6781
6821
|
}
|
|
6782
6822
|
return grants;
|
|
6783
6823
|
}
|
|
6784
|
-
runtimeDelegationFromSession(delegatedSession, entries, spaceId, session, expiresAt) {
|
|
6824
|
+
runtimeDelegationFromSession(delegatedSession, entries, spaceId, session, expiresAt, siweProof) {
|
|
6785
6825
|
const resources = this.delegatedResourcesForEntries(entries, spaceId);
|
|
6786
6826
|
const primary = resources[0];
|
|
6787
6827
|
return {
|
|
@@ -6796,7 +6836,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6796
6836
|
delegateDID: session.verificationMethod,
|
|
6797
6837
|
ownerAddress: session.address,
|
|
6798
6838
|
chainId: session.chainId,
|
|
6799
|
-
host: this.config.host
|
|
6839
|
+
host: this.config.host,
|
|
6840
|
+
siweProof
|
|
6800
6841
|
};
|
|
6801
6842
|
}
|
|
6802
6843
|
runtimeGrantFromDelegation(delegation, session) {
|
|
@@ -6989,6 +7030,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6989
7030
|
if (grantedAction === requestedAction) {
|
|
6990
7031
|
return true;
|
|
6991
7032
|
}
|
|
7033
|
+
if ((0, import_sdk_core8.isExplicitOnlyAction)(requestedAction)) {
|
|
7034
|
+
return false;
|
|
7035
|
+
}
|
|
6992
7036
|
if (grantedAction.endsWith("/*")) {
|
|
6993
7037
|
const prefix = grantedAction.slice(0, -2);
|
|
6994
7038
|
return requestedAction.startsWith(`${prefix}/`);
|
|
@@ -7492,6 +7536,16 @@ var import_sdk_core12 = require("@tinycloud/sdk-core");
|
|
|
7492
7536
|
|
|
7493
7537
|
// src/delegation.ts
|
|
7494
7538
|
var import_sdk_core9 = require("@tinycloud/sdk-core");
|
|
7539
|
+
var RuntimeDelegationRejectedError = class extends Error {
|
|
7540
|
+
constructor(reason, message) {
|
|
7541
|
+
super(message);
|
|
7542
|
+
this.name = "RuntimeDelegationRejectedError";
|
|
7543
|
+
this.reason = reason;
|
|
7544
|
+
}
|
|
7545
|
+
};
|
|
7546
|
+
function reject(reason, message) {
|
|
7547
|
+
throw new RuntimeDelegationRejectedError(reason, message);
|
|
7548
|
+
}
|
|
7495
7549
|
var SERVICE_BY_SHORT_NAME = {
|
|
7496
7550
|
kv: "tinycloud.kv",
|
|
7497
7551
|
sql: "tinycloud.sql",
|
|
@@ -7638,6 +7692,15 @@ function signedAuthorityFromCompactUcan(authorization) {
|
|
|
7638
7692
|
abilities
|
|
7639
7693
|
));
|
|
7640
7694
|
}
|
|
7695
|
+
return canonicalSignedAuthority({
|
|
7696
|
+
issuer: payload.iss,
|
|
7697
|
+
audience: payload.aud,
|
|
7698
|
+
expiry,
|
|
7699
|
+
proofs: [...payload.prf],
|
|
7700
|
+
...remainingRedelegationDepth === void 0 ? {} : { remainingRedelegationDepth }
|
|
7701
|
+
}, permissions);
|
|
7702
|
+
}
|
|
7703
|
+
function canonicalSignedAuthority(signed, permissions) {
|
|
7641
7704
|
if (permissions.length === 0) {
|
|
7642
7705
|
throw new Error("Validated runtime delegation has no signed capabilities.");
|
|
7643
7706
|
}
|
|
@@ -7649,15 +7712,84 @@ function signedAuthorityFromCompactUcan(authorization) {
|
|
|
7649
7712
|
actions: [...permission.actions],
|
|
7650
7713
|
...permission.caveats === void 0 ? {} : { caveats: permission.caveats }
|
|
7651
7714
|
}));
|
|
7652
|
-
return {
|
|
7653
|
-
|
|
7654
|
-
|
|
7655
|
-
|
|
7656
|
-
|
|
7657
|
-
|
|
7658
|
-
|
|
7659
|
-
|
|
7660
|
-
|
|
7715
|
+
return { ...signed, permissions: canonicalPermissions, resources: canonicalResources };
|
|
7716
|
+
}
|
|
7717
|
+
var RAW_ENCRYPTION_PREFIX = "urn:tinycloud:encryption:";
|
|
7718
|
+
var PKH_SPACE = /^tinycloud:pkh:eip155:(\d+):(0x[0-9a-fA-F]{40}):(.+)$/;
|
|
7719
|
+
function comparableSpace(space) {
|
|
7720
|
+
const match = PKH_SPACE.exec(space);
|
|
7721
|
+
return match ? `tinycloud:pkh:eip155:${match[1]}:${match[2].toLowerCase()}:${match[3]}` : space;
|
|
7722
|
+
}
|
|
7723
|
+
function signedAuthorityFromSessionGrant(node, delegation, authorization, cid) {
|
|
7724
|
+
const proof = delegation.siweProof;
|
|
7725
|
+
if (proof === void 0 || proof === null || typeof proof !== "object" || typeof proof.siwe !== "string" || typeof proof.signature !== "string") {
|
|
7726
|
+
reject(
|
|
7727
|
+
"proof_missing",
|
|
7728
|
+
"Runtime delegation is a wallet-signed session grant stored without its signed SIWE proof, so its authority cannot be verified."
|
|
7729
|
+
);
|
|
7730
|
+
}
|
|
7731
|
+
if (typeof node.verifySessionGrant !== "function") {
|
|
7732
|
+
reject("unsupported", "This runtime cannot verify wallet-signed session grants.");
|
|
7733
|
+
}
|
|
7734
|
+
const owner = `did:pkh:eip155:${delegation.chainId}:${delegation.ownerAddress}`;
|
|
7735
|
+
if (typeof node.did !== "string" || !didPrincipalsMatch(owner, node.did)) {
|
|
7736
|
+
reject("owner_mismatch", "Runtime delegation is not signed by this session's owner.");
|
|
7737
|
+
}
|
|
7738
|
+
let verified;
|
|
7739
|
+
try {
|
|
7740
|
+
verified = node.verifySessionGrant({
|
|
7741
|
+
delegationHeader: { Authorization: authorization },
|
|
7742
|
+
delegationCid: cid,
|
|
7743
|
+
spaceId: delegation.spaceId,
|
|
7744
|
+
address: delegation.ownerAddress,
|
|
7745
|
+
chainId: delegation.chainId,
|
|
7746
|
+
siwe: proof.siwe,
|
|
7747
|
+
signature: proof.signature
|
|
7748
|
+
});
|
|
7749
|
+
} catch (error) {
|
|
7750
|
+
reject("proof_invalid", error instanceof Error ? error.message : String(error));
|
|
7751
|
+
}
|
|
7752
|
+
const space = comparableSpace(delegation.spaceId);
|
|
7753
|
+
const spaceOwner = PKH_SPACE.exec(delegation.spaceId);
|
|
7754
|
+
if (!spaceOwner || !didPrincipalsMatch(`did:pkh:eip155:${spaceOwner[1]}:${spaceOwner[2]}`, owner)) {
|
|
7755
|
+
reject("authority_invalid", "Runtime delegation space is not owned by its signer.");
|
|
7756
|
+
}
|
|
7757
|
+
const groups = /* @__PURE__ */ new Map();
|
|
7758
|
+
for (const entry of verified.recap) {
|
|
7759
|
+
const service = entry.service.startsWith("tinycloud.") ? entry.service : `tinycloud.${entry.service}`;
|
|
7760
|
+
const raw = entry.space === "encryption";
|
|
7761
|
+
if (raw) {
|
|
7762
|
+
if (service !== "tinycloud.encryption" || !entry.path.startsWith(RAW_ENCRYPTION_PREFIX)) {
|
|
7763
|
+
reject("authority_invalid", "Runtime delegation has an unsupported signed raw resource.");
|
|
7764
|
+
}
|
|
7765
|
+
const networkOwner = entry.path.slice(RAW_ENCRYPTION_PREFIX.length, entry.path.lastIndexOf(":"));
|
|
7766
|
+
if (!didPrincipalsMatch(networkOwner, owner)) {
|
|
7767
|
+
reject("authority_invalid", "Runtime delegation names an encryption network its signer does not own.");
|
|
7768
|
+
}
|
|
7769
|
+
} else if (service === "tinycloud.encryption" || comparableSpace(entry.space) !== space) {
|
|
7770
|
+
reject("authority_invalid", "Runtime delegation has signed authority outside its space.");
|
|
7771
|
+
}
|
|
7772
|
+
const caveats = signedCaveats(entry.caveats ?? []);
|
|
7773
|
+
const key = JSON.stringify([service, raw ? "" : entry.space, entry.path, (0, import_sdk_core9.canonicalizeRecapCaveats)(caveats)]);
|
|
7774
|
+
const group = groups.get(key) ?? {
|
|
7775
|
+
service,
|
|
7776
|
+
...raw ? {} : { space: entry.space },
|
|
7777
|
+
path: entry.path,
|
|
7778
|
+
actions: [],
|
|
7779
|
+
...caveats === void 0 ? {} : { caveats }
|
|
7780
|
+
};
|
|
7781
|
+
for (const action of entry.actions) {
|
|
7782
|
+
if (!group.actions.includes(action)) group.actions.push(action);
|
|
7783
|
+
}
|
|
7784
|
+
groups.set(key, group);
|
|
7785
|
+
}
|
|
7786
|
+
return canonicalSignedAuthority({
|
|
7787
|
+
issuer: owner,
|
|
7788
|
+
// The verifier bound the SIWE audience to this runtime's own session key.
|
|
7789
|
+
audience: node.sessionDid,
|
|
7790
|
+
expiry: verified.expiresAt,
|
|
7791
|
+
proofs: []
|
|
7792
|
+
}, [...groups.values()]);
|
|
7661
7793
|
}
|
|
7662
7794
|
function canonicalResourcesFromPortableDelegation(delegation) {
|
|
7663
7795
|
if (delegation.resources !== void 0 && delegation.resources.length > 0) {
|
|
@@ -7693,49 +7825,58 @@ function resourcesMatch(actual, expected) {
|
|
|
7693
7825
|
async function activateValidatedRuntimeDelegation(node, delegation, options) {
|
|
7694
7826
|
const host = normalizedHost(options.host);
|
|
7695
7827
|
if (delegation.host !== void 0 && normalizedHost(delegation.host) !== host) {
|
|
7696
|
-
|
|
7828
|
+
reject(
|
|
7829
|
+
"host_mismatch",
|
|
7697
7830
|
`Runtime delegation host '${delegation.host}' does not match expected host '${options.host}'.`
|
|
7698
7831
|
);
|
|
7699
7832
|
}
|
|
7700
7833
|
if (!(delegation.expiry instanceof Date) || Number.isNaN(delegation.expiry.getTime())) {
|
|
7701
|
-
|
|
7834
|
+
reject("authority_invalid", "Runtime delegation has an invalid expiry.");
|
|
7702
7835
|
}
|
|
7703
7836
|
if (delegation.expiry.getTime() <= Date.now()) {
|
|
7704
|
-
|
|
7837
|
+
reject("expired", "Runtime delegation is expired.");
|
|
7705
7838
|
}
|
|
7706
7839
|
const authorization = delegation.delegationHeader?.Authorization;
|
|
7707
7840
|
if (typeof authorization !== "string" || authorization.length === 0) {
|
|
7708
|
-
|
|
7841
|
+
reject("authority_invalid", "Runtime delegation is missing authorization bytes.");
|
|
7709
7842
|
}
|
|
7710
7843
|
const cid = node.computeDelegationCid(authorization);
|
|
7711
7844
|
if (cid !== delegation.cid) {
|
|
7712
|
-
|
|
7845
|
+
reject("cid_mismatch", "Runtime delegation CID does not match authorization bytes.");
|
|
7846
|
+
}
|
|
7847
|
+
const compact = authorizationWithoutBearer(authorization).split(".").length === 3;
|
|
7848
|
+
let signed;
|
|
7849
|
+
try {
|
|
7850
|
+
signed = compact ? signedAuthorityFromCompactUcan(authorization) : signedAuthorityFromSessionGrant(node, delegation, authorization, cid);
|
|
7851
|
+
} catch (error) {
|
|
7852
|
+
if (error instanceof RuntimeDelegationRejectedError) throw error;
|
|
7853
|
+
reject("authority_invalid", error instanceof Error ? error.message : String(error));
|
|
7713
7854
|
}
|
|
7714
|
-
const signed = signedAuthorityFromCompactUcan(authorization);
|
|
7715
7855
|
if (signed.expiry.getTime() <= Date.now()) {
|
|
7716
|
-
|
|
7856
|
+
reject("expired", "Runtime delegation is expired.");
|
|
7717
7857
|
}
|
|
7718
7858
|
if (delegation.expiry.getTime() !== signed.expiry.getTime()) {
|
|
7719
|
-
|
|
7859
|
+
reject("authority_invalid", "Runtime delegation expiry does not match signed authority.");
|
|
7720
7860
|
}
|
|
7721
7861
|
if (!didPrincipalsMatch(signed.audience, node.sessionDid)) {
|
|
7722
|
-
|
|
7862
|
+
reject(
|
|
7863
|
+
"audience_mismatch",
|
|
7723
7864
|
`Runtime delegation targets ${signed.audience} but this session is ${node.sessionDid}.`
|
|
7724
7865
|
);
|
|
7725
7866
|
}
|
|
7726
7867
|
if (!didPrincipalsMatch(delegation.delegateDID, signed.audience)) {
|
|
7727
|
-
|
|
7868
|
+
reject("audience_mismatch", "Runtime delegation audience does not match signed authority.");
|
|
7728
7869
|
}
|
|
7729
|
-
|
|
7730
|
-
|
|
7731
|
-
throw new Error("Runtime delegation resources do not match signed authority.");
|
|
7870
|
+
if (compact && !resourcesMatch(canonicalResourcesFromPortableDelegation(delegation), signed.resources)) {
|
|
7871
|
+
reject("resources_mismatch", "Runtime delegation resources do not match signed authority.");
|
|
7732
7872
|
}
|
|
7733
7873
|
if (options.authorize !== void 0 && !options.authorize(signed.permissions)) {
|
|
7734
7874
|
throw new Error("Runtime delegation is not authorized for this runtime.");
|
|
7735
7875
|
}
|
|
7736
7876
|
const previouslyInstalled = node.getRuntimePermissionDelegations().find((candidate) => candidate.cid === cid);
|
|
7737
7877
|
if (previouslyInstalled !== void 0 && previouslyInstalled.delegationHeader.Authorization !== authorization) {
|
|
7738
|
-
|
|
7878
|
+
reject(
|
|
7879
|
+
"cid_mismatch",
|
|
7739
7880
|
"A different authorization is already installed for this runtime delegation CID."
|
|
7740
7881
|
);
|
|
7741
7882
|
}
|
|
@@ -7765,22 +7906,28 @@ async function activateValidatedRuntimeDelegation(node, delegation, options) {
|
|
|
7765
7906
|
})),
|
|
7766
7907
|
expiry: new Date(signed.expiry),
|
|
7767
7908
|
delegateDID: signed.audience,
|
|
7768
|
-
host
|
|
7909
|
+
host,
|
|
7910
|
+
...compact ? {} : { siweProof: delegation.siweProof }
|
|
7769
7911
|
};
|
|
7770
|
-
|
|
7771
|
-
|
|
7772
|
-
|
|
7773
|
-
|
|
7912
|
+
try {
|
|
7913
|
+
if (node.isSessionOnly === true && typeof node.did === "string" && (0, import_sdk_core9.principalDidEquals)(node.did, node.sessionDid) && typeof node.useDelegation === "function") {
|
|
7914
|
+
await node.useDelegation(installedCandidate);
|
|
7915
|
+
} else {
|
|
7916
|
+
await node.useRuntimeDelegation(installedCandidate);
|
|
7917
|
+
}
|
|
7918
|
+
} catch (error) {
|
|
7919
|
+
reject("activation_rejected", error instanceof Error ? error.message : String(error));
|
|
7774
7920
|
}
|
|
7775
7921
|
const installed = node.getRuntimePermissionDelegations().find((candidate) => candidate.cid === cid);
|
|
7776
7922
|
if (!installed) {
|
|
7777
|
-
|
|
7923
|
+
reject("activation_rejected", "Runtime delegation activation did not install the validated authority.");
|
|
7778
7924
|
}
|
|
7779
7925
|
if (installed.delegationHeader.Authorization !== authorization || !didPrincipalsMatch(installed.delegateDID, signed.audience) || installed.expiry.getTime() !== signed.expiry.getTime() || !resourcesMatch(
|
|
7780
7926
|
canonicalResourcesFromPortableDelegation(installed),
|
|
7781
7927
|
signed.resources
|
|
7782
7928
|
) || normalizedHost(installed.host ?? "") !== host) {
|
|
7783
|
-
|
|
7929
|
+
reject(
|
|
7930
|
+
"activation_rejected",
|
|
7784
7931
|
"Runtime delegation activation installed authority that differs from the validated authority."
|
|
7785
7932
|
);
|
|
7786
7933
|
}
|