@ibgib/space-gib 0.0.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.
Files changed (84) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/Dockerfile +14 -0
  3. package/IMPLEMENTATION.md +484 -0
  4. package/README.md +46 -0
  5. package/dist/client/bootstrap.mjs +58 -0
  6. package/dist/client/bootstrap.mjs.map +7 -0
  7. package/dist/client/chunk-CT47Z5WU.mjs +21 -0
  8. package/dist/client/chunk-CT47Z5WU.mjs.map +7 -0
  9. package/dist/client/chunk-RHEDTRKF.mjs +235 -0
  10. package/dist/client/chunk-RHEDTRKF.mjs.map +7 -0
  11. package/dist/client/index.html +147 -0
  12. package/dist/client/index.mjs +2 -0
  13. package/dist/client/index.mjs.map +7 -0
  14. package/dist/client/script.mjs +2 -0
  15. package/dist/client/script.mjs.map +7 -0
  16. package/dist/client/style.css +605 -0
  17. package/dist/respec-gib.node.mjs +5 -0
  18. package/dist/server/server.mjs +20157 -0
  19. package/dist/server/server.mjs.map +7 -0
  20. package/generate-version-file.js +35 -0
  21. package/package.json +27 -0
  22. package/src/client/AUTO-GENERATED-version.mts +11 -0
  23. package/src/client/README.md +19 -0
  24. package/src/client/api/function-infos.web.mts +38 -0
  25. package/src/client/api/space-gib-api-bridge.mts +85 -0
  26. package/src/client/bootstrap.mts +49 -0
  27. package/src/client/components/keystone-creator/keystone-creator.css +139 -0
  28. package/src/client/components/keystone-creator/keystone-creator.html +26 -0
  29. package/src/client/components/keystone-creator/keystone-creator.mts +229 -0
  30. package/src/client/constants.mts +76 -0
  31. package/src/client/custom.d.ts +11 -0
  32. package/src/client/dev-tools.mts +540 -0
  33. package/src/client/helpers.web.mts +178 -0
  34. package/src/client/index.html +147 -0
  35. package/src/client/index.mts +59 -0
  36. package/src/client/script.mts +13 -0
  37. package/src/client/style.css +605 -0
  38. package/src/client/types.mts +85 -0
  39. package/src/client/ui/shell/space-gib-shell-constants.mts +24 -0
  40. package/src/client/ui/shell/space-gib-shell-service.mts +233 -0
  41. package/src/client/ui/shell/space-gib-shell-types.mts +5 -0
  42. package/src/client/witness/app/space-gib/space-gib-app-v1.mts +160 -0
  43. package/src/client/witness/app/space-gib/space-gib-constants.mts +38 -0
  44. package/src/client/witness/app/space-gib/space-gib-helper.mts +72 -0
  45. package/src/client/witness/app/space-gib/space-gib-types.mts +47 -0
  46. package/src/common/keystone-policies.mts +159 -0
  47. package/src/respec-gib.node.mts +6 -0
  48. package/src/server/README.md +18 -0
  49. package/src/server/bootstrap-helper.mts +141 -0
  50. package/src/server/bootstrap-helper.respec.mts +100 -0
  51. package/src/server/metaspace-nodeindexedspace/metaspace-nodeindexedspace.mts +85 -0
  52. package/src/server/path-constants.mts +89 -0
  53. package/src/server/path-helper.mts +101 -0
  54. package/src/server/path-helper.respec.mts +94 -0
  55. package/src/server/serve-gib/CHANGELOG.md +29 -0
  56. package/src/server/serve-gib/README.md +34 -0
  57. package/src/server/serve-gib/constants.mts +1 -0
  58. package/src/server/serve-gib/handlers/api/debug/ws-echo.handler.mts +104 -0
  59. package/src/server/serve-gib/handlers/api/health.handler.mts +23 -0
  60. package/src/server/serve-gib/handlers/api/health.respec.mts +51 -0
  61. package/src/server/serve-gib/handlers/api/ibgib/ibgib-handler-types.mts +49 -0
  62. package/src/server/serve-gib/handlers/api/ibgib/ibgib.handler.mts +176 -0
  63. package/src/server/serve-gib/handlers/api/keystone/keystone-evolve.handler.mts +261 -0
  64. package/src/server/serve-gib/handlers/api/keystone/keystone-genesis.handler.mts +146 -0
  65. package/src/server/serve-gib/handlers/api/keystone/keystone-get.handler.mts +198 -0
  66. package/src/server/serve-gib/handlers/api/keystone/keystone-get.respec.mts +107 -0
  67. package/src/server/serve-gib/handlers/api/keystone/keystone-handler-types.mts +29 -0
  68. package/src/server/serve-gib/handlers/api/keystone/keystone-post.handler.mts +70 -0
  69. package/src/server/serve-gib/handlers/api/keystone/keystone-post.respec.mts +130 -0
  70. package/src/server/serve-gib/handlers/error-handler.mts +36 -0
  71. package/src/server/serve-gib/handlers/handler-base.mts +383 -0
  72. package/src/server/serve-gib/handlers/static-handler.mts +82 -0
  73. package/src/server/serve-gib/handlers/ws/sync-upgrade.handler.mts +498 -0
  74. package/src/server/serve-gib/handlers/ws/ws-helper.mts +111 -0
  75. package/src/server/serve-gib/handlers/ws/ws-types.mts +53 -0
  76. package/src/server/serve-gib/serve-gib-helpers.mts +32 -0
  77. package/src/server/serve-gib/serve-gib-v1.mts +172 -0
  78. package/src/server/serve-gib/serve-gib.respec.mts +90 -0
  79. package/src/server/serve-gib/types.mts +102 -0
  80. package/src/server/server-constants.mts +2 -0
  81. package/src/server/server.mts +96 -0
  82. package/tsconfig.json +29 -0
  83. package/tsconfig.server.json +29 -0
  84. package/tsconfig.test.json +27 -0
@@ -0,0 +1,76 @@
1
+ import { tagTextToIb } from "@ibgib/core-gib/dist/common/other/ibgib-helper.mjs";
2
+ import { ZERO_SPACE_ID } from "@ibgib/core-gib/dist/witness/space/space-constants.mjs";
3
+ import { IbGibAmbientContextConfig } from "@ibgib/web-gib/dist/app-bootstrap/types.mjs";
4
+
5
+ // ---------------------------------------------------------------------------
6
+ // Logging / debug
7
+ // ---------------------------------------------------------------------------
8
+
9
+ export const GLOBAL_LOG_A_LOT = false;
10
+
11
+ // ---------------------------------------------------------------------------
12
+ // Storage — unique per app instance
13
+ // ---------------------------------------------------------------------------
14
+
15
+ export const SPACE_GIB_DB_NAME = 'space_gib_db';
16
+ export const SPACE_GIB_STORE_NAME = 'space_gib_store';
17
+ export const SPACE_GIB_API_KEY_NAME = 'space_gib_api_key';
18
+
19
+ export const SPACE_GIB_INDEXEDDB_KEY_LOCAL_SPACE_NAME = 'local_space_name';
20
+ /**
21
+ * Prefix for the automatically generated local space name on first visit.
22
+ * A random suffix is appended at runtime.
23
+ */
24
+ export const SPACE_GIB_INDEXEDDB_LOCAL_SPACE_NAME_PREFIX = 'space_gib_';
25
+
26
+ // ---------------------------------------------------------------------------
27
+ // App identity discriminator
28
+ // ---------------------------------------------------------------------------
29
+
30
+ export const HTML_META_APP_ID_NAME = "ibgib-app-id";
31
+ export const HTML_META_APP_ID_CONTENT = "0c45ac92e668410190b115512d5e2d51";
32
+
33
+ // ---------------------------------------------------------------------------
34
+ // Agent / AI (optional)
35
+ // ---------------------------------------------------------------------------
36
+
37
+ export const GEMINI_API_KEY_REGEXP = /^[a-zA-Z0-9\-_]{32,64}$/;
38
+ export const CONFIG_OPTION_GEMINI_API_KEY_LOCATION_HELP = `Gemini API Key config option (⚙️ icon)`;
39
+
40
+ // ---------------------------------------------------------------------------
41
+ // Tags
42
+ // ---------------------------------------------------------------------------
43
+
44
+ export const TAG_AGENT_TEXT = "agent";
45
+ export const TAG_AGENT_ICON = "body-outline";
46
+ export const TAG_AGENT_DESCRIPTION =
47
+ "This tag tracks the active agents for the current local user space.";
48
+ export const TAG_AGENT_IB = tagTextToIb(TAG_AGENT_TEXT);
49
+
50
+ // ---------------------------------------------------------------------------
51
+ // Colors
52
+ // ---------------------------------------------------------------------------
53
+
54
+ export const DEFAULT_IBGIB_COLOR = '#78f87e88';
55
+ export const DEFAULT_IBGIB_TRANSLUCENT = '#78f87e10';
56
+ export const DEFAULT_IBGIB_COLOR_CONTRAST = '#ffffff';
57
+ export const DEFAULT_TJP_COLOR = '#78f87e88';
58
+ export const DEFAULT_TJP_COLOR_TRANSLUCENT = '#78f87e10';
59
+ export const DEFAULT_TJP_COLOR_CONTRAST = '#ffffff';
60
+
61
+ // ---------------------------------------------------------------------------
62
+ // Server API base URL (relative — same origin serves both API and SPA)
63
+ // ---------------------------------------------------------------------------
64
+
65
+ export const API_BASE_URL = '/api';
66
+
67
+ // ---------------------------------------------------------------------------
68
+ // Ambient context config
69
+ // ---------------------------------------------------------------------------
70
+
71
+ export const APP_CONFIG: IbGibAmbientContextConfig = {
72
+ dbName: SPACE_GIB_DB_NAME,
73
+ storeName: SPACE_GIB_STORE_NAME,
74
+ additionalStoreNames: [ZERO_SPACE_ID],
75
+ apiKeyName: SPACE_GIB_API_KEY_NAME,
76
+ };
@@ -0,0 +1,11 @@
1
+ // Tell TypeScript that importing a .html file will return a string.
2
+ declare module '*.html' {
3
+ const value: string;
4
+ export default value;
5
+ }
6
+
7
+ // Tell TypeScript that importing a .css file will return a string.
8
+ declare module '*.css' {
9
+ const value: string;
10
+ export default value;
11
+ }
@@ -0,0 +1,540 @@
1
+ /**
2
+ * @module client/dev-tools
3
+ *
4
+ * Wires up the Dev Tools panel button handlers for manual testing during
5
+ * development. Import this from index.mts inside the DOMContentLoaded handler.
6
+ *
7
+ * ## intent
8
+ *
9
+ * Remove or gate this module once the real sync WebSocket endpoint is confirmed
10
+ * working end-to-end.
11
+ */
12
+
13
+ import { extractErrorMsg, getTimestamp, getUUID, HashAlgorithm } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
14
+ import { Factory_V1 as factory } from '@ibgib/ts-gib/dist/V1/factory.mjs';
15
+ import { ROOT } from '@ibgib/ts-gib/dist/V1/constants.mjs';
16
+ import { getIbGibAddr, } from '@ibgib/ts-gib/dist/helper.mjs';
17
+ import { IbGib_V1 } from '@ibgib/ts-gib/dist/V1/types.mjs';
18
+ import { KeystoneService_V1 } from '@ibgib/core-gib/dist/keystone/keystone-service-v1.mjs';
19
+ import { KeystoneChallengeType, KeystoneIbGib_V1, KeystoneReplenishStrategy } from '@ibgib/core-gib/dist/keystone/keystone-types.mjs';
20
+ import { createManagePoolConfig } from '@ibgib/core-gib/dist/keystone/keystone-config-builder.mjs';
21
+ import { getGlobalMetaspace_waitIfNeeded } from "@ibgib/web-gib/dist/helpers.mjs";
22
+
23
+ import { GLOBAL_LOG_A_LOT } from './constants.mjs';
24
+ import { SESSION_KEYSTONE_POLICY, getHandshakeChallenge } from "../common/keystone-policies.mjs";
25
+ import { KeystoneStrategyFactory } from '@ibgib/core-gib/dist/keystone/strategy/keystone-strategy-factory.mjs';
26
+ import { SpaceGibApiBridge } from './api/space-gib-api-bridge.mjs';
27
+ import { KeystoneVerb } from '@ibgib/core-gib/dist/keystone/keystone-constants.mjs';
28
+
29
+ const logalot = GLOBAL_LOG_A_LOT
30
+ const lc = '[dev-tools]';
31
+
32
+ interface DebugState {
33
+ domainI?: KeystoneIbGib_V1;
34
+ domainIMasterSecret?: string;
35
+ targetX?: IbGib_V1;
36
+ sessionS?: KeystoneIbGib_V1;
37
+ sessionSecret?: string;
38
+ }
39
+
40
+ export const debugState: DebugState = {};
41
+ (window as any).ibgibDebugState = debugState;
42
+
43
+ // ---------------------------------------------------------------------------
44
+ // Dev panel log helper
45
+ // ---------------------------------------------------------------------------
46
+
47
+ export function devLog(msg: string): void {
48
+ const logEl = document.getElementById('dev-panel-log') as HTMLPreElement | null;
49
+ if (!logEl) { return; }
50
+ const timestamp = new Date().toISOString().slice(11, 23); // HH:MM:SS.mmm
51
+ logEl.textContent = `[${timestamp}] ${msg}\n` + logEl.textContent;
52
+ }
53
+
54
+ // ---------------------------------------------------------------------------
55
+ // Test WebSocket button
56
+ // ---------------------------------------------------------------------------
57
+
58
+ let _ws: WebSocket | null = null;
59
+
60
+ function initWsTestButton(): void {
61
+ const lc_fn = `${lc}[initWsTestButton]`;
62
+ const btn = document.getElementById('btn-test-ws') as HTMLButtonElement | null;
63
+ if (!btn) {
64
+ console.warn(`${lc_fn} btn-test-ws not found — skipping`);
65
+ return;
66
+ }
67
+
68
+ btn.addEventListener('click', () => {
69
+ try {
70
+ if (_ws && _ws.readyState === WebSocket.OPEN) {
71
+ // Already connected — send a test ping instead
72
+ _ws.send(JSON.stringify({ ping: 'hello from client', ts: Date.now() }));
73
+ devLog('→ sent ping on existing connection');
74
+ return;
75
+ }
76
+
77
+ // Build the WebSocket URL from the current origin (wss:// if https://)
78
+ const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
79
+ const wsUrl = `${protocol}//${location.host}/api/debug/ws-echo`;
80
+ devLog(`→ connecting to ${wsUrl}`);
81
+
82
+ _ws = new WebSocket(wsUrl);
83
+
84
+ _ws.addEventListener('open', () => {
85
+ devLog('✓ WebSocket connected');
86
+ btn.textContent = 'Send WS Ping';
87
+ });
88
+
89
+ _ws.addEventListener('message', (ev) => {
90
+ devLog(`← ${ev.data}`);
91
+ });
92
+
93
+ _ws.addEventListener('close', (ev) => {
94
+ devLog(`✗ WebSocket closed (code ${ev.code})`);
95
+ btn.textContent = 'Test WebSocket';
96
+ _ws = null;
97
+ });
98
+
99
+ _ws.addEventListener('error', (ev) => {
100
+ devLog(`✗ WebSocket error — check console`);
101
+ console.error(`${lc_fn} WebSocket error`, ev);
102
+ });
103
+
104
+ } catch (error) {
105
+ devLog(`✗ ${extractErrorMsg(error)}`);
106
+ console.error(`${lc_fn} ${extractErrorMsg(error)}`);
107
+ }
108
+ });
109
+ }
110
+
111
+ // ---------------------------------------------------------------------------
112
+ // Print Dev State button
113
+ // ---------------------------------------------------------------------------
114
+
115
+ function initPrintStateButton(): void {
116
+ const btn = document.getElementById('btn-print-dev-state') as HTMLButtonElement | null;
117
+ if (!btn) return;
118
+ btn.addEventListener('click', () => {
119
+ devLog(`Printing debug state to console...`);
120
+ console.dir(debugState);
121
+ });
122
+ }
123
+
124
+ // ---------------------------------------------------------------------------
125
+ // Create Domain Keystone (I) button
126
+ // ---------------------------------------------------------------------------
127
+
128
+ function initCreateDomainKeystoneButton(): void {
129
+ const lc_fn = `${lc}[initCreateDomainKeystoneButton]`;
130
+ const btn = document.getElementById('btn-create-domain-keystone') as HTMLButtonElement | null;
131
+ if (!btn) return;
132
+
133
+ btn.addEventListener('click', async () => {
134
+ try {
135
+ btn.disabled = true;
136
+ devLog('Generating dummy Domain Keystone (I)...');
137
+
138
+ const metaspace = await getGlobalMetaspace_waitIfNeeded();
139
+ const space = await metaspace.getLocalUserSpace({});
140
+ if (!space) { throw new Error("No default space found in metaspace."); }
141
+
142
+ const masterSecret = 'password'; // Hardcoded for debug as requested
143
+ const keystoneService = new KeystoneService_V1();
144
+
145
+ const domainI = await keystoneService.genesis({
146
+ masterSecret,
147
+ configs: [
148
+ createManagePoolConfig({
149
+ id: 'default',
150
+ salt: await getUUID(),
151
+ replenishStrategy: KeystoneReplenishStrategy.topUp,
152
+ }),
153
+ ],
154
+ metaspace,
155
+ space,
156
+ frameDetails: { client: 'space-gib-web-dev', timestamp: getTimestamp() }
157
+ });
158
+
159
+ const iAddr = getIbGibAddr({ ibGib: domainI });
160
+ devLog(`✓ Local genesis complete: ${iAddr}`);
161
+
162
+ debugState.domainI = domainI;
163
+ debugState.domainIMasterSecret = masterSecret;
164
+
165
+ // Post to server
166
+ devLog(`Posting genesis keystone to server...`);
167
+ const apiBridge = new SpaceGibApiBridge();
168
+ const resSync = await apiBridge.postGenesisKeystone(domainI);
169
+ if (!resSync.success) {
170
+ devLog(`✗ Server rejected genesis keystone: ${resSync.message}`);
171
+ btn.disabled = false;
172
+ return;
173
+ }
174
+
175
+ devLog(`✓ Server accepted Domain Keystone (I)`);
176
+ btn.textContent = '✓ Domain Keystone Created';
177
+
178
+ // Enable next step
179
+ const nextBtn = document.getElementById('btn-create-test-ibgib') as HTMLButtonElement;
180
+ if (nextBtn) nextBtn.disabled = false;
181
+
182
+ } catch (error) {
183
+ devLog(`✗ ${extractErrorMsg(error)}`);
184
+ console.error(`${lc_fn} ${extractErrorMsg(error)}`);
185
+ btn.disabled = false;
186
+ }
187
+ });
188
+ }
189
+
190
+ // ---------------------------------------------------------------------------
191
+ // Create Test IbGib button
192
+ // ---------------------------------------------------------------------------
193
+
194
+ function initCreateTestIbGibButton(): void {
195
+ const lc_fn = `${lc}[initCreateTestIbGibButton]`;
196
+ const btn = document.getElementById('btn-create-test-ibgib') as HTMLButtonElement | null;
197
+ if (!btn) {
198
+ console.warn(`${lc_fn} btn-create-test-ibgib not found — skipping`);
199
+ return;
200
+ }
201
+
202
+ btn.addEventListener('click', async () => {
203
+ try {
204
+ devLog('Generating dummy X^Xtjp ibgib...');
205
+ const resX = await factory.firstGen({
206
+ parentIbGib: ROOT,
207
+ ib: 'test data',
208
+ data: { hello: 'world', random: Math.random() },
209
+ dna: true,
210
+ nCounter: true,
211
+ tjp: { uuid: true, timestamp: true }
212
+ });
213
+ const xIbGib = resX.newIbGib;
214
+ const xAddr = getIbGibAddr({ ibGib: xIbGib });
215
+
216
+ debugState.targetX = xIbGib;
217
+ devLog(`✓ Created X: ${xAddr}`);
218
+ btn.textContent = '✓ Test IbGib (X) Created';
219
+
220
+ // Enable next step
221
+ const nextBtn = document.getElementById('btn-create-session-keystone') as HTMLButtonElement;
222
+ if (nextBtn) nextBtn.disabled = false;
223
+ } catch (error) {
224
+ devLog(`✗ ${extractErrorMsg(error)}`);
225
+ console.error(`${lc_fn} ${extractErrorMsg(error)}`);
226
+ }
227
+ });
228
+ }
229
+
230
+ // ---------------------------------------------------------------------------
231
+ // Create Session Keystone (S^Stjp) button
232
+ // ---------------------------------------------------------------------------
233
+
234
+ function initCreateSessionKeystoneButton(): void {
235
+ const lc_fn = `${lc}[initCreateSessionKeystoneButton]`;
236
+ const btn = document.getElementById('btn-create-session-keystone') as HTMLButtonElement | null;
237
+ if (!btn) {
238
+ console.warn(`${lc_fn} btn-create-session-keystone not found — skipping`);
239
+ return;
240
+ }
241
+
242
+ btn.addEventListener('click', async () => {
243
+ try {
244
+ devLog('Generating Session Keystone (S^Stjp)...');
245
+
246
+ const domainI = debugState.domainI;
247
+ const targetX = debugState.targetX;
248
+
249
+ if (!domainI) { devLog('⚠ No domain keystone (I) found in state. Please Create Domain Keystone first.'); return; }
250
+ if (!targetX) { devLog('⚠ No target ibgib (X) found in state. Please Create Test IbGib first.'); return; }
251
+
252
+ const metaspace = await getGlobalMetaspace_waitIfNeeded();
253
+ const space = await metaspace.getLocalUserSpace({});
254
+ if (!space) { throw new Error("No default space found in metaspace."); }
255
+
256
+ const sessionSecret = 'ephemeral-' + Math.random().toString();
257
+ const keystoneService = new KeystoneService_V1();
258
+
259
+ console.warn(`${lc}[NAG] placeholder genesis keystone config right now (W: 69b108687cf8bec0f9a8e148bcca8d26)`);
260
+ const sIbGib = await keystoneService.genesis({
261
+ masterSecret: sessionSecret,
262
+ configs: [
263
+ {
264
+ id: SESSION_KEYSTONE_POLICY.DEFAULT_POOL.ID,
265
+ salt: await getUUID(),
266
+ type: SESSION_KEYSTONE_POLICY.COMMON.TYPE,
267
+ algo: SESSION_KEYSTONE_POLICY.COMMON.ALGO,
268
+ rounds: SESSION_KEYSTONE_POLICY.COMMON.ROUNDS,
269
+ behavior: {
270
+ size: SESSION_KEYSTONE_POLICY.DEFAULT_POOL.SIZE,
271
+ replenish: SESSION_KEYSTONE_POLICY.COMMON.REPLENISH,
272
+ selectSequentially: SESSION_KEYSTONE_POLICY.DEFAULT_POOL.SELECT_SEQUENTIALLY,
273
+ selectRandomly: SESSION_KEYSTONE_POLICY.DEFAULT_POOL.SELECT_RANDOMLY,
274
+ targetBindingChars: SESSION_KEYSTONE_POLICY.DEFAULT_POOL.TARGET_BINDING_CHARS,
275
+ },
276
+ allowedVerbs: [],
277
+ },
278
+ {
279
+ id: SESSION_KEYSTONE_POLICY.HANDSHAKE_POOL.ID,
280
+ salt: await getUUID(),
281
+ type: SESSION_KEYSTONE_POLICY.COMMON.TYPE,
282
+ algo: SESSION_KEYSTONE_POLICY.COMMON.ALGO,
283
+ rounds: SESSION_KEYSTONE_POLICY.COMMON.ROUNDS,
284
+ behavior: {
285
+ size: SESSION_KEYSTONE_POLICY.HANDSHAKE_POOL.SIZE,
286
+ replenish: SESSION_KEYSTONE_POLICY.COMMON.REPLENISH,
287
+ selectSequentially: SESSION_KEYSTONE_POLICY.HANDSHAKE_POOL.SELECT_SEQUENTIALLY,
288
+ selectRandomly: SESSION_KEYSTONE_POLICY.HANDSHAKE_POOL.SELECT_RANDOMLY,
289
+ targetBindingChars: SESSION_KEYSTONE_POLICY.HANDSHAKE_POOL.TARGET_BINDING_CHARS,
290
+ },
291
+ allowedVerbs: [SESSION_KEYSTONE_POLICY.HANDSHAKE_POOL.VERB],
292
+ }
293
+ ],
294
+ metaspace,
295
+ space,
296
+ frameDetails: {
297
+ client: 'space-gib-web-session',
298
+ target_I: domainI ? getIbGibAddr({ ibGib: domainI }) : undefined,
299
+ target_X: targetX ? getIbGibAddr({ ibGib: targetX }) : undefined,
300
+ }
301
+ });
302
+
303
+ const sAddr = getIbGibAddr({ ibGib: sIbGib });
304
+ debugState.sessionS = sIbGib;
305
+ debugState.sessionSecret = sessionSecret;
306
+
307
+ devLog(`✓ Created Session Keystone: ${sAddr}`);
308
+ btn.textContent = '✓ Session Keystone Created';
309
+
310
+ // Enable next step
311
+ const nextBtn = document.getElementById('btn-evolve-domain-keystone') as HTMLButtonElement;
312
+ if (nextBtn) nextBtn.disabled = false;
313
+ } catch (error) {
314
+ devLog(`✗ ${extractErrorMsg(error)}`);
315
+ console.error(`${lc_fn} ${extractErrorMsg(error)}`);
316
+ }
317
+ });
318
+ }
319
+
320
+ // ---------------------------------------------------------------------------
321
+ // Evolve Domain Keystone (I^I1) button
322
+ // ---------------------------------------------------------------------------
323
+
324
+ function initEvolveDomainKeystoneButton(): void {
325
+ const lc_fn = `${lc}[initEvolveDomainKeystoneButton]`;
326
+ const btn = document.getElementById('btn-evolve-domain-keystone') as HTMLButtonElement | null;
327
+ if (!btn) {
328
+ console.warn(`${lc_fn} btn-evolve-domain-keystone not found — skipping`);
329
+ return;
330
+ }
331
+
332
+ btn.addEventListener('click', async () => {
333
+ try {
334
+ btn.disabled = true;
335
+ devLog('Evolving Domain Keystone (I^I1) with sync claim...');
336
+
337
+ const domainI = debugState.domainI;
338
+ const masterSecret = debugState.domainIMasterSecret;
339
+ const sessionS = debugState.sessionS;
340
+
341
+ if (!domainI || !masterSecret) { devLog('⚠ No domain keystone (I) found. Please Create Domain Keystone first.'); return; }
342
+ if (!sessionS) { devLog('⚠ No session keystone (S) found. Please Create Session Keystone first.'); return; }
343
+
344
+ const metaspace = await getGlobalMetaspace_waitIfNeeded();
345
+ const space = await metaspace.getLocalUserSpace({});
346
+ if (!space) { throw new Error("No default space found in metaspace."); }
347
+
348
+ const keystoneService = new KeystoneService_V1();
349
+ const sAddr = getIbGibAddr({ ibGib: sessionS });
350
+
351
+ devLog('Solving challenges to authorize sync...');
352
+ const evolvedI = await keystoneService.sign({
353
+ latestKeystone: domainI,
354
+ masterSecret,
355
+ claim: {
356
+ target: sAddr,
357
+ verb: 'sync'
358
+ },
359
+ metaspace,
360
+ space
361
+ });
362
+
363
+ const evolvedAddr = getIbGibAddr({ ibGib: evolvedI });
364
+ devLog(`✓ Local evolution complete: ${evolvedAddr}`);
365
+
366
+ // Update state with new tip
367
+ debugState.domainI = evolvedI;
368
+
369
+ devLog('Posting evolution to server...');
370
+ const apiBridge = new SpaceGibApiBridge();
371
+ // We pass the domain address (from the original TJP) and the session keystone
372
+ const domainAddr = getIbGibAddr({ ibGib: domainI });
373
+ const resSync = await apiBridge.putEvolveKeystone(domainAddr, evolvedI, [sessionS]);
374
+
375
+ if (!resSync.success) {
376
+ devLog(`✗ Server rejected evolution: ${resSync.message}`);
377
+ btn.disabled = false;
378
+ return;
379
+ }
380
+
381
+ devLog('✓ Server accepted Domain Keystone evolution!');
382
+ btn.textContent = '✓ Domain Keystone Evolved';
383
+
384
+ // Enable next step
385
+ const nextBtn = document.getElementById('btn-perform-sync') as HTMLButtonElement;
386
+ if (nextBtn) nextBtn.disabled = false;
387
+
388
+ } catch (error) {
389
+ devLog(`✗ ${extractErrorMsg(error)}`);
390
+ console.error(`${lc_fn} ${extractErrorMsg(error)}`);
391
+ btn.disabled = false;
392
+ }
393
+ });
394
+ }
395
+
396
+ // ---------------------------------------------------------------------------
397
+ // Perform Sync Handshake (S) button
398
+ // ---------------------------------------------------------------------------
399
+
400
+ function initPerformSyncButton(): void {
401
+ const lc_fn = `${lc}[initPerformSyncButton]`;
402
+ const btn = document.getElementById('btn-perform-sync') as HTMLButtonElement | null;
403
+ if (!btn) return;
404
+
405
+ btn.addEventListener('click', async () => {
406
+ try {
407
+ debugger; // walk through sync
408
+ btn.disabled = true;
409
+ devLog('Initiating Sync Handshake via WebSocket...');
410
+
411
+ const sessionS = debugState.sessionS;
412
+ const sessionSecret = debugState.sessionSecret;
413
+ const domainI = debugState.domainI;
414
+
415
+ if (!sessionS || !sessionSecret || !domainI) {
416
+ devLog('⚠ Incomplete state. Please complete steps 1-4.');
417
+ btn.disabled = false;
418
+ return;
419
+ }
420
+
421
+ const metaspace = await getGlobalMetaspace_waitIfNeeded();
422
+ const space = await metaspace.getLocalUserSpace({});
423
+ if (!space) { throw new Error("No space."); }
424
+
425
+ devLog('Pre-solving upfront picket-fence challenge...');
426
+ const handshakePool = (sessionS.data?.challengePools ?? []).find(p => p.id === SESSION_KEYSTONE_POLICY.HANDSHAKE_POOL.ID);
427
+ if (!handshakePool) {
428
+ throw new Error(`Session keystone missing "${SESSION_KEYSTONE_POLICY.HANDSHAKE_POOL.ID}" pool`);
429
+ }
430
+
431
+ const { challengeId } = getHandshakeChallenge(sessionS);
432
+ const strategy = KeystoneStrategyFactory.create({ config: handshakePool.config });
433
+ const poolSecret = await strategy.derivePoolSecret({ masterSecret: sessionSecret });
434
+ const solution = await strategy.generateSolution({
435
+ poolSecret,
436
+ poolId: SESSION_KEYSTONE_POLICY.HANDSHAKE_POOL.ID,
437
+ challengeId
438
+ });
439
+
440
+ const sAddr = getIbGibAddr({ ibGib: sessionS });
441
+ const domainAddr = getIbGibAddr({ ibGib: domainI });
442
+ const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
443
+ const wsUrl = `${protocol}//${location.host}/api/sync/ws/${encodeURIComponent(domainAddr)}` +
444
+ `?sAddr=${encodeURIComponent(sAddr)}` +
445
+ `&solution=${solution.value}`;
446
+
447
+ devLog(`→ connecting to ${wsUrl}`);
448
+ const ws = new WebSocket(wsUrl);
449
+
450
+ ws.addEventListener('open', () => {
451
+ devLog('✓ WebSocket connected. Waiting for server challenge...');
452
+ });
453
+
454
+ ws.addEventListener('message', async (ev) => {
455
+ try {
456
+ const msg = JSON.parse(ev.data);
457
+ devLog(`← received: ${msg.type ?? 'message'}`);
458
+
459
+ if (msg.type === 'auth-challenge-init') {
460
+ devLog('→ sending auth-init with sAddr...');
461
+ ws.send(JSON.stringify({
462
+ type: 'auth-init',
463
+ sAddr: getIbGibAddr({ ibGib: sessionS })
464
+ }));
465
+ } else if (msg.type === 'auth-challenge') {
466
+ const { challengeUuid, demandedIds } = msg;
467
+ devLog(`→ server demanded ${demandedIds.length} ids. Signing handshake proof...`);
468
+
469
+ const keystoneService = new KeystoneService_V1();
470
+ const proofFrame = await keystoneService.sign({
471
+ latestKeystone: sessionS,
472
+ masterSecret: sessionSecret,
473
+ poolId: 'handshake',
474
+ requiredChallengeIds: demandedIds,
475
+ claim: {
476
+ verb: 'handshake',
477
+ target: challengeUuid
478
+ },
479
+ metaspace,
480
+ space
481
+ });
482
+
483
+ devLog('→ sending auth-proof...');
484
+ ws.send(JSON.stringify({
485
+ type: 'auth-proof',
486
+ proofFrame
487
+ }));
488
+ } else if (msg.type === 'auth-ok') {
489
+ devLog('✓ Handshake SUCCESS! Sync session authorized.');
490
+ btn.textContent = '✓ Sync Authorized';
491
+ } else if (msg.type === 'auth-fail') {
492
+ devLog(`✗ Handshake FAILED: ${msg.message}`);
493
+ btn.disabled = false;
494
+ }
495
+ } catch (error) {
496
+ devLog(`✗ Message handler error: ${extractErrorMsg(error)}`);
497
+ console.error(`${lc_fn} Message handler error:`, error);
498
+ btn.disabled = false;
499
+ }
500
+ });
501
+
502
+ ws.addEventListener('close', (ev) => {
503
+ devLog(`✗ WebSocket closed (code ${ev.code})`);
504
+ btn.disabled = false;
505
+ });
506
+
507
+ ws.addEventListener('error', () => {
508
+ devLog(`✗ WebSocket error — check server`);
509
+ btn.disabled = false;
510
+ });
511
+
512
+ } catch (error) {
513
+ devLog(`✗ ${extractErrorMsg(error)}`);
514
+ console.error(`${lc_fn} ${extractErrorMsg(error)}`);
515
+ btn.disabled = false;
516
+ }
517
+ });
518
+ }
519
+
520
+ // ---------------------------------------------------------------------------
521
+ // Dev Tools UI Init
522
+ // ---------------------------------------------------------------------------
523
+
524
+ /**
525
+ * Call once inside DOMContentLoaded to wire up all dev-tool buttons.
526
+ */
527
+ export function initDevTools(): void {
528
+ const lc_fn = `${lc}[initDevTools]`;
529
+ try {
530
+ initPrintStateButton();
531
+ initCreateDomainKeystoneButton();
532
+ initWsTestButton();
533
+ initCreateTestIbGibButton();
534
+ initCreateSessionKeystoneButton();
535
+ initEvolveDomainKeystoneButton();
536
+ initPerformSyncButton();
537
+ } catch (error) {
538
+ console.error(`${lc_fn} ${extractErrorMsg(error)}`);
539
+ }
540
+ }