@drakkar.software/octospaces-sdk 0.1.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { WrappedKeyEntry } from '@drakkar.software/starfish-keyring';
2
+ import * as _drakkar_software_starfish_client from '@drakkar.software/starfish-client';
2
3
  import { StarfishClient, Encryptor, StarfishCapProvider, PullCache } from '@drakkar.software/starfish-client';
3
4
  import { CapCert, Base64Provider } from '@drakkar.software/starfish-protocol';
4
5
  import { BootstrapOrigin, ScopePreset } from '@drakkar.software/starfish-identities';
@@ -89,19 +90,23 @@ declare function capProviderFor(cap: unknown, devEdPrivHex: string): StarfishCap
89
90
  */
90
91
  declare function makeClient(cap: unknown, devEdPrivHex: string, namespaceOverride?: string): StarfishClient;
91
92
  /**
92
- * Open a SPACE's decryptor, throwing a descriptive error per failure mode
93
+ * Open a node's decryptor, throwing a descriptive error per failure mode
93
94
  * (unreachable server / no keyring yet / not a recipient).
94
95
  *
95
- * A `SpaceAccessError` is a hard access denial; any other thrown error is a
96
- * transient offline state.
96
+ * `keyringPullPath` is the full `/pull/.../_keyring` path (e.g. from
97
+ * `keyringPull(spaceId)`). A `SpaceAccessError` is a hard access
98
+ * denial; any other thrown error is a transient offline state.
97
99
  */
98
- declare function openEncryptor(client: StarfishClient, keys: DeviceKeys, spaceId: string, trustedAdders: string[]): Promise<Encryptor>;
100
+ declare function openEncryptor(client: StarfishClient, keys: DeviceKeys, keyringPullPath: string, trustedAdders: string[]): Promise<Encryptor>;
99
101
  /** Soft variant of {@link openEncryptor}: returns null instead of throwing. */
100
- declare function buildEncryptor(client: StarfishClient, keys: DeviceKeys, spaceId: string, trustedAdders: string[]): Promise<Encryptor | null>;
102
+ declare function buildEncryptor(client: StarfishClient, keys: DeviceKeys, keyringPullPath: string, trustedAdders: string[]): Promise<Encryptor | null>;
101
103
  /**
102
- * Owner-side: create the SPACE keyring if missing, return an encryptor.
104
+ * Owner-side: create a per-node keyring if missing, return an encryptor.
105
+ *
106
+ * `keyringPullPath` / `keyringPushPath` are the full `/pull|push/.../_keyring`
107
+ * paths (e.g. from `keyringPull`/`keyringPush`).
103
108
  */
104
- declare function ownerEnsureKeyring(client: StarfishClient, keys: DeviceKeys, spaceId: string, trustedAdders?: string[]): Promise<Encryptor>;
109
+ declare function ownerEnsureKeyring(client: StarfishClient, keys: DeviceKeys, keyringPullPath: string, keyringPushPath: string, trustedAdders?: string[]): Promise<Encryptor>;
105
110
  /** A user's public profile: display pseudo + optional inline avatar + public identity keys. */
106
111
  interface PublicProfile {
107
112
  pseudo: string | null;
@@ -297,16 +302,14 @@ declare function sealToRecipient(session: Session, recipientKemPub: string, plai
297
302
  declare function unsealFromRecipient(session: Session, blob: SealedBlob): Promise<string>;
298
303
 
299
304
  type ID = string;
300
- /** Maps a joined private space's id → its owner-issued member cap-cert (serialized
301
- * JSON). Persisted both in device-local kv (`member-caps.ts`) and, for durability,
302
- * in the user's own synced `_spaces` doc so a fresh device re-hydrates it. */
305
+ /** Maps a joined space's id → its owner-issued member cap-cert (serialized JSON).
306
+ * Persisted both in device-local kv and, for durability, in the user's own synced
307
+ * `_spaces` doc so a fresh device re-hydrates it. */
303
308
  type CapMap = Record<string, string>;
304
- /** Maps a joined PUBLIC space's id → its invitation credential (the owner-signed cap
305
- * plus the link's ephemeral private key) SEALED to the account's own key. Unlike a
306
- * member cap (safe in the clear see {@link CapMap}), a public-join credential
307
- * embeds a bearer secret, so it is sealed before riding in the plaintext `_spaces`
308
- * doc. Recovered on any device with the same seed. See `account-seal.ts` and
309
- * `space-access-store.ts`. */
309
+ /** Maps a joined link-access key → its sealed invitation credential (cap + ephemeral
310
+ * private key) SEALED to the account's own key. Keys are either `spaceId` (space-level
311
+ * link) or `${spaceId}:${nodeId}` (per-node invite link). Sealed because it embeds a
312
+ * bearer secret; recovered on any device with the same seed. */
310
313
  type PubAccessMap = Record<string, SealedBlob>;
311
314
  /** Maps a DM peer's userId → the private DM-space id shared with them. */
312
315
  type DmMap = Record<string, string>;
@@ -325,8 +328,8 @@ type ReadValue = number;
325
328
  interface ReadPrefs {
326
329
  rooms: Record<string, ReadValue>;
327
330
  }
328
- /** Whether a space encrypts its content client-side. */
329
- type SpaceVisibility = 'private' | 'public';
331
+ /** A joined or listed space. Visibility and encryption are per-node (see ObjectNode),
332
+ * not per-space a space is a neutral container. */
330
333
  interface Space {
331
334
  id: ID;
332
335
  name: string;
@@ -336,93 +339,51 @@ interface Space {
336
339
  image?: string;
337
340
  members: number;
338
341
  unread?: number;
339
- /** 'private' (E2EE keyring, the default) or 'public' (plaintext, joined via a
340
- * space-wide invitation link). Absent ⇒ treat as 'private' (back-compat). */
341
- visibility?: SpaceVisibility;
342
- /** Public spaces only: the owner's userId (derived from the cap issuer). */
343
- ownerId?: string;
344
- /** Public spaces only (joiner side): whether this identity's invite link grants write. */
345
- write?: boolean;
346
- }
347
- /** Legacy room kind — used while apps migrate their content onto objects.
348
- * New code should use {@link RoomSubtype} directly. */
349
- type RoomKind = 'channel' | 'dm' | 'automated';
350
- /** Scheduled-fetch cadence for automated rooms. */
351
- type AutomationSchedule = {
352
- kind: 'interval';
353
- everyMin: number;
354
- } | {
355
- kind: 'daily';
356
- hour: number;
357
- minute: number;
358
- } | {
359
- kind: 'weekly';
360
- weekday: number;
361
- hour: number;
362
- minute: number;
363
- } | {
364
- kind: 'cron';
365
- expression: string;
366
- };
367
- /** Stored, synced configuration of an `automated` room. */
368
- interface AutomationMeta {
369
- providerId: string;
370
- params: Record<string, unknown>;
371
- intervalMin: number;
372
- schedule?: AutomationSchedule;
373
- onOpen?: boolean;
374
- enabled: boolean;
375
- credential: SealedBlob;
376
- botUserId?: string;
377
- runOnDeviceId: string | null;
378
- lastRunAt: number | null;
379
- lastFetchHash?: string | null;
380
- lastError: string | null;
381
342
  }
382
- /** A transitional Room shape (used while apps migrate onto the object model). */
383
- interface Room {
384
- id: ID;
385
- spaceId: ID;
386
- category: string;
387
- name: string;
388
- kind: RoomKind;
389
- topic?: string;
390
- unread?: number;
391
- mention?: boolean;
392
- avatar?: string;
393
- automation?: AutomationMeta;
394
- }
395
- /** The builtin object types. Custom types ride the same `string` field. */
396
- type BuiltinObjectType = 'room' | 'category' | 'automation' | 'doc' | 'project' | 'task';
397
- type ObjectType = BuiltinObjectType | (string & {});
398
- /** Runtime set of builtin type strings for renderer branching. */
399
- declare const BUILTIN_OBJECT_TYPES: readonly BuiltinObjectType[];
400
- /** How an object's content syncs. Builtins infer this; custom types set it explicitly. */
343
+ /** Any string an app assigns as an object's type. No builtins are defined here —
344
+ * each app declares its own type strings in its local SDK. */
345
+ type ObjectType = string;
346
+ /** How an object's content syncs. Apps may set this per-type explicitly. */
401
347
  type ObjectContentKind = 'merge' | 'append' | 'none';
402
- /** When `type === 'room'`, the room flavour. */
403
- type RoomSubtype = 'channel' | 'dm' | 'automation';
348
+ /** Who may read a node's content (independent from whether it is E2EE):
349
+ * - `'public'` — world-readable; anonymous users may access the content. The node
350
+ * is listed in the global object directory (`_index/objects/{shard}`).
351
+ * - `'space'` — any member of the parent space. The default for new nodes.
352
+ * - `'invite'` — only members explicitly invited to this node (via its own per-node
353
+ * keyring for E2EE nodes, or via a per-node cap for plaintext nodes).
354
+ * Non-invited space members see a placeholder row (no title/emoji). */
355
+ type NodeAccess = 'public' | 'space' | 'invite';
404
356
  /**
405
357
  * One entry in a space's object index (`spaces/{spaceId}/objects/_index`).
406
358
  * Identity + tree position + light metadata ONLY — heavy content (messages, doc
407
- * blocks, project event log) lives in a per-object content doc keyed by `id`.
359
+ * blocks, event logs) lives in per-object content docs keyed by `id`.
360
+ *
361
+ * The index doc is always **plaintext** (member-gated). For `invite` nodes the
362
+ * `title` and `emoji` fields are stored empty in the index — only invited members
363
+ * can recover the real title from the node's content doc or encrypted keyring entry.
408
364
  */
409
365
  interface ObjectNode {
410
366
  id: ID;
411
367
  type: ObjectType;
412
- subtype?: RoomSubtype;
413
368
  parentId: ID | null;
414
369
  order: number;
415
370
  title: string;
416
371
  emoji?: string;
417
372
  updatedAt: number;
418
373
  archived?: boolean;
419
- automation?: AutomationMeta;
420
374
  contentKind?: ObjectContentKind;
375
+ /** Who may access this node's content. Absent ⇒ `'space'`. */
376
+ access?: NodeAccess;
377
+ /** True ⇒ this node's content is E2EE under the SPACE-WIDE keyring at
378
+ * `spaces/{spaceId}/_keyring`. All `enc` nodes in a space share one CEK.
379
+ * The combination `public + enc` is invalid. */
380
+ enc?: boolean;
381
+ /** App-specific fields. Apps store type-specific metadata here. */
421
382
  meta?: Record<string, unknown>;
422
383
  }
423
- /** The object-index doc: the union-merged list of every object in a space. */
384
+ /** The object-index doc stored at `spaces/{spaceId}/objects/_index`. */
424
385
  interface ObjectsIndex {
425
- v: 1;
386
+ v: 1 | 2;
426
387
  objects: ObjectNode[];
427
388
  updatedAt: number;
428
389
  }
@@ -443,7 +404,7 @@ declare function randomId(): string;
443
404
  declare function roomSlug(name: string): string;
444
405
 
445
406
  /**
446
- * Collection path + cap-scope helpers (merged from OctoChat + OctoVault).
407
+ * Collection path + cap-scope helpers for OctoSpaces.
447
408
  *
448
409
  * Paths are signed relative to SYNC_BASE; the server mounts the sync router at
449
410
  * root, so they start with /pull or /push. Everything for a space is nested under
@@ -452,11 +413,17 @@ declare function roomSlug(name: string): string;
452
413
  * covers a whole space.
453
414
  *
454
415
  * **Generic object collections** — scopes use the `obj*` collection names (the
455
- * domain-neutral storage layer both apps migrate onto). App-specific collection
456
- * names like `'chat'` are left for the consumer's own `paths.ts` extension until
457
- * that app finishes migrating.
416
+ * domain-neutral storage layer). The access record lives at
417
+ * `spaces/{spaceId}/_access` (collection `spaceregistry`); the space-wide keyring at
418
+ * `spaces/{spaceId}/_keyring` (collection `spacekeyring`). ONE keyring per space
419
+ * encrypts ALL the space's `enc` nodes.
420
+ *
421
+ * Note: `objinv` (invite-plaintext content) is intentionally EXCLUDED from
422
+ * OBJECT_COLLECTIONS / spaceMemberScope — only a per-node cap can reach it.
458
423
  */
459
424
 
425
+ /** Base name used as the `collectionName` arg to `addCollectionRecipient`.
426
+ * Appending `/_keyring` gives the full storage path. */
460
427
  declare const keyringName: (spaceId: string) => string;
461
428
  declare const keyringPull: (spaceId: string) => string;
462
429
  declare const keyringPush: (spaceId: string) => string;
@@ -466,8 +433,8 @@ declare const profilePull: (userId: string) => string;
466
433
  declare const profilePush: (userId: string) => string;
467
434
  declare const spacesPull: (userId: string) => string;
468
435
  declare const spacesPush: (userId: string) => string;
469
- declare const roomsRegistryPull: (spaceId: string) => string;
470
- declare const roomsRegistryPush: (spaceId: string) => string;
436
+ declare const spaceAccessPull: (spaceId: string) => string;
437
+ declare const spaceAccessPush: (spaceId: string) => string;
471
438
  declare const objIndexPull: (spaceId: string) => string;
472
439
  declare const objIndexPush: (spaceId: string) => string;
473
440
  declare const objLogPull: (spaceId: string, objectId: string) => string;
@@ -476,18 +443,32 @@ declare const objDocPull: (spaceId: string, objectId: string) => string;
476
443
  declare const objDocPush: (spaceId: string, objectId: string) => string;
477
444
  declare const objectBlobPull: (spaceId: string, blobId: string) => string;
478
445
  declare const objectBlobPush: (spaceId: string, blobId: string) => string;
446
+ declare const objPubName: (spaceId: string, nodeId: string) => string;
447
+ declare const objPubPull: (spaceId: string, nodeId: string) => string;
448
+ declare const objPubPush: (spaceId: string, nodeId: string) => string;
449
+ declare const objInvName: (spaceId: string, nodeId: string) => string;
450
+ declare const objInvPull: (spaceId: string, nodeId: string) => string;
451
+ declare const objInvPush: (spaceId: string, nodeId: string) => string;
479
452
  declare const typesIndexPull: (spaceId: string) => string;
480
453
  declare const typesIndexPush: (spaceId: string) => string;
481
- declare const spaceIndexPull: (shard: "public") => string;
454
+ declare const objectDirName: (shard?: string) => string;
455
+ declare const objectDirPull: (shard?: string) => string;
482
456
  declare const OBJECT_COLLECTIONS: string[];
483
457
  /** Full owner/device access to every space the identity owns. */
484
458
  declare function ownerScope(): ScopePreset;
485
459
  /**
486
- * Member access to one SPACE — its keyring + every channel's messages and
487
- * attachments + the room registry, all under `spaces/{spaceId}/**`. One cap
488
- * covers current AND future channels.
460
+ * Member access to one SPACE — the space keyring, every node's content docs and
461
+ * attachments, all under `spaces/{spaceId}/**`. Does NOT cover `objinv` (invite-
462
+ * plaintext content) — use `nodeMemberScope` for that. One cap covers current AND
463
+ * future nodes.
489
464
  */
490
465
  declare function spaceMemberScope(spaceId: string, canWrite: boolean): ScopePreset;
466
+ /**
467
+ * Narrow per-node cap for `invite+plaintext` nodes. Covers ONLY the node's `objinv`
468
+ * content path — the space keyring is space-wide and is covered by the broader space
469
+ * member scope. Use `spaceMemberScope` when the bearer also needs to decrypt enc content.
470
+ */
471
+ declare function nodeMemberScope(spaceId: string, nodeId: string, canWrite: boolean): ScopePreset;
491
472
  /**
492
473
  * Personal cap: profile + space registry + device directory + all spaces.
493
474
  * Note: app-specific collections like `'dminbox'` (chat) are NOT included here —
@@ -516,46 +497,62 @@ declare class SpaceAccessError extends Error {
516
497
  }
517
498
 
518
499
  /**
519
- * Space access resolver — returns the right (client, encryptor) pair for any
520
- * space regardless of whether it is private (E2EE) or public (plaintext).
500
+ * Space and node access resolver.
501
+ *
502
+ * Encryption is per-node (each node has an `enc` flag) but keyed under ONE
503
+ * space-wide keyring at `spaces/{spaceId}/_keyring`. All `enc` nodes in a space
504
+ * share the same CEK; `access` gates *fetching*, the keyring gates *decryption*.
521
505
  *
522
- * Replaces `space-encryptor.ts`. The key invariant: public spaces have
523
- * `encryptor: null`; private spaces always have a live `Encryptor`.
506
+ * Two entry points:
507
+ * - `getSpaceClient` — returns the right StarfishClient for member-gated
508
+ * space docs (index, _access). No encryptor.
509
+ * - `getNodeAccess` — resolves the (client, encryptor) for a specific node's
510
+ * CONTENT. Encryptor is null for plaintext nodes; for enc nodes the encryptor
511
+ * opens the SPACE keyring (not a per-node keyring).
524
512
  *
525
- * Resolution order (same semantics as the old `getSpaceEncryptor`):
526
- * 1. Link entry in the access store → sign requests as the ephemeral identity;
527
- * no keyring, encryptor null.
528
- * 2. Member entry → open the keyring as a recipient with the stored cap.
529
- * 3. No entry + visibility === 'public' (from `reg`) → owner mode, no keyring.
530
- * 4. No entry, private either owner (open/mint keyring) or SpaceAccessError
531
- * if the space's roster shows we're a member but we're not holding a cap yet.
513
+ * Resolution order for `getNodeAccess`:
514
+ * 1. Per-node link entry → sign as ephemeral identity; encryptor from space keyring.
515
+ * 2. Per-node member entry → open space keyring as recipient.
516
+ * 3. Space-level link entry → same client; open space keyring if enc.
517
+ * 4. Space-level member entry open space keyring if enc.
518
+ * 5. No entry, owner mint space keyring if enc; plain client otherwise.
519
+ * 6. No entry, non-owner → SpaceAccessError if enc; plain client otherwise.
532
520
  */
533
521
 
534
- interface SpaceAccessHandle {
522
+ interface NodeAccessHandle {
535
523
  encryptor: Encryptor | null;
536
524
  client: StarfishClient;
537
- /** True when opened as the space OWNER (so the caller must seed the room doc). */
525
+ /** True when opened as the space OWNER (may seed / mint the space keyring). */
538
526
  isOwnerOpen: boolean;
539
527
  }
540
528
  /** Drop every cached handle (on account switch — keys are per-identity). */
541
- declare function clearSpaceAccessCache(): void;
529
+ declare function clearNodeAccessCache(): void;
542
530
  /**
543
- * Resolve the right (client, encryptor) for a space, opening and caching on first use.
531
+ * Return the right StarfishClient for reading/writing member-gated space docs
532
+ * (e.g. the `_index`, `_access`). Spaces are always plaintext — no encryptor.
533
+ */
534
+ declare function getSpaceClient(spaceId: string, session: Session): StarfishClient;
535
+ /**
536
+ * Resolve the right (client, encryptor) for a node's CONTENT, opening and
537
+ * caching on first use.
544
538
  *
545
- * `reg` is the space's `_rooms` access record if already known. Pass null when the
546
- * caller has not yet read the registry; the resolver will probe if needed.
539
+ * `node` carries `{ access?, enc? }` the plaintext flags from the index.
540
+ * `reg` is the space's access record if already known; used to determine
541
+ * ownership. Pass null if unknown.
547
542
  */
548
- declare function getSpaceAccess(spaceId: string, session: Session, reg: {
543
+ declare function getNodeAccess(spaceId: string, nodeId: string, node: {
544
+ access?: NodeAccess;
545
+ enc?: boolean;
546
+ }, session: Session, reg?: {
549
547
  owner: string | null;
550
548
  members: string[];
551
- visibility?: SpaceVisibility;
552
- } | null): Promise<SpaceAccessHandle>;
549
+ } | null): Promise<NodeAccessHandle>;
553
550
  /**
554
551
  * SOFT resolve — never mints a keyring, never throws on missing access.
555
- * Returns null when the identity has no usable access for the space yet.
552
+ * Returns null when the identity has no usable access for the node yet.
556
553
  */
557
- declare function buildSpaceAccess(session: Session, spaceId: string, hint?: {
558
- visibility?: SpaceVisibility;
554
+ declare function buildNodeAccess(session: Session, spaceId: string, nodeId: string, node: {
555
+ enc?: boolean;
559
556
  }): Promise<{
560
557
  client: StarfishClient;
561
558
  encryptor: Encryptor | null;
@@ -604,6 +601,12 @@ declare function getSpaceAccessEntry(spaceId: string): SpaceAccessEntry | null;
604
601
  declare function saveSpaceAccessEntry(spaceId: string, entry: SpaceAccessEntry): void;
605
602
  /** Forget one space's access (on leaving that space). */
606
603
  declare function removeSpaceAccessEntry(spaceId: string): void;
604
+ /** Look up a per-node invite access entry. Returns null if not invited or unknown. */
605
+ declare function getNodeAccessEntry(spaceId: string, nodeId: string): SpaceAccessEntry | null;
606
+ /** Persist an invite access entry for one node. */
607
+ declare function saveNodeAccessEntry(spaceId: string, nodeId: string, entry: SpaceAccessEntry): void;
608
+ /** Forget a node's invite access entry (e.g. on leaving the node). */
609
+ declare function removeNodeAccessEntry(spaceId: string, nodeId: string): void;
607
610
  /** A snapshot of the in-memory cache — used by `recoverSpaceAccess` to find entries
608
611
  * not yet on the server. */
609
612
  declare function localSpaceAccessEntries(): SpaceAccessMap;
@@ -618,102 +621,11 @@ declare function linkAccessFromStore(): Record<string, {
618
621
  /** Drop the in-memory cache (on account switch / sign-out). */
619
622
  declare function clearSpaceAccessStore(): void;
620
623
 
621
- /** The bucket new/unfiled objects land in, and the fallback a deleted category's
622
- * objects are reassigned to. */
623
- declare const DEFAULT_CATEGORY = "CHANNELS";
624
- /** Deterministic category-node id from its name, so two devices that concurrently
625
- * create the SAME category mint the SAME id → the union-merge dedupes them. */
626
- declare const categoryId: (name: string) => ID;
627
- /** A node plus its resolved children — the shape a tree view renders. */
628
- interface ObjectTreeNode extends ObjectNode {
629
- depth: number;
630
- children: ObjectTreeNode[];
631
- }
632
- /** Map a legacy {@link Room} `kind` to the unified room {@link RoomSubtype}. */
633
- declare function roomKindToSubtype(kind: Room['kind']): RoomSubtype;
634
- /** Inverse of {@link roomKindToSubtype}. A legacy persisted `'stream'` subtype hits
635
- * the `default` and reads back as a plain `'channel'` (normalization). */
636
- declare function subtypeToRoomKind(subtype: RoomSubtype | undefined): Room['kind'];
637
- /** The order value for a new node appended after `siblings`. */
638
- declare function nextOrder(siblings: ObjectNode[]): number;
639
- /**
640
- * Build the render tree from a flat node list, repairing merge artifacts:
641
- * - **archived** nodes (and their subtrees) are dropped.
642
- * - **orphans** — a `parentId` that is missing or archived — reparent to root.
643
- * - **cycles** — a node reachable from itself via `parentId` — reparent to root.
644
- * - **siblings** sort by {@link compareSiblings} for cross-device determinism.
645
- */
646
- declare function buildTree(nodes: ObjectNode[], includeArchived?: boolean): ObjectTreeNode[];
647
- /** The root→node trail (inclusive) for breadcrumbs. Returns `[]` if unknown. */
648
- declare function breadcrumbs(nodes: ObjectNode[], id: ID): ObjectNode[];
649
- /** The root→parent trail (EXCLUSIVE of the node itself). */
650
- declare function ancestors(nodes: ObjectNode[], id: ID): ObjectNode[];
651
- /** The ids of a node and its whole subtree (for cascade-archive). */
652
- declare function subtreeIds(nodes: ObjectNode[], rootId: ID): Set<ID>;
653
- interface NewObjectInput {
654
- type: ObjectType;
655
- subtype?: RoomSubtype;
656
- parentId?: ID | null;
657
- title: string;
658
- emoji?: string;
659
- automation?: AutomationMeta;
660
- /** Provide to reuse an id (e.g. a room id derived elsewhere); else minted. */
661
- id?: ID;
662
- }
663
- /** Append a new node under `parentId` at the end of its sibling order. */
664
- declare function addObject(nodes: ObjectNode[], input: NewObjectInput, now: number): {
665
- nodes: ObjectNode[];
666
- node: ObjectNode;
667
- };
668
- /** Patch a node's mutable metadata (title/emoji/automation), bumping `updatedAt`. */
669
- declare function patchObject(nodes: ObjectNode[], id: ID, patch: Partial<Pick<ObjectNode, 'title' | 'emoji' | 'automation'>>, now: number): ObjectNode[];
670
- /** Reparent a node (move in the tree). Rejects making a node its own descendant. */
671
- declare function reparentObject(nodes: ObjectNode[], id: ID, parentId: ID | null, now: number): ObjectNode[];
672
- /** Set explicit sibling order (drag-reorder). */
673
- declare function reorderObjects(nodes: ObjectNode[], orderById: Record<ID, number>, now: number): ObjectNode[];
674
- /** Cascade-archive a node and its whole subtree (soft delete). */
675
- declare function archiveObject(nodes: ObjectNode[], id: ID, now: number): ObjectNode[];
676
- /** The category→rooms grouping the legacy UI consumes. */
677
- interface AdaptedCategory {
678
- name: string;
679
- rooms: Room[];
680
- }
681
- /**
682
- * Project the room/category nodes of an index into the legacy `{ name, rooms }[]`
683
- * shape that app UIs still consume. Category nodes become buckets; room nodes become
684
- * {@link Room}s grouped under their parent category (or `fallbackCategory` at root).
685
- * Returns null when the index holds no room/category nodes yet.
686
- *
687
- * @deprecated Use the object tree directly once apps complete their migration.
688
- */
689
- declare function objectsToRoomCategories(nodes: ObjectNode[], spaceId: string, fallbackCategory: string): AdaptedCategory[] | null;
690
- /**
691
- * Drop `kind: 'automated'` rooms from a category list (they belong to an Agents
692
- * view, not the main room list). A category that held only agents is removed too.
693
- *
694
- * @deprecated Use the object tree directly once apps complete their migration.
695
- */
696
- declare function excludeAutomatedRooms(categories: AdaptedCategory[]): AdaptedCategory[];
697
- /** A minimal object descriptor the {@link seedIndexNodes} builder turns into nodes. */
698
- interface SeedRoom {
699
- id: ID;
700
- name: string;
701
- kind: Room['kind'];
702
- category: string;
703
- }
704
- /**
705
- * Build the initial `ObjectNode[]` for a brand-new space's index: a `category` node
706
- * per distinct category and a `room` node per seed object parented under it. Pure +
707
- * deterministic (category ids via {@link categoryId}).
708
- */
709
- declare function seedIndexNodes(rooms: SeedRoom[], now: number): ObjectNode[];
710
-
711
- /** Owner-set, SHARED space identity, persisted in the `_rooms` registry doc
712
- * (plaintext — NOT E2EE). `image` is a data URI. Both optional for back-compat. */
624
+ /** Owner-set, SHARED space identity, persisted in the `_access` registry doc
625
+ * (plaintext NOT E2EE). `image` is a data URI. All fields optional for back-compat. */
713
626
  interface SpaceMeta {
714
627
  name?: string | null;
715
628
  image?: string | null;
716
- visibility?: SpaceVisibility;
717
629
  }
718
630
  /** A resolved name/image update fanned out so the SpacesProvider adopts a
719
631
  * freshly-reconciled value without waiting for its next navigation refresh. */
@@ -753,16 +665,14 @@ declare function updateArchivedDmsDoc(client: StarfishClient, userId: string, mu
753
665
  declare function setDmMapping(client: StarfishClient, userId: string, peerUserId: string, spaceId: string): Promise<void>;
754
666
  declare function writeSpaces(client: StarfishClient, userId: string, spaces: Space[], _hash: string | null): Promise<void>;
755
667
  declare function reorderSpaces(client: StarfishClient, userId: string, order: string[]): Promise<void>;
756
- declare function normalizeCategories(rooms: Room[], stored: unknown): string[];
757
- declare function readRooms(client: StarfishClient, spaceId: string): Promise<{
668
+ declare function readSpaceAccess(client: StarfishClient, spaceId: string): Promise<{
758
669
  owner: string | null;
759
670
  members: string[];
760
- visibility: SpaceVisibility | null;
761
671
  name: string | null;
762
672
  image: string | null;
763
673
  hash: string | null;
764
674
  }>;
765
- declare function writeRooms(client: StarfishClient, spaceId: string, owner: string, members: string[], hash: string | null, meta?: SpaceMeta): Promise<void>;
675
+ declare function writeSpaceAccess(client: StarfishClient, spaceId: string, owner: string, members: string[], hash: string | null, meta?: SpaceMeta): Promise<void>;
766
676
  declare function addSpaceMember(client: StarfishClient, spaceId: string, ownerUserId: string, memberUserId: string): Promise<void>;
767
677
  /** Remove a member from the space roster (used for link revocation). */
768
678
  declare function removeSpaceMember(client: StarfishClient, spaceId: string, memberUserId: string): Promise<void>;
@@ -770,16 +680,10 @@ declare function addJoinedSpace(client: StarfishClient, userId: string, space: S
770
680
  declare function addJoinedSpaceWithCap(client: StarfishClient, userId: string, space: Space, capJson: string): Promise<void>;
771
681
  declare function addJoinedSpaceWithLinkAccess(client: StarfishClient, userId: string, space: Space, sealed: SealedBlob): Promise<void>;
772
682
  /**
773
- * Create a new space owned by the identity. Seeds ONE generic `general` object node
774
- * into the object index (encrypted for private, plaintext for public).
775
- *
776
- * `opts.visibility` defaults to `'private'`.
683
+ * Create a new space owned by the identity. Seeds an empty plaintext object index.
684
+ * Apps populate the index with their own object types after creation using `createNode`.
777
685
  */
778
- declare function createSpace(session: Session, name: string, opts?: {
779
- visibility?: SpaceVisibility;
780
- }): Promise<Space>;
781
- declare class CategoryError extends Error {
782
- }
686
+ declare function createSpace(session: Session, name: string): Promise<Space>;
783
687
  declare function reconcileSpaceMeta(client: StarfishClient, userId: string, spaceId: string, shared: SpaceMeta, knownSpaces?: Space[]): Promise<void>;
784
688
 
785
689
  interface JoinRequest {
@@ -789,22 +693,15 @@ interface JoinRequest {
789
693
  }
790
694
  declare function makeJoinRequest(session: Session): string;
791
695
  /**
792
- * Owner-side: add a recipient's KEM key to a SPACE keyring (one keyring every
793
- * object in the space). Reused by {@link inviteToSpace} and by device pairing.
794
- */
795
- declare function addDeviceToSpaceKeyring(session: Session, spaceId: string, recipient: {
796
- kemPub: string;
797
- userId: string;
798
- }): Promise<void>;
799
- /**
800
- * Owner: invite an identity into a PRIVATE space. Adds them to the keyring, records
801
- * them in the roster, and mints a single space-scoped member cap.
696
+ * Owner: invite an identity into a space. Records them in the roster, mints a
697
+ * space-scoped member cap, and adds them to the space-wide keyring if it exists
698
+ * (so they can decrypt `enc` nodes from the start).
802
699
  * Returns the invite bundle JSON.
803
700
  */
804
701
  declare function inviteToSpace(session: Session, spaceId: string, requestJson: string, canWrite?: boolean, spaceName?: string): Promise<string>;
805
702
  /**
806
- * Invitee: accept a PRIVATE space invite — verify keyring access, store the cap,
807
- * and register the space. Returns the joined space.
703
+ * Invitee: accept a space invite — store the cap and register the space.
704
+ * Returns the joined space.
808
705
  */
809
706
  declare function acceptSpaceInvite(session: Session, inviteJson: string): Promise<Space>;
810
707
  /** A space invite link token (v:1, no ownerId — derive from cap.iss instead). */
@@ -831,10 +728,23 @@ declare function createSpaceInviteLink(session: Session, spaceId: string, spaceN
831
728
  link: string;
832
729
  }>;
833
730
  /**
834
- * Any user: join a PUBLIC space by redeeming an invite link token.
731
+ * Any user: join a space by redeeming an invite link token.
835
732
  * Stores the link credential locally and seals it into the synced `_spaces` doc.
836
733
  */
837
734
  declare function joinSpaceByLink(session: Session, token: SpaceInviteLinkToken): Promise<Space>;
735
+ /**
736
+ * Add a device's KEM key as a recipient of a space's keyring.
737
+ *
738
+ * Call this after device pairing (for each space the new device should be able to
739
+ * decrypt). ONE space keyring encrypts ALL the space's `enc` nodes — adding the device
740
+ * once unlocks the whole space's E2EE content. Silently a no-op if the keyring doesn't
741
+ * exist yet.
742
+ */
743
+ declare function addDeviceToSpaceKeyring(session: Session, spaceId: string, device: {
744
+ kemPub: string;
745
+ edPub: string;
746
+ userId: string;
747
+ }): Promise<void>;
838
748
  /**
839
749
  * Single sign-in hydration: merges server-side caps (plaintext member caps from
840
750
  * `_spaces.caps`) and sealed link access (from `_spaces.pubAccess`) into the
@@ -846,60 +756,214 @@ declare function recoverSpaceAccess(session: Session, server: {
846
756
  pubAccess: Record<string, SealedBlob>;
847
757
  }): Promise<void>;
848
758
 
759
+ interface CreateNodeInput {
760
+ type: ObjectType;
761
+ title: string;
762
+ emoji?: string;
763
+ parentId?: string | null;
764
+ /** Who may reach this node. Default: `'space'`. */
765
+ access?: NodeAccess;
766
+ /** Whether node content is E2EE under the space-wide keyring. Default: `false`. */
767
+ enc?: boolean;
768
+ /** App-specific metadata. */
769
+ meta?: Record<string, unknown>;
770
+ }
849
771
  /**
850
- * Pull + (private: decrypt) + project a space's object index into the legacy
851
- * `{ rooms, categories }` shape. `encryptor` is null for a PUBLIC space.
852
- * Returns null on failure or an empty index.
772
+ * Create a new node in a space's object index.
773
+ *
774
+ * - Rejects the invalid combo `public+enc`.
775
+ * - For `enc` nodes, ensures the space-wide keyring exists (minted once per space,
776
+ * idempotent on subsequent creates).
777
+ * - Returns the created node as it was inserted into the index.
853
778
  */
854
- declare function readIndexRooms(client: StarfishClient, encryptor: Encryptor | null, indexPath: string, spaceId: string): Promise<{
855
- rooms: Room[];
856
- categories: string[];
857
- } | null>;
779
+ declare function createNode(session: Session, spaceId: string, input: CreateNodeInput, reg?: {
780
+ owner: string | null;
781
+ members: string[];
782
+ } | null): Promise<ObjectNode>;
858
783
  /**
859
- * Read a space's index rooms + categories, resolving access automatically.
860
- * Pass `reg` (from `readRooms`) so the accessor picks the right auth mode.
784
+ * Patch the `access`/`enc` axes of a node in the index.
785
+ *
786
+ * - Rejects `public+enc`.
787
+ * - For enabling `enc`, ensures the space keyring exists (idempotent).
788
+ * - Content migration (moving between `objpub`/`objdoc`/`objinv`) is the caller's
789
+ * responsibility — this only flips the metadata flags.
861
790
  */
862
- declare function readSpaceIndexRooms(session: Session, spaceId: string, reg: {
791
+ declare function setNodeAccess(session: Session, spaceId: string, nodeId: string, patch: {
792
+ access?: NodeAccess;
793
+ enc?: boolean;
794
+ }, reg?: {
863
795
  owner: string | null;
864
796
  members: string[];
865
- visibility?: SpaceVisibility;
866
- }): Promise<{
867
- rooms: Room[];
868
- categories: string[];
869
- } | null>;
870
- /** SOFT read never throws, never mints. Returns an empty array on any failure. */
871
- declare function readSpaceRooms(session: Session, spaceId: string, hint?: {
872
- visibility?: SpaceVisibility;
873
- }): Promise<Room[]>;
797
+ } | null): Promise<void>;
798
+ interface NodeInviteBundle {
799
+ spaceId: string;
800
+ nodeId: string;
801
+ nodeName: string;
802
+ /** Space-level member cap (always present grants index read access). */
803
+ cap: unknown;
804
+ /** Per-node narrow cap (only for `invite+plaintext` nodes). */
805
+ nodeCap?: unknown;
806
+ }
874
807
  /**
875
- * Write the create-time seed into a space's index doc with an already-open access handle.
876
- * Accepts a nullable encryptor — plaintext push when null (public spaces).
877
- * Idempotent: a no-op if the index doc already exists (either encrypted or plaintext).
808
+ * Owner: invite an identity to a specific node.
809
+ *
810
+ * - For `enc` nodes: adds the invitee to the space-wide keyring (granting decryption
811
+ * access to ALL enc nodes in the space) and mints a space-level member cap.
812
+ * - For `invite+plaintext` nodes: mints both a space-level cap (index) and a
813
+ * narrow per-node cap (`nodeMemberScope`, covers `objinv` content).
814
+ *
815
+ * Returns the invite bundle JSON; pass to the invitee who calls `acceptNodeInvite`.
878
816
  */
879
- declare function pushIndexSeed(client: StarfishClient, encryptor: Encryptor | null, spaceId: string, rooms: SeedRoom[]): Promise<void>;
817
+ declare function inviteToNode(session: Session, spaceId: string, nodeId: string, requestJson: string, node: {
818
+ enc?: boolean;
819
+ }, nodeName?: string): Promise<string>;
880
820
  /**
881
- * Seed a brand-new space's index as the OWNER.
882
- * For private spaces: opens (minting if needed) the space keyring.
883
- * For public spaces: pushes plaintext nodes.
821
+ * Invitee: accept a direct node invite store the cap(s) and register access.
822
+ * Returns the nodeId.
884
823
  */
885
- declare function seedSpaceObjectIndex(session: Session, spaceId: string, rooms: SeedRoom[], opts?: {
886
- visibility?: SpaceVisibility;
887
- }): Promise<void>;
824
+ declare function acceptNodeInvite(session: Session, bundleJson: string): Promise<string>;
825
+ /** A node invite link token (v:1). */
826
+ interface NodeInviteLinkToken {
827
+ v: 1;
828
+ spaceId: string;
829
+ nodeId: string;
830
+ nodeName: string;
831
+ /** Cap scope depends on `enc`: spaceMemberScope for enc nodes, nodeMemberScope for plaintext. */
832
+ cap: unknown;
833
+ /** The ephemeral subject's Ed25519 private key (hex). */
834
+ key: string;
835
+ write: boolean;
836
+ }
837
+ declare function encodeNodeInviteLink(origin: string, token: NodeInviteLinkToken): string;
838
+ declare function decodeNodeInviteLink(fragment: string): NodeInviteLinkToken;
839
+ /**
840
+ * Owner: create a shareable invite link for a specific node.
841
+ *
842
+ * - For `enc` nodes: adds ephemeral KEM to the space-wide keyring; the link cap uses
843
+ * `spaceMemberScope` so the bearer can read the keyring and decrypt enc content.
844
+ * - For `invite+plaintext` nodes: narrow per-node cap (`nodeMemberScope`), no keyring.
845
+ *
846
+ * Anyone with the link can access the node; revoke by calling
847
+ * `removeSpaceMember(ephemeralUserId)` (and rotating the space keyring for enc nodes).
848
+ */
849
+ declare function createNodeInviteLink(session: Session, spaceId: string, nodeId: string, nodeName: string, node: {
850
+ enc?: boolean;
851
+ }, write: boolean, origin: string): Promise<{
852
+ token: NodeInviteLinkToken;
853
+ link: string;
854
+ }>;
855
+ /**
856
+ * Any user: access a node by redeeming an invite link token.
857
+ * Stores the per-node link entry locally and seals it into the synced `_spaces` doc.
858
+ */
859
+ declare function joinNodeByLink(session: Session, token: NodeInviteLinkToken): Promise<string>;
860
+
861
+ /**
862
+ * Generic object-tree model — pure logic over a space's object index.
863
+ *
864
+ * A space's contents are {@link ObjectNode}s in one union-merged index doc at
865
+ * `spaces/{spaceId}/objects/_index`. This module is the pure, testable core:
866
+ * the tree builder + merge-artifact guards, breadcrumbs, ordering, and the node
867
+ * reducers a `store.set` applies.
868
+ *
869
+ * Because the index is union-merged (per-node last-write-wins keyed on `updatedAt`),
870
+ * the tree is eventually consistent — two devices can concurrently produce a cycle
871
+ * or an orphan. The builder below is the single place those are repaired so every
872
+ * consumer renders a well-formed tree.
873
+ *
874
+ * No domain types (room, category, task, …) are defined here. Apps define their own.
875
+ */
876
+
877
+ /** A node plus its resolved children — the shape a tree view renders. */
878
+ interface ObjectTreeNode extends ObjectNode {
879
+ depth: number;
880
+ children: ObjectTreeNode[];
881
+ }
882
+ /** The order value for a new node appended after `siblings`. */
883
+ declare function nextOrder(siblings: ObjectNode[]): number;
888
884
  /**
889
- * Headless read-modify-write of a space's unified OBJECT INDEX. Works for both
890
- * private (encrypt round-trip) and public (plaintext) spaces. Retries up to 3 times
891
- * on ConflictError.
885
+ * Build the render tree from a flat node list, repairing merge artifacts:
886
+ * - **archived** nodes (and their subtrees) are dropped.
887
+ * - **orphans** — a `parentId` that is missing or archived — reparent to root.
888
+ * - **cycles** — a node reachable from itself via `parentId` — reparent to root.
889
+ * - **siblings** sort by {@link compareSiblings} for cross-device determinism.
890
+ */
891
+ declare function buildTree(nodes: ObjectNode[], includeArchived?: boolean): ObjectTreeNode[];
892
+ /** The root→node trail (inclusive) for breadcrumbs. Returns `[]` if unknown. */
893
+ declare function breadcrumbs(nodes: ObjectNode[], id: ID): ObjectNode[];
894
+ /** The root→parent trail (EXCLUSIVE of the node itself). */
895
+ declare function ancestors(nodes: ObjectNode[], id: ID): ObjectNode[];
896
+ /** The ids of a node and its whole subtree (for cascade-archive). */
897
+ declare function subtreeIds(nodes: ObjectNode[], rootId: ID): Set<ID>;
898
+ interface NewObjectInput {
899
+ type: ObjectType;
900
+ parentId?: ID | null;
901
+ title: string;
902
+ emoji?: string;
903
+ /** App-specific metadata passed through to node.meta. */
904
+ meta?: Record<string, unknown>;
905
+ /** Provide to reuse an id (e.g. a node id derived elsewhere); else minted. */
906
+ id?: ID;
907
+ /** Who may reach this node. Absent ⇒ `'space'` (all space members). */
908
+ access?: NodeAccess;
909
+ /** Whether the node's content is E2EE under its own per-node keyring. Absent ⇒ false. */
910
+ enc?: boolean;
911
+ }
912
+ /** Append a new node under `parentId` at the end of its sibling order. */
913
+ declare function addObject(nodes: ObjectNode[], input: NewObjectInput, now: number): {
914
+ nodes: ObjectNode[];
915
+ node: ObjectNode;
916
+ };
917
+ /** Patch a node's mutable metadata (title/emoji/meta/access/enc), bumping `updatedAt`. */
918
+ declare function patchObject(nodes: ObjectNode[], id: ID, patch: Partial<Pick<ObjectNode, 'title' | 'emoji' | 'meta' | 'access' | 'enc'>>, now: number): ObjectNode[];
919
+ /** Reparent a node (move in the tree). Rejects making a node its own descendant. */
920
+ declare function reparentObject(nodes: ObjectNode[], id: ID, parentId: ID | null, now: number): ObjectNode[];
921
+ /** Set explicit sibling order (drag-reorder). */
922
+ declare function reorderObjects(nodes: ObjectNode[], orderById: Record<ID, number>, now: number): ObjectNode[];
923
+ /** Cascade-archive a node and its whole subtree (soft delete). */
924
+ declare function archiveObject(nodes: ObjectNode[], id: ID, now: number): ObjectNode[];
925
+
926
+ /**
927
+ * Write the create-time seed into a space's index doc.
928
+ * Idempotent: a no-op if the index doc already exists.
929
+ * Pass `nodes` to seed with initial objects; defaults to an empty index.
930
+ */
931
+ declare function pushIndexSeed(client: _drakkar_software_starfish_client.StarfishClient, spaceId: string, nodes?: ObjectNode[]): Promise<void>;
932
+ /**
933
+ * Seed a brand-new space's index as the OWNER. Always plaintext.
934
+ * Pass `nodes` to seed with initial objects; defaults to an empty index.
935
+ */
936
+ declare function seedSpaceObjectIndex(session: Session, spaceId: string, nodes?: ObjectNode[]): Promise<void>;
937
+ /**
938
+ * Headless read-modify-write of a space's unified OBJECT INDEX.
939
+ * Always plaintext. Retries up to 3 times on ConflictError.
940
+ *
941
+ * The mutator receives the current nodes with real (or empty, for invite) titles.
942
+ * Before writing back, invite nodes have their title/emoji stripped again.
892
943
  */
893
944
  declare function updateObjectIndex(session: Session, spaceId: string, mutator: (nodes: ObjectNode[], now: number) => ObjectNode[] | null, reg?: {
894
945
  owner: string | null;
895
946
  members: string[];
896
- visibility?: SpaceVisibility;
897
947
  } | null): Promise<void>;
948
+ /**
949
+ * Read the current object tree (read-only, no mutation). Returns the stored
950
+ * nodes (titles are empty for invite nodes the caller is not invited to).
951
+ */
952
+ declare function readObjectTree(session: Session, spaceId: string): Promise<ObjectNode[]>;
898
953
 
899
954
  /** The QR-payload prefix this SDK uses. Kept distinct from `octochat-pair:` so apps
900
955
  * can route QR payloads to the correct handler during their migration window. */
901
956
  declare const PAIR_PREFIX = "octospaces-pair:";
902
- /** Existing device: provision + PIN-seal a new device, publish to rendezvous, return the QR payload. */
957
+ /**
958
+ * Existing device: provision + PIN-seal a new device, publish to rendezvous, return
959
+ * the QR payload.
960
+ *
961
+ * After pairing, call `addDeviceToSpaceKeyring(session, spaceId, newDeviceKeys)` for
962
+ * each space whose E2EE content the new device should decrypt. ONE space keyring
963
+ * encrypts ALL `enc` nodes in a space — one call per space unlocks everything.
964
+ * Plaintext (`space` / `public`) nodes are immediately accessible via the linked-device
965
+ * cap-cert (no extra keyring step).
966
+ */
903
967
  declare function startDevicePairing(session: Session, pin: string): Promise<string>;
904
968
  interface PairResult {
905
969
  userId: string;
@@ -969,4 +1033,4 @@ declare const starfishBase64: Base64Provider;
969
1033
  declare function toBase64Url(json: string): string;
970
1034
  declare function fromBase64Url(b64url: string): string;
971
1035
 
972
- export { type AdaptedCategory, type ArchivedDms, type AutomationMeta, BUILTIN_OBJECT_TYPES, CONNECT_TIMEOUT_MS, type CapMap, CategoryError, DEFAULT_CATEGORY, type DerivedIdentity, type DeviceKeys, type DmMap, type ID, type JoinRequest, type KvAdapter, type LinkedIdentity, type MutePrefs, type NewObjectInput, OBJECT_COLLECTIONS, type ObjectContentKind, type ObjectNode, type ObjectTreeNode, type ObjectType, type ObjectsIndex, type OctoSpacesConfig, PAIR_PREFIX, PULL_CACHE_MAX_AGE_MS, type PairResult, type PasskeyEnrollment, type PersistedSession, type PubAccessMap, type PublicProfile, type ReadPrefs, type Room, type RoomKind, type RoomSubtype, type SealedBlob, type SeedLock, type SeedRoom, type Session, type Space, type SpaceAccessEntry, SpaceAccessError, type SpaceAccessHandle, type SpaceAccessMap, type SpaceInviteLinkToken, type SpaceMeta, type SpaceMetaUpdate, type SpaceVisibility, type UnlockMethod, type Vault, type VaultLoad, acceptSpaceInvite, accountScope, addDeviceToSpaceKeyring, addJoinedSpace, addJoinedSpaceWithCap, addJoinedSpaceWithLinkAccess, addObject, addSpaceMember, ancestors, archiveObject, attachmentPull, attachmentPush, breadcrumbs, broadcastSpaceMeta, buildAuthHeaders, buildEncryptor, buildLinkedSession, buildSession, buildSpaceAccess, buildTree, bytesToHex, cacheProfile, capProviderFor, categoryId, clearSpaceAccessCache, clearSpaceAccessStore, completeDevicePairing, configureKv, configureOctoSpaces, createSpace, createSpaceInviteLink, decodeSpaceInviteLink, deriveSession, encodeSpaceInviteLink, ensureProfileKeys, ensurePseudo, excludeAutomatedRooms, fetchWithTimeout, fingerprintFromUserId, fromBase64Url, generateSeedWords, getSharedSpacesNamespace, getSpaceAccess, getSpaceAccessEntry, getSyncBase, getSyncNamespace, getSyncPrefix, hydrateSpaceAccessStore, inviteToSpace, isValidSeed, joinSpaceByLink, keyringName, keyringPull, keyringPush, kvGet, kvRemove, kvSet, linkAccessFromStore, linkedDeviceScope, loadCachedProfile, localSpaceAccessEntries, makeClient, makeJoinRequest, memberCapsFromStore, nextOrder, normalizeCategories, objDocPull, objDocPush, objIndexPull, objIndexPush, objLogPull, objLogPush, objectBlobPull, objectBlobPush, objectsToRoomCategories, onSpaceMeta, openEncryptor, ownerEnsureKeyring, ownerScope, ownerTrustedAdders, patchObject, profilePull, profilePush, pullCache, pushIndexSeed, randomId, readIndexRooms, readProfile, readProfiles, readPseudo, readRooms, readSpaceIndexRooms, readSpaceRooms, readSpaces, reconcileSpaceMeta, recoverSpaceAccess, removeSpaceAccessEntry, removeSpaceMember, reorderObjects, reorderSpaces, reparentObject, roomKindToSubtype, roomSlug, roomsRegistryPull, roomsRegistryPush, rootIdentityOf, saveSpaceAccessEntry, sealToRecipient, sealToSelf, seedIndexNodes, seedSpaceObjectIndex, setDmMapping, spaceIndexPull, spaceMemberScope, spacesPull, spacesPush, starfishBase64, startDevicePairing, subtreeIds, subtypeToRoomKind, toBase64Url, typesIndexPull, typesIndexPush, unsealFromRecipient, unsealFromSelf, updateArchivedDmsDoc, updateDmsDoc, updateMutesDoc, updateObjectIndex, updateQuickReactionsDoc, updateReadsDoc, updateSpacesDoc, userIdFromEdPub, writeProfile, writePseudo, writeRooms, writeSpaces };
1036
+ export { type ArchivedDms, CONNECT_TIMEOUT_MS, type CapMap, type CreateNodeInput, type DerivedIdentity, type DeviceKeys, type DmMap, type ID, type JoinRequest, type KvAdapter, type LinkedIdentity, type MutePrefs, type NewObjectInput, type NodeAccess, type NodeAccessHandle, type NodeInviteBundle, type NodeInviteLinkToken, OBJECT_COLLECTIONS, type ObjectContentKind, type ObjectNode, type ObjectTreeNode, type ObjectType, type ObjectsIndex, type OctoSpacesConfig, PAIR_PREFIX, PULL_CACHE_MAX_AGE_MS, type PairResult, type PasskeyEnrollment, type PersistedSession, type PubAccessMap, type PublicProfile, type ReadPrefs, type SealedBlob, type SeedLock, type Session, type Space, type SpaceAccessEntry, SpaceAccessError, type SpaceAccessMap, type SpaceInviteLinkToken, type SpaceMeta, type SpaceMetaUpdate, type UnlockMethod, type Vault, type VaultLoad, acceptNodeInvite, acceptSpaceInvite, accountScope, addDeviceToSpaceKeyring, addJoinedSpace, addJoinedSpaceWithCap, addJoinedSpaceWithLinkAccess, addObject, addSpaceMember, ancestors, archiveObject, attachmentPull, attachmentPush, breadcrumbs, broadcastSpaceMeta, buildAuthHeaders, buildEncryptor, buildLinkedSession, buildNodeAccess, buildSession, buildTree, bytesToHex, cacheProfile, capProviderFor, clearNodeAccessCache, clearSpaceAccessStore, completeDevicePairing, configureKv, configureOctoSpaces, createNode, createNodeInviteLink, createSpace, createSpaceInviteLink, decodeNodeInviteLink, decodeSpaceInviteLink, deriveSession, encodeNodeInviteLink, encodeSpaceInviteLink, ensureProfileKeys, ensurePseudo, fetchWithTimeout, fingerprintFromUserId, fromBase64Url, generateSeedWords, getNodeAccess, getNodeAccessEntry, getSharedSpacesNamespace, getSpaceAccessEntry, getSpaceClient, getSyncBase, getSyncNamespace, getSyncPrefix, hydrateSpaceAccessStore, inviteToNode, inviteToSpace, isValidSeed, joinNodeByLink, joinSpaceByLink, keyringName, keyringPull, keyringPush, kvGet, kvRemove, kvSet, linkAccessFromStore, linkedDeviceScope, loadCachedProfile, localSpaceAccessEntries, makeClient, makeJoinRequest, memberCapsFromStore, nextOrder, nodeMemberScope, objDocPull, objDocPush, objIndexPull, objIndexPush, objInvName, objInvPull, objInvPush, objLogPull, objLogPush, objPubName, objPubPull, objPubPush, objectBlobPull, objectBlobPush, objectDirName, objectDirPull, onSpaceMeta, openEncryptor, ownerEnsureKeyring, ownerScope, ownerTrustedAdders, patchObject, profilePull, profilePush, pullCache, pushIndexSeed, randomId, readObjectTree, readProfile, readProfiles, readPseudo, readSpaceAccess, readSpaces, reconcileSpaceMeta, recoverSpaceAccess, removeNodeAccessEntry, removeSpaceAccessEntry, removeSpaceMember, reorderObjects, reorderSpaces, reparentObject, roomSlug, rootIdentityOf, saveNodeAccessEntry, saveSpaceAccessEntry, sealToRecipient, sealToSelf, seedSpaceObjectIndex, setDmMapping, setNodeAccess, spaceAccessPull, spaceAccessPush, spaceMemberScope, spacesPull, spacesPush, starfishBase64, startDevicePairing, subtreeIds, toBase64Url, typesIndexPull, typesIndexPush, unsealFromRecipient, unsealFromSelf, updateArchivedDmsDoc, updateDmsDoc, updateMutesDoc, updateObjectIndex, updateQuickReactionsDoc, updateReadsDoc, updateSpacesDoc, userIdFromEdPub, writeProfile, writePseudo, writeSpaceAccess, writeSpaces };