@wipcomputer/wip-ldm-os 0.4.85-alpha.10 → 0.4.85-alpha.11
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/package.json
CHANGED
|
@@ -1,80 +1,146 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
|
+
import {
|
|
3
|
+
buildCodexBootstrapPayload,
|
|
4
|
+
createCodexDaemonPubkeyRegistry,
|
|
5
|
+
} from "../src/hosted-mcp/codex-relay-e2ee-registry.mjs";
|
|
2
6
|
|
|
3
7
|
const server = readFileSync("src/hosted-mcp/server.mjs", "utf8");
|
|
8
|
+
const registrySource = readFileSync("src/hosted-mcp/codex-relay-e2ee-registry.mjs", "utf8");
|
|
4
9
|
|
|
5
|
-
function assertContains(needle, label) {
|
|
6
|
-
if (!
|
|
10
|
+
function assertContains(haystack, needle, label) {
|
|
11
|
+
if (!haystack.includes(needle)) {
|
|
7
12
|
throw new Error(`${label} missing expected text: ${needle}`);
|
|
8
13
|
}
|
|
9
14
|
}
|
|
10
15
|
|
|
11
|
-
function assertBefore(first, second, label) {
|
|
12
|
-
const firstIndex =
|
|
13
|
-
const secondIndex =
|
|
16
|
+
function assertBefore(haystack, first, second, label) {
|
|
17
|
+
const firstIndex = haystack.indexOf(first);
|
|
18
|
+
const secondIndex = haystack.indexOf(second);
|
|
14
19
|
if (firstIndex === -1 || secondIndex === -1 || firstIndex >= secondIndex) {
|
|
15
20
|
throw new Error(`${label} expected "${first}" before "${second}"`);
|
|
16
21
|
}
|
|
17
22
|
}
|
|
18
23
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
24
|
+
function assert(condition, label, detail = "") {
|
|
25
|
+
if (!condition) throw new Error(`${label}${detail ? ": " + detail : ""}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function bootstrapPayloadFor(registry, identity, threadId, daemonOnline = true) {
|
|
29
|
+
return buildCodexBootstrapPayload({
|
|
30
|
+
identity,
|
|
31
|
+
threadId,
|
|
32
|
+
daemonOnline,
|
|
33
|
+
daemonKey: registry.get(identity.agentId),
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function createFakePrisma() {
|
|
38
|
+
const rows = new Map();
|
|
39
|
+
return {
|
|
40
|
+
rows,
|
|
41
|
+
async $executeRawUnsafe(sql, tenantId, pubkey, cryptoVersionsJson) {
|
|
42
|
+
if (/CREATE TABLE IF NOT EXISTS codex_daemon_e2ee_keys/.test(sql)) return;
|
|
43
|
+
if (/INSERT INTO codex_daemon_e2ee_keys/.test(sql)) {
|
|
44
|
+
rows.set(tenantId, {
|
|
45
|
+
tenant_id: tenantId,
|
|
46
|
+
pubkey,
|
|
47
|
+
crypto_versions_json: cryptoVersionsJson,
|
|
48
|
+
registered_at: new Date("2026-05-11T17:37:18.000Z"),
|
|
49
|
+
});
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
throw new Error("unexpected fake prisma execute: " + sql);
|
|
53
|
+
},
|
|
54
|
+
async $queryRawUnsafe(sql) {
|
|
55
|
+
if (/FROM codex_daemon_e2ee_keys/.test(sql)) return [...rows.values()];
|
|
56
|
+
throw new Error("unexpected fake prisma query: " + sql);
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function createSilentLogger() {
|
|
62
|
+
return { log() {}, error() {} };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
assertContains(registrySource, "CREATE TABLE IF NOT EXISTS codex_daemon_e2ee_keys", "persistent key table");
|
|
66
|
+
assertContains(registrySource, "async function loadFromDb()", "boot load helper");
|
|
67
|
+
assertContains(registrySource, "async function persist(agentId, pubkey, cryptoVersions)", "persist helper");
|
|
68
|
+
assertContains(registrySource, "function register(agentId, pubkey, cryptoVersions, source)", "registration helper");
|
|
69
|
+
assertContains(registrySource, "pubkeys.set(agentId, {", "registration updates in-memory bootstrap cache");
|
|
70
|
+
assertContains(registrySource, "return persist(agentId, pubkey, normalizedVersions)", "registration persists after cache update");
|
|
71
|
+
assertContains(server, "await codexDaemonPubkeyRegistry.loadFromDb();", "server boot load call");
|
|
72
|
+
assertContains(server, "await codexDaemonPubkeyRegistry.register(identity.agentId, p.daemon_public_key, p.crypto_versions, \"pair-complete\");", "pair-complete persists key");
|
|
73
|
+
assertContains(server, "if (envelope?.type === \"daemon.identity\") {", "daemon reconnect identity frame");
|
|
74
|
+
assertContains(server, "codexDaemonPubkeyRegistry.register(", "daemon reconnect register call");
|
|
75
|
+
assertContains(server, "buildCodexBootstrapPayload({ identity, threadId, daemonOnline, daemonKey })", "bootstrap uses shared payload builder");
|
|
32
76
|
assertBefore(
|
|
33
|
-
|
|
77
|
+
server,
|
|
78
|
+
"await codexDaemonPubkeyRegistry.loadFromDb();",
|
|
34
79
|
"function handleCodexBootstrap(req, res, threadId)",
|
|
35
80
|
"persisted keys load before bootstrap handler",
|
|
36
81
|
);
|
|
37
82
|
|
|
38
|
-
const
|
|
39
|
-
|
|
83
|
+
const identity = {
|
|
84
|
+
agentId: "acct:test-user-a",
|
|
85
|
+
tenantId: "acct:test-user-a",
|
|
86
|
+
handle: "Parker smoke test",
|
|
87
|
+
apiKey: "ck-test",
|
|
88
|
+
};
|
|
89
|
+
const threadId = "019dfa1e-0c3d-7f01-86b9-9a22cd452bde";
|
|
40
90
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
91
|
+
const fakePrisma = createFakePrisma();
|
|
92
|
+
const registryBeforeRestart = createCodexDaemonPubkeyRegistry({
|
|
93
|
+
usePrisma: true,
|
|
94
|
+
prisma: fakePrisma,
|
|
95
|
+
devMode: false,
|
|
96
|
+
logger: createSilentLogger(),
|
|
97
|
+
});
|
|
98
|
+
await registryBeforeRestart.register(identity.agentId, "spki-key-before-restart", ["e2ee-v1"], "pair-complete");
|
|
47
99
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
persistedRows.set(agentId, { pubkey, crypto_versions_json: JSON.stringify(normalized) });
|
|
52
|
-
}
|
|
100
|
+
const beforeRestartBootstrap = bootstrapPayloadFor(registryBeforeRestart, identity, threadId);
|
|
101
|
+
assert(beforeRestartBootstrap.e2ee_available === true, "bootstrap reports e2ee before restart");
|
|
102
|
+
assert(beforeRestartBootstrap.daemon_public_key === "spki-key-before-restart", "bootstrap returns registered daemon key before restart");
|
|
53
103
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
return restored;
|
|
63
|
-
}
|
|
104
|
+
const registryAfterRestart = createCodexDaemonPubkeyRegistry({
|
|
105
|
+
usePrisma: true,
|
|
106
|
+
prisma: fakePrisma,
|
|
107
|
+
devMode: false,
|
|
108
|
+
logger: createSilentLogger(),
|
|
109
|
+
});
|
|
110
|
+
await registryAfterRestart.loadFromDb();
|
|
64
111
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
112
|
+
const afterRestartBootstrap = bootstrapPayloadFor(registryAfterRestart, identity, threadId);
|
|
113
|
+
assert(afterRestartBootstrap.e2ee_available === true, "bootstrap reports e2ee after restart from persisted key");
|
|
114
|
+
assert(afterRestartBootstrap.daemon_public_key === "spki-key-before-restart", "bootstrap restores persisted daemon key after restart");
|
|
115
|
+
assert(afterRestartBootstrap.daemon_crypto_versions?.[0] === "e2ee-v1", "bootstrap restores crypto versions after restart");
|
|
70
116
|
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
117
|
+
const emptyFakePrisma = createFakePrisma();
|
|
118
|
+
const registryBeforeReconnect = createCodexDaemonPubkeyRegistry({
|
|
119
|
+
usePrisma: true,
|
|
120
|
+
prisma: emptyFakePrisma,
|
|
121
|
+
devMode: false,
|
|
122
|
+
logger: createSilentLogger(),
|
|
123
|
+
});
|
|
124
|
+
await registryBeforeReconnect.loadFromDb();
|
|
125
|
+
const beforeReconnectBootstrap = bootstrapPayloadFor(registryBeforeReconnect, identity, threadId);
|
|
126
|
+
assert(beforeReconnectBootstrap.e2ee_available === false, "bootstrap is not e2ee available before daemon reconnect when no key exists");
|
|
127
|
+
assert(beforeReconnectBootstrap.daemon_public_key === null, "bootstrap has no daemon key before daemon reconnect");
|
|
128
|
+
|
|
129
|
+
await registryBeforeReconnect.register(identity.agentId, "spki-key-from-daemon-reconnect", [], "daemon-reconnect");
|
|
130
|
+
const afterReconnectBootstrap = bootstrapPayloadFor(registryBeforeReconnect, identity, threadId);
|
|
131
|
+
assert(afterReconnectBootstrap.e2ee_available === true, "bootstrap reports e2ee after daemon reconnect self-heal");
|
|
132
|
+
assert(afterReconnectBootstrap.daemon_public_key === "spki-key-from-daemon-reconnect", "bootstrap returns daemon reconnect key");
|
|
133
|
+
assert(afterReconnectBootstrap.daemon_crypto_versions?.[0] === "e2ee-v1", "daemon reconnect defaults crypto version");
|
|
134
|
+
|
|
135
|
+
const registryAfterReconnectRestart = createCodexDaemonPubkeyRegistry({
|
|
136
|
+
usePrisma: true,
|
|
137
|
+
prisma: emptyFakePrisma,
|
|
138
|
+
devMode: false,
|
|
139
|
+
logger: createSilentLogger(),
|
|
140
|
+
});
|
|
141
|
+
await registryAfterReconnectRestart.loadFromDb();
|
|
142
|
+
const afterReconnectRestartBootstrap = bootstrapPayloadFor(registryAfterReconnectRestart, identity, threadId);
|
|
143
|
+
assert(afterReconnectRestartBootstrap.e2ee_available === true, "daemon reconnect key is persisted for the next restart");
|
|
144
|
+
assert(afterReconnectRestartBootstrap.daemon_public_key === "spki-key-from-daemon-reconnect", "daemon reconnect key survives restart");
|
|
79
145
|
|
|
80
|
-
console.log("crc e2ee key persistence checks passed");
|
|
146
|
+
console.log("crc e2ee key persistence restart regression checks passed");
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
export function normalizeCodexCryptoVersions(versions) {
|
|
2
|
+
const out = Array.isArray(versions)
|
|
3
|
+
? versions.filter((v) => typeof v === "string" && v.length > 0 && v.length <= 32).slice(0, 8)
|
|
4
|
+
: [];
|
|
5
|
+
return out.length ? out : ["e2ee-v1"];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function buildCodexBootstrapPayload({ identity, threadId, daemonOnline, daemonKey }) {
|
|
9
|
+
return {
|
|
10
|
+
handle: identity.handle,
|
|
11
|
+
thread_id: threadId,
|
|
12
|
+
daemon_online: daemonOnline,
|
|
13
|
+
daemon_public_key: daemonKey ? daemonKey.pubkey : null,
|
|
14
|
+
daemon_crypto_versions: daemonKey ? daemonKey.crypto_versions : null,
|
|
15
|
+
supported_crypto_versions: ["e2ee-v1"],
|
|
16
|
+
e2ee_available: !!daemonKey,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function createCodexDaemonPubkeyRegistry({
|
|
21
|
+
usePrisma,
|
|
22
|
+
prisma,
|
|
23
|
+
devMode = false,
|
|
24
|
+
logger = console,
|
|
25
|
+
} = {}) {
|
|
26
|
+
const pubkeys = new Map();
|
|
27
|
+
|
|
28
|
+
async function ensureStore() {
|
|
29
|
+
if (!usePrisma) return;
|
|
30
|
+
await prisma.$executeRawUnsafe(`
|
|
31
|
+
CREATE TABLE IF NOT EXISTS codex_daemon_e2ee_keys (
|
|
32
|
+
tenant_id TEXT PRIMARY KEY,
|
|
33
|
+
pubkey TEXT NOT NULL,
|
|
34
|
+
crypto_versions_json TEXT NOT NULL,
|
|
35
|
+
registered_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
36
|
+
)
|
|
37
|
+
`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function loadFromDb() {
|
|
41
|
+
if (!usePrisma) return;
|
|
42
|
+
try {
|
|
43
|
+
await ensureStore();
|
|
44
|
+
const rows = await prisma.$queryRawUnsafe(`
|
|
45
|
+
SELECT tenant_id, pubkey, crypto_versions_json, registered_at
|
|
46
|
+
FROM codex_daemon_e2ee_keys
|
|
47
|
+
`);
|
|
48
|
+
for (const row of rows) {
|
|
49
|
+
let cryptoVersions = ["e2ee-v1"];
|
|
50
|
+
try { cryptoVersions = normalizeCodexCryptoVersions(JSON.parse(row.crypto_versions_json)); } catch {}
|
|
51
|
+
pubkeys.set(row.tenant_id, {
|
|
52
|
+
pubkey: row.pubkey,
|
|
53
|
+
crypto_versions: cryptoVersions,
|
|
54
|
+
registered_at: row.registered_at instanceof Date ? row.registered_at.toISOString() : String(row.registered_at),
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
logger.log("codex-relay: loaded " + rows.length + " persisted E2EE daemon pubkey(s)");
|
|
58
|
+
} catch (err) {
|
|
59
|
+
logger.error("codex-relay: failed to load persisted E2EE daemon pubkeys:", err.message);
|
|
60
|
+
if (!devMode) process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function persist(agentId, pubkey, cryptoVersions) {
|
|
65
|
+
if (!usePrisma) return;
|
|
66
|
+
await ensureStore();
|
|
67
|
+
await prisma.$executeRawUnsafe(
|
|
68
|
+
`INSERT INTO codex_daemon_e2ee_keys
|
|
69
|
+
(tenant_id, pubkey, crypto_versions_json, registered_at)
|
|
70
|
+
VALUES ($1, $2, $3, now())
|
|
71
|
+
ON CONFLICT (tenant_id)
|
|
72
|
+
DO UPDATE SET
|
|
73
|
+
pubkey = EXCLUDED.pubkey,
|
|
74
|
+
crypto_versions_json = EXCLUDED.crypto_versions_json,
|
|
75
|
+
registered_at = EXCLUDED.registered_at`,
|
|
76
|
+
agentId,
|
|
77
|
+
pubkey,
|
|
78
|
+
JSON.stringify(cryptoVersions),
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function register(agentId, pubkey, cryptoVersions, source) {
|
|
83
|
+
if (typeof agentId !== "string" || !agentId) return Promise.resolve(false);
|
|
84
|
+
if (typeof pubkey !== "string" || !pubkey || pubkey.length > 1024) return Promise.resolve(false);
|
|
85
|
+
const normalizedVersions = normalizeCodexCryptoVersions(cryptoVersions);
|
|
86
|
+
const registeredAt = new Date().toISOString();
|
|
87
|
+
pubkeys.set(agentId, {
|
|
88
|
+
pubkey,
|
|
89
|
+
crypto_versions: normalizedVersions,
|
|
90
|
+
registered_at: registeredAt,
|
|
91
|
+
});
|
|
92
|
+
logger.log("codex-relay: registered E2EE pubkey for " + agentId + " via " + source);
|
|
93
|
+
return persist(agentId, pubkey, normalizedVersions)
|
|
94
|
+
.then(() => true)
|
|
95
|
+
.catch((err) => {
|
|
96
|
+
logger.error("codex-relay: failed to persist E2EE pubkey for " + agentId + ":", err.message);
|
|
97
|
+
if (!devMode) throw err;
|
|
98
|
+
return false;
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
pubkeys,
|
|
104
|
+
ensureStore,
|
|
105
|
+
loadFromDb,
|
|
106
|
+
register,
|
|
107
|
+
get(agentId) {
|
|
108
|
+
return pubkeys.get(agentId) || null;
|
|
109
|
+
},
|
|
110
|
+
clearMemoryForTest() {
|
|
111
|
+
pubkeys.clear();
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
@@ -23,6 +23,10 @@ import {
|
|
|
23
23
|
import QRCode from "qrcode";
|
|
24
24
|
import { WebSocketServer } from "ws";
|
|
25
25
|
import { parse as parseUrlQs } from "node:querystring";
|
|
26
|
+
import {
|
|
27
|
+
buildCodexBootstrapPayload,
|
|
28
|
+
createCodexDaemonPubkeyRegistry,
|
|
29
|
+
} from "./codex-relay-e2ee-registry.mjs";
|
|
26
30
|
|
|
27
31
|
// ── Settings ─────────────────────────────────────────────────────────
|
|
28
32
|
|
|
@@ -2607,7 +2611,7 @@ const codexE2eeSessionRoutes = new Map(); // `${agentId}:${e2eeSession}` -> { th
|
|
|
2607
2611
|
|
|
2608
2612
|
// E2EE substrate (Phase 2.5).
|
|
2609
2613
|
//
|
|
2610
|
-
//
|
|
2614
|
+
// codexDaemonPubkeyRegistry: per tenant id, the most recently paired daemon's
|
|
2611
2615
|
// public key (P-256 SPKI base64url) + supported crypto versions +
|
|
2612
2616
|
// registration timestamp. This is what the browser fetches via
|
|
2613
2617
|
// bootstrap before opening an encrypted session.
|
|
@@ -2616,92 +2620,17 @@ const codexE2eeSessionRoutes = new Map(); // `${agentId}:${e2eeSession}` -> { th
|
|
|
2616
2620
|
// ?token=ck-... in the browser WebSocket URL. Bound to a specific
|
|
2617
2621
|
// (agentId, threadId) so a leaked ticket cannot drive a different
|
|
2618
2622
|
// route, even by the same authenticated user.
|
|
2619
|
-
const codexDaemonPubkeys = new Map(); // tenantId -> { pubkey, crypto_versions, registered_at }
|
|
2620
2623
|
const codexRelayTickets = new Map(); // ticket -> { agentId, threadId, expires, used }
|
|
2621
2624
|
const CODEX_RELAY_TICKET_TTL_MS = 60 * 1000; // 60s; browser must connect immediately
|
|
2622
2625
|
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
crypto_versions_json TEXT NOT NULL,
|
|
2630
|
-
registered_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
2631
|
-
)
|
|
2632
|
-
`);
|
|
2633
|
-
}
|
|
2634
|
-
|
|
2635
|
-
function normalizeCodexCryptoVersions(versions) {
|
|
2636
|
-
const out = Array.isArray(versions)
|
|
2637
|
-
? versions.filter((v) => typeof v === "string" && v.length > 0 && v.length <= 32).slice(0, 8)
|
|
2638
|
-
: [];
|
|
2639
|
-
return out.length ? out : ["e2ee-v1"];
|
|
2640
|
-
}
|
|
2641
|
-
|
|
2642
|
-
async function loadCodexDaemonPubkeysFromDb() {
|
|
2643
|
-
if (!usePrisma) return;
|
|
2644
|
-
try {
|
|
2645
|
-
await ensureCodexDaemonPubkeyStore();
|
|
2646
|
-
const rows = await prisma.$queryRawUnsafe(`
|
|
2647
|
-
SELECT tenant_id, pubkey, crypto_versions_json, registered_at
|
|
2648
|
-
FROM codex_daemon_e2ee_keys
|
|
2649
|
-
`);
|
|
2650
|
-
for (const row of rows) {
|
|
2651
|
-
let cryptoVersions = ["e2ee-v1"];
|
|
2652
|
-
try { cryptoVersions = normalizeCodexCryptoVersions(JSON.parse(row.crypto_versions_json)); } catch {}
|
|
2653
|
-
codexDaemonPubkeys.set(row.tenant_id, {
|
|
2654
|
-
pubkey: row.pubkey,
|
|
2655
|
-
crypto_versions: cryptoVersions,
|
|
2656
|
-
registered_at: row.registered_at instanceof Date ? row.registered_at.toISOString() : String(row.registered_at),
|
|
2657
|
-
});
|
|
2658
|
-
}
|
|
2659
|
-
console.log("codex-relay: loaded " + rows.length + " persisted E2EE daemon pubkey(s)");
|
|
2660
|
-
} catch (err) {
|
|
2661
|
-
console.error("codex-relay: failed to load persisted E2EE daemon pubkeys:", err.message);
|
|
2662
|
-
if (!DEV_MODE) process.exit(1);
|
|
2663
|
-
}
|
|
2664
|
-
}
|
|
2665
|
-
|
|
2666
|
-
async function persistCodexDaemonPubkey(agentId, pubkey, cryptoVersions) {
|
|
2667
|
-
if (!usePrisma) return;
|
|
2668
|
-
await ensureCodexDaemonPubkeyStore();
|
|
2669
|
-
await prisma.$executeRawUnsafe(
|
|
2670
|
-
`INSERT INTO codex_daemon_e2ee_keys
|
|
2671
|
-
(tenant_id, pubkey, crypto_versions_json, registered_at)
|
|
2672
|
-
VALUES ($1, $2, $3, now())
|
|
2673
|
-
ON CONFLICT (tenant_id)
|
|
2674
|
-
DO UPDATE SET
|
|
2675
|
-
pubkey = EXCLUDED.pubkey,
|
|
2676
|
-
crypto_versions_json = EXCLUDED.crypto_versions_json,
|
|
2677
|
-
registered_at = EXCLUDED.registered_at`,
|
|
2678
|
-
agentId,
|
|
2679
|
-
pubkey,
|
|
2680
|
-
JSON.stringify(cryptoVersions),
|
|
2681
|
-
);
|
|
2682
|
-
}
|
|
2683
|
-
|
|
2684
|
-
function registerCodexDaemonPubkey(agentId, pubkey, cryptoVersions, source) {
|
|
2685
|
-
if (typeof agentId !== "string" || !agentId) return Promise.resolve(false);
|
|
2686
|
-
if (typeof pubkey !== "string" || !pubkey || pubkey.length > 1024) return Promise.resolve(false);
|
|
2687
|
-
const normalizedVersions = normalizeCodexCryptoVersions(cryptoVersions);
|
|
2688
|
-
const registeredAt = new Date().toISOString();
|
|
2689
|
-
codexDaemonPubkeys.set(agentId, {
|
|
2690
|
-
pubkey,
|
|
2691
|
-
crypto_versions: normalizedVersions,
|
|
2692
|
-
registered_at: registeredAt,
|
|
2693
|
-
});
|
|
2694
|
-
console.log("codex-relay: registered E2EE pubkey for " + agentId + " via " + source);
|
|
2695
|
-
return persistCodexDaemonPubkey(agentId, pubkey, normalizedVersions)
|
|
2696
|
-
.then(() => true)
|
|
2697
|
-
.catch((err) => {
|
|
2698
|
-
console.error("codex-relay: failed to persist E2EE pubkey for " + agentId + ":", err.message);
|
|
2699
|
-
if (!DEV_MODE) throw err;
|
|
2700
|
-
return false;
|
|
2701
|
-
});
|
|
2702
|
-
}
|
|
2626
|
+
const codexDaemonPubkeyRegistry = createCodexDaemonPubkeyRegistry({
|
|
2627
|
+
usePrisma,
|
|
2628
|
+
prisma,
|
|
2629
|
+
devMode: DEV_MODE,
|
|
2630
|
+
logger: console,
|
|
2631
|
+
});
|
|
2703
2632
|
|
|
2704
|
-
await
|
|
2633
|
+
await codexDaemonPubkeyRegistry.loadFromDb();
|
|
2705
2634
|
|
|
2706
2635
|
function codexRelayKey(agentId, id) {
|
|
2707
2636
|
return agentId + ":" + id;
|
|
@@ -2876,7 +2805,7 @@ async function handleCodexPairComplete(req, res) {
|
|
|
2876
2805
|
// authenticated immutable tenant id. The display handle is returned
|
|
2877
2806
|
// as metadata only.
|
|
2878
2807
|
if (p.daemon_public_key) {
|
|
2879
|
-
await
|
|
2808
|
+
await codexDaemonPubkeyRegistry.register(identity.agentId, p.daemon_public_key, p.crypto_versions, "pair-complete");
|
|
2880
2809
|
}
|
|
2881
2810
|
delete codexPairingByCode[code];
|
|
2882
2811
|
console.log("codex-relay: paired daemon for tenant " + identity.agentId + " handle " + identity.handle);
|
|
@@ -2901,16 +2830,8 @@ function handleCodexBootstrap(req, res, threadId) {
|
|
|
2901
2830
|
if (!identity) { json(res, 401, { error: "Unauthorized" }); return; }
|
|
2902
2831
|
if (!threadId) { json(res, 400, { error: "missing threadId" }); return; }
|
|
2903
2832
|
const daemonOnline = codexDaemons.has(identity.agentId);
|
|
2904
|
-
const daemonKey =
|
|
2905
|
-
json(res, 200, {
|
|
2906
|
-
handle: identity.handle,
|
|
2907
|
-
thread_id: threadId,
|
|
2908
|
-
daemon_online: daemonOnline,
|
|
2909
|
-
daemon_public_key: daemonKey ? daemonKey.pubkey : null,
|
|
2910
|
-
daemon_crypto_versions: daemonKey ? daemonKey.crypto_versions : null,
|
|
2911
|
-
supported_crypto_versions: ["e2ee-v1"],
|
|
2912
|
-
e2ee_available: !!daemonKey,
|
|
2913
|
-
});
|
|
2833
|
+
const daemonKey = codexDaemonPubkeyRegistry.get(identity.agentId);
|
|
2834
|
+
json(res, 200, buildCodexBootstrapPayload({ identity, threadId, daemonOnline, daemonKey }));
|
|
2914
2835
|
}
|
|
2915
2836
|
|
|
2916
2837
|
// POST /api/codex-relay/ws-ticket
|
|
@@ -3141,7 +3062,7 @@ httpServer.on("upgrade", (req, socket, head) => {
|
|
|
3141
3062
|
let envelope = null;
|
|
3142
3063
|
try { envelope = JSON.parse(text); } catch {}
|
|
3143
3064
|
if (envelope?.type === "daemon.identity") {
|
|
3144
|
-
void
|
|
3065
|
+
void codexDaemonPubkeyRegistry.register(
|
|
3145
3066
|
identity.agentId,
|
|
3146
3067
|
envelope.daemon_public_key,
|
|
3147
3068
|
envelope.crypto_versions,
|