@tinycloud/node-sdk 2.11.0-beta.9 → 3.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -17025,10 +17025,13 @@ var require_utils2 = __commonJS({
17025
17025
  // src/index.ts
17026
17026
  var index_exports = {};
17027
17027
  __export(index_exports, {
17028
+ ACCEPTED_MARKER_VERSIONS: () => ACCEPTED_MARKER_VERSIONS,
17028
17029
  ACCOUNT_REGISTRY_PATH: () => import_sdk_core12.ACCOUNT_REGISTRY_PATH,
17029
17030
  ACCOUNT_REGISTRY_SPACE: () => import_sdk_core12.ACCOUNT_REGISTRY_SPACE,
17030
17031
  AccountService: () => import_sdk_core3.AccountService,
17031
17032
  AutoApproveSpaceCreationHandler: () => import_sdk_core11.AutoApproveSpaceCreationHandler,
17033
+ BOOTSTRAP_COMPLETION_MARKER_KEY: () => BOOTSTRAP_COMPLETION_MARKER_KEY,
17034
+ BOOTSTRAP_COMPLETION_MARKER_VERSION: () => BOOTSTRAP_COMPLETION_MARKER_VERSION,
17032
17035
  CapabilityKeyRegistry: () => import_sdk_core20.CapabilityKeyRegistry,
17033
17036
  CapabilityKeyRegistryErrorCodes: () => import_sdk_core20.CapabilityKeyRegistryErrorCodes,
17034
17037
  CaveatedDelegationUnsupportedError: () => import_sdk_core12.CaveatedDelegationUnsupportedError,
@@ -17089,6 +17092,7 @@ __export(index_exports, {
17089
17092
  VaultPublicSpaceKVActions: () => import_sdk_core16.VaultPublicSpaceKVActions,
17090
17093
  VersionCheckError: () => import_sdk_core22.VersionCheckError,
17091
17094
  WasmKeyProvider: () => WasmKeyProvider,
17095
+ activateCompactRuntimeDelegation: () => activateCompactRuntimeDelegation,
17092
17096
  activateValidatedRuntimeDelegation: () => activateValidatedRuntimeDelegation,
17093
17097
  addressStorageKey: () => import_sdk_core11.addressStorageKey,
17094
17098
  buildCanonicalDecryptRequest: () => import_sdk_core17.buildCanonicalDecryptRequest,
@@ -17165,7 +17169,8 @@ __export(index_exports, {
17165
17169
  tinyCloudDebugLogger: () => import_sdk_core11.tinyCloudDebugLogger,
17166
17170
  validateEnvelope: () => import_sdk_core17.validateEnvelope,
17167
17171
  validateManifest: () => import_sdk_core12.validateManifest,
17168
- verifyDecryptResponse: () => import_sdk_core17.verifyDecryptResponse
17172
+ verifyDecryptResponse: () => import_sdk_core17.verifyDecryptResponse,
17173
+ wireOpenKeyAuthorize: () => wireOpenKeyAuthorize
17169
17174
  });
17170
17175
  module.exports = __toCommonJS(index_exports);
17171
17176
 
@@ -17278,6 +17283,8 @@ var import_sdk_core7 = require("@tinycloud/sdk-core");
17278
17283
  var import_sdk_services = require("@tinycloud/sdk-services");
17279
17284
 
17280
17285
  // src/authorization/NodeUserAuthorization.ts
17286
+ var import_node_crypto = require("crypto");
17287
+ var import_siwe = require("siwe");
17281
17288
  var import_sdk_core2 = require("@tinycloud/sdk-core");
17282
17289
 
17283
17290
  // src/authorization/strategies.ts
@@ -17358,6 +17365,27 @@ var MemorySessionStorage = class {
17358
17365
  // src/authorization/NodeUserAuthorization.ts
17359
17366
  var DECRYPT_ACTION = import_sdk_core2.ENCRYPTION.DECRYPT;
17360
17367
  var NETWORK_CREATE_ACTION = import_sdk_core2.ENCRYPTION.NETWORK_CREATE;
17368
+ function canonicalizeOpenKeyManifestJson(value) {
17369
+ const json = JSON.stringify(value);
17370
+ if (json === void 0) {
17371
+ throw new TypeError("manifest is not JSON-serializable");
17372
+ }
17373
+ return canonicalizeJsonValue(JSON.parse(json));
17374
+ }
17375
+ function canonicalizeJsonValue(value) {
17376
+ if (value === null || typeof value !== "object") {
17377
+ return JSON.stringify(value);
17378
+ }
17379
+ if (Array.isArray(value)) {
17380
+ return `[${value.map(canonicalizeJsonValue).join(",")}]`;
17381
+ }
17382
+ return `{${Object.keys(value).sort().map(
17383
+ (key2) => `${JSON.stringify(key2)}:${canonicalizeJsonValue(value[key2])}`
17384
+ ).join(",")}}`;
17385
+ }
17386
+ function canonicalOpenKeyManifestSha256Hex(value) {
17387
+ return (0, import_node_crypto.createHash)("sha256").update(canonicalizeOpenKeyManifestJson(value)).digest("hex");
17388
+ }
17361
17389
  function didPrincipalMatches(actual, expected) {
17362
17390
  try {
17363
17391
  return (0, import_sdk_core2.principalDidEquals)(actual, expected);
@@ -17375,6 +17403,16 @@ function addRawAbility(rawAbilities, resource, action) {
17375
17403
  actions.push(action);
17376
17404
  }
17377
17405
  }
17406
+ function cloneAbilitiesMap(actions) {
17407
+ return Object.fromEntries(
17408
+ Object.entries(actions).map(([service, paths]) => [
17409
+ service,
17410
+ Object.fromEntries(
17411
+ Object.entries(paths).map(([path, pathActions]) => [path, [...pathActions]])
17412
+ )
17413
+ ])
17414
+ );
17415
+ }
17378
17416
  var NodeUserAuthorization = class {
17379
17417
  constructor(config) {
17380
17418
  this.activeSessionKeyId = "default";
@@ -17646,23 +17684,41 @@ var NodeUserAuthorization = class {
17646
17684
  const defaultNetworkId = this.defaultEncryptionNetworkId(address, chainId);
17647
17685
  const primarySpaceId2 = (0, import_sdk_core2.makePkhSpaceId)(address, chainId, this.spacePrefix);
17648
17686
  const secretsSpaceId = (0, import_sdk_core2.makePkhSpaceId)(address, chainId, "secrets");
17649
- return {
17650
- abilities: this.defaultActions,
17651
- spaceId: primarySpaceId2,
17652
- spaceAbilities: {
17653
- [primarySpaceId2]: this.defaultActions,
17654
- [secretsSpaceId]: {
17655
- kv: {
17656
- "vault/secrets/": [
17657
- import_sdk_core2.KV.GET,
17658
- import_sdk_core2.KV.PUT,
17659
- import_sdk_core2.KV.DEL,
17660
- import_sdk_core2.KV.LIST,
17661
- import_sdk_core2.KV.METADATA
17662
- ]
17687
+ const primaryActions = this.includeAccountRegistryPermissions && this.spacePrefix === import_sdk_core2.ACCOUNT_REGISTRY_SPACE ? cloneAbilitiesMap(this.defaultActions) : this.defaultActions;
17688
+ const spaceAbilities2 = {
17689
+ [primarySpaceId2]: primaryActions,
17690
+ [secretsSpaceId]: {
17691
+ kv: {
17692
+ "vault/secrets/": [
17693
+ import_sdk_core2.KV.GET,
17694
+ import_sdk_core2.KV.PUT,
17695
+ import_sdk_core2.KV.DEL,
17696
+ import_sdk_core2.KV.LIST,
17697
+ import_sdk_core2.KV.METADATA
17698
+ ]
17699
+ }
17700
+ }
17701
+ };
17702
+ if (this.includeAccountRegistryPermissions && this.spacePrefix !== import_sdk_core2.ACCOUNT_REGISTRY_SPACE) {
17703
+ spaceAbilities2[(0, import_sdk_core2.makePkhSpaceId)(address, chainId, import_sdk_core2.ACCOUNT_REGISTRY_SPACE)] = (0, import_sdk_core2.resourceCapabilitiesToAbilitiesMap)(import_sdk_core2.ACCOUNT_MANIFEST_PERMISSIONS);
17704
+ } else if (this.includeAccountRegistryPermissions) {
17705
+ const accountAbilities = (0, import_sdk_core2.resourceCapabilitiesToAbilitiesMap)(
17706
+ import_sdk_core2.ACCOUNT_MANIFEST_PERMISSIONS
17707
+ );
17708
+ for (const [service, paths] of Object.entries(accountAbilities)) {
17709
+ const existingPaths = primaryActions[service] ?? (primaryActions[service] = {});
17710
+ for (const [path, actions] of Object.entries(paths)) {
17711
+ const existingActions = existingPaths[path] ?? (existingPaths[path] = []);
17712
+ for (const action of actions) {
17713
+ if (!existingActions.includes(action)) existingActions.push(action);
17663
17714
  }
17664
17715
  }
17665
- },
17716
+ }
17717
+ }
17718
+ return {
17719
+ abilities: primaryActions,
17720
+ spaceId: primarySpaceId2,
17721
+ spaceAbilities: spaceAbilities2,
17666
17722
  rawAbilities: {
17667
17723
  [defaultNetworkId]: [DECRYPT_ACTION, NETWORK_CREATE_ACTION]
17668
17724
  }
@@ -18243,6 +18299,398 @@ var NodeUserAuthorization = class {
18243
18299
  await this.ensureSpaceExists();
18244
18300
  return clientSession;
18245
18301
  }
18302
+ /**
18303
+ * End-to-end sign-in via OpenKey `authorizeTinyCloud()` (Sol CRITICAL-4).
18304
+ *
18305
+ * This is the production entry point that wires
18306
+ * {@link prepareSessionForSigning} → OpenKey `authorizeTinyCloud()` →
18307
+ * {@link signInWithOpenKeyResult} into one call. Consumers pass in a
18308
+ * caller-provided `authorizeFn` (typically a thin wrapper around
18309
+ * `openkey.authorizeTinyCloud()`) so this method has no direct
18310
+ * dependency on the OpenKey SDK — it works with any transport that
18311
+ * conforms to the versioned protocol.
18312
+ *
18313
+ * The `authorizeFn` MUST:
18314
+ * 1. Send the prepared SIWE bytes to OpenKey.
18315
+ * 2. Return the exact `TinyCloudAuthorizationResultV1` shape (with
18316
+ * `signedMessage` — the bytes the signature actually verifies
18317
+ * against — plus `selectedActionKeys` and `permissions`).
18318
+ *
18319
+ * NodeUserAuthorization then completes the session with `signedMessage`
18320
+ * (never the prepared bytes), verifying the signature, the immutable
18321
+ * SIWE fields, and every subset invariant enforced by
18322
+ * {@link signInWithOpenKeyResult}. This preserves the exact-byte
18323
+ * guarantee: if the user did NOT narrow, the returned `signedMessage`
18324
+ * equals the prepared SIWE byte-for-byte; if the user narrowed, only
18325
+ * the ReCap block differs and every immutable header field survives.
18326
+ *
18327
+ * Legacy exact-byte {@link signIn} callers are unaffected — this method
18328
+ * is opt-in.
18329
+ *
18330
+ * @param authorizeFn Callback that invokes the versioned OpenKey
18331
+ * `authorizeTinyCloud()` protocol and returns the result. Receives
18332
+ * the prepared SIWE bytes plus a `host` hint the widget should bind
18333
+ * into its /authorize-sign-prepare context.
18334
+ * @param options Optional overrides for the OpenKey request.
18335
+ */
18336
+ async signInWithOpenKey(authorizeFn, options) {
18337
+ if (options?.host !== void 0) {
18338
+ const overrideHost = options.host.trim();
18339
+ if (!overrideHost) {
18340
+ throw new Error("OpenKey authorization host must not be empty");
18341
+ }
18342
+ this.tinycloudHosts = [overrideHost];
18343
+ }
18344
+ const signingAddress = (0, import_sdk_core2.canonicalizeAddress)(await this.signer.getAddress());
18345
+ const signingChainId = await this.signer.getChainId();
18346
+ await this.resolveTinyCloudHostsForSignIn(signingAddress, signingChainId);
18347
+ const { prepared, keyId, jwk } = await this.prepareSessionForSigning();
18348
+ const presentation = this.buildPresentationEnvelope(options?.reason);
18349
+ const hostHint = this.primaryTinyCloudHost;
18350
+ const result = await authorizeFn({
18351
+ protocolVersion: 1,
18352
+ siwe: prepared.siwe,
18353
+ jwk,
18354
+ host: hostHint,
18355
+ keyId: options?.openkeyKeyId,
18356
+ presentation
18357
+ });
18358
+ return this.signInWithOpenKeyResult(result, prepared, keyId, jwk);
18359
+ }
18360
+ /**
18361
+ * Build a `CapabilityPresentationEnvelopeV1` from `this._manifest`. The
18362
+ * envelope is display-only — the OpenKey widget renders it under an
18363
+ * honest trust label, and the server /authorize-sign-prepare route
18364
+ * decides whether to upgrade the label to `origin-bound` by fetching
18365
+ * the well-known manifest and comparing its digest to
18366
+ * `manifestDigest`.
18367
+ *
18368
+ * Returns `undefined` when no manifest is set — the widget renders as
18369
+ * "no manifest supplied" (unsigned).
18370
+ *
18371
+ * The digest is a deterministic SHA-256 over canonical JSON of the
18372
+ * first manifest payload; OpenKey parses the fetched well-known manifest
18373
+ * and applies the same canonicalization before comparing digests.
18374
+ */
18375
+ buildPresentationEnvelope(reason) {
18376
+ const manifests = Array.isArray(this._manifest) ? this._manifest : this._manifest ? [this._manifest] : [];
18377
+ if (manifests.length === 0 && !reason) {
18378
+ return void 0;
18379
+ }
18380
+ const primary = manifests[0];
18381
+ let displayName;
18382
+ let manifestId;
18383
+ let manifestDigest;
18384
+ const envelopeManifests = [];
18385
+ for (const m of manifests) {
18386
+ envelopeManifests.push({
18387
+ name: m.name,
18388
+ appId: m.app_id,
18389
+ // Deep-copy to prevent later mutations from affecting the digest.
18390
+ payload: JSON.parse(JSON.stringify(m))
18391
+ });
18392
+ }
18393
+ if (primary) {
18394
+ displayName = primary.name;
18395
+ manifestId = primary.app_id;
18396
+ manifestDigest = canonicalOpenKeyManifestSha256Hex(primary);
18397
+ }
18398
+ return {
18399
+ protocolVersion: 1,
18400
+ displayName,
18401
+ reason: reason && reason.length > 0 ? reason : void 0,
18402
+ manifestId,
18403
+ manifestDigest,
18404
+ manifests: envelopeManifests.length > 0 ? envelopeManifests : void 0
18405
+ };
18406
+ }
18407
+ /**
18408
+ * Complete sign-in with a versioned OpenKey authorization result.
18409
+ *
18410
+ * Where `signInWithPreparedSession` completes with a signature over the
18411
+ * ORIGINAL prepared SIWE, this method completes with the SIWE bytes the
18412
+ * OpenKey widget actually presented and signed (`signedMessage`). It
18413
+ * exists because the versioned authorizeTinyCloud protocol lets the user
18414
+ * narrow capabilities; the returned bytes may differ from the original
18415
+ * request and are the only ones the signature actually verifies against.
18416
+ *
18417
+ * The `result` parameter is validated at the trust boundary via
18418
+ * `validateAuthorizationResultV1` (in sdk-core). Callers should validate
18419
+ * BEFORE calling this method so unknown/missing fields are rejected
18420
+ * before any session state is created.
18421
+ *
18422
+ * Validation performed here (defence-in-depth: even a "valid" v1 payload
18423
+ * must clear these checks before any session state is created):
18424
+ * 1. `result.address` matches the local signer's address.
18425
+ * 2. `result.signature` recovers to `result.address` when verified against
18426
+ * `result.signedMessage`.
18427
+ * 3. Every immutable SIWE header field (domain, address, URI, version,
18428
+ * chainId, nonce, issuedAt) in the original prepared SIWE is preserved
18429
+ * byte-for-byte in `result.signedMessage`. `statement` is NOT in this
18430
+ * list because the WASM renders it from the ReCap contents; it MUST
18431
+ * change when the ReCap is narrowed.
18432
+ * 4. Every capability in `result.signedMessage`'s ReCap block was present
18433
+ * in the original prepared SIWE — narrowing OK, broadening rejected.
18434
+ * 5. Every `result.selectedActionKeys` entry corresponds to a
18435
+ * (resource, action) pair in the returned SIWE.
18436
+ */
18437
+ async signInWithOpenKeyResult(result, prepared, keyId, jwk) {
18438
+ if (result.protocolVersion !== 1) {
18439
+ throw new Error(`Unsupported OpenKey protocol version ${result.protocolVersion}`);
18440
+ }
18441
+ if (!result.signedMessage) {
18442
+ throw new Error("OpenKey result must include signedMessage");
18443
+ }
18444
+ if (!prepared.siwe) {
18445
+ throw new Error(
18446
+ "signInWithOpenKeyResult requires prepared.siwe (the SDK-generated SIWE the sign-in was proposed with)"
18447
+ );
18448
+ }
18449
+ const expectedAddress = (0, import_sdk_core2.canonicalizeAddress)(await this.signer.getAddress());
18450
+ if ((0, import_sdk_core2.canonicalizeAddress)(result.address) !== expectedAddress) {
18451
+ throw new Error(
18452
+ `OpenKey returned signature from ${result.address} but expected ${expectedAddress}`
18453
+ );
18454
+ }
18455
+ let parsedSignedSiwe;
18456
+ try {
18457
+ parsedSignedSiwe = new import_siwe.SiweMessage(result.signedMessage);
18458
+ } catch (err) {
18459
+ throw new Error(
18460
+ `OpenKey signedMessage is not a parseable SIWE: ${err.message}`
18461
+ );
18462
+ }
18463
+ const verifyResult = await parsedSignedSiwe.verify(
18464
+ { signature: result.signature },
18465
+ { suppressExceptions: true }
18466
+ );
18467
+ if (!verifyResult.success) {
18468
+ const errData = verifyResult.error;
18469
+ const type = errData && typeof errData === "object" && "type" in errData ? errData.type : void 0;
18470
+ const details = errData && typeof errData === "object" && "expected" in errData ? ` (expected=${errData.expected}, received=${errData.received})` : "";
18471
+ throw new Error(
18472
+ `OpenKey signature verification failed${type ? `: ${type}` : ""}${details}`
18473
+ );
18474
+ }
18475
+ if ((0, import_sdk_core2.canonicalizeAddress)(parsedSignedSiwe.address) !== expectedAddress) {
18476
+ throw new Error(
18477
+ `OpenKey signedMessage.address (${parsedSignedSiwe.address}) does not match local signer address (${expectedAddress})`
18478
+ );
18479
+ }
18480
+ const originalHasRecap = /(?:^|\n)-\s+urn:recap:/i.test(prepared.siwe);
18481
+ const originalFields = (0, import_sdk_core2.extractImmutableSiweFields)(prepared.siwe);
18482
+ const signedFields = (0, import_sdk_core2.extractImmutableSiweFields)(result.signedMessage);
18483
+ const changedFields = (0, import_sdk_core2.diffImmutableSiweFields)(originalFields, signedFields, {
18484
+ originalHasRecap
18485
+ });
18486
+ if (changedFields.length > 0) {
18487
+ throw new Error(
18488
+ `OpenKey signedMessage altered immutable SIWE fields: ${changedFields.join(", ")}`
18489
+ );
18490
+ }
18491
+ let originalCaps;
18492
+ let signedCaps;
18493
+ try {
18494
+ originalCaps = (0, import_sdk_core2.extractRecapAttenuations)(prepared.siwe);
18495
+ } catch (err) {
18496
+ throw new Error(
18497
+ `Failed to decode prepared SIWE recap: ${err.message}`
18498
+ );
18499
+ }
18500
+ try {
18501
+ signedCaps = (0, import_sdk_core2.extractRecapAttenuations)(result.signedMessage);
18502
+ } catch (err) {
18503
+ throw new Error(
18504
+ `Failed to decode OpenKey signedMessage recap: ${err.message}`
18505
+ );
18506
+ }
18507
+ const unauthorized = (0, import_sdk_core2.unauthorizedRecapCapabilities)(signedCaps, originalCaps);
18508
+ if (unauthorized.length > 0) {
18509
+ const previewLimit = 5;
18510
+ const preview = unauthorized.slice(0, previewLimit).map((u) => `${u.resource}::${u.action}`).join(", ");
18511
+ const overflow = unauthorized.length > previewLimit ? ` (and ${unauthorized.length - previewLimit} more)` : "";
18512
+ throw new Error(
18513
+ `OpenKey signedMessage broadened authorization beyond the original request: ${preview}${overflow}`
18514
+ );
18515
+ }
18516
+ if (!result.signedMessage.includes(prepared.spaceId)) {
18517
+ throw new Error(
18518
+ `OpenKey signedMessage does not reference the expected spaceId ${prepared.spaceId}`
18519
+ );
18520
+ }
18521
+ const grantedPairs = /* @__PURE__ */ new Set();
18522
+ const grantedFourPartIndex = /* @__PURE__ */ new Map();
18523
+ for (const [resource, actions] of Object.entries(signedCaps)) {
18524
+ const { space, path } = (0, import_sdk_core2.parseCanonicalRecapResource)(resource);
18525
+ for (const action of Object.keys(actions)) {
18526
+ grantedPairs.add(`${resource}\0${action}`);
18527
+ const slashIdx = action.indexOf("/");
18528
+ const service = slashIdx > 0 ? action.slice(0, slashIdx) : "";
18529
+ if (service) {
18530
+ grantedFourPartIndex.set(
18531
+ `${service}\0${space}\0${path}\0${action}`,
18532
+ `${resource}\0${action}`
18533
+ );
18534
+ }
18535
+ }
18536
+ }
18537
+ const isStructurallyRequired = (action) => action === "tinycloud.capabilities/read" || action === "capabilities/read";
18538
+ const nonRequiredGrantedPairs = /* @__PURE__ */ new Set();
18539
+ for (const pair of grantedPairs) {
18540
+ const idx = pair.indexOf("\0");
18541
+ const action = idx >= 0 ? pair.slice(idx + 1) : pair;
18542
+ if (!isStructurallyRequired(action)) {
18543
+ nonRequiredGrantedPairs.add(pair);
18544
+ }
18545
+ }
18546
+ const allSignedGrantedPairs = new Set(grantedPairs);
18547
+ const seenRawKeys = /* @__PURE__ */ new Set();
18548
+ for (const rawKey of result.selectedActionKeys) {
18549
+ if (seenRawKeys.has(rawKey)) {
18550
+ throw new Error(
18551
+ `OpenKey selectedActionKeys contains duplicate entry ${rawKey}`
18552
+ );
18553
+ }
18554
+ seenRawKeys.add(rawKey);
18555
+ }
18556
+ const selectedResourceActionPairs = /* @__PURE__ */ new Set();
18557
+ for (const rawKey of result.selectedActionKeys) {
18558
+ const parts = rawKey.split("\0");
18559
+ if (parts.length !== 4) {
18560
+ throw new Error(
18561
+ `OpenKey selectedActionKeys entry is malformed (expected canonical 4-part service\\0space\\0path\\0ability): ${rawKey}`
18562
+ );
18563
+ }
18564
+ const canonical = grantedFourPartIndex.get(rawKey);
18565
+ if (!canonical) {
18566
+ throw new Error(
18567
+ `OpenKey selectedActionKeys entry ${rawKey} is not covered by any granted capability`
18568
+ );
18569
+ }
18570
+ selectedResourceActionPairs.add(canonical);
18571
+ }
18572
+ if (nonRequiredGrantedPairs.size > 0) {
18573
+ const missingPairs = [];
18574
+ for (const pair of nonRequiredGrantedPairs) {
18575
+ if (!selectedResourceActionPairs.has(pair)) {
18576
+ missingPairs.push(pair);
18577
+ }
18578
+ }
18579
+ if (missingPairs.length > 0) {
18580
+ const previewLimit = 5;
18581
+ const preview = missingPairs.slice(0, previewLimit).map((p) => p.replace("\0", "::")).join(", ");
18582
+ const overflow = missingPairs.length > previewLimit ? ` (and ${missingPairs.length - previewLimit} more)` : "";
18583
+ throw new Error(
18584
+ `OpenKey selectedActionKeys is missing entries for signedMessage capabilities: ${preview}${overflow}`
18585
+ );
18586
+ }
18587
+ }
18588
+ if (allSignedGrantedPairs.size > 0 && result.permissions.length === 0) {
18589
+ throw new Error(
18590
+ "OpenKey permissions is empty but signedMessage carries capabilities \u2014 refusing to accept a session without an authoritative grant list"
18591
+ );
18592
+ }
18593
+ const permissionCanonicalKeys = /* @__PURE__ */ new Set();
18594
+ const permissionPairsCovered = /* @__PURE__ */ new Set();
18595
+ for (const perm of result.permissions) {
18596
+ const { service, space, path, actions } = perm;
18597
+ if (typeof service !== "string" || typeof space !== "string" || typeof path !== "string") {
18598
+ throw new Error(
18599
+ `OpenKey permissions entry has non-string service/space/path (service=${service}, space=${space}, path=${path})`
18600
+ );
18601
+ }
18602
+ if (!Array.isArray(actions) || actions.length === 0) {
18603
+ throw new Error(
18604
+ `OpenKey permissions entry (${service} ${space} ${path}) has empty actions \u2014 a capability-bearing entry must list at least one action`
18605
+ );
18606
+ }
18607
+ const canonicalKey = `${service}\0${space}\0${path}`;
18608
+ if (permissionCanonicalKeys.has(canonicalKey)) {
18609
+ throw new Error(
18610
+ `OpenKey permissions has duplicate entry for (${service} ${space} ${path})`
18611
+ );
18612
+ }
18613
+ permissionCanonicalKeys.add(canonicalKey);
18614
+ let expectedResource;
18615
+ if (space.startsWith("tinycloud:")) {
18616
+ const shortService = service.startsWith("tinycloud.") ? service.slice("tinycloud.".length) : service;
18617
+ expectedResource = path ? `${space}/${shortService}/${path}` : `${space}/${shortService}`;
18618
+ } else {
18619
+ if (path) {
18620
+ throw new Error(
18621
+ `OpenKey permissions entry (${service} ${space} ${path}) carries a path on a non-tinycloud: resource URI, which is not a recognized encoding`
18622
+ );
18623
+ }
18624
+ expectedResource = space;
18625
+ }
18626
+ let matchedResource;
18627
+ if (signedCaps[expectedResource] !== void 0) {
18628
+ matchedResource = expectedResource;
18629
+ } else {
18630
+ throw new Error(
18631
+ `OpenKey permissions entry (${service} ${space} ${path}) claims grants not confirmed in signedMessage capabilities`
18632
+ );
18633
+ }
18634
+ const grantedActionsForResource = new Set(
18635
+ Object.keys(signedCaps[matchedResource])
18636
+ );
18637
+ for (const action of actions) {
18638
+ if (typeof action !== "string" || !action) {
18639
+ throw new Error(
18640
+ `OpenKey permissions entry (${service} ${space} ${path}) has non-string action ${String(action)}`
18641
+ );
18642
+ }
18643
+ if (!grantedActionsForResource.has(action)) {
18644
+ throw new Error(
18645
+ `OpenKey permissions entry (${service} ${space} ${path}) claims action ${action} not present in signedMessage capabilities`
18646
+ );
18647
+ }
18648
+ permissionPairsCovered.add(`${matchedResource}\0${action}`);
18649
+ }
18650
+ }
18651
+ if (allSignedGrantedPairs.size > 0) {
18652
+ const missingFromPerms = [];
18653
+ for (const pair of allSignedGrantedPairs) {
18654
+ if (!permissionPairsCovered.has(pair)) {
18655
+ missingFromPerms.push(pair);
18656
+ }
18657
+ }
18658
+ if (missingFromPerms.length > 0) {
18659
+ const previewLimit = 5;
18660
+ const preview = missingFromPerms.slice(0, previewLimit).map((p) => p.replace("\0", "::")).join(", ");
18661
+ const overflow = missingFromPerms.length > previewLimit ? ` (and ${missingFromPerms.length - previewLimit} more)` : "";
18662
+ throw new Error(
18663
+ `OpenKey permissions is missing entries for signedMessage capabilities: ${preview}${overflow}`
18664
+ );
18665
+ }
18666
+ const extrasInPerms = [];
18667
+ for (const pair of permissionPairsCovered) {
18668
+ if (!allSignedGrantedPairs.has(pair)) {
18669
+ extrasInPerms.push(pair);
18670
+ }
18671
+ }
18672
+ if (extrasInPerms.length > 0) {
18673
+ const previewLimit = 5;
18674
+ const preview = extrasInPerms.slice(0, previewLimit).map((p) => p.replace("\0", "::")).join(", ");
18675
+ const overflow = extrasInPerms.length > previewLimit ? ` (and ${extrasInPerms.length - previewLimit} more)` : "";
18676
+ throw new Error(
18677
+ `OpenKey permissions contains entries not present in the signed authority: ${preview}${overflow}`
18678
+ );
18679
+ }
18680
+ }
18681
+ void nonRequiredGrantedPairs;
18682
+ return this.signInWithPreparedSession(
18683
+ {
18684
+ siwe: result.signedMessage,
18685
+ jwk: prepared.jwk,
18686
+ spaceId: prepared.spaceId,
18687
+ verificationMethod: prepared.verificationMethod
18688
+ },
18689
+ result.signature,
18690
+ keyId,
18691
+ jwk
18692
+ );
18693
+ }
18246
18694
  /**
18247
18695
  * Clear persisted session data.
18248
18696
  */
@@ -18733,6 +19181,9 @@ var DEFAULT_HOST = "https://node.tinycloud.xyz";
18733
19181
  var DEFAULT_ENCRYPTION_NETWORK_NAME = "default";
18734
19182
  var NETWORK_CREATE_ACTION2 = import_sdk_core7.ENCRYPTION.NETWORK_CREATE;
18735
19183
  var DECRYPT_ACTION2 = import_sdk_core7.ENCRYPTION.DECRYPT;
19184
+ var BOOTSTRAP_COMPLETION_MARKER_KEY = "system/bootstrap/complete";
19185
+ var BOOTSTRAP_COMPLETION_MARKER_VERSION = 1;
19186
+ var ACCEPTED_MARKER_VERSIONS = [BOOTSTRAP_COMPLETION_MARKER_VERSION];
18736
19187
  var NETWORK_ADMIN_TYPE = "tinycloud.encryption.network-admin/v1";
18737
19188
  function isSecretPayload(value) {
18738
19189
  return typeof value === "object" && value !== null && "value" in value && typeof value.value === "string" && "createdAt" in value && typeof value.createdAt === "string" && "updatedAt" in value && typeof value.updatedAt === "string";
@@ -18756,6 +19207,18 @@ var ROOT_DELEGATION_ACTIONS = [
18756
19207
  import_sdk_core7.DUCKDB.ALL
18757
19208
  ];
18758
19209
  var DEFAULT_SESSION_EXPIRATION_MS = import_sdk_core7.EXPIRY.SESSION_MS;
19210
+ function unifiedRootAttenuation(capabilities) {
19211
+ const attenuation = {};
19212
+ for (const capability of capabilities) {
19213
+ if (attenuation[capability.resource] !== void 0) throw new Error("unified owner root contains a duplicate resource");
19214
+ attenuation[capability.resource] = capability.kind === "encryption" ? { [capability.action]: [{}] } : Object.fromEntries(capability.actions.map((action) => [action, [{
19215
+ type: "xyz.tinycloud.resource/selector",
19216
+ kind: capability.selector,
19217
+ value: capability.resource
19218
+ }]]));
19219
+ }
19220
+ return attenuation;
19221
+ }
18759
19222
  function decodeAuthorizationBytes(authorization) {
18760
19223
  const encoded = authorization.replace(/^Bearer /i, "");
18761
19224
  const match = /^([A-Za-z0-9_-]+)(={1,2})?$/.exec(encoded);
@@ -18775,6 +19238,9 @@ function decodeAuthorizationBytes(authorization) {
18775
19238
  function isOpenKeyAutoSignStrategy(strategy) {
18776
19239
  return strategy?.openKeyAutoSign === true;
18777
19240
  }
19241
+ function openKeyApproval(strategy) {
19242
+ return strategy?.openKeyRequestApproval;
19243
+ }
18778
19244
  function isInteractiveSigner(config) {
18779
19245
  if (config.privateKey) {
18780
19246
  return false;
@@ -19132,6 +19598,8 @@ var _TinyCloudNode = class _TinyCloudNode {
19132
19598
  * hosting is confirmed against the session that was active at the time.
19133
19599
  */
19134
19600
  this.confirmedHostedSpaceIds = /* @__PURE__ */ new Set();
19601
+ /** Serializes account-registry writes for the active node instance. */
19602
+ this.accountRegistryTail = Promise.resolve();
19135
19603
  /**
19136
19604
  * True when the last signIn() detected an interactive signer and skipped
19137
19605
  * client-side bootstrap. Apps can read this to know whether bootstrap was
@@ -19140,9 +19608,12 @@ var _TinyCloudNode = class _TinyCloudNode {
19140
19608
  this._bootstrapSkipped = false;
19141
19609
  /**
19142
19610
  * Outcome of the last signIn()'s account-bootstrap attempt. `skipped` is
19143
- * true when bootstrap did not complete (interactive signer, auto-sign
19144
- * denied, or a bootstrap step failed); `reason` carries the cause so apps
19145
- * can surface a "finish account setup" call-to-action. `warnings` is
19611
+ * true when bootstrap did not complete in this invocation (interactive signer,
19612
+ * auto-bootstrap disabled, already provisioned, or a decision/provisioning
19613
+ * step failed). `already-provisioned` means the bootstrap was not run here;
19614
+ * it does not mean a prior provisioning attempt failed to complete.
19615
+ * `reason` carries the cause so apps can surface a "finish account setup"
19616
+ * call-to-action. `warnings` is
19146
19617
  * present when bootstrap completed, but one or more steps recovered from
19147
19618
  * an ambiguous failure (e.g. a KV batch write reconciled via per-space
19148
19619
  * fallback) — clean vs. recovered runs are otherwise byte-identical.
@@ -19325,7 +19796,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19325
19796
  nonce: config.nonce,
19326
19797
  siweConfig: config.siweConfig,
19327
19798
  manifest: useBootstrapSignInRequest ? void 0 : config.manifest,
19328
- capabilityRequest: useBootstrapSignInRequest ? import_sdk_core7.BOOTSTRAP_SESSION_REQUESTS.default : config.capabilityRequest,
19799
+ capabilityRequest: useBootstrapSignInRequest ? config.includeAccountRegistryPermissions === false ? import_sdk_core7.BOOTSTRAP_DEFAULT_ONLY_SESSION_REQUEST : import_sdk_core7.BOOTSTRAP_SESSION_REQUESTS.default : config.capabilityRequest,
19329
19800
  includeAccountRegistryPermissions: useBootstrapSignInRequest ? false : config.includeAccountRegistryPermissions
19330
19801
  });
19331
19802
  this.tc = new import_sdk_core7.TinyCloud(this.auth, {
@@ -19415,6 +19886,15 @@ var _TinyCloudNode = class _TinyCloudNode {
19415
19886
  get sessionDid() {
19416
19887
  return this.sessionManager.getDID(this.sessionKeyId);
19417
19888
  }
19889
+ /** Bare did:key principal used as the credential subject and holder binding. */
19890
+ get credentialHolderDid() {
19891
+ return this.sessionDid.split("#", 1)[0];
19892
+ }
19893
+ /** Canonical verification method for the active credential holder key. */
19894
+ get credentialHolderKid() {
19895
+ const holder = this.credentialHolderDid;
19896
+ return `${holder}#${holder.slice("did:key:".length)}`;
19897
+ }
19418
19898
  /**
19419
19899
  * Return the current session's signed ReCap capabilities after the session
19420
19900
  * has been authenticated or restored. This is intentionally distinct from
@@ -19516,6 +19996,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19516
19996
  "Cannot signIn() in session-only mode. Provide a privateKey in config to create your own space."
19517
19997
  );
19518
19998
  }
19999
+ await this.accountRegistryTail;
19519
20000
  await this.wasmBindings.ensureInitialized?.();
19520
20001
  this._address = (0, import_sdk_core7.canonicalizeAddress)(await this.signer.getAddress());
19521
20002
  this._chainId = await this.signer.getChainId();
@@ -19568,6 +20049,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19568
20049
  this._bootstrapSkipped = false;
19569
20050
  this._bootstrapStatus = { skipped: false };
19570
20051
  if (this.config.autoBootstrapAccount === false) {
20052
+ this._bootstrapStatus = { skipped: true, reason: "auto-bootstrap-disabled" };
19571
20053
  return false;
19572
20054
  }
19573
20055
  if (!this.auth || !this._address) {
@@ -19582,11 +20064,14 @@ var _TinyCloudNode = class _TinyCloudNode {
19582
20064
  return false;
19583
20065
  }
19584
20066
  const steps = (0, import_sdk_core7.bootstrapSteps)(this._address, this._chainId);
19585
- if (!await this.isFreshBootstrapAccount(steps)) {
19586
- return false;
19587
- }
19588
20067
  try {
19589
- const warnings = await this.runAccountBootstrap(steps);
20068
+ const decision = await this.resolveBootstrapDecision(steps);
20069
+ if (decision.action === "skip") {
20070
+ this._bootstrapStatus = { skipped: true, reason: "already-provisioned" };
20071
+ return false;
20072
+ }
20073
+ const warnings = await this.runAccountBootstrap(steps, { mode: decision.mode });
20074
+ await this.writeBootstrapCompletionMarker(steps);
19590
20075
  if (warnings.length > 0) {
19591
20076
  this._bootstrapStatus = { skipped: false, warnings };
19592
20077
  this.notificationHandler.warning(
@@ -19605,32 +20090,92 @@ var _TinyCloudNode = class _TinyCloudNode {
19605
20090
  }
19606
20091
  return true;
19607
20092
  }
19608
- async isFreshBootstrapAccount(steps) {
20093
+ async resolveBootstrapDecision(steps) {
19609
20094
  const enshrinedSpaceIds = /* @__PURE__ */ new Set();
19610
20095
  for (const step of steps) {
19611
20096
  if (step.kind === "session") {
19612
20097
  enshrinedSpaceIds.add(step.spaceId);
19613
20098
  }
19614
20099
  }
19615
- const skipped = this.auth.lastActivationSkippedSpaceIds;
20100
+ const auth = this.auth;
20101
+ if (!auth) {
20102
+ throw new Error("Account bootstrap requires an active wallet session");
20103
+ }
20104
+ const skipped = auth.lastActivationSkippedSpaceIds;
19616
20105
  if (skipped.some((spaceId) => enshrinedSpaceIds.has(spaceId))) {
19617
- return true;
20106
+ return { action: "run", mode: "fresh" };
19618
20107
  }
19619
20108
  try {
19620
- const indexed = await this.account.index.spaces.list();
19621
- if (indexed.ok && indexed.data.length === 0) {
19622
- return true;
20109
+ const accountSpaceId = this.ownedSpaceId(import_sdk_core7.ACCOUNT_REGISTRY_SPACE);
20110
+ const markerPermission = {
20111
+ service: "tinycloud.kv",
20112
+ space: accountSpaceId,
20113
+ path: BOOTSTRAP_COMPLETION_MARKER_KEY,
20114
+ actions: ["get"]
20115
+ };
20116
+ if (!this.hasRuntimePermissions([markerPermission])) {
20117
+ return { action: "run", mode: "repair" };
19623
20118
  }
19624
- } catch {
20119
+ const marker = await this.readBootstrapCompletionMarker();
20120
+ if (marker.ok) {
20121
+ if (this.isAcceptedBootstrapCompletionMarker(marker.data.data)) {
20122
+ return { action: "skip" };
20123
+ }
20124
+ return { action: "run", mode: "repair" };
20125
+ }
20126
+ if (marker.error.code === import_sdk_core7.ErrorCodes.KV_NOT_FOUND && this.markerReadIsUnhostedSpace(marker.error.meta)) {
20127
+ return { action: "run", mode: "fresh" };
20128
+ }
20129
+ } catch (err) {
20130
+ const reason = err instanceof Error ? err.message : String(err);
20131
+ this.notificationHandler.warning(
20132
+ `Account bootstrap decision failed; attempting one bounded repair: ${reason}`
20133
+ );
20134
+ console.warn(`[TinyCloudNode] account bootstrap decision failed: ${reason}`);
20135
+ return { action: "run", mode: "repair" };
19625
20136
  }
19626
- try {
19627
- const spaces = await this.account.spaces.list();
19628
- return spaces.ok && spaces.data.length === 0;
19629
- } catch {
19630
- return false;
20137
+ return { action: "run", mode: "repair" };
20138
+ }
20139
+ async readBootstrapCompletionMarker() {
20140
+ return this.kvForSpace(this.ownedSpaceId(import_sdk_core7.ACCOUNT_REGISTRY_SPACE)).get(
20141
+ BOOTSTRAP_COMPLETION_MARKER_KEY
20142
+ );
20143
+ }
20144
+ async writeBootstrapCompletionMarker(steps) {
20145
+ if (!this._address) {
20146
+ throw new Error("Bootstrap completion marker requires an active wallet session");
20147
+ }
20148
+ const canonicalStepIds = new Set(
20149
+ (0, import_sdk_core7.bootstrapSteps)(this._address, this._chainId).map((step) => step.id)
20150
+ );
20151
+ const providedStepIds = new Set(steps.map((step) => step.id));
20152
+ if (canonicalStepIds.size !== providedStepIds.size || [...canonicalStepIds].some((stepId) => !providedStepIds.has(stepId))) {
20153
+ throw new Error("Bootstrap completion marker requires the canonical bootstrap step set");
20154
+ }
20155
+ const marker = {
20156
+ v: BOOTSTRAP_COMPLETION_MARKER_VERSION,
20157
+ stepIds: [...canonicalStepIds],
20158
+ completedAt: (/* @__PURE__ */ new Date()).toISOString()
20159
+ };
20160
+ const written = await this.kvForSpace(this.ownedSpaceId(import_sdk_core7.ACCOUNT_REGISTRY_SPACE)).put(
20161
+ BOOTSTRAP_COMPLETION_MARKER_KEY,
20162
+ marker
20163
+ );
20164
+ if (!written.ok) {
20165
+ throw new Error(`Failed to write bootstrap completion marker: ${written.error.message}`);
19631
20166
  }
19632
20167
  }
19633
- async runAccountBootstrap(steps) {
20168
+ isAcceptedBootstrapCompletionMarker(value) {
20169
+ if (typeof value !== "object" || value === null) return false;
20170
+ const entries = Object.entries(value);
20171
+ const markerValue = (name) => entries.find(([key2]) => key2 === name)?.[1];
20172
+ const version23 = markerValue("v");
20173
+ return typeof version23 === "number" && Number.isInteger(version23) && ACCEPTED_MARKER_VERSIONS.some((acceptedVersion) => acceptedVersion === version23);
20174
+ }
20175
+ markerReadIsUnhostedSpace(meta) {
20176
+ return meta?.status === 404;
20177
+ }
20178
+ async runAccountBootstrap(steps, { mode } = { mode: "fresh" }) {
19634
20179
  if (!this.auth || !this._address) {
19635
20180
  throw new Error("Account bootstrap requires an active wallet session");
19636
20181
  }
@@ -19731,9 +20276,8 @@ var _TinyCloudNode = class _TinyCloudNode {
19731
20276
  if (step.kind === "seed-applications") {
19732
20277
  const registered = await this.account.applications.register(
19733
20278
  step.manifests.length > 0 ? [...step.manifests] : import_sdk_core7.TINYCLOUD_SECRETS_BOOTSTRAP_MANIFEST,
19734
- // Bootstrap only runs on an account with no registry records, so the
19735
- // manifest-hash pre-read is definitionally a miss. Skip it; the write
19736
- // it guards is an INSERT OR REPLACE and is safe to repeat.
20279
+ // The write is an INSERT OR REPLACE and is safe to repeat during a
20280
+ // repair, so skip the manifest-hash pre-read.
19737
20281
  { assumeUnregistered: true }
19738
20282
  );
19739
20283
  if (!registered.ok) {
@@ -19741,7 +20285,7 @@ var _TinyCloudNode = class _TinyCloudNode {
19741
20285
  }
19742
20286
  }
19743
20287
  if (step.kind === "encryption-network-create") {
19744
- await this.ensureEncryptionNetwork(step.networkId, { assumeMissing: true });
20288
+ await this.ensureEncryptionNetwork(step.networkId, { assumeMissing: mode === "fresh" });
19745
20289
  }
19746
20290
  if (step.kind === "secret-records-schema") {
19747
20291
  const db = this.sqlForSpace(step.spaceId).db(step.database);
@@ -19934,16 +20478,48 @@ var _TinyCloudNode = class _TinyCloudNode {
19934
20478
  }
19935
20479
  }
19936
20480
  scheduleAccountRegistrySync() {
19937
- void this.withAccountRegistryRetry(async () => {
19938
- void this.account.index.ensure();
19939
- await this.writeManifestRegistryRecords();
19940
- if (this.currentSessionCanListSpaces()) {
19941
- const spaces = await this.account.spaces.syncAccessible();
19942
- if (!spaces.ok) {
19943
- throw new Error(`Failed to sync account spaces: ${spaces.error.message}`);
20481
+ const session = this.currentTinyCloudSession();
20482
+ if (!session || this.pendingAccountRegistrySync?.session === session) {
20483
+ return;
20484
+ }
20485
+ const promise = this.enqueueAccountRegistryOperation(session, async () => {
20486
+ await this.withAccountRegistryRetry(async () => {
20487
+ if (this.currentTinyCloudSession() !== session) {
20488
+ return;
20489
+ }
20490
+ await this.account.index.ensure();
20491
+ if (this.currentTinyCloudSession() !== session) {
20492
+ return;
19944
20493
  }
20494
+ await this.writeManifestRegistryRecords();
20495
+ if (this.currentTinyCloudSession() !== session) {
20496
+ return;
20497
+ }
20498
+ if (this.currentSessionCanListSpaces()) {
20499
+ const spaces = await this.account.spaces.syncAccessible();
20500
+ if (!spaces.ok) {
20501
+ throw new Error(`Failed to sync account spaces: ${spaces.error.message}`);
20502
+ }
20503
+ }
20504
+ });
20505
+ });
20506
+ this.pendingAccountRegistrySync = { session, promise };
20507
+ const clearPendingSync = () => {
20508
+ if (this.pendingAccountRegistrySync?.promise === promise) {
20509
+ this.pendingAccountRegistrySync = void 0;
19945
20510
  }
20511
+ };
20512
+ void promise.then(clearPendingSync, clearPendingSync);
20513
+ }
20514
+ enqueueAccountRegistryOperation(session, task) {
20515
+ const operation = this.accountRegistryTail.then(async () => {
20516
+ if (this.currentTinyCloudSession() === session) {
20517
+ await task();
20518
+ }
20519
+ });
20520
+ this.accountRegistryTail = operation.catch(() => {
19946
20521
  });
20522
+ return operation;
19947
20523
  }
19948
20524
  /**
19949
20525
  * Whether the current primary session may invoke `tinycloud.space/list`.
@@ -20096,7 +20672,8 @@ var _TinyCloudNode = class _TinyCloudNode {
20096
20672
  * @returns The hosted space URI.
20097
20673
  */
20098
20674
  async hostOwnedSpace(name) {
20099
- if (!this.auth || !this.auth.tinyCloudSession) {
20675
+ const session = this.auth?.tinyCloudSession;
20676
+ if (!this.auth || !session) {
20100
20677
  throw new Error("Not signed in. Call signIn() first.");
20101
20678
  }
20102
20679
  const spaceId = this.ownedSpaceId(name);
@@ -20112,22 +20689,26 @@ var _TinyCloudNode = class _TinyCloudNode {
20112
20689
  }
20113
20690
  const activation = await (0, import_sdk_core7.activateSessionWithHost)(
20114
20691
  host,
20115
- this.auth.tinyCloudSession.delegationHeader
20692
+ session.delegationHeader
20116
20693
  );
20117
20694
  if (!activation.success || activation.skipped?.includes(spaceId)) {
20118
20695
  throw new Error(
20119
20696
  `Failed to activate session for owned space ${spaceId}: ${activation.error ?? "space was skipped"}`
20120
20697
  );
20121
20698
  }
20122
- void this.account.spaces.register({
20123
- spaceId,
20124
- name,
20125
- ownerDid: this.did,
20126
- type: "owned",
20127
- permissions: ["*"],
20128
- status: "active"
20129
- }).catch(() => {
20130
- });
20699
+ try {
20700
+ await this.enqueueAccountRegistryOperation(session, async () => {
20701
+ await this.account.spaces.register({
20702
+ spaceId,
20703
+ name,
20704
+ ownerDid: this.did,
20705
+ type: "owned",
20706
+ permissions: ["*"],
20707
+ status: "active"
20708
+ });
20709
+ });
20710
+ } catch {
20711
+ }
20131
20712
  return spaceId;
20132
20713
  }
20133
20714
  /**
@@ -20162,23 +20743,20 @@ var _TinyCloudNode = class _TinyCloudNode {
20162
20743
  if (!this.auth || !this.auth.tinyCloudSession) {
20163
20744
  throw new Error("Not signed in. Call signIn() first.");
20164
20745
  }
20746
+ await this.accountRegistryTail;
20165
20747
  const spaceId = this.ownedSpaceId(name);
20166
20748
  if (await this.isOwnedSpaceRegistered(spaceId)) {
20167
20749
  return spaceId;
20168
20750
  }
20169
- const hosted = await this.hostOwnedSpace(name);
20170
- try {
20171
- await this.account.spaces.register({
20172
- spaceId,
20173
- name,
20174
- ownerDid: this.did,
20175
- type: "owned",
20176
- permissions: ["*"],
20177
- status: "active"
20178
- });
20179
- } catch {
20751
+ return this.hostOwnedSpace(name);
20752
+ }
20753
+ /** Resolve and authenticate the owner encoded by an owned-space URI. */
20754
+ credentialSpaceOwnerDid(spaceId) {
20755
+ const ownerDid = this.ownerDidFromSpaceId(spaceId);
20756
+ if (ownerDid === void 0 || !didPrincipalMatches2(ownerDid, this.did)) {
20757
+ throw new Error("Credential space owner does not match the active TinyCloud owner");
20180
20758
  }
20181
- return hosted;
20759
+ return ownerDid;
20182
20760
  }
20183
20761
  /**
20184
20762
  * Check whether an owned space is already registered/hosted by consulting the
@@ -20723,6 +21301,106 @@ var _TinyCloudNode = class _TinyCloudNode {
20723
21301
  if (session === void 0) throw new Error("Not signed in. Call signIn() first.");
20724
21302
  return signBytesWithJwk(bytes, session.jwk);
20725
21303
  }
21304
+ /** Author a proofless Policy/v3 owner root with the authenticated holder key. */
21305
+ async createUnifiedOwnerRoot(input) {
21306
+ const ownerDid = this.credentialHolderDid;
21307
+ if (input.ownerDid !== ownerDid) throw new Error("unified owner root signer does not match owner DID");
21308
+ const facts = {
21309
+ role: input.role,
21310
+ mode: input.role === "policy-authority" ? "policy-source" : "conditional-mint",
21311
+ ownerDid,
21312
+ policyId: input.policyId,
21313
+ policyDigestHex: input.policyDigestHex,
21314
+ policyCid: input.policyCid,
21315
+ contentSourceDigestHex: input.contentSourceDigestHex,
21316
+ capabilityCeilingHashHex: input.capabilityCeilingHashHex,
21317
+ nativeProjectionHashHex: input.nativeProjectionHashHex,
21318
+ nodeAudience: input.nodeAudience,
21319
+ ...input.role === "policy-enforcement" ? { enforcerDid: input.audienceDid } : {}
21320
+ };
21321
+ const root = await (0, import_sdk_core7.signCompactUcanRootAuthorization)({
21322
+ issuerDid: ownerDid,
21323
+ audienceDid: input.audienceDid,
21324
+ attenuation: unifiedRootAttenuation(input.capabilities),
21325
+ facts: [facts],
21326
+ notBefore: Math.floor(input.notBefore.getTime() / 1e3),
21327
+ expiresAt: Math.floor(input.expiresAt.getTime() / 1e3),
21328
+ nonce: base64UrlEncode(crypto.getRandomValues(new Uint8Array(16))),
21329
+ sign: (bytes) => this.signSessionBytes(bytes)
21330
+ });
21331
+ const kv = input.capabilities.find((capability) => capability.kind === "kv");
21332
+ const resource = kv?.resource ?? "";
21333
+ const marker = resource.indexOf("/kv/");
21334
+ return {
21335
+ cid: root.cid,
21336
+ delegationHeader: { Authorization: root.authorization },
21337
+ delegateDID: input.audienceDid,
21338
+ spaceId: marker === -1 ? "" : resource.slice("tinycloud://".length, marker),
21339
+ path: marker === -1 ? resource : resource.slice(marker + 4),
21340
+ actions: kv?.actions ?? [],
21341
+ expiry: input.expiresAt
21342
+ };
21343
+ }
21344
+ /** Apply only an already-enabled signing policy to the exact credential request. */
21345
+ async autoSignCredentialBytes(bytes) {
21346
+ const strategy = this.config.signStrategy;
21347
+ if (strategy?.type === "auto-sign") return this.signSessionBytes(bytes);
21348
+ if (strategy?.type !== "callback" || !isOpenKeyAutoSignStrategy(strategy)) return void 0;
21349
+ const session = this.currentTinyCloudSession() ?? this._activeServiceSession;
21350
+ if (session === void 0) throw new Error("Not signed in. Call signIn() first.");
21351
+ const request = {
21352
+ address: this.address ?? "",
21353
+ chainId: this.session?.chainId ?? this._chainId,
21354
+ message: new TextDecoder().decode(bytes),
21355
+ type: "message",
21356
+ purpose: "message"
21357
+ };
21358
+ const decision = await strategy.handler(request);
21359
+ if (!decision.approved) {
21360
+ if (decision.needsApproval) return void 0;
21361
+ throw new Error(decision.reason ?? "OpenKey automatic credential signing was rejected");
21362
+ }
21363
+ if (typeof decision.signature !== "string" || !await (0, import_sdk_core7.verifyEip191MessageSignature)(
21364
+ request.message,
21365
+ decision.signature,
21366
+ request.address
21367
+ )) {
21368
+ throw new Error("OpenKey credential signature evidence is invalid");
21369
+ }
21370
+ return this.signSessionBytes(bytes);
21371
+ }
21372
+ /** Invoke the configured interactive approval strategy before session signing. */
21373
+ async approveCredentialBytes(bytes) {
21374
+ const strategy = this.config.signStrategy;
21375
+ const session = this.currentTinyCloudSession() ?? this._activeServiceSession;
21376
+ if (session === void 0) throw new Error("Not signed in. Call signIn() first.");
21377
+ const request = {
21378
+ address: this.address ?? "",
21379
+ chainId: this.session?.chainId ?? this._chainId,
21380
+ message: new TextDecoder().decode(bytes),
21381
+ type: "message",
21382
+ purpose: "message"
21383
+ };
21384
+ const approval = isOpenKeyAutoSignStrategy(strategy) ? openKeyApproval(strategy) : strategy?.type === "callback" ? strategy.handler : void 0;
21385
+ if (approval) {
21386
+ const decision = await approval(request);
21387
+ if (!decision.approved) throw new Error(decision.reason ?? "Credential signing was rejected");
21388
+ if (decision.signature !== void 0 && !await (0, import_sdk_core7.verifyEip191MessageSignature)(
21389
+ request.message,
21390
+ decision.signature,
21391
+ request.address
21392
+ )) {
21393
+ throw new Error("OpenKey credential signature evidence is invalid");
21394
+ }
21395
+ return this.signSessionBytes(bytes);
21396
+ }
21397
+ if (strategy?.type === "auto-reject") throw new Error("Credential signing was rejected");
21398
+ if (this.config.signer) {
21399
+ await this.config.signer.signMessage(request.message);
21400
+ return this.signSessionBytes(bytes);
21401
+ }
21402
+ throw new Error("Interactive OpenKey approval is not configured");
21403
+ }
20726
21404
  /** Bind a session-signed invocation to the canonical service audience. */
20727
21405
  async bindInvocationAudience(authorization, audience) {
20728
21406
  const session = this.currentTinyCloudSession() ?? this._activeServiceSession;
@@ -20790,7 +21468,7 @@ var _TinyCloudNode = class _TinyCloudNode {
20790
21468
  nonce: this.config.nonce,
20791
21469
  siweConfig: this.config.siweConfig,
20792
21470
  manifest: useBootstrapSignInRequest ? void 0 : this.config.manifest,
20793
- capabilityRequest: useBootstrapSignInRequest ? import_sdk_core7.BOOTSTRAP_SESSION_REQUESTS.default : this.config.capabilityRequest,
21471
+ capabilityRequest: useBootstrapSignInRequest ? this.config.includeAccountRegistryPermissions === false ? import_sdk_core7.BOOTSTRAP_DEFAULT_ONLY_SESSION_REQUEST : import_sdk_core7.BOOTSTRAP_SESSION_REQUESTS.default : this.config.capabilityRequest,
20794
21472
  includeAccountRegistryPermissions: useBootstrapSignInRequest ? false : this.config.includeAccountRegistryPermissions
20795
21473
  });
20796
21474
  this.tc = new import_sdk_core7.TinyCloud(this.auth, {
@@ -20843,7 +21521,7 @@ var _TinyCloudNode = class _TinyCloudNode {
20843
21521
  nonce: this.config.nonce,
20844
21522
  siweConfig: this.config.siweConfig,
20845
21523
  manifest: useBootstrapSignInRequest ? void 0 : this.config.manifest,
20846
- capabilityRequest: useBootstrapSignInRequest ? import_sdk_core7.BOOTSTRAP_SESSION_REQUESTS.default : this.config.capabilityRequest,
21524
+ capabilityRequest: useBootstrapSignInRequest ? this.config.includeAccountRegistryPermissions === false ? import_sdk_core7.BOOTSTRAP_DEFAULT_ONLY_SESSION_REQUEST : import_sdk_core7.BOOTSTRAP_SESSION_REQUESTS.default : this.config.capabilityRequest,
20847
21525
  includeAccountRegistryPermissions: useBootstrapSignInRequest ? false : this.config.includeAccountRegistryPermissions
20848
21526
  });
20849
21527
  this.tc = new import_sdk_core7.TinyCloud(this.auth, {
@@ -23592,35 +24270,44 @@ var _TinyCloudNode = class _TinyCloudNode {
23592
24270
  if (parentDelegation.disableSubDelegation) {
23593
24271
  throw new Error("Parent delegation does not allow sub-delegation");
23594
24272
  }
23595
- if (!params.path.startsWith(parentDelegation.path)) {
24273
+ const parentResources = parentDelegation.resources ?? [{
24274
+ service: parentDelegation.actions[0]?.split("/", 1)[0]?.replace(/^tinycloud\./, "") ?? "kv",
24275
+ space: parentDelegation.spaceId,
24276
+ path: parentDelegation.path,
24277
+ actions: parentDelegation.actions
24278
+ }];
24279
+ const requestedResources = params.resources ?? [{
24280
+ service: params.actions[0]?.split("/", 1)[0]?.replace(/^tinycloud\./, "") ?? "kv",
24281
+ space: parentDelegation.spaceId,
24282
+ path: params.path,
24283
+ actions: params.actions
24284
+ }];
24285
+ const resourceIsContained = (parent, child) => {
24286
+ const parentEncryption = parent.service === "encryption" || parent.service === "tinycloud.encryption";
24287
+ const childEncryption = child.service === "encryption" || child.service === "tinycloud.encryption";
24288
+ if (parentEncryption || childEncryption) return parentEncryption && childEncryption && parent.path === child.path && child.actions.every((action) => parent.actions.includes(action));
24289
+ const pathContained = parent.path === child.path || parent.path.endsWith("/") && child.path.startsWith(parent.path) || child.path.startsWith(`${parent.path}/`);
24290
+ return pathContained && child.actions.every((action) => parent.actions.includes(action));
24291
+ };
24292
+ if (requestedResources.length === 0 || requestedResources.some((child) => !parentResources.some((parent) => resourceIsContained(parent, child)))) {
23596
24293
  throw new Error(
23597
- `Sub-delegation path "${params.path}" must be within parent path "${parentDelegation.path}"`
24294
+ "Sub-delegation resources exceed the received delegation"
23598
24295
  );
23599
24296
  }
23600
- const parentActions = new Set(parentDelegation.actions);
23601
- for (const action of params.actions) {
23602
- if (!parentActions.has(action)) {
23603
- throw new Error(
23604
- `Sub-delegation action "${action}" is not in parent's actions: ${parentDelegation.actions.join(", ")}`
23605
- );
23606
- }
23607
- }
23608
24297
  const now = /* @__PURE__ */ new Date();
23609
24298
  const expiryMs = params.expiryMs ?? 60 * 60 * 1e3;
23610
24299
  const requestedExpiry = new Date(now.getTime() + expiryMs);
23611
24300
  const actualExpiry = requestedExpiry > parentDelegation.expiry ? parentDelegation.expiry : requestedExpiry;
23612
24301
  const abilities = {};
23613
- const kvActions = params.actions.filter((a) => a.startsWith("tinycloud.kv/"));
23614
- const sqlActions = params.actions.filter((a) => a.startsWith("tinycloud.sql/"));
23615
- const duckdbActions = params.actions.filter((a) => a.startsWith("tinycloud.duckdb/"));
23616
- if (kvActions.length > 0) {
23617
- abilities.kv = { [params.path]: kvActions };
23618
- }
23619
- if (sqlActions.length > 0) {
23620
- abilities.sql = { [params.path]: sqlActions };
23621
- }
23622
- if (duckdbActions.length > 0) {
23623
- abilities.duckdb = { [params.path]: duckdbActions };
24302
+ const rawAbilities = {};
24303
+ for (const resource of requestedResources) {
24304
+ const service = resource.service.replace(/^tinycloud\./, "");
24305
+ if (service === "encryption") {
24306
+ rawAbilities[resource.path] = [...resource.actions];
24307
+ continue;
24308
+ }
24309
+ abilities[service] ?? (abilities[service] = {});
24310
+ abilities[service][resource.path] = [...resource.actions];
23624
24311
  }
23625
24312
  const targetHost = parentDelegation.host ?? this.config.host;
23626
24313
  const prepared = this.wasmBindings.prepareSession({
@@ -23632,7 +24319,8 @@ var _TinyCloudNode = class _TinyCloudNode {
23632
24319
  expirationTime: actualExpiry.toISOString(),
23633
24320
  spaceId: parentDelegation.spaceId,
23634
24321
  delegateUri: params.delegateDID,
23635
- parents: [parentDelegation.cid]
24322
+ parents: [parentDelegation.cid],
24323
+ ...Object.keys(rawAbilities).length > 0 ? { rawAbilities } : {}
23636
24324
  });
23637
24325
  const signature2 = await this.signer.signMessage(prepared.siwe);
23638
24326
  const subDelegationSession = this.wasmBindings.completeSessionSetup({
@@ -23650,14 +24338,20 @@ var _TinyCloudNode = class _TinyCloudNode {
23650
24338
  cid: subDelegationSession.delegationCid,
23651
24339
  delegationHeader: subDelegationSession.delegationHeader,
23652
24340
  spaceId: parentDelegation.spaceId,
23653
- path: params.path,
23654
- actions: params.actions,
24341
+ path: requestedResources[0].path,
24342
+ actions: requestedResources[0].actions,
23655
24343
  disableSubDelegation: params.disableSubDelegation ?? false,
23656
24344
  expiry: actualExpiry,
23657
24345
  delegateDID: params.delegateDID,
23658
24346
  ownerAddress: parentDelegation.ownerAddress,
23659
24347
  chainId: parentDelegation.chainId,
23660
- host: targetHost
24348
+ host: targetHost,
24349
+ resources: requestedResources.map((resource) => ({
24350
+ service: resource.service.replace(/^tinycloud\./, ""),
24351
+ space: resource.service.replace(/^tinycloud\./, "") === "encryption" ? "encryption" : resource.space ?? parentDelegation.spaceId,
24352
+ path: resource.path,
24353
+ actions: [...resource.actions]
24354
+ }))
23661
24355
  };
23662
24356
  }
23663
24357
  };
@@ -23804,6 +24498,50 @@ var FileSessionStorage = class {
23804
24498
  }
23805
24499
  };
23806
24500
 
24501
+ // src/authorization/openKeyBridge.ts
24502
+ function wireOpenKeyAuthorize(openkey) {
24503
+ return async (input) => {
24504
+ const result = await openkey.authorizeTinyCloud({
24505
+ protocolVersion: 1,
24506
+ siwe: input.siwe,
24507
+ keyId: input.keyId,
24508
+ jwk: input.jwk,
24509
+ host: input.host,
24510
+ // Forward the presentation envelope verbatim — the OpenKey SDK
24511
+ // validates and size-bounds it before use.
24512
+ presentation: input.presentation
24513
+ });
24514
+ if (result.protocolVersion !== 1) {
24515
+ throw new Error(
24516
+ `wireOpenKeyAuthorize: OpenKey returned unsupported protocolVersion ${result.protocolVersion}`
24517
+ );
24518
+ }
24519
+ if (typeof result.signedMessage !== "string" || !result.signedMessage) {
24520
+ throw new Error(
24521
+ "wireOpenKeyAuthorize: OpenKey returned no signedMessage \u2014 cannot complete a TinyCloud session"
24522
+ );
24523
+ }
24524
+ if (!Array.isArray(result.selectedActionKeys)) {
24525
+ throw new Error(
24526
+ "wireOpenKeyAuthorize: OpenKey returned no selectedActionKeys"
24527
+ );
24528
+ }
24529
+ if (!Array.isArray(result.permissions)) {
24530
+ throw new Error(
24531
+ "wireOpenKeyAuthorize: OpenKey returned no permissions"
24532
+ );
24533
+ }
24534
+ return {
24535
+ protocolVersion: 1,
24536
+ address: result.address,
24537
+ signature: result.signature,
24538
+ signedMessage: result.signedMessage,
24539
+ selectedActionKeys: result.selectedActionKeys,
24540
+ permissions: result.permissions
24541
+ };
24542
+ };
24543
+ }
24544
+
23807
24545
  // src/index.ts
23808
24546
  var import_sdk_core12 = require("@tinycloud/sdk-core");
23809
24547
 
@@ -23841,7 +24579,7 @@ function compactUcanPayload(authorization) {
23841
24579
  }
23842
24580
  try {
23843
24581
  const payload = JSON.parse(
23844
- Buffer.from(parts[1], "base64url").toString("utf8")
24582
+ new TextDecoder("utf-8", { fatal: true }).decode((0, import_sdk_core9.decodeBase64Url)(parts[1]))
23845
24583
  );
23846
24584
  if (payload === null || typeof payload !== "object" || Array.isArray(payload)) {
23847
24585
  throw new Error("payload is not an object");
@@ -23892,6 +24630,14 @@ function signedAuthorityFromCompactUcan(authorization) {
23892
24630
  if (typeof payload.aud !== "string" || payload.aud.length === 0) {
23893
24631
  throw new Error("Validated runtime delegation is missing a signed audience.");
23894
24632
  }
24633
+ if (typeof payload.iss !== "string" || payload.iss.length === 0) {
24634
+ throw new Error("Validated runtime delegation is missing a signed issuer.");
24635
+ }
24636
+ if (!Array.isArray(payload.prf) || payload.prf.some(
24637
+ (proof) => typeof proof !== "string" || proof.length === 0
24638
+ )) {
24639
+ throw new Error("Validated runtime delegation has malformed signed proofs.");
24640
+ }
23895
24641
  if (typeof payload.exp !== "number" || !Number.isFinite(payload.exp)) {
23896
24642
  throw new Error("Validated runtime delegation is missing a signed expiry.");
23897
24643
  }
@@ -23902,6 +24648,11 @@ function signedAuthorityFromCompactUcan(authorization) {
23902
24648
  if (payload.att === null || typeof payload.att !== "object" || Array.isArray(payload.att)) {
23903
24649
  throw new Error("Validated runtime delegation is missing a signed attenuation.");
23904
24650
  }
24651
+ const fact = Array.isArray(payload.fct) && payload.fct.length > 0 && payload.fct[0] !== null && typeof payload.fct[0] === "object" && !Array.isArray(payload.fct[0]) ? payload.fct[0] : void 0;
24652
+ const remainingRedelegationDepth = fact?.remainingRedelegationDepth;
24653
+ if (remainingRedelegationDepth !== void 0 && (!Number.isInteger(remainingRedelegationDepth) || remainingRedelegationDepth < 0 || remainingRedelegationDepth > 8)) {
24654
+ throw new Error("Validated runtime delegation has invalid signed redelegation depth.");
24655
+ }
23905
24656
  const permissions = [];
23906
24657
  for (const [resource, abilities] of Object.entries(payload.att)) {
23907
24658
  if (abilities === null || typeof abilities !== "object" || Array.isArray(abilities)) {
@@ -23920,7 +24671,9 @@ function signedAuthorityFromCompactUcan(authorization) {
23920
24671
  ));
23921
24672
  continue;
23922
24673
  }
23923
- const match = /^(tinycloud:[^/]+)\/(kv|sql|duckdb|hooks)\/(.*)$/.exec(resource);
24674
+ const nativeMatch = /^(tinycloud:[^/]+)\/(kv|sql|duckdb|hooks)\/(.*)$/.exec(resource);
24675
+ const policyMatch = /^tinycloud:\/\/([^/]+)\/(kv|sql|duckdb|hooks)\/(.*)$/.exec(resource);
24676
+ const match = nativeMatch ?? policyMatch;
23924
24677
  if (!match) {
23925
24678
  throw new Error(
23926
24679
  `Validated runtime delegation has an unsupported signed resource '${resource}'.`
@@ -23952,8 +24705,11 @@ function signedAuthorityFromCompactUcan(authorization) {
23952
24705
  ...permission.caveats === void 0 ? {} : { caveats: permission.caveats }
23953
24706
  }));
23954
24707
  return {
24708
+ issuer: payload.iss,
23955
24709
  audience: payload.aud,
23956
24710
  expiry,
24711
+ proofs: [...payload.prf],
24712
+ ...remainingRedelegationDepth === void 0 ? {} : { remainingRedelegationDepth },
23957
24713
  permissions: canonicalPermissions,
23958
24714
  resources: canonicalResources
23959
24715
  };
@@ -24044,6 +24800,13 @@ async function activateValidatedRuntimeDelegation(node, delegation, options) {
24044
24800
  delegationHeader: { Authorization: authorization },
24045
24801
  ownerAddress: delegation.ownerAddress,
24046
24802
  chainId: delegation.chainId,
24803
+ delegatorDID: signed2.issuer,
24804
+ ...signed2.proofs.length === 1 ? { parentCid: signed2.proofs[0] } : {},
24805
+ ...signed2.remainingRedelegationDepth === void 0 ? {} : {
24806
+ allowSubDelegation: signed2.remainingRedelegationDepth > 0,
24807
+ disableSubDelegation: signed2.remainingRedelegationDepth === 0
24808
+ },
24809
+ authHeader: authorization,
24047
24810
  spaceId: signedSpace ?? delegation.spaceId,
24048
24811
  path: primary.path,
24049
24812
  actions: [...primary.actions],
@@ -24082,6 +24845,40 @@ async function activateValidatedRuntimeDelegation(node, delegation, options) {
24082
24845
  host: normalizedHost(installed.host ?? host)
24083
24846
  };
24084
24847
  }
24848
+ async function activateCompactRuntimeDelegation(node, input) {
24849
+ const authorization = input.authorization;
24850
+ const cid = node.computeDelegationCid(authorization);
24851
+ if (cid !== input.cid) {
24852
+ throw new Error("Compact runtime delegation CID does not match authorization bytes.");
24853
+ }
24854
+ const signed2 = signedAuthorityFromCompactUcan(authorization);
24855
+ const primary = signed2.resources[0];
24856
+ const signedSpace = signed2.permissions.find(
24857
+ (permission) => permission.service !== "tinycloud.encryption"
24858
+ )?.space;
24859
+ const delegation = {
24860
+ cid,
24861
+ delegationHeader: { Authorization: authorization },
24862
+ ownerAddress: input.ownerAddress,
24863
+ chainId: input.chainId,
24864
+ delegatorDID: signed2.issuer,
24865
+ authHeader: authorization,
24866
+ spaceId: signedSpace ?? primary.space,
24867
+ path: primary.path,
24868
+ actions: [...primary.actions],
24869
+ ...primary.caveats === void 0 ? {} : { caveats: primary.caveats },
24870
+ resources: signed2.resources.map((resource) => ({
24871
+ ...resource,
24872
+ actions: [...resource.actions]
24873
+ })),
24874
+ expiry: new Date(signed2.expiry),
24875
+ delegateDID: signed2.audience,
24876
+ host: normalizedHost(input.host)
24877
+ };
24878
+ return activateValidatedRuntimeDelegation(node, delegation, {
24879
+ host: input.host
24880
+ });
24881
+ }
24085
24882
  async function grantAuthRequest(authority, request, options) {
24086
24883
  if (request.kind !== "tinycloud.auth.request") {
24087
24884
  throw new Error(
@@ -24137,10 +24934,13 @@ var import_sdk_core22 = require("@tinycloud/sdk-core");
24137
24934
  var import_sdk_core23 = require("@tinycloud/sdk-core");
24138
24935
  // Annotate the CommonJS export names for ESM import in node:
24139
24936
  0 && (module.exports = {
24937
+ ACCEPTED_MARKER_VERSIONS,
24140
24938
  ACCOUNT_REGISTRY_PATH,
24141
24939
  ACCOUNT_REGISTRY_SPACE,
24142
24940
  AccountService,
24143
24941
  AutoApproveSpaceCreationHandler,
24942
+ BOOTSTRAP_COMPLETION_MARKER_KEY,
24943
+ BOOTSTRAP_COMPLETION_MARKER_VERSION,
24144
24944
  CapabilityKeyRegistry,
24145
24945
  CapabilityKeyRegistryErrorCodes,
24146
24946
  CaveatedDelegationUnsupportedError,
@@ -24201,6 +25001,7 @@ var import_sdk_core23 = require("@tinycloud/sdk-core");
24201
25001
  VaultPublicSpaceKVActions,
24202
25002
  VersionCheckError,
24203
25003
  WasmKeyProvider,
25004
+ activateCompactRuntimeDelegation,
24204
25005
  activateValidatedRuntimeDelegation,
24205
25006
  addressStorageKey,
24206
25007
  buildCanonicalDecryptRequest,
@@ -24277,7 +25078,8 @@ var import_sdk_core23 = require("@tinycloud/sdk-core");
24277
25078
  tinyCloudDebugLogger,
24278
25079
  validateEnvelope,
24279
25080
  validateManifest,
24280
- verifyDecryptResponse
25081
+ verifyDecryptResponse,
25082
+ wireOpenKeyAuthorize
24281
25083
  });
24282
25084
  /*! Bundled license information:
24283
25085