@cello-protocol/daemon 0.0.53 → 0.0.55

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 (177) hide show
  1. package/dist/agent-handlers.d.ts +57 -0
  2. package/dist/agent-handlers.d.ts.map +1 -0
  3. package/dist/agent-handlers.js +360 -0
  4. package/dist/agent-handlers.js.map +1 -0
  5. package/dist/agent-id-migration.d.ts.map +1 -1
  6. package/dist/agent-id-migration.js +1 -0
  7. package/dist/agent-id-migration.js.map +1 -1
  8. package/dist/agent-loader.d.ts +3 -4
  9. package/dist/agent-loader.d.ts.map +1 -1
  10. package/dist/agent-loader.js +3 -4
  11. package/dist/agent-loader.js.map +1 -1
  12. package/dist/cbor-blob-migration.d.ts +8 -0
  13. package/dist/cbor-blob-migration.d.ts.map +1 -0
  14. package/dist/cbor-blob-migration.js +115 -0
  15. package/dist/cbor-blob-migration.js.map +1 -0
  16. package/dist/close-session-handler.d.ts +41 -0
  17. package/dist/close-session-handler.d.ts.map +1 -0
  18. package/dist/close-session-handler.js +399 -0
  19. package/dist/close-session-handler.js.map +1 -0
  20. package/dist/consortium-bootstrap.d.ts +83 -0
  21. package/dist/consortium-bootstrap.d.ts.map +1 -0
  22. package/dist/consortium-bootstrap.js +120 -0
  23. package/dist/consortium-bootstrap.js.map +1 -0
  24. package/dist/contact-handlers.d.ts +56 -0
  25. package/dist/contact-handlers.d.ts.map +1 -0
  26. package/dist/contact-handlers.js +303 -0
  27. package/dist/contact-handlers.js.map +1 -0
  28. package/dist/content-park-client.d.ts.map +1 -1
  29. package/dist/content-park-client.js +7 -7
  30. package/dist/content-park-client.js.map +1 -1
  31. package/dist/content-park.d.ts +15 -0
  32. package/dist/content-park.d.ts.map +1 -0
  33. package/dist/content-park.js +227 -0
  34. package/dist/content-park.js.map +1 -0
  35. package/dist/daemon.d.ts +4 -10
  36. package/dist/daemon.d.ts.map +1 -1
  37. package/dist/daemon.js +430 -4563
  38. package/dist/daemon.js.map +1 -1
  39. package/dist/db-identity-store.d.ts +31 -35
  40. package/dist/db-identity-store.d.ts.map +1 -1
  41. package/dist/db-identity-store.js +62 -66
  42. package/dist/db-identity-store.js.map +1 -1
  43. package/dist/directory-bootstrap.d.ts +27 -31
  44. package/dist/directory-bootstrap.d.ts.map +1 -1
  45. package/dist/directory-bootstrap.js +18 -21
  46. package/dist/directory-bootstrap.js.map +1 -1
  47. package/dist/frame-values.d.ts +3 -0
  48. package/dist/frame-values.d.ts.map +1 -0
  49. package/dist/frame-values.js +73 -0
  50. package/dist/frame-values.js.map +1 -0
  51. package/dist/identity-migration.d.ts +8 -8
  52. package/dist/identity-migration.d.ts.map +1 -1
  53. package/dist/identity-migration.js +30 -31
  54. package/dist/identity-migration.js.map +1 -1
  55. package/dist/inbound-seal-request.d.ts +17 -0
  56. package/dist/inbound-seal-request.d.ts.map +1 -0
  57. package/dist/inbound-seal-request.js +155 -0
  58. package/dist/inbound-seal-request.js.map +1 -0
  59. package/dist/inbound-sessions.d.ts +54 -0
  60. package/dist/inbound-sessions.d.ts.map +1 -0
  61. package/dist/inbound-sessions.js +603 -0
  62. package/dist/inbound-sessions.js.map +1 -0
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.js +8 -10
  65. package/dist/index.js.map +1 -1
  66. package/dist/initiate-session-handler.d.ts +22 -0
  67. package/dist/initiate-session-handler.d.ts.map +1 -0
  68. package/dist/initiate-session-handler.js +150 -0
  69. package/dist/initiate-session-handler.js.map +1 -0
  70. package/dist/network-directory-node.d.ts +6 -6
  71. package/dist/network-directory-node.d.ts.map +1 -1
  72. package/dist/network-directory-node.js +29 -28
  73. package/dist/network-directory-node.js.map +1 -1
  74. package/dist/notification-handlers.d.ts +32 -0
  75. package/dist/notification-handlers.d.ts.map +1 -0
  76. package/dist/notification-handlers.js +73 -0
  77. package/dist/notification-handlers.js.map +1 -0
  78. package/dist/outbound-sessions.d.ts +54 -0
  79. package/dist/outbound-sessions.d.ts.map +1 -0
  80. package/dist/outbound-sessions.js +429 -0
  81. package/dist/outbound-sessions.js.map +1 -0
  82. package/dist/park-envelope.d.ts.map +1 -1
  83. package/dist/park-envelope.js +3 -2
  84. package/dist/park-envelope.js.map +1 -1
  85. package/dist/register-handler.d.ts +35 -0
  86. package/dist/register-handler.d.ts.map +1 -0
  87. package/dist/register-handler.js +179 -0
  88. package/dist/register-handler.js.map +1 -0
  89. package/dist/registration-manager.d.ts.map +1 -1
  90. package/dist/registration-manager.js +3 -6
  91. package/dist/registration-manager.js.map +1 -1
  92. package/dist/relay-receipt-store.d.ts.map +1 -1
  93. package/dist/relay-receipt-store.js +1 -2
  94. package/dist/relay-receipt-store.js.map +1 -1
  95. package/dist/retry-queue.d.ts +18 -60
  96. package/dist/retry-queue.d.ts.map +1 -1
  97. package/dist/retry-queue.js +46 -97
  98. package/dist/retry-queue.js.map +1 -1
  99. package/dist/seal-coordinator.d.ts +47 -0
  100. package/dist/seal-coordinator.d.ts.map +1 -0
  101. package/dist/seal-coordinator.js +551 -0
  102. package/dist/seal-coordinator.js.map +1 -0
  103. package/dist/seal-flows.d.ts +50 -0
  104. package/dist/seal-flows.d.ts.map +1 -0
  105. package/dist/seal-flows.js +454 -0
  106. package/dist/seal-flows.js.map +1 -0
  107. package/dist/seal-frontier-verify.d.ts +9 -9
  108. package/dist/seal-frontier-verify.d.ts.map +1 -1
  109. package/dist/seal-frontier-verify.js +11 -12
  110. package/dist/seal-frontier-verify.js.map +1 -1
  111. package/dist/seal-leaf.d.ts +58 -0
  112. package/dist/seal-leaf.d.ts.map +1 -0
  113. package/dist/seal-leaf.js +111 -0
  114. package/dist/seal-leaf.js.map +1 -0
  115. package/dist/seal-receipt-upgrade.d.ts +1 -1
  116. package/dist/seal-receipt-upgrade.js +1 -1
  117. package/dist/seal-upgrade.d.ts +10 -10
  118. package/dist/seal-upgrade.d.ts.map +1 -1
  119. package/dist/seal-upgrade.js +23 -24
  120. package/dist/seal-upgrade.js.map +1 -1
  121. package/dist/session-assignment-parser.d.ts +16 -19
  122. package/dist/session-assignment-parser.d.ts.map +1 -1
  123. package/dist/session-assignment-parser.js +30 -29
  124. package/dist/session-assignment-parser.js.map +1 -1
  125. package/dist/session-ceremony.d.ts +3 -6
  126. package/dist/session-ceremony.d.ts.map +1 -1
  127. package/dist/session-ceremony.js +7 -9
  128. package/dist/session-ceremony.js.map +1 -1
  129. package/dist/session-connection-gater.d.ts +4 -2
  130. package/dist/session-connection-gater.d.ts.map +1 -1
  131. package/dist/session-connection-gater.js +16 -10
  132. package/dist/session-connection-gater.js.map +1 -1
  133. package/dist/session-content-handlers.d.ts +25 -0
  134. package/dist/session-content-handlers.d.ts.map +1 -0
  135. package/dist/session-content-handlers.js +436 -0
  136. package/dist/session-content-handlers.js.map +1 -0
  137. package/dist/session-name.d.ts +35 -0
  138. package/dist/session-name.d.ts.map +1 -0
  139. package/dist/session-name.js +60 -0
  140. package/dist/session-name.js.map +1 -0
  141. package/dist/session-node-manager.d.ts +47 -62
  142. package/dist/session-node-manager.d.ts.map +1 -1
  143. package/dist/session-node-manager.js +297 -97
  144. package/dist/session-node-manager.js.map +1 -1
  145. package/dist/session-read-handlers.d.ts +38 -0
  146. package/dist/session-read-handlers.d.ts.map +1 -0
  147. package/dist/session-read-handlers.js +233 -0
  148. package/dist/session-read-handlers.js.map +1 -0
  149. package/dist/session-relay-client.d.ts +8 -8
  150. package/dist/session-relay-client.d.ts.map +1 -1
  151. package/dist/session-relay-client.js +44 -46
  152. package/dist/session-relay-client.js.map +1 -1
  153. package/dist/session-tree.d.ts +11 -23
  154. package/dist/session-tree.d.ts.map +1 -1
  155. package/dist/session-tree.js +14 -26
  156. package/dist/session-tree.js.map +1 -1
  157. package/dist/signaling-connect.d.ts +13 -5
  158. package/dist/signaling-connect.d.ts.map +1 -1
  159. package/dist/signaling-connect.js +55 -13
  160. package/dist/signaling-connect.js.map +1 -1
  161. package/dist/sqlcipher-db.d.ts +18 -19
  162. package/dist/sqlcipher-db.d.ts.map +1 -1
  163. package/dist/sqlcipher-db.js +30 -30
  164. package/dist/sqlcipher-db.js.map +1 -1
  165. package/dist/telegram-doorbell.d.ts +38 -0
  166. package/dist/telegram-doorbell.d.ts.map +1 -0
  167. package/dist/telegram-doorbell.js +139 -0
  168. package/dist/telegram-doorbell.js.map +1 -0
  169. package/dist/types.d.ts +86 -79
  170. package/dist/types.d.ts.map +1 -1
  171. package/dist/types.js +0 -1
  172. package/dist/types.js.map +1 -1
  173. package/dist/vocabulary.d.ts +30 -30
  174. package/dist/vocabulary.d.ts.map +1 -1
  175. package/dist/vocabulary.js +41 -49
  176. package/dist/vocabulary.js.map +1 -1
  177. package/package.json +5 -5
@@ -0,0 +1,50 @@
1
+ import { SignalingManager } from "@cello-protocol/transport";
2
+ import type { KeyProvider } from "@cello-protocol/crypto";
3
+ import type { SessionNodeManager } from "./session-node-manager.js";
4
+ import type { AgentInfo, Logger } from "./types.js";
5
+ export type SealFlowResult = {
6
+ ok: true;
7
+ sessionId: string;
8
+ status: "seal_interrupted_pending";
9
+ } | {
10
+ ok: false;
11
+ reason: string;
12
+ guidance: string;
13
+ };
14
+ export type ActiveSealResult = {
15
+ ok: true;
16
+ sessionId: string;
17
+ status: "seal_interrupted_pending";
18
+ rootHex: string;
19
+ } | {
20
+ ok: false;
21
+ reason: string;
22
+ guidance: string;
23
+ };
24
+ export interface SealFlowDeps {
25
+ logger: Logger;
26
+ sessionNodeManager: SessionNodeManager;
27
+ agents: AgentInfo[];
28
+ getKeyProvider: (agentName: string) => KeyProvider | undefined;
29
+ /** This agent's directory signaling stream, or undefined if it has none. */
30
+ signalingFor: (agentName: string) => SignalingManager | undefined;
31
+ sendOver: (agentName: string, frame: Record<string, unknown>) => Promise<{
32
+ ok: boolean;
33
+ reason?: string;
34
+ }>;
35
+ }
36
+ export declare function createSealFlows(deps: SealFlowDeps): {
37
+ awaitSealAck: (sessionId: string, mgr: SignalingManager | undefined) => Promise<{
38
+ type: "seal_interrupted_ack";
39
+ sealInterruptedLeaf: Record<string, unknown>;
40
+ nonce: string | null;
41
+ } | {
42
+ type: "seal_interrupted_rejection";
43
+ reason: string;
44
+ } | {
45
+ type: "timeout";
46
+ }>;
47
+ handleSealInterruptedFlow: (sessionId: string, record: import("./types.js").SessionRecord, correlationId: string, merkleRootAtInterruption: string) => Promise<SealFlowResult>;
48
+ handleActiveSealFlow: (sessionId: string, record: import("./types.js").SessionRecord, correlationId: string) => Promise<ActiveSealResult>;
49
+ };
50
+ //# sourceMappingURL=seal-flows.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seal-flows.d.ts","sourceRoot":"","sources":["../src/seal-flows.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAE7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEpD,MAAM,MAAM,cAAc,GACtB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,0BAA0B,CAAA;CAAE,GACnE;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpD,MAAM,MAAM,gBAAgB,GACxB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,0BAA0B,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACpF;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,WAAW,GAAG,SAAS,CAAC;IAC/D,4EAA4E;IAC5E,YAAY,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,gBAAgB,GAAG,SAAS,CAAC;IAClE,QAAQ,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC5G;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,YAAY;8BAyCf,MAAM,OAAO,gBAAgB,GAAG,SAAS,KAAG,OAAO;cAJxE,sBAAsB;6BAAuB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;eAAS,MAAM,GAAG,IAAI;;cAC1F,4BAA4B;gBAAU,MAAM;;cAC5C,SAAS;MAE8E;2CAmCtF,MAAM,UACT,OAAO,YAAY,EAAE,aAAa,iBAC3B,MAAM,4BACK,MAAM,KAC/B,OAAO,CAAC,cAAc,CAAC;sCA4Nb,MAAM,UACT,OAAO,YAAY,EAAE,aAAa,iBAC3B,MAAM,KACpB,OAAO,CAAC,gBAAgB,CAAC;EA+K7B"}
@@ -0,0 +1,454 @@
1
+ /**
2
+ * The two seal-initiation flows that cello_close_session dispatches into.
3
+ *
4
+ * handleSealInterruptedFlow — the counterparty is GONE. Exchange seal-interrupted leaves over the
5
+ * directory pass-through, then freeze the session at seal_interrupted_pending.
6
+ * handleActiveSealFlow — the counterparty is LIVE. Run the bilateral close.
7
+ *
8
+ * The daemon holds no FrostThresholdSigner and no directory FROST ceremony client, so it CANNOT
9
+ * produce a threshold signature by itself. That is the sovereign-node invariant, and it is why these
10
+ * functions only ever assemble and exchange leaves — the notarization happens at the directory.
11
+ */
12
+ import { randomUUID } from "node:crypto";
13
+ import { buildSignedSealInterruptedLeaf, verifyCounterpartySealLeaf } from "./seal-leaf.js";
14
+ export function createSealFlows(deps) {
15
+ const { logger, sessionNodeManager, agents, getKeyProvider, signalingFor, sendOver } = deps;
16
+ const keyProviders = { get: getKeyProvider };
17
+ // Seal-interrupted bilateral INITIATOR flow.
18
+ //
19
+ // ⚠️ SCOPE — this flow does NOT complete a FROST-notarized seal, and must not pretend to.
20
+ //
21
+ // What it DOES: both parties produce and exchange real K_local Ed25519-signed SEAL-INTERRUPTED
22
+ // leaves over an agreed {leafCount, merkleRoot}; the initiator verifies the signature, the
23
+ // echoed nonce, and the cross-checks; the verified bilateral commitment is persisted; the
24
+ // session advances to the NON-TERMINAL 'seal_interrupted_pending' state.
25
+ //
26
+ // What it does NOT do: the FROST threshold notarization. The daemon holds no
27
+ // FrostThresholdSigner and no directory FROST ceremony client, so it CANNOT produce a threshold
28
+ // seal here. The session therefore STOPS at the persisted bilateral commitment rather than
29
+ // claim a completed seal. Wiring the real seal means injecting a SealManager adapter.
30
+ //
31
+ // Merkle-ROOT agreement is deliberately NOT compared at this layer. Under concurrent
32
+ // bidirectional traffic the two sides' local append orders — and therefore their roots — can
33
+ // legitimately diverge until the relay-assigned canonical sequence exists. Comparing roots here
34
+ // would reject honest sessions. leafCount agreement (each side against its own tree size, or
35
+ // message_count when no tree exists) is the bilateral check available at this layer; true root
36
+ // agreement belongs to the FROST seal against the directory-held tree.
37
+ //
38
+ // Both sides bind over their OWN tree root + size (SI-001) whenever a non-empty tree exists —
39
+ // e.g. reloaded from session_tree_leaves after a restart. Only sessions with no persisted tree
40
+ // fall back to message_count plus the caller-supplied root.
41
+ // M7-SESSION-001 / DAEMON-004: shared bilateral ack-await machinery. The
42
+ // interrupted-seal flow AND the active-session seal flow both send a
43
+ // `seal_interrupted_request` and wait on the directory signaling stream for the
44
+ // counterparty's `seal_interrupted_ack` / `seal_interrupted_rejection` (or time
45
+ // out). Extracted so the two flows wait identically — the directory pass-through
46
+ // routing (directory-node.ts) is the only wired transport for this exchange.
47
+ const SEAL_INTERRUPTED_TIMEOUT_MS = 30_000;
48
+ function awaitSealAck(sessionId, mgr) {
49
+ return new Promise((resolve) => {
50
+ // CONN-001: await the ack on the OWNING agent's own stream (per-agent in prod, shared in test).
51
+ if (!mgr) {
52
+ resolve({ type: "timeout" });
53
+ return;
54
+ }
55
+ const timeoutHandle = setTimeout(() => {
56
+ unregister();
57
+ resolve({ type: "timeout" });
58
+ }, SEAL_INTERRUPTED_TIMEOUT_MS);
59
+ const unregister = mgr.registerInboundHandler((frame) => {
60
+ if (frame.type !== "seal_interrupted_ack" && frame.type !== "seal_interrupted_rejection") {
61
+ return;
62
+ }
63
+ if (typeof frame.sessionId !== "string" || frame.sessionId !== sessionId)
64
+ return;
65
+ clearTimeout(timeoutHandle);
66
+ unregister();
67
+ if (frame.type === "seal_interrupted_ack") {
68
+ resolve({
69
+ type: "seal_interrupted_ack",
70
+ sealInterruptedLeaf: frame.sealInterruptedLeaf ?? {},
71
+ nonce: typeof frame.nonce === "string" ? frame.nonce : null,
72
+ });
73
+ }
74
+ else {
75
+ resolve({
76
+ type: "seal_interrupted_rejection",
77
+ reason: typeof frame.reason === "string" ? frame.reason : "unknown",
78
+ });
79
+ }
80
+ });
81
+ });
82
+ }
83
+ async function handleSealInterruptedFlow(sessionId, record, correlationId, merkleRootAtInterruption) {
84
+ const nonce = randomUUID();
85
+ // Retrieve the agent's own pubkey from the agent list
86
+ // (the agent_name stored in the session record identifies which agent was in session)
87
+ const agent = agents.find((a) => a.name === record.agent_name);
88
+ const myPubkeyHex = agent?.pubkey ?? "";
89
+ const counterpartyPubkey = record.counterparty_pubkey;
90
+ // DAEMON-004 (AC-007 / SI-001 / finding #2): prefer the daemon-owned tree.
91
+ // After a SIGKILL+restart the active session is forced to 'interrupted' and
92
+ // its Merkle tree is reloaded from session_tree_leaves. When that reloaded
93
+ // tree is non-empty it is the authoritative transcript: the seal binds over
94
+ // the daemon's OWN reloaded root + size, and any caller-supplied
95
+ // merkleRootAtInterruption is IGNORED (SI-001). Only when no tree was ever
96
+ // persisted (legacy / pre-DAEMON-004 sessions) do we fall back to the
97
+ // caller-supplied root and the message_count column (SESSION-001 behavior).
98
+ const reloadedTree = sessionNodeManager.getSessionTree(record.agent_name, sessionId);
99
+ const hasOwnTree = reloadedTree.size() > 0;
100
+ const ownLeafCount = hasOwnTree ? reloadedTree.size() : (record.message_count ?? 0);
101
+ const effectiveRoot = hasOwnTree
102
+ ? sessionNodeManager.getSessionTreeRootHex(record.agent_name, sessionId)
103
+ : merkleRootAtInterruption;
104
+ // DB-001: check signaling status before attempting to send
105
+ if (signalingFor(record.agent_name)?.status === "reconnecting") {
106
+ logger.error("session.interrupted.seal.failed", {
107
+ sessionId,
108
+ agentName: record.agent_name,
109
+ reason: "signaling_reconnecting",
110
+ error: "directory_signaling_reconnecting",
111
+ correlationId,
112
+ });
113
+ return {
114
+ ok: false,
115
+ reason: "signaling_reconnecting",
116
+ guidance: "The directory signaling stream is reconnecting. Wait for directory_signaling to show connected in cello status before initiating seal-interrupted. The daemon reconnects automatically — no manual intervention required.",
117
+ };
118
+ }
119
+ // H-1: construct and K_local-sign our OWN SEAL-INTERRUPTED leaf before sending.
120
+ const myKeyProvider = keyProviders.get(record.agent_name);
121
+ if (!myKeyProvider) {
122
+ logger.error("session.interrupted.seal.failed", {
123
+ sessionId,
124
+ agentName: record.agent_name,
125
+ reason: "signing_key_unavailable",
126
+ error: "no_key_provider_for_agent",
127
+ correlationId,
128
+ });
129
+ return {
130
+ ok: false,
131
+ reason: "signing_key_unavailable",
132
+ guidance: "The signing key for the agent that owned this session could not be loaded. Confirm the agent's key file exists under ~/.cello/agents and restart the daemon.",
133
+ };
134
+ }
135
+ const ownLeaf = await buildSignedSealInterruptedLeaf(myKeyProvider, {
136
+ sessionId,
137
+ leafCount: ownLeafCount,
138
+ merkleRootAtInterruption: effectiveRoot,
139
+ signerPubkeyHex: myPubkeyHex,
140
+ });
141
+ // Send SealInterruptedRequest via directory signaling
142
+ const request = {
143
+ type: "seal_interrupted_request",
144
+ sessionId,
145
+ initiatorPubkey: myPubkeyHex,
146
+ counterpartyPubkey,
147
+ leafCountAtInterruption: ownLeafCount,
148
+ merkleRootAtInterruption: effectiveRoot,
149
+ nonce,
150
+ };
151
+ const sendResult = await sendOver(record.agent_name, request);
152
+ if (!sendResult.ok) {
153
+ logger.error("session.interrupted.seal.failed", {
154
+ sessionId,
155
+ agentName: record.agent_name,
156
+ reason: "seal_interrupted_counterparty_unavailable",
157
+ error: sendResult.reason,
158
+ correlationId,
159
+ });
160
+ return {
161
+ ok: false,
162
+ reason: "seal_interrupted_counterparty_unavailable",
163
+ guidance: "The counterparty is not currently reachable to complete the seal-interrupted flow. Retry when the counterparty is online — check their connection status via cello_status.",
164
+ };
165
+ }
166
+ // Wait for counterparty ack/rejection via the shared signaling await machinery.
167
+ const ackResult = await awaitSealAck(sessionId, signalingFor(record.agent_name));
168
+ if (ackResult.type === "timeout") {
169
+ logger.error("session.interrupted.seal.failed", {
170
+ sessionId,
171
+ agentName: record.agent_name,
172
+ reason: "seal_interrupted_counterparty_unavailable",
173
+ error: "seal_interrupted_response_timeout",
174
+ correlationId,
175
+ });
176
+ return {
177
+ ok: false,
178
+ reason: "seal_interrupted_counterparty_unavailable",
179
+ guidance: "The counterparty is not currently reachable to complete the seal-interrupted flow. Retry when the counterparty is online — check their connection status via cello_status.",
180
+ };
181
+ }
182
+ if (ackResult.type === "seal_interrupted_rejection") {
183
+ logger.error("session.interrupted.seal.failed", {
184
+ sessionId,
185
+ agentName: record.agent_name,
186
+ reason: "seal_interrupted_rejected_by_counterparty",
187
+ error: ackResult.reason,
188
+ correlationId,
189
+ });
190
+ return {
191
+ ok: false,
192
+ reason: "seal_interrupted_rejected_by_counterparty",
193
+ guidance: "The counterparty rejected the seal-interrupted request. This may indicate their session state is inconsistent. Ask the counterparty to check their interrupted sessions via cello status on their end.",
194
+ };
195
+ }
196
+ // ackResult.type === "seal_interrupted_ack"
197
+ {
198
+ const leaf = ackResult.sealInterruptedLeaf;
199
+ // C-1 / SI-002 / SI-003: nonce (L-2), leafCount agreement, and the
200
+ // counterparty's own Ed25519 signature are verified by the shared helper.
201
+ // We compare against our OWN ownLeafCount (an independent value) so a real
202
+ // divergence in transcript length is caught. Merkle-root agreement is NOT
203
+ // verified at this leaf-exchange layer (it is the FROST-seal step's job
204
+ // against the directory-held tree); see the H-1 SCOPE note above.
205
+ const verified = verifyCounterpartySealLeaf({
206
+ leaf,
207
+ sentNonce: nonce,
208
+ ackNonce: ackResult.nonce,
209
+ ownLeafCount,
210
+ expectedCounterpartyPubkey: record.counterparty_pubkey,
211
+ });
212
+ if (!verified.ok) {
213
+ const reasonMap = {
214
+ nonce_mismatch: "seal_interrupted_nonce_mismatch",
215
+ leaf_count_mismatch: "seal_interrupted_leaf_count_mismatch",
216
+ leaf_signature_invalid: "seal_interrupted_leaf_signature_invalid",
217
+ };
218
+ const guidanceMap = {
219
+ nonce_mismatch: "The counterparty's acknowledgement did not echo the expected nonce. This indicates a stale or replayed response. The session remains interrupted — retry cello_close_session.",
220
+ leaf_count_mismatch: "The counterparty's recorded message count at interruption does not match ours. The two sides have divergent session histories and cannot form a bilateral commitment. Compare cello status on both ends before retrying.",
221
+ leaf_signature_invalid: "The counterparty's SEAL-INTERRUPTED leaf signature did not verify. The seal flow has been aborted. The session remains interrupted — retry cello_close_session after confirming the counterparty is using a compatible version.",
222
+ };
223
+ logger.error("session.interrupted.seal.failed", {
224
+ sessionId,
225
+ agentName: record.agent_name,
226
+ reason: reasonMap[verified.reason],
227
+ error: verified.error,
228
+ correlationId,
229
+ });
230
+ return { ok: false, reason: reasonMap[verified.reason], guidance: guidanceMap[verified.reason] };
231
+ }
232
+ // H-1: signature + nonce + cross-checks all passed. We have a VERIFIED
233
+ // bilateral commitment (both K_local-signed leaves over the same
234
+ // {leafCount, merkleRoot}). Persist BOTH leaves and advance the session to
235
+ // the NON-TERMINAL 'seal_interrupted_pending' state. We do NOT write
236
+ // 'sealed' — the FROST threshold notarization has not run (see the H-1
237
+ // SCOPE note above for exactly what blocks it).
238
+ const advanced = sessionNodeManager.persistSealInterruptedCommitment({
239
+ agentName: record.agent_name,
240
+ sessionId,
241
+ role: "initiator",
242
+ ownLeaf,
243
+ counterpartyLeaf: leaf,
244
+ merkleRoot: effectiveRoot,
245
+ nonce,
246
+ });
247
+ if (!advanced) {
248
+ logger.error("session.interrupted.seal.failed", {
249
+ sessionId,
250
+ agentName: record.agent_name,
251
+ reason: "seal_interrupted_persist_failed",
252
+ error: "session row was not in 'interrupted' state at commit time",
253
+ correlationId,
254
+ });
255
+ return {
256
+ ok: false,
257
+ reason: "seal_interrupted_persist_failed",
258
+ guidance: "The bilateral commitment could not be persisted because the session was no longer in the interrupted state. Re-check cello status — it may already be pending or sealed.",
259
+ };
260
+ }
261
+ logger.info("session.interrupted.pending", {
262
+ sessionId,
263
+ agentName: record.agent_name,
264
+ leafCount: ownLeafCount,
265
+ correlationId,
266
+ });
267
+ return { ok: true, sessionId, status: "seal_interrupted_pending" };
268
+ }
269
+ }
270
+ // ─── CELLO-M7-DAEMON-004: active-session seal initiation ─────────────────────
271
+ //
272
+ // Pseudocode (SPARC Phase P):
273
+ // 1. DB-002: if signaling reconnecting → return signaling_reconnecting and do
274
+ // NOT initiate a partial seal that cannot be notarized.
275
+ // 2. SI-001: read the merkle root from the daemon's OWN tree (never a caller param).
276
+ // 3. K_local-sign a SEAL ctrl leaf over that root (SI-003: signed by our own node).
277
+ // 4. Fire session.seal.initiated with rootHex == our own tree root, role:'initiator'.
278
+ // 5. Submit the SEAL to the relay/counterparty + coordinate FROST via signaling,
279
+ // reusing the same signaling stream the interrupted-seal flow (SESSION-001) uses.
280
+ // The bilateral counterparty ack + FROST threshold notarization complete the
281
+ // seal end-to-end (AC-004, exercised under CELLO_E2E_LIVE) — the daemon never
282
+ // synthesizes the counterparty's signature.
283
+ // round-2 [medium]: the IPC return status MUST match the persisted row. The
284
+ // active-seal flow advances the session to 'seal_interrupted_pending' (the same
285
+ // non-terminal bilateral-commitment state the interrupted flow uses); returning
286
+ // a distinct 'seal_initiated' here meant the close response and a subsequent
287
+ // cello_status / cello_list_sessions showed two names for one state. One name.
288
+ async function handleActiveSealFlow(sessionId, record, correlationId) {
289
+ const agent = agents.find((a) => a.name === record.agent_name);
290
+ const myPubkeyHex = agent?.pubkey ?? "";
291
+ const kp = keyProviders.get(record.agent_name);
292
+ // DB-002: never initiate a partial seal while signaling is reconnecting.
293
+ if (signalingFor(record.agent_name)?.status === "reconnecting") {
294
+ return {
295
+ ok: false,
296
+ reason: "signaling_reconnecting",
297
+ guidance: "The directory signaling stream is reconnecting. Wait for directory_signaling to show connected in cello status before retrying the close. The daemon reconnects automatically.",
298
+ };
299
+ }
300
+ if (!kp || !myPubkeyHex) {
301
+ logger.error("session.seal.initiate.failed", {
302
+ sessionId,
303
+ reason: "signing_key_unavailable",
304
+ errorMessage: "no key provider or pubkey for the agent that owns this session",
305
+ correlationId,
306
+ });
307
+ return {
308
+ ok: false,
309
+ reason: "signing_key_unavailable",
310
+ guidance: "The signing key for the agent that owns this session could not be loaded. Confirm the agent's key file exists under ~/.cello/agents and restart the daemon.",
311
+ };
312
+ }
313
+ // SI-001: the root is the daemon's OWN tree root — computed from the leaves it
314
+ // appended itself. Any caller-supplied merkleRoot is never read here.
315
+ const ownRootHex = sessionNodeManager.getSessionTreeRootHex(record.agent_name, sessionId);
316
+ const leafCount = sessionNodeManager.getSessionTree(record.agent_name, sessionId).size();
317
+ const nonce = randomUUID();
318
+ // SI-003: K_local-sign our OWN SEAL leaf over our own root. We reuse the
319
+ // wired SEAL-INTERRUPTED leaf shape so the counterparty co-signs an identical
320
+ // canonical form (the active and interrupted bilateral exchanges share the
321
+ // directory pass-through routing — there is no separate `seal_request`
322
+ // transport, and inventing one silently drops the frame at the directory).
323
+ let ownLeaf;
324
+ try {
325
+ ownLeaf = await buildSignedSealInterruptedLeaf(kp, {
326
+ sessionId,
327
+ leafCount,
328
+ merkleRootAtInterruption: ownRootHex,
329
+ signerPubkeyHex: myPubkeyHex,
330
+ });
331
+ }
332
+ catch (err) {
333
+ logger.error("session.seal.initiate.failed", {
334
+ sessionId,
335
+ reason: "seal_leaf_signing_failed",
336
+ errorMessage: err instanceof Error ? err.message : String(err),
337
+ correlationId,
338
+ });
339
+ return {
340
+ ok: false,
341
+ reason: "seal_leaf_signing_failed",
342
+ guidance: "The SEAL leaf could not be signed. Check the daemon logs for the signing error and confirm the agent key is intact.",
343
+ };
344
+ }
345
+ // AC-003: session.seal.initiated — rootHex MUST equal the daemon's own root.
346
+ logger.info("session.seal.initiated", {
347
+ sessionId,
348
+ rootHex: ownRootHex,
349
+ role: "initiator",
350
+ correlationId,
351
+ });
352
+ // Submit the SEAL request over the directory signaling stream (the SAME wired
353
+ // pass-through the interrupted-seal flow uses) and AWAIT the counterparty's
354
+ // bilateral ack. We never report success on a fire-and-forget send.
355
+ const sendResult = await sendOver(record.agent_name, {
356
+ type: "seal_interrupted_request",
357
+ sessionId,
358
+ initiatorPubkey: myPubkeyHex,
359
+ counterpartyPubkey: record.counterparty_pubkey,
360
+ leafCountAtInterruption: leafCount,
361
+ merkleRootAtInterruption: ownRootHex,
362
+ nonce,
363
+ });
364
+ if (!sendResult.ok) {
365
+ logger.error("session.seal.initiate.failed", {
366
+ sessionId,
367
+ reason: sendResult.reason,
368
+ errorMessage: sendResult.reason,
369
+ correlationId,
370
+ });
371
+ return {
372
+ ok: false,
373
+ reason: sendResult.reason ?? "directory_unreachable",
374
+ guidance: "guidance" in sendResult && typeof sendResult.guidance === "string"
375
+ ? sendResult.guidance
376
+ : "The seal could not be submitted to the directory. Retry once cello status shows directory_signaling connected.",
377
+ };
378
+ }
379
+ // Wait for the counterparty's bilateral ack (or rejection / timeout).
380
+ const ackResult = await awaitSealAck(sessionId, signalingFor(record.agent_name));
381
+ if (ackResult.type === "timeout" || ackResult.type === "seal_interrupted_rejection") {
382
+ const reason = ackResult.type === "timeout" ? "seal_counterparty_unavailable" : "seal_rejected_by_counterparty";
383
+ logger.error("session.seal.initiate.failed", {
384
+ sessionId,
385
+ reason,
386
+ errorMessage: ackResult.type === "timeout" ? "seal_response_timeout" : ackResult.reason,
387
+ correlationId,
388
+ });
389
+ return {
390
+ ok: false,
391
+ reason,
392
+ guidance: ackResult.type === "timeout"
393
+ ? "The counterparty did not acknowledge the seal in time. Retry when they are online — check cello_status. The session remains active and usable."
394
+ : "The counterparty rejected the seal request. Their session state may be inconsistent. Ask them to check cello status before retrying.",
395
+ };
396
+ }
397
+ // SI-002 / SI-003: verify the counterparty's own-signed ack leaf over our root.
398
+ const verified = verifyCounterpartySealLeaf({
399
+ leaf: ackResult.sealInterruptedLeaf,
400
+ sentNonce: nonce,
401
+ ackNonce: ackResult.nonce,
402
+ ownLeafCount: leafCount,
403
+ expectedCounterpartyPubkey: record.counterparty_pubkey,
404
+ });
405
+ if (!verified.ok) {
406
+ logger.error("session.seal.initiate.failed", {
407
+ sessionId,
408
+ reason: `seal_${verified.reason}`,
409
+ errorMessage: verified.error,
410
+ correlationId,
411
+ });
412
+ return {
413
+ ok: false,
414
+ reason: `seal_${verified.reason}`,
415
+ guidance: "The counterparty's seal acknowledgement failed verification (nonce, leaf count, or signature). The session remains active — retry cello_close_session once both sides agree on the transcript.",
416
+ };
417
+ }
418
+ // Verified bilateral commitment over the daemon's OWN root. Persist both
419
+ // signed leaves and advance the session out of 'active'. As in the
420
+ // interrupted flow, we stop at the bilateral commitment ('seal_interrupted_pending');
421
+ // the FROST threshold notarization that finalizes 'sealed' is the deferred
422
+ // directory step (AC-004, exercised under CELLO_E2E_LIVE).
423
+ const advanced = sessionNodeManager.persistSealInterruptedCommitment({
424
+ agentName: record.agent_name,
425
+ sessionId,
426
+ role: "initiator",
427
+ ownLeaf,
428
+ counterpartyLeaf: ackResult.sealInterruptedLeaf,
429
+ merkleRoot: ownRootHex,
430
+ nonce,
431
+ });
432
+ if (!advanced) {
433
+ logger.error("session.seal.initiate.failed", {
434
+ sessionId,
435
+ reason: "seal_persist_failed",
436
+ errorMessage: "session row was not in an active/interrupted state at commit time",
437
+ correlationId,
438
+ });
439
+ return {
440
+ ok: false,
441
+ reason: "seal_persist_failed",
442
+ guidance: "The bilateral seal commitment could not be persisted because the session changed state. Re-check cello status — it may already be pending or sealed.",
443
+ };
444
+ }
445
+ // round-2 finding #5: the session is now frozen at 'seal_interrupted_pending'.
446
+ // Retire its live libp2p node so no further inbound content can arrive (which
447
+ // ingestReceivedContent now also rejects) and so the node is not leaked per
448
+ // active close. retireSessionNode stops the node WITHOUT changing the DB status.
449
+ await sessionNodeManager.retireSessionNode(record.agent_name, sessionId);
450
+ return { ok: true, sessionId, status: "seal_interrupted_pending", rootHex: ownRootHex };
451
+ }
452
+ return { awaitSealAck, handleSealInterruptedFlow, handleActiveSealFlow };
453
+ }
454
+ //# sourceMappingURL=seal-flows.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seal-flows.js","sourceRoot":"","sources":["../src/seal-flows.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * DOD-LEG-2 (SESSION-004 SI-002) — client-side content-frontier re-derivation.
2
+ * Client-side content-frontier re-derivation.
3
3
  *
4
4
  * The directory builds + signs the seal legibility, including each party's
5
5
  * `content_frontier_seq` ("the highest message the party provably received"). The client
@@ -65,12 +65,12 @@ export interface UnilateralFrontierCheck {
65
65
  * - verified: leaves present, no CLIENT-VERIFIABLE ('live') frontier exceeded its leaves.
66
66
  * - corrected: leaves present, one or more 'live' frontiers were inflated and overridden
67
67
  * DOWN to the re-derived value (see `corrections`).
68
- * - directory_attested: no frontier_leaves shipped (a pre-FINDING-5 directory) — the cert stays
69
- * directory-attested (FINDING-3 behavior). No correction possible.
68
+ * - directory_attested: no frontier_leaves shipped (an older directory) — the cert stays
69
+ * directory-attested. No correction possible.
70
70
  * - leaves_invalid: frontier_leaves are forged / cross-session (reDeriveFrontiers failed) — a
71
71
  * tamper signal; persist the directory-attested frontiers, log loudly. NEVER
72
- * rejected (the unilateral dedup guard makes rejection an unrecoverable
73
- * dead-end — cascade-2 reviewer Critical 2).
72
+ * rejected: the unilateral dedup guard makes rejection an unrecoverable
73
+ * dead-end.
74
74
  */
75
75
  status: "verified" | "corrected" | "directory_attested" | "leaves_invalid";
76
76
  /** lowercased pubkey → corrected content_frontier_seq, for inflated 'live' parties. The caller
@@ -79,14 +79,14 @@ export interface UnilateralFrontierCheck {
79
79
  reason?: string;
80
80
  }
81
81
  /**
82
- * FINDING-5 — the present party independently re-verifies (and CORRECTS) the UNILATERAL receipt's
83
- * frontiers. OVERRIDE, never reject.
82
+ * The present party independently re-verifies (and CORRECTS) the UNILATERAL receipt's frontiers.
83
+ * OVERRIDE, never reject.
84
84
  *
85
85
  * The unilateral legibility is FROST-notarized by the directory but the frontier VALUES are NOT bound
86
86
  * into the seal signature (unlike the bilateral seal, whose signer binds the legibility hash), so the
87
87
  * client must not trust them blindly. Because the directory-side dedup guard makes a client REJECTION
88
- * of a unilateral cert unrecoverable (a retry close is silently ignored — no receipt ever produced,
89
- * worse than FINDING-3), the client CORRECTS rather than rejects:
88
+ * of a unilateral cert unrecoverable (a retry close is silently ignored — no receipt is ever
89
+ * produced), the client CORRECTS rather than rejects:
90
90
  * - CLIENT-VERIFIABLE ('live') party: the present (submitting) party carries all its own signed
91
91
  * leaves, so its content_frontier_seq is fully re-derivable. The directory cannot forge signed
92
92
  * leaves, so the re-derived value is the provable truth → an inflated published value is OVERRIDDEN
@@ -1 +1 @@
1
- {"version":3,"file":"seal-frontier-verify.d.ts","sourceRoot":"","sources":["../src/seal-frontier-verify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAKH,MAAM,WAAW,gBAAgB;IAC/B,eAAe,EAAE,UAAU,CAAC;IAC5B,aAAa,EAAE,UAAU,CAAC;IAC1B,gBAAgB,EAAE,UAAU,CAAC;CAC9B;AAED,MAAM,MAAM,cAAc,GACtB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAC5C;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,wBAAwB,GAAG,gBAAgB,GAAG,uBAAuB,CAAA;CAAE,CAAC;AAQjG;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,EAAE,EAAE,iBAAiB,EAAE,UAAU,GAAG,cAAc,CA0B3G;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,YAAY,EAAE,aAAa,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,oBAAoB,EAAE,MAAM,CAAA;CAAE,CAAC,EAC7E,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAC3B;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAQ9E;AAID,MAAM,WAAW,6BAA6B;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,oBAAoB,EAAE,MAAM,CAAC;IAC7B,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,uBAAuB;IACtC;;;;;;;;;;OAUG;IACH,MAAM,EAAE,UAAU,GAAG,WAAW,GAAG,oBAAoB,GAAG,gBAAgB,CAAC;IAC3E;kHAC8G;IAC9G,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,uBAAuB,CACrC,YAAY,EAAE,aAAa,CAAC,6BAA6B,CAAC,EAC1D,cAAc,EAAE,gBAAgB,EAAE,GAAG,SAAS,EAC9C,SAAS,EAAE,UAAU,GACpB,uBAAuB,CAyBzB"}
1
+ {"version":3,"file":"seal-frontier-verify.d.ts","sourceRoot":"","sources":["../src/seal-frontier-verify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAKH,MAAM,WAAW,gBAAgB;IAC/B,eAAe,EAAE,UAAU,CAAC;IAC5B,aAAa,EAAE,UAAU,CAAC;IAC1B,gBAAgB,EAAE,UAAU,CAAC;CAC9B;AAED,MAAM,MAAM,cAAc,GACtB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAC5C;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,wBAAwB,GAAG,gBAAgB,GAAG,uBAAuB,CAAA;CAAE,CAAC;AAQjG;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,EAAE,EAAE,iBAAiB,EAAE,UAAU,GAAG,cAAc,CA0B3G;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,YAAY,EAAE,aAAa,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,oBAAoB,EAAE,MAAM,CAAA;CAAE,CAAC,EAC7E,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAC3B;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAQ9E;AAID,MAAM,WAAW,6BAA6B;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,oBAAoB,EAAE,MAAM,CAAC;IAC7B,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,uBAAuB;IACtC;;;;;;;;;;OAUG;IACH,MAAM,EAAE,UAAU,GAAG,WAAW,GAAG,oBAAoB,GAAG,gBAAgB,CAAC;IAC3E;kHAC8G;IAC9G,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,uBAAuB,CACrC,YAAY,EAAE,aAAa,CAAC,6BAA6B,CAAC,EAC1D,cAAc,EAAE,gBAAgB,EAAE,GAAG,SAAS,EAC9C,SAAS,EAAE,UAAU,GACpB,uBAAuB,CAwBzB"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * DOD-LEG-2 (SESSION-004 SI-002) — client-side content-frontier re-derivation.
2
+ * Client-side content-frontier re-derivation.
3
3
  *
4
4
  * The directory builds + signs the seal legibility, including each party's
5
5
  * `content_frontier_seq` ("the highest message the party provably received"). The client
@@ -85,14 +85,14 @@ export function findInflatedFrontier(participants, derived) {
85
85
  return null;
86
86
  }
87
87
  /**
88
- * FINDING-5 — the present party independently re-verifies (and CORRECTS) the UNILATERAL receipt's
89
- * frontiers. OVERRIDE, never reject.
88
+ * The present party independently re-verifies (and CORRECTS) the UNILATERAL receipt's frontiers.
89
+ * OVERRIDE, never reject.
90
90
  *
91
91
  * The unilateral legibility is FROST-notarized by the directory but the frontier VALUES are NOT bound
92
92
  * into the seal signature (unlike the bilateral seal, whose signer binds the legibility hash), so the
93
93
  * client must not trust them blindly. Because the directory-side dedup guard makes a client REJECTION
94
- * of a unilateral cert unrecoverable (a retry close is silently ignored — no receipt ever produced,
95
- * worse than FINDING-3), the client CORRECTS rather than rejects:
94
+ * of a unilateral cert unrecoverable (a retry close is silently ignored — no receipt is ever
95
+ * produced), the client CORRECTS rather than rejects:
96
96
  * - CLIENT-VERIFIABLE ('live') party: the present (submitting) party carries all its own signed
97
97
  * leaves, so its content_frontier_seq is fully re-derivable. The directory cannot forge signed
98
98
  * leaves, so the re-derived value is the provable truth → an inflated published value is OVERRIDDEN
@@ -108,17 +108,16 @@ export function findInflatedFrontier(participants, derived) {
108
108
  export function checkUnilateralFrontier(participants, frontierLeaves, sessionId) {
109
109
  const corrections = new Map();
110
110
  const haveLeaves = Array.isArray(frontierLeaves) && frontierLeaves.length > 0;
111
- // Pre-FINDING-5 directory (no leaves): keep FINDING-3's directory-attested behavior. Never reject —
112
- // rejecting would regress the shipped FINDING-3 receipt and break unilateral seals against a
113
- // not-yet-upgraded directory during rollout.
111
+ // A directory that ships no leaves leaves the cert directory-attested. Never reject — rejecting
112
+ // would break unilateral seals against a not-yet-upgraded directory during rollout.
114
113
  if (!haveLeaves)
115
114
  return { status: "directory_attested", corrections };
116
115
  const rederived = reDeriveFrontiers(frontierLeaves, sessionId);
117
116
  // Forged / cross-session leaves are the STRONGEST tamper evidence, so they get the strongest
118
- // correction treat them as ZERO trustworthy evidence (an empty derived map), which drives any
119
- // 'live' frontier > 0 down to 0. (Leaving the published value untouched would make malformed leaves
120
- // an EASIER inflation bypass than shipping none — cascade-2 re-review.) We still never REJECT
121
- // (the directory dedup guard makes rejection an unrecoverable dead-end); the caller logs loudly.
117
+ // correction: treat them as ZERO trustworthy evidence (an empty derived map), which drives any
118
+ // 'live' frontier > 0 down to 0. Do NOT leave the published value untouched on invalid leaves
119
+ // that would make malformed leaves an EASIER inflation bypass than shipping none. Still never
120
+ // REJECT (the directory dedup guard makes rejection an unrecoverable dead-end); the caller logs.
122
121
  const derived = rederived.ok ? rederived.frontiers : new Map();
123
122
  // Only CLIENT-VERIFIABLE ('live') parties are re-derivable; override any value above the provable
124
123
  // one DOWN to it. The absent party is directory-attested (its remainder is not re-derivable here).