@tinycloud/node-sdk 3.2.0-beta.2 → 3.2.0-beta.3

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.
@@ -1,5 +1,5 @@
1
1
  import * as _tinycloud_sdk_core from '@tinycloud/sdk-core';
2
- import { ISessionStorage, PersistedSessionData, AutoSignStrategy, AutoRejectStrategy, CallbackStrategy, IUserAuthorization, ISigner, ISpaceCreationHandler, LocalNodeIdentityStore, IWasmBindings, ISessionManager, SiweConfig, Manifest, ComposedManifestRequest, ClientSession, TinyCloudSession, Extension, SignRequest, SpaceHostResult, SignInOptions, CapabilityPresentationEnvelopeV1, Delegation, DelegatedResource, PermissionEntry, TelemetryConfig, IKVService, ISQLService, IDuckDbService, IHooksService, CreateOwnerDelegationParams as CreateOwnerDelegationParams$1, OwnerDelegationReceipt as OwnerDelegationReceipt$1, INotificationHandler, IENSResolver, AccountService, InvokeAnyFunction as InvokeAnyFunction$1, UnifiedPolicyCapability, RegisterPolicyV3Input, RegisterPolicyV3Receipt, CanonicalJson, ShareDeliveryAuthorizationV3Receipt, IDataVaultService, IEncryptionService, NetworkDescriptor, ISecretsService, ICapabilityKeyRegistry, DelegationManager, ISpaceService, ISpace, ISharingService, CreateDelegationParams, DelegationResult, DelegationRevocationReceipt, DelegationStatus, ResolvedDelegate, KeyProvider, JWK } from '@tinycloud/sdk-core';
2
+ import { ISessionStorage, PersistedSessionData, AutoSignStrategy, AutoRejectStrategy, CallbackStrategy, IUserAuthorization, ISigner, ISpaceCreationHandler, LocalNodeIdentityStore, IWasmBindings, ISessionManager, SiweConfig, Manifest, ComposedManifestRequest, ReplicationOptions, ClientSession, TinyCloudSession, PermissionEntry, Extension, SignRequest, SpaceHostResult, SignInOptions, CapabilityPresentationEnvelopeV1, Delegation, DelegatedResource, TelemetryConfig, IKVService, ISQLService, IDuckDbService, IHooksService, CreateOwnerDelegationParams as CreateOwnerDelegationParams$1, OwnerDelegationReceipt as OwnerDelegationReceipt$1, INotificationHandler, IENSResolver, KVReplicaStorage, AccountService, InvokeAnyFunction as InvokeAnyFunction$1, UnifiedPolicyCapability, RegisterPolicyV3Input, RegisterPolicyV3Receipt, CanonicalJson, ShareDeliveryAuthorizationV3Receipt, IDataVaultService, IEncryptionService, NetworkDescriptor, ISecretsService, ICapabilityKeyRegistry, DelegationManager, ISpaceService, ISpace, ISharingService, CreateDelegationParams, DelegationResult, DelegationRevocationReceipt, DelegationStatus, ServiceSession, ReplicationControl, ResolvedDelegate, KeyProvider, JWK } from '@tinycloud/sdk-core';
3
3
  import { EventEmitter } from 'events';
4
4
  import { InvokeFunction, InvokeAnyFunction } from '@tinycloud/sdk-services';
5
5
 
@@ -242,6 +242,14 @@ interface NodeUserAuthorizationConfig {
242
242
  capabilityRequest?: ComposedManifestRequest;
243
243
  /** Include canonical account registry read/create-update/list permissions when composing `manifest` and plain sign-in. Default true. */
244
244
  includeAccountRegistryPermissions?: boolean;
245
+ /**
246
+ * Replication sign-in augmentation (TC-858 §4.1): prefixes whose reads a
247
+ * local replica may serve. For each prefix the session request gains one
248
+ * `kv` entry `{get, sync}` on the primary space — but only where an
249
+ * unrestricted `get` covering it is already requested. Storage and
250
+ * scheduling never reach this layer.
251
+ */
252
+ replication?: Pick<ReplicationOptions, "enabled" | "prefixes" | "allowSecrets">;
245
253
  }
246
254
  interface CreateBootstrapSessionOptions {
247
255
  spaceId: string;
@@ -301,6 +309,7 @@ declare class NodeUserAuthorization implements IUserAuthorization {
301
309
  private readonly expectedNodeDid?;
302
310
  private readonly localNodeIdentityStore?;
303
311
  private readonly enablePublicSpace;
312
+ private readonly replication?;
304
313
  private readonly nonce?;
305
314
  private readonly siweConfig?;
306
315
  private readonly wasm;
@@ -430,6 +439,50 @@ declare class NodeUserAuthorization implements IUserAuthorization {
430
439
  */
431
440
  private getCapabilityRequest;
432
441
  private resolveSpaceName;
442
+ /**
443
+ * The plain-session primary abilities: `defaultActions` (the shared table)
444
+ * unless the primary space IS the account space, where account-registry
445
+ * permissions merge into a private clone so the shared table is never
446
+ * mutated. Shared between `resolveSignInCapabilities` and
447
+ * `signInPermissionInputs` so sign-in and `replicationSignInEntries`
448
+ * can't disagree (§4.6).
449
+ */
450
+ private primaryActionsForSignIn;
451
+ /**
452
+ * The abilities a `spacePrefix: "secrets"` plain session actually requests
453
+ * for its primary space: the vault-secrets subtree only — the
454
+ * `defaultActions` root `get` is REPLACED, not merged (sign-in installs
455
+ * this map under the primary space id). Shared between
456
+ * `resolveSignInCapabilities` and `signInPermissionInputs` so the
457
+ * replication augmentation measures coverage against the effective
458
+ * pre-replication request, never the uninstalled `primaryActions` (§4.1).
459
+ */
460
+ private secretsPrimaryAbilities;
461
+ /**
462
+ * The effective sign-in request as `PermissionEntry`s plus the resolved
463
+ * primary space id — the inputs to the replication augmentation (§4.1).
464
+ * `resolveSignInCapabilities` merges the augmented entries into its
465
+ * abilities; `replicationSignInEntries()` returns them verbatim, so the
466
+ * entries the SIWE recap carries and the restore check sees are the same.
467
+ */
468
+ private signInPermissionInputs;
469
+ /**
470
+ * Merge replication augmentation entries into a space's abilities map
471
+ * without mutating shared tables (`defaultActions` may back the base map):
472
+ * the primary entry in `spaceAbilities` is replaced with a copy whose `kv`
473
+ * paths gain `get`/`sync`. Returns the merged map for `abilities`.
474
+ */
475
+ private mergeReplicationEntries;
476
+ /**
477
+ * The replication entries this authorization's sign-in request carries
478
+ * for the signed-in or restored address (TC-858 §4.1/§4.6): the output of
479
+ * the same augmentation `resolveSignInCapabilities` runs — one
480
+ * `{tinycloud.kv, primarySpace, prefix, [get, sync]}` per configured
481
+ * prefix that already has an unrestricted covering `get` and passes the
482
+ * secrets opt-in gate. Empty when replication is off or no address is
483
+ * known. Pure.
484
+ */
485
+ replicationSignInEntries(): PermissionEntry[];
433
486
  private defaultEncryptionNetworkId;
434
487
  private resolveSignInCapabilities;
435
488
  /**
@@ -470,7 +523,7 @@ declare class NodeUserAuthorization implements IUserAuthorization {
470
523
  * keeps its HTTP status (e.g. a typed 401/403) instead of collapsing to
471
524
  * `false`.
472
525
  */
473
- hostOwnedSpaceResult(spaceId: string, purpose?: SignRequest["purpose"]): Promise<SpaceHostResult>;
526
+ hostOwnedSpaceResult(spaceId: string, purpose?: SignRequest["purpose"], signal?: AbortSignal): Promise<SpaceHostResult>;
474
527
  /**
475
528
  * Ensure the user's space exists on the TinyCloud server.
476
529
  * Creates the space if it doesn't exist and autoCreateSpace is enabled.
@@ -1197,6 +1250,11 @@ interface TinyCloudNodeConfig {
1197
1250
  autoBootstrapAccount?: boolean;
1198
1251
  /** Default-off service telemetry. */
1199
1252
  telemetry?: TelemetryConfig;
1253
+ /** Optional read-through replica configuration; disabled unless explicitly enabled. */
1254
+ replication?: ReplicationOptions & {
1255
+ storage: KVReplicaStorage;
1256
+ mode?: "background" | "foreground";
1257
+ };
1200
1258
  }
1201
1259
  /**
1202
1260
  * Options for {@link TinyCloudNode.delegateTo}.
@@ -1263,6 +1321,56 @@ interface EnsureEncryptionNetworkOptions {
1263
1321
  */
1264
1322
  assumeMissing?: boolean;
1265
1323
  }
1324
+ interface RuntimePermissionOperation {
1325
+ spaceId?: string;
1326
+ resource?: string;
1327
+ service: string;
1328
+ path: string;
1329
+ action: string;
1330
+ /** Exact signed ReCap attenuation for this action, if any. */
1331
+ caveats?: Record<string, unknown>[];
1332
+ }
1333
+ /**
1334
+ * The parent `delegateTo` would sign under (TC-858 §4.3): the session's own
1335
+ * delegation CID on the session path, or the selected runtime grant's
1336
+ * session CID on the runtime path. Refusals classify why no authority can
1337
+ * be derived; `expiredAt`/`missing`/`granted`/`caveated` let `delegateTo`
1338
+ * reproduce its exact typed errors from the same decision.
1339
+ */
1340
+ type DelegationPlan = {
1341
+ path: "session";
1342
+ parentCid: string;
1343
+ expiresAt: number;
1344
+ effectiveExpiration: Date;
1345
+ } | {
1346
+ path: "runtime";
1347
+ parentCid: string;
1348
+ expiresAt: number;
1349
+ effectiveExpiration: Date;
1350
+ grant: RuntimePermissionGrant;
1351
+ operations: RuntimePermissionOperation[];
1352
+ } | {
1353
+ refused: "NOT_COVERED" | "CAVEATED_AUTHORITY" | "SESSION_EXPIRING";
1354
+ expiredAt?: Date;
1355
+ missing?: PermissionEntry[];
1356
+ granted?: PermissionEntry[];
1357
+ caveated?: PermissionEntry[];
1358
+ };
1359
+ interface RuntimePermissionGrant {
1360
+ session: ServiceSession;
1361
+ delegation: PortableDelegation;
1362
+ operations: RuntimePermissionOperation[];
1363
+ expiresAt: Date;
1364
+ /**
1365
+ * Where this grant came from. Drives precedence in
1366
+ * {@link TinyCloudNode.findGrantForOperations} (a `"primary"` grant — the
1367
+ * synthetic representation of the base session's own recap — always
1368
+ * out-ranks other covering grants) and gates the public-surface guardrails
1369
+ * that must never surface the synthetic primary grant. Optional so existing
1370
+ * test constructors and older call sites remain valid.
1371
+ */
1372
+ provenance?: "primary" | "bootstrap" | "delegated" | "runtime";
1373
+ }
1266
1374
  /**
1267
1375
  * High-level TinyCloud API for Node.js environments.
1268
1376
  *
@@ -1314,6 +1422,11 @@ declare class TinyCloudNode {
1314
1422
  private _serviceGraph;
1315
1423
  private _serviceContext?;
1316
1424
  private _kv?;
1425
+ private readonly replicationRuntime?;
1426
+ private replicationRuntimeInstance?;
1427
+ private replicationDelegateSession?;
1428
+ private replicationScopeError?;
1429
+ private readonly replicationControl?;
1317
1430
  private _sql?;
1318
1431
  private _duckdb?;
1319
1432
  private _hooks?;
@@ -1351,6 +1464,8 @@ declare class TinyCloudNode {
1351
1464
  private readonly confirmedHostedSpaceIds;
1352
1465
  /** Serializes account-registry writes for the active node instance. */
1353
1466
  private accountRegistryTail;
1467
+ /** Bounds best-effort registry work on the sign-in critical path. */
1468
+ private accountRegistryDeadlineMs;
1354
1469
  private pendingAccountRegistrySync?;
1355
1470
  /**
1356
1471
  * TinyCloudSession captured by {@link restoreSession} when there's no
@@ -1569,6 +1684,7 @@ declare class TinyCloudNode {
1569
1684
  */
1570
1685
  private registerPrimarySessionGrant;
1571
1686
  private writeManifestRegistryRecords;
1687
+ private createAccountRegistryServices;
1572
1688
  private scheduleAccountRegistrySync;
1573
1689
  private enqueueAccountRegistryOperation;
1574
1690
  /**
@@ -1823,6 +1939,7 @@ declare class TinyCloudNode {
1823
1939
  * @internal
1824
1940
  */
1825
1941
  private initializeServices;
1942
+ private attachReplicationKV;
1826
1943
  private createSpaceScopedKVService;
1827
1944
  getDefaultEncryptionNetworkId(name?: string): string;
1828
1945
  getEncryptionNetworkIdForSpace(spaceId: string, name?: string): string;
@@ -2282,6 +2399,42 @@ declare class TinyCloudNode {
2282
2399
  * delegation.
2283
2400
  */
2284
2401
  delegateTo(did: string, permissions: PermissionEntry[], options?: DelegateToOptions): Promise<DelegateToResult>;
2402
+ /**
2403
+ * What `delegateTo` would select for `entries`, without signing or any
2404
+ * network call (TC-858 plan v3 §4.3): the session path when the signed
2405
+ * recap covers every entry, the runtime-grant path otherwise, or a
2406
+ * refusal. A refusal of `CAVEATED_AUTHORITY` means coverage exists only
2407
+ * through caveated branches, which the action-only WASM child-delegation
2408
+ * boundary cannot preserve — replication never derives authority through
2409
+ * them. `expiredAt`/`missing`/`granted`/`caveated` carry the payload
2410
+ * `delegateTo` turns back into its typed errors.
2411
+ * @internal
2412
+ */
2413
+ private planDerivation;
2414
+ /**
2415
+ * The plan `delegateTo(device, entries)` would follow, as a pure lookup:
2416
+ * no WASM sign and no `POST /delegate`. Backs
2417
+ * `ReplicationAuthority.plan` (and `mint` inside it) so a predicted
2418
+ * parent and expiry can never disagree with what a mint would use (§4.3,
2419
+ * §4.4). Session expiry inside the 60 s margin refuses with
2420
+ * `SESSION_EXPIRING`; the same check delegateTo performs.
2421
+ * @internal
2422
+ */
2423
+ planDelegation(entries: PermissionEntry[], options?: {
2424
+ expiry?: string | number;
2425
+ }): DelegationPlan;
2426
+ /**
2427
+ * The replication entries this node's sign-in request carries for the
2428
+ * current replication config and the signed-in or restored address: the
2429
+ * output of the same augmentation step `resolveSignInCapabilities` runs,
2430
+ * with the unrestricted-get and secrets gates applied (§4.1, §4.6).
2431
+ * Empty when replication is off or no address is known. Pure.
2432
+ */
2433
+ replicationSignInEntries(): PermissionEntry[];
2434
+ private replicationAuthority;
2435
+ private bindReplicationRuntime;
2436
+ /** Stable control facade for this node's optional replication runtime. */
2437
+ get replication(): ReplicationControl | undefined;
2285
2438
  /**
2286
2439
  * Materialize one manifest-declared delegation using the current session key.
2287
2440
  * Delivery is intentionally out of band; callers decide how to transmit the
@@ -2325,6 +2478,13 @@ declare class TinyCloudNode {
2325
2478
  * it only accepts action maps, so reject the selected constrained branch.
2326
2479
  */
2327
2480
  private assertRuntimeGrantCaveatsPreservable;
2481
+ /**
2482
+ * The entries of `grant` that cover a requested operation while carrying
2483
+ * caveats — the branches {@link assertRuntimeGrantCaveatsPreservable}
2484
+ * rejects and {@link planDerivation} classifies as `CAVEATED_AUTHORITY`.
2485
+ * Shared so the pure plan and the mint path can't disagree.
2486
+ */
2487
+ private caveatedRuntimeGrantEntries;
2328
2488
  /** Reject caveated parent branches before action-only child signing. */
2329
2489
  private assertPortableDelegationCaveatsPreservable;
2330
2490
  private shortServiceName;
@@ -1,5 +1,5 @@
1
1
  import * as _tinycloud_sdk_core from '@tinycloud/sdk-core';
2
- import { ISessionStorage, PersistedSessionData, AutoSignStrategy, AutoRejectStrategy, CallbackStrategy, IUserAuthorization, ISigner, ISpaceCreationHandler, LocalNodeIdentityStore, IWasmBindings, ISessionManager, SiweConfig, Manifest, ComposedManifestRequest, ClientSession, TinyCloudSession, Extension, SignRequest, SpaceHostResult, SignInOptions, CapabilityPresentationEnvelopeV1, Delegation, DelegatedResource, PermissionEntry, TelemetryConfig, IKVService, ISQLService, IDuckDbService, IHooksService, CreateOwnerDelegationParams as CreateOwnerDelegationParams$1, OwnerDelegationReceipt as OwnerDelegationReceipt$1, INotificationHandler, IENSResolver, AccountService, InvokeAnyFunction as InvokeAnyFunction$1, UnifiedPolicyCapability, RegisterPolicyV3Input, RegisterPolicyV3Receipt, CanonicalJson, ShareDeliveryAuthorizationV3Receipt, IDataVaultService, IEncryptionService, NetworkDescriptor, ISecretsService, ICapabilityKeyRegistry, DelegationManager, ISpaceService, ISpace, ISharingService, CreateDelegationParams, DelegationResult, DelegationRevocationReceipt, DelegationStatus, ResolvedDelegate, KeyProvider, JWK } from '@tinycloud/sdk-core';
2
+ import { ISessionStorage, PersistedSessionData, AutoSignStrategy, AutoRejectStrategy, CallbackStrategy, IUserAuthorization, ISigner, ISpaceCreationHandler, LocalNodeIdentityStore, IWasmBindings, ISessionManager, SiweConfig, Manifest, ComposedManifestRequest, ReplicationOptions, ClientSession, TinyCloudSession, PermissionEntry, Extension, SignRequest, SpaceHostResult, SignInOptions, CapabilityPresentationEnvelopeV1, Delegation, DelegatedResource, TelemetryConfig, IKVService, ISQLService, IDuckDbService, IHooksService, CreateOwnerDelegationParams as CreateOwnerDelegationParams$1, OwnerDelegationReceipt as OwnerDelegationReceipt$1, INotificationHandler, IENSResolver, KVReplicaStorage, AccountService, InvokeAnyFunction as InvokeAnyFunction$1, UnifiedPolicyCapability, RegisterPolicyV3Input, RegisterPolicyV3Receipt, CanonicalJson, ShareDeliveryAuthorizationV3Receipt, IDataVaultService, IEncryptionService, NetworkDescriptor, ISecretsService, ICapabilityKeyRegistry, DelegationManager, ISpaceService, ISpace, ISharingService, CreateDelegationParams, DelegationResult, DelegationRevocationReceipt, DelegationStatus, ServiceSession, ReplicationControl, ResolvedDelegate, KeyProvider, JWK } from '@tinycloud/sdk-core';
3
3
  import { EventEmitter } from 'events';
4
4
  import { InvokeFunction, InvokeAnyFunction } from '@tinycloud/sdk-services';
5
5
 
@@ -242,6 +242,14 @@ interface NodeUserAuthorizationConfig {
242
242
  capabilityRequest?: ComposedManifestRequest;
243
243
  /** Include canonical account registry read/create-update/list permissions when composing `manifest` and plain sign-in. Default true. */
244
244
  includeAccountRegistryPermissions?: boolean;
245
+ /**
246
+ * Replication sign-in augmentation (TC-858 §4.1): prefixes whose reads a
247
+ * local replica may serve. For each prefix the session request gains one
248
+ * `kv` entry `{get, sync}` on the primary space — but only where an
249
+ * unrestricted `get` covering it is already requested. Storage and
250
+ * scheduling never reach this layer.
251
+ */
252
+ replication?: Pick<ReplicationOptions, "enabled" | "prefixes" | "allowSecrets">;
245
253
  }
246
254
  interface CreateBootstrapSessionOptions {
247
255
  spaceId: string;
@@ -301,6 +309,7 @@ declare class NodeUserAuthorization implements IUserAuthorization {
301
309
  private readonly expectedNodeDid?;
302
310
  private readonly localNodeIdentityStore?;
303
311
  private readonly enablePublicSpace;
312
+ private readonly replication?;
304
313
  private readonly nonce?;
305
314
  private readonly siweConfig?;
306
315
  private readonly wasm;
@@ -430,6 +439,50 @@ declare class NodeUserAuthorization implements IUserAuthorization {
430
439
  */
431
440
  private getCapabilityRequest;
432
441
  private resolveSpaceName;
442
+ /**
443
+ * The plain-session primary abilities: `defaultActions` (the shared table)
444
+ * unless the primary space IS the account space, where account-registry
445
+ * permissions merge into a private clone so the shared table is never
446
+ * mutated. Shared between `resolveSignInCapabilities` and
447
+ * `signInPermissionInputs` so sign-in and `replicationSignInEntries`
448
+ * can't disagree (§4.6).
449
+ */
450
+ private primaryActionsForSignIn;
451
+ /**
452
+ * The abilities a `spacePrefix: "secrets"` plain session actually requests
453
+ * for its primary space: the vault-secrets subtree only — the
454
+ * `defaultActions` root `get` is REPLACED, not merged (sign-in installs
455
+ * this map under the primary space id). Shared between
456
+ * `resolveSignInCapabilities` and `signInPermissionInputs` so the
457
+ * replication augmentation measures coverage against the effective
458
+ * pre-replication request, never the uninstalled `primaryActions` (§4.1).
459
+ */
460
+ private secretsPrimaryAbilities;
461
+ /**
462
+ * The effective sign-in request as `PermissionEntry`s plus the resolved
463
+ * primary space id — the inputs to the replication augmentation (§4.1).
464
+ * `resolveSignInCapabilities` merges the augmented entries into its
465
+ * abilities; `replicationSignInEntries()` returns them verbatim, so the
466
+ * entries the SIWE recap carries and the restore check sees are the same.
467
+ */
468
+ private signInPermissionInputs;
469
+ /**
470
+ * Merge replication augmentation entries into a space's abilities map
471
+ * without mutating shared tables (`defaultActions` may back the base map):
472
+ * the primary entry in `spaceAbilities` is replaced with a copy whose `kv`
473
+ * paths gain `get`/`sync`. Returns the merged map for `abilities`.
474
+ */
475
+ private mergeReplicationEntries;
476
+ /**
477
+ * The replication entries this authorization's sign-in request carries
478
+ * for the signed-in or restored address (TC-858 §4.1/§4.6): the output of
479
+ * the same augmentation `resolveSignInCapabilities` runs — one
480
+ * `{tinycloud.kv, primarySpace, prefix, [get, sync]}` per configured
481
+ * prefix that already has an unrestricted covering `get` and passes the
482
+ * secrets opt-in gate. Empty when replication is off or no address is
483
+ * known. Pure.
484
+ */
485
+ replicationSignInEntries(): PermissionEntry[];
433
486
  private defaultEncryptionNetworkId;
434
487
  private resolveSignInCapabilities;
435
488
  /**
@@ -470,7 +523,7 @@ declare class NodeUserAuthorization implements IUserAuthorization {
470
523
  * keeps its HTTP status (e.g. a typed 401/403) instead of collapsing to
471
524
  * `false`.
472
525
  */
473
- hostOwnedSpaceResult(spaceId: string, purpose?: SignRequest["purpose"]): Promise<SpaceHostResult>;
526
+ hostOwnedSpaceResult(spaceId: string, purpose?: SignRequest["purpose"], signal?: AbortSignal): Promise<SpaceHostResult>;
474
527
  /**
475
528
  * Ensure the user's space exists on the TinyCloud server.
476
529
  * Creates the space if it doesn't exist and autoCreateSpace is enabled.
@@ -1197,6 +1250,11 @@ interface TinyCloudNodeConfig {
1197
1250
  autoBootstrapAccount?: boolean;
1198
1251
  /** Default-off service telemetry. */
1199
1252
  telemetry?: TelemetryConfig;
1253
+ /** Optional read-through replica configuration; disabled unless explicitly enabled. */
1254
+ replication?: ReplicationOptions & {
1255
+ storage: KVReplicaStorage;
1256
+ mode?: "background" | "foreground";
1257
+ };
1200
1258
  }
1201
1259
  /**
1202
1260
  * Options for {@link TinyCloudNode.delegateTo}.
@@ -1263,6 +1321,56 @@ interface EnsureEncryptionNetworkOptions {
1263
1321
  */
1264
1322
  assumeMissing?: boolean;
1265
1323
  }
1324
+ interface RuntimePermissionOperation {
1325
+ spaceId?: string;
1326
+ resource?: string;
1327
+ service: string;
1328
+ path: string;
1329
+ action: string;
1330
+ /** Exact signed ReCap attenuation for this action, if any. */
1331
+ caveats?: Record<string, unknown>[];
1332
+ }
1333
+ /**
1334
+ * The parent `delegateTo` would sign under (TC-858 §4.3): the session's own
1335
+ * delegation CID on the session path, or the selected runtime grant's
1336
+ * session CID on the runtime path. Refusals classify why no authority can
1337
+ * be derived; `expiredAt`/`missing`/`granted`/`caveated` let `delegateTo`
1338
+ * reproduce its exact typed errors from the same decision.
1339
+ */
1340
+ type DelegationPlan = {
1341
+ path: "session";
1342
+ parentCid: string;
1343
+ expiresAt: number;
1344
+ effectiveExpiration: Date;
1345
+ } | {
1346
+ path: "runtime";
1347
+ parentCid: string;
1348
+ expiresAt: number;
1349
+ effectiveExpiration: Date;
1350
+ grant: RuntimePermissionGrant;
1351
+ operations: RuntimePermissionOperation[];
1352
+ } | {
1353
+ refused: "NOT_COVERED" | "CAVEATED_AUTHORITY" | "SESSION_EXPIRING";
1354
+ expiredAt?: Date;
1355
+ missing?: PermissionEntry[];
1356
+ granted?: PermissionEntry[];
1357
+ caveated?: PermissionEntry[];
1358
+ };
1359
+ interface RuntimePermissionGrant {
1360
+ session: ServiceSession;
1361
+ delegation: PortableDelegation;
1362
+ operations: RuntimePermissionOperation[];
1363
+ expiresAt: Date;
1364
+ /**
1365
+ * Where this grant came from. Drives precedence in
1366
+ * {@link TinyCloudNode.findGrantForOperations} (a `"primary"` grant — the
1367
+ * synthetic representation of the base session's own recap — always
1368
+ * out-ranks other covering grants) and gates the public-surface guardrails
1369
+ * that must never surface the synthetic primary grant. Optional so existing
1370
+ * test constructors and older call sites remain valid.
1371
+ */
1372
+ provenance?: "primary" | "bootstrap" | "delegated" | "runtime";
1373
+ }
1266
1374
  /**
1267
1375
  * High-level TinyCloud API for Node.js environments.
1268
1376
  *
@@ -1314,6 +1422,11 @@ declare class TinyCloudNode {
1314
1422
  private _serviceGraph;
1315
1423
  private _serviceContext?;
1316
1424
  private _kv?;
1425
+ private readonly replicationRuntime?;
1426
+ private replicationRuntimeInstance?;
1427
+ private replicationDelegateSession?;
1428
+ private replicationScopeError?;
1429
+ private readonly replicationControl?;
1317
1430
  private _sql?;
1318
1431
  private _duckdb?;
1319
1432
  private _hooks?;
@@ -1351,6 +1464,8 @@ declare class TinyCloudNode {
1351
1464
  private readonly confirmedHostedSpaceIds;
1352
1465
  /** Serializes account-registry writes for the active node instance. */
1353
1466
  private accountRegistryTail;
1467
+ /** Bounds best-effort registry work on the sign-in critical path. */
1468
+ private accountRegistryDeadlineMs;
1354
1469
  private pendingAccountRegistrySync?;
1355
1470
  /**
1356
1471
  * TinyCloudSession captured by {@link restoreSession} when there's no
@@ -1569,6 +1684,7 @@ declare class TinyCloudNode {
1569
1684
  */
1570
1685
  private registerPrimarySessionGrant;
1571
1686
  private writeManifestRegistryRecords;
1687
+ private createAccountRegistryServices;
1572
1688
  private scheduleAccountRegistrySync;
1573
1689
  private enqueueAccountRegistryOperation;
1574
1690
  /**
@@ -1823,6 +1939,7 @@ declare class TinyCloudNode {
1823
1939
  * @internal
1824
1940
  */
1825
1941
  private initializeServices;
1942
+ private attachReplicationKV;
1826
1943
  private createSpaceScopedKVService;
1827
1944
  getDefaultEncryptionNetworkId(name?: string): string;
1828
1945
  getEncryptionNetworkIdForSpace(spaceId: string, name?: string): string;
@@ -2282,6 +2399,42 @@ declare class TinyCloudNode {
2282
2399
  * delegation.
2283
2400
  */
2284
2401
  delegateTo(did: string, permissions: PermissionEntry[], options?: DelegateToOptions): Promise<DelegateToResult>;
2402
+ /**
2403
+ * What `delegateTo` would select for `entries`, without signing or any
2404
+ * network call (TC-858 plan v3 §4.3): the session path when the signed
2405
+ * recap covers every entry, the runtime-grant path otherwise, or a
2406
+ * refusal. A refusal of `CAVEATED_AUTHORITY` means coverage exists only
2407
+ * through caveated branches, which the action-only WASM child-delegation
2408
+ * boundary cannot preserve — replication never derives authority through
2409
+ * them. `expiredAt`/`missing`/`granted`/`caveated` carry the payload
2410
+ * `delegateTo` turns back into its typed errors.
2411
+ * @internal
2412
+ */
2413
+ private planDerivation;
2414
+ /**
2415
+ * The plan `delegateTo(device, entries)` would follow, as a pure lookup:
2416
+ * no WASM sign and no `POST /delegate`. Backs
2417
+ * `ReplicationAuthority.plan` (and `mint` inside it) so a predicted
2418
+ * parent and expiry can never disagree with what a mint would use (§4.3,
2419
+ * §4.4). Session expiry inside the 60 s margin refuses with
2420
+ * `SESSION_EXPIRING`; the same check delegateTo performs.
2421
+ * @internal
2422
+ */
2423
+ planDelegation(entries: PermissionEntry[], options?: {
2424
+ expiry?: string | number;
2425
+ }): DelegationPlan;
2426
+ /**
2427
+ * The replication entries this node's sign-in request carries for the
2428
+ * current replication config and the signed-in or restored address: the
2429
+ * output of the same augmentation step `resolveSignInCapabilities` runs,
2430
+ * with the unrestricted-get and secrets gates applied (§4.1, §4.6).
2431
+ * Empty when replication is off or no address is known. Pure.
2432
+ */
2433
+ replicationSignInEntries(): PermissionEntry[];
2434
+ private replicationAuthority;
2435
+ private bindReplicationRuntime;
2436
+ /** Stable control facade for this node's optional replication runtime. */
2437
+ get replication(): ReplicationControl | undefined;
2285
2438
  /**
2286
2439
  * Materialize one manifest-declared delegation using the current session key.
2287
2440
  * Delivery is intentionally out of band; callers decide how to transmit the
@@ -2325,6 +2478,13 @@ declare class TinyCloudNode {
2325
2478
  * it only accepts action maps, so reject the selected constrained branch.
2326
2479
  */
2327
2480
  private assertRuntimeGrantCaveatsPreservable;
2481
+ /**
2482
+ * The entries of `grant` that cover a requested operation while carrying
2483
+ * caveats — the branches {@link assertRuntimeGrantCaveatsPreservable}
2484
+ * rejects and {@link planDerivation} classifies as `CAVEATED_AUTHORITY`.
2485
+ * Shared so the pure plan and the mint path can't disagree.
2486
+ */
2487
+ private caveatedRuntimeGrantEntries;
2328
2488
  /** Reject caveated parent branches before action-only child signing. */
2329
2489
  private assertPortableDelegationCaveatsPreservable;
2330
2490
  private shortServiceName;
@@ -0,0 +1,97 @@
1
+ import { PermissionEntry, ReplicationOptions as ReplicationOptions$1, TinyCloudSession } from '@tinycloud/sdk-core';
2
+ import { ReplicationOptions, AuthorityRefusal, ReplicationAuthority } from '@tinycloud/sdk-services';
3
+
4
+ /** Constructor-time validation; the space-dependent secrets check runs at sign-in and open. */
5
+ declare function assertValidReplicationConfig(replication: (ReplicationOptions & {
6
+ storage?: unknown;
7
+ }) | undefined): void;
8
+
9
+ declare function hasUnrestrictedGetCoverage(entries: readonly PermissionEntry[], spaceId: string, prefix: string): boolean;
10
+ declare function augmentSignInEntriesWithReplication(input: {
11
+ entries: readonly PermissionEntry[];
12
+ primarySpaceId: string;
13
+ replication?: Pick<ReplicationOptions$1, "prefixes" | "allowSecrets">;
14
+ }): PermissionEntry[];
15
+
16
+ /**
17
+ * Replication authority (TC-858, plan v3 §4).
18
+ *
19
+ * `ReplicationAuthority` answers, per configured prefix, whether this node's
20
+ * session can back a local replica: the session UCAN itself (delegate
21
+ * posture), a child UCAN minted through `delegateTo`, or nothing (the
22
+ * controller then reports `grant_missing` and reads from the network).
23
+ *
24
+ * The unrestricted-authority rule is enforced here, before any network work:
25
+ * a prefix covered only by a caveated `get`/`sync` refuses with
26
+ * `CAVEATED_AUTHORITY` — on the session path, the runtime-grant path, and on
27
+ * any reuse of an installed grant (the adapter marks every grant's
28
+ * `unconstrained` flag; the controller enforces it on reuse).
29
+ */
30
+
31
+ /**
32
+ * Whether the signed `att` grants `ability` over the selected namespace, with
33
+ * each grant unrestricted (no caveats). A bare selector requires both its
34
+ * exact key and its slash descendants; a trailing-slash selector is one range.
35
+ */
36
+ declare function ucanAttCovers(att: Record<string, Record<string, unknown>>, space: string, prefix: string, ability: string, options?: {
37
+ ignoreCaveats?: boolean;
38
+ }): boolean;
39
+ /** `unconstrained` for `ReplicaGrantInfo` (§2.1): get AND sync on the prefix carry no caveats. */
40
+ declare function ucanAttUnconstrainedFor(att: Record<string, Record<string, unknown>>, space: string, prefix: string): boolean;
41
+ interface DelegationPlanResult {
42
+ path: "session" | "runtime";
43
+ parentCid: string;
44
+ expiresAt: number;
45
+ }
46
+ /**
47
+ * The `TinyCloudNode` internals the authority needs. `planDelegation` is the
48
+ * pure extraction of `delegateTo`'s parent selection (§4.3), so `plan` and
49
+ * `mint` can never disagree about which parent or expiry a delegation gets.
50
+ */
51
+ type ReplicationAuthoritySession = Pick<TinyCloudSession, "delegationHeader" | "delegationCid" | "spaceId" | "verificationMethod" | "jwk"> & Partial<Pick<TinyCloudSession, "siwe">>;
52
+ interface ReplicationAuthorityHost {
53
+ replicationSession(): ReplicationAuthoritySession | undefined;
54
+ /** The SIWE "Expiration Time" of the session — the delegateTo delegation cap. */
55
+ siweExpiration(siwe: string): Date | undefined;
56
+ planDelegation(entries: PermissionEntry[], options?: {
57
+ expiry?: string | number;
58
+ }): ({
59
+ grant?: unknown;
60
+ } & DelegationPlanResult) | AuthorityRefusal;
61
+ mintDelegation(deviceDid: string, entries: PermissionEntry[]): Promise<{
62
+ ucan: string;
63
+ expiresAt: number;
64
+ }>;
65
+ }
66
+ /** Milliseconds of validity a session or parent must have left for authority reuse (delegateTo's margin). */
67
+ declare const AUTHORITY_EXPIRY_MARGIN_MS = 60000;
68
+ /** Verify a compact session delegation before using any persisted space metadata. */
69
+ declare function replicationScopeFromSignedSession(input: {
70
+ delegationHeader: {
71
+ Authorization: string;
72
+ };
73
+ delegationCid: string;
74
+ verificationMethod: string;
75
+ persistedSpace: string;
76
+ }): {
77
+ space: string;
78
+ principal: string;
79
+ } | undefined;
80
+ declare function createReplicationAuthority(host: ReplicationAuthorityHost): ReplicationAuthority;
81
+
82
+ declare const __authority_AUTHORITY_EXPIRY_MARGIN_MS: typeof AUTHORITY_EXPIRY_MARGIN_MS;
83
+ type __authority_DelegationPlanResult = DelegationPlanResult;
84
+ type __authority_ReplicationAuthorityHost = ReplicationAuthorityHost;
85
+ type __authority_ReplicationAuthoritySession = ReplicationAuthoritySession;
86
+ declare const __authority_assertValidReplicationConfig: typeof assertValidReplicationConfig;
87
+ declare const __authority_augmentSignInEntriesWithReplication: typeof augmentSignInEntriesWithReplication;
88
+ declare const __authority_createReplicationAuthority: typeof createReplicationAuthority;
89
+ declare const __authority_hasUnrestrictedGetCoverage: typeof hasUnrestrictedGetCoverage;
90
+ declare const __authority_replicationScopeFromSignedSession: typeof replicationScopeFromSignedSession;
91
+ declare const __authority_ucanAttCovers: typeof ucanAttCovers;
92
+ declare const __authority_ucanAttUnconstrainedFor: typeof ucanAttUnconstrainedFor;
93
+ declare namespace __authority {
94
+ export { __authority_AUTHORITY_EXPIRY_MARGIN_MS as AUTHORITY_EXPIRY_MARGIN_MS, type __authority_DelegationPlanResult as DelegationPlanResult, type __authority_ReplicationAuthorityHost as ReplicationAuthorityHost, type __authority_ReplicationAuthoritySession as ReplicationAuthoritySession, __authority_assertValidReplicationConfig as assertValidReplicationConfig, __authority_augmentSignInEntriesWithReplication as augmentSignInEntriesWithReplication, __authority_createReplicationAuthority as createReplicationAuthority, __authority_hasUnrestrictedGetCoverage as hasUnrestrictedGetCoverage, __authority_replicationScopeFromSignedSession as replicationScopeFromSignedSession, __authority_ucanAttCovers as ucanAttCovers, __authority_ucanAttUnconstrainedFor as ucanAttUnconstrainedFor };
95
+ }
96
+
97
+ export { AUTHORITY_EXPIRY_MARGIN_MS as A, type DelegationPlanResult as D, type ReplicationAuthorityHost as R, __authority as _, type ReplicationAuthoritySession as a, assertValidReplicationConfig as b, augmentSignInEntriesWithReplication as c, createReplicationAuthority as d, ucanAttUnconstrainedFor as e, hasUnrestrictedGetCoverage as h, replicationScopeFromSignedSession as r, ucanAttCovers as u };