@tinycloud/sdk-core 2.2.0-beta.1 → 2.2.0-beta.11
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/index.cjs +775 -57
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +204 -20
- package/dist/index.d.ts +204 -20
- package/dist/index.js +732 -33
- package/dist/index.js.map +1 -1
- package/package.json +9 -3
package/dist/index.cjs
CHANGED
|
@@ -36,30 +36,37 @@ __export(index_exports, {
|
|
|
36
36
|
CapabilityKeyRegistry: () => CapabilityKeyRegistry,
|
|
37
37
|
CapabilityKeyRegistryErrorCodes: () => CapabilityKeyRegistryErrorCodes,
|
|
38
38
|
ClientSessionSchema: () => ClientSessionSchema,
|
|
39
|
+
CloudLocationResolutionError: () => CloudLocationResolutionError,
|
|
39
40
|
DEFAULT_DEFAULTS: () => DEFAULT_DEFAULTS,
|
|
40
41
|
DEFAULT_EXPIRY: () => DEFAULT_EXPIRY,
|
|
41
42
|
DEFAULT_MANIFEST_SPACE: () => DEFAULT_MANIFEST_SPACE,
|
|
42
43
|
DEFAULT_MANIFEST_VERSION: () => DEFAULT_MANIFEST_VERSION,
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
DEFAULT_TINYCLOUD_FALLBACK_HOST: () => DEFAULT_TINYCLOUD_FALLBACK_HOST,
|
|
45
|
+
DEFAULT_TINYCLOUD_LOCATION_REGISTRY_URL: () => DEFAULT_TINYCLOUD_LOCATION_REGISTRY_URL,
|
|
46
|
+
DataVaultService: () => import_sdk_services5.DataVaultService,
|
|
47
|
+
DatabaseHandle: () => import_sdk_services5.DatabaseHandle,
|
|
45
48
|
DelegationErrorCodes: () => DelegationErrorCodes,
|
|
46
49
|
DelegationManager: () => DelegationManager,
|
|
47
|
-
DuckDbAction: () =>
|
|
48
|
-
DuckDbDatabaseHandle: () =>
|
|
49
|
-
DuckDbService: () =>
|
|
50
|
+
DuckDbAction: () => import_sdk_services5.DuckDbAction,
|
|
51
|
+
DuckDbDatabaseHandle: () => import_sdk_services5.DuckDbDatabaseHandle,
|
|
52
|
+
DuckDbService: () => import_sdk_services5.DuckDbService,
|
|
53
|
+
EXPIRY: () => EXPIRY,
|
|
50
54
|
EnsDataSchema: () => EnsDataSchema,
|
|
51
|
-
ErrorCodes: () =>
|
|
52
|
-
HooksService: () =>
|
|
53
|
-
KVService: () =>
|
|
55
|
+
ErrorCodes: () => import_sdk_services5.ErrorCodes,
|
|
56
|
+
HooksService: () => import_sdk_services5.HooksService,
|
|
57
|
+
KVService: () => import_sdk_services5.KVService,
|
|
58
|
+
LocationRecordValidationError: () => LocationRecordValidationError,
|
|
54
59
|
ManifestValidationError: () => ManifestValidationError,
|
|
55
60
|
PermissionNotInManifestError: () => PermissionNotInManifestError,
|
|
56
|
-
PrefixedKVService: () =>
|
|
61
|
+
PrefixedKVService: () => import_sdk_services5.PrefixedKVService,
|
|
57
62
|
ProtocolMismatchError: () => ProtocolMismatchError,
|
|
63
|
+
SECRET_NAME_RE: () => import_sdk_services5.SECRET_NAME_RE,
|
|
58
64
|
SERVICE_LONG_TO_SHORT: () => SERVICE_LONG_TO_SHORT,
|
|
59
65
|
SERVICE_SHORT_TO_LONG: () => SERVICE_SHORT_TO_LONG,
|
|
60
|
-
SQLAction: () =>
|
|
61
|
-
SQLService: () =>
|
|
62
|
-
|
|
66
|
+
SQLAction: () => import_sdk_services5.SQLAction,
|
|
67
|
+
SQLService: () => import_sdk_services5.SQLService,
|
|
68
|
+
SecretsService: () => import_sdk_services5.SecretsService,
|
|
69
|
+
ServiceContext: () => import_sdk_services5.ServiceContext,
|
|
63
70
|
SessionExpiredError: () => SessionExpiredError,
|
|
64
71
|
SharingService: () => SharingService,
|
|
65
72
|
SilentNotificationHandler: () => SilentNotificationHandler,
|
|
@@ -70,41 +77,57 @@ __export(index_exports, {
|
|
|
70
77
|
SpaceService: () => SpaceService,
|
|
71
78
|
TinyCloud: () => TinyCloud,
|
|
72
79
|
UnsupportedFeatureError: () => UnsupportedFeatureError,
|
|
73
|
-
|
|
74
|
-
|
|
80
|
+
VAULT_PERMISSION_SERVICE: () => VAULT_PERMISSION_SERVICE,
|
|
81
|
+
VaultHeaders: () => import_sdk_services5.VaultHeaders,
|
|
82
|
+
VaultPublicSpaceKVActions: () => import_sdk_services5.VaultPublicSpaceKVActions,
|
|
75
83
|
VersionCheckError: () => VersionCheckError,
|
|
76
84
|
activateSessionWithHost: () => activateSessionWithHost,
|
|
77
85
|
applyPrefix: () => applyPrefix,
|
|
78
86
|
buildSpaceUri: () => buildSpaceUri,
|
|
87
|
+
canonicalLocationPayload: () => canonicalLocationPayload,
|
|
88
|
+
canonicalizeSecretScope: () => import_sdk_services5.canonicalizeSecretScope,
|
|
79
89
|
checkNodeInfo: () => checkNodeInfo,
|
|
80
90
|
composeManifestRequest: () => composeManifestRequest,
|
|
81
91
|
createCapabilityKeyRegistry: () => createCapabilityKeyRegistry,
|
|
82
92
|
createSharingService: () => createSharingService,
|
|
83
93
|
createSpaceService: () => createSpaceService,
|
|
84
|
-
createVaultCrypto: () =>
|
|
85
|
-
defaultRetryPolicy: () =>
|
|
94
|
+
createVaultCrypto: () => import_sdk_services5.createVaultCrypto,
|
|
95
|
+
defaultRetryPolicy: () => import_sdk_services5.defaultRetryPolicy,
|
|
86
96
|
defaultSignStrategy: () => defaultSignStrategy,
|
|
87
97
|
defaultSpaceCreationHandler: () => defaultSpaceCreationHandler,
|
|
88
|
-
err: () =>
|
|
98
|
+
err: () => import_sdk_services5.err,
|
|
89
99
|
expandActionShortNames: () => expandActionShortNames,
|
|
100
|
+
expandPermissionEntries: () => expandPermissionEntries,
|
|
101
|
+
expandPermissionEntry: () => expandPermissionEntry,
|
|
102
|
+
fetchLocationRecord: () => fetchLocationRecord,
|
|
90
103
|
fetchPeerId: () => fetchPeerId,
|
|
104
|
+
httpUrlToMultiaddr: () => httpUrlToMultiaddr,
|
|
91
105
|
isCapabilitySubset: () => isCapabilitySubset,
|
|
92
106
|
loadManifest: () => loadManifest,
|
|
107
|
+
locationPayloadForRecord: () => locationPayloadForRecord,
|
|
93
108
|
makePublicSpaceId: () => makePublicSpaceId,
|
|
94
109
|
manifestAbilitiesUnion: () => manifestAbilitiesUnion,
|
|
110
|
+
multiaddrToHttpUrl: () => multiaddrToHttpUrl,
|
|
95
111
|
normalizeDefaults: () => normalizeDefaults,
|
|
96
|
-
ok: () =>
|
|
112
|
+
ok: () => import_sdk_services5.ok,
|
|
97
113
|
parseExpiry: () => parseExpiry,
|
|
98
114
|
parseRecapCapabilities: () => parseRecapCapabilities,
|
|
99
115
|
parseSpaceUri: () => parseSpaceUri,
|
|
116
|
+
resolveCloudLocation: () => resolveCloudLocation,
|
|
100
117
|
resolveManifest: () => resolveManifest,
|
|
118
|
+
resolveSecretPath: () => import_sdk_services5.resolveSecretPath,
|
|
119
|
+
resolveTinyCloudHosts: () => resolveTinyCloudHosts,
|
|
101
120
|
resourceCapabilitiesToAbilitiesMap: () => resourceCapabilitiesToAbilitiesMap,
|
|
102
121
|
resourceCapabilitiesToSpaceAbilitiesMap: () => resourceCapabilitiesToSpaceAbilitiesMap,
|
|
103
|
-
serviceError: () =>
|
|
122
|
+
serviceError: () => import_sdk_services5.serviceError,
|
|
123
|
+
signLocationRecord: () => signLocationRecord,
|
|
104
124
|
submitHostDelegation: () => submitHostDelegation,
|
|
105
125
|
validateClientSession: () => validateClientSession,
|
|
126
|
+
validateLocationRecord: () => validateLocationRecord,
|
|
127
|
+
validateLocationRecordPayload: () => validateLocationRecordPayload,
|
|
106
128
|
validateManifest: () => validateManifest,
|
|
107
|
-
validatePersistedSessionData: () => validatePersistedSessionData
|
|
129
|
+
validatePersistedSessionData: () => validatePersistedSessionData,
|
|
130
|
+
verifyLocationRecord: () => verifyLocationRecord
|
|
108
131
|
});
|
|
109
132
|
module.exports = __toCommonJS(index_exports);
|
|
110
133
|
|
|
@@ -254,6 +277,7 @@ var Space = class {
|
|
|
254
277
|
this._id = config.id;
|
|
255
278
|
this._name = config.name;
|
|
256
279
|
this._kv = config.createKV(config.id);
|
|
280
|
+
this._vault = config.createVault(config.id);
|
|
257
281
|
this._delegations = config.createDelegations(config.id);
|
|
258
282
|
this._sharing = config.createSharing(config.id);
|
|
259
283
|
this._getInfo = config.getInfo;
|
|
@@ -276,6 +300,12 @@ var Space = class {
|
|
|
276
300
|
get kv() {
|
|
277
301
|
return this._kv;
|
|
278
302
|
}
|
|
303
|
+
/**
|
|
304
|
+
* Data Vault operations scoped to this space.
|
|
305
|
+
*/
|
|
306
|
+
get vault() {
|
|
307
|
+
return this._vault;
|
|
308
|
+
}
|
|
279
309
|
/**
|
|
280
310
|
* Delegation operations scoped to this space.
|
|
281
311
|
*/
|
|
@@ -659,6 +689,8 @@ var SpaceConfigSchema = import_zod4.z.object({
|
|
|
659
689
|
name: import_zod4.z.string(),
|
|
660
690
|
/** Factory function to create a space-scoped KV service */
|
|
661
691
|
createKV: import_zod4.z.function(),
|
|
692
|
+
/** Factory function to create a space-scoped Data Vault service */
|
|
693
|
+
createVault: import_zod4.z.function(),
|
|
662
694
|
/** Factory function to create space-scoped delegations */
|
|
663
695
|
createDelegations: import_zod4.z.function(),
|
|
664
696
|
/** Factory function to create space-scoped sharing */
|
|
@@ -679,6 +711,8 @@ var SpaceServiceConfigSchema = import_zod4.z.object({
|
|
|
679
711
|
capabilityRegistry: import_zod4.z.unknown().optional(),
|
|
680
712
|
/** Factory function to create a space-scoped KV service */
|
|
681
713
|
createKVService: import_zod4.z.function().optional(),
|
|
714
|
+
/** Factory function to create a space-scoped Data Vault service */
|
|
715
|
+
createVaultService: import_zod4.z.function().optional(),
|
|
682
716
|
/** User's PKH DID (derived from address or provided explicitly) */
|
|
683
717
|
userDid: import_zod4.z.string().optional(),
|
|
684
718
|
/** Optional SharingService for v2 sharing links (client-side) */
|
|
@@ -817,6 +851,20 @@ function validateServerSpaceInfoResponse(data) {
|
|
|
817
851
|
return { ok: true, data: result.data };
|
|
818
852
|
}
|
|
819
853
|
|
|
854
|
+
// src/expiry.ts
|
|
855
|
+
var EPHEMERAL_MS = 60 * 60 * 1e3;
|
|
856
|
+
var SESSION_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
857
|
+
var SHARE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
858
|
+
var APP_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
859
|
+
var MAX_MS = 10 * 365 * 24 * 60 * 60 * 1e3;
|
|
860
|
+
var EXPIRY = {
|
|
861
|
+
EPHEMERAL_MS,
|
|
862
|
+
SESSION_MS,
|
|
863
|
+
SHARE_MS,
|
|
864
|
+
APP_MS,
|
|
865
|
+
MAX_MS
|
|
866
|
+
};
|
|
867
|
+
|
|
820
868
|
// src/spaces/SpaceService.ts
|
|
821
869
|
var SERVICE_NAME = "space";
|
|
822
870
|
var SpaceErrorCodes = {
|
|
@@ -893,7 +941,7 @@ function transformServerDelegations(validatedData, defaultSpaceId) {
|
|
|
893
941
|
spaceId,
|
|
894
942
|
path,
|
|
895
943
|
actions,
|
|
896
|
-
expiry: info.expiry ? new Date(info.expiry) : new Date(Date.now() +
|
|
944
|
+
expiry: info.expiry ? new Date(info.expiry) : new Date(Date.now() + EXPIRY.SHARE_MS),
|
|
897
945
|
isRevoked: false,
|
|
898
946
|
createdAt: info.issued_at ? new Date(info.issued_at) : void 0,
|
|
899
947
|
parentCid: firstStringParent
|
|
@@ -920,6 +968,7 @@ var SpaceService = class {
|
|
|
920
968
|
this.fetchFn = config.fetch ?? globalThis.fetch.bind(globalThis);
|
|
921
969
|
this.capabilityRegistry = config.capabilityRegistry;
|
|
922
970
|
this.createKVServiceFn = config.createKVService;
|
|
971
|
+
this.createVaultServiceFn = config.createVaultService;
|
|
923
972
|
this._userDid = config.userDid;
|
|
924
973
|
this.sharingService = config.sharingService;
|
|
925
974
|
this.createDelegationFn = config.createDelegation;
|
|
@@ -934,6 +983,7 @@ var SpaceService = class {
|
|
|
934
983
|
if (config.fetch) this.fetchFn = config.fetch;
|
|
935
984
|
if (config.capabilityRegistry) this.capabilityRegistry = config.capabilityRegistry;
|
|
936
985
|
if (config.createKVService) this.createKVServiceFn = config.createKVService;
|
|
986
|
+
if (config.createVaultService) this.createVaultServiceFn = config.createVaultService;
|
|
937
987
|
if (config.userDid !== void 0) this._userDid = config.userDid;
|
|
938
988
|
if (config.sharingService) this.sharingService = config.sharingService;
|
|
939
989
|
if (config.createDelegation) this.createDelegationFn = config.createDelegation;
|
|
@@ -1216,6 +1266,7 @@ var SpaceService = class {
|
|
|
1216
1266
|
id: spaceId,
|
|
1217
1267
|
name,
|
|
1218
1268
|
createKV: this.createSpaceScopedKV.bind(this),
|
|
1269
|
+
createVault: this.createSpaceScopedVault.bind(this),
|
|
1219
1270
|
createDelegations: this.createSpaceScopedDelegations.bind(this),
|
|
1220
1271
|
createSharing: this.createSpaceScopedSharing.bind(this),
|
|
1221
1272
|
getInfo: this.getSpaceInfo.bind(this)
|
|
@@ -1345,6 +1396,21 @@ var SpaceService = class {
|
|
|
1345
1396
|
}
|
|
1346
1397
|
});
|
|
1347
1398
|
}
|
|
1399
|
+
/**
|
|
1400
|
+
* Create a space-scoped Data Vault service.
|
|
1401
|
+
*/
|
|
1402
|
+
createSpaceScopedVault(spaceId) {
|
|
1403
|
+
if (this.createVaultServiceFn) {
|
|
1404
|
+
return this.createVaultServiceFn(spaceId);
|
|
1405
|
+
}
|
|
1406
|
+
return new Proxy({}, {
|
|
1407
|
+
get: () => {
|
|
1408
|
+
throw new Error(
|
|
1409
|
+
"Vault service factory not configured. Provide createVaultService in SpaceServiceConfig."
|
|
1410
|
+
);
|
|
1411
|
+
}
|
|
1412
|
+
});
|
|
1413
|
+
}
|
|
1348
1414
|
/**
|
|
1349
1415
|
* Create space-scoped delegation operations.
|
|
1350
1416
|
*/
|
|
@@ -2070,7 +2136,7 @@ var TinyCloud = class _TinyCloud {
|
|
|
2070
2136
|
};
|
|
2071
2137
|
|
|
2072
2138
|
// src/index.ts
|
|
2073
|
-
var
|
|
2139
|
+
var import_sdk_services5 = require("@tinycloud/sdk-services");
|
|
2074
2140
|
|
|
2075
2141
|
// src/space.ts
|
|
2076
2142
|
async function fetchPeerId(host, spaceId) {
|
|
@@ -2259,7 +2325,7 @@ var DelegationManager = class {
|
|
|
2259
2325
|
spaceId: this.session.spaceId,
|
|
2260
2326
|
path: params.path,
|
|
2261
2327
|
actions: params.actions,
|
|
2262
|
-
expiry: params.expiry ?? new Date(Date.now() +
|
|
2328
|
+
expiry: params.expiry ?? new Date(Date.now() + EXPIRY.SHARE_MS),
|
|
2263
2329
|
isRevoked: false,
|
|
2264
2330
|
allowSubDelegation: !(params.disableSubDelegation ?? false),
|
|
2265
2331
|
createdAt: /* @__PURE__ */ new Date()
|
|
@@ -2738,6 +2804,7 @@ function validateEncodedShareData(data) {
|
|
|
2738
2804
|
|
|
2739
2805
|
// src/manifest.ts
|
|
2740
2806
|
var import_ms = __toESM(require("ms"), 1);
|
|
2807
|
+
var import_sdk_services3 = require("@tinycloud/sdk-services");
|
|
2741
2808
|
var ManifestValidationError = class extends Error {
|
|
2742
2809
|
constructor(message) {
|
|
2743
2810
|
super(`Manifest validation failed: ${message}`);
|
|
@@ -2750,6 +2817,8 @@ var DEFAULT_MANIFEST_VERSION = 1;
|
|
|
2750
2817
|
var DEFAULT_MANIFEST_SPACE = "applications";
|
|
2751
2818
|
var ACCOUNT_REGISTRY_SPACE = "account";
|
|
2752
2819
|
var ACCOUNT_REGISTRY_PATH = "applications/";
|
|
2820
|
+
var SECRETS_SPACE = "secrets";
|
|
2821
|
+
var VAULT_PERMISSION_SERVICE = "tinycloud.vault";
|
|
2753
2822
|
var SERVICE_SHORT_TO_LONG = Object.freeze({
|
|
2754
2823
|
kv: "tinycloud.kv",
|
|
2755
2824
|
sql: "tinycloud.sql",
|
|
@@ -2774,12 +2843,6 @@ var DEFAULT_STANDARD_ENTRIES = [
|
|
|
2774
2843
|
space: DEFAULT_MANIFEST_SPACE,
|
|
2775
2844
|
path: "/",
|
|
2776
2845
|
actions: ["read", "write"]
|
|
2777
|
-
},
|
|
2778
|
-
{
|
|
2779
|
-
service: "tinycloud.capabilities",
|
|
2780
|
-
space: DEFAULT_MANIFEST_SPACE,
|
|
2781
|
-
path: "/",
|
|
2782
|
-
actions: ["read"]
|
|
2783
2846
|
}
|
|
2784
2847
|
];
|
|
2785
2848
|
var DEFAULT_ADMIN_ENTRIES = [
|
|
@@ -2794,12 +2857,6 @@ var DEFAULT_ADMIN_ENTRIES = [
|
|
|
2794
2857
|
space: DEFAULT_MANIFEST_SPACE,
|
|
2795
2858
|
path: "/",
|
|
2796
2859
|
actions: ["read", "write", "ddl"]
|
|
2797
|
-
},
|
|
2798
|
-
{
|
|
2799
|
-
service: "tinycloud.capabilities",
|
|
2800
|
-
space: DEFAULT_MANIFEST_SPACE,
|
|
2801
|
-
path: "/",
|
|
2802
|
-
actions: ["read", "admin"]
|
|
2803
2860
|
}
|
|
2804
2861
|
];
|
|
2805
2862
|
var DEFAULT_ALL_ENTRIES = [
|
|
@@ -2820,12 +2877,6 @@ var DEFAULT_ALL_ENTRIES = [
|
|
|
2820
2877
|
space: DEFAULT_MANIFEST_SPACE,
|
|
2821
2878
|
path: "/",
|
|
2822
2879
|
actions: ["read", "write"]
|
|
2823
|
-
},
|
|
2824
|
-
{
|
|
2825
|
-
service: "tinycloud.capabilities",
|
|
2826
|
-
space: DEFAULT_MANIFEST_SPACE,
|
|
2827
|
-
path: "/",
|
|
2828
|
-
actions: ["read", "admin"]
|
|
2829
2880
|
}
|
|
2830
2881
|
];
|
|
2831
2882
|
function parseExpiry(duration) {
|
|
@@ -2850,6 +2901,20 @@ function expandActionShortNames(service, actions) {
|
|
|
2850
2901
|
return `${service}/${a}`;
|
|
2851
2902
|
});
|
|
2852
2903
|
}
|
|
2904
|
+
function expandPermissionEntry(entry) {
|
|
2905
|
+
if (entry.service !== VAULT_PERMISSION_SERVICE) {
|
|
2906
|
+
return [
|
|
2907
|
+
{
|
|
2908
|
+
...entry,
|
|
2909
|
+
actions: expandActionShortNames(entry.service, entry.actions)
|
|
2910
|
+
}
|
|
2911
|
+
];
|
|
2912
|
+
}
|
|
2913
|
+
return expandVaultPermissionEntry(entry);
|
|
2914
|
+
}
|
|
2915
|
+
function expandPermissionEntries(entries) {
|
|
2916
|
+
return entries.flatMap(expandPermissionEntry);
|
|
2917
|
+
}
|
|
2853
2918
|
function applyPrefix(prefix, path, skipPrefix) {
|
|
2854
2919
|
if (skipPrefix) {
|
|
2855
2920
|
return path;
|
|
@@ -2921,8 +2986,49 @@ function validateManifest(input) {
|
|
|
2921
2986
|
(p, i) => validatePermissionEntry(p, `permissions[${i}]`)
|
|
2922
2987
|
);
|
|
2923
2988
|
}
|
|
2989
|
+
if (m.secrets !== void 0) {
|
|
2990
|
+
validateManifestSecrets(m.secrets);
|
|
2991
|
+
}
|
|
2924
2992
|
return m;
|
|
2925
2993
|
}
|
|
2994
|
+
function validateManifestSecrets(secrets) {
|
|
2995
|
+
if (secrets === null || typeof secrets !== "object" || Array.isArray(secrets)) {
|
|
2996
|
+
throw new ManifestValidationError("manifest.secrets must be an object");
|
|
2997
|
+
}
|
|
2998
|
+
for (const [name, spec] of Object.entries(secrets)) {
|
|
2999
|
+
if (!import_sdk_services3.SECRET_NAME_RE.test(name)) {
|
|
3000
|
+
throw new ManifestValidationError(
|
|
3001
|
+
`manifest.secrets.${name} must match ${import_sdk_services3.SECRET_NAME_RE.source}`
|
|
3002
|
+
);
|
|
3003
|
+
}
|
|
3004
|
+
try {
|
|
3005
|
+
(0, import_sdk_services3.resolveSecretPath)(
|
|
3006
|
+
secretNameFromSpec(name, spec),
|
|
3007
|
+
{ scope: secretScopeFromSpec(spec) }
|
|
3008
|
+
);
|
|
3009
|
+
} catch (error) {
|
|
3010
|
+
throw new ManifestValidationError(
|
|
3011
|
+
`manifest.secrets.${name}: ${error instanceof Error ? error.message : String(error)}`
|
|
3012
|
+
);
|
|
3013
|
+
}
|
|
3014
|
+
const actions = secretActionsFromSpec(name, spec);
|
|
3015
|
+
if (actions.length === 0) {
|
|
3016
|
+
throw new ManifestValidationError(
|
|
3017
|
+
`manifest.secrets.${name} actions must be non-empty`
|
|
3018
|
+
);
|
|
3019
|
+
}
|
|
3020
|
+
for (const action of actions) {
|
|
3021
|
+
if (typeof action !== "string" || action.length === 0) {
|
|
3022
|
+
throw new ManifestValidationError(
|
|
3023
|
+
`manifest.secrets.${name} actions must be non-empty strings`
|
|
3024
|
+
);
|
|
3025
|
+
}
|
|
3026
|
+
}
|
|
3027
|
+
if (spec !== null && typeof spec === "object" && !Array.isArray(spec) && spec.expiry !== void 0) {
|
|
3028
|
+
parseExpiry(spec.expiry);
|
|
3029
|
+
}
|
|
3030
|
+
}
|
|
3031
|
+
}
|
|
2926
3032
|
function validatePermissionEntry(p, path) {
|
|
2927
3033
|
if (p === null || typeof p !== "object") {
|
|
2928
3034
|
throw new ManifestValidationError(`${path} must be an object`);
|
|
@@ -2946,6 +3052,16 @@ function validatePermissionEntry(p, path) {
|
|
|
2946
3052
|
`${path}.actions must be a non-empty array`
|
|
2947
3053
|
);
|
|
2948
3054
|
}
|
|
3055
|
+
for (const action of entry.actions) {
|
|
3056
|
+
if (typeof action !== "string" || action.length === 0) {
|
|
3057
|
+
throw new ManifestValidationError(
|
|
3058
|
+
`${path}.actions must contain non-empty strings`
|
|
3059
|
+
);
|
|
3060
|
+
}
|
|
3061
|
+
if (entry.service === VAULT_PERMISSION_SERVICE) {
|
|
3062
|
+
vaultActionExpansion(action);
|
|
3063
|
+
}
|
|
3064
|
+
}
|
|
2949
3065
|
if (entry.expiry !== void 0) {
|
|
2950
3066
|
parseExpiry(entry.expiry);
|
|
2951
3067
|
}
|
|
@@ -2975,7 +3091,8 @@ function defaultEntriesForTier(tier) {
|
|
|
2975
3091
|
service: e.service,
|
|
2976
3092
|
space: e.space,
|
|
2977
3093
|
path: e.path,
|
|
2978
|
-
actions: [...e.actions]
|
|
3094
|
+
actions: [...e.actions],
|
|
3095
|
+
...e.skipPrefix !== void 0 ? { skipPrefix: e.skipPrefix } : {}
|
|
2979
3096
|
}));
|
|
2980
3097
|
}
|
|
2981
3098
|
function resolveManifest(input) {
|
|
@@ -2987,9 +3104,14 @@ function resolveManifest(input) {
|
|
|
2987
3104
|
const tier = normalizeDefaults(manifest.defaults);
|
|
2988
3105
|
const defaultEntries = defaultEntriesForTier(tier);
|
|
2989
3106
|
const explicitEntries = manifest.permissions ?? [];
|
|
2990
|
-
const
|
|
2991
|
-
const
|
|
2992
|
-
|
|
3107
|
+
const secretEntries = secretEntriesForManifest(manifest.secrets);
|
|
3108
|
+
const allEntries = [
|
|
3109
|
+
...defaultEntries,
|
|
3110
|
+
...explicitEntries,
|
|
3111
|
+
...secretEntries
|
|
3112
|
+
];
|
|
3113
|
+
const resources = withCapabilitiesReadForSpaces(
|
|
3114
|
+
allEntries.flatMap((entry) => resolveEntry(entry, prefix, expiryMs, space))
|
|
2993
3115
|
);
|
|
2994
3116
|
const additionalDelegates = manifest.did === void 0 ? [] : [
|
|
2995
3117
|
{
|
|
@@ -3009,25 +3131,191 @@ function resolveManifest(input) {
|
|
|
3009
3131
|
additionalDelegates
|
|
3010
3132
|
};
|
|
3011
3133
|
}
|
|
3134
|
+
function normalizeSecretActions(actions) {
|
|
3135
|
+
const out = [];
|
|
3136
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3137
|
+
const add = (action) => {
|
|
3138
|
+
if (!seen.has(action)) {
|
|
3139
|
+
out.push(action);
|
|
3140
|
+
seen.add(action);
|
|
3141
|
+
}
|
|
3142
|
+
};
|
|
3143
|
+
for (const action of actions) {
|
|
3144
|
+
if (action === "read") {
|
|
3145
|
+
add("get");
|
|
3146
|
+
continue;
|
|
3147
|
+
}
|
|
3148
|
+
if (action === "write") {
|
|
3149
|
+
add("put");
|
|
3150
|
+
continue;
|
|
3151
|
+
}
|
|
3152
|
+
if (action === "delete") {
|
|
3153
|
+
add("del");
|
|
3154
|
+
continue;
|
|
3155
|
+
}
|
|
3156
|
+
if (action === "get" || action === "put" || action === "del" || action === "list" || action === "metadata") {
|
|
3157
|
+
add(action);
|
|
3158
|
+
continue;
|
|
3159
|
+
}
|
|
3160
|
+
if (action === "tinycloud.kv/get" || action === "tinycloud.kv/put" || action === "tinycloud.kv/del" || action === "tinycloud.kv/list" || action === "tinycloud.kv/metadata") {
|
|
3161
|
+
add(action);
|
|
3162
|
+
continue;
|
|
3163
|
+
}
|
|
3164
|
+
throw new ManifestValidationError(
|
|
3165
|
+
`unknown secret action ${JSON.stringify(action)}; expected read, write, delete, list, or metadata`
|
|
3166
|
+
);
|
|
3167
|
+
}
|
|
3168
|
+
return out;
|
|
3169
|
+
}
|
|
3170
|
+
function secretNameFromSpec(fallbackName, spec) {
|
|
3171
|
+
if (spec !== null && typeof spec === "object" && !Array.isArray(spec)) {
|
|
3172
|
+
return spec.name ?? fallbackName;
|
|
3173
|
+
}
|
|
3174
|
+
return fallbackName;
|
|
3175
|
+
}
|
|
3176
|
+
function secretScopeFromSpec(spec) {
|
|
3177
|
+
if (spec !== null && typeof spec === "object" && !Array.isArray(spec)) {
|
|
3178
|
+
return spec.scope;
|
|
3179
|
+
}
|
|
3180
|
+
return void 0;
|
|
3181
|
+
}
|
|
3182
|
+
function secretActionsFromSpec(name, spec) {
|
|
3183
|
+
if (spec === true) {
|
|
3184
|
+
return ["read"];
|
|
3185
|
+
}
|
|
3186
|
+
if (typeof spec === "string") {
|
|
3187
|
+
return [spec];
|
|
3188
|
+
}
|
|
3189
|
+
if (Array.isArray(spec)) {
|
|
3190
|
+
return spec;
|
|
3191
|
+
}
|
|
3192
|
+
if (spec === null || typeof spec !== "object") {
|
|
3193
|
+
throw new ManifestValidationError(
|
|
3194
|
+
`manifest.secrets.${name} must be true, a string action, an actions array, or an object`
|
|
3195
|
+
);
|
|
3196
|
+
}
|
|
3197
|
+
if (spec.actions === void 0) {
|
|
3198
|
+
return ["read"];
|
|
3199
|
+
}
|
|
3200
|
+
if (typeof spec.actions === "string") {
|
|
3201
|
+
return [spec.actions];
|
|
3202
|
+
}
|
|
3203
|
+
if (Array.isArray(spec.actions)) {
|
|
3204
|
+
return spec.actions;
|
|
3205
|
+
}
|
|
3206
|
+
throw new ManifestValidationError(
|
|
3207
|
+
`manifest.secrets.${name}.actions must be a string or array`
|
|
3208
|
+
);
|
|
3209
|
+
}
|
|
3210
|
+
function secretEntriesForManifest(secrets) {
|
|
3211
|
+
if (secrets === void 0) {
|
|
3212
|
+
return [];
|
|
3213
|
+
}
|
|
3214
|
+
const entries = [];
|
|
3215
|
+
for (const [name, spec] of Object.entries(secrets)) {
|
|
3216
|
+
const actions = secretActionsFromSpec(name, spec);
|
|
3217
|
+
const secretPath = (0, import_sdk_services3.resolveSecretPath)(
|
|
3218
|
+
secretNameFromSpec(name, spec),
|
|
3219
|
+
{ scope: secretScopeFromSpec(spec) }
|
|
3220
|
+
);
|
|
3221
|
+
const extra = spec !== true && typeof spec === "object" && !Array.isArray(spec) ? spec : {};
|
|
3222
|
+
entries.push({
|
|
3223
|
+
service: VAULT_PERMISSION_SERVICE,
|
|
3224
|
+
space: SECRETS_SPACE,
|
|
3225
|
+
path: secretPath.vaultKey,
|
|
3226
|
+
actions: normalizeSecretActions(actions),
|
|
3227
|
+
skipPrefix: true,
|
|
3228
|
+
...extra.expiry !== void 0 ? { expiry: extra.expiry } : {},
|
|
3229
|
+
...extra.description !== void 0 ? { description: extra.description } : {}
|
|
3230
|
+
});
|
|
3231
|
+
}
|
|
3232
|
+
return entries;
|
|
3233
|
+
}
|
|
3012
3234
|
function resolveEntry(entry, prefix, _inheritedExpiryMs, inheritedSpace) {
|
|
3013
3235
|
const resolvedPath = applyPrefix(
|
|
3014
3236
|
prefix,
|
|
3015
3237
|
entry.path,
|
|
3016
3238
|
entry.skipPrefix === true
|
|
3017
3239
|
);
|
|
3018
|
-
const resolvedActions = expandActionShortNames(entry.service, entry.actions);
|
|
3019
3240
|
const entryExpiryMs = entry.expiry !== void 0 ? parseExpiry(entry.expiry) : void 0;
|
|
3020
|
-
return {
|
|
3021
|
-
|
|
3241
|
+
return expandPermissionEntry({
|
|
3242
|
+
...entry,
|
|
3022
3243
|
space: entry.space ?? inheritedSpace,
|
|
3023
3244
|
path: resolvedPath,
|
|
3024
|
-
|
|
3245
|
+
skipPrefix: true
|
|
3246
|
+
}).map((expanded) => ({
|
|
3247
|
+
service: expanded.service,
|
|
3248
|
+
space: expanded.space ?? inheritedSpace,
|
|
3249
|
+
path: expanded.path,
|
|
3250
|
+
actions: expanded.actions,
|
|
3025
3251
|
// Only populate `expiryMs` when the entry had its own expiry override.
|
|
3026
3252
|
// When absent, callers use the parent (delegation or manifest) expiry
|
|
3027
3253
|
// which is carried on ResolvedDelegate.expiryMs / ResolvedCapabilities.expiryMs.
|
|
3028
3254
|
...entryExpiryMs !== void 0 ? { expiryMs: entryExpiryMs } : {},
|
|
3029
3255
|
...entry.description !== void 0 ? { description: entry.description } : {}
|
|
3030
|
-
};
|
|
3256
|
+
}));
|
|
3257
|
+
}
|
|
3258
|
+
function expandVaultPermissionEntry(entry) {
|
|
3259
|
+
const byBase = /* @__PURE__ */ new Map();
|
|
3260
|
+
for (const action of entry.actions) {
|
|
3261
|
+
const expansion = vaultActionExpansion(action);
|
|
3262
|
+
for (const base of expansion.bases) {
|
|
3263
|
+
const actions = byBase.get(base) ?? [];
|
|
3264
|
+
if (!actions.includes(expansion.action)) {
|
|
3265
|
+
actions.push(expansion.action);
|
|
3266
|
+
}
|
|
3267
|
+
byBase.set(base, actions);
|
|
3268
|
+
}
|
|
3269
|
+
}
|
|
3270
|
+
return [...byBase.entries()].map(([base, actions]) => ({
|
|
3271
|
+
...entry,
|
|
3272
|
+
service: "tinycloud.kv",
|
|
3273
|
+
path: vaultKVPath(base, entry.path),
|
|
3274
|
+
actions,
|
|
3275
|
+
skipPrefix: true
|
|
3276
|
+
}));
|
|
3277
|
+
}
|
|
3278
|
+
function vaultActionExpansion(action) {
|
|
3279
|
+
const normalized = normalizeVaultAction(action);
|
|
3280
|
+
if (normalized === "read" || normalized === "get") {
|
|
3281
|
+
return { bases: ["keys", "vault"], action: "tinycloud.kv/get" };
|
|
3282
|
+
}
|
|
3283
|
+
if (normalized === "write" || normalized === "put") {
|
|
3284
|
+
return { bases: ["keys", "vault"], action: "tinycloud.kv/put" };
|
|
3285
|
+
}
|
|
3286
|
+
if (normalized === "delete" || normalized === "del") {
|
|
3287
|
+
return { bases: ["keys", "vault"], action: "tinycloud.kv/del" };
|
|
3288
|
+
}
|
|
3289
|
+
if (normalized === "list") {
|
|
3290
|
+
return { bases: ["vault"], action: "tinycloud.kv/list" };
|
|
3291
|
+
}
|
|
3292
|
+
if (normalized === "head") {
|
|
3293
|
+
return { bases: ["vault"], action: "tinycloud.kv/get" };
|
|
3294
|
+
}
|
|
3295
|
+
if (normalized === "metadata") {
|
|
3296
|
+
return { bases: ["vault"], action: "tinycloud.kv/metadata" };
|
|
3297
|
+
}
|
|
3298
|
+
throw new ManifestValidationError(
|
|
3299
|
+
`unknown vault action ${JSON.stringify(action)}; expected read, write, delete, get, put, del, list, head, or metadata`
|
|
3300
|
+
);
|
|
3301
|
+
}
|
|
3302
|
+
function normalizeVaultAction(action) {
|
|
3303
|
+
if (action.startsWith(`${VAULT_PERMISSION_SERVICE}/`)) {
|
|
3304
|
+
return action.slice(`${VAULT_PERMISSION_SERVICE}/`.length);
|
|
3305
|
+
}
|
|
3306
|
+
if (action.startsWith("tinycloud.kv/")) {
|
|
3307
|
+
return action.slice("tinycloud.kv/".length);
|
|
3308
|
+
}
|
|
3309
|
+
if (action.includes("/")) {
|
|
3310
|
+
throw new ManifestValidationError(
|
|
3311
|
+
`unknown vault action ${JSON.stringify(action)}; expected a tinycloud.vault or tinycloud.kv action`
|
|
3312
|
+
);
|
|
3313
|
+
}
|
|
3314
|
+
return action;
|
|
3315
|
+
}
|
|
3316
|
+
function vaultKVPath(base, path) {
|
|
3317
|
+
const normalized = path.startsWith("/") ? path.slice(1) : path;
|
|
3318
|
+
return `${base}/${normalized}`;
|
|
3031
3319
|
}
|
|
3032
3320
|
function cloneResourceCapability(entry) {
|
|
3033
3321
|
return {
|
|
@@ -3072,6 +3360,24 @@ function dedupeResources(resources) {
|
|
|
3072
3360
|
}
|
|
3073
3361
|
return [...byKey.values()];
|
|
3074
3362
|
}
|
|
3363
|
+
function capabilitiesReadPermission(space) {
|
|
3364
|
+
return {
|
|
3365
|
+
service: "tinycloud.capabilities",
|
|
3366
|
+
space,
|
|
3367
|
+
path: "",
|
|
3368
|
+
actions: ["tinycloud.capabilities/read"]
|
|
3369
|
+
};
|
|
3370
|
+
}
|
|
3371
|
+
function withCapabilitiesReadForSpaces(resources) {
|
|
3372
|
+
if (resources.length === 0) {
|
|
3373
|
+
return [];
|
|
3374
|
+
}
|
|
3375
|
+
const spaces = new Set(resources.map((resource) => resource.space));
|
|
3376
|
+
return dedupeResources([
|
|
3377
|
+
...resources,
|
|
3378
|
+
...[...spaces].map(capabilitiesReadPermission)
|
|
3379
|
+
]);
|
|
3380
|
+
}
|
|
3075
3381
|
function accountRegistryPermission() {
|
|
3076
3382
|
return {
|
|
3077
3383
|
service: "tinycloud.kv",
|
|
@@ -3099,6 +3405,7 @@ function composeManifestRequest(inputs, options = {}) {
|
|
|
3099
3405
|
if (includeAccountRegistryPermissions) {
|
|
3100
3406
|
resources.push(accountRegistryPermission());
|
|
3101
3407
|
}
|
|
3408
|
+
const resourcesWithImplicitCapabilities = withCapabilitiesReadForSpaces(resources);
|
|
3102
3409
|
const manifestsByAppId = /* @__PURE__ */ new Map();
|
|
3103
3410
|
for (const manifest of manifests) {
|
|
3104
3411
|
const current = manifestsByAppId.get(manifest.app_id);
|
|
@@ -3118,7 +3425,7 @@ function composeManifestRequest(inputs, options = {}) {
|
|
|
3118
3425
|
})) : [];
|
|
3119
3426
|
return {
|
|
3120
3427
|
manifests,
|
|
3121
|
-
resources:
|
|
3428
|
+
resources: resourcesWithImplicitCapabilities,
|
|
3122
3429
|
delegationTargets,
|
|
3123
3430
|
registryRecords,
|
|
3124
3431
|
expiryMs: Math.max(...resolved.map((entry) => entry.expiryMs)),
|
|
@@ -3197,7 +3504,7 @@ function inferShortServiceFromActionUrns(actions) {
|
|
|
3197
3504
|
return short;
|
|
3198
3505
|
}
|
|
3199
3506
|
var DEFAULT_READ_ACTIONS = ["tinycloud.kv/get", "tinycloud.kv/metadata"];
|
|
3200
|
-
var DEFAULT_EXPIRY_MS =
|
|
3507
|
+
var DEFAULT_EXPIRY_MS = EXPIRY.SHARE_MS;
|
|
3201
3508
|
var BASE64_PREFIX = "tc1:";
|
|
3202
3509
|
function createError2(code, message, cause, meta) {
|
|
3203
3510
|
return {
|
|
@@ -3839,7 +4146,7 @@ function createSharingService(config) {
|
|
|
3839
4146
|
}
|
|
3840
4147
|
|
|
3841
4148
|
// src/authorization/CapabilityKeyRegistry.ts
|
|
3842
|
-
var
|
|
4149
|
+
var import_sdk_services4 = require("@tinycloud/sdk-services");
|
|
3843
4150
|
var SERVICE_NAME2 = "capability-key-registry";
|
|
3844
4151
|
var CapabilityKeyRegistryErrorCodes = {
|
|
3845
4152
|
/** Key not found in registry */
|
|
@@ -4057,8 +4364,8 @@ var CapabilityKeyRegistry = class {
|
|
|
4057
4364
|
revokeDelegation(cid) {
|
|
4058
4365
|
const stored = this.store.byCid.get(cid);
|
|
4059
4366
|
if (!stored) {
|
|
4060
|
-
return (0,
|
|
4061
|
-
(0,
|
|
4367
|
+
return (0, import_sdk_services4.err)(
|
|
4368
|
+
(0, import_sdk_services4.serviceError)(
|
|
4062
4369
|
CapabilityKeyRegistryErrorCodes.KEY_NOT_FOUND,
|
|
4063
4370
|
`Delegation not found: ${cid}`,
|
|
4064
4371
|
SERVICE_NAME2
|
|
@@ -4080,7 +4387,7 @@ var CapabilityKeyRegistry = class {
|
|
|
4080
4387
|
}
|
|
4081
4388
|
}
|
|
4082
4389
|
}
|
|
4083
|
-
return (0,
|
|
4390
|
+
return (0, import_sdk_services4.ok)(void 0);
|
|
4084
4391
|
}
|
|
4085
4392
|
// ===========================================================================
|
|
4086
4393
|
// Search
|
|
@@ -4330,6 +4637,394 @@ async function checkNodeInfo(host, sdkProtocol, fetchFn = globalThis.fetch.bind(
|
|
|
4330
4637
|
};
|
|
4331
4638
|
}
|
|
4332
4639
|
|
|
4640
|
+
// src/location.ts
|
|
4641
|
+
var import_multiaddr = require("@multiformats/multiaddr");
|
|
4642
|
+
var import_multiaddr_to_uri = require("@multiformats/multiaddr-to-uri");
|
|
4643
|
+
var import_uri_to_multiaddr = require("@multiformats/uri-to-multiaddr");
|
|
4644
|
+
var import_ed25519 = require("@noble/curves/ed25519");
|
|
4645
|
+
var import_basics = require("multiformats/basics");
|
|
4646
|
+
var import_viem = require("viem");
|
|
4647
|
+
var DEFAULT_TINYCLOUD_LOCATION_REGISTRY_URL = "https://registry.tinycloud.xyz";
|
|
4648
|
+
var DEFAULT_TINYCLOUD_FALLBACK_HOST = "https://node.tinycloud.xyz";
|
|
4649
|
+
var LocationRecordValidationError = class extends Error {
|
|
4650
|
+
constructor(message) {
|
|
4651
|
+
super(`Location record validation failed: ${message}`);
|
|
4652
|
+
this.name = "LocationRecordValidationError";
|
|
4653
|
+
}
|
|
4654
|
+
};
|
|
4655
|
+
var CloudLocationResolutionError = class extends Error {
|
|
4656
|
+
constructor(subject, attempts) {
|
|
4657
|
+
super(`Unable to resolve TinyCloud location for ${subject}`);
|
|
4658
|
+
this.name = "CloudLocationResolutionError";
|
|
4659
|
+
this.attempts = attempts;
|
|
4660
|
+
}
|
|
4661
|
+
};
|
|
4662
|
+
function locationPayloadForRecord(record) {
|
|
4663
|
+
return {
|
|
4664
|
+
version: record.version,
|
|
4665
|
+
subject: record.subject,
|
|
4666
|
+
multiaddrs: [...record.multiaddrs],
|
|
4667
|
+
updated_at: record.updated_at,
|
|
4668
|
+
sequence: record.sequence
|
|
4669
|
+
};
|
|
4670
|
+
}
|
|
4671
|
+
function canonicalLocationPayload(payload) {
|
|
4672
|
+
return JSON.stringify({
|
|
4673
|
+
version: payload.version,
|
|
4674
|
+
subject: payload.subject,
|
|
4675
|
+
multiaddrs: payload.multiaddrs,
|
|
4676
|
+
updated_at: payload.updated_at,
|
|
4677
|
+
sequence: payload.sequence
|
|
4678
|
+
});
|
|
4679
|
+
}
|
|
4680
|
+
async function signLocationRecord(payload, signer) {
|
|
4681
|
+
validateLocationRecordPayload(payload);
|
|
4682
|
+
const message = canonicalLocationPayload(payload);
|
|
4683
|
+
const signature = signer.type === "did:pkh" ? await signer.signMessage(message) : base64UrlEncode2(
|
|
4684
|
+
await signer.signBytes(new TextEncoder().encode(message))
|
|
4685
|
+
);
|
|
4686
|
+
return { ...payload, signature };
|
|
4687
|
+
}
|
|
4688
|
+
function validateLocationRecordPayload(input) {
|
|
4689
|
+
if (input === null || typeof input !== "object") {
|
|
4690
|
+
throw new LocationRecordValidationError("payload must be an object");
|
|
4691
|
+
}
|
|
4692
|
+
const payload = input;
|
|
4693
|
+
if (payload.version !== 1) {
|
|
4694
|
+
throw new LocationRecordValidationError("version must be 1");
|
|
4695
|
+
}
|
|
4696
|
+
validateSubject(payload.subject);
|
|
4697
|
+
validateMultiaddrs(payload.multiaddrs);
|
|
4698
|
+
if (typeof payload.updated_at !== "string" || Number.isNaN(Date.parse(payload.updated_at))) {
|
|
4699
|
+
throw new LocationRecordValidationError(
|
|
4700
|
+
"updated_at must be an ISO timestamp"
|
|
4701
|
+
);
|
|
4702
|
+
}
|
|
4703
|
+
if (typeof payload.sequence !== "number" || !Number.isSafeInteger(payload.sequence) || payload.sequence < 0) {
|
|
4704
|
+
throw new LocationRecordValidationError(
|
|
4705
|
+
"sequence must be a non-negative safe integer"
|
|
4706
|
+
);
|
|
4707
|
+
}
|
|
4708
|
+
return {
|
|
4709
|
+
version: 1,
|
|
4710
|
+
subject: payload.subject,
|
|
4711
|
+
multiaddrs: [...payload.multiaddrs],
|
|
4712
|
+
updated_at: payload.updated_at,
|
|
4713
|
+
sequence: payload.sequence
|
|
4714
|
+
};
|
|
4715
|
+
}
|
|
4716
|
+
function validateLocationRecord(input) {
|
|
4717
|
+
const payload = validateLocationRecordPayload(input);
|
|
4718
|
+
const signature = input.signature;
|
|
4719
|
+
if (typeof signature !== "string" || signature.length === 0) {
|
|
4720
|
+
throw new LocationRecordValidationError(
|
|
4721
|
+
"signature must be a non-empty string"
|
|
4722
|
+
);
|
|
4723
|
+
}
|
|
4724
|
+
return { ...payload, signature };
|
|
4725
|
+
}
|
|
4726
|
+
async function verifyLocationRecord(input) {
|
|
4727
|
+
const record = validateLocationRecord(input);
|
|
4728
|
+
const payload = canonicalLocationPayload(locationPayloadForRecord(record));
|
|
4729
|
+
if (record.subject.startsWith("did:pkh:")) {
|
|
4730
|
+
return verifyPkhSignature(record.subject, payload, record.signature);
|
|
4731
|
+
}
|
|
4732
|
+
if (record.subject.startsWith("did:key:")) {
|
|
4733
|
+
return verifyDidKeySignature(record.subject, payload, record.signature);
|
|
4734
|
+
}
|
|
4735
|
+
return false;
|
|
4736
|
+
}
|
|
4737
|
+
async function fetchLocationRecord(registryUrl, subject, fetchFn = globalThis.fetch) {
|
|
4738
|
+
const url = `${registryUrl.replace(/\/$/, "")}/v1/locations/${encodeURIComponent(subject)}`;
|
|
4739
|
+
const response = await fetchFn(url);
|
|
4740
|
+
if (response.status === 404) {
|
|
4741
|
+
return null;
|
|
4742
|
+
}
|
|
4743
|
+
if (!response.ok) {
|
|
4744
|
+
throw new Error(`location registry returned HTTP ${response.status}`);
|
|
4745
|
+
}
|
|
4746
|
+
const body = await response.json();
|
|
4747
|
+
if (body.record === void 0) {
|
|
4748
|
+
throw new LocationRecordValidationError("registry response missing record");
|
|
4749
|
+
}
|
|
4750
|
+
return validateLocationRecord(body.record);
|
|
4751
|
+
}
|
|
4752
|
+
async function resolveCloudLocation(subject, options = {}) {
|
|
4753
|
+
validateSubject(subject);
|
|
4754
|
+
const verifyRecords = options.verifyRecords ?? true;
|
|
4755
|
+
const attempts = await Promise.all([
|
|
4756
|
+
resolveExplicit(subject, options.explicitMultiaddrs),
|
|
4757
|
+
resolveBlockchain(subject, options.blockchain, verifyRecords),
|
|
4758
|
+
resolveCentralized(subject, options, verifyRecords),
|
|
4759
|
+
resolveFallback(subject, options.fallbackMultiaddrs)
|
|
4760
|
+
]);
|
|
4761
|
+
const winner = attempts.find((attempt) => attempt.candidate)?.candidate;
|
|
4762
|
+
if (!winner) {
|
|
4763
|
+
throw new CloudLocationResolutionError(subject, attempts);
|
|
4764
|
+
}
|
|
4765
|
+
return {
|
|
4766
|
+
subject,
|
|
4767
|
+
source: winner.source,
|
|
4768
|
+
multiaddrs: [...winner.multiaddrs],
|
|
4769
|
+
...winner.record ? { record: winner.record } : {},
|
|
4770
|
+
attempts,
|
|
4771
|
+
resolvedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4772
|
+
};
|
|
4773
|
+
}
|
|
4774
|
+
async function resolveTinyCloudHosts(subject, options = {}) {
|
|
4775
|
+
const location = await resolveCloudLocation(subject, {
|
|
4776
|
+
explicitMultiaddrs: hostsToMultiaddrs(options.explicitHosts),
|
|
4777
|
+
blockchain: options.blockchain,
|
|
4778
|
+
centralizedRegistryUrl: options.registryUrl === null ? void 0 : options.registryUrl ?? DEFAULT_TINYCLOUD_LOCATION_REGISTRY_URL,
|
|
4779
|
+
fallbackMultiaddrs: hostsToMultiaddrs(
|
|
4780
|
+
options.fallbackHosts === null ? void 0 : options.fallbackHosts ?? [DEFAULT_TINYCLOUD_FALLBACK_HOST]
|
|
4781
|
+
),
|
|
4782
|
+
fetch: options.fetch,
|
|
4783
|
+
verifyRecords: options.verifyRecords
|
|
4784
|
+
});
|
|
4785
|
+
return {
|
|
4786
|
+
hosts: location.multiaddrs.map((addr) => multiaddrToHttpUrl(addr)),
|
|
4787
|
+
location
|
|
4788
|
+
};
|
|
4789
|
+
}
|
|
4790
|
+
function multiaddrToHttpUrl(input) {
|
|
4791
|
+
const uri = (0, import_multiaddr_to_uri.multiaddrToUri)((0, import_multiaddr.multiaddr)(input));
|
|
4792
|
+
if (!uri.startsWith("http://") && !uri.startsWith("https://")) {
|
|
4793
|
+
throw new LocationRecordValidationError(
|
|
4794
|
+
`multiaddr does not resolve to http/https: ${input}`
|
|
4795
|
+
);
|
|
4796
|
+
}
|
|
4797
|
+
return uri;
|
|
4798
|
+
}
|
|
4799
|
+
function httpUrlToMultiaddr(input) {
|
|
4800
|
+
const url = new URL(input);
|
|
4801
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
4802
|
+
throw new LocationRecordValidationError("URL must use http or https");
|
|
4803
|
+
}
|
|
4804
|
+
return (0, import_uri_to_multiaddr.uriToMultiaddr)(url.toString()).toString();
|
|
4805
|
+
}
|
|
4806
|
+
function hostsToMultiaddrs(hosts) {
|
|
4807
|
+
if (hosts === void 0 || hosts.length === 0) {
|
|
4808
|
+
return void 0;
|
|
4809
|
+
}
|
|
4810
|
+
return hosts.map(
|
|
4811
|
+
(host) => host.startsWith("/") ? host : httpUrlToMultiaddr(host)
|
|
4812
|
+
);
|
|
4813
|
+
}
|
|
4814
|
+
async function resolveExplicit(subject, multiaddrs) {
|
|
4815
|
+
return resolveAttempt("explicit", async () => {
|
|
4816
|
+
if (multiaddrs === void 0 || multiaddrs.length === 0) {
|
|
4817
|
+
return null;
|
|
4818
|
+
}
|
|
4819
|
+
return toCandidate(subject, "explicit", multiaddrs, false);
|
|
4820
|
+
});
|
|
4821
|
+
}
|
|
4822
|
+
async function resolveBlockchain(subject, resolver, verifyRecords) {
|
|
4823
|
+
return resolveAttempt("blockchain", async () => {
|
|
4824
|
+
if (!resolver) {
|
|
4825
|
+
return null;
|
|
4826
|
+
}
|
|
4827
|
+
return toCandidate(
|
|
4828
|
+
subject,
|
|
4829
|
+
"blockchain",
|
|
4830
|
+
await resolver(subject),
|
|
4831
|
+
verifyRecords
|
|
4832
|
+
);
|
|
4833
|
+
});
|
|
4834
|
+
}
|
|
4835
|
+
async function resolveCentralized(subject, options, verifyRecords) {
|
|
4836
|
+
return resolveAttempt("centralized", async () => {
|
|
4837
|
+
if (!options.centralizedRegistryUrl) {
|
|
4838
|
+
return null;
|
|
4839
|
+
}
|
|
4840
|
+
const record = await fetchLocationRecord(
|
|
4841
|
+
options.centralizedRegistryUrl,
|
|
4842
|
+
subject,
|
|
4843
|
+
options.fetch
|
|
4844
|
+
);
|
|
4845
|
+
return toCandidate(subject, "centralized", record, verifyRecords);
|
|
4846
|
+
});
|
|
4847
|
+
}
|
|
4848
|
+
async function resolveFallback(subject, multiaddrs) {
|
|
4849
|
+
return resolveAttempt("fallback", async () => {
|
|
4850
|
+
if (multiaddrs === void 0 || multiaddrs.length === 0) {
|
|
4851
|
+
return null;
|
|
4852
|
+
}
|
|
4853
|
+
return toCandidate(subject, "fallback", multiaddrs, false);
|
|
4854
|
+
});
|
|
4855
|
+
}
|
|
4856
|
+
async function resolveAttempt(source, resolve) {
|
|
4857
|
+
try {
|
|
4858
|
+
const candidate = await resolve();
|
|
4859
|
+
return candidate ? { source, candidate } : { source };
|
|
4860
|
+
} catch (error) {
|
|
4861
|
+
return {
|
|
4862
|
+
source,
|
|
4863
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
4864
|
+
};
|
|
4865
|
+
}
|
|
4866
|
+
}
|
|
4867
|
+
async function toCandidate(subject, source, input, verifyRecord) {
|
|
4868
|
+
if (input === null || input === void 0) {
|
|
4869
|
+
return null;
|
|
4870
|
+
}
|
|
4871
|
+
if (Array.isArray(input)) {
|
|
4872
|
+
validateMultiaddrs(input);
|
|
4873
|
+
return { source, multiaddrs: [...input] };
|
|
4874
|
+
}
|
|
4875
|
+
const maybeRecord = input;
|
|
4876
|
+
if (maybeRecord.version === 1 && maybeRecord.signature !== void 0) {
|
|
4877
|
+
const record = validateLocationRecord(input);
|
|
4878
|
+
if (record.subject !== subject) {
|
|
4879
|
+
throw new LocationRecordValidationError(
|
|
4880
|
+
"location record subject does not match requested subject"
|
|
4881
|
+
);
|
|
4882
|
+
}
|
|
4883
|
+
if (verifyRecord && !await verifyLocationRecord(record)) {
|
|
4884
|
+
throw new LocationRecordValidationError(
|
|
4885
|
+
"location record signature is invalid"
|
|
4886
|
+
);
|
|
4887
|
+
}
|
|
4888
|
+
return { source, multiaddrs: [...record.multiaddrs], record };
|
|
4889
|
+
}
|
|
4890
|
+
const candidateInput = input;
|
|
4891
|
+
if (!Array.isArray(candidateInput.multiaddrs)) {
|
|
4892
|
+
throw new LocationRecordValidationError(
|
|
4893
|
+
"candidate multiaddrs must be an array"
|
|
4894
|
+
);
|
|
4895
|
+
}
|
|
4896
|
+
validateMultiaddrs(candidateInput.multiaddrs);
|
|
4897
|
+
if (candidateInput.record !== void 0) {
|
|
4898
|
+
const record = validateLocationRecord(candidateInput.record);
|
|
4899
|
+
if (record.subject !== subject) {
|
|
4900
|
+
throw new LocationRecordValidationError(
|
|
4901
|
+
"location record subject does not match requested subject"
|
|
4902
|
+
);
|
|
4903
|
+
}
|
|
4904
|
+
if (verifyRecord && !await verifyLocationRecord(record)) {
|
|
4905
|
+
throw new LocationRecordValidationError(
|
|
4906
|
+
"location record signature is invalid"
|
|
4907
|
+
);
|
|
4908
|
+
}
|
|
4909
|
+
return { source, multiaddrs: [...candidateInput.multiaddrs], record };
|
|
4910
|
+
}
|
|
4911
|
+
return { source, multiaddrs: [...candidateInput.multiaddrs] };
|
|
4912
|
+
}
|
|
4913
|
+
function validateSubject(subject) {
|
|
4914
|
+
if (typeof subject !== "string" || subject.length === 0) {
|
|
4915
|
+
throw new LocationRecordValidationError(
|
|
4916
|
+
"subject must be a non-empty string"
|
|
4917
|
+
);
|
|
4918
|
+
}
|
|
4919
|
+
if (!subject.startsWith("did:pkh:") && !subject.startsWith("did:key:")) {
|
|
4920
|
+
throw new LocationRecordValidationError(
|
|
4921
|
+
"subject must be did:pkh or did:key"
|
|
4922
|
+
);
|
|
4923
|
+
}
|
|
4924
|
+
}
|
|
4925
|
+
function validateMultiaddrs(input) {
|
|
4926
|
+
if (!Array.isArray(input)) {
|
|
4927
|
+
throw new LocationRecordValidationError("multiaddrs must be an array");
|
|
4928
|
+
}
|
|
4929
|
+
for (const addr of input) {
|
|
4930
|
+
if (typeof addr !== "string" || addr.length === 0) {
|
|
4931
|
+
throw new LocationRecordValidationError(
|
|
4932
|
+
"multiaddr entries must be non-empty strings"
|
|
4933
|
+
);
|
|
4934
|
+
}
|
|
4935
|
+
try {
|
|
4936
|
+
(0, import_multiaddr.multiaddr)(addr);
|
|
4937
|
+
} catch {
|
|
4938
|
+
throw new LocationRecordValidationError(`invalid multiaddr: ${addr}`);
|
|
4939
|
+
}
|
|
4940
|
+
}
|
|
4941
|
+
}
|
|
4942
|
+
async function verifyPkhSignature(did, payload, signature) {
|
|
4943
|
+
const address = did.split(":").at(-1);
|
|
4944
|
+
if (!address || !/^0x[a-fA-F0-9]{40}$/.test(address)) {
|
|
4945
|
+
throw new LocationRecordValidationError(
|
|
4946
|
+
"did:pkh subject must end with an EVM address"
|
|
4947
|
+
);
|
|
4948
|
+
}
|
|
4949
|
+
if (!/^0x[0-9a-fA-F]+$/.test(signature)) {
|
|
4950
|
+
throw new LocationRecordValidationError("did:pkh signature must be hex");
|
|
4951
|
+
}
|
|
4952
|
+
return (0, import_viem.verifyMessage)({
|
|
4953
|
+
address,
|
|
4954
|
+
message: payload,
|
|
4955
|
+
signature
|
|
4956
|
+
});
|
|
4957
|
+
}
|
|
4958
|
+
function verifyDidKeySignature(did, payload, signature) {
|
|
4959
|
+
const publicKey = ed25519PublicKeyFromDidKey(did);
|
|
4960
|
+
const signatureBytes = decodeBase64Url(signature);
|
|
4961
|
+
if (signatureBytes.length !== 64) {
|
|
4962
|
+
throw new LocationRecordValidationError(
|
|
4963
|
+
"did:key signature must be a base64url Ed25519 signature"
|
|
4964
|
+
);
|
|
4965
|
+
}
|
|
4966
|
+
return import_ed25519.ed25519.verify(
|
|
4967
|
+
signatureBytes,
|
|
4968
|
+
new TextEncoder().encode(payload),
|
|
4969
|
+
publicKey
|
|
4970
|
+
);
|
|
4971
|
+
}
|
|
4972
|
+
function ed25519PublicKeyFromDidKey(did) {
|
|
4973
|
+
const identifier = did.slice("did:key:".length);
|
|
4974
|
+
if (!identifier.startsWith("z")) {
|
|
4975
|
+
throw new LocationRecordValidationError(
|
|
4976
|
+
"did:key must use base58btc multibase"
|
|
4977
|
+
);
|
|
4978
|
+
}
|
|
4979
|
+
const bytes = import_basics.bases.base58btc.decode(identifier);
|
|
4980
|
+
if (bytes.length !== 34 || bytes[0] !== 237 || bytes[1] !== 1) {
|
|
4981
|
+
throw new LocationRecordValidationError(
|
|
4982
|
+
"did:key must be an Ed25519 public key"
|
|
4983
|
+
);
|
|
4984
|
+
}
|
|
4985
|
+
return bytes.slice(2);
|
|
4986
|
+
}
|
|
4987
|
+
function base64UrlEncode2(bytes) {
|
|
4988
|
+
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
4989
|
+
let output = "";
|
|
4990
|
+
for (let i = 0; i < bytes.length; i += 3) {
|
|
4991
|
+
const a = bytes[i];
|
|
4992
|
+
const b = bytes[i + 1];
|
|
4993
|
+
const c = bytes[i + 2];
|
|
4994
|
+
const triplet = a << 16 | (b ?? 0) << 8 | (c ?? 0);
|
|
4995
|
+
output += alphabet[triplet >> 18 & 63];
|
|
4996
|
+
output += alphabet[triplet >> 12 & 63];
|
|
4997
|
+
if (i + 1 < bytes.length) {
|
|
4998
|
+
output += alphabet[triplet >> 6 & 63];
|
|
4999
|
+
}
|
|
5000
|
+
if (i + 2 < bytes.length) {
|
|
5001
|
+
output += alphabet[triplet & 63];
|
|
5002
|
+
}
|
|
5003
|
+
}
|
|
5004
|
+
return output;
|
|
5005
|
+
}
|
|
5006
|
+
function decodeBase64Url(value) {
|
|
5007
|
+
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
5008
|
+
const bytes = [];
|
|
5009
|
+
let buffer = 0;
|
|
5010
|
+
let bits = 0;
|
|
5011
|
+
for (const char of value) {
|
|
5012
|
+
const index = alphabet.indexOf(char);
|
|
5013
|
+
if (index < 0) {
|
|
5014
|
+
throw new LocationRecordValidationError(
|
|
5015
|
+
"did:key signature must be base64url"
|
|
5016
|
+
);
|
|
5017
|
+
}
|
|
5018
|
+
buffer = buffer << 6 | index;
|
|
5019
|
+
bits += 6;
|
|
5020
|
+
if (bits >= 8) {
|
|
5021
|
+
bits -= 8;
|
|
5022
|
+
bytes.push(buffer >> bits & 255);
|
|
5023
|
+
}
|
|
5024
|
+
}
|
|
5025
|
+
return Uint8Array.from(bytes);
|
|
5026
|
+
}
|
|
5027
|
+
|
|
4333
5028
|
// src/capabilities.ts
|
|
4334
5029
|
var PermissionNotInManifestError = class extends Error {
|
|
4335
5030
|
constructor(missing, granted) {
|
|
@@ -4451,10 +5146,13 @@ function parseRecapCapabilities(parseWasm, siwe) {
|
|
|
4451
5146
|
CapabilityKeyRegistry,
|
|
4452
5147
|
CapabilityKeyRegistryErrorCodes,
|
|
4453
5148
|
ClientSessionSchema,
|
|
5149
|
+
CloudLocationResolutionError,
|
|
4454
5150
|
DEFAULT_DEFAULTS,
|
|
4455
5151
|
DEFAULT_EXPIRY,
|
|
4456
5152
|
DEFAULT_MANIFEST_SPACE,
|
|
4457
5153
|
DEFAULT_MANIFEST_VERSION,
|
|
5154
|
+
DEFAULT_TINYCLOUD_FALLBACK_HOST,
|
|
5155
|
+
DEFAULT_TINYCLOUD_LOCATION_REGISTRY_URL,
|
|
4458
5156
|
DataVaultService,
|
|
4459
5157
|
DatabaseHandle,
|
|
4460
5158
|
DelegationErrorCodes,
|
|
@@ -4462,18 +5160,22 @@ function parseRecapCapabilities(parseWasm, siwe) {
|
|
|
4462
5160
|
DuckDbAction,
|
|
4463
5161
|
DuckDbDatabaseHandle,
|
|
4464
5162
|
DuckDbService,
|
|
5163
|
+
EXPIRY,
|
|
4465
5164
|
EnsDataSchema,
|
|
4466
5165
|
ErrorCodes,
|
|
4467
5166
|
HooksService,
|
|
4468
5167
|
KVService,
|
|
5168
|
+
LocationRecordValidationError,
|
|
4469
5169
|
ManifestValidationError,
|
|
4470
5170
|
PermissionNotInManifestError,
|
|
4471
5171
|
PrefixedKVService,
|
|
4472
5172
|
ProtocolMismatchError,
|
|
5173
|
+
SECRET_NAME_RE,
|
|
4473
5174
|
SERVICE_LONG_TO_SHORT,
|
|
4474
5175
|
SERVICE_SHORT_TO_LONG,
|
|
4475
5176
|
SQLAction,
|
|
4476
5177
|
SQLService,
|
|
5178
|
+
SecretsService,
|
|
4477
5179
|
ServiceContext,
|
|
4478
5180
|
SessionExpiredError,
|
|
4479
5181
|
SharingService,
|
|
@@ -4485,12 +5187,15 @@ function parseRecapCapabilities(parseWasm, siwe) {
|
|
|
4485
5187
|
SpaceService,
|
|
4486
5188
|
TinyCloud,
|
|
4487
5189
|
UnsupportedFeatureError,
|
|
5190
|
+
VAULT_PERMISSION_SERVICE,
|
|
4488
5191
|
VaultHeaders,
|
|
4489
5192
|
VaultPublicSpaceKVActions,
|
|
4490
5193
|
VersionCheckError,
|
|
4491
5194
|
activateSessionWithHost,
|
|
4492
5195
|
applyPrefix,
|
|
4493
5196
|
buildSpaceUri,
|
|
5197
|
+
canonicalLocationPayload,
|
|
5198
|
+
canonicalizeSecretScope,
|
|
4494
5199
|
checkNodeInfo,
|
|
4495
5200
|
composeManifestRequest,
|
|
4496
5201
|
createCapabilityKeyRegistry,
|
|
@@ -4502,23 +5207,36 @@ function parseRecapCapabilities(parseWasm, siwe) {
|
|
|
4502
5207
|
defaultSpaceCreationHandler,
|
|
4503
5208
|
err,
|
|
4504
5209
|
expandActionShortNames,
|
|
5210
|
+
expandPermissionEntries,
|
|
5211
|
+
expandPermissionEntry,
|
|
5212
|
+
fetchLocationRecord,
|
|
4505
5213
|
fetchPeerId,
|
|
5214
|
+
httpUrlToMultiaddr,
|
|
4506
5215
|
isCapabilitySubset,
|
|
4507
5216
|
loadManifest,
|
|
5217
|
+
locationPayloadForRecord,
|
|
4508
5218
|
makePublicSpaceId,
|
|
4509
5219
|
manifestAbilitiesUnion,
|
|
5220
|
+
multiaddrToHttpUrl,
|
|
4510
5221
|
normalizeDefaults,
|
|
4511
5222
|
ok,
|
|
4512
5223
|
parseExpiry,
|
|
4513
5224
|
parseRecapCapabilities,
|
|
4514
5225
|
parseSpaceUri,
|
|
5226
|
+
resolveCloudLocation,
|
|
4515
5227
|
resolveManifest,
|
|
5228
|
+
resolveSecretPath,
|
|
5229
|
+
resolveTinyCloudHosts,
|
|
4516
5230
|
resourceCapabilitiesToAbilitiesMap,
|
|
4517
5231
|
resourceCapabilitiesToSpaceAbilitiesMap,
|
|
4518
5232
|
serviceError,
|
|
5233
|
+
signLocationRecord,
|
|
4519
5234
|
submitHostDelegation,
|
|
4520
5235
|
validateClientSession,
|
|
5236
|
+
validateLocationRecord,
|
|
5237
|
+
validateLocationRecordPayload,
|
|
4521
5238
|
validateManifest,
|
|
4522
|
-
validatePersistedSessionData
|
|
5239
|
+
validatePersistedSessionData,
|
|
5240
|
+
verifyLocationRecord
|
|
4523
5241
|
});
|
|
4524
5242
|
//# sourceMappingURL=index.cjs.map
|