@ibgib/space-gib 0.0.4 → 0.0.6

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.
Files changed (42) hide show
  1. package/README.md +7 -0
  2. package/dist/client/bootstrap.mjs +1 -1
  3. package/dist/client/chunk-734MMI4C.mjs +42 -0
  4. package/dist/client/chunk-734MMI4C.mjs.map +7 -0
  5. package/dist/client/{chunk-ZUEU37Z5.mjs → chunk-SMOZ2D5E.mjs} +170 -41
  6. package/dist/client/chunk-SMOZ2D5E.mjs.map +7 -0
  7. package/dist/client/index.html +35 -14
  8. package/dist/client/index.mjs +1 -1
  9. package/dist/client/script.mjs +1 -1
  10. package/dist/client/style.css +29 -5
  11. package/dist/server/server.mjs +338 -31
  12. package/dist/server/server.mjs.map +3 -3
  13. package/package.json +4 -4
  14. package/src/client/AUTO-GENERATED-version.mts +1 -1
  15. package/src/client/components/identity-header/identity-header.css +40 -2
  16. package/src/client/components/identity-header/identity-header.html +3 -2
  17. package/src/client/components/identity-header/identity-header.mts +87 -26
  18. package/src/client/components/identity-manager/IMPLEMENTATION.md +10 -10
  19. package/src/client/components/identity-manager/identity-manager.css +39 -0
  20. package/src/client/components/identity-manager/identity-manager.html +20 -0
  21. package/src/client/components/identity-manager/identity-manager.mts +59 -0
  22. package/src/client/components/keystone-creator/keystone-creator.mts +88 -12
  23. package/src/client/dev-tools/common.mts +505 -10
  24. package/src/client/dev-tools/phase-4-1.mts +19 -4
  25. package/src/client/dev-tools/phase-4-10.mts +77 -305
  26. package/src/client/dev-tools/phase-4-2.mts +46 -212
  27. package/src/client/dev-tools/phase-4-3.mts +57 -198
  28. package/src/client/dev-tools/phase-4-4.mts +57 -170
  29. package/src/client/dev-tools/phase-4-5.mts +48 -204
  30. package/src/client/dev-tools/phase-4-6.mts +37 -103
  31. package/src/client/dev-tools/phase-4-7.mts +44 -228
  32. package/src/client/dev-tools/phase-4-8.mts +33 -136
  33. package/src/client/dev-tools/phase-4-9.mts +33 -137
  34. package/src/client/dev-tools/phase-4.mts +30 -6
  35. package/src/client/dev-tools.mts +18 -0
  36. package/src/client/index.html +35 -14
  37. package/src/client/style.css +29 -5
  38. package/src/client/types.mts +4 -1
  39. package/dist/client/chunk-NCXKCVYS.mjs +0 -42
  40. package/dist/client/chunk-NCXKCVYS.mjs.map +0 -7
  41. package/dist/client/chunk-ZUEU37Z5.mjs.map +0 -7
  42. package/dist/respec-gib.node.mjs +0 -5
@@ -1,4 +1,4 @@
1
- import { extractErrorMsg, getTimestamp, getUUID, } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
1
+ import { extractErrorMsg, getTimestamp, getUUID, hash } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
2
2
  import { Factory_V1 as factory } from '@ibgib/ts-gib/dist/V1/factory.mjs';
3
3
  import { ROOT } from '@ibgib/ts-gib/dist/V1/constants.mjs';
4
4
  import { getIbGibAddr, } from '@ibgib/ts-gib/dist/helper.mjs';
@@ -10,6 +10,7 @@ import { SyncSagaCoordinator } from '@ibgib/core-gib/dist/sync/sync-saga-coordin
10
10
  import { getGlobalMetaspace_waitIfNeeded } from "@ibgib/web-gib/dist/helpers.mjs";
11
11
  import { SESSION_KEYSTONE_POLICY, getSpaceGibPoolConfig } from "../../common/keystone-policies.mjs";
12
12
  import { SpaceGibApiBridge } from '../api/space-gib-api-bridge.mjs';
13
+ import { KEYSTONE_VERB_SYNC } from '@ibgib/core-gib/dist/keystone/keystone-constants.mjs';
13
14
  import { SyncConflictStrategy } from '@ibgib/core-gib/dist/sync/sync-constants.mjs';
14
15
  import { GLOBAL_LOG_A_LOT } from '../constants.mjs';
15
16
 
@@ -57,6 +58,99 @@ export interface PhaseSetupOptions {
57
58
  data: any;
58
59
  }
59
60
 
61
+ export async function createAndRegisterSyncDelegate({
62
+ domainI,
63
+ masterSecret,
64
+ syncSalt,
65
+ metaspace,
66
+ space,
67
+ apiBridge
68
+ }: {
69
+ domainI: KeystoneIbGib_V1;
70
+ masterSecret: string;
71
+ syncSalt: string;
72
+ metaspace: any;
73
+ space: any;
74
+ apiBridge: SpaceGibApiBridge;
75
+ }): Promise<{ evolvedDomainI: KeystoneIbGib_V1; delegateKeystone: KeystoneIbGib_V1; delegateSecret: string }> {
76
+ const keystoneService = new KeystoneService_V1();
77
+
78
+ // 1. Derive delegate secret
79
+ const delegateSecret = await hash({ s: `${masterSecret}-sync-delegate` });
80
+
81
+ // 2. Genesis the delegate keystone
82
+ const delegateKeystone = await keystoneService.genesis({
83
+ masterSecret: delegateSecret,
84
+ configs: [
85
+ getSpaceGibPoolConfig('sync', `${syncSalt}-delegate`),
86
+ ],
87
+ metaspace,
88
+ space,
89
+ frameDetails: { client: 'space-gib-web-dev-delegate', timestamp: getTimestamp() }
90
+ });
91
+
92
+ // 3. Register delegate on parent Domain identity
93
+ const evolvedDomainI = await keystoneService.registerDelegate({
94
+ parentKeystone: domainI,
95
+ delegateKeystone,
96
+ masterSecret,
97
+ metaspace,
98
+ space,
99
+ allowedVerbs: [KEYSTONE_VERB_SYNC]
100
+ });
101
+
102
+ const domainAddr = getIbGibAddr({ ibGib: domainI });
103
+
104
+ // 4. Sync evolved parent and delegate to server
105
+ const resEvolve = await apiBridge.putEvolveKeystone(
106
+ domainAddr,
107
+ evolvedDomainI,
108
+ [delegateKeystone]
109
+ );
110
+ if (!resEvolve.success) {
111
+ throw new Error(`Server rejected delegate evolution: ${resEvolve.message}`);
112
+ }
113
+
114
+ return { evolvedDomainI, delegateKeystone, delegateSecret };
115
+ }
116
+
117
+ export async function getSenderIdentityInfo({
118
+ domainI,
119
+ masterSecret,
120
+ metaspace,
121
+ space
122
+ }: {
123
+ domainI: KeystoneIbGib_V1;
124
+ masterSecret: string;
125
+ metaspace: any;
126
+ space: any;
127
+ }): Promise<{ senderIdentity: KeystoneIbGib_V1; fnSenderSecret: () => Promise<string> }> {
128
+ let senderIdentity = domainI;
129
+ let fnSenderSecret = async () => masterSecret;
130
+
131
+ // Find the delegate that is authorized for sync (not just [0] — there may be others)
132
+ const syncDelegateEntry = Object.values(domainI.data?.delegates ?? {}).find(
133
+ d => d.allowedVerbs?.includes(KEYSTONE_VERB_SYNC)
134
+ );
135
+
136
+ if (syncDelegateEntry) {
137
+ const delegateTjpAddr = syncDelegateEntry.delegateTjpAddr;
138
+ if (delegateTjpAddr) {
139
+ // Always resolve the LATEST evolved delegate tip, not the genesis frame
140
+ const latestDelegateAddr = await metaspace.getLatestAddr({ addr: delegateTjpAddr, space });
141
+ if (latestDelegateAddr) {
142
+ const resDel = await metaspace.get({ addr: latestDelegateAddr, space });
143
+ if (resDel.success && resDel.ibGibs && resDel.ibGibs.length > 0) {
144
+ senderIdentity = resDel.ibGibs[0] as KeystoneIbGib_V1;
145
+ const delegateSecret = await hash({ s: `${masterSecret}-sync-delegate` });
146
+ fnSenderSecret = async () => delegateSecret;
147
+ }
148
+ }
149
+ }
150
+ }
151
+ return { senderIdentity, fnSenderSecret };
152
+ }
153
+
60
154
  export async function performPhaseSetup(opts: PhaseSetupOptions): Promise<void> {
61
155
  const lc_fn = `${lc}[performPhaseSetup(${opts.phaseText})]`;
62
156
  const btn = document.getElementById(opts.btnId) as HTMLButtonElement | null;
@@ -72,11 +166,11 @@ export async function performPhaseSetup(opts: PhaseSetupOptions): Promise<void>
72
166
 
73
167
  const keystoneService = new KeystoneService_V1();
74
168
 
75
- // 1. Create Genesis Domain Keystone (I^Itjp) in local space
169
+ // 1. Create Genesis Domain Keystone (I^Itjp) in local space.
170
+ // The domain identity only needs a 'manage' pool — the delegate gets 'sync'.
76
171
  const domainI = await keystoneService.genesis({
77
172
  masterSecret: opts.masterSecret,
78
173
  configs: [
79
- getSpaceGibPoolConfig('sync', opts.syncSalt),
80
174
  getSpaceGibPoolConfig('manage', opts.manageSalt),
81
175
  ],
82
176
  metaspace,
@@ -84,9 +178,6 @@ export async function performPhaseSetup(opts: PhaseSetupOptions): Promise<void>
84
178
  frameDetails: { client: 'space-gib-web-dev', timestamp: getTimestamp() }
85
179
  });
86
180
 
87
- debugState.domainI = domainI;
88
- debugState.domainIMasterSecret = opts.masterSecret;
89
-
90
181
  devLog(`${opts.phaseText} Setup: ✓ Domain Keystone (I) created locally: ${getIbGibAddr({ ibGib: domainI })}`);
91
182
  devLog(`${opts.phaseText} Setup: Starting postGenesisKeystone...`);
92
183
 
@@ -100,6 +191,22 @@ export async function performPhaseSetup(opts: PhaseSetupOptions): Promise<void>
100
191
  throw new Error(`${opts.phaseText} Setup: Server rejected genesis domain keystone: ${resGenesis.message}`);
101
192
  }
102
193
 
194
+ // 2.5 Generate and register delegate
195
+ devLog(`${opts.phaseText} Setup: Generating and registering sync delegate...`);
196
+ const { evolvedDomainI, delegateKeystone } = await createAndRegisterSyncDelegate({
197
+ domainI,
198
+ masterSecret: opts.masterSecret,
199
+ syncSalt: opts.syncSalt,
200
+ metaspace,
201
+ space,
202
+ apiBridge
203
+ });
204
+
205
+ debugState.domainI = evolvedDomainI;
206
+ debugState.domainIMasterSecret = opts.masterSecret;
207
+
208
+ devLog(`${opts.phaseText} Setup: ✓ Registered Sync Delegate: ${getIbGibAddr({ ibGib: delegateKeystone })}`);
209
+
103
210
  // 3. Create Test IbGib (X) locally
104
211
  const resX = await factory.firstGen({
105
212
  parentIbGib: ROOT,
@@ -173,11 +280,18 @@ export async function performPhaseSync(opts: PhaseSyncOptions): Promise<void> {
173
280
  debugState.sagaId = sagaId;
174
281
  debugState.senderPeer = senderPeer;
175
282
 
283
+ const { senderIdentity, fnSenderSecret } = await getSenderIdentityInfo({
284
+ domainI,
285
+ masterSecret: debugState.domainIMasterSecret!,
286
+ metaspace,
287
+ space
288
+ });
289
+
176
290
  await senderPeer.initializeOpts({
177
291
  localMetaspace: metaspace,
178
292
  localSpace: space,
179
- senderIdentity: domainI,
180
- fnSenderSecret: async () => debugState.domainIMasterSecret!,
293
+ senderIdentity,
294
+ fnSenderSecret,
181
295
  sagaId,
182
296
  sessionConnectPoolConfig: SESSION_KEYSTONE_POLICY.CONNECT_POOL as any,
183
297
  sessionSyncPoolConfig: SESSION_KEYSTONE_POLICY.DEFAULT_POOL as any,
@@ -187,8 +301,11 @@ export async function performPhaseSync(opts: PhaseSyncOptions): Promise<void> {
187
301
  try {
188
302
  const syncSaga = await coordinator.sync({
189
303
  domainIbGibs: [targetX],
190
- senderIdentity: domainI,
191
- fnSenderSecret: async () => debugState.domainIMasterSecret!,
304
+ senderIdentityInfo: {
305
+ senderIdentity,
306
+ senderDomainAddr: domainAddr,
307
+ },
308
+ fnSenderSecret,
192
309
  peer: senderPeer,
193
310
  localSpace: space,
194
311
  metaspace,
@@ -215,3 +332,381 @@ export async function performPhaseSync(opts: PhaseSyncOptions): Promise<void> {
215
332
  btn.disabled = false;
216
333
  }
217
334
  }
335
+
336
+ export async function getDelegateAndSessionInfo({
337
+ localI,
338
+ metaspace,
339
+ space
340
+ }: {
341
+ localI: KeystoneIbGib_V1;
342
+ metaspace: any;
343
+ space: any;
344
+ }): Promise<{
345
+ delegateTjpAddr?: string;
346
+ latestLocalDelegateAddr?: string;
347
+ localDelegate?: KeystoneIbGib_V1;
348
+ sessionIdentityTjpAddr?: string;
349
+ }> {
350
+ // Find the delegate that is specifically authorized for sync
351
+ const delegateInfo = Object.values(localI.data?.delegates ?? {}).find(
352
+ d => d.allowedVerbs?.includes(KEYSTONE_VERB_SYNC)
353
+ );
354
+ if (!delegateInfo) { return {}; }
355
+ const delegateTjpAddr = delegateInfo.delegateTjpAddr;
356
+ if (!delegateTjpAddr) { return {}; }
357
+ const latestLocalDelegateAddr = await metaspace.getLatestAddr({
358
+ addr: delegateTjpAddr,
359
+ space
360
+ });
361
+ if (!latestLocalDelegateAddr) { return { delegateTjpAddr }; }
362
+ const getDelRes = await metaspace.get({ addrs: [latestLocalDelegateAddr], space });
363
+ const localDelegate = getDelRes.ibGibs?.[0] as KeystoneIbGib_V1 | undefined;
364
+ if (!localDelegate) { return { delegateTjpAddr, latestLocalDelegateAddr }; }
365
+ const syncProof = localDelegate.data?.proofs?.find(p => p.claim?.verb === 'sync');
366
+ const sessionIdentityTjpAddr = syncProof?.claim?.target;
367
+ return {
368
+ delegateTjpAddr,
369
+ latestLocalDelegateAddr,
370
+ localDelegate,
371
+ sessionIdentityTjpAddr
372
+ };
373
+ }
374
+
375
+ /**
376
+ * Executes one sync pass using the delegate keystone as the sender identity.
377
+ * Always resolves the current delegate tip from the domain identity stored in
378
+ * debugState, so it is safe to call in sequence across multiple passes.
379
+ *
380
+ * @param domainIbGibs - The ibgibs to sync in this pass.
381
+ * @param passLabel - Label used in devLog messages (e.g. "Pass 1 (Sync C1)").
382
+ */
383
+ export async function runSyncPass({
384
+ domainIbGibs,
385
+ passLabel,
386
+ metaspace,
387
+ space,
388
+ }: {
389
+ domainIbGibs: any[];
390
+ passLabel: string;
391
+ metaspace: any;
392
+ space: any;
393
+ }): Promise<void> {
394
+ const domainI = debugState.domainI;
395
+ const masterSecret = debugState.domainIMasterSecret;
396
+ if (!domainI || !masterSecret) {
397
+ throw new Error(`runSyncPass[${passLabel}]: Missing domain identity or secret in debugState. Run Setup first.`);
398
+ }
399
+
400
+ const domainAddr = getIbGibAddr({ ibGib: domainI });
401
+ const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
402
+
403
+ const { senderIdentity, fnSenderSecret } = await getSenderIdentityInfo({
404
+ domainI,
405
+ masterSecret,
406
+ metaspace,
407
+ space,
408
+ });
409
+
410
+ devLog(`Sync: ${passLabel} — using sender: ${getIbGibAddr({ ibGib: senderIdentity }).slice(0, 40)}...`);
411
+
412
+ const senderPeer = new SyncPeerWebSocketSender_V1({
413
+ classname: 'SyncPeerWebSocketSender_V1',
414
+ httpEvolveUrl: `${location.protocol}//${location.host}/api/keystone/evolve/${encodeURIComponent(domainAddr)}`,
415
+ wsUrl: `${protocol}//${location.host}/api/sync/ws/${encodeURIComponent(domainAddr)}`
416
+ });
417
+ const coordinator = new SyncSagaCoordinator();
418
+ const sagaId = await getUUID();
419
+ debugState.sagaId = sagaId;
420
+ debugState.senderPeer = senderPeer;
421
+
422
+ await senderPeer.initializeOpts({
423
+ localMetaspace: metaspace,
424
+ localSpace: space,
425
+ senderIdentity,
426
+ fnSenderSecret,
427
+ sagaId,
428
+ sessionConnectPoolConfig: SESSION_KEYSTONE_POLICY.CONNECT_POOL as any,
429
+ sessionSyncPoolConfig: SESSION_KEYSTONE_POLICY.DEFAULT_POOL as any,
430
+ targetAddrs: [domainAddr]
431
+ });
432
+
433
+ const syncSaga = await coordinator.sync({
434
+ domainIbGibs,
435
+ senderIdentityInfo: {
436
+ senderIdentity,
437
+ senderDomainAddr: domainAddr,
438
+ },
439
+ fnSenderSecret,
440
+ peer: senderPeer,
441
+ localSpace: space,
442
+ metaspace,
443
+ conflictStrategy: SyncConflictStrategy.optimisticWithLCS,
444
+ });
445
+ await syncSaga.done;
446
+
447
+ // After each pass the domain identity may have evolved — refresh debugState
448
+ const latestDomainAddr = await metaspace.getLatestAddr({ addr: domainAddr, space });
449
+ if (latestDomainAddr && latestDomainAddr !== getIbGibAddr({ ibGib: domainI })) {
450
+ const res = await metaspace.get({ addrs: [latestDomainAddr], space });
451
+ if (res.ibGibs?.[0]) { debugState.domainI = res.ibGibs[0] as KeystoneIbGib_V1; }
452
+ }
453
+ }
454
+
455
+ /**
456
+ * Like `runSyncPass` but accepts an explicit space, domainI, and masterSecret.
457
+ * Use this when syncing from a secondary (e.g., "remote") space that is NOT
458
+ * the current default local user space.
459
+ *
460
+ * After the sync, copies the evolved domain identity tip back to `primarySpace`
461
+ * (if provided) and updates debugState.domainI.
462
+ */
463
+ export async function runSyncPassFromSpace({
464
+ domainIbGibs,
465
+ passLabel,
466
+ metaspace,
467
+ space,
468
+ primarySpace,
469
+ domainI,
470
+ masterSecret,
471
+ }: {
472
+ domainIbGibs: any[];
473
+ passLabel: string;
474
+ metaspace: any;
475
+ space: any;
476
+ /** If provided, the evolved domain identity is copied back here after sync. */
477
+ primarySpace?: any;
478
+ domainI: KeystoneIbGib_V1;
479
+ masterSecret: string;
480
+ }): Promise<void> {
481
+ const domainAddr = getIbGibAddr({ ibGib: domainI });
482
+ const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
483
+
484
+ const { senderIdentity, fnSenderSecret } = await getSenderIdentityInfo({
485
+ domainI,
486
+ masterSecret,
487
+ metaspace,
488
+ space,
489
+ });
490
+
491
+ devLog(`Sync: ${passLabel} — using sender: ${getIbGibAddr({ ibGib: senderIdentity }).slice(0, 40)}...`);
492
+
493
+ const senderPeer = new SyncPeerWebSocketSender_V1({
494
+ classname: 'SyncPeerWebSocketSender_V1',
495
+ httpEvolveUrl: `${location.protocol}//${location.host}/api/keystone/evolve/${encodeURIComponent(domainAddr)}`,
496
+ wsUrl: `${protocol}//${location.host}/api/sync/ws/${encodeURIComponent(domainAddr)}`
497
+ });
498
+ const coordinator = new SyncSagaCoordinator();
499
+ const sagaId = await getUUID();
500
+ debugState.sagaId = sagaId;
501
+ debugState.senderPeer = senderPeer;
502
+
503
+ await senderPeer.initializeOpts({
504
+ localMetaspace: metaspace,
505
+ localSpace: space,
506
+ senderIdentity,
507
+ fnSenderSecret,
508
+ sagaId,
509
+ sessionConnectPoolConfig: SESSION_KEYSTONE_POLICY.CONNECT_POOL as any,
510
+ sessionSyncPoolConfig: SESSION_KEYSTONE_POLICY.DEFAULT_POOL as any,
511
+ targetAddrs: [domainAddr]
512
+ });
513
+
514
+ const syncSaga = await coordinator.sync({
515
+ domainIbGibs,
516
+ senderIdentityInfo: {
517
+ senderIdentity,
518
+ senderDomainAddr: domainAddr,
519
+ },
520
+ fnSenderSecret,
521
+ peer: senderPeer,
522
+ localSpace: space,
523
+ metaspace,
524
+ conflictStrategy: SyncConflictStrategy.optimisticWithLCS,
525
+ });
526
+ await syncSaga.done;
527
+
528
+ // After sync, refresh evolved domain identity
529
+ const latestDomainAddr = await metaspace.getLatestAddr({ addr: domainAddr, space });
530
+ if (latestDomainAddr) {
531
+ const res = await metaspace.get({ addrs: [latestDomainAddr], space });
532
+ const evolved = res.ibGibs?.[0] as KeystoneIbGib_V1 | undefined;
533
+ if (evolved) {
534
+ debugState.domainI = evolved;
535
+ // If a primary space was provided, copy the evolved domain and delegate identities back there
536
+ if (primarySpace) {
537
+ await copyIdentitiesToSpace({
538
+ domainI: evolved,
539
+ metaspace,
540
+ fromSpace: space,
541
+ toSpace: primarySpace
542
+ });
543
+ }
544
+ }
545
+ }
546
+ }
547
+
548
+ export async function setupDomainAndDelegateIdentities({
549
+ masterSecret,
550
+ syncSalt,
551
+ manageSalt,
552
+ metaspace,
553
+ space,
554
+ apiBridge,
555
+ phaseText
556
+ }: {
557
+ masterSecret: string;
558
+ syncSalt: string;
559
+ manageSalt: string;
560
+ metaspace: any;
561
+ space: any;
562
+ apiBridge: SpaceGibApiBridge;
563
+ phaseText: string;
564
+ }): Promise<{ domainI: KeystoneIbGib_V1; delegateKeystone: KeystoneIbGib_V1 }> {
565
+ const keystoneService = new KeystoneService_V1();
566
+
567
+ // 1. Create Genesis Domain Keystone (I^Itjp) in local space.
568
+ // The domain identity only needs a 'manage' pool — the delegate gets 'sync'.
569
+ const domainI = await keystoneService.genesis({
570
+ masterSecret,
571
+ configs: [
572
+ getSpaceGibPoolConfig('manage', manageSalt),
573
+ ],
574
+ metaspace,
575
+ space,
576
+ frameDetails: { client: 'space-gib-web-dev', timestamp: getTimestamp() }
577
+ });
578
+
579
+ devLog(`${phaseText} Setup: ✓ Domain Keystone (I) created locally: ${getIbGibAddr({ ibGib: domainI })}`);
580
+ devLog(`${phaseText} Setup: Starting postGenesisKeystone...`);
581
+
582
+ // 2. Post Genesis Domain Keystone to receiver/server
583
+ const resGenesis = await apiBridge.postGenesisKeystone(domainI);
584
+ if (resGenesis.success) {
585
+ devLog(`${phaseText} Setup: ✓ Domain Keystone registered and stored on receiver (server).`);
586
+ } else {
587
+ devLog(`${phaseText} Setup: X postGenesisKeystone failed: ${resGenesis.message}.`);
588
+ throw new Error(`${phaseText} Setup: Server rejected genesis domain keystone: ${resGenesis.message}`);
589
+ }
590
+
591
+ // 3. Generate and register delegate
592
+ devLog(`${phaseText} Setup: Generating and registering sync delegate...`);
593
+ const { evolvedDomainI, delegateKeystone } = await createAndRegisterSyncDelegate({
594
+ domainI,
595
+ masterSecret,
596
+ syncSalt,
597
+ metaspace,
598
+ space,
599
+ apiBridge
600
+ });
601
+
602
+ debugState.domainI = evolvedDomainI;
603
+ debugState.domainIMasterSecret = masterSecret;
604
+
605
+ return { domainI: evolvedDomainI, delegateKeystone };
606
+ }
607
+
608
+ export async function verifyIdentitiesOnServer({
609
+ localI,
610
+ metaspace,
611
+ space,
612
+ apiBridge,
613
+ domainAddr
614
+ }: {
615
+ localI: KeystoneIbGib_V1;
616
+ metaspace: any;
617
+ space: any;
618
+ apiBridge: SpaceGibApiBridge;
619
+ domainAddr: string;
620
+ }): Promise<{
621
+ latestLocalIAddr: string;
622
+ latestLocalDelegateAddr: string;
623
+ sessionIdentityTjpAddr: string;
624
+ }> {
625
+ const latestLocalIAddr = await metaspace.getLatestAddr({
626
+ addr: domainAddr,
627
+ space
628
+ });
629
+ if (!latestLocalIAddr) {
630
+ throw new Error('Evolved domain keystone tip NOT found in local space!');
631
+ }
632
+ devLog(`✓ Check: Evolved Domain Keystone exists locally.`);
633
+
634
+ const {
635
+ latestLocalDelegateAddr,
636
+ sessionIdentityTjpAddr
637
+ } = await getDelegateAndSessionInfo({ localI, metaspace, space });
638
+
639
+ if (!latestLocalDelegateAddr) {
640
+ throw new Error('Evolved delegate keystone tip NOT found in local space!');
641
+ }
642
+ if (!sessionIdentityTjpAddr) {
643
+ throw new Error('Delegate sync claim does not target a session keystone.');
644
+ }
645
+ devLog(`✓ Check: Delegate Keystone and Sync Claim to Session verified locally.`);
646
+
647
+ const serverDelGetRes = await apiBridge.getIbGib(domainAddr, latestLocalDelegateAddr);
648
+ if (!serverDelGetRes.success || !serverDelGetRes.ibGib) {
649
+ throw new Error(`Evolved Delegate Keystone NOT found on server!`);
650
+ }
651
+ devLog(`✓ Check: Evolved Delegate Keystone exists on server.`);
652
+
653
+ const serverIGetRes = await apiBridge.getIbGib(domainAddr, latestLocalIAddr);
654
+ if (!serverIGetRes.success || !serverIGetRes.ibGib) {
655
+ throw new Error(`Evolved Domain Keystone NOT found on server!`);
656
+ }
657
+ devLog(`✓ Check: Evolved Domain Keystone exists on server.`);
658
+
659
+ return {
660
+ latestLocalIAddr,
661
+ latestLocalDelegateAddr,
662
+ sessionIdentityTjpAddr
663
+ };
664
+ }
665
+
666
+ export async function copyIdentitiesToSpace({
667
+ domainI,
668
+ metaspace,
669
+ fromSpace,
670
+ toSpace
671
+ }: {
672
+ domainI: KeystoneIbGib_V1;
673
+ metaspace: any;
674
+ fromSpace: any;
675
+ toSpace: any;
676
+ }): Promise<void> {
677
+ const domainAddr = getIbGibAddr({ ibGib: domainI });
678
+
679
+ // 1. Get latest Domain I and its graph, put and register
680
+ const latestDomainAddr = await metaspace.getLatestAddr({ addr: domainAddr, space: fromSpace });
681
+ if (!latestDomainAddr) { throw new Error("Could not find latest domain I address."); }
682
+
683
+ const resDomain = await metaspace.get({ addrs: [latestDomainAddr], space: fromSpace });
684
+ const latestDomainI = resDomain.ibGibs?.[0] as KeystoneIbGib_V1 | undefined;
685
+ if (!latestDomainI) { throw new Error("Could not get latest domain I."); }
686
+
687
+ const graphI = await metaspace.getDependencyGraph({ ibGibAddr: latestDomainAddr, space: fromSpace });
688
+ await metaspace.put({ ibGibs: Object.values(graphI), space: toSpace });
689
+ await metaspace.registerNewIbGib({ ibGib: latestDomainI, space: toSpace });
690
+
691
+ // 2. Find and copy delegate(s)
692
+ const syncDelegateEntry = Object.values(latestDomainI.data?.delegates ?? {}).find(
693
+ d => d.allowedVerbs?.includes(KEYSTONE_VERB_SYNC)
694
+ );
695
+
696
+ if (syncDelegateEntry) {
697
+ const delegateTjpAddr = syncDelegateEntry.delegateTjpAddr;
698
+ if (delegateTjpAddr) {
699
+ const latestDelegateAddr = await metaspace.getLatestAddr({ addr: delegateTjpAddr, space: fromSpace });
700
+ if (latestDelegateAddr) {
701
+ const resDel = await metaspace.get({ addrs: [latestDelegateAddr], space: fromSpace });
702
+ const latestDelegate = resDel.ibGibs?.[0] as KeystoneIbGib_V1 | undefined;
703
+ if (latestDelegate) {
704
+ const graphDel = await metaspace.getDependencyGraph({ ibGibAddr: latestDelegateAddr, space: fromSpace });
705
+ await metaspace.put({ ibGibs: Object.values(graphDel), space: toSpace });
706
+ await metaspace.registerNewIbGib({ ibGib: latestDelegate, space: toSpace });
707
+ }
708
+ }
709
+ }
710
+ }
711
+ }
712
+
@@ -13,7 +13,7 @@ import { SyncConflictStrategy } from '@ibgib/core-gib/dist/sync/sync-constants.m
13
13
  import { SESSION_KEYSTONE_POLICY, getSpaceGibPoolConfig } from "../../common/keystone-policies.mjs";
14
14
  import { SpaceGibApiBridge } from '../api/space-gib-api-bridge.mjs';
15
15
 
16
- import { debugState, devLog, lc, performPhaseSetup, performPhaseSync } from './common.mjs';
16
+ import { debugState, devLog, lc, performPhaseSetup, performPhaseSync, getDelegateAndSessionInfo } from './common.mjs';
17
17
 
18
18
  export function init4_1bSetupButton(): void {
19
19
  const btn = document.getElementById('btn-4-1b-setup') as HTMLButtonElement | null;
@@ -120,12 +120,27 @@ export function init4_1bCheckButton(): void {
120
120
  return;
121
121
  }
122
122
 
123
- const syncProof = localI.data?.proofs?.find(p => p.claim?.verb === 'sync');
124
- const sessionIdentityTjpAddr = syncProof?.claim?.target;
123
+ const {
124
+ latestLocalDelegateAddr,
125
+ sessionIdentityTjpAddr
126
+ } = await getDelegateAndSessionInfo({ localI, metaspace, space });
127
+
128
+ if (!latestLocalDelegateAddr) {
129
+ devLog('✗ Check: Evolved delegate keystone tip NOT found in local space!');
130
+ return;
131
+ }
125
132
  if (!sessionIdentityTjpAddr) {
126
- devLog('✗ Check: I1 sync claim does not target a session keystone.');
133
+ devLog('✗ Check: Delegate sync claim does not target a session keystone.');
134
+ return;
135
+ }
136
+ devLog(`✓ Check: Delegate Keystone and Sync Claim to Session verified locally.`);
137
+
138
+ const serverDelGetRes = await apiBridge.getIbGib(domainAddr, latestLocalDelegateAddr);
139
+ if (!serverDelGetRes.success || !serverDelGetRes.ibGib) {
140
+ devLog(`✗ Check: Evolved Delegate Keystone NOT found on server!`);
127
141
  return;
128
142
  }
143
+ devLog(`✓ Check: Evolved Delegate Keystone exists on server.`);
129
144
 
130
145
  const serverIGetRes = await apiBridge.getIbGib(domainAddr, latestLocalIAddr);
131
146
  if (!serverIGetRes.success || !serverIGetRes.ibGib) {