@cello-protocol/daemon 0.0.35 → 0.0.37

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;AAQ/D,OAAO,EAAoD,KAAK,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAsB7G,OAAO,KAAK,EAAE,kBAAkB,EAA+C,MAAM,yBAAyB,CAAC;AAO/G,OAAO,EAAoB,KAAK,eAAe,EAAE,MAAM,2BAA2B,CAAC;AA0InF,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;AA2CD,eAAO,MAAM,sBAAsB,QAAsB,CAAC;AAE1D,wBAAsB,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CA+7K7E"}
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;AAQ/D,OAAO,EAAoD,KAAK,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAsB7G,OAAO,KAAK,EAAE,kBAAkB,EAA+C,MAAM,yBAAyB,CAAC;AAO/G,OAAO,EAAoB,KAAK,eAAe,EAAE,MAAM,2BAA2B,CAAC;AA0InF,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;AA2CD,eAAO,MAAM,sBAAsB,QAAsB,CAAC;AAE1D,wBAAsB,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAw9K7E"}
package/dist/daemon.js CHANGED
@@ -559,6 +559,7 @@ export async function startDaemon(config) {
559
559
  agentName,
560
560
  persistence: getPersistence(agentName),
561
561
  agentPubkeyHex,
562
+ keyProvider: agentKeyProvider,
562
563
  getNode: entry.getNode,
563
564
  getDirectoryEndpoint: getFailoverEndpoint,
564
565
  getConsortiumEndpoints: resolveConsortiumRoster,
@@ -570,6 +571,7 @@ export async function startDaemon(config) {
570
571
  agentName,
571
572
  persistence: getPersistence(agentName),
572
573
  agentPubkeyHex,
574
+ keyProvider: agentKeyProvider,
573
575
  getNode: entry.getNode,
574
576
  getDirectoryEndpoint: getFailoverEndpoint,
575
577
  getConsortiumEndpoints: resolveConsortiumRoster,
@@ -637,6 +639,7 @@ export async function startDaemon(config) {
637
639
  agentName: agent.name,
638
640
  persistence: getPersistence(agent.name),
639
641
  agentPubkeyHex: agent.pubkey,
642
+ keyProvider: agent.keyProvider,
640
643
  getNode: getDirectoryNode,
641
644
  getDirectoryEndpoint: getFailoverEndpoint,
642
645
  getConsortiumEndpoints: resolveConsortiumRoster,
@@ -647,6 +650,7 @@ export async function startDaemon(config) {
647
650
  agentName: agent.name,
648
651
  persistence: getPersistence(agent.name),
649
652
  agentPubkeyHex: agent.pubkey,
653
+ keyProvider: agent.keyProvider,
650
654
  getNode: getDirectoryNode,
651
655
  getDirectoryEndpoint: getFailoverEndpoint,
652
656
  getConsortiumEndpoints: resolveConsortiumRoster,
@@ -1143,6 +1147,7 @@ export async function startDaemon(config) {
1143
1147
  agentName,
1144
1148
  persistence: getPersistence(agentName),
1145
1149
  agentPubkeyHex,
1150
+ keyProvider: agentKeyProvider,
1146
1151
  getNode: () => nodeRef,
1147
1152
  getDirectoryEndpoint: getFailoverEndpoint,
1148
1153
  getConsortiumEndpoints: resolveConsortiumRoster,
@@ -1159,6 +1164,7 @@ export async function startDaemon(config) {
1159
1164
  agentName,
1160
1165
  persistence: getPersistence(agentName),
1161
1166
  agentPubkeyHex,
1167
+ keyProvider: agentKeyProvider,
1162
1168
  getNode: () => nodeRef,
1163
1169
  getDirectoryEndpoint: getFailoverEndpoint,
1164
1170
  getConsortiumEndpoints: resolveConsortiumRoster,
@@ -1559,13 +1565,19 @@ export async function startDaemon(config) {
1559
1565
  // no background timer). A session the standing receiver opened from an inbound offer the initiator
1560
1566
  // ABANDONED stays "active" forever (the counterparty never joins), clutters the open/active lists, and
1561
1567
  // its normal close fires an unsealable bilateral seal. Mark such a session terminal ("abandoned") once
1562
- // it is PROVABLY dead: status active + counterparty never established (liveness != "alive" AND 0
1563
- // RECEIVED messages — message_count alone counts our own auto-"Dispatched." ack, so it is NOT the
1564
- // signal) + age past the grace TTL (a genuinely fresh session still setting up must survive).
1568
+ // it is PROVABLY dead: counterparty never established (liveness != "alive" AND 0 RECEIVED messages —
1569
+ // message_count alone counts our own auto-"Dispatched." ack, so it is NOT the signal) + age past the
1570
+ // grace TTL (a genuinely fresh session still setting up must survive).
1571
+ // CC-10 (live 2026-07-08 Phase-2 block): scan 'interrupted' too, not just 'active'. A daemon restart
1572
+ // flips dead half-opens to 'interrupted'; those classify as "failed" (invisible in every list) yet
1573
+ // still count toward the unknown-sender acceptance bound (D18 deliberately counts 'interrupted') — so
1574
+ // a stranger whose first handshakes died was silently locked out FOREVER. Reaping only 0-RECEIVED
1575
+ // ghosts keeps D18 intact: the disconnect-evasion attacker's sessions always carry received content.
1565
1576
  const HALF_OPEN_TTL_MS = Number(process.env["CELLO_HALF_OPEN_TTL_MS"]) || 5 * 60 * 1000;
1566
1577
  function reapDeadHalfOpenSessions(agentName) {
1567
1578
  const now = Date.now();
1568
- for (const row of sessionNodeManager.getSessionsByStatus("active")) {
1579
+ const candidates = [...sessionNodeManager.getSessionsByStatus("active"), ...sessionNodeManager.getSessionsByStatus("interrupted")];
1580
+ for (const row of candidates) {
1569
1581
  if (agentName !== undefined && row.agent_name !== agentName)
1570
1582
  continue;
1571
1583
  if (now - row.created_at <= HALF_OPEN_TTL_MS)
@@ -1575,11 +1587,18 @@ export async function startDaemon(config) {
1575
1587
  if (sessionNodeManager.countReceivedMessages(row.agent_name, row.session_id) > 0)
1576
1588
  continue; // counterparty spoke
1577
1589
  // Non-awaited: abandonSession flips the DB status synchronously (before its first await), so THIS
1578
- // read reflects it; the async node teardown finishes in the background.
1579
- void sessionNodeManager.abandonSession(row.agent_name, row.session_id).catch((err) => {
1590
+ // read reflects it; the async node teardown finishes in the background. CC-10 reviewer LOW: only
1591
+ // log "reaped" if the status flip actually wrote — a swallowed write failure already logs
1592
+ // session.status.write.failed, and reporting success over it would hide a still-counting ghost.
1593
+ void sessionNodeManager.abandonSession(row.agent_name, row.session_id)
1594
+ .then((flipped) => {
1595
+ if (flipped) {
1596
+ logger.info("session.half_open.reaped", { agentName: row.agent_name, sessionId: row.session_id, priorStatus: row.status, ageMs: now - row.created_at });
1597
+ }
1598
+ })
1599
+ .catch((err) => {
1580
1600
  logger.warn("session.half_open.reap.failed", { agentName: row.agent_name, sessionId: row.session_id, reason: err instanceof Error ? err.message : String(err) });
1581
1601
  });
1582
- logger.info("session.half_open.reaped", { agentName: row.agent_name, sessionId: row.session_id, ageMs: now - row.created_at });
1583
1602
  }
1584
1603
  }
1585
1604
  // M8B F16: per-session liveness for ACTIVE sessions, shared by both status surfaces
@@ -2707,6 +2726,7 @@ export async function startDaemon(config) {
2707
2726
  agentName,
2708
2727
  persistence: getPersistence(agentName),
2709
2728
  agentPubkeyHex: loaded.pubkey,
2729
+ keyProvider: loaded.keyProvider,
2710
2730
  getNode: entry.getNode,
2711
2731
  getDirectoryEndpoint: getFailoverEndpoint,
2712
2732
  getConsortiumEndpoints: resolveConsortiumRoster,
@@ -4044,6 +4064,11 @@ export async function startDaemon(config) {
4044
4064
  // senders — a per-sender cap (many sessions from ONE stranger) and a global cap (many
4045
4065
  // sessions across ALL strangers combined). Known contacts are exempt ("bounded only by
4046
4066
  // disk" — DoD). Checked FIRST, before any standing-receiver work, so a refusal is cheap.
4067
+ // CC-10: reap provably-dead ghosts for THIS agent before counting — otherwise invisible
4068
+ // interrupted 0-received sessions (post-restart shape) consume the sender's budget forever
4069
+ // and lock the stranger out with no list read ever clearing them. Safe here: abandonSession
4070
+ // flips the DB status synchronously, so the bound query below sees the reaped state.
4071
+ reapDeadHalfOpenSessions(agentName);
4047
4072
  const bound = sessionNodeManager.checkUnknownSenderAcceptanceBound(agentName, parsed.participantAPubkeyHex);
4048
4073
  if (!bound.ok) {
4049
4074
  logger.warn("session.inbound.accept.failed", {