@tinycloud/sdk-core 2.8.0 → 2.9.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.d.cts CHANGED
@@ -5331,9 +5331,25 @@ interface LocationRecordPayload {
5331
5331
  interface LocationRecord extends LocationRecordPayload {
5332
5332
  signature: string;
5333
5333
  }
5334
- type LocationSource = "explicit" | "blockchain" | "centralized" | "fallback";
5334
+ /**
5335
+ * Where a resolved TinyCloud host came from, ordered highest to lowest
5336
+ * priority. `local-loopback` and `local-link` are probed + identity-verified
5337
+ * (see {@link discoverLocalTinyCloudNode}); the rest are resolved the same
5338
+ * way they always have been (no liveness probing). New local-discovery
5339
+ * sources (e.g. a future `tunnel` or `mdns` source) slot in next to
5340
+ * `local-link` without touching the unprobed sources below them.
5341
+ */
5342
+ type LocationSource = "explicit" | "local-loopback" | "local-link" | "blockchain" | "centralized" | "fallback";
5335
5343
  declare const DEFAULT_TINYCLOUD_LOCATION_REGISTRY_URL = "https://registry.tinycloud.xyz";
5336
5344
  declare const DEFAULT_TINYCLOUD_FALLBACK_HOST = "https://node.tinycloud.xyz";
5345
+ /** Default local loopback node URL probed before registry/fallback resolution. */
5346
+ declare const DEFAULT_LOCAL_NODE_URL = "http://127.0.0.1:8000";
5347
+ /** Probe timeout for the local loopback candidate. No retries. */
5348
+ declare const LOCAL_LOOPBACK_PROBE_TIMEOUT_MS = 250;
5349
+ /** Probe timeout for local.tinycloud.link candidates. No retries. */
5350
+ declare const LOCAL_LINK_PROBE_TIMEOUT_MS = 750;
5351
+ /** Hostname suffix identifying a local-link tunnel candidate. */
5352
+ declare const LOCAL_LINK_HOST_SUFFIX = ".local.tinycloud.link";
5337
5353
  interface LocationCandidate {
5338
5354
  source: LocationSource;
5339
5355
  multiaddrs: string[];
@@ -5370,6 +5386,69 @@ interface ResolvedTinyCloudHosts {
5370
5386
  hosts: string[];
5371
5387
  location: ResolvedCloudLocation;
5372
5388
  }
5389
+ /**
5390
+ * Trust-on-first-use pin store for locally-discovered node identities, keyed
5391
+ * by candidate URL. The first successful `/info` check for a URL with no
5392
+ * existing pin adopts and pins that node's DID; every later check for the
5393
+ * same URL must match the pin. Implementations: CLI (profile store), web
5394
+ * (localStorage), node-sdk (config-provided callback, else in-memory).
5395
+ */
5396
+ interface LocalNodeIdentityStore {
5397
+ get(url: string): string | undefined | Promise<string | undefined>;
5398
+ set(url: string, nodeDid: string): void | Promise<void>;
5399
+ }
5400
+ /** In-memory {@link LocalNodeIdentityStore}. Does not persist across process restarts. */
5401
+ declare function createInMemoryLocalNodeIdentityStore(): LocalNodeIdentityStore;
5402
+ /**
5403
+ * Minimal subset of the DOM `Storage` interface. Declared locally so
5404
+ * sdk-core (no DOM lib) doesn't need to depend on browser types.
5405
+ */
5406
+ interface WebStorageLike {
5407
+ getItem(key: string): string | null;
5408
+ setItem(key: string, value: string): void;
5409
+ removeItem(key: string): void;
5410
+ }
5411
+ /** Default namespaced key {@link createLocalStorageLocalNodeIdentityStore} stores its pins under. */
5412
+ declare const DEFAULT_LOCAL_NODE_PIN_STORAGE_KEY = "tinycloud.localNodePins.v1";
5413
+ /**
5414
+ * `localStorage`-backed {@link LocalNodeIdentityStore}, namespaced under a
5415
+ * single JSON blob (default key {@link DEFAULT_LOCAL_NODE_PIN_STORAGE_KEY}).
5416
+ * Falls back to an in-memory store when `localStorage` is unavailable (SSR,
5417
+ * Node.js, sandboxed contexts that throw on access, etc.), so callers never
5418
+ * have to branch on environment. Corrupt stored JSON is reset rather than
5419
+ * thrown.
5420
+ */
5421
+ declare function createLocalStorageLocalNodeIdentityStore(storage?: WebStorageLike, storageKey?: string): LocalNodeIdentityStore;
5422
+ interface DiscoverLocalTinyCloudNodeOptions {
5423
+ /**
5424
+ * Subject DID used to look up the registry LocationRecord for
5425
+ * `*.local.tinycloud.link` multiaddrs (source `local-link`). Omit to skip
5426
+ * that lookup — the local-loopback and `localLinkName` candidates don't
5427
+ * need it.
5428
+ */
5429
+ subject?: string;
5430
+ /** Enable local-node auto-discovery. Default true. */
5431
+ autoDiscoverLocalNode?: boolean;
5432
+ /** Local loopback node URL to probe. Default http://127.0.0.1:8000. */
5433
+ localNodeUrl?: string;
5434
+ /** Known `*.local.tinycloud.link` subdomain name, probed directly. */
5435
+ localLinkName?: string;
5436
+ /** Centralized location registry URL, used for the `local-link` registry lookup. Default https://registry.tinycloud.xyz. */
5437
+ registryUrl?: string | null;
5438
+ /** Expected local node DID. Wins over any pinned value; a mismatch rejects the candidate. */
5439
+ expectedNodeDid?: string;
5440
+ /** Pin store for trust-on-first-use identity verification. Defaults to an in-memory store. */
5441
+ identityStore?: LocalNodeIdentityStore;
5442
+ /** Verify the registry LocationRecord signature before trusting its `local-link` multiaddrs. Default true. */
5443
+ verifyRecords?: boolean;
5444
+ /** Custom fetch implementation. Defaults to globalThis.fetch. */
5445
+ fetch?: typeof fetch;
5446
+ }
5447
+ interface DiscoveredLocalTinyCloudNode {
5448
+ source: Extract<LocationSource, "local-loopback" | "local-link">;
5449
+ url: string;
5450
+ nodeDid: string;
5451
+ }
5373
5452
  interface ResolveTinyCloudHostsOptions {
5374
5453
  /** Highest-priority TinyCloud HTTP host URLs or multiaddrs supplied directly. */
5375
5454
  explicitHosts?: string[];
@@ -5383,6 +5462,20 @@ interface ResolveTinyCloudHostsOptions {
5383
5462
  fetch?: typeof fetch;
5384
5463
  /** Verify centralized/blockchain record signatures. Default true. */
5385
5464
  verifyRecords?: boolean;
5465
+ /**
5466
+ * Probe for a locally-running TinyCloud node before falling back to
5467
+ * registry/hosted resolution. Default true. Setting false restores the
5468
+ * exact pre-TC-106 resolution order (registry → fallback).
5469
+ */
5470
+ autoDiscoverLocalNode?: boolean;
5471
+ /** Local loopback node URL to probe. Default http://127.0.0.1:8000. */
5472
+ localNodeUrl?: string;
5473
+ /** Known `*.local.tinycloud.link` subdomain name, probed directly. */
5474
+ localLinkName?: string;
5475
+ /** Expected local node DID. Wins over any pinned value; a mismatch rejects the candidate. */
5476
+ expectedNodeDid?: string;
5477
+ /** Pin store for trust-on-first-use local node identity verification. Defaults to an in-memory store. */
5478
+ localNodeIdentityStore?: LocalNodeIdentityStore;
5386
5479
  }
5387
5480
  type LocationCandidateInput = string[] | LocationRecord | {
5388
5481
  multiaddrs: string[];
@@ -5411,6 +5504,23 @@ declare function verifyLocationRecord(input: LocationRecord): Promise<boolean>;
5411
5504
  declare function fetchLocationRecord(registryUrl: string, subject: string, fetchFn?: typeof fetch): Promise<LocationRecord | null>;
5412
5505
  declare function resolveCloudLocation(subject: string, options?: ResolveCloudLocationOptions): Promise<ResolvedCloudLocation>;
5413
5506
  declare function resolveTinyCloudHosts(subject: string, options?: ResolveTinyCloudHostsOptions): Promise<ResolvedTinyCloudHosts>;
5507
+ /**
5508
+ * Probe for a locally-running TinyCloud node and verify its identity before
5509
+ * trusting it. Tries, in order: the local loopback URL, the explicit
5510
+ * `localLinkName` candidate (if given), then any `*.local.tinycloud.link`
5511
+ * multiaddr found in the subject's registry LocationRecord (if `subject` and
5512
+ * a registry are given). Each candidate is probed with `GET {url}/healthz`
5513
+ * (short timeout, no retries) — any failure (refused, timeout, NXDOMAIN,
5514
+ * offline) silently skips to the next candidate, no error, no log above
5515
+ * debug level. A candidate that answers healthy is then identity-checked via
5516
+ * `GET {url}/info`: if an expected DID is known (explicit or previously
5517
+ * pinned), the node's `nodeId` DID must match or the candidate is rejected;
5518
+ * if no expectation exists yet, the DID is trusted and pinned (TOFU).
5519
+ *
5520
+ * Returns `null` (never throws) when no local node is found or verified —
5521
+ * callers fall through to their normal remote resolution.
5522
+ */
5523
+ declare function discoverLocalTinyCloudNode(options?: DiscoverLocalTinyCloudNodeOptions): Promise<DiscoveredLocalTinyCloudNode | null>;
5414
5524
  declare function multiaddrToHttpUrl(input: string): string;
5415
5525
  declare function httpUrlToMultiaddr(input: string): string;
5416
5526
  declare function verifyDidKeyEd25519Signature(did: string, payload: Uint8Array, signature: Uint8Array): boolean;
@@ -5444,4 +5554,4 @@ declare const EXPIRY: {
5444
5554
  declare const DEFAULT_SIGNED_READ_URL_EXPIRY_MS: number;
5445
5555
  type ExpiryTier = keyof typeof EXPIRY;
5446
5556
 
5447
- export { ACCOUNT_REGISTRY_PATH, ACCOUNT_REGISTRY_SPACE, type AbilitiesMap, type AccountApplication, type AccountApplicationListOptions, type AccountDelegation, type AccountDelegationListOptions, type AccountDelegationPage, type AccountDelegationQueryOptions, type AccountDelegationRecord, type AccountDelegationResource, type AccountDelegationRevokeOptions, type AccountIndexEnsureResult, type AccountIndexRebuildResult, type AccountIndexStatus, type AccountIndexedReadOptions, AccountService, type AccountServiceConfig, type AccountSpace, type AccountSpaceListOptions, type AccountStatus, AutoApproveSpaceCreationHandler, type AutoRejectStrategy, type AutoSignStrategy, type Bytes, type CallbackStrategy, type CanonicalAddress, type CanonicalParsedNetworkId, type CapabilityEntry, CapabilityKeyRegistry, type CapabilityKeyRegistryErrorCode, CapabilityKeyRegistryErrorCodes, CaveatedDelegationUnsupportedError, type ClientSession, ClientSessionSchema, CloudLocationResolutionError, type ComposeManifestOptions, type ComposedManifestRequest, type CreateDelegationFunction, type CreateDelegationParams, type CreateDelegationWasmParams, type CreateDelegationWasmResult, DEFAULT_DEFAULTS, DEFAULT_EXPIRY, DEFAULT_KNOWLEDGE_ROOT, DEFAULT_MANIFEST_SPACE, DEFAULT_MANIFEST_VERSION, DEFAULT_SIGNED_READ_URL_EXPIRY_MS, DEFAULT_TINYCLOUD_FALLBACK_HOST, DEFAULT_TINYCLOUD_LOCATION_REGISTRY_URL, type DelegateReceivedShareParams, type DelegatedResource, type DelegatedShareAccess, type Delegation, type DelegationApiResponse, type DelegationChain, type DelegationChainV2, type DelegationDirection, type DelegationError, type DelegationErrorCode, DelegationErrorCodes, type DelegationFilters, DelegationManager, type DelegationManagerConfig, type DelegationRecord, type Result as DelegationResult, type DelegationRevocationReceipt, type DelegationStatus, type DidCacheKeyOptions, type DidEqualsOptions, ENCRYPTION_MANIFEST_SPACE, ENCRYPTION_PERMISSION_SERVICE, EXPIRY, type EncodedShareData, type EnsData, EnsDataSchema, type EventEmitterStrategy, type ExpiryTier, type Extension, type GenerateShareParams, type ICapabilityKeyRegistry, type IENSResolver, type INotificationHandler, type ISessionManager, type ISessionStorage, type ISharingService, type ISigner, type ISpace, type ISpaceCreationHandler, type ISpaceScopedDelegations, type ISpaceScopedSharing, type ISpaceService, type IUserAuthorization, type IWasmBindings, IdentityParseError, type IngestOptions, type JWK, type KeyInfo, type KeyProvider, type KeyType, type LocationCandidate, type LocationCandidateInput, type LocationRecord, type LocationRecordPayload, type LocationRecordSigner, LocationRecordValidationError, type LocationResolutionAttempt, type LocationSource, type Manifest, type ManifestDefaults, type ManifestKnowledge, type ManifestRegistryRecord, type ManifestSecretActions, ManifestValidationError, type NodeInfo, type OpenKeyCallbackStrategy, type OpenKeySigningRequestBody, type OpenKeySigningResponseBody, type OpenKeySigningStrategyOptions, type ParseRecapFromSiwe, type PartialSiweMessage, type PermissionEntry, PermissionNotInManifestError, type PersistedSessionData, type PersistedSessionProof, type PersistedTinyCloudSession, type PkhDidParts, ProtocolMismatchError, type ReceiveOptions, type ResolveCloudLocationOptions, type ResolveTinyCloudHostsOptions, type ResolvedCapabilities, type ResolvedCloudLocation, type ResolvedDelegate, type ResolvedTinyCloudHosts, type ResourceCapability, SECRETS_SPACE, SERVICE_LONG_TO_SHORT, SERVICE_SHORT_TO_LONG, type ServerHost, SessionExpiredError, type ShareAccess, type ShareLink, type ShareLinkData, type ShareSchema, SharingService, type SharingServiceConfig, type SignCallback, type SignInOptions, type SignRequest, type SignResponse, type SignStrategy, SilentNotificationHandler, type SiweConfig, SiweConfigSchema, Space, type SpaceAbilitiesMap, type SpaceConfig, type SpaceCreationContext, type SpaceDelegationParams, type SpaceErrorCode, SpaceErrorCodes, type SpaceHostResult, type SpaceInfo, type SpaceOwnership, SpaceService, type SpaceServiceConfig, type StoredDelegationChain, type SubsetCheckResult, TinyCloud, type TinyCloudConfig, type TinyCloudSession, UnsupportedFeatureError, type UserAuthorizationConfig, VAULT_PERMISSION_SERVICE, type ValidatedPersistedSessionProof, type ValidationError, VersionCheckError, type WasmRecapEntry, actionContains, activateSessionWithHost, addressStorageKey, applyPrefix, buildSpaceUri, canonicalLocationPayload, canonicalizeAddress, canonicalizeDid, canonicalizeDidUrl, canonicalizeNetworkId, canonicalizeRecapCaveats, checkNodeInfo, composeManifestRequest, createCapabilityKeyRegistry, createOpenKeyCallbackSigningStrategy, createSharingService, createSpaceService, defaultSignStrategy, defaultSpaceCreationHandler, didCacheKey, didEquals, expandActionShortNames, expandPermissionEntries, expandPermissionEntry, fetchLocationRecord, fetchPeerId, httpUrlToMultiaddr, isCapabilitySubset, isEvmAddress, loadManifest, locationPayloadForRecord, makePkhSpaceId, makePublicSpaceId, manifestAbilitiesUnion, multiaddrToHttpUrl, normalizeDefaults, parseCanonicalNetworkId, parseExpiry, parsePkhDid, parseRecapCapabilities, parseSpaceUri, pkhDid, principalDid, principalDidEquals, recapCaveatsEqual, resolveCloudLocation, resolveManifest, resolveManifestKnowledgeRoot, resolveTinyCloudHosts, resourceCapabilitiesToAbilitiesMap, resourceCapabilitiesToSpaceAbilitiesMap, signLocationRecord, submitHostDelegation, validateClientSession, validateLocationRecord, validateLocationRecordPayload, validateManifest, validatePersistedSessionData, verifyDidKeyEd25519Signature, verifyLocationRecord };
5557
+ export { ACCOUNT_REGISTRY_PATH, ACCOUNT_REGISTRY_SPACE, type AbilitiesMap, type AccountApplication, type AccountApplicationListOptions, type AccountDelegation, type AccountDelegationListOptions, type AccountDelegationPage, type AccountDelegationQueryOptions, type AccountDelegationRecord, type AccountDelegationResource, type AccountDelegationRevokeOptions, type AccountIndexEnsureResult, type AccountIndexRebuildResult, type AccountIndexStatus, type AccountIndexedReadOptions, AccountService, type AccountServiceConfig, type AccountSpace, type AccountSpaceListOptions, type AccountStatus, AutoApproveSpaceCreationHandler, type AutoRejectStrategy, type AutoSignStrategy, type Bytes, type CallbackStrategy, type CanonicalAddress, type CanonicalParsedNetworkId, type CapabilityEntry, CapabilityKeyRegistry, type CapabilityKeyRegistryErrorCode, CapabilityKeyRegistryErrorCodes, CaveatedDelegationUnsupportedError, type ClientSession, ClientSessionSchema, CloudLocationResolutionError, type ComposeManifestOptions, type ComposedManifestRequest, type CreateDelegationFunction, type CreateDelegationParams, type CreateDelegationWasmParams, type CreateDelegationWasmResult, DEFAULT_DEFAULTS, DEFAULT_EXPIRY, DEFAULT_KNOWLEDGE_ROOT, DEFAULT_LOCAL_NODE_PIN_STORAGE_KEY, DEFAULT_LOCAL_NODE_URL, DEFAULT_MANIFEST_SPACE, DEFAULT_MANIFEST_VERSION, DEFAULT_SIGNED_READ_URL_EXPIRY_MS, DEFAULT_TINYCLOUD_FALLBACK_HOST, DEFAULT_TINYCLOUD_LOCATION_REGISTRY_URL, type DelegateReceivedShareParams, type DelegatedResource, type DelegatedShareAccess, type Delegation, type DelegationApiResponse, type DelegationChain, type DelegationChainV2, type DelegationDirection, type DelegationError, type DelegationErrorCode, DelegationErrorCodes, type DelegationFilters, DelegationManager, type DelegationManagerConfig, type DelegationRecord, type Result as DelegationResult, type DelegationRevocationReceipt, type DelegationStatus, type DidCacheKeyOptions, type DidEqualsOptions, type DiscoverLocalTinyCloudNodeOptions, type DiscoveredLocalTinyCloudNode, ENCRYPTION_MANIFEST_SPACE, ENCRYPTION_PERMISSION_SERVICE, EXPIRY, type EncodedShareData, type EnsData, EnsDataSchema, type EventEmitterStrategy, type ExpiryTier, type Extension, type GenerateShareParams, type ICapabilityKeyRegistry, type IENSResolver, type INotificationHandler, type ISessionManager, type ISessionStorage, type ISharingService, type ISigner, type ISpace, type ISpaceCreationHandler, type ISpaceScopedDelegations, type ISpaceScopedSharing, type ISpaceService, type IUserAuthorization, type IWasmBindings, IdentityParseError, type IngestOptions, type JWK, type KeyInfo, type KeyProvider, type KeyType, LOCAL_LINK_HOST_SUFFIX, LOCAL_LINK_PROBE_TIMEOUT_MS, LOCAL_LOOPBACK_PROBE_TIMEOUT_MS, type LocalNodeIdentityStore, type LocationCandidate, type LocationCandidateInput, type LocationRecord, type LocationRecordPayload, type LocationRecordSigner, LocationRecordValidationError, type LocationResolutionAttempt, type LocationSource, type Manifest, type ManifestDefaults, type ManifestKnowledge, type ManifestRegistryRecord, type ManifestSecretActions, ManifestValidationError, type NodeInfo, type OpenKeyCallbackStrategy, type OpenKeySigningRequestBody, type OpenKeySigningResponseBody, type OpenKeySigningStrategyOptions, type ParseRecapFromSiwe, type PartialSiweMessage, type PermissionEntry, PermissionNotInManifestError, type PersistedSessionData, type PersistedSessionProof, type PersistedTinyCloudSession, type PkhDidParts, ProtocolMismatchError, type ReceiveOptions, type ResolveCloudLocationOptions, type ResolveTinyCloudHostsOptions, type ResolvedCapabilities, type ResolvedCloudLocation, type ResolvedDelegate, type ResolvedTinyCloudHosts, type ResourceCapability, SECRETS_SPACE, SERVICE_LONG_TO_SHORT, SERVICE_SHORT_TO_LONG, type ServerHost, SessionExpiredError, type ShareAccess, type ShareLink, type ShareLinkData, type ShareSchema, SharingService, type SharingServiceConfig, type SignCallback, type SignInOptions, type SignRequest, type SignResponse, type SignStrategy, SilentNotificationHandler, type SiweConfig, SiweConfigSchema, Space, type SpaceAbilitiesMap, type SpaceConfig, type SpaceCreationContext, type SpaceDelegationParams, type SpaceErrorCode, SpaceErrorCodes, type SpaceHostResult, type SpaceInfo, type SpaceOwnership, SpaceService, type SpaceServiceConfig, type StoredDelegationChain, type SubsetCheckResult, TinyCloud, type TinyCloudConfig, type TinyCloudSession, UnsupportedFeatureError, type UserAuthorizationConfig, VAULT_PERMISSION_SERVICE, type ValidatedPersistedSessionProof, type ValidationError, VersionCheckError, type WasmRecapEntry, type WebStorageLike, actionContains, activateSessionWithHost, addressStorageKey, applyPrefix, buildSpaceUri, canonicalLocationPayload, canonicalizeAddress, canonicalizeDid, canonicalizeDidUrl, canonicalizeNetworkId, canonicalizeRecapCaveats, checkNodeInfo, composeManifestRequest, createCapabilityKeyRegistry, createInMemoryLocalNodeIdentityStore, createLocalStorageLocalNodeIdentityStore, createOpenKeyCallbackSigningStrategy, createSharingService, createSpaceService, defaultSignStrategy, defaultSpaceCreationHandler, didCacheKey, didEquals, discoverLocalTinyCloudNode, expandActionShortNames, expandPermissionEntries, expandPermissionEntry, fetchLocationRecord, fetchPeerId, httpUrlToMultiaddr, isCapabilitySubset, isEvmAddress, loadManifest, locationPayloadForRecord, makePkhSpaceId, makePublicSpaceId, manifestAbilitiesUnion, multiaddrToHttpUrl, normalizeDefaults, parseCanonicalNetworkId, parseExpiry, parsePkhDid, parseRecapCapabilities, parseSpaceUri, pkhDid, principalDid, principalDidEquals, recapCaveatsEqual, resolveCloudLocation, resolveManifest, resolveManifestKnowledgeRoot, resolveTinyCloudHosts, resourceCapabilitiesToAbilitiesMap, resourceCapabilitiesToSpaceAbilitiesMap, signLocationRecord, submitHostDelegation, validateClientSession, validateLocationRecord, validateLocationRecordPayload, validateManifest, validatePersistedSessionData, verifyDidKeyEd25519Signature, verifyLocationRecord };
package/dist/index.d.ts CHANGED
@@ -5331,9 +5331,25 @@ interface LocationRecordPayload {
5331
5331
  interface LocationRecord extends LocationRecordPayload {
5332
5332
  signature: string;
5333
5333
  }
5334
- type LocationSource = "explicit" | "blockchain" | "centralized" | "fallback";
5334
+ /**
5335
+ * Where a resolved TinyCloud host came from, ordered highest to lowest
5336
+ * priority. `local-loopback` and `local-link` are probed + identity-verified
5337
+ * (see {@link discoverLocalTinyCloudNode}); the rest are resolved the same
5338
+ * way they always have been (no liveness probing). New local-discovery
5339
+ * sources (e.g. a future `tunnel` or `mdns` source) slot in next to
5340
+ * `local-link` without touching the unprobed sources below them.
5341
+ */
5342
+ type LocationSource = "explicit" | "local-loopback" | "local-link" | "blockchain" | "centralized" | "fallback";
5335
5343
  declare const DEFAULT_TINYCLOUD_LOCATION_REGISTRY_URL = "https://registry.tinycloud.xyz";
5336
5344
  declare const DEFAULT_TINYCLOUD_FALLBACK_HOST = "https://node.tinycloud.xyz";
5345
+ /** Default local loopback node URL probed before registry/fallback resolution. */
5346
+ declare const DEFAULT_LOCAL_NODE_URL = "http://127.0.0.1:8000";
5347
+ /** Probe timeout for the local loopback candidate. No retries. */
5348
+ declare const LOCAL_LOOPBACK_PROBE_TIMEOUT_MS = 250;
5349
+ /** Probe timeout for local.tinycloud.link candidates. No retries. */
5350
+ declare const LOCAL_LINK_PROBE_TIMEOUT_MS = 750;
5351
+ /** Hostname suffix identifying a local-link tunnel candidate. */
5352
+ declare const LOCAL_LINK_HOST_SUFFIX = ".local.tinycloud.link";
5337
5353
  interface LocationCandidate {
5338
5354
  source: LocationSource;
5339
5355
  multiaddrs: string[];
@@ -5370,6 +5386,69 @@ interface ResolvedTinyCloudHosts {
5370
5386
  hosts: string[];
5371
5387
  location: ResolvedCloudLocation;
5372
5388
  }
5389
+ /**
5390
+ * Trust-on-first-use pin store for locally-discovered node identities, keyed
5391
+ * by candidate URL. The first successful `/info` check for a URL with no
5392
+ * existing pin adopts and pins that node's DID; every later check for the
5393
+ * same URL must match the pin. Implementations: CLI (profile store), web
5394
+ * (localStorage), node-sdk (config-provided callback, else in-memory).
5395
+ */
5396
+ interface LocalNodeIdentityStore {
5397
+ get(url: string): string | undefined | Promise<string | undefined>;
5398
+ set(url: string, nodeDid: string): void | Promise<void>;
5399
+ }
5400
+ /** In-memory {@link LocalNodeIdentityStore}. Does not persist across process restarts. */
5401
+ declare function createInMemoryLocalNodeIdentityStore(): LocalNodeIdentityStore;
5402
+ /**
5403
+ * Minimal subset of the DOM `Storage` interface. Declared locally so
5404
+ * sdk-core (no DOM lib) doesn't need to depend on browser types.
5405
+ */
5406
+ interface WebStorageLike {
5407
+ getItem(key: string): string | null;
5408
+ setItem(key: string, value: string): void;
5409
+ removeItem(key: string): void;
5410
+ }
5411
+ /** Default namespaced key {@link createLocalStorageLocalNodeIdentityStore} stores its pins under. */
5412
+ declare const DEFAULT_LOCAL_NODE_PIN_STORAGE_KEY = "tinycloud.localNodePins.v1";
5413
+ /**
5414
+ * `localStorage`-backed {@link LocalNodeIdentityStore}, namespaced under a
5415
+ * single JSON blob (default key {@link DEFAULT_LOCAL_NODE_PIN_STORAGE_KEY}).
5416
+ * Falls back to an in-memory store when `localStorage` is unavailable (SSR,
5417
+ * Node.js, sandboxed contexts that throw on access, etc.), so callers never
5418
+ * have to branch on environment. Corrupt stored JSON is reset rather than
5419
+ * thrown.
5420
+ */
5421
+ declare function createLocalStorageLocalNodeIdentityStore(storage?: WebStorageLike, storageKey?: string): LocalNodeIdentityStore;
5422
+ interface DiscoverLocalTinyCloudNodeOptions {
5423
+ /**
5424
+ * Subject DID used to look up the registry LocationRecord for
5425
+ * `*.local.tinycloud.link` multiaddrs (source `local-link`). Omit to skip
5426
+ * that lookup — the local-loopback and `localLinkName` candidates don't
5427
+ * need it.
5428
+ */
5429
+ subject?: string;
5430
+ /** Enable local-node auto-discovery. Default true. */
5431
+ autoDiscoverLocalNode?: boolean;
5432
+ /** Local loopback node URL to probe. Default http://127.0.0.1:8000. */
5433
+ localNodeUrl?: string;
5434
+ /** Known `*.local.tinycloud.link` subdomain name, probed directly. */
5435
+ localLinkName?: string;
5436
+ /** Centralized location registry URL, used for the `local-link` registry lookup. Default https://registry.tinycloud.xyz. */
5437
+ registryUrl?: string | null;
5438
+ /** Expected local node DID. Wins over any pinned value; a mismatch rejects the candidate. */
5439
+ expectedNodeDid?: string;
5440
+ /** Pin store for trust-on-first-use identity verification. Defaults to an in-memory store. */
5441
+ identityStore?: LocalNodeIdentityStore;
5442
+ /** Verify the registry LocationRecord signature before trusting its `local-link` multiaddrs. Default true. */
5443
+ verifyRecords?: boolean;
5444
+ /** Custom fetch implementation. Defaults to globalThis.fetch. */
5445
+ fetch?: typeof fetch;
5446
+ }
5447
+ interface DiscoveredLocalTinyCloudNode {
5448
+ source: Extract<LocationSource, "local-loopback" | "local-link">;
5449
+ url: string;
5450
+ nodeDid: string;
5451
+ }
5373
5452
  interface ResolveTinyCloudHostsOptions {
5374
5453
  /** Highest-priority TinyCloud HTTP host URLs or multiaddrs supplied directly. */
5375
5454
  explicitHosts?: string[];
@@ -5383,6 +5462,20 @@ interface ResolveTinyCloudHostsOptions {
5383
5462
  fetch?: typeof fetch;
5384
5463
  /** Verify centralized/blockchain record signatures. Default true. */
5385
5464
  verifyRecords?: boolean;
5465
+ /**
5466
+ * Probe for a locally-running TinyCloud node before falling back to
5467
+ * registry/hosted resolution. Default true. Setting false restores the
5468
+ * exact pre-TC-106 resolution order (registry → fallback).
5469
+ */
5470
+ autoDiscoverLocalNode?: boolean;
5471
+ /** Local loopback node URL to probe. Default http://127.0.0.1:8000. */
5472
+ localNodeUrl?: string;
5473
+ /** Known `*.local.tinycloud.link` subdomain name, probed directly. */
5474
+ localLinkName?: string;
5475
+ /** Expected local node DID. Wins over any pinned value; a mismatch rejects the candidate. */
5476
+ expectedNodeDid?: string;
5477
+ /** Pin store for trust-on-first-use local node identity verification. Defaults to an in-memory store. */
5478
+ localNodeIdentityStore?: LocalNodeIdentityStore;
5386
5479
  }
5387
5480
  type LocationCandidateInput = string[] | LocationRecord | {
5388
5481
  multiaddrs: string[];
@@ -5411,6 +5504,23 @@ declare function verifyLocationRecord(input: LocationRecord): Promise<boolean>;
5411
5504
  declare function fetchLocationRecord(registryUrl: string, subject: string, fetchFn?: typeof fetch): Promise<LocationRecord | null>;
5412
5505
  declare function resolveCloudLocation(subject: string, options?: ResolveCloudLocationOptions): Promise<ResolvedCloudLocation>;
5413
5506
  declare function resolveTinyCloudHosts(subject: string, options?: ResolveTinyCloudHostsOptions): Promise<ResolvedTinyCloudHosts>;
5507
+ /**
5508
+ * Probe for a locally-running TinyCloud node and verify its identity before
5509
+ * trusting it. Tries, in order: the local loopback URL, the explicit
5510
+ * `localLinkName` candidate (if given), then any `*.local.tinycloud.link`
5511
+ * multiaddr found in the subject's registry LocationRecord (if `subject` and
5512
+ * a registry are given). Each candidate is probed with `GET {url}/healthz`
5513
+ * (short timeout, no retries) — any failure (refused, timeout, NXDOMAIN,
5514
+ * offline) silently skips to the next candidate, no error, no log above
5515
+ * debug level. A candidate that answers healthy is then identity-checked via
5516
+ * `GET {url}/info`: if an expected DID is known (explicit or previously
5517
+ * pinned), the node's `nodeId` DID must match or the candidate is rejected;
5518
+ * if no expectation exists yet, the DID is trusted and pinned (TOFU).
5519
+ *
5520
+ * Returns `null` (never throws) when no local node is found or verified —
5521
+ * callers fall through to their normal remote resolution.
5522
+ */
5523
+ declare function discoverLocalTinyCloudNode(options?: DiscoverLocalTinyCloudNodeOptions): Promise<DiscoveredLocalTinyCloudNode | null>;
5414
5524
  declare function multiaddrToHttpUrl(input: string): string;
5415
5525
  declare function httpUrlToMultiaddr(input: string): string;
5416
5526
  declare function verifyDidKeyEd25519Signature(did: string, payload: Uint8Array, signature: Uint8Array): boolean;
@@ -5444,4 +5554,4 @@ declare const EXPIRY: {
5444
5554
  declare const DEFAULT_SIGNED_READ_URL_EXPIRY_MS: number;
5445
5555
  type ExpiryTier = keyof typeof EXPIRY;
5446
5556
 
5447
- export { ACCOUNT_REGISTRY_PATH, ACCOUNT_REGISTRY_SPACE, type AbilitiesMap, type AccountApplication, type AccountApplicationListOptions, type AccountDelegation, type AccountDelegationListOptions, type AccountDelegationPage, type AccountDelegationQueryOptions, type AccountDelegationRecord, type AccountDelegationResource, type AccountDelegationRevokeOptions, type AccountIndexEnsureResult, type AccountIndexRebuildResult, type AccountIndexStatus, type AccountIndexedReadOptions, AccountService, type AccountServiceConfig, type AccountSpace, type AccountSpaceListOptions, type AccountStatus, AutoApproveSpaceCreationHandler, type AutoRejectStrategy, type AutoSignStrategy, type Bytes, type CallbackStrategy, type CanonicalAddress, type CanonicalParsedNetworkId, type CapabilityEntry, CapabilityKeyRegistry, type CapabilityKeyRegistryErrorCode, CapabilityKeyRegistryErrorCodes, CaveatedDelegationUnsupportedError, type ClientSession, ClientSessionSchema, CloudLocationResolutionError, type ComposeManifestOptions, type ComposedManifestRequest, type CreateDelegationFunction, type CreateDelegationParams, type CreateDelegationWasmParams, type CreateDelegationWasmResult, DEFAULT_DEFAULTS, DEFAULT_EXPIRY, DEFAULT_KNOWLEDGE_ROOT, DEFAULT_MANIFEST_SPACE, DEFAULT_MANIFEST_VERSION, DEFAULT_SIGNED_READ_URL_EXPIRY_MS, DEFAULT_TINYCLOUD_FALLBACK_HOST, DEFAULT_TINYCLOUD_LOCATION_REGISTRY_URL, type DelegateReceivedShareParams, type DelegatedResource, type DelegatedShareAccess, type Delegation, type DelegationApiResponse, type DelegationChain, type DelegationChainV2, type DelegationDirection, type DelegationError, type DelegationErrorCode, DelegationErrorCodes, type DelegationFilters, DelegationManager, type DelegationManagerConfig, type DelegationRecord, type Result as DelegationResult, type DelegationRevocationReceipt, type DelegationStatus, type DidCacheKeyOptions, type DidEqualsOptions, ENCRYPTION_MANIFEST_SPACE, ENCRYPTION_PERMISSION_SERVICE, EXPIRY, type EncodedShareData, type EnsData, EnsDataSchema, type EventEmitterStrategy, type ExpiryTier, type Extension, type GenerateShareParams, type ICapabilityKeyRegistry, type IENSResolver, type INotificationHandler, type ISessionManager, type ISessionStorage, type ISharingService, type ISigner, type ISpace, type ISpaceCreationHandler, type ISpaceScopedDelegations, type ISpaceScopedSharing, type ISpaceService, type IUserAuthorization, type IWasmBindings, IdentityParseError, type IngestOptions, type JWK, type KeyInfo, type KeyProvider, type KeyType, type LocationCandidate, type LocationCandidateInput, type LocationRecord, type LocationRecordPayload, type LocationRecordSigner, LocationRecordValidationError, type LocationResolutionAttempt, type LocationSource, type Manifest, type ManifestDefaults, type ManifestKnowledge, type ManifestRegistryRecord, type ManifestSecretActions, ManifestValidationError, type NodeInfo, type OpenKeyCallbackStrategy, type OpenKeySigningRequestBody, type OpenKeySigningResponseBody, type OpenKeySigningStrategyOptions, type ParseRecapFromSiwe, type PartialSiweMessage, type PermissionEntry, PermissionNotInManifestError, type PersistedSessionData, type PersistedSessionProof, type PersistedTinyCloudSession, type PkhDidParts, ProtocolMismatchError, type ReceiveOptions, type ResolveCloudLocationOptions, type ResolveTinyCloudHostsOptions, type ResolvedCapabilities, type ResolvedCloudLocation, type ResolvedDelegate, type ResolvedTinyCloudHosts, type ResourceCapability, SECRETS_SPACE, SERVICE_LONG_TO_SHORT, SERVICE_SHORT_TO_LONG, type ServerHost, SessionExpiredError, type ShareAccess, type ShareLink, type ShareLinkData, type ShareSchema, SharingService, type SharingServiceConfig, type SignCallback, type SignInOptions, type SignRequest, type SignResponse, type SignStrategy, SilentNotificationHandler, type SiweConfig, SiweConfigSchema, Space, type SpaceAbilitiesMap, type SpaceConfig, type SpaceCreationContext, type SpaceDelegationParams, type SpaceErrorCode, SpaceErrorCodes, type SpaceHostResult, type SpaceInfo, type SpaceOwnership, SpaceService, type SpaceServiceConfig, type StoredDelegationChain, type SubsetCheckResult, TinyCloud, type TinyCloudConfig, type TinyCloudSession, UnsupportedFeatureError, type UserAuthorizationConfig, VAULT_PERMISSION_SERVICE, type ValidatedPersistedSessionProof, type ValidationError, VersionCheckError, type WasmRecapEntry, actionContains, activateSessionWithHost, addressStorageKey, applyPrefix, buildSpaceUri, canonicalLocationPayload, canonicalizeAddress, canonicalizeDid, canonicalizeDidUrl, canonicalizeNetworkId, canonicalizeRecapCaveats, checkNodeInfo, composeManifestRequest, createCapabilityKeyRegistry, createOpenKeyCallbackSigningStrategy, createSharingService, createSpaceService, defaultSignStrategy, defaultSpaceCreationHandler, didCacheKey, didEquals, expandActionShortNames, expandPermissionEntries, expandPermissionEntry, fetchLocationRecord, fetchPeerId, httpUrlToMultiaddr, isCapabilitySubset, isEvmAddress, loadManifest, locationPayloadForRecord, makePkhSpaceId, makePublicSpaceId, manifestAbilitiesUnion, multiaddrToHttpUrl, normalizeDefaults, parseCanonicalNetworkId, parseExpiry, parsePkhDid, parseRecapCapabilities, parseSpaceUri, pkhDid, principalDid, principalDidEquals, recapCaveatsEqual, resolveCloudLocation, resolveManifest, resolveManifestKnowledgeRoot, resolveTinyCloudHosts, resourceCapabilitiesToAbilitiesMap, resourceCapabilitiesToSpaceAbilitiesMap, signLocationRecord, submitHostDelegation, validateClientSession, validateLocationRecord, validateLocationRecordPayload, validateManifest, validatePersistedSessionData, verifyDidKeyEd25519Signature, verifyLocationRecord };
5557
+ export { ACCOUNT_REGISTRY_PATH, ACCOUNT_REGISTRY_SPACE, type AbilitiesMap, type AccountApplication, type AccountApplicationListOptions, type AccountDelegation, type AccountDelegationListOptions, type AccountDelegationPage, type AccountDelegationQueryOptions, type AccountDelegationRecord, type AccountDelegationResource, type AccountDelegationRevokeOptions, type AccountIndexEnsureResult, type AccountIndexRebuildResult, type AccountIndexStatus, type AccountIndexedReadOptions, AccountService, type AccountServiceConfig, type AccountSpace, type AccountSpaceListOptions, type AccountStatus, AutoApproveSpaceCreationHandler, type AutoRejectStrategy, type AutoSignStrategy, type Bytes, type CallbackStrategy, type CanonicalAddress, type CanonicalParsedNetworkId, type CapabilityEntry, CapabilityKeyRegistry, type CapabilityKeyRegistryErrorCode, CapabilityKeyRegistryErrorCodes, CaveatedDelegationUnsupportedError, type ClientSession, ClientSessionSchema, CloudLocationResolutionError, type ComposeManifestOptions, type ComposedManifestRequest, type CreateDelegationFunction, type CreateDelegationParams, type CreateDelegationWasmParams, type CreateDelegationWasmResult, DEFAULT_DEFAULTS, DEFAULT_EXPIRY, DEFAULT_KNOWLEDGE_ROOT, DEFAULT_LOCAL_NODE_PIN_STORAGE_KEY, DEFAULT_LOCAL_NODE_URL, DEFAULT_MANIFEST_SPACE, DEFAULT_MANIFEST_VERSION, DEFAULT_SIGNED_READ_URL_EXPIRY_MS, DEFAULT_TINYCLOUD_FALLBACK_HOST, DEFAULT_TINYCLOUD_LOCATION_REGISTRY_URL, type DelegateReceivedShareParams, type DelegatedResource, type DelegatedShareAccess, type Delegation, type DelegationApiResponse, type DelegationChain, type DelegationChainV2, type DelegationDirection, type DelegationError, type DelegationErrorCode, DelegationErrorCodes, type DelegationFilters, DelegationManager, type DelegationManagerConfig, type DelegationRecord, type Result as DelegationResult, type DelegationRevocationReceipt, type DelegationStatus, type DidCacheKeyOptions, type DidEqualsOptions, type DiscoverLocalTinyCloudNodeOptions, type DiscoveredLocalTinyCloudNode, ENCRYPTION_MANIFEST_SPACE, ENCRYPTION_PERMISSION_SERVICE, EXPIRY, type EncodedShareData, type EnsData, EnsDataSchema, type EventEmitterStrategy, type ExpiryTier, type Extension, type GenerateShareParams, type ICapabilityKeyRegistry, type IENSResolver, type INotificationHandler, type ISessionManager, type ISessionStorage, type ISharingService, type ISigner, type ISpace, type ISpaceCreationHandler, type ISpaceScopedDelegations, type ISpaceScopedSharing, type ISpaceService, type IUserAuthorization, type IWasmBindings, IdentityParseError, type IngestOptions, type JWK, type KeyInfo, type KeyProvider, type KeyType, LOCAL_LINK_HOST_SUFFIX, LOCAL_LINK_PROBE_TIMEOUT_MS, LOCAL_LOOPBACK_PROBE_TIMEOUT_MS, type LocalNodeIdentityStore, type LocationCandidate, type LocationCandidateInput, type LocationRecord, type LocationRecordPayload, type LocationRecordSigner, LocationRecordValidationError, type LocationResolutionAttempt, type LocationSource, type Manifest, type ManifestDefaults, type ManifestKnowledge, type ManifestRegistryRecord, type ManifestSecretActions, ManifestValidationError, type NodeInfo, type OpenKeyCallbackStrategy, type OpenKeySigningRequestBody, type OpenKeySigningResponseBody, type OpenKeySigningStrategyOptions, type ParseRecapFromSiwe, type PartialSiweMessage, type PermissionEntry, PermissionNotInManifestError, type PersistedSessionData, type PersistedSessionProof, type PersistedTinyCloudSession, type PkhDidParts, ProtocolMismatchError, type ReceiveOptions, type ResolveCloudLocationOptions, type ResolveTinyCloudHostsOptions, type ResolvedCapabilities, type ResolvedCloudLocation, type ResolvedDelegate, type ResolvedTinyCloudHosts, type ResourceCapability, SECRETS_SPACE, SERVICE_LONG_TO_SHORT, SERVICE_SHORT_TO_LONG, type ServerHost, SessionExpiredError, type ShareAccess, type ShareLink, type ShareLinkData, type ShareSchema, SharingService, type SharingServiceConfig, type SignCallback, type SignInOptions, type SignRequest, type SignResponse, type SignStrategy, SilentNotificationHandler, type SiweConfig, SiweConfigSchema, Space, type SpaceAbilitiesMap, type SpaceConfig, type SpaceCreationContext, type SpaceDelegationParams, type SpaceErrorCode, SpaceErrorCodes, type SpaceHostResult, type SpaceInfo, type SpaceOwnership, SpaceService, type SpaceServiceConfig, type StoredDelegationChain, type SubsetCheckResult, TinyCloud, type TinyCloudConfig, type TinyCloudSession, UnsupportedFeatureError, type UserAuthorizationConfig, VAULT_PERMISSION_SERVICE, type ValidatedPersistedSessionProof, type ValidationError, VersionCheckError, type WasmRecapEntry, type WebStorageLike, actionContains, activateSessionWithHost, addressStorageKey, applyPrefix, buildSpaceUri, canonicalLocationPayload, canonicalizeAddress, canonicalizeDid, canonicalizeDidUrl, canonicalizeNetworkId, canonicalizeRecapCaveats, checkNodeInfo, composeManifestRequest, createCapabilityKeyRegistry, createInMemoryLocalNodeIdentityStore, createLocalStorageLocalNodeIdentityStore, createOpenKeyCallbackSigningStrategy, createSharingService, createSpaceService, defaultSignStrategy, defaultSpaceCreationHandler, didCacheKey, didEquals, discoverLocalTinyCloudNode, expandActionShortNames, expandPermissionEntries, expandPermissionEntry, fetchLocationRecord, fetchPeerId, httpUrlToMultiaddr, isCapabilitySubset, isEvmAddress, loadManifest, locationPayloadForRecord, makePkhSpaceId, makePublicSpaceId, manifestAbilitiesUnion, multiaddrToHttpUrl, normalizeDefaults, parseCanonicalNetworkId, parseExpiry, parsePkhDid, parseRecapCapabilities, parseSpaceUri, pkhDid, principalDid, principalDidEquals, recapCaveatsEqual, resolveCloudLocation, resolveManifest, resolveManifestKnowledgeRoot, resolveTinyCloudHosts, resourceCapabilitiesToAbilitiesMap, resourceCapabilitiesToSpaceAbilitiesMap, signLocationRecord, submitHostDelegation, validateClientSession, validateLocationRecord, validateLocationRecordPayload, validateManifest, validatePersistedSessionData, verifyDidKeyEd25519Signature, verifyLocationRecord };
package/dist/index.js CHANGED
@@ -12282,6 +12282,54 @@ import { ed25519 as ed255193 } from "@noble/curves/ed25519";
12282
12282
  import { verifyMessage as verifyMessage2 } from "viem";
12283
12283
  var DEFAULT_TINYCLOUD_LOCATION_REGISTRY_URL = "https://registry.tinycloud.xyz";
12284
12284
  var DEFAULT_TINYCLOUD_FALLBACK_HOST = "https://node.tinycloud.xyz";
12285
+ var DEFAULT_LOCAL_NODE_URL = "http://127.0.0.1:8000";
12286
+ var LOCAL_LOOPBACK_PROBE_TIMEOUT_MS = 250;
12287
+ var LOCAL_LINK_PROBE_TIMEOUT_MS = 750;
12288
+ var LOCAL_LINK_HOST_SUFFIX = ".local.tinycloud.link";
12289
+ function createInMemoryLocalNodeIdentityStore() {
12290
+ const pins = /* @__PURE__ */ new Map();
12291
+ return {
12292
+ get: (url) => pins.get(url),
12293
+ set: (url, nodeDid) => {
12294
+ pins.set(url, nodeDid);
12295
+ }
12296
+ };
12297
+ }
12298
+ var DEFAULT_LOCAL_NODE_PIN_STORAGE_KEY = "tinycloud.localNodePins.v1";
12299
+ function defaultWebStorage() {
12300
+ try {
12301
+ return globalThis.localStorage;
12302
+ } catch {
12303
+ return void 0;
12304
+ }
12305
+ }
12306
+ function createLocalStorageLocalNodeIdentityStore(storage, storageKey = DEFAULT_LOCAL_NODE_PIN_STORAGE_KEY) {
12307
+ const backing = storage ?? defaultWebStorage();
12308
+ if (!backing) {
12309
+ return createInMemoryLocalNodeIdentityStore();
12310
+ }
12311
+ const readPins = () => {
12312
+ const raw = backing.getItem(storageKey);
12313
+ if (!raw) return {};
12314
+ try {
12315
+ const parsed = JSON.parse(raw);
12316
+ if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
12317
+ return parsed;
12318
+ }
12319
+ } catch {
12320
+ }
12321
+ backing.removeItem(storageKey);
12322
+ return {};
12323
+ };
12324
+ return {
12325
+ get: (url) => readPins()[url],
12326
+ set: (url, nodeDid) => {
12327
+ const pins = readPins();
12328
+ pins[url] = nodeDid;
12329
+ backing.setItem(storageKey, JSON.stringify(pins));
12330
+ }
12331
+ };
12332
+ }
12285
12333
  var LocationRecordValidationError = class extends Error {
12286
12334
  constructor(message) {
12287
12335
  super(`Location record validation failed: ${message}`);
@@ -12408,6 +12456,33 @@ async function resolveCloudLocation(subject, options2 = {}) {
12408
12456
  };
12409
12457
  }
12410
12458
  async function resolveTinyCloudHosts(subject, options2 = {}) {
12459
+ const hasExplicitHosts = options2.explicitHosts !== void 0 && options2.explicitHosts.length > 0;
12460
+ const fetchFn = memoizeFetch(options2.fetch ?? globalThis.fetch);
12461
+ if (!hasExplicitHosts && (options2.autoDiscoverLocalNode ?? true)) {
12462
+ const local = await discoverLocalTinyCloudNode({
12463
+ subject,
12464
+ autoDiscoverLocalNode: true,
12465
+ localNodeUrl: options2.localNodeUrl,
12466
+ localLinkName: options2.localLinkName,
12467
+ registryUrl: options2.registryUrl,
12468
+ expectedNodeDid: options2.expectedNodeDid,
12469
+ identityStore: options2.localNodeIdentityStore,
12470
+ verifyRecords: options2.verifyRecords,
12471
+ fetch: fetchFn
12472
+ });
12473
+ if (local) {
12474
+ const multiaddrs = [httpUrlToMultiaddr(local.url)];
12475
+ const candidate = { source: local.source, multiaddrs };
12476
+ const location2 = {
12477
+ subject,
12478
+ source: local.source,
12479
+ multiaddrs,
12480
+ attempts: [{ source: local.source, candidate }],
12481
+ resolvedAt: (/* @__PURE__ */ new Date()).toISOString()
12482
+ };
12483
+ return { hosts: [local.url], location: location2 };
12484
+ }
12485
+ }
12411
12486
  const location = await resolveCloudLocation(subject, {
12412
12487
  explicitMultiaddrs: hostsToMultiaddrs(options2.explicitHosts),
12413
12488
  blockchain: options2.blockchain,
@@ -12415,7 +12490,7 @@ async function resolveTinyCloudHosts(subject, options2 = {}) {
12415
12490
  fallbackMultiaddrs: hostsToMultiaddrs(
12416
12491
  options2.fallbackHosts === null ? void 0 : options2.fallbackHosts ?? [DEFAULT_TINYCLOUD_FALLBACK_HOST]
12417
12492
  ),
12418
- fetch: options2.fetch,
12493
+ fetch: fetchFn,
12419
12494
  verifyRecords: options2.verifyRecords
12420
12495
  });
12421
12496
  return {
@@ -12423,6 +12498,181 @@ async function resolveTinyCloudHosts(subject, options2 = {}) {
12423
12498
  location
12424
12499
  };
12425
12500
  }
12501
+ function memoizeFetch(fetchFn) {
12502
+ const cache2 = /* @__PURE__ */ new Map();
12503
+ return (async (input, init) => {
12504
+ const key = String(input);
12505
+ let cached = cache2.get(key);
12506
+ if (!cached) {
12507
+ cached = fetchFn(input, init);
12508
+ cache2.set(key, cached);
12509
+ }
12510
+ const response = await cached;
12511
+ return response.clone();
12512
+ });
12513
+ }
12514
+ async function discoverLocalTinyCloudNode(options2 = {}) {
12515
+ if ((options2.autoDiscoverLocalNode ?? true) === false) {
12516
+ return null;
12517
+ }
12518
+ const fetchFn = options2.fetch ?? globalThis.fetch;
12519
+ const identityStore = options2.identityStore ?? defaultLocalNodeIdentityStore;
12520
+ const staticCandidates = [
12521
+ {
12522
+ source: "local-loopback",
12523
+ url: options2.localNodeUrl ?? DEFAULT_LOCAL_NODE_URL,
12524
+ timeoutMs: LOCAL_LOOPBACK_PROBE_TIMEOUT_MS
12525
+ }
12526
+ ];
12527
+ if (options2.localLinkName) {
12528
+ if (isValidDnsLabel(options2.localLinkName)) {
12529
+ staticCandidates.push({
12530
+ source: "local-link",
12531
+ url: `https://${options2.localLinkName}${LOCAL_LINK_HOST_SUFFIX}`,
12532
+ timeoutMs: LOCAL_LINK_PROBE_TIMEOUT_MS
12533
+ });
12534
+ } else {
12535
+ debugLog(
12536
+ `localLinkName "${options2.localLinkName}" is not a valid DNS label; skipping`
12537
+ );
12538
+ }
12539
+ }
12540
+ for (const candidate of staticCandidates) {
12541
+ const adopted = await probeAndVerifyLocalCandidate(
12542
+ candidate,
12543
+ fetchFn,
12544
+ identityStore,
12545
+ options2.expectedNodeDid
12546
+ );
12547
+ if (adopted) {
12548
+ return adopted;
12549
+ }
12550
+ }
12551
+ const registryCandidates = await fetchRegistryLocalLinkCandidates(
12552
+ options2,
12553
+ fetchFn,
12554
+ staticCandidates
12555
+ );
12556
+ for (const candidate of registryCandidates) {
12557
+ const adopted = await probeAndVerifyLocalCandidate(
12558
+ candidate,
12559
+ fetchFn,
12560
+ identityStore,
12561
+ options2.expectedNodeDid
12562
+ );
12563
+ if (adopted) {
12564
+ return adopted;
12565
+ }
12566
+ }
12567
+ return null;
12568
+ }
12569
+ var defaultLocalNodeIdentityStore = createInMemoryLocalNodeIdentityStore();
12570
+ function debugLog(message) {
12571
+ console.debug(`[tinycloud:location] ${message}`);
12572
+ }
12573
+ async function probeAndVerifyLocalCandidate(candidate, fetchFn, identityStore, expectedNodeDid) {
12574
+ const healthy = await probeHealthz(candidate.url, candidate.timeoutMs, fetchFn);
12575
+ if (!healthy) {
12576
+ return null;
12577
+ }
12578
+ const info = await fetchLocalNodeInfo(candidate.url, candidate.timeoutMs, fetchFn);
12579
+ if (!info?.nodeDid) {
12580
+ debugLog(`local node at ${candidate.url} did not report a nodeId DID; skipping`);
12581
+ return null;
12582
+ }
12583
+ const pinnedNodeDid = await identityStore.get(candidate.url);
12584
+ const expected = expectedNodeDid ?? pinnedNodeDid;
12585
+ if (expected !== void 0 && expected !== info.nodeDid) {
12586
+ debugLog(
12587
+ `local node DID mismatch at ${candidate.url}: expected ${expected}, got ${info.nodeDid}`
12588
+ );
12589
+ return null;
12590
+ }
12591
+ if (pinnedNodeDid === void 0 || expectedNodeDid !== void 0) {
12592
+ await identityStore.set(candidate.url, info.nodeDid);
12593
+ }
12594
+ return { source: candidate.source, url: candidate.url, nodeDid: info.nodeDid };
12595
+ }
12596
+ async function fetchRegistryLocalLinkCandidates(options2, fetchFn, existingCandidates) {
12597
+ if (!options2.subject || options2.registryUrl === null) {
12598
+ return [];
12599
+ }
12600
+ const registryUrl = options2.registryUrl ?? DEFAULT_TINYCLOUD_LOCATION_REGISTRY_URL;
12601
+ try {
12602
+ const record = await fetchLocationRecord(registryUrl, options2.subject, fetchFn);
12603
+ if (!record) {
12604
+ return [];
12605
+ }
12606
+ const shouldVerify = options2.verifyRecords ?? true;
12607
+ if (shouldVerify && !await verifyLocationRecord(record)) {
12608
+ debugLog(
12609
+ `registry record signature invalid for ${options2.subject}; skipping local-link lookup`
12610
+ );
12611
+ return [];
12612
+ }
12613
+ return extractLocalLinkUrls(record).filter((url) => !existingCandidates.some((c) => c.url === url)).map((url) => ({
12614
+ source: "local-link",
12615
+ url,
12616
+ timeoutMs: LOCAL_LINK_PROBE_TIMEOUT_MS
12617
+ }));
12618
+ } catch {
12619
+ return [];
12620
+ }
12621
+ }
12622
+ function extractLocalLinkUrls(record) {
12623
+ if (!record) {
12624
+ return [];
12625
+ }
12626
+ const urls = [];
12627
+ for (const addr of record.multiaddrs) {
12628
+ let url;
12629
+ try {
12630
+ url = multiaddrToHttpUrl(addr);
12631
+ } catch {
12632
+ continue;
12633
+ }
12634
+ if (isLocalLinkUrl(url)) {
12635
+ urls.push(url);
12636
+ }
12637
+ }
12638
+ return urls;
12639
+ }
12640
+ var DNS_LABEL_REGEX = /^[a-z0-9]([a-z0-9-]{1,30}[a-z0-9])?$/;
12641
+ function isValidDnsLabel(value) {
12642
+ return DNS_LABEL_REGEX.test(value);
12643
+ }
12644
+ function isLocalLinkUrl(url) {
12645
+ try {
12646
+ const parsed = new URL(url);
12647
+ return parsed.protocol === "https:" && parsed.hostname.endsWith(LOCAL_LINK_HOST_SUFFIX);
12648
+ } catch {
12649
+ return false;
12650
+ }
12651
+ }
12652
+ async function probeHealthz(url, timeoutMs, fetchFn) {
12653
+ try {
12654
+ const response = await fetchFn(`${url.replace(/\/$/, "")}/healthz`, {
12655
+ signal: AbortSignal.timeout(timeoutMs)
12656
+ });
12657
+ return response.ok;
12658
+ } catch {
12659
+ return false;
12660
+ }
12661
+ }
12662
+ async function fetchLocalNodeInfo(url, timeoutMs, fetchFn) {
12663
+ try {
12664
+ const response = await fetchFn(`${url.replace(/\/$/, "")}/info`, {
12665
+ signal: AbortSignal.timeout(timeoutMs)
12666
+ });
12667
+ if (!response.ok) {
12668
+ return null;
12669
+ }
12670
+ const body = await response.json();
12671
+ return { nodeDid: body.nodeId };
12672
+ } catch {
12673
+ return null;
12674
+ }
12675
+ }
12426
12676
  function multiaddrToHttpUrl(input) {
12427
12677
  const uri = multiaddrToUri(multiaddr(input));
12428
12678
  if (!uri.startsWith("http://") && !uri.startsWith("https://")) {
@@ -12698,6 +12948,8 @@ export {
12698
12948
  DEFAULT_EXPIRY,
12699
12949
  DEFAULT_KEY_VERSION,
12700
12950
  DEFAULT_KNOWLEDGE_ROOT,
12951
+ DEFAULT_LOCAL_NODE_PIN_STORAGE_KEY,
12952
+ DEFAULT_LOCAL_NODE_URL,
12701
12953
  DEFAULT_MANIFEST_SPACE,
12702
12954
  DEFAULT_MANIFEST_VERSION,
12703
12955
  DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
@@ -12732,6 +12984,9 @@ export {
12732
12984
  KV,
12733
12985
  KVService2 as KVService,
12734
12986
  LISTEN_SQL_STATEMENT_CATALOG,
12987
+ LOCAL_LINK_HOST_SUFFIX,
12988
+ LOCAL_LINK_PROBE_TIMEOUT_MS,
12989
+ LOCAL_LOOPBACK_PROBE_TIMEOUT_MS,
12735
12990
  LocationRecordValidationError,
12736
12991
  ManifestValidationError,
12737
12992
  NETWORK_NAME_PATTERN,
@@ -12827,6 +13082,8 @@ export {
12827
13082
  createAndSignPolicyStatus,
12828
13083
  createAndSignSignedObject,
12829
13084
  createCapabilityKeyRegistry,
13085
+ createInMemoryLocalNodeIdentityStore,
13086
+ createLocalStorageLocalNodeIdentityStore,
12830
13087
  createOpenKeyCallbackSigningStrategy,
12831
13088
  createSharingService,
12832
13089
  createSpaceService,
@@ -12842,6 +13099,7 @@ export {
12842
13099
  didCacheKey,
12843
13100
  didEquals,
12844
13101
  disableTinyCloudDebug,
13102
+ discoverLocalTinyCloudNode,
12845
13103
  discoverNetwork,
12846
13104
  enableTinyCloudDebug,
12847
13105
  encryptToNetwork,