@ibgib/space-gib 0.0.2 → 0.0.4

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 (67) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/IMPLEMENTATION.md +9 -13
  3. package/dist/client/bootstrap.mjs +1 -1
  4. package/dist/client/bootstrap.mjs.map +1 -1
  5. package/dist/client/chunk-NCXKCVYS.mjs +42 -0
  6. package/dist/client/chunk-NCXKCVYS.mjs.map +7 -0
  7. package/dist/client/chunk-ZUEU37Z5.mjs +1920 -0
  8. package/dist/client/chunk-ZUEU37Z5.mjs.map +7 -0
  9. package/dist/client/index.html +108 -8
  10. package/dist/client/index.mjs +1 -1
  11. package/dist/client/script.mjs +1 -1
  12. package/dist/client/style.css +466 -61
  13. package/dist/respec-gib.node.mjs +5 -0
  14. package/dist/server/server.mjs +815 -316
  15. package/dist/server/server.mjs.map +4 -4
  16. package/package.json +6 -6
  17. package/src/client/AUTO-GENERATED-version.mts +1 -1
  18. package/src/client/api/space-gib-api-bridge.mts +35 -0
  19. package/src/client/components/identity-header/IMPLEMENTATION.md +45 -0
  20. package/src/client/components/identity-header/identity-header.css +74 -0
  21. package/src/client/components/identity-header/identity-header.html +10 -0
  22. package/src/client/components/identity-header/identity-header.mts +361 -0
  23. package/src/client/components/identity-manager/IMPLEMENTATION.md +100 -0
  24. package/src/client/components/identity-manager/identity-manager.css +467 -0
  25. package/src/client/components/identity-manager/identity-manager.html +113 -0
  26. package/src/client/components/identity-manager/identity-manager.mts +767 -0
  27. package/src/client/components/keystone-creator/keystone-creator.css +2 -76
  28. package/src/client/components/keystone-creator/keystone-creator.html +41 -26
  29. package/src/client/components/keystone-creator/keystone-creator.mts +178 -41
  30. package/src/client/dev-tools/base-tools.mts +252 -0
  31. package/src/client/dev-tools/common.mts +217 -0
  32. package/src/client/dev-tools/phase-1.mts +156 -0
  33. package/src/client/dev-tools/phase-2.mts +143 -0
  34. package/src/client/dev-tools/phase-3.mts +189 -0
  35. package/src/client/dev-tools/phase-4-1.mts +197 -0
  36. package/src/client/dev-tools/phase-4-10.mts +884 -0
  37. package/src/client/dev-tools/phase-4-2.mts +388 -0
  38. package/src/client/dev-tools/phase-4-3.mts +391 -0
  39. package/src/client/dev-tools/phase-4-4.mts +374 -0
  40. package/src/client/dev-tools/phase-4-5.mts +376 -0
  41. package/src/client/dev-tools/phase-4-6.mts +273 -0
  42. package/src/client/dev-tools/phase-4-7.mts +399 -0
  43. package/src/client/dev-tools/phase-4-8.mts +430 -0
  44. package/src/client/dev-tools/phase-4-9.mts +398 -0
  45. package/src/client/dev-tools/phase-4.mts +1302 -0
  46. package/src/client/dev-tools.mts +55 -1096
  47. package/src/client/index.html +108 -8
  48. package/src/client/style.css +466 -61
  49. package/src/client/ui/shell/space-gib-shell-constants.mts +0 -2
  50. package/src/client/ui/shell/space-gib-shell-service.mts +82 -10
  51. package/src/common/common-constants.mts +0 -0
  52. package/src/common/keystone-policies.json +40 -43
  53. package/src/common/keystone-policies.mts +4 -6
  54. package/src/server/path-helper.respec.mts +99 -94
  55. package/src/server/serve-gib/README.md +9 -0
  56. package/src/server/serve-gib/handlers/api/keystone/keystone-evolve.handler.mts +1 -1
  57. package/src/server/serve-gib/handlers/api/keystone/keystone-genesis.handler.mts +1 -1
  58. package/src/server/serve-gib/handlers/api/keystone/keystone-get.respec.mts +4 -4
  59. package/src/server/serve-gib/handlers/api/keystone/keystone-post.handler.mts +1 -1
  60. package/src/server/serve-gib/handlers/ws/sync-upgrade-handler-base.mts +37 -5
  61. package/src/server/serve-gib/handlers/ws/ws-helper.mts +73 -45
  62. package/dist/client/chunk-BL2SGXS4.mjs +0 -18994
  63. package/dist/client/chunk-RDTAT5G4.mjs +0 -235
  64. package/dist/client/chunk-RDTAT5G4.mjs.map +0 -7
  65. package/dist/client/chunk-RE7XSMHH.mjs +0 -31
  66. package/dist/client/chunk-RE7XSMHH.mjs.map +0 -7
  67. package/dist/client/chunk-YUSGN3J4.mjs +0 -23119
@@ -20,5 +20,3 @@ export const ID_BTN_CREATE_KEYSTONE = 'btn-create-keystone';
20
20
  // Other
21
21
  export const ID_CENTER_PANEL_CONTENT = 'center-panel-content';
22
22
 
23
- // Events
24
- export const EVENT_SHELL_READY = 'space-gib-shell-ready';
@@ -9,11 +9,14 @@ import {
9
9
  ID_APP_ROOT, ID_HEADER_PANEL, ID_LEFT_PANEL, ID_CENTER_PANEL,
10
10
  ID_RIGHT_PANEL, ID_FOOTER_PANEL, ID_LEFT_RESIZER, ID_RIGHT_RESIZER,
11
11
  ID_FOOTER_RESIZER, ID_BTN_LEFT_PANEL_TOGGLE, ID_BTN_RIGHT_PANEL_TOGGLE,
12
- ID_BTN_CREATE_KEYSTONE, ID_CENTER_PANEL_CONTENT,
13
- EVENT_SHELL_READY
12
+ ID_BTN_CREATE_KEYSTONE, ID_CENTER_PANEL_CONTENT
14
13
  } from "./space-gib-shell-constants.mjs";
14
+ import { EVENT_IBGIB_SHELL_READY, EVENT_IBGIB_UI_BUSY } from "@ibgib/web-gib/dist/ui/ui-constants.mjs";
15
+ import { showFullscreenDialog, FullscreenDialogController } from "@ibgib/web-gib/dist/ui/ui-helpers.mjs";
15
16
  import { PanelState } from "./space-gib-shell-types.mjs";
16
17
  import { KeystoneCreatorComponentMeta } from "../../components/keystone-creator/keystone-creator.mjs";
18
+ import { IdentityManagerComponentMeta } from "../../components/identity-manager/identity-manager.mjs";
19
+ import { IdentityHeaderComponentMeta } from "../../components/identity-header/identity-header.mjs";
17
20
 
18
21
  export class SpaceGibShellService {
19
22
  private lc: string = `[SpaceGibShellService]`;
@@ -93,9 +96,66 @@ export class SpaceGibShellService {
93
96
  // Create Keystone Button
94
97
  const btnCreate = document.getElementById(ID_BTN_CREATE_KEYSTONE);
95
98
  if (btnCreate) {
96
- btnCreate.addEventListener('click', () => this.showKeystoneCreator());
99
+ btnCreate.addEventListener('click', () => this.showIdentityManager());
97
100
  }
98
101
 
102
+ // Listen for global busy events from components
103
+ let currentBusyOverlay: FullscreenDialogController | undefined;
104
+ let isShellBusy = false;
105
+ window.addEventListener(EVENT_IBGIB_UI_BUSY, (ev: any) => {
106
+ const { isBusy, title, msg, animationEmoji } = ev.detail;
107
+ if (isBusy) {
108
+ isShellBusy = true;
109
+ if (currentBusyOverlay) {
110
+ currentBusyOverlay.update({ title, msg, animationEmoji });
111
+ } else {
112
+ // we use a .then() only because we are within
113
+ // synchronous code.
114
+ showFullscreenDialog({
115
+ title: title || 'Processing...',
116
+ msg: msg || 'Please do not navigate away or refresh.',
117
+ isBusy: true,
118
+ animationEmoji
119
+ }).then((overlay) => {
120
+ if (isShellBusy) {
121
+ currentBusyOverlay = overlay;
122
+ } else {
123
+ overlay.close();
124
+ }
125
+ }).catch((e) => {
126
+ console.error(`${lc}[showFullscreenDialog] ${extractErrorMsg(e)}`)
127
+ });
128
+ }
129
+ } else {
130
+ isShellBusy = false;
131
+ if (currentBusyOverlay) {
132
+ currentBusyOverlay.close();
133
+ currentBusyOverlay = undefined;
134
+ }
135
+ }
136
+ });
137
+
138
+ } catch (error) {
139
+ console.error(`${lc} ${extractErrorMsg(error)}`);
140
+ }
141
+ }
142
+
143
+ public async showIdentityManager() {
144
+ const lc = `${this.lc}[${this.showIdentityManager.name}]`;
145
+ try {
146
+ // 2. Instantiate and inject the component
147
+ const componentSvc = await getComponentSvc();
148
+ const component = await componentSvc.getComponentInstance({
149
+ path: 'ibgib-identity-manager',
150
+ ibGibAddr: ROOT_ADDR, // Virtual address for initial render
151
+ useRegExpPrefilter: true,
152
+ });
153
+
154
+ const centerPanel = document.getElementById(ID_CENTER_PANEL_CONTENT);
155
+ if (centerPanel && component) {
156
+ centerPanel.innerHTML = ''; // Clear hero section
157
+ centerPanel.appendChild(component as any);
158
+ }
99
159
  } catch (error) {
100
160
  console.error(`${lc} ${extractErrorMsg(error)}`);
101
161
  }
@@ -104,19 +164,15 @@ export class SpaceGibShellService {
104
164
  private async showKeystoneCreator() {
105
165
  const lc = `${this.lc}[${this.showKeystoneCreator.name}]`;
106
166
  try {
107
- // 1. Expand side panels (as requested for visual transition)
108
- if (this.leftPanelState === 'collapsed') { this.toggleLeftPanel(); }
109
- if (this.rightPanelState === 'collapsed') { this.toggleRightPanel(); }
110
-
111
167
  // 2. Instantiate and inject the component
112
168
  const componentSvc = await getComponentSvc();
113
169
  const component = await componentSvc.getComponentInstance({
114
- path: 'space-gib-keystone-creator',
170
+ path: 'ibgib-keystone-creator',
115
171
  ibGibAddr: ROOT_ADDR, // Virtual address for initial render
116
172
  useRegExpPrefilter: true,
117
173
  });
118
174
 
119
- const centerPanel = document.getElementById(ID_CENTER_PANEL);
175
+ const centerPanel = document.getElementById(ID_CENTER_PANEL_CONTENT);
120
176
  if (centerPanel && component) {
121
177
  centerPanel.innerHTML = ''; // Clear hero section
122
178
  centerPanel.appendChild(component as any);
@@ -190,6 +246,8 @@ export class SpaceGibShellService {
190
246
  try {
191
247
  const componentsMeta: IbGibDynamicComponentMeta[] = [
192
248
  new KeystoneCreatorComponentMeta(),
249
+ new IdentityManagerComponentMeta(),
250
+ new IdentityHeaderComponentMeta(),
193
251
  ];
194
252
  const componentSvc = await getComponentSvc();
195
253
  for (let componentMeta of componentsMeta) {
@@ -213,7 +271,21 @@ export class SpaceGibShellService {
213
271
  statusSection.style.display = 'none';
214
272
  }
215
273
 
216
- window.dispatchEvent(new CustomEvent(EVENT_SHELL_READY, { detail: { timestamp: Date.now() } }));
274
+ // Mount identity-header component in the header bar
275
+ const componentSvc = await getComponentSvc();
276
+ const identityHeader = await componentSvc.getComponentInstance({
277
+ path: 'ibgib-identity-header',
278
+ ibGibAddr: ROOT_ADDR,
279
+ useRegExpPrefilter: true,
280
+ });
281
+
282
+ const headerContainer = document.getElementById('identity-header-container');
283
+ if (headerContainer && identityHeader) {
284
+ headerContainer.innerHTML = '';
285
+ headerContainer.appendChild(identityHeader as any);
286
+ }
287
+
288
+ window.dispatchEvent(new CustomEvent(EVENT_IBGIB_SHELL_READY, { detail: { timestamp: Date.now() } }));
217
289
  } catch (error) {
218
290
  console.error(`${lc} ${extractErrorMsg(error)}`);
219
291
  }
File without changes
@@ -1,64 +1,61 @@
1
1
  {
2
- "$schema": "../../../../libs/core-gib/src/keystone/keystone-policy.schema.json",
3
- "behaviorProfiles": {
4
- "standard": {
5
- "size": 200,
6
- "replenish": "top-up",
7
- "selectSequentially": 2,
8
- "selectRandomly": 2,
9
- "targetBindingChars": 5
10
- },
11
- "high-security": {
12
- "size": 2000,
13
- "replenish": "replace-all",
14
- "selectSequentially": 5,
15
- "selectRandomly": 5,
16
- "targetBindingChars": 16
17
- },
18
- "session-connect": {
19
- "size": 20,
20
- "replenish": "top-up",
21
- "selectSequentially": 2,
22
- "selectRandomly": 2,
23
- "targetBindingChars": 3
24
- }
2
+ "$schema": "../../../../libs/core-gib/dist/keystone/policy/schemas/keystone.medium.schema.json",
3
+ "metadata": {
4
+ "name": "Space-Gib Default Identity Policy",
5
+ "securityLevel": "medium"
25
6
  },
26
7
  "pools": {
27
8
  "sync": {
28
9
  "id": "sync",
29
- "allowedVerbs": [
30
- "sync"
31
- ],
32
- "behaviorProfile": "standard",
10
+ "allowedVerbs": ["sync"],
33
11
  "algo": "SHA-256",
34
- "rounds": 1
12
+ "rounds": 1,
13
+ "behavior": {
14
+ "size": 200,
15
+ "replenish": "top-up",
16
+ "selectSequentially": 2,
17
+ "selectRandomly": 2,
18
+ "targetBindingCount": 5
19
+ }
35
20
  },
36
21
  "manage": {
37
22
  "id": "manage",
38
- "allowedVerbs": [
39
- "manage"
40
- ],
41
- "behaviorProfile": "high-security",
23
+ "allowedVerbs": ["manage"],
42
24
  "algo": "SHA-512",
43
- "rounds": 10
25
+ "rounds": 10,
26
+ "behavior": {
27
+ "size": 2000,
28
+ "replenish": "replace-all",
29
+ "selectSequentially": 5,
30
+ "selectRandomly": 5,
31
+ "targetBindingCount": 16
32
+ }
44
33
  },
45
34
  "revoke": {
46
35
  "id": "revoke",
47
- "allowedVerbs": [
48
- "revoke"
49
- ],
50
- "behaviorProfile": "high-security",
36
+ "allowedVerbs": ["revoke"],
51
37
  "algo": "SHA-512",
52
- "rounds": 10
38
+ "rounds": 10,
39
+ "behavior": {
40
+ "size": 2000,
41
+ "replenish": "delete-all",
42
+ "selectSequentially": 5,
43
+ "selectRandomly": 5,
44
+ "targetBindingCount": 16
45
+ }
53
46
  },
54
47
  "connect": {
55
48
  "id": "connect",
56
- "allowedVerbs": [
57
- "connect"
58
- ],
59
- "behaviorProfile": "session-connect",
49
+ "allowedVerbs": ["connect"],
60
50
  "algo": "SHA-256",
61
- "rounds": 1
51
+ "rounds": 1,
52
+ "behavior": {
53
+ "size": 20,
54
+ "replenish": "top-up",
55
+ "selectSequentially": 2,
56
+ "selectRandomly": 2,
57
+ "targetBindingCount": 3
58
+ }
62
59
  }
63
60
  }
64
61
  }
@@ -15,8 +15,8 @@ import {
15
15
  SESSION_KEYSTONE_POLICY as CORE_SESSION_KEYSTONE_POLICY,
16
16
  getConnectChallenge as coreGetConnectChallenge,
17
17
  checkConnectSolution as coreCheckConnectSolution
18
- } from '@ibgib/core-gib/dist/sync/sync-peer/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs';
19
- import { createPoolConfigFromJson, KeystonePoolTemplate, KeystoneBehaviorProfileTemplate } from '@ibgib/core-gib/dist/keystone/keystone-config-builder.mjs';
18
+ } from '@ibgib/core-gib/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs';
19
+ import { createPoolConfigFromJson, KeystonePoolTemplate } from '@ibgib/core-gib/dist/keystone/keystone-config-builder.mjs';
20
20
  import { KeystonePoolConfig } from '@ibgib/core-gib/dist/keystone/keystone-types.mjs';
21
21
 
22
22
  import rawPolicies from './keystone-policies.json' assert { type: 'json' };
@@ -25,8 +25,7 @@ import rawPolicies from './keystone-policies.json' assert { type: 'json' };
25
25
  // 1. JSON Configuration Mapping & Hybrid Fallbacks
26
26
  // ---------------------------------------------------------------------------
27
27
 
28
- export const behaviorProfiles = rawPolicies.behaviorProfiles as Record<string, KeystoneBehaviorProfileTemplate>;
29
- export const pools = rawPolicies.pools as Record<string, KeystonePoolTemplate>;
28
+ export const pools = rawPolicies.pools as unknown as Record<string, KeystonePoolTemplate>;
30
29
 
31
30
  /**
32
31
  * Resolves a pool config by key from the JSON policy file, with fallback behavior.
@@ -38,7 +37,6 @@ export function getSpaceGibPoolConfig(poolKey: string, salt: string): KeystonePo
38
37
  }
39
38
  return createPoolConfigFromJson({
40
39
  template,
41
- behaviorProfiles,
42
40
  salt
43
41
  });
44
42
  }
@@ -69,7 +67,7 @@ export const SESSION_KEYSTONE_POLICY = {
69
67
  * We want more for this, with future implementations we will not have
70
68
  * resource exhaustion.
71
69
  */
72
- TARGET_BINDING_CHARS: 3,
70
+ TARGET_BINDING_COUNT: 3,
73
71
  },
74
72
 
75
73
  /**
@@ -1,94 +1,99 @@
1
- import { respecfully, iReckon, ifWe } from '@ibgib/helper-gib/dist/respec-gib/respec-gib.mjs';
2
- import { isSafePath, isValidStaticPath, API_PATHS, getPathParams, MAX_PATH_LENGTH, validateAddressSegment } from './path-helper.mjs';
3
-
4
- const sir = `[${import.meta.url}]`;
5
-
6
- await respecfully(sir, 'Path Helper Validation', async () => {
7
-
8
- await respecfully(sir, 'isSafePath', async () => {
9
- await ifWe(sir, 'valid simple paths', async () => {
10
- iReckon(sir, isSafePath('/api/health')).isGonnaBeTrue();
11
- iReckon(sir, isSafePath('/index.html')).isGonnaBeTrue();
12
- });
13
-
14
- await ifWe(sir, 'too long paths', async () => {
15
- const longPath = '/'.repeat(MAX_PATH_LENGTH + 1);
16
- iReckon(sir, isSafePath(longPath)).isGonnaBeFalse();
17
- });
18
-
19
- await ifWe(sir, 'directory traversal attempts', async () => {
20
- iReckon(sir, isSafePath('/../etc/passwd')).isGonnaBeFalse();
21
- iReckon(sir, isSafePath('/api/../../etc/passwd')).isGonnaBeFalse();
22
- });
23
-
24
- await ifWe(sir, 'null bytes', async () => {
25
- iReckon(sir, isSafePath('/api/health\0')).isGonnaBeFalse();
26
- });
27
-
28
- await ifWe(sir, 'redundant slashes', async () => {
29
- iReckon(sir, isSafePath('/api//health')).isGonnaBeFalse();
30
- });
31
-
32
- await ifWe(sir, 'dot-file access', async () => {
33
- iReckon(sir, isSafePath('/.env')).isGonnaBeFalse();
34
- iReckon(sir, isSafePath('/scripts/.hidden')).isGonnaBeFalse();
35
- iReckon(sir, isSafePath('/.git/config')).isGonnaBeFalse();
36
- });
37
- });
38
-
39
- await respecfully(sir, 'validateAddressSegment', async () => {
40
- await ifWe(sir, 'valid encoded addresses', async () => {
41
- const validGib = 'a'.repeat(64);
42
- iReckon(sir, validateAddressSegment(`someib%5E${validGib}`)).isGonnaBeTrue();
43
- // gib with dots (common in sha256.sha256)
44
- const dotGib = `${validGib}.${validGib}`;
45
- iReckon(sir, validateAddressSegment(`someib%5E${dotGib}`)).isGonnaBeTrue();
46
- });
47
-
48
- await ifWe(sir, 'invalid encoded addresses', async () => {
49
- iReckon(sir, validateAddressSegment('no_delim')).isGonnaBeFalse();
50
- iReckon(sir, validateAddressSegment('%5Estarts_with_delim')).isGonnaBeFalse();
51
- iReckon(sir, validateAddressSegment('too%5Emany%5Edelims')).isGonnaBeFalse();
52
- iReckon(sir, validateAddressSegment('too%5Emany..dots')).isGonnaBeFalse();
53
- iReckon(sir, validateAddressSegment('invalid_chars_!%5Eabc')).isGonnaBeFalse();
54
- });
55
- });
56
-
57
- await respecfully(sir, 'API_PATHS Regexes', async () => {
58
- await ifWe(sir, 'HEALTH regex', async () => {
59
- iReckon(sir, API_PATHS.HEALTH.test('/api/health')).isGonnaBeTrue();
60
- iReckon(sir, API_PATHS.HEALTH.test('/api/health/')).isGonnaBeTrue();
61
- iReckon(sir, API_PATHS.HEALTH.test('/api/health/more')).isGonnaBeFalse();
62
- });
63
-
64
- await ifWe(sir, 'IBGIB_ADDR regex', async () => {
65
- // valid encoded addr: someib%5Esomegib
66
- const addr = '/api/ibgib/someib%5Esomegib';
67
- iReckon(sir, API_PATHS.IBGIB_ADDR.test(addr)).isGonnaBeTrue();
68
-
69
- const params = getPathParams(addr, API_PATHS.IBGIB_ADDR);
70
- iReckon(sir, params?.[0]).isGonnaBe('someib%5Esomegib');
71
- });
72
-
73
- await ifWe(sir, 'IBGIB_GRAPH regex', async () => {
74
- const path = '/api/ibgib/graph/someib%5Esomegib';
75
- iReckon(sir, API_PATHS.IBGIB_GRAPH.test(path)).isGonnaBeTrue();
76
-
77
- const params = getPathParams(path, API_PATHS.IBGIB_GRAPH);
78
- iReckon(sir, params?.[0]).isGonnaBe('someib%5Esomegib');
79
- });
80
-
81
- await ifWe(sir, 'KEYSTONE regex', async () => {
82
- iReckon(sir, API_PATHS.KEYSTONE.test('/api/keystone')).isGonnaBeTrue();
83
- iReckon(sir, API_PATHS.KEYSTONE.test('/api/keystone/')).isGonnaBeTrue();
84
- });
85
-
86
- await ifWe(sir, 'KEYSTONE_EVOLVE regex', async () => {
87
- const path = '/api/keystone/evolve/someib%5Esomegib';
88
- iReckon(sir, API_PATHS.KEYSTONE_EVOLVE.test(path)).isGonnaBeTrue();
89
-
90
- const params = getPathParams(path, API_PATHS.KEYSTONE_EVOLVE);
91
- iReckon(sir, params?.[0]).isGonnaBe('someib%5Esomegib');
92
- });
93
- });
94
- });
1
+ // import { respecfully, iReckon, ifWe } from '@ibgib/helper-gib/dist/respec-gib/respec-gib.mjs';
2
+ // import {
3
+ // isSafePath, isValidStaticPath,
4
+ // // API_PATHS, getPathParams, MAX_PATH_LENGTH,
5
+ // validateAddressSegment
6
+ // } from './path-helper.mjs';
7
+ // import { MAX_PATH_LENGTH } from './path-constants.mts';
8
+
9
+ // const sir = `[${import.meta.url}]`;
10
+
11
+ // await respecfully(sir, 'Path Helper Validation', async () => {
12
+
13
+ // await respecfully(sir, 'isSafePath', async () => {
14
+ // await ifWe(sir, 'valid simple paths', async () => {
15
+ // iReckon(sir, isSafePath('/api/health')).isGonnaBeTrue();
16
+ // iReckon(sir, isSafePath('/index.html')).isGonnaBeTrue();
17
+ // });
18
+
19
+ // await ifWe(sir, 'too long paths', async () => {
20
+ // const longPath = '/'.repeat(MAX_PATH_LENGTH + 1);
21
+ // iReckon(sir, isSafePath(longPath)).isGonnaBeFalse();
22
+ // });
23
+
24
+ // await ifWe(sir, 'directory traversal attempts', async () => {
25
+ // iReckon(sir, isSafePath('/../etc/passwd')).isGonnaBeFalse();
26
+ // iReckon(sir, isSafePath('/api/../../etc/passwd')).isGonnaBeFalse();
27
+ // });
28
+
29
+ // await ifWe(sir, 'null bytes', async () => {
30
+ // iReckon(sir, isSafePath('/api/health\0')).isGonnaBeFalse();
31
+ // });
32
+
33
+ // await ifWe(sir, 'redundant slashes', async () => {
34
+ // iReckon(sir, isSafePath('/api//health')).isGonnaBeFalse();
35
+ // });
36
+
37
+ // await ifWe(sir, 'dot-file access', async () => {
38
+ // iReckon(sir, isSafePath('/.env')).isGonnaBeFalse();
39
+ // iReckon(sir, isSafePath('/scripts/.hidden')).isGonnaBeFalse();
40
+ // iReckon(sir, isSafePath('/.git/config')).isGonnaBeFalse();
41
+ // });
42
+ // });
43
+
44
+ // await respecfully(sir, 'validateAddressSegment', async () => {
45
+ // await ifWe(sir, 'valid encoded addresses', async () => {
46
+ // const validGib = 'a'.repeat(64);
47
+ // iReckon(sir, validateAddressSegment(`someib%5E${validGib}`)).isGonnaBeTrue();
48
+ // // gib with dots (common in sha256.sha256)
49
+ // const dotGib = `${validGib}.${validGib}`;
50
+ // iReckon(sir, validateAddressSegment(`someib%5E${dotGib}`)).isGonnaBeTrue();
51
+ // });
52
+
53
+ // await ifWe(sir, 'invalid encoded addresses', async () => {
54
+ // iReckon(sir, validateAddressSegment('no_delim')).isGonnaBeFalse();
55
+ // iReckon(sir, validateAddressSegment('%5Estarts_with_delim')).isGonnaBeFalse();
56
+ // iReckon(sir, validateAddressSegment('too%5Emany%5Edelims')).isGonnaBeFalse();
57
+ // iReckon(sir, validateAddressSegment('too%5Emany..dots')).isGonnaBeFalse();
58
+ // iReckon(sir, validateAddressSegment('invalid_chars_!%5Eabc')).isGonnaBeFalse();
59
+ // });
60
+ // });
61
+
62
+ // await respecfully(sir, 'API_PATHS Regexes', async () => {
63
+ // await ifWe(sir, 'HEALTH regex', async () => {
64
+ // iReckon(sir, API_PATHS.HEALTH.test('/api/health')).isGonnaBeTrue();
65
+ // iReckon(sir, API_PATHS.HEALTH.test('/api/health/')).isGonnaBeTrue();
66
+ // iReckon(sir, API_PATHS.HEALTH.test('/api/health/more')).isGonnaBeFalse();
67
+ // });
68
+
69
+ // await ifWe(sir, 'IBGIB_ADDR regex', async () => {
70
+ // // valid encoded addr: someib%5Esomegib
71
+ // const addr = '/api/ibgib/someib%5Esomegib';
72
+ // iReckon(sir, API_PATHS.IBGIB_ADDR.test(addr)).isGonnaBeTrue();
73
+
74
+ // const params = getPathParams(addr, API_PATHS.IBGIB_ADDR);
75
+ // iReckon(sir, params?.[0]).isGonnaBe('someib%5Esomegib');
76
+ // });
77
+
78
+ // await ifWe(sir, 'IBGIB_GRAPH regex', async () => {
79
+ // const path = '/api/ibgib/graph/someib%5Esomegib';
80
+ // iReckon(sir, API_PATHS.IBGIB_GRAPH.test(path)).isGonnaBeTrue();
81
+
82
+ // const params = getPathParams(path, API_PATHS.IBGIB_GRAPH);
83
+ // iReckon(sir, params?.[0]).isGonnaBe('someib%5Esomegib');
84
+ // });
85
+
86
+ // await ifWe(sir, 'KEYSTONE regex', async () => {
87
+ // iReckon(sir, API_PATHS.KEYSTONE.test('/api/keystone')).isGonnaBeTrue();
88
+ // iReckon(sir, API_PATHS.KEYSTONE.test('/api/keystone/')).isGonnaBeTrue();
89
+ // });
90
+
91
+ // await ifWe(sir, 'KEYSTONE_EVOLVE regex', async () => {
92
+ // const path = '/api/keystone/evolve/someib%5Esomegib';
93
+ // iReckon(sir, API_PATHS.KEYSTONE_EVOLVE.test(path)).isGonnaBeTrue();
94
+
95
+ // const params = getPathParams(path, API_PATHS.KEYSTONE_EVOLVE);
96
+ // iReckon(sir, params?.[0]).isGonnaBe('someib%5Esomegib');
97
+ // });
98
+ // });
99
+ // });
@@ -32,3 +32,12 @@ The `RouteInfo` object provides an enriched view of the incoming request:
32
32
  The `ServeGib_V1` executes handlers in the order they are provided in the `handlers` array. The first handler to return a non-undefined `ResponseResult` wins.
33
33
  * **Tip**: Order specific routes (like `/api/ibgib/graph`) before more general ones (like `/api/ibgib`).
34
34
  * **Fallback**: The `ErrorHandler` is used as a final catch-all for 404s and fatal errors.
35
+
36
+ ## Docker / Server Resilience Note
37
+ When deploying or running this service (or any downstream applications) within a Docker Compose environment, ensure that the `docker-compose.yml` configuration specifies a restart policy such as:
38
+ ```yaml
39
+ services:
40
+ space-gib:
41
+ restart: unless-stopped
42
+ ```
43
+ This ensures that the container auto-recovers and restarts if the node process crashes due to unhandled events (such as raw TCP socket `ECONNRESET` exceptions from client refreshes).
@@ -13,7 +13,7 @@
13
13
  import { extractErrorMsg } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
14
14
  import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
15
15
  import { IbGib_V1 } from '@ibgib/ts-gib/dist/V1/types.mjs';
16
- import { validateAndRegisterEvolveKeystone } from '@ibgib/core-gib/dist/sync/sync-peer/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs';
16
+ import { validateAndRegisterEvolveKeystone } from '@ibgib/core-gib/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs';
17
17
  import { KeystoneIbGib_V1 } from '@ibgib/core-gib/dist/keystone/keystone-types.mjs';
18
18
 
19
19
  import { GLOBAL_LOG_A_LOT } from '../../../constants.mjs';
@@ -133,7 +133,7 @@ export class KeystoneGenesisHandler extends ServeGibHandlerBase<undefined, undef
133
133
  await metaspace.registerNewIbGib({ ibGib: keystoneIbGib, space });
134
134
 
135
135
  console.log(`${lc} domain created and keystone persisted: ${addr}`);
136
- if (logalot) { console.log(`${lc} keystoneIbGib: ${pretty(keystoneIbGib)} (I: d598c6ff7a48997d585b84c19c464826)`); }
136
+ // if (logalot) { console.log(`${lc} keystoneIbGib: ${pretty(keystoneIbGib)} (I: d598c6ff7a48997d585b84c19c464826)`); }
137
137
  return this.ok({ success: true, addr }, 201);
138
138
  } catch (error) {
139
139
  const emsg = extractErrorMsg(error);
@@ -38,7 +38,7 @@ await respecfully(sir, 'Keystone GET Integration', async () => {
38
38
  }
39
39
  mkdirSync(testDataDir, { recursive: true });
40
40
 
41
- await ifWeMight(sir, 'retrieve a keystone chain via GET /api/keystone/:addr', async () => {
41
+ await ifWe(sir, 'retrieve a keystone chain via GET /api/keystone/:addr', async () => {
42
42
  const keystoneService = new KeystoneService_V1();
43
43
  const config = createStandardPoolConfig({ id: POOL_ID_DEFAULT, salt: POOL_ID_DEFAULT });
44
44
 
@@ -67,8 +67,8 @@ await respecfully(sir, 'Keystone GET Integration', async () => {
67
67
  method: 'POST',
68
68
  headers: { 'ibgib-domain': addr, 'content-type': 'application/json' },
69
69
  on: (event: string, cb: any) => {
70
- if (event === 'data') cb(Buffer.from(JSON.stringify({ ibGibs: [genesis] })));
71
- if (event === 'end') cb();
70
+ if (event === 'data') { cb(Buffer.from(JSON.stringify({ ibGibs: [genesis] }))); }
71
+ if (event === 'end') { cb(); }
72
72
  }
73
73
  };
74
74
  const postRes: any = {
@@ -83,7 +83,7 @@ await respecfully(sir, 'Keystone GET Integration', async () => {
83
83
  method: 'GET',
84
84
  headers: { 'ibgib-domain': addr },
85
85
  on: (event: string, cb: any) => {
86
- if (event === 'end') cb();
86
+ if (event === 'end') { cb(); }
87
87
  }
88
88
  };
89
89
 
@@ -3,7 +3,7 @@
3
3
  */
4
4
 
5
5
  import { extractErrorMsg } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
6
- import { validateAndRegisterPostKeystones } from '@ibgib/core-gib/dist/sync/sync-peer/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs';
6
+ import { validateAndRegisterPostKeystones } from '@ibgib/core-gib/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs';
7
7
 
8
8
  import { GLOBAL_LOG_A_LOT } from '../../../constants.mjs';
9
9
  import { ServeGibHandlerBase } from '../../handler-base.mjs';
@@ -11,8 +11,8 @@ import { extractErrorMsg } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs
11
11
  import { getIbGibAddr, getIbAndGib } from '@ibgib/ts-gib/dist/helper.mjs';
12
12
  import { validateIbGibAddr } from '@ibgib/ts-gib/dist/V1/validate-helper.mjs';
13
13
  import { parseKeystoneIb } from '@ibgib/core-gib/dist/keystone/keystone-helpers.mjs';
14
- import { SyncPeerWebSocketReceiver_V1, IWebSocketWrapper } from '@ibgib/core-gib/dist/sync/sync-peer/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.mjs';
15
- import { validateUpfrontConnect } from '@ibgib/core-gib/dist/sync/sync-peer/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs';
14
+ import { SyncPeerWebSocketReceiver_V1, IWebSocketWrapper } from '@ibgib/core-gib/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.mjs';
15
+ import { validateUpfrontConnect } from '@ibgib/core-gib/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs';
16
16
  import { SyncSagaCoordinator } from '@ibgib/core-gib/dist/sync/sync-saga-coordinator.mjs';
17
17
 
18
18
  import { GLOBAL_LOG_A_LOT } from '../../constants.mjs';
@@ -94,10 +94,26 @@ export abstract class SyncUpgradeHandlerBase extends ServeGibHandlerWithMetaspac
94
94
  sagaId: undefined, // Resolved dynamically inside runtime turns
95
95
  });
96
96
 
97
+ // Handle raw socket errors to prevent unhandled 'error' crash (e.g. ECONNRESET)
98
+ socket.on('error', (err) => {
99
+ if (logalot) { console.warn(`[SyncUpgradeHandlerBase] Socket error: ${extractErrorMsg(err)}`); }
100
+ try {
101
+ socket.destroy();
102
+ } catch (e) {
103
+ // ignore
104
+ }
105
+ });
106
+
97
107
  // 6. Map standard Node.js Socket to platform-agnostic IWebSocketWrapper
98
108
  const socketWrapper: IWebSocketWrapper = {
99
109
  send(data: string): void {
100
- socket.write(encodeTextFrame(data));
110
+ if (socket.writable) {
111
+ try {
112
+ socket.write(encodeTextFrame(data));
113
+ } catch (e) {
114
+ if (logalot) { console.warn(`[SyncUpgradeHandlerBase] failed to write message frame: ${extractErrorMsg(e)}`); }
115
+ }
116
+ }
101
117
  },
102
118
  onMessage(callback: (data: string) => void): void {
103
119
  const decoder = new WebSocketFrameDecoder();
@@ -111,13 +127,29 @@ export abstract class SyncUpgradeHandlerBase extends ServeGibHandlerWithMetaspac
111
127
  }
112
128
  } catch (error) {
113
129
  if (logalot) { console.warn(`[SyncUpgradeHandlerBase] closing connection due to decoder error: ${extractErrorMsg(error)}`); }
114
- socket.write(encodeCloseFrame());
130
+ if (socket.writable) {
131
+ try {
132
+ socket.write(encodeCloseFrame());
133
+ } catch (e) {
134
+ // ignore
135
+ }
136
+ }
115
137
  socket.end();
116
138
  }
117
139
  });
118
140
  },
119
141
  onClose(callback: () => void): void {
120
142
  socket.on('close', callback);
143
+ },
144
+ close(): void {
145
+ if (socket.writable) {
146
+ try {
147
+ socket.write(encodeCloseFrame());
148
+ } catch (e) {
149
+ // ignore
150
+ }
151
+ }
152
+ socket.end();
121
153
  }
122
154
  };
123
155
 
@@ -134,7 +166,7 @@ export abstract class SyncUpgradeHandlerBase extends ServeGibHandlerWithMetaspac
134
166
 
135
167
  protected override async parseParamsImpl(reqCtx: RequestContext<ParamsWithDomain>): Promise<ParamsWithDomain | undefined> {
136
168
  const match = reqCtx.pathname.match(this.regex);
137
- if (!match) return undefined;
169
+ if (!match) { return undefined; }
138
170
  const domainIb = decodeURIComponent(match[1]);
139
171
  const domainGib = decodeURIComponent(match[2]);
140
172
  const domainAddr = getIbGibAddr({ ib: domainIb, gib: domainGib });