@tinycloud/sdk-core 2.11.1-beta.0 → 3.0.0-beta.1

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 CHANGED
@@ -136,6 +136,7 @@ __export(index_exports, {
136
136
  NETWORK_NAME_PATTERN: () => import_sdk_services8.NETWORK_NAME_PATTERN,
137
137
  NetworkIdError: () => import_sdk_services8.NetworkIdError,
138
138
  OPENKEY_ACTION_ID_SEPARATOR: () => OPENKEY_ACTION_ID_SEPARATOR,
139
+ OpenKeyManageKeyError: () => OpenKeyManageKeyError,
139
140
  POLICY_CAPABILITY_V1_DOMAIN: () => POLICY_CAPABILITY_V1_DOMAIN,
140
141
  POLICY_CAPABILITY_V1_SCHEMA: () => POLICY_CAPABILITY_V1_SCHEMA,
141
142
  POLICY_CREDENTIAL_REQUIREMENT_V1_TYPE: () => POLICY_CREDENTIAL_REQUIREMENT_V1_TYPE,
@@ -206,7 +207,9 @@ __export(index_exports, {
206
207
  SpaceService: () => SpaceService,
207
208
  TINYCLOUD_ACCOUNT_SPACE_MANIFEST: () => import_bootstrap4.TINYCLOUD_ACCOUNT_SPACE_MANIFEST,
208
209
  TINYCLOUD_APPLICATIONS_SPACE_MANIFEST: () => import_bootstrap4.TINYCLOUD_APPLICATIONS_SPACE_MANIFEST,
210
+ TINYCLOUD_CANONICAL_IDENTITY_CLAIM: () => TINYCLOUD_CANONICAL_IDENTITY_CLAIM,
209
211
  TINYCLOUD_DEFAULT_SPACE_MANIFEST: () => import_bootstrap4.TINYCLOUD_DEFAULT_SPACE_MANIFEST,
212
+ TINYCLOUD_MANAGE_KEY_SCOPE: () => TINYCLOUD_MANAGE_KEY_SCOPE,
210
213
  TINYCLOUD_PUBLIC_SPACE_MANIFEST: () => import_bootstrap4.TINYCLOUD_PUBLIC_SPACE_MANIFEST,
211
214
  TINYCLOUD_SECRETS_BOOTSTRAP_MANIFEST: () => import_bootstrap4.TINYCLOUD_SECRETS_BOOTSTRAP_MANIFEST,
212
215
  TinyCloud: () => TinyCloud,
@@ -267,6 +270,7 @@ __export(index_exports, {
267
270
  createInMemoryLocalNodeIdentityStore: () => createInMemoryLocalNodeIdentityStore,
268
271
  createLocalStorageLocalNodeIdentityStore: () => createLocalStorageLocalNodeIdentityStore,
269
272
  createOpenKeyCallbackSigningStrategy: () => createOpenKeyCallbackSigningStrategy,
273
+ createOpenKeyManageKeySigningStrategy: () => createOpenKeyManageKeySigningStrategy,
270
274
  createPolicyEnforcementDelegation: () => createPolicyEnforcementDelegation,
271
275
  createShareArtifact: () => createShareArtifact,
272
276
  createShareArtifactAsync: () => createShareArtifactAsync,
@@ -325,6 +329,7 @@ __export(index_exports, {
325
329
  generateRandomReceiverKey: () => import_sdk_services8.generateRandomReceiverKey,
326
330
  getPolicyRootStatusV3: () => getPolicyRootStatusV3,
327
331
  getTinyCloudDebugLogs: () => import_sdk_services8.getTinyCloudDebugLogs,
332
+ hasTinyCloudManageKeyScope: () => hasTinyCloudManageKeyScope,
328
333
  hexDecode: () => import_sdk_services8.hexDecode,
329
334
  hexEncode: () => import_sdk_services8.hexEncode,
330
335
  holderBindingCanonicalBytes: () => holderBindingCanonicalBytes,
@@ -357,6 +362,8 @@ __export(index_exports, {
357
362
  openWrappedKey: () => import_sdk_services8.openWrappedKey,
358
363
  parseCanonicalNetworkId: () => parseCanonicalNetworkId,
359
364
  parseCanonicalRecapResource: () => parseCanonicalRecapResource,
365
+ parseCanonicalTinyCloudIdentity: () => parseCanonicalTinyCloudIdentity,
366
+ parseCanonicalTinyCloudIdentityClaims: () => parseCanonicalTinyCloudIdentityClaims,
360
367
  parseCompactUcanAuthorization: () => parseCompactUcanAuthorization,
361
368
  parseExpiry: () => parseExpiry,
362
369
  parseNetworkId: () => import_sdk_services8.parseNetworkId,
@@ -379,6 +386,7 @@ __export(index_exports, {
379
386
  projectUnifiedPolicyCapability: () => projectUnifiedPolicyCapability,
380
387
  recapCaveatsEqual: () => recapCaveatsEqual,
381
388
  renewPolicyRootStatusV3: () => renewPolicyRootStatusV3,
389
+ requestTinyCloudManageKeyScope: () => requestTinyCloudManageKeyScope,
382
390
  resolveCloudLocation: () => resolveCloudLocation,
383
391
  resolveManifest: () => resolveManifest,
384
392
  resolveManifestKnowledgeRoot: () => resolveManifestKnowledgeRoot,
@@ -15367,6 +15375,236 @@ function createOpenKeyCallbackSigningStrategy(options2) {
15367
15375
  }
15368
15376
  var defaultSignStrategy = { type: "auto-sign" };
15369
15377
 
15378
+ // src/authorization/manage-key.ts
15379
+ var TINYCLOUD_MANAGE_KEY_SCOPE = "tinycloud:manage-key";
15380
+ var TINYCLOUD_CANONICAL_IDENTITY_CLAIM = "https://tinycloud.xyz/canonical_identity";
15381
+ function requestTinyCloudManageKeyScope(scopes = []) {
15382
+ const requested = typeof scopes === "string" ? scopes.split(/\s+/u) : scopes;
15383
+ return [
15384
+ ...new Set([...requested, TINYCLOUD_MANAGE_KEY_SCOPE].filter(Boolean))
15385
+ ].sort().join(" ");
15386
+ }
15387
+ function hasTinyCloudManageKeyScope(scopes) {
15388
+ const values = Array.isArray(scopes) ? scopes : typeof scopes === "string" ? scopes.split(/\s+/u) : [];
15389
+ return values.includes(TINYCLOUD_MANAGE_KEY_SCOPE);
15390
+ }
15391
+ var OpenKeyManageKeyError = class extends Error {
15392
+ constructor(code3, message, approvalUrl, retryable = false) {
15393
+ super(message);
15394
+ this.code = code3;
15395
+ this.approvalUrl = approvalUrl;
15396
+ this.retryable = retryable;
15397
+ this.name = "OpenKeyManageKeyError";
15398
+ }
15399
+ };
15400
+ function isRecord2(value) {
15401
+ return value !== null && typeof value === "object" && !Array.isArray(value);
15402
+ }
15403
+ function rejected(message) {
15404
+ return new OpenKeyManageKeyError("MESSAGE_REJECTED", message);
15405
+ }
15406
+ function identityMismatch(message) {
15407
+ return new OpenKeyManageKeyError("IDENTITY_MISMATCH", message);
15408
+ }
15409
+ function parseCanonicalTinyCloudIdentity(value) {
15410
+ if (!isRecord2(value))
15411
+ throw rejected("Canonical TinyCloud identity is missing");
15412
+ const expectedKeys = [
15413
+ "address",
15414
+ "chainId",
15415
+ "did",
15416
+ "keyId",
15417
+ "spaceId",
15418
+ "version"
15419
+ ];
15420
+ const actualKeys = Object.keys(value).sort();
15421
+ if (actualKeys.length !== expectedKeys.length || actualKeys.some((key, index) => key !== expectedKeys[index])) {
15422
+ throw rejected("Canonical TinyCloud identity has an invalid shape");
15423
+ }
15424
+ if (value.version !== "v1")
15425
+ throw rejected("Canonical TinyCloud identity version is unsupported");
15426
+ if (typeof value.keyId !== "string" || value.keyId.trim() === "") {
15427
+ throw rejected("Canonical TinyCloud identity key ID is invalid");
15428
+ }
15429
+ if (typeof value.address !== "string") {
15430
+ throw rejected("Canonical TinyCloud identity address is invalid");
15431
+ }
15432
+ let address;
15433
+ try {
15434
+ address = canonicalizeAddress(value.address);
15435
+ } catch {
15436
+ throw rejected("Canonical TinyCloud identity address is invalid");
15437
+ }
15438
+ if (address !== value.address) {
15439
+ throw rejected(
15440
+ "Canonical TinyCloud identity address must use EIP-55 checksum casing"
15441
+ );
15442
+ }
15443
+ if (typeof value.chainId !== "number" || !Number.isSafeInteger(value.chainId) || value.chainId <= 0) {
15444
+ throw rejected("Canonical TinyCloud identity chain ID is invalid");
15445
+ }
15446
+ const did = pkhDid(address, value.chainId);
15447
+ const spacePrefix = `tinycloud:pkh:eip155:${value.chainId}:${address}:`;
15448
+ if (typeof value.spaceId !== "string" || !value.spaceId.startsWith(spacePrefix)) {
15449
+ throw rejected("Canonical TinyCloud identity space is invalid");
15450
+ }
15451
+ const spaceName = value.spaceId.slice(spacePrefix.length);
15452
+ let spaceId;
15453
+ try {
15454
+ spaceId = makePkhSpaceId(address, value.chainId, spaceName);
15455
+ } catch {
15456
+ throw rejected("Canonical TinyCloud identity space is invalid");
15457
+ }
15458
+ if (value.did !== did || value.spaceId !== spaceId) {
15459
+ throw rejected("Canonical TinyCloud identity does not match its address");
15460
+ }
15461
+ return {
15462
+ version: "v1",
15463
+ keyId: value.keyId,
15464
+ address,
15465
+ chainId: value.chainId,
15466
+ did,
15467
+ spaceId
15468
+ };
15469
+ }
15470
+ function parseCanonicalTinyCloudIdentityClaims(claims) {
15471
+ if (!isRecord2(claims)) throw rejected("OAuth claims are missing");
15472
+ return parseCanonicalTinyCloudIdentity(
15473
+ claims[TINYCLOUD_CANONICAL_IDENTITY_CLAIM]
15474
+ );
15475
+ }
15476
+ function identitiesEqual(left, right) {
15477
+ return left.version === right.version && left.keyId === right.keyId && left.address === right.address && left.chainId === right.chainId && left.did === right.did && left.spaceId === right.spaceId;
15478
+ }
15479
+ function errorForResponse(body, status) {
15480
+ const message = body?.reason ?? body?.error ?? `OpenKey manage-key signing failed with HTTP ${status}`;
15481
+ switch (body?.code ?? body?.error) {
15482
+ case "missing_scope":
15483
+ case "consent_required":
15484
+ return new OpenKeyManageKeyError("CONSENT_REQUIRED", message, body?.approvalUrl);
15485
+ case "client_disabled":
15486
+ case "signing_disabled":
15487
+ case "grant_disabled":
15488
+ return new OpenKeyManageKeyError("GRANT_DISABLED", message, body?.approvalUrl);
15489
+ case "user_exclusive":
15490
+ return new OpenKeyManageKeyError("USER_EXCLUSIVE", message, body?.approvalUrl);
15491
+ case "token_expired":
15492
+ case "token_too_old":
15493
+ case "invalid_token":
15494
+ case "expired_token":
15495
+ return new OpenKeyManageKeyError("TOKEN_EXPIRED", message, body?.approvalUrl);
15496
+ case "canonical_key_unavailable":
15497
+ case "signer_failed":
15498
+ return new OpenKeyManageKeyError(
15499
+ "SIGNER_UNAVAILABLE",
15500
+ "The canonical key signer is temporarily unavailable",
15501
+ void 0,
15502
+ true
15503
+ );
15504
+ default:
15505
+ return new OpenKeyManageKeyError(
15506
+ status === 401 ? "TOKEN_EXPIRED" : "MESSAGE_REJECTED",
15507
+ message,
15508
+ body?.approvalUrl
15509
+ );
15510
+ }
15511
+ }
15512
+ async function resolveToken(token) {
15513
+ const value = typeof token === "function" ? await token() : token;
15514
+ if (typeof value !== "string" || value.trim() === "") {
15515
+ throw new OpenKeyManageKeyError(
15516
+ "CONSENT_REQUIRED",
15517
+ "A manage-key OAuth bearer token is required"
15518
+ );
15519
+ }
15520
+ return value;
15521
+ }
15522
+ function normalizeEndpoint(value) {
15523
+ let endpoint;
15524
+ try {
15525
+ endpoint = new URL(value);
15526
+ } catch {
15527
+ throw rejected("OpenKey manage-key signing endpoint must be an absolute URL");
15528
+ }
15529
+ const rawHttpAuthority = value.match(/^http:\/\/([^/?#]*)/iu)?.[1];
15530
+ const loopback = rawHttpAuthority !== void 0 && /^(?:localhost|127\.0\.0\.1|\[::1\])(?::[0-9]+)?$/u.test(rawHttpAuthority);
15531
+ if (endpoint.protocol !== "https:" && !(endpoint.protocol === "http:" && loopback) || endpoint.username !== "" || endpoint.password !== "" || endpoint.search !== "" || endpoint.hash !== "") {
15532
+ throw rejected("OpenKey manage-key signing endpoint must use HTTPS or exact loopback HTTP");
15533
+ }
15534
+ return endpoint.href;
15535
+ }
15536
+ function assertSignInRequest(request, identity3) {
15537
+ if (request.type !== "siwe" || request.purpose !== "sign-in" || request.chainId !== identity3.chainId || request.address !== identity3.address || typeof request.message !== "string" || request.message.length === 0) {
15538
+ throw rejected(
15539
+ "Manage-key signing only permits the canonical sign-in SIWE request"
15540
+ );
15541
+ }
15542
+ }
15543
+ function createOpenKeyManageKeySigningStrategy(options2) {
15544
+ const identity3 = parseCanonicalTinyCloudIdentity(options2.identity);
15545
+ const endpoint = normalizeEndpoint(options2.endpoint);
15546
+ return {
15547
+ type: "callback",
15548
+ openKeyAutoSign: true,
15549
+ handler: async (request) => {
15550
+ assertSignInRequest(request, identity3);
15551
+ const fetchImpl = options2.fetch ?? globalThis.fetch;
15552
+ if (!fetchImpl)
15553
+ throw rejected("OpenKey manage-key signing requires fetch");
15554
+ if (!hasTinyCloudManageKeyScope(options2.scopes)) {
15555
+ throw new OpenKeyManageKeyError(
15556
+ "CONSENT_REQUIRED",
15557
+ "The OAuth bearer token was not granted tinycloud:manage-key"
15558
+ );
15559
+ }
15560
+ const token = await resolveToken(options2.token);
15561
+ const response = await fetchImpl(endpoint, {
15562
+ method: "POST",
15563
+ credentials: "omit",
15564
+ headers: {
15565
+ authorization: `Bearer ${token}`,
15566
+ "content-type": "application/json"
15567
+ },
15568
+ body: JSON.stringify({
15569
+ address: request.address,
15570
+ chainId: request.chainId,
15571
+ message: request.message,
15572
+ type: "siwe"
15573
+ })
15574
+ });
15575
+ let body;
15576
+ try {
15577
+ const value = await response.json();
15578
+ body = isRecord2(value) ? value : void 0;
15579
+ } catch {
15580
+ body = void 0;
15581
+ }
15582
+ if (!response.ok || body?.approved !== true)
15583
+ throw errorForResponse(body, response.status);
15584
+ if (typeof body.signature !== "string")
15585
+ throw rejected("OpenKey manage-key signer did not return a signature");
15586
+ const responseIdentity = parseCanonicalTinyCloudIdentity(
15587
+ body.canonicalIdentity
15588
+ );
15589
+ if (!identitiesEqual(identity3, responseIdentity)) {
15590
+ throw identityMismatch(
15591
+ "OpenKey manage-key signer returned a different canonical identity"
15592
+ );
15593
+ }
15594
+ if (!await verifyEip191MessageSignature(
15595
+ request.message,
15596
+ body.signature,
15597
+ identity3.address
15598
+ )) {
15599
+ throw rejected(
15600
+ "OpenKey manage-key signer returned an invalid signature"
15601
+ );
15602
+ }
15603
+ return { approved: true, signature: body.signature };
15604
+ }
15605
+ };
15606
+ }
15607
+
15370
15608
  // src/authorization/spaceCreation.ts
15371
15609
  var AutoApproveSpaceCreationHandler = class {
15372
15610
  /**