@ibgib/space-gib 0.0.1 → 0.0.3
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/client/bootstrap.mjs +1 -1
- package/dist/client/bootstrap.mjs.map +1 -1
- package/dist/client/chunk-2KJC5XKE.mjs +31 -0
- package/dist/client/chunk-2KJC5XKE.mjs.map +7 -0
- package/dist/client/chunk-QNIXTRFO.mjs +235 -0
- package/dist/client/chunk-QNIXTRFO.mjs.map +7 -0
- package/dist/client/index.html +47 -9
- package/dist/client/index.mjs +1 -1
- package/dist/client/script.mjs +1 -1
- package/dist/client/style.css +26 -0
- package/dist/server/server.mjs +5752 -1010
- package/dist/server/server.mjs.map +4 -4
- package/package.json +1 -1
- package/src/client/AUTO-GENERATED-version.mts +1 -1
- package/src/client/api/space-gib-api-bridge.mts +119 -8
- package/src/client/dev-tools.mts +717 -31
- package/src/client/index.html +47 -9
- package/src/client/style.css +26 -0
- package/src/common/keystone-policies.json +64 -0
- package/src/common/keystone-policies.mts +39 -86
- package/src/server/serve-gib/handlers/api/debug/ws-echo.handler.mts +13 -12
- package/src/server/serve-gib/handlers/api/keystone/keystone-evolve.handler.mts +14 -167
- package/src/server/serve-gib/handlers/api/keystone/keystone-genesis.handler.mts +6 -6
- package/src/server/serve-gib/handlers/api/keystone/keystone-get.respec.mts +3 -3
- package/src/server/serve-gib/handlers/api/keystone/keystone-post.handler.mts +10 -25
- package/src/server/serve-gib/handlers/ws/sync-upgrade-handler-base.mts +205 -0
- package/src/server/serve-gib/handlers/ws/sync-upgrade.handler.mts +13 -487
- package/src/server/serve-gib/handlers/ws/ws-helper.mts +80 -3
- package/dist/client/chunk-CT47Z5WU.mjs +0 -21
- package/dist/client/chunk-CT47Z5WU.mjs.map +0 -7
- package/dist/client/chunk-RHEDTRKF.mjs +0 -235
- package/dist/client/chunk-RHEDTRKF.mjs.map +0 -7
- package/dist/respec-gib.node.mjs +0 -5
package/src/client/index.html
CHANGED
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
<p class="placeholder-text">Left Panel Content</p>
|
|
74
74
|
</div>
|
|
75
75
|
</aside>
|
|
76
|
-
|
|
76
|
+
|
|
77
77
|
<!-- Left Resizer -->
|
|
78
78
|
<div id="left-resizer" class="resizer resizer-v"></div>
|
|
79
79
|
|
|
@@ -103,15 +103,53 @@
|
|
|
103
103
|
<!-- DEV TOOLS — remove or hide once WS sync is confirmed working -->
|
|
104
104
|
<section class="dev-panel" id="dev-panel" aria-label="Developer tools">
|
|
105
105
|
<h2 class="dev-panel-title">Dev Tools</h2>
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
<
|
|
110
|
-
<
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
106
|
+
|
|
107
|
+
<!-- Row 00: Raw POC buttons -->
|
|
108
|
+
<div class="dev-panel-row-container">
|
|
109
|
+
<span class="dev-panel-row-label">00</span>
|
|
110
|
+
<div class="dev-panel-row">
|
|
111
|
+
<button id="btn-print-dev-state" class="dev-btn">Print Dev State</button>
|
|
112
|
+
<button id="btn-create-domain-keystone" class="dev-btn">1. Create Domain Keystone (I)</button>
|
|
113
|
+
<button id="btn-create-test-ibgib" class="dev-btn" disabled>2. Create Test IbGib (X)</button>
|
|
114
|
+
<button id="btn-create-session-keystone" class="dev-btn" disabled>3. Create Session Keystone
|
|
115
|
+
(S)</button>
|
|
116
|
+
<button id="btn-evolve-domain-keystone" class="dev-btn" disabled>4. Evolve Domain Keystone
|
|
117
|
+
(I^I1)</button>
|
|
118
|
+
<button id="btn-perform-sync" class="dev-btn" disabled>5. Perform Sync Handshake (S)</button>
|
|
119
|
+
<button id="btn-test-ws" class="dev-btn">Test WebSocket Echo</button>
|
|
120
|
+
</div>
|
|
114
121
|
</div>
|
|
122
|
+
|
|
123
|
+
<!-- Row 1B: Phase 1B -->
|
|
124
|
+
<div class="dev-panel-row-container">
|
|
125
|
+
<span class="dev-panel-row-label">1B</span>
|
|
126
|
+
<div class="dev-panel-row">
|
|
127
|
+
<button id="btn-1b-setup" class="dev-btn">1B Setup</button>
|
|
128
|
+
<button id="btn-1b-sync" class="dev-btn" disabled>1B Sync</button>
|
|
129
|
+
<button id="btn-1b-check" class="dev-btn" disabled>1B Check</button>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
|
|
133
|
+
<!-- Row 2B: Phase 2B -->
|
|
134
|
+
<div class="dev-panel-row-container">
|
|
135
|
+
<span class="dev-panel-row-label">2B</span>
|
|
136
|
+
<div class="dev-panel-row">
|
|
137
|
+
<button id="btn-2b-setup" class="dev-btn">2B Setup</button>
|
|
138
|
+
<button id="btn-2b-sync" class="dev-btn" disabled>2B Sync</button>
|
|
139
|
+
<button id="btn-2b-check" class="dev-btn" disabled>2B Check</button>
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
|
|
143
|
+
<!-- Row 3B: Phase 3B -->
|
|
144
|
+
<div class="dev-panel-row-container">
|
|
145
|
+
<span class="dev-panel-row-label">3B</span>
|
|
146
|
+
<div class="dev-panel-row">
|
|
147
|
+
<button id="btn-3b-setup" class="dev-btn">3B Setup</button>
|
|
148
|
+
<button id="btn-3b-sync" class="dev-btn" disabled>3B Sync</button>
|
|
149
|
+
<button id="btn-3b-check" class="dev-btn" disabled>3B Check</button>
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
|
|
115
153
|
<pre id="dev-panel-log" class="dev-panel-log" aria-live="polite" aria-label="Dev log"></pre>
|
|
116
154
|
</section>
|
|
117
155
|
</div>
|
package/src/client/style.css
CHANGED
|
@@ -603,3 +603,29 @@ body::before {
|
|
|
603
603
|
word-break: break-all;
|
|
604
604
|
min-height: 3rem;
|
|
605
605
|
}
|
|
606
|
+
|
|
607
|
+
/* dev tools row styles */
|
|
608
|
+
.dev-panel-row-container {
|
|
609
|
+
display: flex;
|
|
610
|
+
align-items: center;
|
|
611
|
+
gap: 1.25rem;
|
|
612
|
+
border-bottom: 1px solid rgba(255, 204, 68, 0.08);
|
|
613
|
+
padding-bottom: 0.85rem;
|
|
614
|
+
}
|
|
615
|
+
.dev-panel-row-container:last-of-type {
|
|
616
|
+
border-bottom: none;
|
|
617
|
+
padding-bottom: 0;
|
|
618
|
+
}
|
|
619
|
+
.dev-panel-row-label {
|
|
620
|
+
font-family: var(--font-mono);
|
|
621
|
+
font-size: 0.85rem;
|
|
622
|
+
font-weight: 700;
|
|
623
|
+
color: var(--clr-warn);
|
|
624
|
+
min-width: 2.2rem;
|
|
625
|
+
text-align: center;
|
|
626
|
+
border: 1px solid rgba(255, 204, 68, 0.25);
|
|
627
|
+
border-radius: var(--radius-sm);
|
|
628
|
+
padding: 0.25rem 0.45rem;
|
|
629
|
+
background: rgba(255, 204, 68, 0.06);
|
|
630
|
+
user-select: none;
|
|
631
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
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
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"pools": {
|
|
27
|
+
"sync": {
|
|
28
|
+
"id": "sync",
|
|
29
|
+
"allowedVerbs": [
|
|
30
|
+
"sync"
|
|
31
|
+
],
|
|
32
|
+
"behaviorProfile": "standard",
|
|
33
|
+
"algo": "SHA-256",
|
|
34
|
+
"rounds": 1
|
|
35
|
+
},
|
|
36
|
+
"manage": {
|
|
37
|
+
"id": "manage",
|
|
38
|
+
"allowedVerbs": [
|
|
39
|
+
"manage"
|
|
40
|
+
],
|
|
41
|
+
"behaviorProfile": "high-security",
|
|
42
|
+
"algo": "SHA-512",
|
|
43
|
+
"rounds": 10
|
|
44
|
+
},
|
|
45
|
+
"revoke": {
|
|
46
|
+
"id": "revoke",
|
|
47
|
+
"allowedVerbs": [
|
|
48
|
+
"revoke"
|
|
49
|
+
],
|
|
50
|
+
"behaviorProfile": "high-security",
|
|
51
|
+
"algo": "SHA-512",
|
|
52
|
+
"rounds": 10
|
|
53
|
+
},
|
|
54
|
+
"connect": {
|
|
55
|
+
"id": "connect",
|
|
56
|
+
"allowedVerbs": [
|
|
57
|
+
"connect"
|
|
58
|
+
],
|
|
59
|
+
"behaviorProfile": "session-connect",
|
|
60
|
+
"algo": "SHA-256",
|
|
61
|
+
"rounds": 1
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -12,15 +12,39 @@
|
|
|
12
12
|
|
|
13
13
|
import { HashAlgorithm } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
|
|
14
14
|
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
} from '@ibgib/core-gib/dist/keystone/keystone-
|
|
20
|
-
import {
|
|
15
|
+
SESSION_KEYSTONE_POLICY as CORE_SESSION_KEYSTONE_POLICY,
|
|
16
|
+
getConnectChallenge as coreGetConnectChallenge,
|
|
17
|
+
checkConnectSolution as coreCheckConnectSolution
|
|
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, KeystoneBehaviorProfileTemplate } from '@ibgib/core-gib/dist/keystone/keystone-config-builder.mjs';
|
|
20
|
+
import { KeystonePoolConfig } from '@ibgib/core-gib/dist/keystone/keystone-types.mjs';
|
|
21
|
+
|
|
22
|
+
import rawPolicies from './keystone-policies.json' assert { type: 'json' };
|
|
21
23
|
|
|
22
24
|
// ---------------------------------------------------------------------------
|
|
23
|
-
// 1.
|
|
25
|
+
// 1. JSON Configuration Mapping & Hybrid Fallbacks
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
export const behaviorProfiles = rawPolicies.behaviorProfiles as Record<string, KeystoneBehaviorProfileTemplate>;
|
|
29
|
+
export const pools = rawPolicies.pools as Record<string, KeystonePoolTemplate>;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Resolves a pool config by key from the JSON policy file, with fallback behavior.
|
|
33
|
+
*/
|
|
34
|
+
export function getSpaceGibPoolConfig(poolKey: string, salt: string): KeystonePoolConfig {
|
|
35
|
+
const template = pools[poolKey];
|
|
36
|
+
if (!template) {
|
|
37
|
+
throw new Error(`Pool template not found in space-gib JSON config: ${poolKey}`);
|
|
38
|
+
}
|
|
39
|
+
return createPoolConfigFromJson({
|
|
40
|
+
template,
|
|
41
|
+
behaviorProfiles,
|
|
42
|
+
salt
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// 2. Compatibility Layer: Session Keystone (S) Policies
|
|
24
48
|
// ---------------------------------------------------------------------------
|
|
25
49
|
|
|
26
50
|
/**
|
|
@@ -31,12 +55,7 @@ export const SESSION_KEYSTONE_POLICY = {
|
|
|
31
55
|
/**
|
|
32
56
|
* Shared parameters across all session pools
|
|
33
57
|
*/
|
|
34
|
-
COMMON:
|
|
35
|
-
ALGO: HashAlgorithm.sha_256,
|
|
36
|
-
TYPE: KeystoneChallengeType.hash_reveal_v1,
|
|
37
|
-
ROUNDS: 2,
|
|
38
|
-
REPLENISH: KeystoneReplenishStrategy.topUp,
|
|
39
|
-
},
|
|
58
|
+
COMMON: CORE_SESSION_KEYSTONE_POLICY.COMMON,
|
|
40
59
|
|
|
41
60
|
/**
|
|
42
61
|
* The primary authorization pool for sync actions (e.g. putting/getting ibgibs).
|
|
@@ -56,28 +75,16 @@ export const SESSION_KEYSTONE_POLICY = {
|
|
|
56
75
|
/**
|
|
57
76
|
* Dedicated pool for the WebSocket sync protocol handshake.
|
|
58
77
|
*/
|
|
59
|
-
|
|
60
|
-
ID: 'handshake',
|
|
61
|
-
VERB: 'handshake',
|
|
62
|
-
SIZE: 10,
|
|
63
|
-
SELECT_SEQUENTIALLY: 2,
|
|
64
|
-
SELECT_RANDOMLY: 2,
|
|
65
|
-
TARGET_BINDING_CHARS: 0,
|
|
66
|
-
/** Number of IDs the server should demand during the handshake. */
|
|
67
|
-
SERVER_DEMAND_COUNT: 3,
|
|
68
|
-
}
|
|
78
|
+
CONNECT_POOL: CORE_SESSION_KEYSTONE_POLICY.CONNECT_POOL
|
|
69
79
|
} as const;
|
|
70
80
|
|
|
71
81
|
|
|
72
82
|
// ---------------------------------------------------------------------------
|
|
73
|
-
//
|
|
83
|
+
// 3. Compatibility Layer: Domain/Identity Keystone (I) Policies
|
|
74
84
|
// ---------------------------------------------------------------------------
|
|
75
85
|
|
|
76
86
|
/**
|
|
77
87
|
* Server-side minimum requirements for long-lived primary identity keystones.
|
|
78
|
-
*
|
|
79
|
-
* [Nag] These are initial placeholder standards and should be reviewed for
|
|
80
|
-
* production hardening.
|
|
81
88
|
*/
|
|
82
89
|
export const DOMAIN_KEYSTONE_SECURITY_STANDARDS = {
|
|
83
90
|
MIN_DEFAULT_POOL_SIZE: 50,
|
|
@@ -89,71 +96,17 @@ export const DOMAIN_KEYSTONE_SECURITY_STANDARDS = {
|
|
|
89
96
|
|
|
90
97
|
|
|
91
98
|
// ---------------------------------------------------------------------------
|
|
92
|
-
//
|
|
99
|
+
// 4. Upfront Picket-Fence Connect Helpers
|
|
93
100
|
// ---------------------------------------------------------------------------
|
|
94
101
|
|
|
95
102
|
/**
|
|
96
103
|
* Returns the lexicographically first challenge from the session keystone's
|
|
97
|
-
* '
|
|
104
|
+
* 'connect' pool to be used as a deterministic, dynamic upfront pre-filter challenge.
|
|
98
105
|
*/
|
|
99
|
-
export
|
|
100
|
-
const handshakePool = (keystone.data?.challengePools ?? []).find(
|
|
101
|
-
(p) => p.id === SESSION_KEYSTONE_POLICY.HANDSHAKE_POOL.ID
|
|
102
|
-
);
|
|
103
|
-
if (!handshakePool) {
|
|
104
|
-
throw new Error(`Keystone missing "${SESSION_KEYSTONE_POLICY.HANDSHAKE_POOL.ID}" pool`);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const challengeIds = Object.keys(handshakePool.challenges).sort();
|
|
108
|
-
if (challengeIds.length === 0) {
|
|
109
|
-
throw new Error(`Handshake pool has no challenges`);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const challengeId = challengeIds[0];
|
|
113
|
-
const challenge = handshakePool.challenges[challengeId];
|
|
114
|
-
return { challengeId, challenge };
|
|
115
|
-
}
|
|
106
|
+
export const getConnectChallenge = coreGetConnectChallenge;
|
|
116
107
|
|
|
117
108
|
/**
|
|
118
109
|
* Cryptographically validates the upfront picket-fence solution against the session keystone.
|
|
119
|
-
* Verifies that the client solved the lexicographically first challenge from the '
|
|
110
|
+
* Verifies that the client solved the lexicographically first challenge from the 'connect' pool.
|
|
120
111
|
*/
|
|
121
|
-
export
|
|
122
|
-
keystone: KeystoneIbGib_V1,
|
|
123
|
-
solutionValue: string
|
|
124
|
-
): Promise<boolean> {
|
|
125
|
-
const handshakePool = (keystone.data?.challengePools ?? []).find(
|
|
126
|
-
(p) => p.id === SESSION_KEYSTONE_POLICY.HANDSHAKE_POOL.ID
|
|
127
|
-
);
|
|
128
|
-
if (!handshakePool) {
|
|
129
|
-
return false;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const { challengeId, challenge } = getHandshakeChallenge(keystone);
|
|
133
|
-
const strategy = KeystoneStrategyFactory.create({ config: handshakePool.config });
|
|
134
|
-
const solution: KeystoneSolution = {
|
|
135
|
-
type: 'hash-reveal-v1',
|
|
136
|
-
challengeId,
|
|
137
|
-
poolId: SESSION_KEYSTONE_POLICY.HANDSHAKE_POOL.ID,
|
|
138
|
-
value: solutionValue
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
return await strategy.validateSolution({ solution, challenge });
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// this is what we had by default inside the sync saga coordinator
|
|
145
|
-
// const primaryPoolConfig: KeystonePoolConfig_HashV1 = {
|
|
146
|
-
// allowedVerbs: [KEYSTONE_VERB_MANAGE],
|
|
147
|
-
// id: SESSION_IDENTITY_KEYSTONE_PRIMARY_POOL_ID,
|
|
148
|
-
// salt: sagaId,
|
|
149
|
-
// behavior: {
|
|
150
|
-
// size: SESSION_IDENTITY_KEYSTONE_CONFIG_SIZE, // Large pool for many signatures
|
|
151
|
-
// replenish: SESSION_IDENTITY_KEYSTONE_CONFIG_REPLENISH_STRATEGY,
|
|
152
|
-
// selectSequentially: SESSION_IDENTITY_KEYSTONE_CONFIG_SEQUENTIAL,
|
|
153
|
-
// selectRandomly: SESSION_IDENTITY_KEYSTONE_CONFIG_RANDOM,
|
|
154
|
-
// targetBindingChars: SESSION_IDENTITY_KEYSTONE_CONFIG_TARGET_BINDING,
|
|
155
|
-
// },
|
|
156
|
-
// type: KeystoneChallengeType.hash_reveal_v1,
|
|
157
|
-
// algo: SESSION_IDENTITY_KEYSTONE_CONFIG_ALGO,
|
|
158
|
-
// rounds: SESSION_IDENTITY_KEYSTONE_CONFIG_ROUNDS,
|
|
159
|
-
// };
|
|
112
|
+
export const checkConnectSolution = coreCheckConnectSolution;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* This is a diagnostic-only endpoint. The goal is to confirm that:
|
|
10
10
|
* 1. Traefik correctly forwards WS upgrade requests to the container
|
|
11
|
-
* 2. The Node.js `upgrade` event fires and we can accept the handshake
|
|
11
|
+
* 2. The Node.js `upgrade` event fires and we can accept the connect handshake
|
|
12
12
|
* 3. The browser client can open a wss:// connection and exchange frames
|
|
13
13
|
*
|
|
14
14
|
* Once WS sync is confirmed end-to-end, this handler can be removed or
|
|
@@ -30,7 +30,8 @@ import { extractErrorMsg } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs
|
|
|
30
30
|
|
|
31
31
|
import { GLOBAL_LOG_A_LOT } from '../../../constants.mjs';
|
|
32
32
|
import {
|
|
33
|
-
encodeTextFrame, decodeTextFrame, encodeCloseFrame,
|
|
33
|
+
encodeTextFrame, decodeTextFrame, encodeCloseFrame, performConnect,
|
|
34
|
+
WebSocketFrameDecoder
|
|
34
35
|
} from '../../ws/ws-helper.mjs';
|
|
35
36
|
import { API_PATH_REGEXES } from '../../../../path-constants.mjs';
|
|
36
37
|
|
|
@@ -63,27 +64,27 @@ export function handleWsEchoUpgrade(req: IncomingMessage, socket: Socket, head:
|
|
|
63
64
|
try {
|
|
64
65
|
if (logalot) { console.log(`${lc_fn} starting...`); }
|
|
65
66
|
|
|
66
|
-
const ok =
|
|
67
|
+
const ok = performConnect(req, socket);
|
|
67
68
|
if (!ok) { return; }
|
|
68
69
|
|
|
69
70
|
// Send the "connected" greeting
|
|
70
71
|
socket.write(encodeTextFrame(JSON.stringify({ status: 'connected', msg: 'ws-echo ready' })));
|
|
71
72
|
|
|
72
73
|
// Echo loop
|
|
74
|
+
const decoder = new WebSocketFrameDecoder();
|
|
73
75
|
socket.on('data', (data: Buffer) => {
|
|
74
76
|
try {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
socket.
|
|
80
|
-
|
|
77
|
+
decoder.addChunk(data);
|
|
78
|
+
let frameText = decoder.nextFrame();
|
|
79
|
+
while (frameText !== null) {
|
|
80
|
+
if (logalot) { console.log(`${lc_fn} echo: ${frameText}`); }
|
|
81
|
+
socket.write(encodeTextFrame(JSON.stringify({ echo: frameText })));
|
|
82
|
+
frameText = decoder.nextFrame();
|
|
81
83
|
}
|
|
82
|
-
if (logalot) { console.log(`${lc_fn} echo: ${text}`); }
|
|
83
|
-
socket.write(encodeTextFrame(JSON.stringify({ echo: text })));
|
|
84
84
|
} catch (error) {
|
|
85
85
|
console.error(`${lc_fn} error in data handler: ${extractErrorMsg(error)}`);
|
|
86
|
-
socket.
|
|
86
|
+
socket.write(encodeCloseFrame(1000));
|
|
87
|
+
socket.end();
|
|
87
88
|
}
|
|
88
89
|
});
|
|
89
90
|
|
|
@@ -10,19 +10,16 @@
|
|
|
10
10
|
* them to the domain metaspace to authorize the sync session.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { extractErrorMsg
|
|
13
|
+
import { extractErrorMsg } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
|
|
14
14
|
import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
|
|
15
|
-
import { validateIbGibIntrinsically } from '@ibgib/ts-gib/dist/V1/validate-helper.mjs';
|
|
16
|
-
import { getGibInfo } from '@ibgib/ts-gib/dist/V1/transforms/transform-helper.mjs';
|
|
17
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/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs';
|
|
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';
|
|
20
20
|
import { ServeGibHandlerWithMetaspaceBase } from '../../handler-base.mjs';
|
|
21
21
|
import { API_PATH_REGEXES } from '../../../../path-constants.mjs';
|
|
22
|
-
import {
|
|
23
|
-
import { KeystoneService_V1 } from '@ibgib/core-gib/dist/keystone/keystone-service-v1.mjs';
|
|
24
|
-
import { KeystoneIbGib_V1 } from '@ibgib/core-gib/dist/keystone/keystone-types.mjs';
|
|
25
|
-
import { IbGibSpaceAny } from '@ibgib/core-gib/dist/witness/space/space-base-v1.mjs';
|
|
22
|
+
import { ParamsWithDomain, RequestContext, ResponseResult } from '../../../types.mjs';
|
|
26
23
|
|
|
27
24
|
const logalot = GLOBAL_LOG_A_LOT || true;
|
|
28
25
|
|
|
@@ -50,8 +47,9 @@ export class KeystoneEvolveHandler extends ServeGibHandlerWithMetaspaceBase<Evol
|
|
|
50
47
|
domainAddr,
|
|
51
48
|
domainInfo: this.getDomainInfo({ domainAddr })
|
|
52
49
|
};
|
|
50
|
+
} else {
|
|
51
|
+
return undefined;
|
|
53
52
|
}
|
|
54
|
-
return undefined;
|
|
55
53
|
}
|
|
56
54
|
|
|
57
55
|
protected override async validateQueryParams({ queryParams }: { queryParams: any; }): Promise<string[]> {
|
|
@@ -82,142 +80,19 @@ export class KeystoneEvolveHandler extends ServeGibHandlerWithMetaspaceBase<Evol
|
|
|
82
80
|
return this.error(400, 'Invalid request. Body must contain keystoneIbGib (E: e234647f79282982c8012fe8015f5826)');
|
|
83
81
|
}
|
|
84
82
|
|
|
85
|
-
const addr = getIbGibAddr({ ibGib: keystoneIbGib });
|
|
86
|
-
|
|
87
|
-
// 1. Intrinsic hash validation
|
|
88
|
-
const intrinsicErrors = await validateIbGibIntrinsically({ ibGib: keystoneIbGib });
|
|
89
|
-
if (intrinsicErrors && intrinsicErrors.length > 0) {
|
|
90
|
-
return this.error(422, 'Intrinsic keystone validation failed. (E: 6efeb756cb0fb6c3187c8448c5035826)', { errors: intrinsicErrors });
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// 2. Verify URL :domainAddr matches the keystone's tjp
|
|
94
|
-
const { tjpGib } = getGibInfo({ ibGibAddr: addr });
|
|
95
|
-
const expectedTjpGib = getGibInfo({ ibGibAddr: domainAddr }).punctiliarHash;
|
|
96
|
-
if (tjpGib !== expectedTjpGib) {
|
|
97
|
-
return this.error(422, `Keystone tjpGib (${tjpGib}) does not match URL domainAddr tjpGib (${expectedTjpGib}) (E: 703d3c64b7382f21f863c1e83533c826)`);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// 3. Load previous frame (the current tip in metaspace)
|
|
101
83
|
const space = await reqCtx.metaspace.getLocalUserSpace({ lock: false });
|
|
102
84
|
if (!space) {
|
|
103
85
|
return this.error(500, 'No local user space found (E: a1144e5d6cd9641fb82d6b359f131126)');
|
|
104
86
|
}
|
|
105
|
-
const keystoneService = new KeystoneService_V1();
|
|
106
|
-
let prevIbGib: KeystoneIbGib_V1;
|
|
107
|
-
try {
|
|
108
|
-
prevIbGib = await keystoneService.getLatestKeystone({
|
|
109
|
-
addr: domainAddr,
|
|
110
|
-
metaspace: reqCtx.metaspace,
|
|
111
|
-
space
|
|
112
|
-
});
|
|
113
|
-
} catch (error) {
|
|
114
|
-
return this.error(422, `Could not retrieve previous keystone tip for ${domainAddr} (E: bc2da8fb4dbda2c65851de9dd0b0eb26)`, { error: extractErrorMsg(error) });
|
|
115
|
-
}
|
|
116
87
|
|
|
117
|
-
//
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
88
|
+
// Delegate all intrinsic checks, transition rules, pool restrictions, and registration/persistence to core-gib helper
|
|
89
|
+
await validateAndRegisterEvolveKeystone({
|
|
90
|
+
domainAddr,
|
|
91
|
+
keystoneIbGib,
|
|
92
|
+
relatedIbGibs,
|
|
93
|
+
metaspace: reqCtx.metaspace,
|
|
94
|
+
space
|
|
121
95
|
});
|
|
122
|
-
if (validationErrors && validationErrors.length > 0) {
|
|
123
|
-
return this.error(422, 'Keystone evolution validation failed', { errors: validationErrors });
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// 5. Check Single Pool Restriction
|
|
127
|
-
const restrictionError = this.validateSinglePoolRestriction(keystoneIbGib);
|
|
128
|
-
if (restrictionError) {
|
|
129
|
-
return this.error(422, restrictionError); /* <<<< returns early */
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// 6. Route by claim verb
|
|
133
|
-
const proofs = keystoneIbGib.data?.proofs ?? [];
|
|
134
|
-
if (proofs.length !== 1) { throw new Error(`(UNEXPECTED) evolving keystone with proofs.length !== 1? we just checked that the length of proofs was exactly 1 in checkSinglePoolRestriction (E: 3021bb0ce9783e27c8e3492820aeb926)`); }
|
|
135
|
-
const { claim } = proofs[0];
|
|
136
|
-
if (!claim) { throw new Error(`proof.claim is falsy (E: e1dd983db341185528a75768919ea826)`); }
|
|
137
|
-
const { verb } = claim;
|
|
138
|
-
if (!verb) { throw new Error(`(UNEXPECTED) claim.verb falsy? atow (05/15/2026) I thought all claims should have a verb. I could be wrong of course as it's still early days. (E: 933a9856d6625b547d7ec158d678a826)`); }
|
|
139
|
-
|
|
140
|
-
switch (verb) {
|
|
141
|
-
case 'sync':
|
|
142
|
-
return await this.handleSyncEvolution(reqCtx, keystoneIbGib, relatedIbGibs, space);
|
|
143
|
-
default:
|
|
144
|
-
throw new Error(`non-sync keystone evolution not yet implemented (E: 83cb29bf67d681542bfd486d0f91e726)`);
|
|
145
|
-
// return await this.handleDefaultEvolution(reqCtx, keystoneIbGib, space);
|
|
146
|
-
}
|
|
147
|
-
} catch (error) {
|
|
148
|
-
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
149
|
-
return this.error(500, `Internal error evolving keystone`, { details: extractErrorMsg(error) });
|
|
150
|
-
} finally {
|
|
151
|
-
if (logalot) { console.log(`${lc} complete.`); }
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
protected validateSinglePoolRestriction(keystoneIbGib: KeystoneIbGib_V1): string | undefined {
|
|
156
|
-
const proofs = keystoneIbGib.data?.proofs ?? [];
|
|
157
|
-
if (proofs.length === 0) {
|
|
158
|
-
return 'No proofs found in evolved keystone data';
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const poolIds = new Set<string>();
|
|
162
|
-
for (const proof of proofs) {
|
|
163
|
-
for (const solution of proof.solutions || []) {
|
|
164
|
-
if (solution.poolId) {
|
|
165
|
-
poolIds.add(solution.poolId);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (poolIds.size > 1) {
|
|
171
|
-
return `Multiple pools evolved (${Array.from(poolIds).join(', ')}). Only one pool evolution allowed at a time.`;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
return undefined; // no error
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
protected async handleSyncEvolution(
|
|
178
|
-
reqCtx: RequestContext<EvolveParams>,
|
|
179
|
-
keystoneIbGib: KeystoneIbGib_V1,
|
|
180
|
-
relatedIbGibs: IbGib_V1[],
|
|
181
|
-
space: IbGibSpaceAny
|
|
182
|
-
): Promise<ResponseResult> {
|
|
183
|
-
const lc = `${this.lc}[${this.handleSyncEvolution.name}]`;
|
|
184
|
-
try {
|
|
185
|
-
if (logalot) { console.log(`${lc} starting... (I: 80cf7ba10a781fad87171b747de10826)`); }
|
|
186
|
-
|
|
187
|
-
if (logalot) { console.log(`${lc} starting...`); }
|
|
188
|
-
|
|
189
|
-
const proofs = keystoneIbGib.data?.proofs ?? [];
|
|
190
|
-
if (proofs.length !== 1) { throw new Error(`(UNEXPECTED) proofs.length !== 1? (E: ac9748a187f87455bc01fdeeb4363826)`); }
|
|
191
|
-
const claim = proofs[0].claim;
|
|
192
|
-
const sAddr = claim?.target;
|
|
193
|
-
if (!sAddr) {
|
|
194
|
-
return this.error(422, 'No target address found in sync claim');
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Validate S^Stjp (session keystone ibgib) from relatedIbGibs
|
|
198
|
-
const sIbGib = relatedIbGibs.find(ibGib => getIbGibAddr({ ibGib }) === sAddr) as KeystoneIbGib_V1 | undefined;
|
|
199
|
-
if (!sIbGib) {
|
|
200
|
-
return this.error(422, `Session keystone ${sAddr} must be provided in relatedIbGibs`);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const sIntrinsicErrors = await validateIbGibIntrinsically({ ibGib: sIbGib });
|
|
204
|
-
if (sIntrinsicErrors && sIntrinsicErrors.length > 0) {
|
|
205
|
-
return this.error(422, 'Intrinsic validation failed for S^Stjp', { errors: sIntrinsicErrors });
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
const keystoneService = new KeystoneService_V1();
|
|
209
|
-
const sGenesisErrors = await keystoneService.validateGenesisKeystone({ keystoneIbGib: sIbGib });
|
|
210
|
-
if (sGenesisErrors && sGenesisErrors.length > 0) {
|
|
211
|
-
return this.error(422, 'Genesis validation failed for S^Stjp', { errors: sGenesisErrors });
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Persist both I and S
|
|
215
|
-
await reqCtx.metaspace!.put({ ibGibs: [keystoneIbGib, sIbGib], space });
|
|
216
|
-
await reqCtx.metaspace!.registerNewIbGib({ ibGib: keystoneIbGib, space });
|
|
217
|
-
await reqCtx.metaspace!.registerNewIbGib({ ibGib: sIbGib, space });
|
|
218
|
-
|
|
219
|
-
const addr = getIbGibAddr({ ibGib: keystoneIbGib });
|
|
220
|
-
if (logalot) { console.log(`${lc} domain evolution stored and registered: ${addr}`); }
|
|
221
96
|
|
|
222
97
|
return this.ok({
|
|
223
98
|
message: `Successfully evolved domain keystone with session keystone`,
|
|
@@ -225,37 +100,9 @@ export class KeystoneEvolveHandler extends ServeGibHandlerWithMetaspaceBase<Evol
|
|
|
225
100
|
|
|
226
101
|
} catch (error) {
|
|
227
102
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
228
|
-
|
|
103
|
+
return this.error(422, `Evolve validation/persistence failed: ${extractErrorMsg(error)}`);
|
|
229
104
|
} finally {
|
|
230
105
|
if (logalot) { console.log(`${lc} complete.`); }
|
|
231
106
|
}
|
|
232
107
|
}
|
|
233
|
-
|
|
234
|
-
protected async handleDefaultEvolution(
|
|
235
|
-
reqCtx: RequestContext<EvolveParams>,
|
|
236
|
-
keystoneIbGib: KeystoneIbGib_V1,
|
|
237
|
-
space: IbGibSpaceAny
|
|
238
|
-
): Promise<ResponseResult> {
|
|
239
|
-
const lc = `${this.lc}[${this.handleDefaultEvolution.name}]`;
|
|
240
|
-
try {
|
|
241
|
-
if (logalot) { console.log(`${lc} starting... (I: 8b755b4087b16c0d760d3ca397fad726)`); }
|
|
242
|
-
|
|
243
|
-
// Persist only I
|
|
244
|
-
await reqCtx.metaspace!.put({ ibGibs: [keystoneIbGib], space });
|
|
245
|
-
await reqCtx.metaspace!.registerNewIbGib({ ibGib: keystoneIbGib, space });
|
|
246
|
-
|
|
247
|
-
const addr = getIbGibAddr({ ibGib: keystoneIbGib });
|
|
248
|
-
if (logalot) { console.log(`${lc} domain evolution stored and registered: ${addr} (I: 990d686a19d645afaa624e386caac826)`); }
|
|
249
|
-
|
|
250
|
-
return this.ok({
|
|
251
|
-
message: `Successfully evolved domain keystone. (I: 4cd058a08a171069982a7f3808cbe826)`,
|
|
252
|
-
});
|
|
253
|
-
} catch (error) {
|
|
254
|
-
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
255
|
-
throw error;
|
|
256
|
-
} finally {
|
|
257
|
-
if (logalot) { console.log(`${lc} complete.`); }
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
108
|
}
|
|
@@ -74,12 +74,12 @@ export class KeystoneGenesisHandler extends ServeGibHandlerBase<undefined, undef
|
|
|
74
74
|
try {
|
|
75
75
|
parsed = JSON.parse(reqCtx.body);
|
|
76
76
|
} catch {
|
|
77
|
-
return this.error(400, 'Request body must be valid JSON');
|
|
77
|
+
return this.error(400, 'Request body must be valid JSON (E: dd8618857568f8fc5e9c62b8ed135826)');
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
const { keystoneIbGib } = parsed;
|
|
81
81
|
if (!keystoneIbGib) {
|
|
82
|
-
return this.error(400, 'Body must contain keystoneIbGib');
|
|
82
|
+
return this.error(400, 'Body must contain keystoneIbGib (E: 9c8c5d979d7842cd716ba64f69349c26)');
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
// ----------------------------------------------------------------
|
|
@@ -87,7 +87,7 @@ export class KeystoneGenesisHandler extends ServeGibHandlerBase<undefined, undef
|
|
|
87
87
|
// ----------------------------------------------------------------
|
|
88
88
|
const intrinsicErrors = await validateIbGibIntrinsically({ ibGib: keystoneIbGib });
|
|
89
89
|
if (intrinsicErrors) {
|
|
90
|
-
return this.error(422, 'Intrinsic keystone validation failed', { errors: intrinsicErrors });
|
|
90
|
+
return this.error(422, 'Intrinsic keystone validation failed (E: 76d388e198f23a87dd6596f59c36e826)', { errors: intrinsicErrors });
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
// ----------------------------------------------------------------
|
|
@@ -98,7 +98,7 @@ export class KeystoneGenesisHandler extends ServeGibHandlerBase<undefined, undef
|
|
|
98
98
|
keystoneIbGib
|
|
99
99
|
});
|
|
100
100
|
if (keystoneErrors && keystoneErrors.length > 0) {
|
|
101
|
-
return this.error(422,
|
|
101
|
+
return this.error(422, `Keystone genesis validation failed. Errors: ${keystoneErrors} (E: 8384285be7a8ab0d41cfd1df2e2aa826)`, { errors: keystoneErrors });
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
// ----------------------------------------------------------------
|
|
@@ -108,12 +108,12 @@ export class KeystoneGenesisHandler extends ServeGibHandlerBase<undefined, undef
|
|
|
108
108
|
const { tjpGib, punctiliarHash } = getGibInfo({ ibGibAddr: addr });
|
|
109
109
|
const domainGib = tjpGib || punctiliarHash;
|
|
110
110
|
if (!domainGib) {
|
|
111
|
-
return this.error(422, `Could not extract domain identifier from keystone address: ${addr}`);
|
|
111
|
+
return this.error(422, `Could not extract domain identifier from keystone address: ${addr} (E: 2758588a6d7cc77f078e97989feed326)`);
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
const domainRootPath = getDomainRootPath(domainGib, reqCtx.dataDir);
|
|
115
115
|
if (existsSync(domainRootPath)) {
|
|
116
|
-
return this.error(409, `Domain already exists for this keystone (tjpGib: ${domainGib}). Use PUT /api/keystone/evolve/:domainAddr to evolve
|
|
116
|
+
return this.error(409, `Domain already exists for this keystone (tjpGib: ${domainGib}). Use PUT /api/keystone/evolve/:domainAddr to evolve. (E: 1c56c8885781c86df89cbdfc1b310626)`);
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
// ----------------------------------------------------------------
|