@cello-protocol/daemon 0.0.15 → 0.0.17

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.
@@ -1 +1 @@
1
- {"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAKH,OAAO,KAAK,EACV,YAAY,EACZ,oBAAoB,EAQrB,MAAM,YAAY,CAAC;AAIpB,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAM/D,OAAO,EAAoD,KAAK,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAoB7G,OAAO,KAAK,EAAE,kBAAkB,EAA+C,MAAM,yBAAyB,CAAC;AAM/G,OAAO,EAAoB,KAAK,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAqInF,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,SAAS,IAAI,oBAAoB,CAAC;IAClC;;;;OAIG;IACH,qBAAqB,IAAI,kBAAkB,CAAC;IAC5C;;;;;;;;OAQG;IACH,gBAAgB,IAAI,SAAS,GAAG,IAAI,CAAC;IACrC;;;;OAIG;IACH,oBAAoB,IAAI,kBAAkB,CAAC;IAC3C;;;OAGG;IACH,iBAAiB,IAAI,eAAe,CAAC;CACtC;AAyCD,wBAAsB,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CA8vH7E"}
1
+ {"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAKH,OAAO,KAAK,EACV,YAAY,EACZ,oBAAoB,EAQrB,MAAM,YAAY,CAAC;AAIpB,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAM/D,OAAO,EAAoD,KAAK,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAoB7G,OAAO,KAAK,EAAE,kBAAkB,EAA+C,MAAM,yBAAyB,CAAC;AAM/G,OAAO,EAAoB,KAAK,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAqInF,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,SAAS,IAAI,oBAAoB,CAAC;IAClC;;;;OAIG;IACH,qBAAqB,IAAI,kBAAkB,CAAC;IAC5C;;;;;;;;OAQG;IACH,gBAAgB,IAAI,SAAS,GAAG,IAAI,CAAC;IACrC;;;;OAIG;IACH,oBAAoB,IAAI,kBAAkB,CAAC;IAC3C;;;OAGG;IACH,iBAAiB,IAAI,eAAe,CAAC;CACtC;AAyCD,wBAAsB,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAyzH7E"}
package/dist/daemon.js CHANGED
@@ -55,7 +55,7 @@ import { wireSessionCeremonyHandler, wireSessionOfferHandler, wireSealCeremonyHa
55
55
  import { reDeriveFrontiers, findInflatedFrontier } from "./seal-frontier-verify.js";
56
56
  import { LocalAutoNatStub } from "@cello-protocol/transport";
57
57
  import { startHttpManifestPoll } from "./http-manifest-poll.js";
58
- import { resolveDirectoryUrl } from "./directory-bootstrap.js";
58
+ import { resolveDirectoryUrl, manifestNodesToEndpoints } from "./directory-bootstrap.js";
59
59
  /**
60
60
  * M7-SESSION-001 (H-1): canonical byte encoding of a SEAL-INTERRUPTED leaf for
61
61
  * Ed25519 signing/verification. Field order is fixed and deterministic. Both the
@@ -226,6 +226,10 @@ export async function startDaemon(config) {
226
226
  // M7 Keystone: the version of the verified manifest, surfaced in ConnectResult.
227
227
  // Stays 0 when no manifestProvider is configured (the M6 backward-compat path).
228
228
  let verifiedManifestVersion = 0;
229
+ // DOD-MANIFEST-1: the consortium node set resolved to live directory endpoints from
230
+ // the VERIFIED manifest — the N-node roster a T-of-N ceremony (DOD-DKG-1) fans out to.
231
+ // Empty in the M6 backward-compat path (no manifest) or if no node resolves.
232
+ let consortiumEndpoints = [];
229
233
  if (manifestProvider && manifestRootKeys && manifestThreshold !== undefined) {
230
234
  try {
231
235
  const manifest = await manifestProvider.loadAndVerify(manifestRootKeys, manifestThreshold);
@@ -262,6 +266,38 @@ export async function startDaemon(config) {
262
266
  manifestVersion: manifest.version,
263
267
  signerCount: manifest.signatures.length,
264
268
  });
269
+ // DOD-MANIFEST-1: resolve the FULL verified node set to live directory
270
+ // endpoints (replaces the implicit single-endpoint assumption). This is the
271
+ // roster T-of-N ceremonies fan out to. Availability-aware — a node that is
272
+ // down is skipped, the rest still resolve (redundancy invariant: a ceremony
273
+ // needs only T of N). The resolved count is logged so an operator sees the
274
+ // real reachable consortium at startup; the ceremony layer (DOD-DKG-1)
275
+ // re-checks the threshold against this roster and never silently falls back
276
+ // to the single hardcoded endpoint for a missing/forged node.
277
+ consortiumEndpoints = await manifestNodesToEndpoints(manifest.nodes, { logger });
278
+ const declaredNodes = manifest.nodes.length;
279
+ const resolvedNodes = consortiumEndpoints.length;
280
+ // Carry the nodeId↔peerId pairing (not just peerIds) so a consumer/test can verify
281
+ // each manifest identity bound to the right live directory, not merely the set.
282
+ const consortiumLog = {
283
+ manifestVersion: manifest.version,
284
+ declaredNodes,
285
+ resolvedNodes,
286
+ nodes: consortiumEndpoints.map((e) => ({ nodeId: e.nodeId, peerId: e.peerId })),
287
+ };
288
+ // Degraded ≠ healthy: a verified manifest whose nodes don't all resolve must NOT
289
+ // log at the same severity as a full roster. Threshold-REFUSAL (refusing to run a
290
+ // ceremony below T) is DOD-DKG-1's gate; here we make the health signal loud so a
291
+ // shrunken/empty roster is visible and never buried at info.
292
+ if (resolvedNodes === 0) {
293
+ logger.error("directory.consortium.none", consortiumLog);
294
+ }
295
+ else if (resolvedNodes < declaredNodes) {
296
+ logger.warn("directory.consortium.partial", consortiumLog);
297
+ }
298
+ else {
299
+ logger.info("directory.consortium.resolved", consortiumLog);
300
+ }
265
301
  }
266
302
  }
267
303
  }
@@ -286,6 +322,14 @@ export async function startDaemon(config) {
286
322
  throw new Error("Manifest verification failed. The daemon cannot start with an unverified manifest when manifestProvider is configured. " +
287
323
  "Check the logs for the specific failure reason (manifest_signature_invalid, manifest_expired, or manifest_version_rollback).");
288
324
  }
325
+ // DOD-SIGN-1: resolve the consortium roster for a session/seal FROST ceremony (same source as
326
+ // registration's DOD-DKG-1 path — the verified manifest, re-resolved at ceremony time). NULL when
327
+ // no consortium manifest is configured → single-node ceremony (M6/M7 back-compat). Shared by all
328
+ // wireSessionCeremonyHandler / wireSealCeremonyHandler call sites below.
329
+ const resolveConsortiumRoster = async () => {
330
+ const m = manifestProvider?.getCurrentManifest();
331
+ return m ? await manifestNodesToEndpoints(m.nodes, { logger }) : null;
332
+ };
289
333
  // CELLO-M7-CONN-001 (DOD-CONN-3): start the daemon-level HTTP manifest poll. It fetches
290
334
  // ${directoryHttpUrl}/manifest, verifies the threshold signature against the locally-pinned
291
335
  // root keys, applies anti-rollback + expiry, and adopts a newer manifest — independent of any
@@ -440,6 +484,7 @@ export async function startDaemon(config) {
440
484
  agentPubkeyHex,
441
485
  getNode: entry.getNode,
442
486
  getDirectoryEndpoint: async () => (directoryEndpointResolver ? (await directoryEndpointResolver()) ?? null : null),
487
+ getConsortiumEndpoints: resolveConsortiumRoster,
443
488
  signaling: mgr,
444
489
  logger,
445
490
  });
@@ -450,6 +495,7 @@ export async function startDaemon(config) {
450
495
  agentPubkeyHex,
451
496
  getNode: entry.getNode,
452
497
  getDirectoryEndpoint: async () => (directoryEndpointResolver ? (await directoryEndpointResolver()) ?? null : null),
498
+ getConsortiumEndpoints: resolveConsortiumRoster,
453
499
  signaling: mgr,
454
500
  logger,
455
501
  });
@@ -516,6 +562,7 @@ export async function startDaemon(config) {
516
562
  agentPubkeyHex: agent.pubkey,
517
563
  getNode: getDirectoryNode,
518
564
  getDirectoryEndpoint: async () => (directoryEndpointResolver ? (await directoryEndpointResolver()) ?? null : null),
565
+ getConsortiumEndpoints: resolveConsortiumRoster,
519
566
  signaling: mgr,
520
567
  logger,
521
568
  });
@@ -525,6 +572,7 @@ export async function startDaemon(config) {
525
572
  agentPubkeyHex: agent.pubkey,
526
573
  getNode: getDirectoryNode,
527
574
  getDirectoryEndpoint: async () => (directoryEndpointResolver ? (await directoryEndpointResolver()) ?? null : null),
575
+ getConsortiumEndpoints: resolveConsortiumRoster,
528
576
  signaling: mgr,
529
577
  logger,
530
578
  });
@@ -1373,10 +1421,21 @@ export async function startDaemon(config) {
1373
1421
  };
1374
1422
  }
1375
1423
  const persistence = getPersistence(name);
1424
+ // DOD-DKG-1: resolve the full consortium roster from the VERIFIED manifest so the DKG fans
1425
+ // across all N directory nodes. Re-resolved here (ceremony time) for fresh failover
1426
+ // coordinates. NULL when NO manifest is configured (→ single-node DKG, M6/M7 back-compat);
1427
+ // a (possibly EMPTY) array when a manifest IS configured. The null-vs-empty distinction is
1428
+ // load-bearing: an empty roster (consortium configured but unreachable) must REFUSE in
1429
+ // registration-manager, NOT downgrade to single-node (code-reviewer B1 / fallback-finder).
1430
+ const currentManifest = manifestProvider?.getCurrentManifest();
1431
+ const consortiumRoster = currentManifest
1432
+ ? await manifestNodesToEndpoints(currentManifest.nodes, { logger })
1433
+ : null;
1376
1434
  const ctx = new DaemonRegistrationContext({
1377
1435
  signaling: agentSignaling,
1378
1436
  getDirectoryNode: agentGetNode,
1379
1437
  getDirectoryEndpoint: () => directoryEndpoint,
1438
+ getConsortiumEndpoints: () => consortiumRoster,
1380
1439
  keyProvider,
1381
1440
  persistence,
1382
1441
  logger,